P-0+o (Proto) Board, Proto Board 780 Holes and Eurorack Stripboard now have an Underside Display like the other boards, with the P-0+o's top copper faded in the mirror; the P-0+o credits no longer run off the board and show the designer's name correctly

This commit is contained in:
Bane Stojkovic
2026-11-12 23:13:35 +01:00
parent fbbd468989
commit 2b37bd172e
10 changed files with 345 additions and 7 deletions
+48 -1
View File
@@ -99,6 +99,7 @@
"namePrefix" : "BB",
"description" : "David Haillant's eurorack format stripboard for prototyping and building simple modules",
"creationMethod" : "SINGLE_CLICK",
"notes" : [ "Set 'Underside Display' only if explicitly asked to. The values other than 'NONE' render the mirror image of the board in the provided direction with the offset controlled by 'Underside Offset'" ],
"properties" : [ {
"name" : "Board Color",
"type" : "Color",
@@ -127,6 +128,21 @@
"type" : "Color",
"format" : "hex",
"defaultValue" : "#FFC593"
}, {
"name" : "Underside Display",
"type" : "BoardUndersideDisplay",
"possibleValues" : [ "NONE", "ABOVE", "BELOW", "LEFT", "RIGHT" ],
"defaultValue" : "NONE"
}, {
"name" : "Underside Offset",
"type" : "Size",
"format" : "(value)(unit)",
"possibleUnits" : [ "px", "mm", "cm", "m", "in", "ft", "yd" ],
"defaultValue" : "0.1in"
}, {
"name" : "Underside Transparency",
"type" : "Boolean",
"defaultValue" : "true"
} ]
}, {
"name" : "Eyelet Board",
@@ -322,7 +338,7 @@
"namePrefix" : "BB",
"description" : "A prototyping board for modular synths, central bus with V+ GND and V-",
"creationMethod" : "SINGLE_CLICK",
"notes" : [ "The control point is the top-left corner of the 2in x 4in body and 'Orientation' rotates the board around it", "With 'Orientation' DEFAULT, pad row n (1-33) is (0.4 + n x 0.1)in below the control point. Each row is split into separately connected groups, labeled by column: 1-2, 3-5, 6-8, 9-11, 12-14 and 15-16, at 0.1-0.2, 0.4-0.6, 0.7-0.9, 1.2-1.4, 1.5-1.7 and 1.8-1.9in to the right of it" ],
"notes" : [ "Set 'Underside Display' only if explicitly asked to. The values other than 'NONE' render the mirror image of the board in the provided direction with the offset controlled by 'Underside Offset'", "The control point is the top-left corner of the 2in x 4in body and 'Orientation' rotates the board around it", "With 'Orientation' DEFAULT, pad row n (1-33) is (0.4 + n x 0.1)in below the control point. Each row is split into separately connected groups, labeled by column: 1-2, 3-5, 6-8, 9-11, 12-14 and 15-16, at 0.1-0.2, 0.4-0.6, 0.7-0.9, 1.2-1.4, 1.5-1.7 and 1.8-1.9in to the right of it" ],
"properties" : [ {
"name" : "Board Color",
"type" : "Color",
@@ -351,6 +367,17 @@
"type" : "Color",
"format" : "hex",
"defaultValue" : "#FFC593"
}, {
"name" : "Underside Display",
"type" : "BoardUndersideDisplay",
"possibleValues" : [ "NONE", "ABOVE", "BELOW", "LEFT", "RIGHT" ],
"defaultValue" : "NONE"
}, {
"name" : "Underside Offset",
"type" : "Size",
"format" : "(value)(unit)",
"possibleUnits" : [ "px", "mm", "cm", "m", "in", "ft", "yd" ],
"defaultValue" : "0.1in"
}, {
"name" : "Underside Pad Color",
"type" : "Color",
@@ -361,6 +388,10 @@
"type" : "Color",
"format" : "hex",
"defaultValue" : "#D7B2FF"
}, {
"name" : "Underside Transparency",
"type" : "Boolean",
"defaultValue" : "true"
} ]
}, {
"name" : "Perf Board w/ Pads",
@@ -465,6 +496,7 @@
"namePrefix" : "BB",
"description" : "Prototyping board similar to Radio Shack 276-168B or MPJA 33304",
"creationMethod" : "SINGLE_CLICK",
"notes" : [ "Set 'Underside Display' only if explicitly asked to. The values other than 'NONE' render the mirror image of the board in the provided direction with the offset controlled by 'Underside Offset'" ],
"properties" : [ {
"name" : "Board Color",
"type" : "Color",
@@ -498,6 +530,21 @@
"type" : "ProtoBoard780Type",
"possibleValues" : [ "RS", "MPJA" ],
"defaultValue" : "MPJA"
}, {
"name" : "Underside Display",
"type" : "BoardUndersideDisplay",
"possibleValues" : [ "NONE", "ABOVE", "BELOW", "LEFT", "RIGHT" ],
"defaultValue" : "NONE"
}, {
"name" : "Underside Offset",
"type" : "Size",
"format" : "(value)(unit)",
"possibleUnits" : [ "px", "mm", "cm", "m", "in", "ft", "yd" ],
"defaultValue" : "0.1in"
}, {
"name" : "Underside Transparency",
"type" : "Boolean",
"defaultValue" : "true"
} ]
}, {
"name" : "Tag Strip",
@@ -1,10 +1,13 @@
package org.diylc.core;
import org.diylc.core.measures.Size;
import org.diylc.core.measures.SizeUnit;
import java.awt.geom.Rectangle2D;
public interface IBoard {
Size DEFAULT_UNDERSIDE_OFFSET = new Size(0.1, SizeUnit.in);
Rectangle2D getBoardRectangle();
@@ -669,6 +669,10 @@
<changeType>IMPROVEMENT</changeType>
<description>The 'Trace' layer in the Layers menu is split into 'Bottom Traces' and 'Top Traces', so the copper of one side can be hidden, locked or selected on its own; vias, eyelets, turrets and plated pads go through the board and belong to both, and jumpers are on the top. The layer shortcuts follow the menu order now, Ctrl+1 to Ctrl+7. Projects with the old 'Trace' layer hidden or locked open with both sides hidden or locked</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>'P-0+o (Proto) Board', 'Proto Board 780 Holes' and 'Eurorack Stripboard' now have an 'Underside Display' like the other boards, so their solder side can be shown and worked on next to them; the P-0+o's top traces are faded there</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>BUG_FIX</changeType>
<description>Rotated labels in pasted components and building blocks were drawn in the wrong place and at the wrong angle while following the mouse, until the components were placed</description>
@@ -50,7 +50,6 @@ import org.diylc.core.measures.SizeUnit;
public abstract class AbstractBoard extends AbstractTransparentComponent<String> implements IBoard {
private static final long serialVersionUID = 1L;
public static final Size DEFAULT_UNDERSIDE_OFFSET = new Size(0.1, SizeUnit.in);
public static Color BORDER_COLOR = PHENOLIC_COLOR.darker();
public static Color COORDINATE_COLOR = Color.gray.brighter();
@@ -0,0 +1,111 @@
/*
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.components.boards;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import org.diylc.core.BoardUndersideDisplay;
import org.diylc.core.IBoard;
import org.diylc.core.annotations.AiNote;
import org.diylc.core.annotations.EditableProperty;
import org.diylc.core.measures.Size;
/**
* A prototyping board that parts are soldered to, which can show its underside next to it like the
* boards that extend {@link AbstractBoard}. The underside properties are declared with the same
* names as there, so both kinds of boards offer them in the same way.
*
* @author Branislav Stojkovic
*/
@AiNote("Set 'Underside Display' only if explicitly asked to. The values other than 'NONE' render the mirror image of the board in the provided direction with the offset controlled by 'Underside Offset'")
public abstract class AbstractSolderedProtoBoard extends AbstractProtoBoard implements IBoard {
private static final long serialVersionUID = 1L;
protected BoardUndersideDisplay boardUndersideDisplay = BoardUndersideDisplay.NONE;
protected Size undersideOffset = IBoard.DEFAULT_UNDERSIDE_OFFSET;
private Boolean undersideTransparency = true;
/**
* @return the body of the board as drawn before it is turned to its orientation
*/
protected abstract Rectangle2D getUnrotatedBody();
/**
* @return the point the board is turned around to its orientation
*/
protected abstract Point2D getRotationCenter();
@Override
public Rectangle2D getBoardRectangle() {
Point2D center = getRotationCenter();
AffineTransform rotation = AffineTransform.getRotateInstance(getOrientation().toRadians(),
center.getX(), center.getY());
return rotation.createTransformedShape(getUnrotatedBody()).getBounds2D();
}
@EditableProperty(name = "Underside Display")
@Override
public BoardUndersideDisplay getUndersideDisplay() {
if (boardUndersideDisplay == null) {
boardUndersideDisplay = BoardUndersideDisplay.NONE;
}
return boardUndersideDisplay;
}
public void setUndersideDisplay(BoardUndersideDisplay boardUndersideDisplay) {
this.boardUndersideDisplay = boardUndersideDisplay;
}
@EditableProperty(name = "Underside Offset")
@Override
public Size getUndersideOffset() {
if (undersideOffset == null) {
undersideOffset = IBoard.DEFAULT_UNDERSIDE_OFFSET;
}
return undersideOffset;
}
public void setUndersideOffset(Size undersideOffset) {
this.undersideOffset = undersideOffset;
}
@EditableProperty(name = "Underside Transparency")
@Override
public Boolean getUndersideTransparency() {
if (undersideTransparency == null) {
undersideTransparency = true;
}
return undersideTransparency;
}
public void setUndersideTransparency(Boolean undersideTransparency) {
this.undersideTransparency = undersideTransparency;
}
@Override
public boolean shouldExportToGerber() {
return false;
}
}
@@ -55,7 +55,7 @@ import org.diylc.core.measures.SizeUnit;
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME,
keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME, enableCache = true,
transformer = ProtoBoardTransformer.class)
public class EurorackStripboard extends AbstractProtoBoard {
public class EurorackStripboard extends AbstractSolderedProtoBoard {
private static final long serialVersionUID = 1L;
@@ -720,6 +720,18 @@ public class EurorackStripboard extends AbstractProtoBoard {
return null;
}
@Override
protected Rectangle2D getUnrotatedBody() {
int bodyOffsetY = (int) BODY_OFFSET_Y.convertToPixels();
return new Rectangle2D.Double(point.getX(), point.getY() - bodyOffsetY,
(int) WIDTH_SIZE.convertToPixels(), (int) LENGTH_SIZE.convertToPixels());
}
@Override
protected Point2D getRotationCenter() {
return point;
}
@Override
public Rectangle2D getCachingBounds() {
Point2D finalSecondPoint = getControlPoint(1);
@@ -53,7 +53,7 @@ import org.diylc.core.measures.SizeUnit;
description = "A prototyping board for modular synths, central bus with V+ GND and V-", instanceNamePrefix = "BB",
zOrder = IDIYComponent.BOARD, bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME,
keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME, enableCache = true, transformer = ProtoBoardTransformer.class)
public class ProtoBoard extends AbstractProtoBoard {
public class ProtoBoard extends AbstractSolderedProtoBoard {
private static final long serialVersionUID = 1L;
@@ -146,6 +146,7 @@ public class ProtoBoard extends AbstractProtoBoard {
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(padSize / 4));
drawingObserver.startTrackingContinuityArea(true);
drawingObserver.startDrawingSide(PCBLayer._2);
// draw segment connectors
for (int y = 0; y < ROW_COUNT; y++) {
@@ -170,6 +171,7 @@ public class ProtoBoard extends AbstractProtoBoard {
(int) (point.getX() + (1 + SEGMENTS_SUM[3] + 2.5) * spacing), (int) (point.getY() + (yOffset + y - 0.25d) * spacing + 1));
}
drawingObserver.stopDrawingSide();
drawingObserver.startDrawingSide(PCBLayer._1);
g2d.setColor(undersideTraceColor);
@@ -187,6 +189,7 @@ public class ProtoBoard extends AbstractProtoBoard {
drawingObserver.stopDrawingSide();
g2d.setColor(traceColor);
drawingObserver.startDrawingSide(PCBLayer._2);
// traces to top and bottom 3 pads
g2d.drawPolyline(new int[] {
@@ -223,6 +226,7 @@ public class ProtoBoard extends AbstractProtoBoard {
(int) (point.getY() + (yOffset + ROW_COUNT) * spacing),
(int) (point.getY() + (yOffset + ROW_COUNT - 0.35) * spacing)}, 3);
drawingObserver.stopDrawingSide();
drawingObserver.stopTrackingContinuityArea();
int miniPadSize = getClosestOdd((2d * spacing - 6) / 5);
@@ -231,12 +235,14 @@ public class ProtoBoard extends AbstractProtoBoard {
g2d.setColor(traceColor);
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(padSize / 4));
drawingObserver.startTrackingContinuityArea(true);
drawingObserver.startDrawingSide(PCBLayer._2);
g2d.drawLine((int) (point.getX() + (1 + SEGMENTS_SUM[3] + 0.5) * spacing), (int) Math.round(point.getY() + 1 * spacing),
(int) (point.getX() + (1 + SEGMENTS_SUM[3] + 0.5) * spacing), (int) Math.round(point.getY() + 3.5 * spacing));
g2d.drawLine((int) (point.getX() + (1 + SEGMENTS_SUM[3] + 1.5) * spacing + miniPadSize / 2), (int) Math.round(point.getY() + 1 * spacing),
(int) (point.getX() + (1 + SEGMENTS_SUM[3] + 1.5) * spacing + miniPadSize / 2), (int) Math.round(point.getY() + 3.5 * spacing));
g2d.drawLine((int) (point.getX() + (1 + SEGMENTS_SUM[3] + 2.5) * spacing), (int) Math.round(point.getY() + 1 * spacing),
(int) (point.getX() + (1 + SEGMENTS_SUM[3] + 2.5) * spacing), (int) Math.round(point.getY() + 3.5 * spacing));
drawingObserver.stopDrawingSide();
drawingObserver.stopTrackingContinuityArea();
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
@@ -354,8 +360,10 @@ public class ProtoBoard extends AbstractProtoBoard {
drawingObserver.startTrackingContinuityArea(true);
// Draw two horizontal traces in the top-right section
drawingObserver.startDrawingSide(PCBLayer._2);
g2d.drawLine((int) Math.round(point.getX() + width - 4 * spacing), (int) Math.round(point.getY() + 0.5 * spacing), (int) (point.getX() + width - (2) * spacing), (int) Math.round(point.getY() + 0.5 * spacing));
g2d.drawLine((int) Math.round(point.getX() + width - 4 * spacing), (int) Math.round(point.getY() + 1.5 * spacing), (int) (point.getX() + width - (2) * spacing), (int) Math.round(point.getY() + 1.5 * spacing));
drawingObserver.stopDrawingSide();
// Draw top-right pads
for (int x = 0; x < 6; x++) {
@@ -363,7 +371,9 @@ public class ProtoBoard extends AbstractProtoBoard {
g2d.setColor(traceColor);
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(padSize / 4));
drawingObserver.startDrawingSide(PCBLayer._2);
g2d.drawLine(padX, (int) Math.round(point.getY() + 0.5 * spacing), padX, (int) Math.round(point.getY() + 3 * spacing));
drawingObserver.stopDrawingSide();
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
@@ -473,7 +483,7 @@ public class ProtoBoard extends AbstractProtoBoard {
g2d.setColor(textColor);
g2d.setFont(LABEL_FONT.deriveFont(COORDINATE_FONT_SIZE + 2));
StringUtils.drawCenteredText(g2d, "P-0+o by:\nKristian Bl�sol 2019", (int) Math.round(point.getX() + width * 0.70d), (int) (point.getY() + (yOffset + ROW_COUNT + 0.9) * spacing),
StringUtils.drawCenteredText(g2d, "P-0+o by:\nKristian Bl\u00e5sol 2019", (int) Math.round(point.getX() + spacing / 2), (int) (point.getY() + (yOffset + ROW_COUNT + 0.9) * spacing),
HorizontalAlignment.LEFT, VerticalAlignment.TOP);
// draw central mini pads
@@ -481,6 +491,7 @@ public class ProtoBoard extends AbstractProtoBoard {
g2d.setColor(padColor);
drawingObserver.startTrackingContinuityArea(true);
drawingObserver.startDrawingSide(PCBLayer._2);
// top row
g2d.fillRect((int) (point.getX() + (1 + SEGMENTS_SUM[3] + 0.5) * spacing) + 1, (int) (point.getY() + (yOffset + y - 0.5d) * spacing + 2), miniPadSize, miniPadSize);
@@ -496,6 +507,7 @@ public class ProtoBoard extends AbstractProtoBoard {
g2d.setColor(padColor.darker());
g2d.drawRect((int) (point.getX() + (1 + SEGMENTS_SUM[3] + 0.5) * spacing) + 1, (int) (point.getY() + (yOffset + y - 0.5d) * spacing + 2), miniPadSize, miniPadSize);
g2d.drawRect((int) (point.getX() + (1 + SEGMENTS_SUM[3] + 0.5) * spacing) + miniPadSize + 3, (int) (point.getY() + (yOffset + y - 0.5d) * spacing + 2), miniPadSize, miniPadSize);
drawingObserver.stopDrawingSide();
drawingObserver.startDrawingSide(PCBLayer._1);
g2d.setColor(undersidePadColor);
@@ -696,6 +708,17 @@ public class ProtoBoard extends AbstractProtoBoard {
return null;
}
@Override
protected Rectangle2D getUnrotatedBody() {
return new Rectangle2D.Double(point.getX(), point.getY(), (int) WIDTH_SIZE.convertToPixels(),
(int) LENGTH_SIZE.convertToPixels());
}
@Override
protected Point2D getRotationCenter() {
return point;
}
@Override
public Rectangle2D getCachingBounds() {
Point2D finalSecondPoint = getControlPoint(1);
@@ -50,7 +50,7 @@ import org.diylc.core.measures.SizeUnit;
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME,
keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME, enableCache = true,
transformer = ProtoBoardTransformer.class)
public class ProtoBoard780 extends AbstractProtoBoard {
public class ProtoBoard780 extends AbstractSolderedProtoBoard {
private static final long serialVersionUID = 1L;
@@ -519,6 +519,29 @@ public class ProtoBoard780 extends AbstractProtoBoard {
return null;
}
@Override
protected Rectangle2D getUnrotatedBody() {
return new Rectangle2D.Double(point.getX(), point.getY(), (int) WIDTH_SIZE.convertToPixels(),
(int) LENGTH_SIZE.convertToPixels());
}
@Override
protected Point2D getRotationCenter() {
return point;
}
@Override
public Rectangle2D getBoardRectangle() {
Rectangle2D rect = super.getBoardRectangle();
if (!translateToGrid) {
return rect;
}
// the whole board is drawn shifted onto the grid, after it is turned
rect.setRect(rect.getX() - X_OFFSET.convertToPixels(),
rect.getY() + SPACING.convertToPixels() / 2, rect.getWidth(), rect.getHeight());
return rect;
}
@Override
public Rectangle2D getCachingBounds() {
double spacing = SPACING.convertToPixels();
@@ -0,0 +1,115 @@
/*
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.components.boards;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
import org.diylc.common.Orientation;
import org.diylc.core.BoardUndersideDisplay;
import org.diylc.core.IBoard;
import org.diylc.serialization.ProjectFileManager;
import org.junit.Assert;
import org.junit.Test;
/**
* The prototyping boards parts are soldered to show their underside like other boards, and their
* board rectangle, which places the mirror, follows the board as it is turned.
*/
public class SolderedProtoBoardTest {
private static final double DELTA = 1e-6;
private static XStream createXStream() {
XStream xStream = new XStream(new DomDriver("UTF-8"));
ProjectFileManager.configure(xStream);
return xStream;
}
@Test
public void solderlessBreadboardHasNoUnderside() {
Assert.assertFalse(IBoard.class.isAssignableFrom(Breadboard.class));
}
@Test
public void boardRectangleIsTheBodyAtDefaultOrientation() {
ProtoBoard board = new ProtoBoard();
board.setControlPoint(new Point2D.Double(100, 50), 0);
assertRect(100, 50, ProtoBoard.WIDTH_SIZE.convertToPixels(),
ProtoBoard.LENGTH_SIZE.convertToPixels(), board.getBoardRectangle());
}
@Test
public void boardRectangleTurnsWithTheBoard() {
EurorackStripboard board = new EurorackStripboard();
board.setControlPoint(new Point2D.Double(1000, 50), 0);
board.setOrientation(Orientation._90);
double width = (int) EurorackStripboard.WIDTH_SIZE.convertToPixels();
double length = (int) EurorackStripboard.LENGTH_SIZE.convertToPixels();
double offset = (int) EurorackStripboard.BODY_OFFSET_Y.convertToPixels();
// a quarter turn clockwise around the control point swings the body to its left
assertRect(1000 - length + offset, 50, length, width, board.getBoardRectangle());
}
@Test
public void boardRectangleFollowsTheGridShift() {
ProtoBoard780 board = new ProtoBoard780();
board.setControlPoint(new Point2D.Double(100, 50), 0);
assertRect(100 - ProtoBoard780.X_OFFSET.convertToPixels(),
50 + ProtoBoard780.SPACING.convertToPixels() / 2,
(int) ProtoBoard780.WIDTH_SIZE.convertToPixels(),
(int) ProtoBoard780.LENGTH_SIZE.convertToPixels(), board.getBoardRectangle());
}
@Test
public void boardSavedBeforeUndersideDisplayLoadsWithoutOne() {
String xml = createXStream().toXML(new EurorackStripboard());
Assert.assertTrue(xml.contains("<boardUndersideDisplay>"));
EurorackStripboard loaded = (EurorackStripboard) createXStream().fromXML(xml
.replaceAll("\\s*<boardUndersideDisplay>[^<]*</boardUndersideDisplay>", "")
.replaceAll("\\s*<undersideOffset[\\s\\S]*?</undersideOffset>", "")
.replaceAll("\\s*<undersideTransparency>[^<]*</undersideTransparency>", ""));
Assert.assertEquals(BoardUndersideDisplay.NONE, loaded.getUndersideDisplay());
Assert.assertEquals(IBoard.DEFAULT_UNDERSIDE_OFFSET, loaded.getUndersideOffset());
Assert.assertTrue(loaded.getUndersideTransparency());
}
@Test
public void undersideDisplayIsSaved() {
ProtoBoard board = new ProtoBoard();
board.setUndersideDisplay(BoardUndersideDisplay.BELOW);
ProtoBoard loaded = (ProtoBoard) createXStream().fromXML(createXStream().toXML(board));
Assert.assertEquals(BoardUndersideDisplay.BELOW, loaded.getUndersideDisplay());
}
private static void assertRect(double x, double y, double width, double height,
Rectangle2D actual) {
Assert.assertEquals(x, actual.getX(), DELTA);
Assert.assertEquals(y, actual.getY(), DELTA);
Assert.assertEquals(width, actual.getWidth(), DELTA);
Assert.assertEquals(height, actual.getHeight(), DELTA);
}
}
+2 -1
View File
@@ -32,7 +32,8 @@ were regenerated for the faded copper. The open questions below remain.
| Paint a component does not declare is component side, except a board's own paint in the mirror, which is drawn as near side | Components need no changes. A board's substrate is seen from both sides, so it must not fade in the mirror. |
| Holes and trace cuts are visible from both sides (`startDrawingBothSides()`) and never fade; a board's own holes are already solid as board paint | A faded hole reads as copper, and a faded cut hides the break the user is looking for. |
| `Jumper`, `Eyelet`, `Turret`, `Top`-layer copper and `EyeletBoard` eyelets are component side | Jumpers run over the board; eyelets and turrets are visible from the top. |
| `ProtoBoard780` declares its traces and pads bottom side, like `PerfBoard`. `ProtoBoard` declares only the traces and pads its code paints in the underside colours. `EurorackStripboard` declares nothing | The 780-hole boards carry copper on the bottom only. `ProtoBoard` is plated through with some traces on top. Which side `EurorackStripboard`'s traces are on is not sourced. |
| `ProtoBoard780` declares its traces and pads bottom side, like `PerfBoard`. `ProtoBoard` declares the traces and pads its code paints in the underside colours bottom side, its other traces and the pads without a hole top side, and leaves its plated pads undeclared. `EurorackStripboard` declares nothing | The 780-hole boards carry copper on the bottom only. `ProtoBoard` is plated through with some traces on top, which must fade in its mirror like any top copper. Which side `EurorackStripboard`'s traces are on is not sourced; its holes are plated, so its pads are solid in both views. |
| `ProtoBoard780`, `ProtoBoard` and `EurorackStripboard` have an Underside Display, through `AbstractSolderedProtoBoard`; `Breadboard` does not | Parts are soldered to them, so their copper side is worth working on. A solderless breadboard has nothing on its bottom. |
| `Dot` has a Board Side property, Top by default | Dots are junctions in schematics and wiring diagrams, which must not fade, and solder joints on stripboard, which are on the copper side. Setting a solder joint's side to Bottom fades it in the main view and draws it solid in the mirror; a dot placed in the mirror goes to the bottom on its own (`bottom-side-components.md`). |
| `Breadboard`, `BlankBoard`, `MarshallPerfBoard`, `TagStrip`, `TerminalStrip` and the maker boards declare nothing | They have no bottom copper to show. |