The silkscreen print-out has a separate page for parts placed on the bottom of the board

This commit is contained in:
Bane Stojkovic
2026-11-11 15:37:27 +01:00
parent 246763419c
commit 122a91a6f0
6 changed files with 173 additions and 9 deletions
@@ -1,6 +1,8 @@
package org.diylc.filter;
import org.diylc.common.PCBLayer;
import org.diylc.core.IBoard;
import org.diylc.core.IBoardSideComponent;
import org.diylc.core.IDIYComponent;
import org.diylc.core.gerber.GerberRenderMode;
import org.diylc.core.gerber.IGerberComponent;
@@ -9,6 +11,20 @@ import java.util.Set;
public class SilkScreenFilter implements IComponentFilter {
private PCBLayer side;
public SilkScreenFilter() {
this(null);
}
/**
* @param side the side of the board whose silkscreen to keep, or null for both; board outlines
* are kept on either side
*/
public SilkScreenFilter(PCBLayer side) {
this.side = side;
}
@Override
public boolean testComponent(IDIYComponent<?> component) {
if (component instanceof IBoard board) {
@@ -17,7 +33,16 @@ public class SilkScreenFilter implements IComponentFilter {
if (!(component instanceof IGerberComponent gerberComponent)) {
return false;
}
if (side != null && getSide(component) != side) {
return false;
}
Set<GerberRenderMode> gerberRenderModes = gerberComponent.getGerberRenderModes();
return gerberRenderModes.contains(GerberRenderMode.Outline);
}
// components that do not say which side they are on are on top
private static PCBLayer getSide(IDIYComponent<?> component) {
return component instanceof IBoardSideComponent sided && sided.getBoardSide() == PCBLayer._1
? PCBLayer._1 : PCBLayer._2;
}
}
@@ -635,7 +635,7 @@
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
<description>Resistors, capacitors, diodes, ICs, transistors, switches, jacks, tube sockets and other PCB parts have a new 'Board Side' property to place them on the bottom of the board; bottom parts are drawn faded on the canvas and solid in the board's Underside Display, and Gerber export puts their silkscreen on the bottom layer with the text mirrored</description>
<description>Resistors, capacitors, diodes, ICs, transistors, switches, jacks, tube sockets and other PCB parts have a new 'Board Side' property to place them on the bottom of the board; bottom parts are drawn faded on the canvas and solid in the board's Underside Display, and Gerber export puts their silkscreen on the bottom layer with the text mirrored, and the silkscreen print-out has a separate page for the bottom</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>NEW_FEATURE</changeType>
@@ -0,0 +1,76 @@
/*
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.filter;
import static org.junit.Assert.assertTrue;
import org.diylc.common.PCBLayer;
import org.diylc.core.gerber.GerberExporterTest.SidedGerberComponent;
import org.diylc.core.gerber.GerberExporterTest.SimpleGerberComponent;
import org.diylc.presenter.DrawingManagerTest.MockBoard;
import org.junit.Test;
public class SilkScreenFilterTest {
@Test
public void testPartsGoToTheirOwnSide() {
SidedGerberComponent top = new SidedGerberComponent(PCBLayer._2);
SidedGerberComponent bottom = new SidedGerberComponent(PCBLayer._1);
assertTrue(new SilkScreenFilter(PCBLayer._2).testComponent(top));
assertTrue(!new SilkScreenFilter(PCBLayer._2).testComponent(bottom));
assertTrue(new SilkScreenFilter(PCBLayer._1).testComponent(bottom));
assertTrue(!new SilkScreenFilter(PCBLayer._1).testComponent(top));
}
@Test
public void testPartsWithoutSideAreOnTop() {
SimpleGerberComponent part = new SimpleGerberComponent(null);
assertTrue(new SilkScreenFilter(PCBLayer._2).testComponent(part));
assertTrue(!new SilkScreenFilter(PCBLayer._1).testComponent(part));
}
@Test
public void testBoardOutlineOnBothSides() {
MockBoard board = new ExportedBoard();
assertTrue(new SilkScreenFilter(PCBLayer._2).testComponent(board));
assertTrue(new SilkScreenFilter(PCBLayer._1).testComponent(board));
}
@Test
public void testNoSideKeepsEverything() {
assertTrue(new SilkScreenFilter().testComponent(new SidedGerberComponent(PCBLayer._1)));
assertTrue(new SilkScreenFilter().testComponent(new SidedGerberComponent(PCBLayer._2)));
}
public static class ExportedBoard extends MockBoard {
private static final long serialVersionUID = 1L;
@Override
public boolean shouldExportToGerber() {
return true;
}
}
}
@@ -23,6 +23,9 @@ package org.diylc.swing.plugins.file;
import org.diylc.common.DrawOption;
import org.diylc.common.IPlugInPort;
import org.diylc.common.PCBLayer;
import org.diylc.core.IBoardSideComponent;
import org.diylc.core.IDIYComponent;
import org.diylc.filter.PCBLayerFilter;
import org.diylc.filter.SilkScreenFilter;
import org.diylc.swingframework.IDrawingProvider;
@@ -53,11 +56,17 @@ public class SilkScreenDrawingProvider implements IDrawingProvider {
@Override
public void draw(int page, Graphics g, double zoomFactor) {
plugInPort.draw((Graphics2D) g, EnumSet.of(DrawOption.ANTIALIASING, DrawOption.OUTLINE_MODE, DrawOption.MONOCHROMATIC),
new SilkScreenFilter(), zoomFactor, null, null);
new SilkScreenFilter(page == 0 ? PCBLayer._2 : PCBLayer._1), zoomFactor, null, null);
}
// the top side, followed by the bottom side when a part is mounted there
@Override
public int getPageCount() {
for (IDIYComponent<?> c : plugInPort.getCurrentProject().getComponents()) {
if (c instanceof IBoardSideComponent && ((IBoardSideComponent) c).getBoardSide() == PCBLayer._1) {
return 2;
}
}
return 1;
}
}
@@ -0,0 +1,50 @@
/*
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.swing.plugins.file;
import static org.junit.Assert.assertEquals;
import java.awt.Point;
import org.diylc.TestBase;
import org.diylc.common.PCBLayer;
import org.diylc.components.passive.Resistor;
import org.junit.Test;
public class SilkScreenDrawingProviderTest extends TestBase {
@Test
public void testOnePageWithoutBottomParts() {
instantiateTwoClick("Passive", "Resistor", new Point(1000, 1000), new Point(1200, 1000));
assertEquals(1, new SilkScreenDrawingProvider(presenter).getPageCount());
}
@Test
public void testBottomPageForBottomParts() {
instantiateTwoClick("Passive", "Resistor", new Point(1000, 1000), new Point(1200, 1000));
Resistor resistor = (Resistor) presenter.getCurrentProject().getComponents().get(0);
resistor.setBoardSide(PCBLayer._1);
assertEquals(2, new SilkScreenDrawingProvider(presenter).getPageCount());
}
}
+11 -7
View File
@@ -6,7 +6,8 @@ Gerber layers and the canvas shows it as a part on the far side of the board.
Status: 45 parts have a Board Side property, and Gerber export sends a bottom-side part's
silkscreen to the bottom layer with its text mirrored. Parts can be selected, dragged and stretched
through the Underside Display mirror. The canvas draws a bottom-side part faded in the main view and
solid in the mirror. The next steps below remain.
solid in the mirror. The silkscreen print-out has a separate page for the bottom. The next steps
below remain.
## 1. Current state
@@ -83,9 +84,11 @@ solid in the mirror. The next steps below remain.
### diylc-swing
- **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`.
- **PCB print-outs (done)**: `SilkScreenDrawingProvider` prints the top side, and a second page for
the bottom when a part is mounted there, so a project without bottom parts still prints one page.
`SilkScreenFilter` takes the side: a part with a Board Side goes on its own side's page, parts
without one count as top, and board outlines go on both pages so that they line up. Neither
page is mirrored or captioned, the same as the trace-mask pages.
### Editing through the Underside Display mirror
@@ -137,6 +140,8 @@ order.
- `GerberLayerTest` covers `flip()`. `GerberExporterTest` covers the dispatch for simple components,
including a stub `IBoardSideComponent` on top, bottom and `null`.
- `SilkScreenFilterTest` covers which side's page each part and board outline goes on, and
`SilkScreenDrawingProviderTest` checks that the bottom page appears only for a bottom part.
- `DrawingManagerTest` checks rendered pixels for a part that reports its own side: a bottom part
is faded only with `FADE_FAR_SIDE`, also when drawn from the image cache; it is solid in the
mirror, where a top part is faded; and a locked bottom part is faded once, not twice.
@@ -156,6 +161,5 @@ order.
## 6. Next steps
1. Split the silkscreen print-out by side.
2. Mirror stage 3: placing in the mirror.
3. Run the regression suite.
1. Mirror stage 3: placing in the mirror.
2. Run the regression suite.