Gerber export decides each component's layer in one place, and Gerber layers can be mapped to the other side of the board

This commit is contained in:
Bane Stojkovic
2026-11-11 14:46:02 +01:00
parent 39c7743c30
commit 324cb20634
6 changed files with 190 additions and 44 deletions
+20 -23
View File
@@ -3,7 +3,8 @@
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.
Status: the Gerber dispatch is a single method and `GerberLayer.flip()` exists, with tests; the
next steps below remain.
## 1. Current state
@@ -19,9 +20,10 @@ Status: not started.
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(), ...)`.
- One method decides the layer for a simple component: `GerberExporter.drawComponent`, which
calls `g2d.startGerberOutput(getGerberLayer(), ...)`. It is package-private so that
`GerberExporterTest` can call it. `GerberLayer.flip()` maps a layer to the other side of the
board.
- 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.
@@ -44,12 +46,8 @@ Status: not started.
- **`org.diylc.core.IBoardSideComponent`**: `PCBLayer getBoardSide();` with Javadoc and
`@author`, in the same shape as `ILayeredComponent` but deliberately separate from it (see
Decisions). It returns `_1` or `_2` only. 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 `_1`. Merge
`GerberExporter.drawComponent` and `GerberPathRenderer.drawComponent` into one method first, so
the rule is written once.
- **Gerber dispatch**: in the simple-component branch of `GerberExporter.drawComponent`, use
`getGerberLayer().flip()` when the component is an `IBoardSideComponent` on `_1`.
- **Canvas rendering**: bottom-side components use the far-side fade from `copper-side-view.md`,
wrapping their painting in `startDrawingSide(getBoardSide())`. They are then faded in the main
view and solid in the Underside Display mirror, the same as bottom copper.
@@ -135,8 +133,8 @@ order.
## 5. Verification
- Unit tests in `diylc-core` for `GerberLayer.flip()` and for the merged dispatch method, using a
stub `IBoardSideComponent` (top, bottom and `null`).
- `GerberLayerTest` covers `flip()`. `GerberExporterTest` covers the dispatch for simple components;
extend it with a stub `IBoardSideComponent` (top, bottom and `null`).
- Load an old `.diy` with PCB parts and confirm every component reports `_2` (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
@@ -150,16 +148,15 @@ order.
## 6. Next steps
1. Merge the two `drawComponent` copies in `diylc-core` and add `GerberLayer.flip()` with tests.
2. Add `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
1. Add `IBoardSideComponent` and swap the Gerber layer in the dispatch.
2. Add the field to the library components, putting it on shared bases where they exist. Settle
the silkscreen-text question while doing so.
4. Wrap bottom-side painting in `startDrawingSide(getBoardSide())`.
5. Add the Flip to Other Side action.
6. Split the silkscreen print-out by side.
7. Extract the mirror geometry helper, with tests, and use it in `drawMirroredComponent`.
8. Mirror stage 1: selecting through the mirror. It does not depend on steps 1-6 and can ship
3. Wrap bottom-side painting in `startDrawingSide(getBoardSide())`.
4. Add the Flip to Other Side action.
5. Split the silkscreen print-out by side.
6. Extract the mirror geometry helper, with tests, and use it in `drawMirroredComponent`.
7. Mirror stage 1: selecting through the mirror. It does not depend on steps 1-5 and can ship
early.
9. Mirror stage 2: dragging through the mirror.
10. Mirror stage 3: placing in the mirror, after Flip to Other Side exists.
11. Run the regression suite.
8. Mirror stage 2: dragging through the mirror.
9. Mirror stage 3: placing in the mirror, after Flip to Other Side exists.
10. Run the regression suite.