mirror of
https://github.com/bancika/diy-layout-creator
synced 2026-11-13 02:32:09 +10:00
A part's Gerber silkscreen layer follows its Board Side in IGerberComponentSimple's default, so the exporter no longer swaps layers after the fact and GerberLayer.flip() is gone
This commit is contained in:
@@ -160,15 +160,10 @@ public class GerberExporter {
|
|||||||
gerberComponentCustom.draw(g2d, ComponentState.NORMAL, outlineMode, currentProject, g2d, g2d);
|
gerberComponentCustom.draw(g2d, ComponentState.NORMAL, outlineMode, currentProject, g2d, g2d);
|
||||||
} else if (gerberComponent instanceof IGerberComponentSimple) {
|
} else if (gerberComponent instanceof IGerberComponentSimple) {
|
||||||
IGerberComponentSimple gerberComponentSimple = (IGerberComponentSimple) gerberComponent;
|
IGerberComponentSimple gerberComponentSimple = (IGerberComponentSimple) gerberComponent;
|
||||||
GerberLayer layer = gerberComponentSimple.getGerberLayer();
|
g2d.startGerberOutput(gerberComponentSimple.getGerberLayer(),
|
||||||
// components emit their top-side layer; a part mounted underneath goes to the bottom one
|
gerberComponentSimple.getGerberFunction(), gerberComponentSimple.isGerberNegative());
|
||||||
boolean bottomSide = IBoardSideComponent.sideOf(c) == PCBLayer._1;
|
// text on the bottom silkscreen is read from below, so it is written mirrored
|
||||||
if (bottomSide) {
|
g2d.setMirrorText(IBoardSideComponent.sideOf(c) == PCBLayer._1);
|
||||||
layer = layer.flip();
|
|
||||||
}
|
|
||||||
g2d.startGerberOutput(layer, gerberComponentSimple.getGerberFunction(),
|
|
||||||
gerberComponentSimple.isGerberNegative());
|
|
||||||
g2d.setMirrorText(bottomSide);
|
|
||||||
c.draw(g2d, ComponentState.NORMAL, outlineMode, currentProject, g2d);
|
c.draw(g2d, ComponentState.NORMAL, outlineMode, currentProject, g2d);
|
||||||
g2d.setMirrorText(false);
|
g2d.setMirrorText(false);
|
||||||
g2d.stopGerberOutput();
|
g2d.stopGerberOutput();
|
||||||
|
|||||||
@@ -55,29 +55,6 @@ public enum GerberLayer {
|
|||||||
return mirrored;
|
return mirrored;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the same layer on the other side of the board; the outline and the drill layers go
|
|
||||||
* through the whole board and are returned unchanged
|
|
||||||
*/
|
|
||||||
public GerberLayer flip() {
|
|
||||||
switch (this) {
|
|
||||||
case CopperBot:
|
|
||||||
return CopperTop;
|
|
||||||
case CopperTop:
|
|
||||||
return CopperBot;
|
|
||||||
case SilkscreenBot:
|
|
||||||
return SilkscreenTop;
|
|
||||||
case SilkscreenTop:
|
|
||||||
return SilkscreenBot;
|
|
||||||
case SolderMaskBot:
|
|
||||||
return SolderMaskTop;
|
|
||||||
case SolderMaskTop:
|
|
||||||
return SolderMaskBot;
|
|
||||||
default:
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String formatFileName(String fileNameBase, String boardName) {
|
public String formatFileName(String fileNameBase, String boardName) {
|
||||||
return fileNameBase + (fileNameBase.endsWith(".") ? "" : ".")
|
return fileNameBase + (fileNameBase.endsWith(".") ? "" : ".")
|
||||||
+ boardName + "-" + getSuffix() + "." + getExtension();
|
+ boardName + "-" + getSuffix() + "." + getExtension();
|
||||||
|
|||||||
@@ -1,9 +1,18 @@
|
|||||||
package org.diylc.core.gerber;
|
package org.diylc.core.gerber;
|
||||||
|
|
||||||
|
import org.diylc.common.PCBLayer;
|
||||||
|
import org.diylc.core.IBoardSideComponent;
|
||||||
|
import org.diylc.core.IDIYComponent;
|
||||||
|
|
||||||
public interface IGerberComponentSimple extends IGerberComponent {
|
public interface IGerberComponentSimple extends IGerberComponent {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the silkscreen on the side of the board the component is mounted on
|
||||||
|
*/
|
||||||
default GerberLayer getGerberLayer() {
|
default GerberLayer getGerberLayer() {
|
||||||
return GerberLayer.SilkscreenTop;
|
return this instanceof IDIYComponent<?> component
|
||||||
|
&& IBoardSideComponent.sideOf(component) == PCBLayer._1
|
||||||
|
? GerberLayer.SilkscreenBot : GerberLayer.SilkscreenTop;
|
||||||
}
|
}
|
||||||
|
|
||||||
default String getGerberFunction() {
|
default String getGerberFunction() {
|
||||||
|
|||||||
@@ -1,53 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
DIY Layout Creator (DIYLC).
|
|
||||||
Copyright (c) 2009-2025 held jointly by the individual authors.
|
|
||||||
|
|
||||||
This file is part of DIYLC.
|
|
||||||
|
|
||||||
DIYLC is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
DIYLC is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
*/
|
|
||||||
package org.diylc.core.gerber;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
public class GerberLayerTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testFlipSwapsSides() {
|
|
||||||
assertEquals(GerberLayer.CopperBot, GerberLayer.CopperTop.flip());
|
|
||||||
assertEquals(GerberLayer.CopperTop, GerberLayer.CopperBot.flip());
|
|
||||||
assertEquals(GerberLayer.SilkscreenBot, GerberLayer.SilkscreenTop.flip());
|
|
||||||
assertEquals(GerberLayer.SilkscreenTop, GerberLayer.SilkscreenBot.flip());
|
|
||||||
assertEquals(GerberLayer.SolderMaskBot, GerberLayer.SolderMaskTop.flip());
|
|
||||||
assertEquals(GerberLayer.SolderMaskTop, GerberLayer.SolderMaskBot.flip());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testFlipKeepsLayersThroughTheBoard() {
|
|
||||||
assertEquals(GerberLayer.Outline, GerberLayer.Outline.flip());
|
|
||||||
assertEquals(GerberLayer.DrillPlated, GerberLayer.DrillPlated.flip());
|
|
||||||
assertEquals(GerberLayer.DrillNonPlated, GerberLayer.DrillNonPlated.flip());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testFlipTwiceReturnsLayer() {
|
|
||||||
for (GerberLayer layer : GerberLayer.values()) {
|
|
||||||
assertEquals(layer, layer.flip().flip());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -29,9 +29,10 @@ projects; the open questions below remain.
|
|||||||
use it. Copper that sits on a layer - `CopperTrace`, `CurvedTrace`, `GroundFill`, `SolderPad` and
|
use it. Copper that sits on a layer - `CopperTrace`, `CurvedTrace`, `GroundFill`, `SolderPad` and
|
||||||
`PCBText` - implements `IPCBLayerComponent` (`getLayer`/`setLayer`), which `PCBLayerFilter`, the
|
`PCBText` - implements `IPCBLayerComponent` (`getLayer`/`setLayer`), which `PCBLayerFilter`, the
|
||||||
trace-mask print-out and `MirrorPlacement` use instead of looking the methods up by name.
|
trace-mask print-out and `MirrorPlacement` use instead of looking the methods up by name.
|
||||||
- One method decides the layer for a simple component: `GerberExporter.drawComponent`. It is
|
- A simple component's Gerber layer follows its side: the default
|
||||||
package-private so that `GerberExporterTest` can call it. It uses `getGerberLayer()`, flipped
|
`IGerberComponentSimple.getGerberLayer()` returns `SilkscreenBot` for an `IBoardSideComponent` on
|
||||||
with `GerberLayer.flip()` when the component is an `IBoardSideComponent` on `_1` (Bottom).
|
`_1` (Bottom) and `SilkscreenTop` otherwise. `GerberExporter.drawComponent`, package-private so
|
||||||
|
that `GerberExporterTest` can call it, mirrors the text of bottom-side parts.
|
||||||
- **`org.diylc.core.IBoardSideComponent`**: `PCBLayer getBoardSide()`, returning `_1` or `_2`
|
- **`org.diylc.core.IBoardSideComponent`**: `PCBLayer getBoardSide()`, returning `_1` or `_2`
|
||||||
only. It is deliberately separate from `ILayeredComponent` (see Decisions). Core knows a part's
|
only. It is deliberately separate from `ILayeredComponent` (see Decisions). Core knows a part's
|
||||||
side only through this interface.
|
side only through this interface.
|
||||||
@@ -47,7 +48,7 @@ projects; the open questions below remain.
|
|||||||
| 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 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. |
|
| 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. |
|
||||||
| The side is a plain property, and the app never mirrors a part for the user | Seen from above, a part under the board is a mirror image, so a part with an asymmetric footprint moved to the bottom also needs Mirror Horizontally. Doing that for the user was tried as a Flip to Other Side action and dropped as menu clutter; the user sets the side and mirrors the part where needed. |
|
| The side is a plain property, and the app never mirrors a part for the user | Seen from above, a part under the board is a mirror image, so a part with an asymmetric footprint moved to the bottom also needs Mirror Horizontally. Doing that for the user was tried as a Flip to Other Side action and dropped as menu clutter; the user sets the side and mirrors the part where needed. |
|
||||||
| The Gerber layer is swapped centrally, not inside each component | Components keep emitting their `*Top` layer. One place maps it to the other side. |
|
| The Gerber layer follows the side in the `IGerberComponentSimple` default, not in each component or in the exporter | Side and layer are decided in one place on the part, with no adjustment afterwards. None of the sided parts overrides `getGerberLayer()`. |
|
||||||
| Default `_2` (Top), with `null` meaning Top | Fields added to existing classes deserialize as `null` in old files. |
|
| Default `_2` (Top), with `null` meaning Top | Fields added to existing classes deserialize as `null` in old files. |
|
||||||
| Bottom-side text is mirrored centrally in the Gerber output | Parts draw their labels readable from the top, which is backwards on the bottom silkscreen once the board is turned over. While `GerberExporter.drawComponent` draws a bottom-side part, `GerberG2DWrapper.setMirrorText` mirrors each string about the centre of its layout box, so no component changes and the canvas keeps readable labels. |
|
| Bottom-side text is mirrored centrally in the Gerber output | Parts draw their labels readable from the top, which is backwards on the bottom silkscreen once the board is turned over. While `GerberExporter.drawComponent` draws a bottom-side part, `GerberG2DWrapper.setMirrorText` mirrors each string about the centre of its layout box, so no component changes and the canvas keeps readable labels. |
|
||||||
| A part without a side placed in the mirror is placed unchanged | A jumper, eyelet or wire has no side to set, so it is placed at the reflected point like any other part rather than refused. |
|
| A part without a side placed in the mirror is placed unchanged | A jumper, eyelet or wire has no side to set, so it is placed at the reflected point like any other part rather than refused. |
|
||||||
@@ -154,7 +155,7 @@ order.
|
|||||||
|
|
||||||
## 5. Verification
|
## 5. Verification
|
||||||
|
|
||||||
- `GerberLayerTest` covers `flip()`. `GerberExporterTest` covers the dispatch for simple components,
|
- `GerberExporterTest` covers the layer and text direction of simple components,
|
||||||
including a stub `IBoardSideComponent` on top, bottom and `null`.
|
including a stub `IBoardSideComponent` on top, bottom and `null`.
|
||||||
- `MirrorPlacementTest` covers what placing through the mirror changes, and
|
- `MirrorPlacementTest` covers what placing through the mirror changes, and
|
||||||
`PresenterTests.testPlaceThroughUndersideMirror` and `testPlaceUnsidedPartThroughUndersideMirror`
|
`PresenterTests.testPlaceThroughUndersideMirror` and `testPlaceUnsidedPartThroughUndersideMirror`
|
||||||
|
|||||||
Reference in New Issue
Block a user