Convention: typedef'd types are called pcb_*_t - the corresponding struct is pcb_*_s.
There is only one board being edited. A board is the model of the whole world for pcb-rnd - any other, auxiliary data is only a partial description of a board. The data struct for board is pcb_board_t. As of now, pcb-rnd edits only one board at a time, and it is stored in a global variable called PCB. The code is single threaded and is not reentrant.
Half of the board structure describes global board properties:
Relevant structs, variables and functions are in board.[ch].
A pcb_data_t contains everything to describe the 2d geometry of an existing board:
Relevant structs, variables and functions are in data.[ch].
However, it does not contain the layer stackup, but references to it. This means a data_t can be copied or moved from within one board to another, even with mismatching layer stack. When a pcb_data_t is within a pcb_board_t, the layer references are fixed, pointing to actual layers within the board's layer stack (e.g. "layer 3 in the stack"). Such a layer is also called a real layer. When a pcb_data_t is used outside of a board (e.g. as a buffer), a generalized, recipe-like layer description is used (e.g. "second copper layer counted from the top"). Such a layer is not a real layer, but can be bound to a real layer. The binding means that while it keeps it's recipe-like description, it is also referring to an actual layer of the stack of the current PCB structure. (This happens to subcircuits: they are specified with generalized, recipe-like layers that are bound to the board's layer stack once the subcircuit is placed on the board.)
Paste buffers are pcb_buffer_t; the main field is pcb_data_t.
Relevant structs, variables and functions are in buffer.[ch].
A paste buffer doesn't have a layer stack. When data is copied from a board to a buffer, the layer references are generalized.
Layers are abstract canvases also serving as a logical grouping of drawing primitives. Every layer is part of exactly one layer group. A layer group is close to what a physical layer is on the FR4.
The location of a layer group on of (flags are in layer.h):
In pcb-rnd most layer types are fully explicit:
The layers of a composite layer group are combined + and -, depending on the PCB_LYC_SUB in their comb field.
Assumptions: there are two silk layers, one for the top and one for the bottom side and there are always a top and a bottom copper layer. (Long term assumptions will be removed.) The outline layer has to be global.
The rest of the layers are virtual layers, often just GUI hacks, e.g.:
Global data affect all layers. The most trivial example is padstack: it has a hole and potentially a copper ring on all layers. A padstack directly under the board's pcb_data_t is considered a "via" (by convention, by purpose), a padstack under subcircuit's pcb_data_t is usually a "pin" or "pad" (when has a term ID) or a "via". The quoted terms don't exist in the code, they are just conventions. Padtsacks are vertical constructions, they may affect multiple layers.
The other global object is subcircuit; using its own layer list, it potentially can affect all layers of the board. The children objects of a subcircuit is a pcb_data_t, which allows arbitrary (loop-free) recursion in data.
The data struct has a pcb_layer_t for each logical layer, to host the per layer objects (drawing primitives).
Layer data is stored in struct pcb_layer_t. A layer has a list for each object type (drawing primitive type): arcs, lines, polygons, etc. These lists are local: in a tree of subcircuits, the layer list contains only what's strictly directly on the given layer, there's no recursion.
Relevant structs, variables and functions are in layer.[ch].
A tree can be built using subcircuits. The top level is always a board or a buffer, which are essentially just different wrappers around pcb_data_t. Then pcb_data_t can host subcircuits in it global data. A subcircuit is also just a 3rd kind of wrapper around a pcb_data_t, which means it opens a new level of the tree.
When we will start supporting subc-in-sunc this means the root is:
(When the user opens a subcircuit for editing, the root is still a pcb_board_t, it just has a field that tells it is a fake board for a single subc. Still the real subc to edit is in the fake board's pcb_data_t. This is needed so we have all the global states stored in pcb_baord_t.)
Then the next levels are arbitrary encapsulation of subcircuits.
Each level of pcb_data_t has its own lists storing the local objects.
The root of the tree also ha an rtrees for each object type. An rtree is a spatial data structure that provides efficient coordinate lookups. All objects, recursively, are added in these rtrees. That means, if there's a line on the board, that's really a line stored on a pcb_layer_t's list, and the full tree path is somehting like this:
pcb_board_t -> pcb_data_t -> pcb_layer_t -> linelist_t
The same line is also added in the same layer's rtree (called line_tree for lines):
pcb_board_t -> pcb_data_t -> pcb_layer_t -> pcb_rtree_t
On subsequent levels, the list is the same. If a line is part of a subcircuit, it's sitting on one of the linelists on one of the bound layers provided by the subc:
pcb_board_t -> pcb_data_t -> pcb_subclist_t -> pcb_data_t -> pcb_layer_t -> pcb_linelist_t
The subcircuit's pcb_layer_t would also have an rtree. But instead of having it's own rtree, it's rather linked to the root's rtree. Which means any access to any rtree under a subc is really access to the corresponding rtree of the root! In other words, object lists are level-local, but rtrees are tree-global.
This also means as a programmer, you can alsways do two kind of searches or iterations:
Rationale: this way if we need to look up what's on a specific coord (e.g. the user clicks or find.c needs to check what is connected to a given point), we can use the global rtree's and we will get the answers without having to recurse into subcircuits.
Note: layer bindings complicate this a bit. A subc layer is bound to one of the root's real layers. When that binding is created, the rtrees are also linked together, and all subc layer objects on the given layer are added in the common rtree. When the layer binding needs to change, that means we first need to unbind the subcircuit's bound layer from the root's real layer: when we do that, we need to unlink the rtrees and remove all the objects we added to the global tree from our subcircuit layer.