← Back to team overview

kicad-developers team mailing list archive

Re: Experiments and considerations for more layer


On Tue, Sep 03, 2013 at 04:15:39AM -0700, Cirilo Bernardo wrote:
> A down side to keeping things manageable (such as merging components into the PCB) would be that there has to be a mapping which KiCAD enforces for the integer layer ID and the usage. That is certainly possible and when implemented I suspect the biggest nuisance would be to people who use custom layers - but even that can be addressed by reserving a block of IDs which KiCAD will never use in the main tree.

Nothing forbids to reserve a range of layer IDs for that. Eagle for
example has the first 100 layers 'reserved'. We can decide, for example
that 0-15 (or maybe 0-31) are copper layers, the next ten or so are the
special layers and over a 'safe' number (64, or 100) are available to
the user for his stuff.

That would simplify the reconciling code a bit and let use integer
literals for the special layers (with ominous operator definitions,
obviously). Of course user defined layers would need the stackup object
help to correlate. Semi-formal example:

Board A 'domain'
Stackup object STACKA
    0 Front
    15 Back
    16 Silk Front
    100 Peel Mask

Board B 'domain' (could be for a library module, maybe)
Stackup object STACKB
    0 Front
    15 Back
    16 Silk Front
    150 Peel Mask
    160 Plating

The stackup would be vectors or sets, depending on the maximum number of
layers you want to handle. Also O(1) vs O(log n) performance. The usual

A layer id would be an ordered pair containing the stackup and the layer
number. A pointer and an integers in other words. Significantly larger
than an int but still acceptable. These things have to be designed to be
passed around by value, like numbers (C++ can do that without problems).

So STACKA:15 is the back copper in the board A domain, STACKB:160 is the
plating layer in the board B domain.

Also, a 'small integer' is valid as a layer id for fixed meaning layers.
So 16 is *always* silk front, 0 is always copper front and so on. 150
has *no meaning* by itself since it's not a common layer. IIRC you can
define these implicit constructors, too.

Then, establish an equivalence relationship same-layer-as where:
0 same-layer-as STACKA:0, 
0 same-layer-as STACKB:0,
STACKA:100 same-layer-as STACKB:150

(things to clarify: should it this be on the name given by the user? also,
what if a layer with the same name has different attributes on the
opposite sides? example: STACKA:100 could have a paired layer,
STACKB:150 not necessarily; an 'incompatible definition' exception
should be resolved in some way)

Finally, add an operation change-domain so that:
0 change-domain STACKA -> 0 (no need to qualify it!)
STACKA:100 change-domain STACKB -> STACKB:150 (a lookup)
STACKB:160 change-domain STACKA -> STACKA:somenewnumber (the merge
obviously after that
STACKA:thepreviousnumber change-domain STACKB -> STACKB:160

For the masking issue: as correctly shown *most* operation actually need
only one layers (or a range, for vias). OTOH pads need a bitmask or
something similar. These could be represented as vector<bool> or
set<int> (same consideration as before). Layer masks need to reference
to a stackup, as layer ids. So valid masks could be:

STACKA:0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 (a THT pad)
STACKB:0,160 (a card egde pad, maybe)

Optional: the 1-14 range could be optimized with a flag 'all inner
layers', since it's a very common thing and wastes both time to manage
and space in the mask structure.

Define on this object the change-domain operation (trivial) and a lookup
operation for containment (trivial, too, and actually the most frequent
operation on layer masks). Testing a layer for containment in a mask
from another domain could simply apply change-domain to the checked
layer before testing.

In short, instead of (mask & LAYER_FRONT) use
mask.contains(LAYER_N_FRONT). Many mask uses actually in a loop like

for (i = LAYER_N_FRONT; i <= LAYER_N_BACK; ++i) {
    LAYER_MASK msk = GetLayerMask(i);
    if (the_mask & msk) stuff();

and that becomes (I find it more elegant)

for (i = LAYER_N_FRONT; i <= LAYER_N_BACK; ++i) {
    /* i is a reserved layer number so an int is acceptable! */
    if (the_mask.contains(i)) stuff();

of course member iteration would be an interesting thing to do, if

I don't remember if intersection between arbitrary masks is ever
used... that could be defined as logical/set intersection, no problem
with that.

Add useful predicates like: IsFrontLayer (for a layer) or HasFrontLayer
(for a mask, to check if it contains at least one layer on the front
side). The list of these predicates has to be determined by inspection
(i.e. during implementation :D). Some of these are already in use (look
in the layer header). Also the fliplayer function. A layer can access
its stackup so it can ask it for its paired layer.

IIRC these are all the operations needed (well, serialization too but
that's mostly a loop of layer-in-mask checks or an enumeration of some

That should implement the requested features.

ALSO, most importantly, add domain changes whenever a pad/module/entity
changes board (and with board I mean the module editor/library, too!).
*That* is the real painful thing to do for making this work.

Have I hit on the mark or have I forgot something? (the time needed to
do all this of course is out of question:P)

Lorenzo Marcantonio
Logos Srl

Follow ups