New solder pads have a plated through hole by default, while pads saved without a hole type stay non-plated; Gerber marks pads with a hole as component pads and pads without one as SMD pads

This commit is contained in:
Bane Stojkovic
2026-11-12 11:27:07 +01:00
parent 2f888a0090
commit be87c08e7e
6 changed files with 100 additions and 6 deletions
@@ -75,7 +75,7 @@ public class SolderPad extends AbstractTransparentComponent<Void>
private Type type = Type.ROUND;
private Size holeSize = HOLE_SIZE;
private PCBLayer layer = PCBLayer._1;
private HoleType holeType = HoleType.NTPH;
private HoleType holeType = HoleType.PTH;
private String nodeName = "";
@Override
@@ -97,8 +97,7 @@ public class SolderPad extends AbstractTransparentComponent<Void>
: new GerberLayer[] {getLayer().toGerberCopperLayer()};
if (gerberDrawingObserver != null) {
for (GerberLayer gerberCopperLayer : gerberCopperLayers) {
gerberDrawingObserver.startGerberOutput(gerberCopperLayer, GerberFunctions.CONNECTOR_PAD,
false);
gerberDrawingObserver.startGerberOutput(gerberCopperLayer, getGerberPadFunction(), false);
gerberDrawingObserver.startGerberOutput(getMaskLayer(gerberCopperLayer), "Material", false);
}
}
@@ -178,6 +177,7 @@ public class SolderPad extends AbstractTransparentComponent<Void>
@EditableProperty(name = "Hole Type")
public HoleType getHoleType() {
// pads saved before the hole type existed had non-plated holes; new pads are plated
if (holeType == null) {
holeType = HoleType.NTPH;
}
@@ -226,6 +226,12 @@ public class SolderPad extends AbstractTransparentComponent<Void>
return HoleType.PTH.equals(getHoleType());
}
// a pad with a hole takes a part's lead; one without is a surface mount pad
private String getGerberPadFunction() {
return HoleType.NONE.equals(getHoleType()) ? GerberFunctions.SMDPAD_CU_DEF
: GerberFunctions.COMPONENT_PAD;
}
// the mask is opened on the side the copper is on
private static GerberLayer getMaskLayer(GerberLayer gerberCopperLayer) {
return gerberCopperLayer == GerberLayer.CopperTop ? GerberLayer.SolderMaskTop
@@ -97,6 +97,23 @@ public class SolderPadGerberTest {
assertNoCutOut(drawGerber(new Via()));
}
@Test
public void testPadWithHoleIsComponentPad() {
SolderPad pad = new SolderPad();
pad.setHoleType(SolderPad.HoleType.NTPH);
String gerber = drawGerber(pad).getLayerMap().get(GerberLayer.CopperBot).dumpGerberToString();
assertTrue(gerber.contains("TA.AperFunction,ComponentPad*"));
assertFalse(gerber.contains("ConnectorPad"));
}
@Test
public void testPadWithoutHoleIsSmdPad() {
SolderPad pad = new SolderPad();
pad.setHoleType(SolderPad.HoleType.NONE);
String gerber = drawGerber(pad).getLayerMap().get(GerberLayer.CopperBot).dumpGerberToString();
assertTrue(gerber.contains("TA.AperFunction,SMDPad,CuDef*"));
}
@Test
public void testViaIsDrilledOnceAsViaDrill() {
String excellon = drawGerber(new Via()).getDrillLayerMap().get(GerberLayer.DrillPlated)
@@ -0,0 +1,65 @@
/*
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.serialization;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
import org.diylc.components.connectivity.SolderPad;
import org.junit.Assert;
import org.junit.Test;
/**
* New solder pads are plated, but pads saved before the hole type existed had non-plated holes
* and must keep them, so that old projects export and net as before.
*/
public class SolderPadCompatibilityTest {
private static XStream createXStream() {
XStream xStream = new XStream(new DomDriver("UTF-8"));
ProjectFileManager.configure(xStream);
return xStream;
}
@Test
public void newPadIsPlated() {
Assert.assertEquals(SolderPad.HoleType.PTH, new SolderPad().getHoleType());
}
@Test
public void padSavedWithoutHoleTypeLoadsNonPlated() {
String xml = createXStream().toXML(new SolderPad());
Assert.assertTrue(xml.contains("<holeType>PTH</holeType>"));
SolderPad loaded =
(SolderPad) createXStream().fromXML(xml.replaceAll("\\s*<holeType>PTH</holeType>", ""));
Assert.assertEquals(SolderPad.HoleType.NTPH, loaded.getHoleType());
}
@Test
public void savedNonPlatedPadStaysNonPlated() {
SolderPad pad = new SolderPad();
pad.setHoleType(SolderPad.HoleType.NTPH);
SolderPad loaded = (SolderPad) createXStream().fromXML(createXStream().toXML(pad));
Assert.assertEquals(SolderPad.HoleType.NTPH, loaded.getHoleType());
}
}