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
@@ -145,8 +145,12 @@ public class GerberExporter {
LOG.info("Completed export to gerber");
}
private static void drawComponent(Project currentProject, GerberG2DWrapper g2d,
IDIYComponent<?> c, boolean outlineMode) {
/**
* Draws the specified component into the Gerber layers it belongs to. This is the only place that
* decides which layer a component's output goes to.
*/
static void drawComponent(Project currentProject, GerberG2DWrapper g2d,
IDIYComponent<?> c, boolean outlineMode) {
IGerberComponent gerberComponent = (IGerberComponent) c;
g2d.startedDrawingComponent();
if (gerberComponent instanceof IGerberComponentCustom) {
@@ -54,6 +54,29 @@ public enum GerberLayer {
public boolean isMirrored() {
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) {
return fileNameBase + (fileNameBase.endsWith(".") ? "" : ".")
@@ -25,29 +25,10 @@ import com.bancika.gerberwriter.Point;
import com.bancika.gerberwriter.path.MoveTo;
import com.bancika.gerberwriter.path.Path;
import org.diylc.core.ComponentState;
import org.diylc.core.IDIYComponent;
import org.diylc.core.Project;
import org.diylc.core.measures.SizeUnit;
public class GerberPathRenderer {
public static void drawComponent(Project currentProject, GerberG2DWrapper g2d, IDIYComponent<?> c,
boolean outlineMode) {
IGerberComponent gerberComponent = (IGerberComponent) c;
g2d.startedDrawingComponent();
if (gerberComponent instanceof IGerberComponentCustom) {
IGerberComponentCustom gerberComponentCustom = (IGerberComponentCustom) gerberComponent;
gerberComponentCustom.draw(g2d, ComponentState.NORMAL, outlineMode, currentProject, g2d, g2d);
} else if (gerberComponent instanceof IGerberComponentSimple) {
IGerberComponentSimple gerberComponentSimple = (IGerberComponentSimple) gerberComponent;
g2d.startGerberOutput(gerberComponentSimple.getGerberLayer(),
gerberComponentSimple.getGerberFunction(), gerberComponentSimple.isGerberNegative());
c.draw(g2d, ComponentState.NORMAL, outlineMode, currentProject, g2d);
g2d.stopGerberOutput();
}
}
public static void outputPathArea(PathIterator pathIterator, DataLayer dataLayer, double d,
boolean isNegative, String function) {
List<PathWithNegative> outputPath =
@@ -0,0 +1,88 @@
/*
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 java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.util.EnumSet;
import org.diylc.core.ComponentState;
import org.diylc.core.IDrawingObserver;
import org.diylc.core.Project;
import org.diylc.testcomponents.MockDIYComponent;
import org.junit.Test;
public class GerberExporterTest {
@Test
public void testSimpleComponentGoesToSilkscreenTopByDefault() {
GerberG2DWrapper g2d = draw(new SimpleGerberComponent(null));
assertEquals(EnumSet.of(GerberLayer.SilkscreenTop), EnumSet.copyOf(g2d.getLayerMap().keySet()));
}
@Test
public void testSimpleComponentGoesToItsOwnLayer() {
GerberG2DWrapper g2d = draw(new SimpleGerberComponent(GerberLayer.CopperBot));
assertEquals(EnumSet.of(GerberLayer.CopperBot), EnumSet.copyOf(g2d.getLayerMap().keySet()));
}
private static GerberG2DWrapper draw(SimpleGerberComponent component) {
BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB);
Graphics2D graphics = img.createGraphics();
GerberG2DWrapper g2d =
new GerberG2DWrapper(graphics, "test", new Rectangle2D.Double(0, 0, 100, 100));
GerberExporter.drawComponent(new Project(), g2d, component, false);
graphics.dispose();
return g2d;
}
public static class SimpleGerberComponent extends MockDIYComponent
implements IGerberComponentSimple {
private static final long serialVersionUID = 1L;
// null uses the interface default
private GerberLayer layer;
public SimpleGerberComponent(GerberLayer layer) {
this.layer = layer;
}
@Override
public GerberLayer getGerberLayer() {
return layer == null ? IGerberComponentSimple.super.getGerberLayer() : layer;
}
@Override
public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode,
Project project, IDrawingObserver drawingObserver) {
g2d.setColor(Color.black);
g2d.fill(new Rectangle2D.Double(10, 10, 20, 20));
}
}
}
@@ -0,0 +1,53 @@
/*
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());
}
}
}