pcb-rnd knowledge pool
How subcircuit source/transformations are tracked
subc_transf by Tibor 'Igor2' Palinkas on 2018-01-22
Tags: insight, data, model, subc, pick&place, pick, place, rotation, mirror, transformation
Abstract: How source and transformation of subcircuits are tracked. How pick and place output is generated using this information. What happens to the old gEDA/PCB elements.
Technical background: ref vs. copy
In generic programming sense, there are mainly two ways to store groups:
- reference : store the group once and just place references to it whenever the group is placed on the drawing; the reference should normally have the transformation instructions (e.g. rotate, mirror); this is how padstacks work
- copy : store a new, full copy of the group each time it is placed; store the transformed version; this is how subcircuits work.
Both methods have their advantages and drawbacks.
The reference version saves a lot of copying, but it makes it a bit harder to make local deviations. It may always suffer from rounding errors when all transformations apply, so it is a bit harder to guarantee "pixel-to-pixel" (or nanometer) correctness of a rendered reference, especially across different computer platforms.
The copy method overcomes these difficulties by simply storing the transformed version, so as long as the code can render simple, atomic objects properly, no amount of transformations can introduce a rendering error that behaves differently on different versions of the software or different computer platforms. It is also easy to make local deviations, like "make this one pad larger in this one instance of the subcircuit". But in return, the data is copied over and over so it's harder to:
- figure if two copies are derived from the same source
- figure what transformations the copy went through, compared to the original source
The unreproducible transformation problem starts to get worse when you allow transformation-in-transformation, because at the end you may end up needing to execute a long series of calculations because of many levels of embedded transformations. As a result rounding errors add up. They do too, when we use the copy method: but at least there the final copy does have all the accumulated errors ironed in, so the errors will look the same everywhere. With the reference method and transformation-in-transformation, the errors would be reproduced every time the object is rendered and in case of a long series of calculations it would be extremely hard to guarantee the same result on different software versions and computer platform. See also: svg.
The decision for storing padstacks referenced and subcircuits copied is made because:
- a padstack is a leaf object in our data tree: it never has children, thus a transformation on a padstack can not add to a series of transformations
- we will definitely have subcircuit-in-subcircuit later on; if we would store transformation on a subcircuit, this would mean transformation-in-transformation, which would lead to hard-to-control rounding errors.
Note: "how to change all instances at once" or "what if I still want to do a local deviation" did not affect the choice; those are available in both versions, they are just cheaper or more expensive in one or the other.
The rest of this article deals with how the specific problems induced by the subc copy approach are solved.
How sources are tracked
Each subcircuit has an unique ID, an uuid. This uuid is generated once, when the subcircuit is first made - e.g. when it is converted from objects to subcircuit. Once the uuid is assigned, it never changes, unless the user explicitly requests so. Thus the uuid always identifies the original source. The uuid is preserved when copies are made (subcircuits are placed in a drawing) or when copies are transformed or modified.
The uuid can be used to track back the source of the subcircuit, e.g. into a random library of footprints. This allows the user to replace existing copies with a new version from the library, even if the existing copies had transformations. However, local deviations (like "shrink this one pad") are not tracked and would be simply overwritten on such a "replace this from the lib" operation.
The uuid does not do version control: it's the user's responsibility to decide when to change the uuid. For example if there's a substantial change to a footprint in the lib, it's either a change the user does want to apply to existing boards (uuid shouldn't change, so the replace works) or it makes up a new footprint (uuid should change). If a new footprint is created by copying an old subcircuit and editing it, the user shall change the uuid.
How transformations are tracked
Every subcircuit has a hidden virtual layer called subc-aux. On this layer, there are a few mandatory objects and a few optional objects, including a point and two lines, all tagged with attributes. The two lines are of known length and are axis aligned when the subcircuit is made. Any time a transformation is applied to the subcircuit, it is applied to all layers, including the subc-aux layers. At the end, when the code looks at a transformed subcircuit, comparing how these lines look like on an untransformed subcircuit, it is possible to extract the final (accumulated) transformation:
- translation (a.k.a. move, origin, offset)
- scaling (which the UI doesn't support yet)
Pick & place - how to design a footprint
The pick and place process needs two information per subcircuit: the centroid (placement or translation) and the rotation. How they are determined is a tricky question - pick and place fabs usually will need to fine tune the xy file, knowing their machine, their process and the actual parts.
When designing a footprint, the rule of thumb is:
- either set the subcircuit origin to the pick-and-place origin, or mark the pick-and-place origin explicitly on the subc-aux layer (TODO: how this can be done in the GUI?)
- draw the footprint at the neutral, 0 degree rotation state
As of what's the pick-and-place origin and the neutral rotation, refer to IPC-7351 (unfortunately that document is non-free - some people still don't understand how standards should work). The idea is to have the pick-and-place origin and the 'centroid', which is the center-of-mass calculated by the pads. Pad 1 should be upper left.
The old gEDA/PCB model: elements
This chapter has only historical relevance; pcb-rnd doesn't depend on this old model.
The old PCB element model uses the copy approach too, but does not store any information about transformations. Thus it is impossible to properly determine the rotation of an element. The XY export code used to guess by trying to apply a home-grew heuristics that was invented around the same time as IPC-7351 came out. Later on the code has been switched over to follow IPC-7351.
However, guessing is guessing; the guessing handles only integer multiplication of 90 degree rotations.
Since pcb-rnd has solved this issue for good with subcircuits, pcb-rnd is not affected - until it is forced to load an old gEDA/PCB file stuffed with elements. io_pcb loads such files by converting elements to subcircuits. When that happens, the heuristics are executed and the subcircuit rotation and pick-and-place origin are set properly. However, this works to N*90 degrees too.
Conclusion: when using subcircuits only, rotations are handled properly; when old elements are loaded, the code has the same limitation as PCB had for exporting elements to XY.
Other alien formats
There are two types of alien formats: those that store the origin/rotation infromation in one way or another, and those that don't. The gEDA/PCB format falls in the second group, among with a lot of older formats.
When pcb-rnd loads element data or footprints from an alien format, it tries to reuse the rotation/origin info provided. When it can not be reused, the same heuristics is used as described above, for PCB.