diff --git a/diylc/diylc-core/src/org/diylc/presenter/update.xml b/diylc/diylc-core/src/org/diylc/presenter/update.xml index 05d4bc3a..cccab76c 100644 --- a/diylc/diylc-core/src/org/diylc/presenter/update.xml +++ b/diylc/diylc-core/src/org/diylc/presenter/update.xml @@ -3523,9 +3523,13 @@ 17 0 - 2021-01-17 00:00:00.000 CET + 2021-02-14 00:00:00.000 CET + + NEW_FEATURE + Added 'Eurorack Stripboard' component, designed by David Haillant + NEW_FEATURE Allow multi-line text in label components diff --git a/diylc/diylc-library/src/org/diylc/components/boards/EurorackStripboard.java b/diylc/diylc-library/src/org/diylc/components/boards/EurorackStripboard.java new file mode 100644 index 00000000..6994a41e --- /dev/null +++ b/diylc/diylc-library/src/org/diylc/components/boards/EurorackStripboard.java @@ -0,0 +1,774 @@ +/* + * + * DIY Layout Creator (DIYLC). Copyright (c) 2009-2018 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.boards; + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.geom.AffineTransform; +import java.awt.geom.Area; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.awt.geom.RoundRectangle2D; +import java.util.ArrayList; +import java.util.List; + +import org.diylc.awt.StringUtils; +import org.diylc.common.HorizontalAlignment; +import org.diylc.common.ObjectCache; +import org.diylc.common.VerticalAlignment; +import org.diylc.components.transform.ProtoBoardTransformer; +import org.diylc.core.ComponentState; +import org.diylc.core.IDIYComponent; +import org.diylc.core.IDrawingObserver; +import org.diylc.core.Project; +import org.diylc.core.VisibilityPolicy; +import org.diylc.core.annotations.BomPolicy; +import org.diylc.core.annotations.ComponentDescriptor; +import org.diylc.core.annotations.EditableProperty; +import org.diylc.core.annotations.KeywordPolicy; +import org.diylc.core.measures.Size; +import org.diylc.core.measures.SizeUnit; + +import com.sun.prism.BasicStroke; + +@ComponentDescriptor(name = "Eurorack Stripboard", category = "Boards", + author = "Branislav Stojkovic", + description = "David Haillant's eurorack format stripboard for prototyping and building simple modules", + instanceNamePrefix = "BB", zOrder = IDIYComponent.BOARD, + bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, autoEdit = false, + keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME, enableCache = true, + transformer = ProtoBoardTransformer.class) +public class EurorackStripboard extends AbstractProtoBoard { + + private static final long serialVersionUID = 1L; + + public static Color FILL_COLOR = Color.decode("#6B9B6B"); + public static Size SPACING = new Size(0.1d, SizeUnit.in); + public static Size CORNER_RADIUS = new Size(0.1d, SizeUnit.in); + public static Color SELECTION_COLOR = Color.red; + public static Color PAD_COLOR = COPPER_COLOR; + public static Color TRACE_COLOR = PAD_COLOR.brighter(); + public static Color HOLE_COLOR = Color.white; + + public static float COORDINATE_FONT_SIZE = 10f; + public static Color COORDINATE_COLOR = Color.decode("#DDDDDD"); + + public static Size PAD_SIZE = new Size(2d, SizeUnit.mm); + public static Size FILTER_CAP_SIZE = new Size(0.23d, SizeUnit.in); + public static Size LARGE_PAD_SIZE = new Size(0.25d, SizeUnit.in); + + public static Size WIDTH_SIZE = new Size(2d, SizeUnit.in); + public static Size LENGTH_SIZE = new Size(4d, SizeUnit.in); + public static Size BODY_OFFSET_Y = new Size(0.1d, SizeUnit.in); + + protected Point2D point = new Point2D.Double(0, 0); + + protected Color boardColor = FILL_COLOR; + protected Color padColor = PAD_COLOR; + protected Color traceColor = TRACE_COLOR; + protected Color textColor = COORDINATE_COLOR; + + private static String[] connectorLabels = + new String[] {"Conn1", "GND", "GND", "GND", "Conn2", "+5V", "CV", "Gate"}; + transient private List segments; + transient private List potPads; + transient private List filterPads; + transient private List potTraces; + + public List getSegments() { + if (segments == null) { + segments = new ArrayList(); + segments.add(new Segment(5, 0, 5, 1, "CenterLeft0")); + segments.add(new Segment(10, 0, 5, 1, "CenterRight0")); + segments.add(new Segment(5, 1, 5, 1, "CenterLeft1")); + segments.add(new Segment(10, 1, 5, 1, "CenterRight1")); + + segments.add(new Segment(5, 2, 1, 30, "GND")); + + for (int i = 0; i < 29; i++) { + segments.add(new Segment(6, 2 + i, 4, 1, "CenterLeft" + (2 + i))); + segments.add(new Segment(10, 2 + i, 5, 1, "CenterRight" + (2 + i))); + } + segments.add(new Segment(6, 2 + 29, 4, 1, "CenterRight29")); + + segments.add(new Segment(15, 3, 1, 30, "-")); + segments.add(new Segment(16, 3, 1, 30, "GND")); + segments.add(new Segment(17, 3, 1, 30, "+")); + + for (int i = 0; i < 5; i++) { + for (int j = 0; j < 5; j++) { + segments.add(new Segment(3, 8 * i + j, 2, 1, ((char) ('A' + j)) + Integer.toString(i), false)); + } + if (i < 4) { + for (int j = 0; j < 3; j++) { + segments.add(new Segment(0, 8 * i + j + 5, 5, 1, i + "-" + (j + 5))); + } + } + } + } + return segments; + } + + public List getPotPads() { + if (potPads == null) { + potPads = new ArrayList(); + potPads.add(new Pad(1.5, 0.2, "A")); + + potPads.add(new Pad(0.5, 0, "B", true)); + potPads.add(new Pad(0, 0.2, "B", false)); + potPads.add(new Pad(1, 1, "B", true)); + + potPads.add(new Pad(0.5, 2, "C")); + potPads.add(new Pad(1, 2, "C")); + + potPads.add(new Pad(0.5, 4, "D")); + potPads.add(new Pad(0, 3.8, "D")); + potPads.add(new Pad(1, 3, "D")); + + potPads.add(new Pad(1.5, 3.8, "E")); + } + return potPads; + } + + public List getFilterPads() { + if (filterPads == null) { + filterPads = new ArrayList(); + + filterPads.add(new Pad(6, 32.8, "GND", true)); + filterPads.add(new Pad(6, 33.8, "-")); + + filterPads.add(new Pad(8, 33, "-")); + filterPads.add(new Pad(8, 34, "Conn1", true)); + + filterPads.add(new Pad(9, 33, "+", true)); + filterPads.add(new Pad(9, 34, "Conn2")); + + filterPads.add(new Pad(11, 32.8, "+", true)); + filterPads.add(new Pad(11, 33.8, "GND")); + + filterPads.add(new Pad(11.5, 31, "+5V", true)); + filterPads.add(new Pad(12.5, 31, "CV")); + filterPads.add(new Pad(13.5, 31, "Gate")); + } + return filterPads; + } + + public List getPotTraces() { + if (potTraces == null) { + potTraces = new ArrayList(); + potTraces.add(new Point2D[] {new Point2D.Double(1.5, 0.2), new Point2D.Double(3, 0.2)}); + potTraces.add(new Point2D[] {new Point2D.Double(1, 2), new Point2D.Double(3, 2)}); + potTraces.add(new Point2D[] {new Point2D.Double(1, 1), new Point2D.Double(3, 1)}); + potTraces.add(new Point2D[] {new Point2D.Double(1, 3), new Point2D.Double(3, 3)}); + potTraces.add(new Point2D[] {new Point2D.Double(1.5, 3.8), new Point2D.Double(3, 3.8)}); + + potTraces.add(new Point2D[] {new Point2D.Double(0.8, 0.2), new Point2D.Double(0.8, 1)}); + potTraces.add(new Point2D[] {new Point2D.Double(0.8, 3.8), new Point2D.Double(0.8, 3)}); + } + return potTraces; + } + + @SuppressWarnings("incomplete-switch") + @Override + public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode, + Project project, IDrawingObserver drawingObserver) { + if (checkPointsClipped(g2d.getClip())) { + return; + } + + // adjust the angle + double theta = 0; + switch (getOrientation()) { + case _90: + theta = Math.PI / 2; + break; + case _180: + theta = Math.PI; + break; + case _270: + theta = Math.PI * 3 / 2; + break; + } + if (theta != 0) { + g2d.rotate(theta, point.getX(), point.getY()); + } + + double spacing = SPACING.convertToPixels(); + + // draw body + g2d.setColor(boardColor); + int width = (int) WIDTH_SIZE.convertToPixels(); + int length = (int) LENGTH_SIZE.convertToPixels(); + int radius = (int) CORNER_RADIUS.convertToPixels(); + int bodyOffsetY = (int) BODY_OFFSET_Y.convertToPixels(); + + Shape rect = new RoundRectangle2D.Double(point.getX(), point.getY() - bodyOffsetY, width, + length, radius, radius); + g2d.fill(rect); + g2d.setColor( + componentState == ComponentState.SELECTED || componentState == ComponentState.DRAGGING + ? SELECTION_COLOR + : boardColor.darker()); + g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1)); + + drawingObserver.stopTracking(); + + g2d.draw(rect); + + int padSize = getClosestOdd(PAD_SIZE.convertToPixels()); + int holeSize = getClosestOdd(padSize / 3); + + g2d.setFont(LABEL_FONT.deriveFont(COORDINATE_FONT_SIZE)); + g2d.setColor(textColor); + + double x0 = point.getX() + spacing; + double y0 = point.getY() + spacing; + + for (int i = 0; i < 3; i++) { + String text = (char) ((int) 'A' + (i * 5)) + ""; + StringUtils.drawCenteredText(g2d, text, x0 + spacing * i * 5, y0 - spacing - 2, + HorizontalAlignment.CENTER, VerticalAlignment.CENTER); + text = (char) ((int) 'A' + (i * 5 + 4)) + ""; + StringUtils.drawCenteredText(g2d, text, x0 + spacing * (i * 5 + 4), y0 - spacing - 2, + HorizontalAlignment.CENTER, VerticalAlignment.CENTER); + } + + StringUtils.drawCenteredText(g2d, "GND", x0 + spacing * 16, y0 + 2 * spacing - 2, + HorizontalAlignment.CENTER, VerticalAlignment.BOTTOM); + StringUtils.drawCenteredText(g2d, "+", x0 + spacing * 17, y0 + 2 * spacing, + HorizontalAlignment.CENTER, VerticalAlignment.BOTTOM); + StringUtils.drawCenteredText(g2d, "GND", x0 + spacing * 16, y0 + 33 * spacing + 2, + HorizontalAlignment.CENTER, VerticalAlignment.TOP); + StringUtils.drawCenteredText(g2d, "+", x0 + spacing * 17, y0 + 33 * spacing + 2, + HorizontalAlignment.CENTER, VerticalAlignment.TOP); + + g2d.setFont(LABEL_FONT); + StringUtils.drawCenteredText(g2d, "-", x0 + spacing * 15, y0 + 2 * spacing + 2, + HorizontalAlignment.CENTER, VerticalAlignment.BOTTOM); + StringUtils.drawCenteredText(g2d, "-", x0 + spacing * 15, y0 + 33 * spacing, + HorizontalAlignment.CENTER, VerticalAlignment.TOP); + + g2d.setFont(LABEL_FONT.deriveFont(COORDINATE_FONT_SIZE)); + for (int i = 0; i < 6; i++) { + StringUtils.drawCenteredText(g2d, Integer.toString((i + 1) * 5), x0 + spacing * 18 - 4, + y0 + (5 * (i + 1) - 1) * spacing, HorizontalAlignment.LEFT, VerticalAlignment.CENTER); + } + + StringUtils.drawCenteredText(g2d, "Eurorack Stripboard", x0 - 8, y0 + (36.8) * spacing, + HorizontalAlignment.LEFT, VerticalAlignment.BOTTOM); + StringUtils.drawCenteredText(g2d, "davidhaillant.com", x0 - 8, y0 + (37.3) * spacing, + HorizontalAlignment.LEFT, VerticalAlignment.BOTTOM); + + StringUtils.drawCenteredText(g2d, "GND", x0 + 17 * spacing, y0 + (37) * spacing, + HorizontalAlignment.CENTER, VerticalAlignment.BOTTOM); + + // filter caps + g2d.setStroke(ObjectCache.getInstance().fetchZoomableStroke(2)); + int filterCapSize = (int) FILTER_CAP_SIZE.convertToPixels(); + + int centerX = (int) (x0 + 6 * spacing); + int centerY = (int) (y0 + 33.3 * spacing); + + Ellipse2D filterCap = new Ellipse2D.Double(centerX - filterCapSize / 2, + centerY - filterCapSize / 2, filterCapSize, filterCapSize); + g2d.draw(filterCap); + Area area = new Area(filterCap); + area.subtract(new Area(new Rectangle2D.Double(centerX - filterCapSize, centerY - filterCapSize, + filterCapSize * 2, filterCapSize))); + g2d.fill(area); + + centerX = (int) (x0 + 11 * spacing); + filterCap = new Ellipse2D.Double(centerX - filterCapSize / 2, centerY - filterCapSize / 2, + filterCapSize, filterCapSize); + g2d.draw(filterCap); + area = new Area(filterCap); + area.subtract(new Area(new Rectangle2D.Double(centerX - filterCapSize, centerY - filterCapSize, + filterCapSize * 2, filterCapSize))); + g2d.fill(area); + + int circleSize = getClosestOdd(padSize * 1.2); + centerX = (int) (x0 + 8 * spacing); + centerY = (int) (y0 + 33 * spacing); + g2d.drawOval(centerX - circleSize / 2, centerY - circleSize / 2, circleSize, circleSize); + g2d.drawLine(centerX - circleSize / 2, (int) (centerY + circleSize / 2 + spacing + 1), centerX + circleSize / 2, (int) (centerY + circleSize / 2 + spacing + 1)); + + centerX = (int) (x0 + 9 * spacing); + centerY = (int) (y0 + 34 * spacing); + g2d.drawOval(centerX - circleSize / 2, centerY - circleSize / 2, circleSize, circleSize); + g2d.drawLine(centerX - circleSize / 2, (int) (centerY - circleSize / 2 - spacing), centerX + circleSize / 2, (int) (centerY - circleSize / 2 - spacing)); + + StringUtils.drawCenteredText(g2d, "GND", x0 + spacing * 5.1, y0 + 31.6 * spacing, + HorizontalAlignment.CENTER, VerticalAlignment.BOTTOM); + StringUtils.drawCenteredText(g2d, "+5V", x0 + spacing * 11.3, y0 + 31.5 * spacing, + HorizontalAlignment.CENTER, VerticalAlignment.BOTTOM); + StringUtils.drawCenteredText(g2d, "CV", x0 + spacing * 12.5, y0 + 31.5 * spacing, + HorizontalAlignment.CENTER, VerticalAlignment.BOTTOM); + StringUtils.drawCenteredText(g2d, "Gate", x0 + spacing * 13.7, y0 + 31.5 * spacing, + HorizontalAlignment.CENTER, VerticalAlignment.BOTTOM); + + // pot traces + List potTraces = getPotTraces(); + g2d.setColor(traceColor); + g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(padSize / 4)); + for (int i = 0; i < 5; i++) { + for (Point2D[] trace : potTraces) { + int padX1 = (int) (x0 + trace[0].getX() * spacing); + int padY1 = (int) (y0 + (trace[0].getY() + 8 * i) * spacing); + int padX2 = (int) (x0 + trace[1].getX() * spacing); + int padY2 = (int) (y0 + (trace[1].getY() + 8 * i) * spacing); + g2d.drawLine(padX1, padY1, padX2, padY2); + } + } + + // segments + for (Segment segment : getSegments()) { + + if (segment.spanX > 1) { + drawingObserver.stopTrackingContinuityArea(); + g2d.setColor(textColor); + Rectangle2D segmentRect = new Rectangle2D.Double(x0 + segment.x * spacing - spacing / 2, + y0 + segment.y * spacing - spacing / 2, (segment.spanX) * spacing, spacing); + g2d.setStroke(ObjectCache.getInstance().fetchZoomableStroke(2)); + g2d.draw(segmentRect); + + if (segment.includeTrace) { + g2d.setColor(traceColor); + g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(padSize / 4)); + g2d.drawLine((int) (x0 + segment.x * spacing), (int) (y0 + segment.y * spacing), + (int) (x0 + (segment.x + segment.spanX - 1) * spacing), + (int) (y0 + segment.y * spacing)); + } + + } else if (segment.spanY > 1) { + drawingObserver.stopTrackingContinuityArea(); + g2d.setColor(textColor); + Rectangle2D segmentRect = new Rectangle2D.Double(x0 + segment.x * spacing - spacing / 2, + y0 + segment.y * spacing - spacing / 2, spacing, (segment.spanY) * spacing); + g2d.setStroke(ObjectCache.getInstance().fetchZoomableStroke(2)); + g2d.draw(segmentRect); + + if (segment.includeTrace) { + g2d.setColor(traceColor); + g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(padSize / 4)); + g2d.drawLine((int) (x0 + segment.x * spacing), (int) (y0 + segment.y * spacing), + (int) (x0 + segment.x * spacing), + (int) (y0 + (segment.y + segment.spanY - 1) * spacing)); + } + } + + g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1)); + + for (int i = 0; i < segment.spanX; i++) { + for (int j = 0; j < segment.spanY; j++) { + + int padX = (int) (x0 + (segment.x + i) * spacing); + int padY = (int) (y0 + (segment.y + j) * spacing); + + drawingObserver.startTrackingContinuityArea(true); + drawingObserver.setContinuityMarker(segment.label); + + g2d.setColor(padColor); + g2d.fillRect(padX - padSize / 2, padY - padSize / 2, padSize, padSize); + + drawingObserver.setContinuityMarker(null); + + drawingObserver.stopTrackingContinuityArea(); + + g2d.setColor(padColor.darker()); + g2d.drawRect(padX - padSize / 2, padY - padSize / 2, padSize, padSize); + + g2d.setColor(HOLE_COLOR); + g2d.fillOval(padX - holeSize / 2, padY - holeSize / 2, holeSize, holeSize); + } + } + } + + g2d.setColor(textColor); + g2d.setStroke(ObjectCache.getInstance().fetchZoomableStroke(3)); + g2d.drawLine((int) (x0 + 5 * spacing), (int) (y0 + 35 * spacing), (int) (x0 + 15 * spacing), + (int) (y0 + 35 * spacing)); + g2d.drawLine((int) (x0 + 15 * spacing), (int) (y0 + 35 * spacing), (int) (x0 + 15 * spacing), + (int) (y0 + 38 * spacing) - 2); + g2d.drawLine((int) (x0 + 5 * spacing), (int) (y0 + 35 * spacing), (int) (x0 + 5 * spacing), + (int) (y0 + 38 * spacing) - 2); + + g2d.setStroke(ObjectCache.getInstance().fetchStroke(1, new float[] { 3, 6 }, 6, BasicStroke.CAP_SQUARE)); + g2d.drawLine((int) (x0 + 11 * spacing), (int) (y0 + 35.25 * spacing), (int) (x0 + 11 * spacing), + (int) (y0 + 38 * spacing) - 2); + + + g2d.fillRect((int) (x0 + 5 * spacing + 4), (int) (y0 + 35 * spacing + 4), 8, + (int) (3 * spacing) - 4); + + // pots + int potPadSize = (int) (padSize * 0.9); + List potPads = getPotPads(); + for (int i = 0; i < 5; i++) { + + g2d.setColor(textColor); + g2d.setStroke(ObjectCache.getInstance().fetchZoomableStroke(3)); + g2d.drawRect((int) (x0 - 0.8 * spacing), (int) (y0 + (8 * i) * spacing), (int) (3 * spacing), + (int) (4 * spacing)); + + g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1)); + + for (Pad potPad : potPads) { + int padX = (int) (x0 + potPad.x * spacing); + int padY = (int) (y0 + (potPad.y + 8 * i) * spacing); + + drawingObserver.startTrackingContinuityArea(true); + drawingObserver.setContinuityMarker(potPad.label + i); + + g2d.setColor(padColor); + if (potPad.square) + g2d.fillRect(padX - potPadSize / 2, padY - potPadSize / 2, potPadSize, potPadSize); + else + g2d.fillOval(padX - potPadSize / 2, padY - potPadSize / 2, potPadSize, potPadSize); + + drawingObserver.setContinuityMarker(null); + + drawingObserver.stopTrackingContinuityArea(); + + g2d.setColor(padColor.darker()); + if (potPad.square) + g2d.drawRect(padX - potPadSize / 2, padY - potPadSize / 2, potPadSize, potPadSize); + else + g2d.drawOval(padX - potPadSize / 2, padY - potPadSize / 2, potPadSize, potPadSize); + + g2d.setColor(HOLE_COLOR); + g2d.fillOval(padX - holeSize / 2, padY - holeSize / 2, holeSize, holeSize); + } + + g2d.setColor(textColor); + g2d.setFont(LABEL_FONT.deriveFont(16f)); + + StringUtils.drawCenteredText(g2d, Integer.toString(i + 1), x0 - 4, y0 + (2 + i * 8) * spacing, + HorizontalAlignment.CENTER, VerticalAlignment.CENTER); + + g2d.setFont(LABEL_FONT.deriveFont(COORDINATE_FONT_SIZE)); + + StringUtils.drawCenteredText(g2d, "B", x0 - 2, y0 + (1 + i * 8) * spacing - 4, + HorizontalAlignment.RIGHT, VerticalAlignment.CENTER); + StringUtils.drawCenteredText(g2d, "D", x0 - 2, y0 + (3 + i * 8) * spacing + 4, + HorizontalAlignment.RIGHT, VerticalAlignment.CENTER); + + g2d.setFont(LABEL_FONT.deriveFont(COORDINATE_FONT_SIZE * 0.9f)); + for (int j = 0; j < 5; j++) { + StringUtils.drawCenteredText(g2d, (char)('A' + j) + "", x0 + 3.5 * spacing, y0 + (j + i * 8) * spacing, + HorizontalAlignment.CENTER, VerticalAlignment.CENTER); + } + } + + List filterPads = getFilterPads(); + g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1)); + + // filter pads + for (Pad potPad : filterPads) { + int padX = (int) (x0 + potPad.x * spacing); + int padY = (int) (y0 + potPad.y * spacing); + + drawingObserver.startTrackingContinuityArea(true); + drawingObserver.setContinuityMarker(potPad.label); + + g2d.setColor(padColor); + if (potPad.square) + g2d.fillRect(padX - potPadSize / 2, padY - potPadSize / 2, potPadSize, potPadSize); + else + g2d.fillOval(padX - potPadSize / 2, padY - potPadSize / 2, potPadSize, potPadSize); + + drawingObserver.setContinuityMarker(null); + + drawingObserver.stopTrackingContinuityArea(); + + g2d.setColor(padColor.darker()); + if (potPad.square) + g2d.drawRect(padX - potPadSize / 2, padY - potPadSize / 2, potPadSize, potPadSize); + else + g2d.drawOval(padX - potPadSize / 2, padY - potPadSize / 2, potPadSize, potPadSize); + + g2d.setColor(HOLE_COLOR); + g2d.fillOval(padX - holeSize / 2, padY - holeSize / 2, holeSize, holeSize); + } + + + // bottom connector + for (int i = 0; i < 8; i++) { + for (int j = 0; j < 2; j++) { + int padX = (int) (x0 + (6.5 + i) * spacing); + int padY = (int) (y0 + (36 + j) * spacing); + + drawingObserver.startTrackingContinuityArea(true); + drawingObserver.setContinuityMarker(connectorLabels[i]); + + g2d.setColor(padColor); + if (i == 0 && j == 1) + g2d.fillRect(padX - padSize / 2, padY - padSize / 2, padSize, padSize); + else + g2d.fillOval(padX - padSize / 2, padY - padSize / 2, padSize, padSize); + + drawingObserver.setContinuityMarker(null); + drawingObserver.stopTrackingContinuityArea(); + + g2d.setColor(padColor.darker()); + if (i == 0 && j == 1) + g2d.drawRect(padX - padSize / 2, padY - padSize / 2, padSize, padSize); + else + g2d.drawOval(padX - padSize / 2, padY - padSize / 2, padSize, padSize); + + g2d.setColor(HOLE_COLOR); + g2d.fillOval(padX - holeSize / 2, padY - holeSize / 2, holeSize, holeSize); + } + } + + // GND pad + int largePadSize = (int) LARGE_PAD_SIZE.convertToPixels(); + int largeHoleSize = (int) (LARGE_PAD_SIZE.convertToPixels() * 0.6); + + int padX = (int) (x0 + (17) * spacing); + int padY = (int) (y0 + 35.5 * spacing); + + drawingObserver.startTrackingContinuityArea(true); + drawingObserver.setContinuityMarker("GND"); + + g2d.setColor(padColor); + g2d.fillOval(padX - largePadSize / 2, padY - largePadSize / 2, largePadSize, largePadSize); + g2d.setColor(HOLE_COLOR); + g2d.fillOval(padX - largeHoleSize / 2, padY - largeHoleSize / 2, largeHoleSize, largeHoleSize); + + drawingObserver.setContinuityMarker(null); + drawingObserver.stopTrackingContinuityArea(); + + padY = (int) (y0 + 0.5 * spacing); + + g2d.setColor(textColor); + g2d.fillOval(padX - largePadSize / 2, padY - largePadSize / 2, largePadSize, largePadSize); + g2d.setColor(HOLE_COLOR); + g2d.fillOval(padX - largeHoleSize / 2, padY - largeHoleSize / 2, largeHoleSize, largeHoleSize); + } + + @Override + public void drawIcon(Graphics2D g2d, int width, int height) { + int factor = 32 / width; + g2d.setColor(FILL_COLOR); + g2d.fillRect(2 / factor, 2 / factor, width - 4 / factor, height - 4 / factor); + g2d.setColor(FILL_COLOR.darker()); + g2d.drawRect(2 / factor, 2 / factor, width - 4 / factor, height - 4 / factor); + + g2d.setColor(PAD_COLOR.brighter()); + g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(2 / factor)); + + int size = (int) (8.0 / factor); + + g2d.drawLine(width / 3 - 2 / factor + size / 2, 6 / factor + size / 2, width, + 6 / factor + size / 2); + g2d.drawLine(width / 3 - 2 / factor + size / 2, 2 * width / 3 - 2 / factor + size / 2, width, + 2 * width / 3 - 2 / factor + size / 2); + + + g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1)); + + g2d.setColor(PAD_COLOR); + g2d.fillRect(width / 3 - 2 / factor, 6 / factor, size, size); + g2d.setColor(PAD_COLOR.darker()); + g2d.drawRect(width / 3 - 2 / factor, 6 / factor, size, size); + + g2d.setColor(PAD_COLOR); + g2d.fillRect(2 * width / 3 - 2 / factor, 6 / factor, size, size); + g2d.setColor(PAD_COLOR.darker()); + g2d.drawRect(2 * width / 3 - 2 / factor, 6 / factor, size, size); + + g2d.setColor(PAD_COLOR); + g2d.fillRect(width / 3 - 2 / factor, 2 * width / 3 - 2 / factor, size, size); + g2d.setColor(PAD_COLOR.darker()); + g2d.drawRect(width / 3 - 2 / factor, 2 * width / 3 - 2 / factor, size, size); + + g2d.setColor(PAD_COLOR); + g2d.fillRect(2 * width / 3 - 2 / factor, 2 * width / 3 - 2 / factor, size, size); + g2d.setColor(PAD_COLOR.darker()); + g2d.drawRect(2 * width / 3 - 2 / factor, 2 * width / 3 - 2 / factor, size, size); + + g2d.setColor(HOLE_COLOR); + g2d.fillOval(width / 3 - 2 / factor + size / 2 - 1, 6 / factor + size / 2 - 1, 3, 3); + g2d.fillOval(width / 3 - 2 / factor + size / 2 - 1, 2 * width / 3 - 2 / factor + size / 2 - 1, + 3, 3); + g2d.fillOval(2 * width / 3 - 2 / factor + size / 2 - 1, 6 / factor + size / 2 - 1, 3, 3); + g2d.fillOval(2 * width / 3 - 2 / factor + size / 2 - 1, + 2 * width / 3 - 2 / factor + size / 2 - 1, 3, 3); + } + + @EditableProperty(name = "Board Color") + public Color getBoardColor() { + return boardColor; + } + + public void setBoardColor(Color boardColor) { + this.boardColor = boardColor; + } + + @EditableProperty(name = "Pad Color") + public Color getPadColor() { + return padColor; + } + + public void setPadColor(Color padColor) { + this.padColor = padColor; + } + + @EditableProperty(name = "Trace Color") + public Color getTraceColor() { + return traceColor; + } + + public void setTraceColor(Color traceColor) { + this.traceColor = traceColor; + } + + @EditableProperty(name = "Text Color") + public Color getTextColor() { + return textColor; + } + + public void setTextColor(Color textColor) { + this.textColor = textColor; + } + + @Override + public int getControlPointCount() { + return 2; + } + + @Override + public boolean canPointMoveFreely(int pointIndex) { + return false; + } + + @SuppressWarnings("incomplete-switch") + @Override + public Point2D getControlPoint(int index) { + if (index == 0) + return point; + + // create a synthetic second control point for clipping check and to make sure that we cannot + // drag the right side of the board off the screen + + // adjust the angle + double theta = 0; + switch (getOrientation()) { + case _90: + theta = Math.PI / 2; + break; + case _180: + theta = Math.PI; + break; + case _270: + theta = Math.PI * 3 / 2; + break; + } + int width = (int) WIDTH_SIZE.convertToPixels(); + int length = (int) LENGTH_SIZE.convertToPixels(); + Point2D secondPoint = new Point2D.Double(point.getX() + width, point.getY() + length); + if (theta != 0) { + AffineTransform tx = AffineTransform.getRotateInstance(theta, point.getX(), point.getY()); + tx.transform(secondPoint, secondPoint); + } + return secondPoint; + } + + @Override + public boolean isControlPointSticky(int index) { + return false; + } + + @Override + public VisibilityPolicy getControlPointVisibilityPolicy(int index) { + return VisibilityPolicy.NEVER; + } + + @Override + public void setControlPoint(Point2D point, int index) { + if (index == 0) + this.point.setLocation(point); + } + + @Override + public Void getValue() { + return null; + } + + @Override + public void setValue(Void value) {} + + @Override + public String getControlPointNodeName(int index) { + return null; + } + + @Override + public Rectangle2D getCachingBounds() { + Point2D finalSecondPoint = getControlPoint(1); + double offset = BODY_OFFSET_Y.convertToPixels(); + return new Rectangle2D.Double(Math.min(point.getX(), finalSecondPoint.getX()) - offset, + Math.min(point.getY(), finalSecondPoint.getY()) - offset, + Math.abs(finalSecondPoint.getX() - point.getX()) + offset + 2, + Math.abs(finalSecondPoint.getY() - point.getY()) + offset + 2); + } + + class Segment { + int x; + int y; + int spanX; + int spanY; + String label; + boolean includeTrace = true; + + public Segment(int x, int y, int spanX, int spanY, String label) { + super(); + this.x = x; + this.y = y; + this.spanX = spanX; + this.spanY = spanY; + this.label = label; + } + + public Segment(int x, int y, int spanX, int spanY, String label, boolean includeTrace) { + this(x, y, spanX, spanY, label); + this.includeTrace = includeTrace; + } + } + + class Pad { + double x; + double y; + String label; + boolean square; + + public Pad(double x, double y, String label, boolean square) { + this.x = x; + this.y = y; + this.label = label; + this.square = square; + } + + public Pad(double x, double y, String label) { + this(x, y, label, false); + } + } +} diff --git a/diylc/diylc-swing/build.xml b/diylc/diylc-swing/build.xml index f3df1bba..dbdc9d84 100644 --- a/diylc/diylc-swing/build.xml +++ b/diylc/diylc-swing/build.xml @@ -10,7 +10,7 @@ - + diff --git a/diylc/diylc-swing/library/main.jar b/diylc/diylc-swing/library/main.jar index 0f14c469..5cf9c61a 100644 Binary files a/diylc/diylc-swing/library/main.jar and b/diylc/diylc-swing/library/main.jar differ