mirror of
https://github.com/bancika/diy-layout-creator
synced 2026-11-13 10:42:09 +10:00
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:
@@ -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();
|
||||
|
||||
+111
@@ -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;
|
||||
}
|
||||
}
|
||||
+13
-1
@@ -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();
|
||||
|
||||
+115
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user