diff --git a/diylc/diylc-core/src/main/java/org/diylc/core/gerber/GerberExporter.java b/diylc/diylc-core/src/main/java/org/diylc/core/gerber/GerberExporter.java index decba7804..a7315c8da 100644 --- a/diylc/diylc-core/src/main/java/org/diylc/core/gerber/GerberExporter.java +++ b/diylc/diylc-core/src/main/java/org/diylc/core/gerber/GerberExporter.java @@ -162,13 +162,16 @@ public class GerberExporter { IGerberComponentSimple gerberComponentSimple = (IGerberComponentSimple) gerberComponent; GerberLayer layer = gerberComponentSimple.getGerberLayer(); // components emit their top-side layer; a part mounted underneath goes to the bottom one - if (c instanceof IBoardSideComponent - && ((IBoardSideComponent) c).getBoardSide() == PCBLayer._1) { + boolean bottomSide = c instanceof IBoardSideComponent + && ((IBoardSideComponent) c).getBoardSide() == PCBLayer._1; + if (bottomSide) { layer = layer.flip(); } g2d.startGerberOutput(layer, gerberComponentSimple.getGerberFunction(), gerberComponentSimple.isGerberNegative()); + g2d.setMirrorText(bottomSide); c.draw(g2d, ComponentState.NORMAL, outlineMode, currentProject, g2d); + g2d.setMirrorText(false); g2d.stopGerberOutput(); } } diff --git a/diylc/diylc-core/src/main/java/org/diylc/core/gerber/GerberG2DWrapper.java b/diylc/diylc-core/src/main/java/org/diylc/core/gerber/GerberG2DWrapper.java index 0479da65e..2ad489d79 100644 --- a/diylc/diylc-core/src/main/java/org/diylc/core/gerber/GerberG2DWrapper.java +++ b/diylc/diylc-core/src/main/java/org/diylc/core/gerber/GerberG2DWrapper.java @@ -58,6 +58,7 @@ public class GerberG2DWrapper extends Graphics2D private double tolerance; private Rectangle2D boardRect; + private boolean mirrorText = false; public GerberG2DWrapper(Graphics2D graphics2d, String diylcVersion, Rectangle2D boardRect) { super(); @@ -154,6 +155,15 @@ public class GerberG2DWrapper extends Graphics2D return layerMap; } + /** + * Sets whether text is written mirrored, each string about its own centre. The output is in top + * view, so text on the bottom side has to be mirrored to read correctly once the board is turned + * over. + */ + public void setMirrorText(boolean mirrorText) { + this.mirrorText = mirrorText; + } + @Override public void draw(Shape s) { if (trackingGerber && !getColor().equals(Constants.TRANSPARENT_COLOR)) { @@ -181,6 +191,10 @@ public class GerberG2DWrapper extends Graphics2D GlyphVector gv = font.createGlyphVector(graphics2d.getFontRenderContext(), str); AffineTransform translateInstance = AffineTransform.getTranslateInstance(x, y); + if (mirrorText) { + translateInstance.translate(gv.getLogicalBounds().getWidth(), 0); + translateInstance.scale(-1, 1); + } for (int i = 0; i < gv.getNumGlyphs(); i++) { Shape glyphShape = gv.getGlyphOutline(i); diff --git a/diylc/diylc-core/src/test/java/org/diylc/core/gerber/GerberExporterTest.java b/diylc/diylc-core/src/test/java/org/diylc/core/gerber/GerberExporterTest.java index 71afb77d6..f4148055b 100644 --- a/diylc/diylc-core/src/test/java/org/diylc/core/gerber/GerberExporterTest.java +++ b/diylc/diylc-core/src/test/java/org/diylc/core/gerber/GerberExporterTest.java @@ -22,12 +22,17 @@ package org.diylc.core.gerber; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import java.awt.Color; +import java.awt.Font; import java.awt.Graphics2D; +import java.awt.Shape; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; +import java.util.ArrayList; import java.util.EnumSet; +import java.util.List; import org.diylc.common.PCBLayer; import org.diylc.core.ComponentState; @@ -74,6 +79,49 @@ public class GerberExporterTest { assertEquals(EnumSet.of(GerberLayer.SilkscreenTop), EnumSet.copyOf(g2d.getLayerMap().keySet())); } + @Test + public void testTopSideTextReadsLeftToRight() { + List glyphs = drawText(PCBLayer._2); + + assertTrue(glyphs.get(0).getMinX() < glyphs.get(1).getMinX()); + } + + @Test + public void testBottomSideTextIsMirrored() { + List top = drawText(PCBLayer._2); + List bottom = drawText(PCBLayer._1); + + // seen from the top, the first letter of a bottom-side label is on the right + assertTrue(bottom.get(0).getMinX() > bottom.get(1).getMinX()); + // mirrored about the centre of its layout box, so the text stays where it was; the glyphs' + // uneven side bearings move their drawn bounds by a pixel or so + assertEquals(union(top).getCenterX(), union(bottom).getCenterX(), 2); + } + + private static List drawText(PCBLayer side) { + List glyphs = new ArrayList(); + 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)) { + @Override + public void fillShape(Shape s) { + glyphs.add(s.getBounds2D()); + } + }; + GerberExporter.drawComponent(new Project(), g2d, new TextGerberComponent(side), false); + graphics.dispose(); + return glyphs; + } + + private static Rectangle2D union(List rects) { + Rectangle2D union = (Rectangle2D) rects.get(0).clone(); + for (Rectangle2D rect : rects) { + union.add(rect); + } + return union; + } + private static GerberG2DWrapper draw(SimpleGerberComponent component) { BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB); Graphics2D graphics = img.createGraphics(); @@ -126,4 +174,21 @@ public class GerberExporterTest { return side; } } + + public static class TextGerberComponent extends SidedGerberComponent { + + private static final long serialVersionUID = 1L; + + public TextGerberComponent(PCBLayer side) { + super(side); + } + + @Override + public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode, + Project project, IDrawingObserver drawingObserver) { + g2d.setColor(Color.black); + g2d.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 20)); + g2d.drawString("AB", 10, 50); + } + } } diff --git a/diylc/diylc-library/src/main/java/org/diylc/components/connectivity/PCBTerminalBlock.java b/diylc/diylc-library/src/main/java/org/diylc/components/connectivity/PCBTerminalBlock.java index f7ca250ce..915add0d9 100644 --- a/diylc/diylc-library/src/main/java/org/diylc/components/connectivity/PCBTerminalBlock.java +++ b/diylc/diylc-library/src/main/java/org/diylc/components/connectivity/PCBTerminalBlock.java @@ -32,10 +32,12 @@ import org.diylc.appframework.miscutils.ConfigurationManager; import org.diylc.common.IPlugInPort; import org.diylc.common.ObjectCache; import org.diylc.common.Orientation; +import org.diylc.common.PCBLayer; import org.diylc.components.AbstractTransparentComponent; import org.diylc.components.connectivity.PCBTerminalBlock.PCBTerminalBlockCount; import org.diylc.components.transform.PCBTerminalBlockTransformer; import org.diylc.core.ComponentState; +import org.diylc.core.IBoardSideComponent; import org.diylc.core.IDIYComponent; import org.diylc.core.IDrawingObserver; import org.diylc.core.Project; @@ -52,7 +54,7 @@ import org.diylc.utils.Constants; author = "Branislav Stojkovic", description = "Horizontal PCB terminal block with configurable pitch", zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "TB", enableCache = true, transformer = PCBTerminalBlockTransformer.class) -public class PCBTerminalBlock extends AbstractTransparentComponent implements IGerberComponentSimple { +public class PCBTerminalBlock extends AbstractTransparentComponent implements IGerberComponentSimple, IBoardSideComponent { private static final long serialVersionUID = 1L; @@ -72,6 +74,7 @@ public class PCBTerminalBlock extends AbstractTransparentComponent implements IGerberComponentSimple { +public class CliffJack1_4 extends AbstractMultiPartComponent implements IGerberComponentSimple, IBoardSideComponent { private static final long serialVersionUID = 1L; @@ -84,6 +86,7 @@ public class CliffJack1_4 extends AbstractMultiPartComponent implement @SuppressWarnings("unused") @Deprecated private transient String value = ""; + private PCBLayer boardSide; public CliffJack1_4() { super(); @@ -373,4 +376,14 @@ public class CliffJack1_4 extends AbstractMultiPartComponent implement public boolean canPointMoveFreely(int pointIndex) { return false; } + + @EditableProperty(name = "Board Side") + @Override + public PCBLayer getBoardSide() { + return boardSide == null ? PCBLayer._2 : boardSide; + } + + public void setBoardSide(PCBLayer boardSide) { + this.boardSide = boardSide; + } } diff --git a/diylc/diylc-library/src/main/java/org/diylc/components/electromechanical/CliffJack1_8.java b/diylc/diylc-library/src/main/java/org/diylc/components/electromechanical/CliffJack1_8.java index 3dcca7501..426d015c2 100644 --- a/diylc/diylc-library/src/main/java/org/diylc/components/electromechanical/CliffJack1_8.java +++ b/diylc/diylc-library/src/main/java/org/diylc/components/electromechanical/CliffJack1_8.java @@ -36,10 +36,12 @@ import org.diylc.common.HorizontalAlignment; import org.diylc.common.IPlugInPort; import org.diylc.common.ObjectCache; import org.diylc.common.Orientation; +import org.diylc.common.PCBLayer; import org.diylc.common.VerticalAlignment; import org.diylc.components.AbstractMultiPartComponent; import org.diylc.components.transform.CliffJackTransformer; import org.diylc.core.ComponentState; +import org.diylc.core.IBoardSideComponent; import org.diylc.core.IDIYComponent; import org.diylc.core.IDrawingObserver; import org.diylc.core.Project; @@ -56,7 +58,7 @@ import org.diylc.utils.Constants; author = "Branislav Stojkovic", description = "Cliff-style closed 1/8\" phono jack", zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "J", transformer = CliffJackTransformer.class, enableCache = true) -public class CliffJack1_8 extends AbstractMultiPartComponent implements IGerberComponentSimple { +public class CliffJack1_8 extends AbstractMultiPartComponent implements IGerberComponentSimple, IBoardSideComponent { private static final long serialVersionUID = 1L; @@ -80,6 +82,7 @@ public class CliffJack1_8 extends AbstractMultiPartComponent implement private Orientation orientation = Orientation.DEFAULT; transient private Shape[] body; private boolean nut = false; + private PCBLayer boardSide; public CliffJack1_8() { super(); @@ -394,4 +397,14 @@ public class CliffJack1_8 extends AbstractMultiPartComponent implement public boolean canPointMoveFreely(int pointIndex) { return false; } + + @EditableProperty(name = "Board Side") + @Override + public PCBLayer getBoardSide() { + return boardSide == null ? PCBLayer._2 : boardSide; + } + + public void setBoardSide(PCBLayer boardSide) { + this.boardSide = boardSide; + } } diff --git a/diylc/diylc-library/src/main/java/org/diylc/components/electromechanical/ClosedJack1_4.java b/diylc/diylc-library/src/main/java/org/diylc/components/electromechanical/ClosedJack1_4.java index 141c1493f..b50719098 100644 --- a/diylc/diylc-library/src/main/java/org/diylc/components/electromechanical/ClosedJack1_4.java +++ b/diylc/diylc-library/src/main/java/org/diylc/components/electromechanical/ClosedJack1_4.java @@ -41,10 +41,12 @@ import org.diylc.common.HorizontalAlignment; import org.diylc.common.IPlugInPort; import org.diylc.common.ObjectCache; import org.diylc.common.Orientation; +import org.diylc.common.PCBLayer; import org.diylc.common.VerticalAlignment; import org.diylc.components.AbstractMultiPartComponent; import org.diylc.components.transform.ClosedJackTransformer; import org.diylc.core.ComponentState; +import org.diylc.core.IBoardSideComponent; import org.diylc.core.IDIYComponent; import org.diylc.core.IDrawingObserver; import org.diylc.core.Project; @@ -62,7 +64,7 @@ import org.diylc.utils.Constants; @ComponentDescriptor(name = "Closed 1/4\" Jack", category = "Electro-Mechanical", author = "Branislav Stojkovic", description = "Enclosed panel mount 1/4\" phono jack", zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "J", transformer = ClosedJackTransformer.class, enableCache = true) -public class ClosedJack1_4 extends AbstractMultiPartComponent implements IGerberComponentSimple { +public class ClosedJack1_4 extends AbstractMultiPartComponent implements IGerberComponentSimple, IBoardSideComponent { private static final long serialVersionUID = 1L; @@ -86,6 +88,7 @@ public class ClosedJack1_4 extends AbstractMultiPartComponent implemen @SuppressWarnings("unused") @Deprecated private transient String value = ""; + private PCBLayer boardSide; public ClosedJack1_4() { super(); @@ -380,4 +383,14 @@ public class ClosedJack1_4 extends AbstractMultiPartComponent implemen public boolean canPointMoveFreely(int pointIndex) { return false; } + + @EditableProperty(name = "Board Side") + @Override + public PCBLayer getBoardSide() { + return boardSide == null ? PCBLayer._2 : boardSide; + } + + public void setBoardSide(PCBLayer boardSide) { + this.boardSide = boardSide; + } } diff --git a/diylc/diylc-library/src/main/java/org/diylc/components/electromechanical/MiniToggleSwitch.java b/diylc/diylc-library/src/main/java/org/diylc/components/electromechanical/MiniToggleSwitch.java index 061c11ec1..313a474f4 100644 --- a/diylc/diylc-library/src/main/java/org/diylc/components/electromechanical/MiniToggleSwitch.java +++ b/diylc/diylc-library/src/main/java/org/diylc/components/electromechanical/MiniToggleSwitch.java @@ -40,6 +40,7 @@ import org.diylc.components.AbstractTransparentComponent; import org.diylc.components.guitar.ToggleSwitchPositionPropertyValueSource; import org.diylc.components.transform.MiniToggleSwitchTransformer; import org.diylc.core.ComponentState; +import org.diylc.core.IBoardSideComponent; import org.diylc.core.IDIYComponent; import org.diylc.core.IDrawingObserver; import org.diylc.core.ILayeredComponent; @@ -62,7 +63,7 @@ import org.diylc.utils.SwitchUtils; @ComponentDescriptor(name = "Mini Toggle Switch", category = "Electro-Mechanical", author = "Branislav Stojkovic", description = "Panel mounted mini toggle switch", zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "SW", enableCache = true, transformer = MiniToggleSwitchTransformer.class) -public class MiniToggleSwitch extends AbstractTransparentComponent implements ISwitch, ILayeredComponent, IGerberComponentSimple { +public class MiniToggleSwitch extends AbstractTransparentComponent implements ISwitch, ILayeredComponent, IGerberComponentSimple, IBoardSideComponent { private static final long serialVersionUID = 1L; @@ -96,6 +97,7 @@ public class MiniToggleSwitch extends AbstractTransparentComponent implements IGerberComponentSimple { +public abstract class AbstractFilmCapacitor extends AbstractLeadedComponent implements IGerberComponentSimple, IBoardSideComponent { private static final long serialVersionUID = 1L; @@ -49,6 +51,7 @@ public abstract class AbstractFilmCapacitor extends AbstractLeadedComponent implements IGerberComponentSimple { +public abstract class AbstractPotentiometer extends AbstractLabeledComponent implements IGerberComponentSimple, IBoardSideComponent { private static final long serialVersionUID = 1L; @@ -40,6 +42,7 @@ public abstract class AbstractPotentiometer extends AbstractLabeledComponent */ @AiNote("The two control points are the holes; the body's own leads are 'Lead Spacing' apart and centred between them, with lead wire drawn out to each hole. Place the points exactly 'Lead Spacing' apart for straight leads") -public abstract class AbstractRadialComponent extends AbstractLeadedComponent implements IGerberComponentSimple { +public abstract class AbstractRadialComponent extends AbstractLeadedComponent implements IGerberComponentSimple, IBoardSideComponent { private static final long serialVersionUID = 1L; @@ -48,6 +50,7 @@ public abstract class AbstractRadialComponent extends AbstractLeadedComponent protected Size pinSpacing = PIN_SPACING; protected String type; + private PCBLayer boardSide; @Override protected int calculatePinSpacing(Rectangle shapeRect) { @@ -74,4 +77,14 @@ public abstract class AbstractRadialComponent extends AbstractLeadedComponent public void setType(String type) { this.type = type; } + + @EditableProperty(name = "Board Side") + @Override + public PCBLayer getBoardSide() { + return boardSide == null ? PCBLayer._2 : boardSide; + } + + public void setBoardSide(PCBLayer boardSide) { + this.boardSide = boardSide; + } } diff --git a/diylc/diylc-library/src/main/java/org/diylc/components/passive/AxialElectrolyticCapacitor.java b/diylc/diylc-library/src/main/java/org/diylc/components/passive/AxialElectrolyticCapacitor.java index d80fcf9f4..4e3546dda 100644 --- a/diylc/diylc-library/src/main/java/org/diylc/components/passive/AxialElectrolyticCapacitor.java +++ b/diylc/diylc-library/src/main/java/org/diylc/components/passive/AxialElectrolyticCapacitor.java @@ -29,10 +29,12 @@ import org.diylc.appframework.miscutils.ConfigurationManager; import org.diylc.common.IPlugInPort; import org.diylc.common.ObjectCache; +import org.diylc.common.PCBLayer; import org.diylc.components.AbstractLeadedComponent; import org.diylc.components.passive.CapacitorDatasheetService.CapacitorDatasheet; import org.diylc.components.transform.SimpleComponentTransformer; import org.diylc.core.CreationMethod; +import org.diylc.core.IBoardSideComponent; import org.diylc.core.IDIYComponent; import org.diylc.core.IDatasheetSupport; import org.diylc.core.Theme; @@ -53,7 +55,7 @@ import org.diylc.utils.Constants; description = "Axial electrolytic capacitor, similar to Sprague Atom, F&T, etc", zOrder = IDIYComponent.COMPONENT, transformer = SimpleComponentTransformer.class, enableCache = true, enableDatasheet = true, datasheetCreationStepCount = 3) -public class AxialElectrolyticCapacitor extends AbstractLeadedComponent implements IDatasheetSupport, IGerberComponentSimple { +public class AxialElectrolyticCapacitor extends AbstractLeadedComponent implements IDatasheetSupport, IGerberComponentSimple, IBoardSideComponent { private static final long serialVersionUID = 1L; @@ -73,6 +75,7 @@ public class AxialElectrolyticCapacitor extends AbstractLeadedComponent implements IGerberComponentSimple { +public class CrystalOscillator extends AbstractLeadedComponent implements IGerberComponentSimple, IBoardSideComponent { private static final long serialVersionUID = 1L; @@ -59,6 +61,7 @@ public class CrystalOscillator extends AbstractLeadedComponent implem private Size pinSpacing = PIN_SPACING; private Frequency value; + private PCBLayer boardSide; public CrystalOscillator() { super(); @@ -133,4 +136,14 @@ public class CrystalOscillator extends AbstractLeadedComponent implem protected Size getDefaultWidth() { return DEFAULT_WIDTH; } + + @EditableProperty(name = "Board Side") + @Override + public PCBLayer getBoardSide() { + return boardSide == null ? PCBLayer._2 : boardSide; + } + + public void setBoardSide(PCBLayer boardSide) { + this.boardSide = boardSide; + } } diff --git a/diylc/diylc-library/src/main/java/org/diylc/components/passive/HeatSinkResistor.java b/diylc/diylc-library/src/main/java/org/diylc/components/passive/HeatSinkResistor.java index a7fbfd06b..b01141614 100644 --- a/diylc/diylc-library/src/main/java/org/diylc/components/passive/HeatSinkResistor.java +++ b/diylc/diylc-library/src/main/java/org/diylc/components/passive/HeatSinkResistor.java @@ -36,9 +36,11 @@ import java.awt.geom.Rectangle2D; import org.diylc.appframework.miscutils.ConfigurationManager; import org.diylc.common.IPlugInPort; import org.diylc.common.ObjectCache; +import org.diylc.common.PCBLayer; import org.diylc.components.AbstractLeadedComponent; import org.diylc.components.transform.SimpleComponentTransformer; import org.diylc.core.CreationMethod; +import org.diylc.core.IBoardSideComponent; import org.diylc.core.IDIYComponent; import org.diylc.core.IDatasheetSupport; import org.diylc.core.annotations.AiNote; @@ -63,7 +65,7 @@ import org.diylc.utils.Constants; zOrder = IDIYComponent.COMPONENT, transformer = SimpleComponentTransformer.class, enableCache = true, enableDatasheet = true, datasheetCreationStepCount = 2, datasheetModelColumn = 1) public class HeatSinkResistor extends AbstractLeadedComponent - implements IGerberComponentSimple, IDatasheetSupport { + implements IGerberComponentSimple, IDatasheetSupport, IBoardSideComponent { private static final long serialVersionUID = 1L; @@ -101,6 +103,7 @@ public class HeatSinkResistor extends AbstractLeadedComponent * Top/bottom lip thickness; main body height = A − 2 × lip = D (HS25: A=28, D=14 → lip 7 mm each). */ private Size lipSize = DEFAULT_LIP_SIZE; + private PCBLayer boardSide; public HeatSinkResistor() { super(); @@ -544,6 +547,16 @@ public class HeatSinkResistor extends AbstractLeadedComponent g2d.draw(a); } + @EditableProperty(name = "Board Side") + @Override + public PCBLayer getBoardSide() { + return boardSide == null ? PCBLayer._2 : boardSide; + } + + public void setBoardSide(PCBLayer boardSide) { + this.boardSide = boardSide; + } + public enum MountingHoleCount { TWO_DIAGONAL("2 diagonal"), FOUR("4 holes"), diff --git a/diylc/diylc-library/src/main/java/org/diylc/components/passive/Resistor.java b/diylc/diylc-library/src/main/java/org/diylc/components/passive/Resistor.java index 2f4641b78..e4e56ec08 100644 --- a/diylc/diylc-library/src/main/java/org/diylc/components/passive/Resistor.java +++ b/diylc/diylc-library/src/main/java/org/diylc/components/passive/Resistor.java @@ -32,10 +32,12 @@ import java.awt.geom.Rectangle2D; import java.awt.geom.RoundRectangle2D; import org.diylc.common.ObjectCache; +import org.diylc.common.PCBLayer; import org.diylc.common.ResistorColorCode; import org.diylc.components.AbstractLeadedComponent; import org.diylc.components.transform.SimpleComponentTransformer; import org.diylc.core.CreationMethod; +import org.diylc.core.IBoardSideComponent; import org.diylc.core.IDIYComponent; import org.diylc.core.IDatasheetSupport; import org.diylc.core.annotations.AiNote; @@ -55,7 +57,7 @@ import org.diylc.core.measures.SizeUnit; zOrder = IDIYComponent.COMPONENT, transformer = SimpleComponentTransformer.class, enableCache = true, enableDatasheet = true, datasheetCreationStepCount = 2) public class Resistor extends AbstractLeadedComponent - implements IGerberComponentSimple, IDatasheetSupport { + implements IGerberComponentSimple, IDatasheetSupport, IBoardSideComponent { private static final long serialVersionUID = 1L; @@ -72,6 +74,7 @@ public class Resistor extends AbstractLeadedComponent private org.diylc.core.measures.Power powerNew = null; private ResistorColorCode colorCode = ResistorColorCode._5_BAND; private ResistorShape shape = ResistorShape.Standard; + private PCBLayer boardSide; public Resistor() { super(); @@ -263,6 +266,16 @@ public class Resistor extends AbstractLeadedComponent return super.getFlipStanding(); } + @EditableProperty(name = "Board Side") + @Override + public PCBLayer getBoardSide() { + return boardSide == null ? PCBLayer._2 : boardSide; + } + + public void setBoardSide(PCBLayer boardSide) { + this.boardSide = boardSide; + } + public enum ResistorShape { Tubular, Tubular_Beveled, Standard; diff --git a/diylc/diylc-library/src/main/java/org/diylc/components/passive/ToroidalInductor.java b/diylc/diylc-library/src/main/java/org/diylc/components/passive/ToroidalInductor.java index 5573401f2..e7092b918 100644 --- a/diylc/diylc-library/src/main/java/org/diylc/components/passive/ToroidalInductor.java +++ b/diylc/diylc-library/src/main/java/org/diylc/components/passive/ToroidalInductor.java @@ -28,9 +28,11 @@ import java.awt.geom.Rectangle2D; import java.awt.geom.RoundRectangle2D; import org.diylc.common.ObjectCache; +import org.diylc.common.PCBLayer; import org.diylc.components.AbstractLeadedComponent; import org.diylc.components.transform.SimpleComponentTransformer; import org.diylc.core.CreationMethod; +import org.diylc.core.IBoardSideComponent; import org.diylc.core.IDIYComponent; import org.diylc.core.annotations.ComponentDescriptor; import org.diylc.core.annotations.EditableProperty; @@ -45,7 +47,7 @@ import org.diylc.core.measures.SizeUnit; creationMethod = CreationMethod.POINT_BY_POINT, instanceNamePrefix = "L", description = "Ferrite core toroidal inductor mounted vertically", zOrder = IDIYComponent.COMPONENT, transformer = SimpleComponentTransformer.class) -public class ToroidalInductor extends AbstractLeadedComponent implements IGerberComponentSimple { +public class ToroidalInductor extends AbstractLeadedComponent implements IGerberComponentSimple, IBoardSideComponent { private static final long serialVersionUID = 1L; @@ -59,6 +61,7 @@ public class ToroidalInductor extends AbstractLeadedComponent implem private Inductance value = null; private Resistance resistance = null; + private PCBLayer boardSide; public ToroidalInductor() { super(); @@ -165,4 +168,14 @@ public class ToroidalInductor extends AbstractLeadedComponent implem public void setResistance(Resistance resistance) { this.resistance = resistance; } + + @EditableProperty(name = "Board Side") + @Override + public PCBLayer getBoardSide() { + return boardSide == null ? PCBLayer._2 : boardSide; + } + + public void setBoardSide(PCBLayer boardSide) { + this.boardSide = boardSide; + } } diff --git a/diylc/diylc-library/src/main/java/org/diylc/components/semiconductors/DIL_IC.java b/diylc/diylc-library/src/main/java/org/diylc/components/semiconductors/DIL_IC.java index c271b4d16..40459e341 100644 --- a/diylc/diylc-library/src/main/java/org/diylc/components/semiconductors/DIL_IC.java +++ b/diylc/diylc-library/src/main/java/org/diylc/components/semiconductors/DIL_IC.java @@ -39,11 +39,13 @@ import org.diylc.common.HorizontalAlignment; import org.diylc.common.IPlugInPort; import org.diylc.common.ObjectCache; import org.diylc.common.Orientation; +import org.diylc.common.PCBLayer; import org.diylc.common.VerticalAlignment; import org.diylc.components.AbstractLabeledComponent; import org.diylc.components.AbstractMakerBoard; import org.diylc.components.transform.DIL_ICTransformer; import org.diylc.core.ComponentState; +import org.diylc.core.IBoardSideComponent; import org.diylc.core.IDIYComponent; import org.diylc.core.IDatasheetSupport; import org.diylc.core.IDrawingObserver; @@ -69,7 +71,7 @@ import org.diylc.utils.Constants; transformer = DIL_ICTransformer.class, enableCache = true, enableDatasheet = true, datasheetCreationStepCount = 2, datasheetModelColumn = 1) public class DIL_IC extends AbstractLabeledComponent - implements IGerberComponentSimple, IDatasheetSupport { + implements IGerberComponentSimple, IDatasheetSupport, IBoardSideComponent { private static final long serialVersionUID = 1L; @@ -97,6 +99,7 @@ public class DIL_IC extends AbstractLabeledComponent private Color indentColor = INDENT_COLOR; private DisplayNumbers displayNumbers = DISPLAY_NUMBERS; private String pinNames = null; + private PCBLayer boardSide; // new Point(0, pinSpacing.convertToPixels()), // new Point(0, 2 * pinSpacing.convertToPixels()), // new Point(0, 3 * pinSpacing.convertToPixels()), @@ -695,6 +698,16 @@ public class DIL_IC extends AbstractLabeledComponent return name == null ? Integer.toString(pinNumber) : name; } + @EditableProperty(name = "Board Side") + @Override + public PCBLayer getBoardSide() { + return boardSide == null ? PCBLayer._2 : boardSide; + } + + public void setBoardSide(PCBLayer boardSide) { + this.boardSide = boardSide; + } + public static enum PinCount { _4, _6, _8, _10, _12, _14, _16, _18, _20, _22, _24, _26, _28, _30, _32, _34, _36, _38, _40, _42, _44, _46, _48, _50; diff --git a/diylc/diylc-library/src/main/java/org/diylc/components/semiconductors/DiodeGlass.java b/diylc/diylc-library/src/main/java/org/diylc/components/semiconductors/DiodeGlass.java index b64a625f3..b351d9548 100644 --- a/diylc/diylc-library/src/main/java/org/diylc/components/semiconductors/DiodeGlass.java +++ b/diylc/diylc-library/src/main/java/org/diylc/components/semiconductors/DiodeGlass.java @@ -28,9 +28,11 @@ import java.awt.geom.Rectangle2D; import org.diylc.appframework.miscutils.ConfigurationManager; import org.diylc.common.IPlugInPort; +import org.diylc.common.PCBLayer; import org.diylc.components.AbstractLeadedComponent; import org.diylc.components.transform.SimpleComponentTransformer; import org.diylc.core.CreationMethod; +import org.diylc.core.IBoardSideComponent; import org.diylc.core.IDatasheetSupport; import org.diylc.core.IDIYComponent; import org.diylc.core.Theme; @@ -51,7 +53,7 @@ import org.diylc.utils.Constants; transformer = SimpleComponentTransformer.class, enableCache = true, enableDatasheet = true, datasheetCreationStepCount = 2, datasheetModelColumn = 1) public class DiodeGlass extends AbstractLeadedComponent - implements IGerberComponentSimple, IDatasheetSupport { + implements IGerberComponentSimple, IDatasheetSupport, IBoardSideComponent { private static final long serialVersionUID = 1L; @@ -67,6 +69,7 @@ public class DiodeGlass extends AbstractLeadedComponent private String value = ""; private Color markerColor = MARKER_COLOR; private Color insideColor = INSIDE_COLOR; + private PCBLayer boardSide; public DiodeGlass() { super(); @@ -204,4 +207,14 @@ public class DiodeGlass extends AbstractLeadedComponent public void setInsideColor(Color insideColor) { this.insideColor = insideColor; } + + @EditableProperty(name = "Board Side") + @Override + public PCBLayer getBoardSide() { + return boardSide == null ? PCBLayer._2 : boardSide; + } + + public void setBoardSide(PCBLayer boardSide) { + this.boardSide = boardSide; + } } diff --git a/diylc/diylc-library/src/main/java/org/diylc/components/semiconductors/DiodePlastic.java b/diylc/diylc-library/src/main/java/org/diylc/components/semiconductors/DiodePlastic.java index ca7a88af2..b05670c57 100644 --- a/diylc/diylc-library/src/main/java/org/diylc/components/semiconductors/DiodePlastic.java +++ b/diylc/diylc-library/src/main/java/org/diylc/components/semiconductors/DiodePlastic.java @@ -28,9 +28,11 @@ import java.awt.geom.Rectangle2D; import org.diylc.appframework.miscutils.ConfigurationManager; import org.diylc.common.IPlugInPort; +import org.diylc.common.PCBLayer; import org.diylc.components.AbstractLeadedComponent; import org.diylc.components.transform.SimpleComponentTransformer; import org.diylc.core.CreationMethod; +import org.diylc.core.IBoardSideComponent; import org.diylc.core.IDatasheetSupport; import org.diylc.core.IDIYComponent; import org.diylc.core.Theme; @@ -51,7 +53,7 @@ import org.diylc.utils.Constants; transformer = SimpleComponentTransformer.class, enableCache = true, enableDatasheet = true, datasheetCreationStepCount = 2, datasheetModelColumn = 1) public class DiodePlastic extends AbstractLeadedComponent - implements IGerberComponentSimple, IDatasheetSupport { + implements IGerberComponentSimple, IDatasheetSupport, IBoardSideComponent { private static final long serialVersionUID = 1L; @@ -65,6 +67,7 @@ public class DiodePlastic extends AbstractLeadedComponent private String value = ""; private Color markerColor = MARKER_COLOR; + private PCBLayer boardSide; public DiodePlastic() { super(); @@ -178,4 +181,14 @@ public class DiodePlastic extends AbstractLeadedComponent public void setMarkerColor(Color markerColor) { this.markerColor = markerColor; } + + @EditableProperty(name = "Board Side") + @Override + public PCBLayer getBoardSide() { + return boardSide == null ? PCBLayer._2 : boardSide; + } + + public void setBoardSide(PCBLayer boardSide) { + this.boardSide = boardSide; + } } diff --git a/diylc/diylc-library/src/main/java/org/diylc/components/semiconductors/SIL_IC.java b/diylc/diylc-library/src/main/java/org/diylc/components/semiconductors/SIL_IC.java index 11ff3da44..3a88864d7 100644 --- a/diylc/diylc-library/src/main/java/org/diylc/components/semiconductors/SIL_IC.java +++ b/diylc/diylc-library/src/main/java/org/diylc/components/semiconductors/SIL_IC.java @@ -40,6 +40,7 @@ import org.diylc.components.AbstractMakerBoard; import org.diylc.components.AbstractTransparentComponent; import org.diylc.components.transform.SIL_ICTransformer; import org.diylc.core.ComponentState; +import org.diylc.core.IBoardSideComponent; import org.diylc.core.IDIYComponent; import org.diylc.core.IDatasheetSupport; import org.diylc.core.IDrawingObserver; @@ -65,7 +66,7 @@ import org.diylc.utils.Constants; enableCache = true, enableDatasheet = true, datasheetCreationStepCount = 2, datasheetModelColumn = 1) public class SIL_IC extends AbstractTransparentComponent - implements IGerberComponentSimple, IDatasheetSupport { + implements IGerberComponentSimple, IDatasheetSupport, IBoardSideComponent { private static final long serialVersionUID = 1L; @@ -90,6 +91,7 @@ public class SIL_IC extends AbstractTransparentComponent private Color indentColor = INDENT_COLOR; private String pinNames = null; private PinLabels pinLabels = null; + private PCBLayer boardSide; // new Point(0, pinSpacing.convertToPixels()), // new Point(0, 2 * pinSpacing.convertToPixels()), // new Point(0, 3 * pinSpacing.convertToPixels()), @@ -553,6 +555,16 @@ public class SIL_IC extends AbstractTransparentComponent return new Rectangle2D.Double(minX - margin, minY - margin, maxX - minX + 2 * margin, maxY - minY + 2 * margin); } + @EditableProperty(name = "Board Side") + @Override + public PCBLayer getBoardSide() { + return boardSide == null ? PCBLayer._2 : boardSide; + } + + public void setBoardSide(PCBLayer boardSide) { + this.boardSide = boardSide; + } + public static enum PinCount { _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20; diff --git a/diylc/diylc-library/src/main/java/org/diylc/components/semiconductors/TransistorTO1.java b/diylc/diylc-library/src/main/java/org/diylc/components/semiconductors/TransistorTO1.java index d41714dd0..2a1c40435 100644 --- a/diylc/diylc-library/src/main/java/org/diylc/components/semiconductors/TransistorTO1.java +++ b/diylc/diylc-library/src/main/java/org/diylc/components/semiconductors/TransistorTO1.java @@ -37,9 +37,11 @@ import org.diylc.appframework.miscutils.ConfigurationManager; import org.diylc.common.IPlugInPort; import org.diylc.common.ObjectCache; +import org.diylc.common.PCBLayer; import org.diylc.common.Percentage; import org.diylc.components.transform.TO1Transformer; import org.diylc.core.ComponentState; +import org.diylc.core.IBoardSideComponent; import org.diylc.core.IDIYComponent; import org.diylc.core.IDrawingObserver; import org.diylc.core.Project; @@ -60,7 +62,7 @@ import org.diylc.utils.Constants; zOrder = IDIYComponent.COMPONENT, keywordPolicy = KeywordPolicy.SHOW_VALUE, transformer = TO1Transformer.class, enableCache = true, enableDatasheet = true, datasheetCreationStepCount = 2, datasheetModelColumn = 1) -public class TransistorTO1 extends AbstractTransistorPackage { +public class TransistorTO1 extends AbstractTransistorPackage implements IBoardSideComponent { private static final long serialVersionUID = 1L; @@ -77,6 +79,7 @@ public class TransistorTO1 extends AbstractTransistorPackage { private Size pinSpacing = PIN_SPACING; private Boolean flange = true; + private PCBLayer boardSide; public TransistorTO1() { super(); @@ -329,4 +332,14 @@ public class TransistorTO1 extends AbstractTransistorPackage { protected boolean flipPinoutLabel() { return true; } + + @EditableProperty(name = "Board Side") + @Override + public PCBLayer getBoardSide() { + return boardSide == null ? PCBLayer._2 : boardSide; + } + + public void setBoardSide(PCBLayer boardSide) { + this.boardSide = boardSide; + } } diff --git a/diylc/diylc-library/src/main/java/org/diylc/components/semiconductors/TransistorTO220.java b/diylc/diylc-library/src/main/java/org/diylc/components/semiconductors/TransistorTO220.java index 915cd7741..10ce5923a 100644 --- a/diylc/diylc-library/src/main/java/org/diylc/components/semiconductors/TransistorTO220.java +++ b/diylc/diylc-library/src/main/java/org/diylc/components/semiconductors/TransistorTO220.java @@ -37,9 +37,11 @@ import org.diylc.appframework.miscutils.ConfigurationManager; import org.diylc.common.IPlugInPort; import org.diylc.common.ObjectCache; +import org.diylc.common.PCBLayer; import org.diylc.common.Percentage; import org.diylc.components.transform.TO220Transformer; import org.diylc.core.ComponentState; +import org.diylc.core.IBoardSideComponent; import org.diylc.core.IDIYComponent; import org.diylc.core.IDrawingObserver; import org.diylc.core.Project; @@ -60,7 +62,7 @@ import org.diylc.utils.Constants; zOrder = IDIYComponent.COMPONENT, keywordPolicy = KeywordPolicy.SHOW_VALUE, transformer = TO220Transformer.class, enableCache = true, enableDatasheet = true, datasheetCreationStepCount = 2, datasheetModelColumn = 1) -public class TransistorTO220 extends AbstractTransistorPackage { +public class TransistorTO220 extends AbstractTransistorPackage implements IBoardSideComponent { private static final long serialVersionUID = 1L; @@ -83,6 +85,7 @@ public class TransistorTO220 extends AbstractTransistorPackage { private Color tabColor = TAB_COLOR; private Color tabBorderColor = TAB_BORDER_COLOR; private Size leadLength = LEAD_LENGTH; + private PCBLayer boardSide; public TransistorTO220() { super(); @@ -437,4 +440,14 @@ public class TransistorTO220 extends AbstractTransistorPackage { protected boolean flipPinoutLabel() { return true; } + + @EditableProperty(name = "Board Side") + @Override + public PCBLayer getBoardSide() { + return boardSide == null ? PCBLayer._2 : boardSide; + } + + public void setBoardSide(PCBLayer boardSide) { + this.boardSide = boardSide; + } } diff --git a/diylc/diylc-library/src/main/java/org/diylc/components/semiconductors/TransistorTO92.java b/diylc/diylc-library/src/main/java/org/diylc/components/semiconductors/TransistorTO92.java index 759ef6fa0..ffc60831f 100644 --- a/diylc/diylc-library/src/main/java/org/diylc/components/semiconductors/TransistorTO92.java +++ b/diylc/diylc-library/src/main/java/org/diylc/components/semiconductors/TransistorTO92.java @@ -36,9 +36,11 @@ import org.diylc.appframework.miscutils.ConfigurationManager; import org.diylc.common.IPlugInPort; import org.diylc.common.ObjectCache; +import org.diylc.common.PCBLayer; import org.diylc.common.Percentage; import org.diylc.components.transform.TO92Transformer; import org.diylc.core.ComponentState; +import org.diylc.core.IBoardSideComponent; import org.diylc.core.IDIYComponent; import org.diylc.core.IDrawingObserver; import org.diylc.core.Project; @@ -60,7 +62,7 @@ import org.diylc.utils.Constants; zOrder = IDIYComponent.COMPONENT, keywordPolicy = KeywordPolicy.SHOW_VALUE, transformer = TO92Transformer.class, enableCache = true, enableDatasheet = true, datasheetCreationStepCount = 2, datasheetModelColumn = 1) -public class TransistorTO92 extends AbstractTransistorPackage { +public class TransistorTO92 extends AbstractTransistorPackage implements IBoardSideComponent { private static final long serialVersionUID = 1L; @@ -72,6 +74,7 @@ public class TransistorTO92 extends AbstractTransistorPackage { public static Size BODY_DIAMETER = new Size(0.2d, SizeUnit.in); private Size pinSpacing = PIN_SPACING; + private PCBLayer boardSide; public TransistorTO92() { super(); @@ -285,4 +288,14 @@ public class TransistorTO92 extends AbstractTransistorPackage { g2d.fillOval(width / 2 - pinSize / 2, (height / 4) * (i + 1), pinSize, pinSize); } } + + @EditableProperty(name = "Board Side") + @Override + public PCBLayer getBoardSide() { + return boardSide == null ? PCBLayer._2 : boardSide; + } + + public void setBoardSide(PCBLayer boardSide) { + this.boardSide = boardSide; + } } diff --git a/diylc/diylc-library/src/main/java/org/diylc/components/smd/PassiveSMDComponent.java b/diylc/diylc-library/src/main/java/org/diylc/components/smd/PassiveSMDComponent.java index 2c68f7926..fea4ad001 100644 --- a/diylc/diylc-library/src/main/java/org/diylc/components/smd/PassiveSMDComponent.java +++ b/diylc/diylc-library/src/main/java/org/diylc/components/smd/PassiveSMDComponent.java @@ -37,8 +37,10 @@ import org.diylc.common.Display; import org.diylc.common.IPlugInPort; import org.diylc.common.ObjectCache; import org.diylc.common.Orientation; +import org.diylc.common.PCBLayer; import org.diylc.components.AbstractLabeledComponent; import org.diylc.core.ComponentState; +import org.diylc.core.IBoardSideComponent; import org.diylc.core.IDrawingObserver; import org.diylc.core.Project; import org.diylc.core.Theme; @@ -49,7 +51,7 @@ import org.diylc.core.measures.Size; import org.diylc.core.measures.SizeUnit; import org.diylc.utils.Constants; -public abstract class PassiveSMDComponent extends AbstractLabeledComponent implements IGerberComponentSimple { +public abstract class PassiveSMDComponent extends AbstractLabeledComponent implements IGerberComponentSimple, IBoardSideComponent { private static final long serialVersionUID = 1L; @@ -68,6 +70,7 @@ public abstract class PassiveSMDComponent extends AbstractLabeledComponent protected Color bodyColor; protected Color borderColor; private Color labelColor = LABEL_COLOR; + private PCBLayer boardSide; transient private Area[] body; public PassiveSMDComponent() { @@ -413,6 +416,16 @@ public abstract class PassiveSMDComponent extends AbstractLabeledComponent return false; } + @EditableProperty(name = "Board Side") + @Override + public PCBLayer getBoardSide() { + return boardSide == null ? PCBLayer._2 : boardSide; + } + + public void setBoardSide(PCBLayer boardSide) { + this.boardSide = boardSide; + } + public static enum SMDSize { _0805(new Size(0.05d, SizeUnit.in), new Size(0.08d, SizeUnit.in)), _1206(new Size(1.6d, SizeUnit.mm), new Size(3.2d, diff --git a/diylc/diylc-library/src/test/java/org/diylc/components/BoardSideComponentsTest.java b/diylc/diylc-library/src/test/java/org/diylc/components/BoardSideComponentsTest.java new file mode 100644 index 000000000..d82fc6e5f --- /dev/null +++ b/diylc/diylc-library/src/test/java/org/diylc/components/BoardSideComponentsTest.java @@ -0,0 +1,80 @@ +/* + + 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 . + +*/ +package org.diylc.components; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; + +import org.diylc.common.ComponentType; +import org.diylc.common.PCBLayer; +import org.diylc.core.IBoardSideComponent; +import org.diylc.core.IDIYComponent; +import org.diylc.presenter.ComponentProcessor; +import org.diylc.utils.ReflectionUtils; +import org.junit.Test; + +public class BoardSideComponentsTest { + + @Test + public void testBoardSideComponentsStartOnTop() throws Exception { + for (IDIYComponent component : createBoardSideComponents()) { + assertEquals(component.getClass().getName(), PCBLayer._2, + ((IBoardSideComponent) component).getBoardSide()); + } + } + + @Test + public void testBoardSideCanBeChanged() throws Exception { + for (IDIYComponent component : createBoardSideComponents()) { + component.getClass().getMethod("setBoardSide", PCBLayer.class).invoke(component, PCBLayer._1); + assertEquals(component.getClass().getName(), PCBLayer._1, + ((IBoardSideComponent) component).getBoardSide()); + } + } + + // a part moved to the bottom is seen mirrored from the top, so it must be possible to mirror it + @Test + public void testBoardSideComponentsCanBeMirrored() throws Exception { + for (IDIYComponent component : createBoardSideComponents()) { + @SuppressWarnings("unchecked") + ComponentType type = ComponentProcessor.getInstance().extractComponentTypeFrom( + (Class>) component.getClass()); + assertTrue(component.getClass().getName(), type.getTransformer().canMirror(component)); + } + } + + private static List> createBoardSideComponents() throws Exception { + List> components = new ArrayList>(); + for (Class type : ReflectionUtils.getIndexedClasses(IDIYComponent.class)) { + if (IBoardSideComponent.class.isAssignableFrom(type) + && !Modifier.isAbstract(type.getModifiers())) { + components.add((IDIYComponent) type.getDeclaredConstructor().newInstance()); + } + } + assertTrue(components.size() > 0); + return components; + } +} diff --git a/diylc/diylc-swing/src/main/resources/lang/Deutsch.txt b/diylc/diylc-swing/src/main/resources/lang/Deutsch.txt index c682215f3..d977878d8 100644 --- a/diylc/diylc-swing/src/main/resources/lang/Deutsch.txt +++ b/diylc/diylc-swing/src/main/resources/lang/Deutsch.txt @@ -733,3 +733,4 @@ Value Notation|Wertnotation Decimal (4.7K, 0.22uF, 6.3V)|Dezimal (4.7K, 0.22uF, 6.3V) RKM (4K7, 0u22, 6V3)|RKM (4K7, 0u22, 6V3) Fade Copper Under Board|Kupfer unter der Platine abblenden +Board Side|Platinenseite diff --git a/diylc/diylc-swing/src/main/resources/lang/English.txt b/diylc/diylc-swing/src/main/resources/lang/English.txt index 1be074325..4b28622a8 100644 --- a/diylc/diylc-swing/src/main/resources/lang/English.txt +++ b/diylc/diylc-swing/src/main/resources/lang/English.txt @@ -726,3 +726,4 @@ Value Notation Decimal (4.7K, 0.22uF, 6.3V) RKM (4K7, 0u22, 6V3) Fade Copper Under Board +Board Side diff --git a/diylc/diylc-swing/src/main/resources/lang/Español.txt b/diylc/diylc-swing/src/main/resources/lang/Español.txt index 0a884489f..965d5f0c8 100644 --- a/diylc/diylc-swing/src/main/resources/lang/Español.txt +++ b/diylc/diylc-swing/src/main/resources/lang/Español.txt @@ -729,3 +729,4 @@ Value Notation|Notación de valores Decimal (4.7K, 0.22uF, 6.3V)|Decimal (4.7K, 0.22uF, 6.3V) RKM (4K7, 0u22, 6V3)|RKM (4K7, 0u22, 6V3) Fade Copper Under Board|Atenuar cobre bajo la placa +Board Side|Lado de la placa diff --git a/diylc/diylc-swing/src/main/resources/lang/Français.txt b/diylc/diylc-swing/src/main/resources/lang/Français.txt index fb375d62b..760a357b0 100644 --- a/diylc/diylc-swing/src/main/resources/lang/Français.txt +++ b/diylc/diylc-swing/src/main/resources/lang/Français.txt @@ -728,3 +728,4 @@ Value Notation|Notation des valeurs Decimal (4.7K, 0.22uF, 6.3V)|Décimale (4.7K, 0.22uF, 6.3V) RKM (4K7, 0u22, 6V3)|RKM (4K7, 0u22, 6V3) Fade Copper Under Board|Estomper le cuivre sous la carte +Board Side|Côté de la carte diff --git a/diylc/diylc-swing/src/main/resources/lang/Italiano.txt b/diylc/diylc-swing/src/main/resources/lang/Italiano.txt index 3a4a82e48..563549bc8 100644 --- a/diylc/diylc-swing/src/main/resources/lang/Italiano.txt +++ b/diylc/diylc-swing/src/main/resources/lang/Italiano.txt @@ -730,3 +730,4 @@ Value Notation|Notazione dei valori Decimal (4.7K, 0.22uF, 6.3V)|Decimale (4.7K, 0.22uF, 6.3V) RKM (4K7, 0u22, 6V3)|RKM (4K7, 0u22, 6V3) Fade Copper Under Board|Attenua il rame sotto la scheda +Board Side|Lato della scheda diff --git a/diylc/diylc-swing/src/main/resources/lang/Nederlands.txt b/diylc/diylc-swing/src/main/resources/lang/Nederlands.txt index 452135b6a..63ab0e8f9 100644 --- a/diylc/diylc-swing/src/main/resources/lang/Nederlands.txt +++ b/diylc/diylc-swing/src/main/resources/lang/Nederlands.txt @@ -734,3 +734,4 @@ Value Notation|Waardenotatie Decimal (4.7K, 0.22uF, 6.3V)|Decimaal (4.7K, 0.22uF, 6.3V) RKM (4K7, 0u22, 6V3)|RKM (4K7, 0u22, 6V3) Fade Copper Under Board|Koper onder de print vervagen +Board Side|Printzijde diff --git a/diylc/diylc-swing/src/main/resources/lang/Português.txt b/diylc/diylc-swing/src/main/resources/lang/Português.txt index 4942b19cc..b9a6b670f 100644 --- a/diylc/diylc-swing/src/main/resources/lang/Português.txt +++ b/diylc/diylc-swing/src/main/resources/lang/Português.txt @@ -729,3 +729,4 @@ Value Notation|Notação de valores Decimal (4.7K, 0.22uF, 6.3V)|Decimal (4.7K, 0.22uF, 6.3V) RKM (4K7, 0u22, 6V3)|RKM (4K7, 0u22, 6V3) Fade Copper Under Board|Esmaecer cobre sob a placa +Board Side|Lado da placa diff --git a/diylc/diylc-swing/src/main/resources/lang/Srpski.txt b/diylc/diylc-swing/src/main/resources/lang/Srpski.txt index 88e7ecd55..cc9ab8e46 100644 --- a/diylc/diylc-swing/src/main/resources/lang/Srpski.txt +++ b/diylc/diylc-swing/src/main/resources/lang/Srpski.txt @@ -725,3 +725,4 @@ Value Notation|Zapis vrednosti Decimal (4.7K, 0.22uF, 6.3V)|Decimalni (4.7K, 0.22uF, 6.3V) RKM (4K7, 0u22, 6V3)|RKM (4K7, 0u22, 6V3) Fade Copper Under Board|Prigušen Bakar Ispod Ploče +Board Side|Strana Ploče diff --git a/diylc/diylc-swing/src/main/resources/lang/हिंदी.txt b/diylc/diylc-swing/src/main/resources/lang/हिंदी.txt index 8ee3f86dc..f0d502b53 100644 --- a/diylc/diylc-swing/src/main/resources/lang/हिंदी.txt +++ b/diylc/diylc-swing/src/main/resources/lang/हिंदी.txt @@ -727,3 +727,4 @@ Value Notation|मान संकेतन Decimal (4.7K, 0.22uF, 6.3V)|दशमलव (4.7K, 0.22uF, 6.3V) RKM (4K7, 0u22, 6V3)|RKM (4K7, 0u22, 6V3) Fade Copper Under Board|बोर्ड के नीचे तांबा धुंधला करें +Board Side|बोर्ड का पक्ष diff --git a/diylc/diylc-swing/src/main/resources/lang/简体中文.txt b/diylc/diylc-swing/src/main/resources/lang/简体中文.txt index 17e29a8fd..8c6dabd5e 100644 --- a/diylc/diylc-swing/src/main/resources/lang/简体中文.txt +++ b/diylc/diylc-swing/src/main/resources/lang/简体中文.txt @@ -727,3 +727,4 @@ Value Notation|数值表示法 Decimal (4.7K, 0.22uF, 6.3V)|小数 (4.7K, 0.22uF, 6.3V) RKM (4K7, 0u22, 6V3)|RKM (4K7, 0u22, 6V3) Fade Copper Under Board|淡化板下铜箔 +Board Side|板面 diff --git a/diylc/docs/plans/bottom-side-components.md b/diylc/docs/plans/bottom-side-components.md index bf22193e2..684cdd5d6 100644 --- a/diylc/docs/plans/bottom-side-components.md +++ b/diylc/docs/plans/bottom-side-components.md @@ -3,9 +3,10 @@ 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: the core Gerber side is done, with tests: `IBoardSideComponent` exists and the export -sends a bottom-side part to the bottom layers, but no component implements it yet. Parts can be -selected, dragged and stretched through the Underside Display mirror. The next steps below remain. +Status: 32 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 does not show the side yet, and the Flip to Other +Side action does not exist; the next steps below remain. ## 1. Current state @@ -41,6 +42,9 @@ selected, dragged and stretched through the Underside Display mirror. The next s | Flipping a part is an action that sets the side, mirrors the part and sends it back | A part seen from above through the board is a mirror image. Changing the side without mirroring gives a footprint that is wrong for fabrication. | | The Gerber layer is swapped centrally, not inside each component | Components keep emitting their `*Top` layer. One place maps it to the other side. | | Default `_2` (Top), with `null` meaning Top | Fields added to existing classes deserialize as `null` in old files. | +| The side is both an editable property and the Flip to Other Side action | The action is the normal way to move a part, because it also mirrors it. The property stays for a part that is already drawn mirrored and only needs its side set. | +| 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 `SolderPad` opens the solder mask on its own side | It used to write `SolderMaskBot` whatever its layer, which was wrong for top-layer pads. It now writes `SolderMaskTop` for a pad on `_2`. | ## 3. Design @@ -52,18 +56,28 @@ selected, dragged and stretched through the Underside Display mirror. The next s ### diylc-library -- The ~35 classes that use the `SilkscreenTop` default implement `IBoardSideComponent`. Each adds a - nullable `PCBLayer boardSide` field and a getter/setter pair, with - `@EditableProperty(name = "Board Side")` on the getter. The getter returns `_2` (Top) when the - field is `null`. This duplicates the field across classes in the same way the `PCBLayer` field is - duplicated today. Where several of these classes share an abstract base - (`AbstractRadialComponent`, `AbstractFilmCapacitor`, `AbstractTransistorPackage`, ...), put it on - the base. +- **Done.** Every part that uses the `SilkscreenTop` default and whose transformer can mirror it + implements `IBoardSideComponent`: a nullable `PCBLayer boardSide` field and a getter/setter pair + with `@EditableProperty(name = "Board Side")` on the getter, which returns `_2` (Top) when the + field is `null`. The key is in every language file. + - On a shared base, covering every subclass: `AbstractFilmCapacitor`, `AbstractPotentiometer`, + `AbstractRadialComponent`, `PassiveSMDComponent`. + - On the class itself: `Resistor`, `HeatSinkResistor`, `AxialElectrolyticCapacitor`, + `CrystalOscillator`, `ToroidalInductor`, `DiodePlastic`, `DiodeGlass`, `DIL_IC`, `SIL_IC`, + `TransistorTO1`, `TransistorTO92`, `TransistorTO220`, `CliffJack1_4`, `CliffJack1_8`, + `ClosedJack1_4`, `MiniToggleSwitch`, `PCBTerminalBlock`. The transistors are not done on + `AbstractTransistorPackage` because `TransistorTO126` cannot be mirrored, and a property on the + base cannot be hidden from one subclass. + - Left out because they cannot be mirrored: `TubeSocket`, `RotarySwitchOpen`, + `RotarySwitchSealed`, `RotarySelectorSwitch`, `SlideSwitch`, `PlasticDCJack`, + `TactileMicroSwitch`, `MiniRelay`, `AudioTransformer`, `MultiSectionCapacitor`, `MultiwattIC`, + `BridgeRectifier`, `TransistorTO126`. Making one of them mirrorable is what it takes to give it + a side. + - `Label` is left out too: it is text, not a mounted part, and `PCBText` already has a layer. + - `BoardSideComponentsTest` checks that every such part starts on Top, takes Bottom, and can be + mirrored. - `CopperTrace`, `CurvedTrace`, `GroundFill`, `SolderPad`, `PCBText` and `DrillHole` do **not** implement it. Their `PCBLayer` already says where they are. -- Before allowing Bottom on a class, check that its transformer can mirror it - (`canMirror`). A class that cannot be mirrored must not offer Bottom, or the footprint would be - wrong. ### diylc-swing @@ -115,17 +129,6 @@ order. ## 4. Open questions -- **Bottom silkscreen text.** Component labels drawn through `IGerberComponentSimple` have to read - correctly once the board is turned over, which means they must look mirrored from the top. Check - whether mirroring through the transformer does that for every class, or whether labels are drawn - unmirrored regardless. `GerberLayer.mirrored` is currently `false` for every layer, and the - exporter output must stay in top view. -- **SolderPad solder mask.** `SolderPad` always writes its mask opening to `SolderMaskBot`, - whatever its own `PCBLayer`. Confirm whether this is intended for top-layer SMD pads before - `GerberLayer.flip()` is introduced next to it. -- **Plain property or action only.** A plain editable property lets a user change the side without - mirroring. Either keep both (the property for already-mirrored parts) or show the side as - read-only and change it only through the action. - **Components that belong to no side.** In mirror stage 3 a jumper, eyelet or wire dropped in the mirror has no side to set. Confirm that placing it at the reflected point unchanged is what users expect, or whether such parts should be refused in the mirror. @@ -153,10 +156,8 @@ order. ## 6. Next steps -1. Add the field to the library components, putting it on shared bases where they exist. Settle - the silkscreen-text question while doing so. -2. Wrap bottom-side painting in `startDrawingSide(getBoardSide())`. -3. Add the Flip to Other Side action. -4. Split the silkscreen print-out by side. -5. Mirror stage 3: placing in the mirror, after Flip to Other Side exists. -6. Run the regression suite. +1. Wrap bottom-side painting in `startDrawingSide(getBoardSide())`. +2. Add the Flip to Other Side action. +3. Split the silkscreen print-out by side. +4. Mirror stage 3: placing in the mirror, after Flip to Other Side exists. +5. Run the regression suite.