delorie.com/archives/browse.cgi   search  
Mail Archives: geda-user/2016/01/21/16:29:44

X-Authentication-Warning: delorie.com: mail set sender to geda-user-bounces using -f
X-Recipient: geda-user AT delorie DOT com
Date: Thu, 21 Jan 2016 22:26:41 +0100 (CET)
From: Roland Lutz <rlutz AT hedmen DOT org>
To: "Britton Kerin (britton DOT kerin AT gmail DOT com) [via geda-user AT delorie DOT com]" <geda-user AT delorie DOT com>
Subject: [geda-user] PCB data structures (was: features: layers stack,
padstack/vias)
In-Reply-To: <CAC4O8c_tFOOXCA5ABEMuSU8BnXMZWauV+uJYy-TJO7nJYBS9+A@mail.gmail.com>
Message-ID: <alpine.DEB.2.11.1601111649470.5421@nimbus>
References: <1512221837 DOT AA25291 AT ivan DOT Harhan DOT ORG> <20160106133049 DOT 5A0E9809D79B AT turkos DOT aspodata DOT se> <CACwWb3Cyk4yLwt3=V1Mu5C4RieOQEjYH3ej5MXZSNnLPbshqDg AT mail DOT gmail DOT com> <20160106143629 DOT 4D39D809D79B AT turkos DOT aspodata DOT se> <CACwWb3BXbnQXs+DwVVzmC8DrhwOYxPgVyUhZTPL9bM9cJbHimw AT mail DOT gmail DOT com>
<20160106164022 DOT D0D4E809D79B AT turkos DOT aspodata DOT se> <20160106180912 DOT 42ddf4079d91384f206b7c35 AT gmail DOT com> <20160106191433 DOT 5dc5cb59 AT jive DOT levalinux DOT org> <20160106202817 DOT 56197b2c539d426a1b724c9e AT gmail DOT com> <568E09ED DOT 1080508 AT m0n5t3r DOT info>
<CACwWb3AhSh-+NNu--bVMGZBfjaoA+hHg7gbXnoyNv3oMq=e17g AT mail DOT gmail DOT com> <568E6354 DOT 80302 AT m0n5t3r DOT info> <20160108002640 DOT 03233b24 AT jive DOT levalinux DOT org> <20160108175259 DOT 127a3f073616758434f7edff AT gmail DOT com> <20160109020345 DOT 1e07cb84 AT jive>
<CAC4O8c-nqs2+9rgsD-Gsks-wSmJ1eCkJ9PFMi3XqMrYE2FO3Ew AT mail DOT gmail DOT com> <20160109112851 DOT 1129dc38 AT wind DOT levalinux DOT org> <CAC4O8c_tFOOXCA5ABEMuSU8BnXMZWauV+uJYy-TJO7nJYBS9+A AT mail DOT gmail DOT com>
User-Agent: Alpine 2.11 (DEB 23 2013-08-11)
MIME-Version: 1.0
Reply-To: geda-user AT delorie DOT com
Errors-To: nobody AT delorie DOT com
X-Mailing-List: geda-user AT delorie DOT com
X-Unsubscribes-To: listserv AT delorie DOT com

  This message is in MIME format.  The first part should be readable text,
  while the remaining parts are likely unreadable without MIME-aware tools.

--8323329-247969367-1452532489=:5421
Content-Type: TEXT/PLAIN; CHARSET=ISO-8859-15; FORMAT=flowed
Content-Transfer-Encoding: 8BIT
Content-ID: <alpine DOT DEB DOT 2 DOT 11 DOT 1601212055121 DOT 27501 AT nimbus>

On Sat, 9 Jan 2016, Britton Kerin (britton DOT kerin AT gmail DOT com) [via 
geda-user AT delorie DOT com] wrote:
> First the existing parser needs to get separated from the innards of pcb.

I agree.

> As things stand now there's no single data structure that corresponds
> one-to-one with the format. Once this is done other equivalent formats
> could be implemented.

So let's start with that! :-)  I translated the PCB file format definition 
into a set of C structs which you find below.  The rationale behind this 
data structures is as follows:

- An "object" should be something that is located at some position on the 
board, and which makes sense to manipulate as an entity (e.g., both an 
"element instance" and a "pad" are objects, but an attribute is not).

- The location of an object in the hierarchy should be independent from 
the actual data which makes up the object (i.e., there are no pointers to 
child objects in the data structures; the hierarchy is managed separately 
and points to each object's data).

- The data structure should be semantic; any value should have an explicit 
field, explicit type, and explicit meaning.  Attributes are implemented as 
fields in a struct so they are semantically accessible to other users of 
the data structure.

- All code operating on these data structures should be in the main 
repository, so if some change to the data structure is necessary, it can 
be performed without having to worry about breaking code elsewhere.

- The data structure should not contain any fields that are internal to 
the PCB implementation.

How the in-memory object hierarchy should look like is actually a 
non-trivial question.  I think it's generally a good idea to have the 
state of the object tree represent something on which you can do a 
meaningful undo/redo: for schematics, for example, this would be each 
referenced or embedded symbol, and the schematic itself.  But a PCB file 
consists of four different kinds of things: the PCB layout, the imported 
netlist, the elements in the layout, and the stroke font.

The netlist is a bit different in that it doesn't have a geometric 
structure; it's not "objects" in the definition above.  The stroke font 
symbols are obvious candidates for separate object trees.  The hard 
question is whether to treat an element as a "group object" which contains 
a bunch of element line/arc, pin, and pad objects, or to treat it as an 
instantiation of an embedded element definition (a.k.a. footprint).

I think from a logical perspective it makes more sense to treat it as an 
instantiation (you might want to update an embedded footprint, or to not 
embed but reference a footprint in the future), but the way elements are 
currently used suggests a group object notion, too (for example changing 
the sizes of individual pads/pins in an existing element).  Since I had to 
choose, I finally went with treating them as an instantiation (this is at 
least consistent with the way we treat symbols in gEDA/gaf).


### Object hierarchy ###

A PCB file contains a PCB layout, an arbitrary number of footprints, and 
(usually) 94 symbols.

The PCB layout has an object of type pcb_layout as the root object, which 
can contain an arbitrary number of pcb_layer objects--which in turn 
contain the geometric primitives--and pcb_element objects which aren't 
associated with a layer.  Objects of type pcb_polygon can have child 
objects of type pcb_polygon_hole:

pcb_layout
+- pcb_layer
    +- pcb_arc
    +- pcb_line
    +- pcb_pad
    +- pcb_pin
    +- pcb_polygon
       +- pcb_polygon_hole
    +- pcb_rat
    +- pcb_text
    +- pcb_via
+- pcb_element

A footprint (element definition) has a pcb_footprint object as the root 
object which can only contain objects of the types pcb_element_arc, 
pcb_element_line, pcb_pad, and pcb_pin:

pcb_footprint
+- pcb_element_arc
+- pcb_element_line
+- pcb_pad
+- pcb_pin

A symbol definition has a pcb_symbol object as the root object which can 
contain pcb_symbol_line objects:

pcb_symbol
+- pcb_symbol_line


### Struct definitions ###

I'm using double coordinates here.  This is not a meant as a statement 
regarding integer vs. floating-point values; you can simply replace 
"double" with your favorite kind of "int".

The "flags" field gave me a bit of trouble since it is a kind of 
general-purpose field which contains a lot of unrelated information. 
Most of this is only valid for certain object types, though, so I moved 
these flags to the appropriate type structs.  What remained is five flags 
which apply to most geometric objects:

/* Helper struct
    used by arc, line, pad, pin, rat, text, via, element, and element text */

struct pcb_flags {
 	bool found;
 	bool selected;
 	bool failed_drc;
 	bool locked;
 	bool connected;
};

It may make sense to move some of this information (found and selected 
objects, DRC result) to a different place later since it represents a 
set of objects, not a state of an individual object.

There are some directives (Grid, PolyArea, Thermal, DRC, Styles) which 
relate to the PCB file as a whole and can't be used more than once, so I 
grouped them into the pcb_layout data structure:

struct pcb_layout {
 	struct string name;
 	struct double2d size;
 	struct {
 		double step;
 		struct double2d offset;
 		bool visible;
 	} grid;
 	struct {
 		double area;
 	} poly_area;
 	struct {
 		double scale;
 	} thermal;
 	struct {
 		double bloat;
 		double shrink;
 		double line;
 		double silk;
 		double drill;
 		double ring;
 	} drc;
 	struct {
 		bool showdrc;
 		bool rubberband;
 		bool nameonpcb;
 		bool autodrc;
 		/* ... flags ... */
 	} flags;
 	struct string groups;
 	struct {
 		struct string name;
 		double thickness;
 		double diameter;
 		double drill;
 		double keepaway;
 	} styles[4];
 	struct {
 		bool grid_imperial;
 		double grid_size;
 		/* ... attributes ... */
 	} attributes;
};

I probably missed some flags and attributes because I couldn't find a list 
in the documentation and guessed from an example file.

There isn't much data associated with a layer, just the number and a name:

struct pcb_layer {
 	int number;
 	struct string name;
};

The geometric objects contain a "flags" field (except for pcb_polygon). 
Some flags which are only valid for certain object types are defined 
directly in the object's data structure, and pcb_element contains two sets 
of flags, one for the element itself and one for its text label:

struct pcb_arc {
 	struct double2d pos;
 	struct double2d radius;
 	double thickness;
 	double clearance;
 	int startangle;
 	int deltaangle;
 	bool clear_polygons;
 	struct pcb_flags flags;
};

struct pcb_element {
 	struct pcb_flags flags;
 	struct string desc;
 	struct string name;
 	struct string value;
 	struct double2d mark;
 	struct double2d text;
 	int tdir;
 	double tscale;
 	bool hide_element_name;
 	bool show_pin_names;
 	bool onsolder;
 	struct pcb_flags tflags;
 	struct pointer footprint;
};

struct pcb_line {
 	struct double2d pos[2];
 	double thickness;
 	double clearance;
 	bool clear_polygons;
 	bool autorouted;
 	struct pcb_flags flags;
};

struct pcb_pad {
 	struct double2d pos[2];
 	double thickness;
 	double clearance;
 	double mask;
 	struct string name;
 	struct string number;
 	bool nopaste;
 	bool onsolder;
 	bool square;
 	bool warn;
 	bool edge2;
 	struct pcb_flags flags;
};

struct pcb_pin {
 	struct double2d pos;
 	double thickness;
 	double clearance;
 	double mask;
 	double drill;
 	struct string name;
 	struct string number;
 	bool hole;
 	bool square;
 	bool warn;
 	bool octagon;
 	bool edge2;
 	struct pcb_flags flags;
};

struct pcb_polygon {
 	struct double2d_list vertices;
 	bool clear;
 	bool full;
};

struct pcb_polygon_hole {
 	struct double2d_list vertices;
};

struct pcb_rat {
 	struct double2d pos[2];
 	int group[2];
 	struct pcb_flags flags;
};

struct pcb_text {
 	struct double2d pos;
 	int direction;
 	double scale;
 	struct string string;
 	struct pcb_flags flags;
};

struct pcb_via {
 	struct double2d pos;
 	double thickness;
 	double clearance;
 	double mask;
 	double drill;
 	struct string name;
 	bool hole;
 	bool autorouted;
 	bool warn;
 	bool octagon;
 	struct pcb_flags flags;
};

The pcb_footprint structure has much duplication with pcb_element.  Some 
of this can probably be removed since some fields only make sense for an 
element instance and others for a footprint, but since I'm not too 
familiar with the way PCB works, I thought it would be safer to keep them 
for now:

struct pcb_footprint {
 	struct string desc;
 	struct string name;
 	struct string value;
 	struct double2d mark;
 	struct double2d text;
 	int tdir;
 	double tscale;
 	bool hide_element_name;
 	bool show_pin_names;
 	bool onsolder;
 	struct {
 		struct string device;
 		struct string manufacturer;
 		struct string manufacturer_part_number;
 		struct string vendor;
 		struct string vendor_part_number;
 		/* ... attributes ... */
 	} attributes;
};

As with the PCB file, I guessed the attributes from an example file and 
probably missed some.

struct pcb_element_arc {
 	struct double2d pos;
 	struct double2d radius;
 	int startangle;
 	int deltaangle;
 	double thickness;
};

struct pcb_element_line {
 	struct double2d pos[2];
 	double thickness;
};

Finally, symbol definitions:

struct pcb_symbol {
 	wchar_t ch;
 	double delta;
};

struct pcb_symbol_line {
 	struct double2d pos[2];
 	double thickness;
};

--8323329-247969367-1452532489=:5421--

- Raw text -


  webmaster     delorie software   privacy  
  Copyright 2019   by DJ Delorie     Updated Jul 2019