mirror of
https://github.com/bancika/diy-layout-creator
synced 2026-11-07 12:24:58 +10:00
Create bottom-side-components.md
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
# Bottom-Side Components
|
||||
|
||||
Let a component be mounted on the bottom of a PCB, so that its silkscreen goes to the bottom
|
||||
Gerber layers and the canvas shows it as a part on the far side of the board.
|
||||
|
||||
Status: not started.
|
||||
|
||||
## 1. Current state
|
||||
|
||||
- Traces, pads, PCB text and ground fills carry a `PCBLayer` (`Bottom`, `Top`, `Inner 1..6`).
|
||||
It feeds `PCBLayer.toGerberCopperLayer()`, `PCBLayerFilter`, the trace-mask print and
|
||||
continuity (`ILayeredComponent.getLayerId()`).
|
||||
- Every other Gerber-aware component implements `IGerberComponentSimple`, whose
|
||||
`getGerberLayer()` defaults to `SilkscreenTop`. About 35 classes in `diylc-library` rely on that
|
||||
default (resistors, film and radial capacitors, diodes, LEDs, transistor packages, DIL/SIL ICs,
|
||||
SMD passives, switches, jacks, relays, transformers, tube sockets, ...). None of them emit copper.
|
||||
Their pads are separate `SolderPad` components, which already have a layer.
|
||||
- Two copies of the same dispatch decide the layer for a simple component:
|
||||
`GerberExporter.drawComponent` and `GerberPathRenderer.drawComponent`. Both call
|
||||
`g2d.startGerberOutput(getGerberLayer(), ...)`.
|
||||
- Z-order is paint order only. A trace on the bottom layer already paints above the board
|
||||
(`zOrder = TRACE`), so the codebase already treats the physical side as separate from z-order.
|
||||
|
||||
## 2. Decisions
|
||||
|
||||
| Decision | Rationale |
|
||||
| --- | --- |
|
||||
| A new two-value `BoardSide` (`Top`, `Bottom`), not a reuse of `PCBLayer` | Parts mount on the outer faces only. Inner layers would be invalid choices in the editor. |
|
||||
| The side is the physical fact. Z-order stays paint order and is not tied to it | Boards are drawn opaque, so painting bottom parts beneath the board would hide them. Bottom-layer traces already follow this rule. |
|
||||
| The canvas shows the side visually and does not rely on z-order for it | That is what removes the ambiguity of a "bottom" part drawn above the board. |
|
||||
| Flipping a part is an action that sets the side, mirrors the part and sends it back | A part seen from above through the board is a mirror image. Changing the side without mirroring gives a footprint that is wrong for fabrication. |
|
||||
| The Gerber layer is swapped centrally, not inside each component | Components keep emitting their `*Top` layer. One place maps it to the other side. |
|
||||
| Default `Top`, with `null` meaning `Top` | Fields added to existing classes deserialize as `null` in old files. |
|
||||
|
||||
## 3. Design
|
||||
|
||||
### diylc-core
|
||||
|
||||
- **`org.diylc.common.BoardSide`**: enum `Top`, `Bottom`, with a `toString()` label like `PCBLayer`.
|
||||
- **`org.diylc.core.IBoardSideComponent`**: `BoardSide getBoardSide();` with Javadoc and
|
||||
`@author`, in the same shape as `ILayeredComponent`. Core knows the side only through this
|
||||
interface.
|
||||
- **`GerberLayer.flip()`**: maps `CopperTop`/`CopperBot`, `SilkscreenTop`/`SilkscreenBot` and
|
||||
`SolderMaskTop`/`SolderMaskBot` to each other. `Outline` and the drill layers map to themselves.
|
||||
- **Gerber dispatch**: in the simple-component branch, use
|
||||
`getGerberLayer().flip()` when the component is an `IBoardSideComponent` on `Bottom`. Merge
|
||||
`GerberExporter.drawComponent` and `GerberPathRenderer.drawComponent` into one method first, so
|
||||
the rule is written once.
|
||||
- **Canvas rendering**: `DrawingManager.drawProject` draws bottom-side components with a
|
||||
composite that sets them apart. Locked components already use a 0.5 alpha composite
|
||||
(`lockedComposite`), so the bottom-side treatment has to look different from that, for example
|
||||
a lower alpha or a tint. Pick one and check it against a locked bottom-side part.
|
||||
|
||||
### diylc-library
|
||||
|
||||
- The ~35 classes that use the `SilkscreenTop` default implement `IBoardSideComponent`. Each adds a
|
||||
nullable `BoardSide boardSide` field and a getter/setter pair, with
|
||||
`@EditableProperty(name = "Board Side")` on the getter. The getter returns `Top` when the field
|
||||
is `null`. This duplicates the field across classes in the same way the `PCBLayer` field is
|
||||
duplicated today. Where several of these classes share an abstract base
|
||||
(`AbstractRadialComponent`, `AbstractFilmCapacitor`, `AbstractTransistorPackage`, ...), put it on
|
||||
the base.
|
||||
- `CopperTrace`, `CurvedTrace`, `GroundFill`, `SolderPad`, `PCBText` and `DrillHole` do **not**
|
||||
implement it. Their `PCBLayer` already says where they are.
|
||||
- Before allowing `Bottom` on a class, check that its transformer can mirror it
|
||||
(`canMirror`). A class that cannot be mirrored must not offer `Bottom`, or the footprint would be
|
||||
wrong.
|
||||
|
||||
### diylc-swing
|
||||
|
||||
- **Flip to Other Side** action in the Edit menu and the canvas popup menu, following the
|
||||
align-distribute actions. For each selected `IBoardSideComponent` it:
|
||||
1. toggles the side,
|
||||
2. mirrors horizontally about the selection centre with the component's `IComponentTransformer`,
|
||||
3. sends the component to the back of its z-order band.
|
||||
|
||||
The whole action is one undo step, and it is enabled only when the selection holds at least one
|
||||
`IBoardSideComponent`. The work is done in `Presenter` behind `IPlugInPort`, with the plugin
|
||||
only wiring the menu items.
|
||||
- **PCB print-outs**: `SilkScreenDrawingProvider` prints one page of everything that has a
|
||||
silkscreen outline. Split it into top and bottom pages, filtered by side, the same way
|
||||
`TraceMaskDrawingProvider` gives one page per `PCBLayer`.
|
||||
|
||||
## 4. Open questions
|
||||
|
||||
- **Bottom silkscreen text.** Component labels drawn through `IGerberComponentSimple` have to read
|
||||
correctly once the board is turned over, which means they must look mirrored from the top. Check
|
||||
whether mirroring through the transformer does that for every class, or whether labels are drawn
|
||||
unmirrored regardless. `GerberLayer.mirrored` is currently `false` for every layer, and the
|
||||
exporter output must stay in top view.
|
||||
- **SolderPad solder mask.** `SolderPad` always writes its mask opening to `SolderMaskBot`,
|
||||
whatever its own `PCBLayer`. Confirm whether this is intended for top-layer SMD pads before
|
||||
`GerberLayer.flip()` is introduced next to it.
|
||||
- **Plain property or action only.** A plain editable property lets a user change the side without
|
||||
mirroring. Either keep both (the property for already-mirrored parts) or show the side as
|
||||
read-only and change it only through the action.
|
||||
|
||||
## 5. Verification
|
||||
|
||||
- Unit tests in `diylc-core` for `GerberLayer.flip()` and for the merged dispatch method, using a
|
||||
stub `IBoardSideComponent` (top, bottom and `null`).
|
||||
- Load an old `.diy` with PCB parts and confirm every component reports `Top` and that the Gerber
|
||||
output is byte-identical to before.
|
||||
- Export a board with one part on each side and open the files in a Gerber viewer: the bottom part
|
||||
appears only in `.gbo`, mirrored, and in the right position.
|
||||
- Run the regression suite, since rendering and serialization both change.
|
||||
|
||||
## 6. Next steps
|
||||
|
||||
1. Merge the two `drawComponent` copies in `diylc-core` and add `GerberLayer.flip()` with tests.
|
||||
2. Add `BoardSide` and `IBoardSideComponent`, and swap the Gerber layer in the dispatch.
|
||||
3. Add the field to the library components, putting it on shared bases where they exist. Settle
|
||||
the silkscreen-text question while doing so.
|
||||
4. Add the bottom-side composite in `DrawingManager`.
|
||||
5. Add the Flip to Other Side action.
|
||||
6. Split the silkscreen print-out by side.
|
||||
7. Run the regression suite.
|
||||
Reference in New Issue
Block a user