diff --git a/diylc-library/.classpath b/diylc-library/.classpath
new file mode 100644
index 00000000..09ad88fe
--- /dev/null
+++ b/diylc-library/.classpath
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/diylc-library/.project b/diylc-library/.project
new file mode 100644
index 00000000..d33c5534
--- /dev/null
+++ b/diylc-library/.project
@@ -0,0 +1,17 @@
+
+
+ diylc-library
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/diylc-library/build.xml b/diylc-library/build.xml
new file mode 100644
index 00000000..ec00f750
--- /dev/null
+++ b/diylc-library/build.xml
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Compile with JDK ${java.runtime.version}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/diylc-library/lib/diylc-core.jar b/diylc-library/lib/diylc-core.jar
new file mode 100644
index 00000000..29dd1951
Binary files /dev/null and b/diylc-library/lib/diylc-core.jar differ
diff --git a/diylc-library/lib/log4j-1.2.15.jar b/diylc-library/lib/log4j-1.2.15.jar
new file mode 100644
index 00000000..c930a6ab
Binary files /dev/null and b/diylc-library/lib/log4j-1.2.15.jar differ
diff --git a/diylc-library/lib/xstream-1.3.1.jar b/diylc-library/lib/xstream-1.3.1.jar
new file mode 100644
index 00000000..4ef4219c
Binary files /dev/null and b/diylc-library/lib/xstream-1.3.1.jar differ
diff --git a/diylc-library/src/org/diylc/components/boards/AbstractBoard.java b/diylc-library/src/org/diylc/components/boards/AbstractBoard.java
new file mode 100644
index 00000000..9c4f89e5
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/boards/AbstractBoard.java
@@ -0,0 +1,183 @@
+package org.diylc.components.boards;
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Composite;
+import java.awt.Graphics2D;
+import java.awt.Point;
+
+import org.diylc.common.HorizontalAlignment;
+import org.diylc.common.ObjectCache;
+import org.diylc.common.VerticalAlignment;
+import org.diylc.components.AbstractTransparentComponent;
+import org.diylc.core.ComponentState;
+import org.diylc.core.IDrawingObserver;
+import org.diylc.core.Project;
+import org.diylc.core.VisibilityPolicy;
+import org.diylc.core.annotations.EditableProperty;
+import org.diylc.core.measures.Size;
+import org.diylc.core.measures.SizeUnit;
+
+public abstract class AbstractBoard extends AbstractTransparentComponent {
+
+ private static final long serialVersionUID = 1L;
+
+ public static Color BOARD_COLOR = Color.decode("#F8EBB3");
+ public static Color BORDER_COLOR = BOARD_COLOR.darker();
+ public static Color COORDINATE_COLOR = Color.gray.brighter();
+ public static float COORDINATE_FONT_SIZE = 9f;
+ public static Size DEFAULT_WIDTH = new Size(1.5d, SizeUnit.in);
+ public static Size DEFAULT_HEIGHT = new Size(1.2d, SizeUnit.in);
+
+ protected String value = "";
+ protected Point[] controlPoints = new Point[] {
+ new Point(0, 0),
+ new Point((int) DEFAULT_WIDTH.convertToPixels(), (int) DEFAULT_HEIGHT.convertToPixels()) };
+ protected Point firstPoint = new Point();
+ protected Point secondPoint = new Point();
+
+ protected Color boardColor = BOARD_COLOR;
+ protected Color borderColor = BORDER_COLOR;
+ protected Color coordinateColor = COORDINATE_COLOR;
+ protected Boolean drawCoordinates = true;
+
+ @Override
+ public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode,
+ Project project, IDrawingObserver drawingObserver) {
+ g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
+ if (componentState != ComponentState.DRAGGING) {
+ Composite oldComposite = g2d.getComposite();
+ if (alpha < MAX_ALPHA) {
+ g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha
+ / MAX_ALPHA));
+ }
+ g2d.setColor(boardColor);
+ g2d.fillRect(firstPoint.x, firstPoint.y, secondPoint.x - firstPoint.x, secondPoint.y
+ - firstPoint.y);
+ g2d.setComposite(oldComposite);
+ }
+ g2d.setColor(componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? SELECTION_COLOR : borderColor);
+ g2d.drawRect(firstPoint.x, firstPoint.y, secondPoint.x - firstPoint.x, secondPoint.y
+ - firstPoint.y);
+ // Do not track any changes that follow because the whole board has been
+ // tracked so far.
+ drawingObserver.stopTracking();
+ }
+
+ protected void drawCoordinates(Graphics2D g2d, int spacing) {
+ if (!getDrawCoordinates())
+ return;
+ Point p = new Point(firstPoint);
+ g2d.setColor(coordinateColor);
+ g2d.setFont(LABEL_FONT.deriveFont(COORDINATE_FONT_SIZE));
+
+ int t = 1;
+ while (p.y < secondPoint.y - spacing) {
+ p.y += spacing;
+ super.drawCenteredText(g2d, getCoordinateLabel(t++), p.x + 2, p.y,
+ HorizontalAlignment.LEFT, VerticalAlignment.CENTER);
+ }
+
+ p = new Point(firstPoint);
+ t = 1;
+ while (p.x < secondPoint.x - spacing) {
+ p.x += spacing;
+ super.drawCenteredText(g2d, getCoordinateLabel(t++), p.x, p.y - 2,
+ HorizontalAlignment.CENTER, VerticalAlignment.TOP);
+ }
+ }
+
+ private String getCoordinateLabel(int coordinate) {
+ String result = "";
+ while (coordinate > 0) {
+ int digit = coordinate % 26;
+ coordinate /= 26;
+ if (digit == 0) {
+ result = 'Z' + result;
+ coordinate--;
+ } else {
+ result = (char) ((int) 'A' + digit - 1) + result;
+ }
+ }
+ return result;
+ }
+
+ @EditableProperty(name = "Color")
+ public Color getBoardColor() {
+ return boardColor;
+ }
+
+ public void setBoardColor(Color boardColor) {
+ this.boardColor = boardColor;
+ }
+
+ @EditableProperty(name = "Coordinate Color")
+ public Color getCoordinateColor() {
+ // Null protection for older files
+ return coordinateColor == null ? COORDINATE_COLOR : coordinateColor;
+ }
+
+ public void setCoordinateColor(Color coordinateColor) {
+ this.coordinateColor = coordinateColor;
+ }
+
+ @EditableProperty(name = "Border")
+ public Color getBorderColor() {
+ return borderColor;
+ }
+
+ public void setBorderColor(Color borderColor) {
+ this.borderColor = borderColor;
+ }
+
+ @EditableProperty(name = "Draw Coordinates")
+ public boolean getDrawCoordinates() {
+ // Null protection for older files
+ return drawCoordinates == null || drawCoordinates;
+ }
+
+ public void setDrawCoordinates(boolean drawCoordinates) {
+ this.drawCoordinates = drawCoordinates;
+ }
+
+ @Override
+ public int getControlPointCount() {
+ return controlPoints.length;
+ }
+
+ @Override
+ public Point getControlPoint(int index) {
+ return controlPoints[index];
+ }
+
+ @Override
+ public boolean isControlPointSticky(int index) {
+ return false;
+ }
+
+ @Override
+ public VisibilityPolicy getControlPointVisibilityPolicy(int index) {
+ return VisibilityPolicy.WHEN_SELECTED;
+ }
+
+ @Override
+ public void setControlPoint(Point point, int index) {
+ controlPoints[index].setLocation(point);
+ firstPoint.setLocation(Math.min(controlPoints[0].x, controlPoints[1].x), Math.min(
+ controlPoints[0].y, controlPoints[1].y));
+ secondPoint.setLocation(Math.max(controlPoints[0].x, controlPoints[1].x), Math.max(
+ controlPoints[0].y, controlPoints[1].y));
+ }
+
+ @EditableProperty
+ @Override
+ public String getValue() {
+ return value;
+ }
+
+ @Override
+ public void setValue(String value) {
+ this.value = value;
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/boards/BlankBoard.java b/diylc-library/src/org/diylc/components/boards/BlankBoard.java
new file mode 100644
index 00000000..9c72f0c1
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/boards/BlankBoard.java
@@ -0,0 +1,27 @@
+package org.diylc.components.boards;
+
+import java.awt.Graphics2D;
+
+import org.diylc.core.IDIYComponent;
+import org.diylc.core.annotations.BomPolicy;
+import org.diylc.core.annotations.ComponentDescriptor;
+
+@ComponentDescriptor(name = "Blank Board", category = "Boards", author = "Branislav Stojkovic", zOrder = IDIYComponent.BOARD, instanceNamePrefix = "Board", description = "Blank circuit board", bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME)
+public class BlankBoard extends AbstractBoard {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public void drawIcon(Graphics2D g2d, int width, int height) {
+ g2d.setColor(BOARD_COLOR);
+ g2d.fillRect(2, 2, width - 4, height - 4);
+ g2d.setColor(BORDER_COLOR);
+ g2d.drawRect(2, 2, width - 4, height - 4);
+ }
+
+ @Override
+ public boolean getDrawCoordinates() {
+ // Override to prevent editing.
+ return super.getDrawCoordinates();
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/boards/EyeletBoard.java b/diylc-library/src/org/diylc/components/boards/EyeletBoard.java
new file mode 100644
index 00000000..5977fa85
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/boards/EyeletBoard.java
@@ -0,0 +1,116 @@
+package org.diylc.components.boards;
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.Shape;
+
+import org.diylc.core.ComponentState;
+import org.diylc.core.IDIYComponent;
+import org.diylc.core.IDrawingObserver;
+import org.diylc.core.Project;
+import org.diylc.core.annotations.BomPolicy;
+import org.diylc.core.annotations.ComponentDescriptor;
+import org.diylc.core.annotations.EditableProperty;
+import org.diylc.core.measures.Size;
+import org.diylc.core.measures.SizeUnit;
+import org.diylc.utils.Constants;
+
+@ComponentDescriptor(name = "Eyelet Board", category = "Boards", author = "Branislav Stojkovic", zOrder = IDIYComponent.BOARD, instanceNamePrefix = "Board", description = "Perforated board with eyelets", bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME)
+public class EyeletBoard extends AbstractBoard {
+
+ private static final long serialVersionUID = 1L;
+
+ public static Color BOARD_COLOR = Color.decode("#CCFFCC");
+ public static Color BORDER_COLOR = BOARD_COLOR.darker();
+ public static Color EYELET_COLOR = Color.decode("#C3E4ED");
+
+ public static Size SPACING = new Size(0.5d, SizeUnit.in);
+ public static Size EYELET_SIZE = new Size(0.2d, SizeUnit.in);
+ public static Size HOLE_SIZE = new Size(0.1d, SizeUnit.in);
+
+ // private Area copperArea;
+ protected Size spacing = SPACING;
+ protected Color eyeletColor = EYELET_COLOR;
+
+ public EyeletBoard() {
+ super();
+ this.boardColor = BOARD_COLOR;
+ this.borderColor = BORDER_COLOR;
+ }
+
+ @Override
+ public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode,
+ Project project, IDrawingObserver drawingObserver) {
+ Shape clip = g2d.getClip();
+ if (checkPointsClipped(clip) && !clip.contains(firstPoint.x, secondPoint.y)
+ && !clip.contains(secondPoint.x, firstPoint.y)) {
+ return;
+ }
+ super.draw(g2d, componentState, outlineMode, project, drawingObserver);
+ if (componentState != ComponentState.DRAGGING) {
+ if (alpha < MAX_ALPHA) {
+ g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha
+ / MAX_ALPHA));
+ }
+ Point p = new Point(firstPoint);
+ int diameter = getClosestOdd((int) EYELET_SIZE.convertToPixels());
+ int holeDiameter = getClosestOdd((int) HOLE_SIZE.convertToPixels());
+ int spacing = (int) this.spacing.convertToPixels();
+
+ while (p.y < secondPoint.y - spacing) {
+ p.x = firstPoint.x;
+ p.y += spacing;
+ while (p.x < secondPoint.x - spacing - diameter) {
+ p.x += spacing;
+ g2d.setColor(eyeletColor);
+ g2d.fillOval(p.x - diameter / 2, p.y - diameter / 2, diameter, diameter);
+ g2d.setColor(eyeletColor.darker());
+ g2d.drawOval(p.x - diameter / 2, p.y - diameter / 2, diameter, diameter);
+ g2d.setColor(Constants.CANVAS_COLOR);
+ g2d.fillOval(p.x - holeDiameter / 2, p.y - holeDiameter / 2, holeDiameter,
+ holeDiameter);
+ g2d.setColor(eyeletColor.darker());
+ g2d.drawOval(p.x - holeDiameter / 2, p.y - holeDiameter / 2, holeDiameter,
+ holeDiameter);
+ }
+ }
+ super.drawCoordinates(g2d, spacing);
+ }
+ }
+
+ @EditableProperty(name = "Eyelet color")
+ public Color getEyeletColor() {
+ return eyeletColor;
+ }
+
+ public void setEyeletColor(Color padColor) {
+ this.eyeletColor = padColor;
+ }
+
+ @EditableProperty
+ public Size getSpacing() {
+ return spacing;
+ }
+
+ public void setSpacing(Size spacing) {
+ this.spacing = spacing;
+ }
+
+ @Override
+ public void drawIcon(Graphics2D g2d, int width, int height) {
+ g2d.setColor(BOARD_COLOR);
+ g2d.fillRect(2, 2, width - 4, height - 4);
+ g2d.setColor(BORDER_COLOR);
+ g2d.drawRect(2, 2, width - 4, height - 4);
+ g2d.setColor(EYELET_COLOR);
+ g2d.fillOval(width / 4, width / 4, width / 2, width / 2);
+ g2d.setColor(EYELET_COLOR.darker());
+ g2d.drawOval(width / 4, width / 4, width / 2, width / 2);
+ g2d.setColor(Constants.CANVAS_COLOR);
+ g2d.fillOval(width / 2 - 2, width / 2 - 2, 5, 5);
+ g2d.setColor(EYELET_COLOR.darker());
+ g2d.drawOval(width / 2 - 2, width / 2 - 2, 5, 5);
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/boards/MarshallPerfBoard.java b/diylc-library/src/org/diylc/components/boards/MarshallPerfBoard.java
new file mode 100644
index 00000000..797594b2
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/boards/MarshallPerfBoard.java
@@ -0,0 +1,110 @@
+package org.diylc.components.boards;
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.Shape;
+
+import org.diylc.core.ComponentState;
+import org.diylc.core.IDIYComponent;
+import org.diylc.core.IDrawingObserver;
+import org.diylc.core.Project;
+import org.diylc.core.annotations.BomPolicy;
+import org.diylc.core.annotations.ComponentDescriptor;
+import org.diylc.core.annotations.EditableProperty;
+import org.diylc.core.measures.Size;
+import org.diylc.core.measures.SizeUnit;
+import org.diylc.utils.Constants;
+
+@ComponentDescriptor(name = "Marshall Style Perf Board", category = "Boards", author = "Branislav Stojkovic", zOrder = IDIYComponent.BOARD, instanceNamePrefix = "Board", description = "Perforated board as found on some Marshall and Trainwreck amps", bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME)
+public class MarshallPerfBoard extends AbstractBoard {
+
+ private static final long serialVersionUID = 1L;
+
+ public static Color BOARD_COLOR = Color.decode("#CD8500");
+ public static Color BORDER_COLOR = BOARD_COLOR.darker();
+ public static Size SPACING = new Size(3 / 8d, SizeUnit.in);
+ public static Size HOLE_SIZE = new Size(1 / 8d, SizeUnit.in);
+
+ // private Area copperArea;
+ protected Size spacing = SPACING;
+
+ public MarshallPerfBoard() {
+ super();
+ this.boardColor = BOARD_COLOR;
+ this.borderColor = BORDER_COLOR;
+ }
+
+ @Override
+ public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode,
+ Project project, IDrawingObserver drawingObserver) {
+ Shape clip = g2d.getClip();
+ if (checkPointsClipped(clip) && !clip.contains(firstPoint.x, secondPoint.y)
+ && !clip.contains(secondPoint.x, firstPoint.y)) {
+ return;
+ }
+ super.draw(g2d, componentState, outlineMode, project, drawingObserver);
+ if (componentState != ComponentState.DRAGGING) {
+ if (alpha < MAX_ALPHA) {
+ g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha
+ / MAX_ALPHA));
+ }
+ Point p = new Point(firstPoint);
+ int holeDiameter = getClosestOdd((int) HOLE_SIZE.convertToPixels());
+ int spacing = (int) this.spacing.convertToPixels();
+
+ while (p.y < secondPoint.y - spacing) {
+ p.x = firstPoint.x;
+ p.y += spacing;
+ while (p.x < secondPoint.x - spacing - holeDiameter) {
+ p.x += spacing;
+ g2d.setColor(Constants.CANVAS_COLOR);
+ g2d.fillOval(p.x - holeDiameter / 2, p.y - holeDiameter / 2, holeDiameter,
+ holeDiameter);
+ g2d.setColor(borderColor);
+ g2d.drawOval(p.x - holeDiameter / 2, p.y - holeDiameter / 2, holeDiameter,
+ holeDiameter);
+ }
+ }
+ super.drawCoordinates(g2d, spacing);
+ }
+ }
+
+ @EditableProperty
+ public Size getSpacing() {
+ return spacing;
+ }
+
+ public void setSpacing(Size spacing) {
+ this.spacing = spacing;
+ }
+
+ @Override
+ public void drawIcon(Graphics2D g2d, int width, int height) {
+ g2d.setColor(BOARD_COLOR);
+ g2d.fillRect(2, 2, width - 4, height - 4);
+ g2d.setColor(BORDER_COLOR);
+ g2d.drawRect(2, 2, width - 4, height - 4);
+
+ g2d.setColor(Constants.CANVAS_COLOR);
+ g2d.fillOval(width / 3 - 2, width / 3 - 2, 5, 5);
+ g2d.setColor(BORDER_COLOR);
+ g2d.drawOval(width / 3 - 2, width / 3 - 2, 5, 5);
+
+ g2d.setColor(Constants.CANVAS_COLOR);
+ g2d.fillOval(2 * width / 3 - 2, width / 3 - 2, 5, 5);
+ g2d.setColor(BORDER_COLOR);
+ g2d.drawOval(2 * width / 3 - 2, width / 3 - 2, 5, 5);
+
+ g2d.setColor(Constants.CANVAS_COLOR);
+ g2d.fillOval(width / 3 - 2, 2 * width / 3 - 2, 5, 5);
+ g2d.setColor(BORDER_COLOR);
+ g2d.drawOval(width / 3 - 2, 2 * width / 3 - 2, 5, 5);
+
+ g2d.setColor(Constants.CANVAS_COLOR);
+ g2d.fillOval(2 * width / 3 - 2, 2 * width / 3 - 2, 5, 5);
+ g2d.setColor(BORDER_COLOR);
+ g2d.drawOval(2 * width / 3 - 2, 2 * width / 3 - 2, 5, 5);
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/boards/PerfBoard.java b/diylc-library/src/org/diylc/components/boards/PerfBoard.java
new file mode 100644
index 00000000..c263329e
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/boards/PerfBoard.java
@@ -0,0 +1,108 @@
+package org.diylc.components.boards;
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.Shape;
+
+import org.diylc.core.ComponentState;
+import org.diylc.core.IDIYComponent;
+import org.diylc.core.IDrawingObserver;
+import org.diylc.core.Project;
+import org.diylc.core.annotations.BomPolicy;
+import org.diylc.core.annotations.ComponentDescriptor;
+import org.diylc.core.annotations.EditableProperty;
+import org.diylc.core.measures.Size;
+import org.diylc.core.measures.SizeUnit;
+import org.diylc.utils.Constants;
+
+@ComponentDescriptor(name = "Perf Board w/ Pads", category = "Boards", author = "Branislav Stojkovic", zOrder = IDIYComponent.BOARD, instanceNamePrefix = "Board", description = "Perforated board with solder pads", bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME)
+public class PerfBoard extends AbstractBoard {
+
+ private static final long serialVersionUID = 1L;
+
+ public static Color COPPER_COLOR = Color.decode("#DA8A67");
+
+ public static Size SPACING = new Size(0.1d, SizeUnit.in);
+ public static Size PAD_SIZE = new Size(0.08d, SizeUnit.in);
+ public static Size HOLE_SIZE = new Size(0.7d, SizeUnit.mm);
+
+ // private Area copperArea;
+ protected Size spacing = SPACING;
+ protected Color padColor = COPPER_COLOR;
+
+ @Override
+ public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode,
+ Project project, IDrawingObserver drawingObserver) {
+ Shape clip = g2d.getClip();
+ if (checkPointsClipped(clip) && !clip.contains(firstPoint.x, secondPoint.y)
+ && !clip.contains(secondPoint.x, firstPoint.y)) {
+ return;
+ }
+ super.draw(g2d, componentState, outlineMode, project, drawingObserver);
+ if (componentState != ComponentState.DRAGGING) {
+ if (alpha < MAX_ALPHA) {
+ g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha
+ / MAX_ALPHA));
+ }
+ Point p = new Point(firstPoint);
+ int diameter = getClosestOdd((int) PAD_SIZE.convertToPixels());
+ int holeDiameter = getClosestOdd((int) HOLE_SIZE.convertToPixels());
+ int spacing = (int) this.spacing.convertToPixels();
+
+ while (p.y < secondPoint.y - spacing) {
+ p.x = firstPoint.x;
+ p.y += spacing;
+ while (p.x < secondPoint.x - spacing - diameter) {
+ p.x += spacing;
+ g2d.setColor(padColor);
+ g2d.fillOval(p.x - diameter / 2, p.y - diameter / 2, diameter, diameter);
+ g2d.setColor(padColor.darker());
+ g2d.drawOval(p.x - diameter / 2, p.y - diameter / 2, diameter, diameter);
+ g2d.setColor(Constants.CANVAS_COLOR);
+ g2d.fillOval(p.x - holeDiameter / 2, p.y - holeDiameter / 2, holeDiameter,
+ holeDiameter);
+ g2d.setColor(padColor.darker());
+ g2d.drawOval(p.x - holeDiameter / 2, p.y - holeDiameter / 2, holeDiameter,
+ holeDiameter);
+ }
+ }
+ super.drawCoordinates(g2d, spacing);
+ }
+ }
+
+ @EditableProperty(name = "Pad color")
+ public Color getPadColor() {
+ return padColor;
+ }
+
+ public void setPadColor(Color padColor) {
+ this.padColor = padColor;
+ }
+
+ @EditableProperty
+ public Size getSpacing() {
+ return spacing;
+ }
+
+ public void setSpacing(Size spacing) {
+ this.spacing = spacing;
+ }
+
+ @Override
+ public void drawIcon(Graphics2D g2d, int width, int height) {
+ g2d.setColor(BOARD_COLOR);
+ g2d.fillRect(2, 2, width - 4, height - 4);
+ g2d.setColor(BORDER_COLOR);
+ g2d.drawRect(2, 2, width - 4, height - 4);
+ g2d.setColor(COPPER_COLOR);
+ g2d.fillOval(width / 4, width / 4, width / 2, width / 2);
+ g2d.setColor(COPPER_COLOR.darker());
+ g2d.drawOval(width / 4, width / 4, width / 2, width / 2);
+ g2d.setColor(Constants.CANVAS_COLOR);
+ g2d.fillOval(width / 2 - 2, width / 2 - 2, 5, 5);
+ g2d.setColor(COPPER_COLOR.darker());
+ g2d.drawOval(width / 2 - 2, width / 2 - 2, 5, 5);
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/boards/VeroBoard.java b/diylc-library/src/org/diylc/components/boards/VeroBoard.java
new file mode 100644
index 00000000..8cd2d7a8
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/boards/VeroBoard.java
@@ -0,0 +1,144 @@
+package org.diylc.components.boards;
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Composite;
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.Shape;
+
+import org.diylc.common.OrientationHV;
+import org.diylc.core.ComponentState;
+import org.diylc.core.IDIYComponent;
+import org.diylc.core.IDrawingObserver;
+import org.diylc.core.Project;
+import org.diylc.core.annotations.BomPolicy;
+import org.diylc.core.annotations.ComponentDescriptor;
+import org.diylc.core.annotations.EditableProperty;
+import org.diylc.core.measures.Size;
+import org.diylc.core.measures.SizeUnit;
+import org.diylc.utils.Constants;
+
+@ComponentDescriptor(name = "Vero Board", category = "Boards", author = "Branislav Stojkovic", zOrder = IDIYComponent.BOARD, instanceNamePrefix = "Board", description = "Perforated FR4 board with copper strips connecting all holes in a row", bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME)
+public class VeroBoard extends AbstractBoard {
+
+ private static final long serialVersionUID = 1L;
+
+ public static Color STRIP_COLOR = Color.decode("#DA8A67");
+ public static Color BORDER_COLOR = BOARD_COLOR.darker();
+
+ public static Size SPACING = new Size(0.1d, SizeUnit.in);
+ public static Size STRIP_SIZE = new Size(0.07d, SizeUnit.in);
+ public static Size HOLE_SIZE = new Size(0.7d, SizeUnit.mm);
+
+ protected Size spacing = SPACING;
+ protected Color stripColor = STRIP_COLOR;
+ protected OrientationHV orientation = OrientationHV.HORIZONTAL;
+
+ @Override
+ public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode,
+ Project project, IDrawingObserver drawingObserver) {
+ Shape clip = g2d.getClip();
+ if (checkPointsClipped(clip) && !clip.contains(firstPoint.x, secondPoint.y)
+ && !clip.contains(secondPoint.x, firstPoint.y)) {
+ return;
+ }
+ super.draw(g2d, componentState, outlineMode, project, drawingObserver);
+ if (componentState != ComponentState.DRAGGING) {
+ Composite oldComposite = g2d.getComposite();
+ if (alpha < MAX_ALPHA) {
+ g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha
+ / MAX_ALPHA));
+ }
+ Point p = new Point(firstPoint);
+ int stripSize = getClosestOdd((int) STRIP_SIZE.convertToPixels());
+ int holeSize = getClosestOdd((int) HOLE_SIZE.convertToPixels());
+ int spacing = (int) this.spacing.convertToPixels();
+
+ if (orientation == OrientationHV.HORIZONTAL) {
+ while (p.y < secondPoint.y - spacing) {
+ p.x = firstPoint.x;
+ p.y += spacing;
+ g2d.setColor(stripColor);
+ g2d.fillRect(p.x + spacing / 2, p.y - stripSize / 2, secondPoint.x - spacing
+ - p.x, stripSize);
+ g2d.setColor(stripColor.darker());
+ g2d.drawRect(p.x + spacing / 2, p.y - stripSize / 2, secondPoint.x - spacing
+ - p.x, stripSize);
+ while (p.x < secondPoint.x - spacing - holeSize) {
+ p.x += spacing;
+ g2d.setColor(Constants.CANVAS_COLOR);
+ g2d.fillOval(p.x - holeSize / 2, p.y - holeSize / 2, holeSize, holeSize);
+ g2d.setColor(stripColor.darker());
+ g2d.drawOval(p.x - holeSize / 2, p.y - holeSize / 2, holeSize, holeSize);
+ }
+ }
+ } else {
+ while (p.x < secondPoint.x - spacing) {
+ p.x += spacing;
+ p.y = firstPoint.y;
+ g2d.setColor(stripColor);
+ g2d.fillRect(p.x - stripSize / 2, p.y + spacing / 2, stripSize, secondPoint.y
+ - spacing - p.y);
+ g2d.setColor(stripColor.darker());
+ g2d.drawRect(p.x - stripSize / 2, p.y + spacing / 2, stripSize, secondPoint.y
+ - spacing - p.y);
+ while (p.y < secondPoint.y - spacing - holeSize) {
+ p.y += spacing;
+ g2d.setColor(Constants.CANVAS_COLOR);
+ g2d.fillOval(p.x - holeSize / 2, p.y - holeSize / 2, holeSize, holeSize);
+ g2d.setColor(stripColor.darker());
+ g2d.drawOval(p.x - holeSize / 2, p.y - holeSize / 2, holeSize, holeSize);
+ }
+ }
+ }
+ g2d.setComposite(oldComposite);
+ super.drawCoordinates(g2d, spacing);
+ }
+ }
+
+ @EditableProperty(name = "Strip color")
+ public Color getStripColor() {
+ return stripColor;
+ }
+
+ public void setStripColor(Color padColor) {
+ this.stripColor = padColor;
+ }
+
+ @EditableProperty
+ public Size getSpacing() {
+ return spacing;
+ }
+
+ public void setSpacing(Size spacing) {
+ this.spacing = spacing;
+ }
+
+ @EditableProperty
+ public OrientationHV getOrientation() {
+ return orientation;
+ }
+
+ public void setOrientation(OrientationHV orientation) {
+ this.orientation = orientation;
+ }
+
+ @Override
+ public void drawIcon(Graphics2D g2d, int width, int height) {
+ g2d.setColor(BOARD_COLOR);
+ g2d.fillRect(2, 2, width - 4, height - 4);
+ g2d.setColor(BORDER_COLOR);
+ g2d.drawRect(2, 2, width - 4, height - 4);
+ g2d.setColor(STRIP_COLOR);
+ g2d.fillRect(4, width / 4, width - 8, width / 2);
+ g2d.setColor(STRIP_COLOR.darker());
+ g2d.drawRect(4, width / 4, width - 8, width / 2);
+ g2d.setColor(Constants.CANVAS_COLOR);
+ g2d.fillOval(width / 3 - 2, width / 2 - 2, 5, 5);
+ g2d.fillOval(2 * width / 3 - 2, width / 2 - 2, 5, 5);
+ g2d.setColor(STRIP_COLOR.darker());
+ g2d.drawOval(width / 3 - 2, width / 2 - 2, 5, 5);
+ g2d.drawOval(2 * width / 3 - 2, width / 2 - 2, 5, 5);
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/connectivity/AWG.java b/diylc-library/src/org/diylc/components/connectivity/AWG.java
new file mode 100644
index 00000000..d1edc701
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/connectivity/AWG.java
@@ -0,0 +1,15 @@
+package org.diylc.components.connectivity;
+
+public enum AWG {
+
+ _8, _10, _12, _14, _16, _18, _20, _22, _24, _26, _28, _30;
+
+ @Override
+ public String toString() {
+ return "#" + name().replace("_", "");
+ }
+
+ public int getValue() {
+ return Integer.parseInt(name().replace("_", ""));
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/connectivity/CopperTrace.java b/diylc-library/src/org/diylc/components/connectivity/CopperTrace.java
new file mode 100644
index 00000000..52f50f27
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/connectivity/CopperTrace.java
@@ -0,0 +1,123 @@
+package org.diylc.components.connectivity;
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Shape;
+
+import org.diylc.common.ObjectCache;
+import org.diylc.components.AbstractLeadedComponent;
+import org.diylc.core.ComponentState;
+import org.diylc.core.CreationMethod;
+import org.diylc.core.IDIYComponent;
+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.measures.Size;
+import org.diylc.core.measures.SizeUnit;
+
+@ComponentDescriptor(name = "Copper Trace", author = "Branislav Stojkovic", category = "Connectivity", creationMethod = CreationMethod.POINT_BY_POINT, instanceNamePrefix = "Trace", description = "Straight copper trace", zOrder = IDIYComponent.TRACE, bomPolicy = BomPolicy.NEVER_SHOW)
+public class CopperTrace extends AbstractLeadedComponent {
+
+ private static final long serialVersionUID = 1L;
+
+ public static Size THICKNESS = new Size(1d, SizeUnit.mm);
+ public static Color COLOR = Color.black;
+
+ private Color leadColor = COLOR;
+ private Size thickness = THICKNESS;
+
+ @Override
+ public void drawIcon(Graphics2D g2d, int width, int height) {
+ g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(3));
+ g2d.setColor(COLOR);
+ g2d.drawLine(1, height - 2, width - 2, 1);
+ }
+
+ @Override
+ protected Color getLeadColor(ComponentState componentState) {
+ return componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? SELECTION_COLOR : leadColor;
+ }
+
+ @Override
+ public VisibilityPolicy getControlPointVisibilityPolicy(int index) {
+ return VisibilityPolicy.WHEN_SELECTED;
+ }
+
+ @EditableProperty(name = "Color")
+ public Color getLeadColor() {
+ return leadColor;
+ }
+
+ public void setLeadColor(Color leadColor) {
+ this.leadColor = leadColor;
+ }
+
+ @EditableProperty(name = "Width")
+ public Size getThickness() {
+ return thickness;
+ }
+
+ public void setThickness(Size thickness) {
+ this.thickness = thickness;
+ }
+
+ @Override
+ protected int getLeadThickness() {
+ return (int) getThickness().convertToPixels();
+ }
+
+ @Override
+ protected boolean shouldShadeLeads() {
+ return false;
+ }
+
+ public Color getBodyColor() {
+ return super.getBodyColor();
+ }
+
+ @Override
+ public Color getBorderColor() {
+ return super.getBorderColor();
+ }
+
+ @Override
+ public Byte getAlpha() {
+ return super.getAlpha();
+ }
+
+ @Override
+ public Size getLength() {
+ return super.getLength();
+ }
+
+ @Override
+ public Size getWidth() {
+ return super.getWidth();
+ }
+
+ @Override
+ public Void getValue() {
+ return null;
+ }
+
+ @Override
+ public void setValue(Void value) {
+ }
+
+ @Override
+ protected Shape getBodyShape() {
+ return null;
+ }
+
+ @Override
+ protected Size getDefaultWidth() {
+ return null;
+ }
+
+ @Override
+ protected Size getDefaultLength() {
+ return null;
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/connectivity/CurvedTrace.java b/diylc-library/src/org/diylc/components/connectivity/CurvedTrace.java
new file mode 100644
index 00000000..282ad685
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/connectivity/CurvedTrace.java
@@ -0,0 +1,69 @@
+package org.diylc.components.connectivity;
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.geom.CubicCurve2D;
+
+import org.diylc.common.ObjectCache;
+import org.diylc.components.AbstractCurvedComponent;
+import org.diylc.core.ComponentState;
+import org.diylc.core.IDIYComponent;
+import org.diylc.core.annotations.BomPolicy;
+import org.diylc.core.annotations.ComponentDescriptor;
+import org.diylc.core.annotations.EditableProperty;
+import org.diylc.core.measures.Size;
+import org.diylc.core.measures.SizeUnit;
+
+@ComponentDescriptor(name = "Curved Trace", author = "Branislav Stojkovic", category = "Connectivity", instanceNamePrefix = "Trace", description = "Curved copper trace with two control points", zOrder = IDIYComponent.TRACE, bomPolicy = BomPolicy.NEVER_SHOW)
+public class CurvedTrace extends AbstractCurvedComponent {
+
+ private static final long serialVersionUID = 1L;
+
+ public static Color COLOR = Color.black;
+ public static Size SIZE = new Size(1d, SizeUnit.mm);
+
+ protected Size size = SIZE;
+
+ @Override
+ protected Color getDefaultColor() {
+ return COLOR;
+ }
+
+ @Override
+ protected void drawCurve(CubicCurve2D curve, Graphics2D g2d, ComponentState componentState) {
+ int thickness = (int) size.convertToPixels();
+ g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(thickness));
+ Color curveColor = componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? SELECTION_COLOR : color;
+ g2d.setColor(curveColor);
+ g2d.draw(curve);
+ }
+
+ @EditableProperty(name = "Width")
+ public Size getThickness() {
+ return size;
+ }
+
+ public void setThickness(Size size) {
+ this.size = size;
+ }
+
+ @Override
+ public Void getValue() {
+ return null;
+ }
+
+ @Override
+ public void setValue(Void value) {
+ }
+
+ @Override
+ public Byte getAlpha() {
+ return super.getAlpha();
+ }
+
+ @Override
+ public void setAlpha(Byte alpha) {
+ super.setAlpha(alpha);
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/connectivity/Eyelet.java b/diylc-library/src/org/diylc/components/connectivity/Eyelet.java
new file mode 100644
index 00000000..4ef45eca
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/connectivity/Eyelet.java
@@ -0,0 +1,140 @@
+package org.diylc.components.connectivity;
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Point;
+
+import org.diylc.components.AbstractComponent;
+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.measures.Size;
+import org.diylc.core.measures.SizeUnit;
+import org.diylc.utils.Constants;
+
+@ComponentDescriptor(name = "Eyelet", category = "Connectivity", author = "Branislav Stojkovic", description = "Eyelet or turret terminal", instanceNamePrefix = "Eyelet", stretchable = false, zOrder = IDIYComponent.TRACE + 0.1, bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME)
+public class Eyelet extends AbstractComponent {
+
+ private static final long serialVersionUID = 1L;
+
+ public static Size SIZE = new Size(0.2d, SizeUnit.in);
+ public static Size HOLE_SIZE = new Size(0.1d, SizeUnit.in);
+ public static Color COLOR = Color.decode("#C3E4ED");
+
+ private Size size = SIZE;
+ private Size holeSize = HOLE_SIZE;
+ private Color color = COLOR;
+ private Point point = new Point(0, 0);
+ private String value = "";
+
+ @Override
+ public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode,
+ Project project, IDrawingObserver drawingObserver) {
+ if (checkPointsClipped(g2d.getClip())) {
+ return;
+ }
+ int diameter = getClosestOdd((int) size.convertToPixels());
+ int holeDiameter = getClosestOdd((int) holeSize.convertToPixels());
+ g2d.setColor(color);
+ g2d.fillOval(point.x - diameter / 2, point.y - diameter / 2, diameter, diameter);
+ g2d.setColor(componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? SELECTION_COLOR : color.darker());
+ g2d.drawOval(point.x - diameter / 2, point.y - diameter / 2, diameter, diameter);
+ g2d.setColor(Constants.CANVAS_COLOR);
+ g2d.fillOval(point.x - holeDiameter / 2, point.y - holeDiameter / 2, holeDiameter,
+ holeDiameter);
+ g2d.setColor(componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? SELECTION_COLOR : color.darker());
+ g2d.drawOval(point.x - holeDiameter / 2, point.y - holeDiameter / 2, holeDiameter,
+ holeDiameter);
+ }
+
+ @Override
+ public void drawIcon(Graphics2D g2d, int width, int height) {
+ int diameter = getClosestOdd(width / 2);
+ int holeDiameter = 5;
+ g2d.setColor(COLOR);
+ g2d.fillOval((width - diameter) / 2, (height - diameter) / 2, diameter, diameter);
+ g2d.setColor(COLOR.darker());
+ g2d.drawOval((width - diameter) / 2, (height - diameter) / 2, diameter, diameter);
+ g2d.setColor(Constants.CANVAS_COLOR);
+ g2d.fillOval((width - holeDiameter) / 2, (height - holeDiameter) / 2, holeDiameter,
+ holeDiameter);
+ g2d.setColor(COLOR.darker());
+ g2d.drawOval((width - holeDiameter) / 2, (height - holeDiameter) / 2, holeDiameter,
+ holeDiameter);
+ }
+
+ @EditableProperty
+ public Size getSize() {
+ return size;
+ }
+
+ public void setSize(Size size) {
+ this.size = size;
+ }
+
+ @EditableProperty(name = "Hole size")
+ public Size getHoleSize() {
+ return holeSize;
+ }
+
+ public void setHoleSize(Size holeSize) {
+ this.holeSize = holeSize;
+ }
+
+ @Override
+ public String getName() {
+ return super.getName();
+ }
+
+ @Override
+ public int getControlPointCount() {
+ return 1;
+ }
+
+ @Override
+ public boolean isControlPointSticky(int index) {
+ return true;
+ }
+
+ @Override
+ public VisibilityPolicy getControlPointVisibilityPolicy(int index) {
+ return VisibilityPolicy.NEVER;
+ }
+
+ @Override
+ public Point getControlPoint(int index) {
+ return point;
+ }
+
+ @Override
+ public void setControlPoint(Point point, int index) {
+ this.point.setLocation(point);
+ }
+
+ @EditableProperty(name = "Color")
+ public Color getColor() {
+ return color;
+ }
+
+ public void setColor(Color color) {
+ this.color = color;
+ }
+
+ @Override
+ @EditableProperty
+ public String getValue() {
+ return value;
+ }
+
+ @Override
+ public void setValue(String value) {
+ this.value = value;
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/connectivity/HookupWire.java b/diylc-library/src/org/diylc/components/connectivity/HookupWire.java
new file mode 100644
index 00000000..1bd235af
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/connectivity/HookupWire.java
@@ -0,0 +1,68 @@
+package org.diylc.components.connectivity;
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.geom.CubicCurve2D;
+
+import org.diylc.common.ObjectCache;
+import org.diylc.components.AbstractCurvedComponent;
+import org.diylc.core.ComponentState;
+import org.diylc.core.IDIYComponent;
+import org.diylc.core.annotations.BomPolicy;
+import org.diylc.core.annotations.ComponentDescriptor;
+import org.diylc.core.annotations.EditableProperty;
+import org.diylc.utils.Constants;
+
+@ComponentDescriptor(name = "Hookup Wire", author = "Branislav Stojkovic", category = "Connectivity", instanceNamePrefix = "W", description = "Flexible wire with two control points", zOrder = IDIYComponent.COMPONENT, bomPolicy = BomPolicy.NEVER_SHOW)
+public class HookupWire extends AbstractCurvedComponent {
+
+ private static final long serialVersionUID = 1L;
+
+ public static Color COLOR = Color.green;
+ public static double INSULATION_THICKNESS_PCT = 0.3;
+
+ protected AWG gauge = AWG._22;
+
+ @Override
+ protected Color getDefaultColor() {
+ return COLOR;
+ }
+
+ @Override
+ protected void drawCurve(CubicCurve2D curve, Graphics2D g2d,
+ ComponentState componentState) {
+ int thickness = (int) (Math.pow(Math.E, -1.12436 - 0.11594
+ * gauge.getValue())
+ * Constants.PIXELS_PER_INCH * (1 + 2 * INSULATION_THICKNESS_PCT));
+ Color curveColor = componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? SELECTION_COLOR
+ : color.darker();
+ g2d.setColor(curveColor);
+ g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(thickness));
+ g2d.draw(curve);
+ if (componentState == ComponentState.NORMAL) {
+ g2d.setColor(color);
+ g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(
+ thickness - 2));
+ g2d.draw(curve);
+ }
+ }
+
+ @EditableProperty(name = "AWG")
+ public AWG getGauge() {
+ return gauge;
+ }
+
+ public void setGauge(AWG gauge) {
+ this.gauge = gauge;
+ }
+
+ @Override
+ public Void getValue() {
+ return null;
+ }
+
+ @Override
+ public void setValue(Void value) {
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/connectivity/Jumper.java b/diylc-library/src/org/diylc/components/connectivity/Jumper.java
new file mode 100644
index 00000000..7350179e
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/connectivity/Jumper.java
@@ -0,0 +1,98 @@
+package org.diylc.components.connectivity;
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Shape;
+
+import org.diylc.common.ObjectCache;
+import org.diylc.components.AbstractLeadedComponent;
+import org.diylc.core.ComponentState;
+import org.diylc.core.CreationMethod;
+import org.diylc.core.IDIYComponent;
+import org.diylc.core.annotations.BomPolicy;
+import org.diylc.core.annotations.ComponentDescriptor;
+import org.diylc.core.annotations.EditableProperty;
+import org.diylc.core.measures.Size;
+
+@ComponentDescriptor(name = "Jumper", author = "Branislav Stojkovic", category = "Connectivity", creationMethod = CreationMethod.POINT_BY_POINT, instanceNamePrefix = "J", description = "", zOrder = IDIYComponent.COMPONENT, bomPolicy = BomPolicy.NEVER_SHOW)
+public class Jumper extends AbstractLeadedComponent {
+
+ private static final long serialVersionUID = 1L;
+
+ public static Color COLOR = Color.blue;
+
+ private Color color = COLOR;
+
+ @Override
+ public void drawIcon(Graphics2D g2d, int width, int height) {
+ g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(3));
+ g2d.setColor(COLOR.darker());
+ g2d.drawLine(1, height - 2, width - 2, 1);
+ g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
+ g2d.setColor(COLOR);
+ g2d.drawLine(1, height - 2, width - 2, 1);
+ }
+
+ @Override
+ public Color getLeadColor(ComponentState componentState) {
+ return componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? SELECTION_COLOR : color;
+ }
+
+ @EditableProperty
+ public Color getColor() {
+ return color;
+ }
+
+ public void setColor(Color color) {
+ this.color = color;
+ }
+
+ public Color getBodyColor() {
+ return super.getBodyColor();
+ }
+
+ @Override
+ public Color getBorderColor() {
+ return super.getBorderColor();
+ }
+
+ @Override
+ public Byte getAlpha() {
+ return super.getAlpha();
+ }
+
+ @Override
+ public Size getLength() {
+ return super.getLength();
+ }
+
+ @Override
+ public Size getWidth() {
+ return super.getWidth();
+ }
+
+ @Override
+ public Void getValue() {
+ return null;
+ }
+
+ @Override
+ public void setValue(Void value) {
+ }
+
+ @Override
+ protected Shape getBodyShape() {
+ return null;
+ }
+
+ @Override
+ protected Size getDefaultWidth() {
+ return null;
+ }
+
+ @Override
+ protected Size getDefaultLength() {
+ return null;
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/connectivity/Line.java b/diylc-library/src/org/diylc/components/connectivity/Line.java
new file mode 100644
index 00000000..001efcd4
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/connectivity/Line.java
@@ -0,0 +1,106 @@
+package org.diylc.components.connectivity;
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Shape;
+
+import org.diylc.common.ObjectCache;
+import org.diylc.components.AbstractLeadedComponent;
+import org.diylc.core.ComponentState;
+import org.diylc.core.CreationMethod;
+import org.diylc.core.IDIYComponent;
+import org.diylc.core.IDrawingObserver;
+import org.diylc.core.Project;
+import org.diylc.core.annotations.BomPolicy;
+import org.diylc.core.annotations.ComponentDescriptor;
+import org.diylc.core.annotations.EditableProperty;
+import org.diylc.core.measures.Size;
+
+@ComponentDescriptor(name = "Line", author = "Branislav Stojkovic", category = "Connectivity", creationMethod = CreationMethod.POINT_BY_POINT, instanceNamePrefix = "LN", description = "", zOrder = IDIYComponent.COMPONENT, bomPolicy = BomPolicy.NEVER_SHOW)
+public class Line extends AbstractLeadedComponent {
+
+ private static final long serialVersionUID = 1L;
+
+ public static Color COLOR = Color.black;
+
+ private Color color = COLOR;
+
+ @Override
+ public void drawIcon(Graphics2D g2d, int width, int height) {
+ g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
+ g2d.setColor(COLOR);
+ g2d.drawLine(1, height - 2, width - 2, 1);
+ }
+
+ @Override
+ public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode,
+ Project project, IDrawingObserver drawingObserver) {
+ g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
+ g2d.setColor(color);
+ g2d.drawLine(getControlPoint(0).x, getControlPoint(0).y, getControlPoint(1).x,
+ getControlPoint(1).y);
+ }
+
+ @Override
+ public Color getLeadColor(ComponentState componentState) {
+ return componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? SELECTION_COLOR : color;
+ }
+
+ @EditableProperty
+ public Color getColor() {
+ return color;
+ }
+
+ public void setColor(Color color) {
+ this.color = color;
+ }
+
+ public Color getBodyColor() {
+ return super.getBodyColor();
+ }
+
+ @Override
+ public Color getBorderColor() {
+ return super.getBorderColor();
+ }
+
+ @Override
+ public Byte getAlpha() {
+ return super.getAlpha();
+ }
+
+ @Override
+ public Size getLength() {
+ return super.getLength();
+ }
+
+ @Override
+ public Size getWidth() {
+ return super.getWidth();
+ }
+
+ @Override
+ public Void getValue() {
+ return null;
+ }
+
+ @Override
+ public void setValue(Void value) {
+ }
+
+ @Override
+ protected Shape getBodyShape() {
+ return null;
+ }
+
+ @Override
+ protected Size getDefaultWidth() {
+ return null;
+ }
+
+ @Override
+ protected Size getDefaultLength() {
+ return null;
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/connectivity/SolderPad.java b/diylc-library/src/org/diylc/components/connectivity/SolderPad.java
new file mode 100644
index 00000000..a4cbd0f4
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/connectivity/SolderPad.java
@@ -0,0 +1,152 @@
+package org.diylc.components.connectivity;
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Point;
+
+import org.diylc.components.AbstractComponent;
+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.measures.Size;
+import org.diylc.core.measures.SizeUnit;
+import org.diylc.utils.Constants;
+
+@ComponentDescriptor(name = "Solder Pad", category = "Connectivity", author = "Branislav Stojkovic", description = "test", instanceNamePrefix = "Pad", stretchable = false, zOrder = IDIYComponent.TRACE + 0.1, bomPolicy = BomPolicy.NEVER_SHOW)
+public class SolderPad extends AbstractComponent {
+
+ private static final long serialVersionUID = 1L;
+
+ public static Size SIZE = new Size(0.09d, SizeUnit.in);
+ public static Size HOLE_SIZE = new Size(0.8d, SizeUnit.mm);
+ public static Color COLOR = Color.black;
+
+ private Size size = SIZE;
+ private Color color = COLOR;
+ private Point point = new Point(0, 0);
+ private Type type = Type.ROUND;
+ private Size holeSize = HOLE_SIZE;
+
+ @Override
+ public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode,
+ Project project, IDrawingObserver drawingObserver) {
+ if (checkPointsClipped(g2d.getClip())) {
+ return;
+ }
+ int diameter = getClosestOdd((int) getSize().convertToPixels());
+ int holeDiameter = getClosestOdd((int) getHoleSize().convertToPixels());
+ g2d.setColor(componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? SELECTION_COLOR : color);
+ if (type == Type.ROUND) {
+ g2d.fillOval(point.x - diameter / 2, point.y - diameter / 2, diameter, diameter);
+ } else {
+ g2d.fillRect(point.x - diameter / 2, point.y - diameter / 2, diameter, diameter);
+ }
+ g2d.setColor(Constants.CANVAS_COLOR);
+ g2d.fillOval(point.x - holeDiameter / 2, point.y - holeDiameter / 2, holeDiameter,
+ holeDiameter);
+ }
+
+ @Override
+ public void drawIcon(Graphics2D g2d, int width, int height) {
+ int diameter = getClosestOdd(width / 2);
+ int holeDiameter = 5;
+ g2d.setColor(COLOR);
+ g2d.fillOval((width - diameter) / 2, (height - diameter) / 2, diameter, diameter);
+ g2d.setColor(Constants.CANVAS_COLOR);
+ g2d.fillOval((width - holeDiameter) / 2, (height - holeDiameter) / 2, holeDiameter,
+ holeDiameter);
+ }
+
+ @EditableProperty
+ public Size getSize() {
+ return size;
+ }
+
+ public void setSize(Size size) {
+ this.size = size;
+ }
+
+ @EditableProperty(name = "Hole")
+ public Size getHoleSize() {
+ if (holeSize == null) {
+ holeSize = HOLE_SIZE;
+ }
+ return holeSize;
+ }
+
+ public void setHoleSize(Size holeSize) {
+ this.holeSize = holeSize;
+ }
+
+ @Override
+ public String getName() {
+ return super.getName();
+ }
+
+ @Override
+ public int getControlPointCount() {
+ return 1;
+ }
+
+ @Override
+ public boolean isControlPointSticky(int index) {
+ return true;
+ }
+
+ @Override
+ public VisibilityPolicy getControlPointVisibilityPolicy(int index) {
+ return VisibilityPolicy.NEVER;
+ }
+
+ @Override
+ public Point getControlPoint(int index) {
+ return point;
+ }
+
+ @Override
+ public void setControlPoint(Point point, int index) {
+ this.point.setLocation(point);
+ }
+
+ @EditableProperty(name = "Color")
+ public Color getColor() {
+ return color;
+ }
+
+ public void setColor(Color color) {
+ this.color = color;
+ }
+
+ @EditableProperty
+ public Type getType() {
+ return type;
+ }
+
+ public void setType(Type type) {
+ this.type = type;
+ }
+
+ @Override
+ public Void getValue() {
+ return null;
+ }
+
+ @Override
+ public void setValue(Void value) {
+ }
+
+ static enum Type {
+ ROUND, SQUARE;
+
+ @Override
+ public String toString() {
+ return name().substring(0, 1) + name().substring(1).toLowerCase();
+ }
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/connectivity/TraceCut.java b/diylc-library/src/org/diylc/components/connectivity/TraceCut.java
new file mode 100644
index 00000000..16ab1694
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/connectivity/TraceCut.java
@@ -0,0 +1,133 @@
+package org.diylc.components.connectivity;
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Point;
+
+import org.diylc.common.ObjectCache;
+import org.diylc.components.AbstractComponent;
+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.measures.Size;
+import org.diylc.core.measures.SizeUnit;
+
+@ComponentDescriptor(name = "Trace Cut", category = "Connectivity", author = "Branislav Stojkovic", description = "Designates the place where a trace on the vero board needs to be cut", instanceNamePrefix = "Cut", stretchable = false, zOrder = IDIYComponent.BOARD + 1, bomPolicy = BomPolicy.NEVER_SHOW)
+public class TraceCut extends AbstractComponent {
+
+ private static final long serialVersionUID = 1L;
+
+ public static Size SIZE = new Size(0.07d, SizeUnit.in);
+ public static Color FILL_COLOR = Color.white;
+ public static Color BORDER_COLOR = Color.red;
+ public static Color SELECTION_COLOR = Color.blue;
+
+ private Size size = SIZE;
+ private Color fillColor = FILL_COLOR;
+ private Color borderColor = BORDER_COLOR;
+
+ protected Point point = new Point(0, 0);
+
+ @Override
+ public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode,
+ Project project, IDrawingObserver drawingObserver) {
+ if (checkPointsClipped(g2d.getClip())) {
+ return;
+ }
+ int size = getClosestOdd((int) this.size.convertToPixels());
+ int dotDiameter = size - 6;
+ g2d.setColor(fillColor);
+ g2d.fillRect(point.x - size / 2, point.y - size / 2, size, size);
+ g2d.setColor(componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? SELECTION_COLOR : borderColor);
+ g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
+ g2d.drawRect(point.x - size / 2, point.y - size / 2, size, size);
+ g2d
+ .fillOval(point.x - dotDiameter / 2, point.y - dotDiameter / 2, dotDiameter,
+ dotDiameter);
+ }
+
+ @Override
+ public void drawIcon(Graphics2D g2d, int width, int height) {
+ int size = 16;
+ int dotDiameter = size / 4 * 2;
+ g2d.setColor(FILL_COLOR);
+ g2d.fillRect((width - size) / 2, (height - size) / 2, size, size);
+ g2d.setColor(BORDER_COLOR);
+ g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
+ g2d.drawRect((width - size) / 2, (height - size) / 2, size, size);
+ g2d.fillOval((width - dotDiameter) / 2, (height - dotDiameter) / 2, dotDiameter,
+ dotDiameter);
+ }
+
+ @Override
+ public int getControlPointCount() {
+ return 1;
+ }
+
+ @Override
+ public Point getControlPoint(int index) {
+ return point;
+ }
+
+ @Override
+ public boolean isControlPointSticky(int index) {
+ return false;
+ }
+
+ @Override
+ public VisibilityPolicy getControlPointVisibilityPolicy(int index) {
+ return VisibilityPolicy.NEVER;
+ }
+
+ @Override
+ public void setControlPoint(Point point, int index) {
+ this.point.setLocation(point);
+ }
+
+ @Override
+ public Void getValue() {
+ return null;
+ }
+
+ @Override
+ public void setValue(Void value) {
+ }
+
+ @EditableProperty
+ public Size getSize() {
+ return size;
+ }
+
+ public void setSize(Size size) {
+ this.size = size;
+ }
+
+ @EditableProperty(name = "Fill")
+ public Color getFillColor() {
+ return fillColor;
+ }
+
+ public void setFillColor(Color fillColor) {
+ this.fillColor = fillColor;
+ }
+
+ @EditableProperty(name = "Border")
+ public Color getBorderColor() {
+ return borderColor;
+ }
+
+ public void setBorderColor(Color borderColor) {
+ this.borderColor = borderColor;
+ }
+
+ @Override
+ public String getName() {
+ return super.getName();
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/electromechanical/BatterySnap9V.java b/diylc-library/src/org/diylc/components/electromechanical/BatterySnap9V.java
new file mode 100644
index 00000000..461c3c0a
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/electromechanical/BatterySnap9V.java
@@ -0,0 +1,252 @@
+package org.diylc.components.electromechanical;
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Composite;
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.Polygon;
+import java.awt.Shape;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Area;
+import java.awt.geom.Ellipse2D;
+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.Orientation;
+import org.diylc.components.AbstractTransparentComponent;
+import org.diylc.core.ComponentState;
+import org.diylc.core.IDIYComponent;
+import org.diylc.core.IDrawingObserver;
+import org.diylc.core.Project;
+import org.diylc.core.Theme;
+import org.diylc.core.VisibilityPolicy;
+import org.diylc.core.annotations.ComponentDescriptor;
+import org.diylc.core.annotations.EditableProperty;
+import org.diylc.core.measures.Size;
+import org.diylc.core.measures.SizeUnit;
+import org.diylc.utils.Constants;
+
+@ComponentDescriptor(name = "9V Battery Snap", category = "Electromechanical", author = "Branislav Stojkovic", description = "", stretchable = false, zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "BTR")
+public class BatterySnap9V extends AbstractTransparentComponent {
+
+ private static final long serialVersionUID = 1L;
+
+ private static Color BODY_COLOR = Color.darkGray;
+ private static Size WIDTH = new Size(0.5d, SizeUnit.in);
+ private static Size LENGTH = new Size(0.75d, SizeUnit.in);
+ private static Size TERMINAL_DIAMETER = new Size(0.3d, SizeUnit.in);
+ private static Size TERMINAL_SPACING = new Size(0.5d, SizeUnit.in);
+ private static Size TERMINAL_BORDER = new Size(0.7d, SizeUnit.mm);
+
+ private String value = "";
+ private Point controlPoint = new Point(0, 0);
+ transient Shape[] body;
+ private Orientation orientation = Orientation.DEFAULT;
+ private Color color = BODY_COLOR;
+
+ @Override
+ public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode,
+ Project project, IDrawingObserver drawingObserver) {
+ Shape[] body = getBody();
+
+ g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
+ if (componentState != ComponentState.DRAGGING) {
+ Composite oldComposite = g2d.getComposite();
+ if (alpha < MAX_ALPHA) {
+ g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha
+ / MAX_ALPHA));
+ }
+ g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : color);
+ g2d.fill(body[0]);
+ g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : METAL_COLOR);
+ g2d.fill(body[1]);
+ g2d.fill(body[2]);
+ g2d.setComposite(oldComposite);
+ }
+
+ Color finalBorderColor;
+ if (outlineMode) {
+ Theme theme = (Theme) ConfigurationManager.getInstance().readObject(
+ IPlugInPort.THEME_KEY, Constants.DEFAULT_THEME);
+ finalBorderColor = componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? SELECTION_COLOR : theme
+ .getOutlineColor();
+ } else {
+ finalBorderColor = componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? SELECTION_COLOR : color
+ .brighter();
+ }
+
+ g2d.setColor(finalBorderColor);
+ g2d.draw(body[0]);
+ g2d.setColor(METAL_COLOR.darker());
+ g2d.draw(body[1]);
+ g2d.setColor(METAL_COLOR.darker());
+ g2d.draw(body[2]);
+ }
+
+ public Shape[] getBody() {
+ if (body == null) {
+ body = new Shape[3];
+
+ int x = controlPoint.x;
+ int y = controlPoint.y;
+ int width = (int) WIDTH.convertToPixels();
+ int length = (int) LENGTH.convertToPixels();
+ int totalLength = length + width / 2;
+ int terminalDiameter = (int) TERMINAL_DIAMETER.convertToPixels();
+ int terminalSpacing = (int) TERMINAL_SPACING.convertToPixels();
+ int terminalBorder = (int) TERMINAL_BORDER.convertToPixels();
+
+ Area mainArea = new Area(new Rectangle2D.Double(x, y - width / 2, length, width));
+ mainArea.add(new Area(new Ellipse2D.Double(x + length - width / 2, y - width / 2,
+ width, width)));
+
+ body[0] = mainArea;
+
+ Area terminalArea = new Area(new Ellipse2D.Double(x + (totalLength - terminalSpacing)
+ / 2 - terminalDiameter / 2, y - terminalDiameter / 2, terminalDiameter,
+ terminalDiameter));
+
+ int centerX = x + (totalLength + terminalSpacing) / 2;
+ int[] terminalX = new int[6];
+ int[] terminalY = new int[6];
+
+ for (int i = 0; i < 6; i++) {
+ terminalX[i] = (int) (centerX + Math.cos(Math.PI / 3 * i) * terminalDiameter / 2);
+ terminalY[i] = (int) (y + Math.sin(Math.PI / 3 * i) * terminalDiameter / 2);
+ }
+ terminalArea.add(new Area(new Polygon(terminalX, terminalY, 6)));
+
+ body[1] = terminalArea;
+
+ terminalArea = new Area(new Ellipse2D.Double(x + (totalLength - terminalSpacing) / 2
+ - terminalDiameter / 2 + terminalBorder, y - terminalDiameter / 2
+ + terminalBorder, terminalDiameter - 2 * terminalBorder, terminalDiameter - 2
+ * terminalBorder));
+
+ for (int i = 0; i < 6; i++) {
+ terminalX[i] = (int) (centerX + Math.cos(Math.PI / 3 * i)
+ * (terminalDiameter / 2 + terminalBorder));
+ terminalY[i] = (int) (y + Math.sin(Math.PI / 3 * i)
+ * (terminalDiameter / 2 + terminalBorder));
+ }
+ terminalArea.add(new Area(new Polygon(terminalX, terminalY, 6)));
+
+ body[2] = terminalArea;
+
+ // Rotate if needed
+ if (orientation != Orientation.DEFAULT) {
+ double theta = 0;
+ switch (orientation) {
+ case _90:
+ theta = Math.PI / 2;
+ break;
+ case _180:
+ theta = Math.PI;
+ break;
+ case _270:
+ theta = Math.PI * 3 / 2;
+ break;
+ }
+ AffineTransform rotation = AffineTransform.getRotateInstance(theta, x, y);
+ for (Shape shape : body) {
+ Area area = (Area) shape;
+ area.transform(rotation);
+ }
+ }
+ }
+ return body;
+ }
+
+ @Override
+ public void drawIcon(Graphics2D g2d, int width, int height) {
+ g2d.setColor(BODY_COLOR);
+ g2d.fillOval(width / 4, width / 32, width / 2, width / 2);
+ g2d.setColor(BODY_COLOR.darker());
+ g2d.drawOval(width / 4, width / 32, width / 2, width / 2);
+ g2d.setColor(BODY_COLOR);
+ g2d.fillRect(width / 4, width / 32 + width / 4, width / 2, width * 3 / 4 - 2 * width / 32);
+ g2d.setColor(BODY_COLOR.darker());
+ g2d.drawRect(width / 4, width / 32 + width / 4, width / 2, width * 3 / 4 - 2 * width / 32);
+ g2d.setColor(METAL_COLOR);
+ g2d.fillOval(width / 4 + 3 * width / 32, width / 32 + 3 * width / 32, width / 2 - 6 * width
+ / 32, width / 2 - 6 * width / 32);
+
+ int centerX = width / 2;
+ int centerY = height * 7 / 9;
+ int terminalDiameter = width / 2 - 4 * width / 32;
+ int[] terminalX = new int[6];
+ int[] terminalY = new int[6];
+
+ for (int i = 0; i < 6; i++) {
+ terminalX[i] = (int) (centerX + Math.cos(Math.PI / 3 * i) * terminalDiameter / 2);
+ terminalY[i] = (int) (centerY + Math.sin(Math.PI / 3 * i) * terminalDiameter / 2);
+ }
+ g2d.fillPolygon(terminalX, terminalY, 6);
+ g2d.setColor(METAL_COLOR.darker());
+ g2d.drawOval(width / 4 + 3 * width / 32, width / 32 + 3 * width / 32, width / 2 - 6 * width
+ / 32, width / 2 - 6 * width / 32);
+ g2d.drawPolygon(terminalX, terminalY, 6);
+ }
+
+ @Override
+ public int getControlPointCount() {
+ return 1;
+ }
+
+ @Override
+ public VisibilityPolicy getControlPointVisibilityPolicy(int index) {
+ return VisibilityPolicy.WHEN_SELECTED;
+ }
+
+ @Override
+ public boolean isControlPointSticky(int index) {
+ return true;
+ }
+
+ @Override
+ public Point getControlPoint(int index) {
+ return controlPoint;
+ }
+
+ @Override
+ public void setControlPoint(Point point, int index) {
+ this.controlPoint.setLocation(point);
+ // Invalidate the body
+ body = null;
+ }
+
+ @Override
+ public String getValue() {
+ return value;
+ }
+
+ @Override
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ @EditableProperty
+ public Orientation getOrientation() {
+ return orientation;
+ }
+
+ public void setOrientation(Orientation orientation) {
+ this.orientation = orientation;
+ // Invalidate the body
+ body = null;
+ }
+
+ @EditableProperty
+ public Color getColor() {
+ return color;
+ }
+
+ public void setColor(Color color) {
+ this.color = color;
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/electromechanical/CliffJack1_4.java b/diylc-library/src/org/diylc/components/electromechanical/CliffJack1_4.java
new file mode 100644
index 00000000..36621a54
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/electromechanical/CliffJack1_4.java
@@ -0,0 +1,331 @@
+package org.diylc.components.electromechanical;
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Composite;
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Area;
+import java.awt.geom.Ellipse2D;
+import java.awt.geom.RoundRectangle2D;
+
+import org.diylc.appframework.miscutils.ConfigurationManager;
+import org.diylc.common.HorizontalAlignment;
+import org.diylc.common.IPlugInPort;
+import org.diylc.common.ObjectCache;
+import org.diylc.common.Orientation;
+import org.diylc.common.VerticalAlignment;
+import org.diylc.components.AbstractTransparentComponent;
+import org.diylc.core.ComponentState;
+import org.diylc.core.IDIYComponent;
+import org.diylc.core.IDrawingObserver;
+import org.diylc.core.Project;
+import org.diylc.core.Theme;
+import org.diylc.core.VisibilityPolicy;
+import org.diylc.core.annotations.ComponentDescriptor;
+import org.diylc.core.annotations.EditableProperty;
+import org.diylc.core.measures.Size;
+import org.diylc.core.measures.SizeUnit;
+import org.diylc.utils.Constants;
+
+@ComponentDescriptor(name = "Cliff 1/4\" Jack", category = "Electromechanical", author = "Branislav Stojkovic", description = "Cliff-style closed panel mount 1/4\" phono jack", stretchable = false, zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "J")
+public class CliffJack1_4 extends AbstractTransparentComponent {
+
+ private static final long serialVersionUID = 1L;
+
+ private static Size SPACING = new Size(0.3d, SizeUnit.in);
+ private static Size PIN_WIDTH = new Size(0.1d, SizeUnit.in);
+ private static Size PIN_THICKNESS = new Size(0.02d, SizeUnit.in);
+ private static Color BODY_COLOR = Color.decode("#666666");
+ private static Color NUT_COLOR = Color.decode("#999999");
+ private static Color BORDER_COLOR = Color.black;
+ private static Color LABEL_COLOR = Color.white;
+ private static Size BODY_WIDTH = new Size(3/4d, SizeUnit.in);
+ private static Size BODY_LENGTH = new Size(0.9d, SizeUnit.in);
+ private static Size TAIL_LENGTH = new Size(0.1d, SizeUnit.in);
+
+ private Point[] controlPoints = new Point[] { new Point(0, 0) };
+ private JackType type = JackType.MONO;
+ private Orientation orientation = Orientation.DEFAULT;
+ transient private Shape[] body;
+ private String value = "";
+
+ public CliffJack1_4() {
+ super();
+ updateControlPoints();
+ }
+
+ private void updateControlPoints() {
+ // invalidate body shape
+ body = null;
+ int x = controlPoints[0].x;
+ int y = controlPoints[0].y;
+ int spacing = (int) SPACING.convertToPixels();
+ controlPoints = new Point[type == JackType.STEREO ? 6 : 4];
+
+ controlPoints[0] = new Point(x, y);
+ controlPoints[1] = new Point(x, y + 2 * spacing);
+ controlPoints[2] = new Point(x + 2 * spacing, y);
+ controlPoints[3] = new Point(x + 2 * spacing, y + 2 * spacing);
+ if (type == JackType.STEREO) {
+ controlPoints[4] = new Point(x + spacing, y);
+ controlPoints[5] = new Point(x + spacing, y + 2 * spacing);
+ }
+
+ // Apply rotation if necessary
+ double angle = getAngle();
+ if (angle != 0) {
+ AffineTransform rotation = AffineTransform.getRotateInstance(angle, x, y);
+ for (int i = 1; i < controlPoints.length; i++) {
+ rotation.transform(controlPoints[i], controlPoints[i]);
+ }
+ }
+ }
+
+ private double getAngle() {
+ // Apply rotation if necessary
+ double angle;
+ switch (orientation) {
+ case _90:
+ angle = Math.PI / 2;
+ break;
+ case _180:
+ angle = Math.PI;
+ break;
+ case _270:
+ angle = Math.PI * 3 / 2;
+ break;
+ default:
+ angle = 0;
+ }
+
+ return angle;
+ }
+
+ public Shape[] getBody() {
+ if (body == null) {
+ body = new Shape[5];
+
+ // Create body.
+ int bodyLength = (int) BODY_LENGTH.convertToPixels();
+ int bodyWidth = (int) BODY_WIDTH.convertToPixels();
+ int centerX = (controlPoints[0].x + controlPoints[3].x) / 2;
+ int centerY = (controlPoints[0].y + controlPoints[3].y) / 2;
+ body[0] = new Rectangle(centerX - bodyLength / 2, centerY - bodyWidth / 2, bodyLength,
+ bodyWidth);
+
+ int tailLength = (int) TAIL_LENGTH.convertToPixels();
+ body[1] = new RoundRectangle2D.Double(centerX - bodyLength / 2 - tailLength, centerY
+ - bodyWidth / 4, tailLength * 2, bodyWidth / 2, tailLength, tailLength);
+ Area tailArea = new Area(body[1]);
+ tailArea.subtract(new Area(body[0]));
+ body[1] = tailArea;
+
+ body[2] = new Rectangle(centerX + bodyLength / 2, centerY - bodyWidth / 4, tailLength,
+ bodyWidth / 2);
+
+ body[3] = new Rectangle(centerX + bodyLength / 2 + tailLength, centerY - bodyWidth / 4,
+ tailLength, bodyWidth / 2);
+ tailArea = new Area(body[3]);
+ int radius = bodyLength / 2 + tailLength * 2;
+ tailArea.intersect(new Area(new Ellipse2D.Double(centerX - radius, centerY - radius,
+ radius * 2, radius * 2)));
+ body[3] = tailArea;
+
+ // Apply rotation if necessary
+ double angle = getAngle();
+ if (angle != 0) {
+ AffineTransform rotation = AffineTransform.getRotateInstance(angle, centerX,
+ centerY);
+ for (int i = 0; i < body.length; i++) {
+ if (body[i] != null) {
+ Area area = new Area(body[i]);
+ area.transform(rotation);
+ body[i] = area;
+ }
+ }
+ }
+
+ // Create pins.
+ Area pins = new Area();
+
+ int pinWidth = (int) PIN_WIDTH.convertToPixels();
+ int pinThickness = (int) PIN_THICKNESS.convertToPixels();
+ for (int i = 0; i < getControlPointCount(); i++) {
+ Point point = getControlPoint(i);
+ Rectangle pin;
+ if (orientation == Orientation.DEFAULT || orientation == Orientation._180) {
+ pin = new Rectangle(point.x - pinWidth / 2, point.y - pinThickness / 2,
+ pinWidth, pinThickness);
+ } else {
+ pin = new Rectangle(point.x - pinThickness / 2, point.y - pinWidth / 2,
+ pinThickness, pinWidth);
+ }
+ pins.add(new Area(pin));
+ }
+
+ body[4] = pins;
+ }
+ return body;
+ }
+
+ @Override
+ public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode,
+ Project project, IDrawingObserver drawingObserver) {
+ Shape[] body = getBody();
+
+ g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
+ if (componentState != ComponentState.DRAGGING) {
+ Composite oldComposite = g2d.getComposite();
+ if (alpha < MAX_ALPHA) {
+ g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha
+ / MAX_ALPHA));
+ }
+ g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : BODY_COLOR);
+ for (int i = 0; i < body.length - 1; i++) {
+ // Nut is brighter colored.
+ if (i == body.length - 2)
+ g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : NUT_COLOR);
+ g2d.fill(body[i]);
+ }
+ g2d.setComposite(oldComposite);
+ }
+
+ Color finalBorderColor;
+ if (outlineMode) {
+ Theme theme = (Theme) ConfigurationManager.getInstance().readObject(
+ IPlugInPort.THEME_KEY, Constants.DEFAULT_THEME);
+ finalBorderColor = componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? SELECTION_COLOR : theme
+ .getOutlineColor();
+ } else {
+ finalBorderColor = componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? SELECTION_COLOR
+ : BORDER_COLOR;
+ }
+
+ g2d.setColor(finalBorderColor);
+ for (int i = 0; i < body.length - 1; i++) {
+ g2d.draw(body[i]);
+ }
+
+ // Pins are the last piece.
+ Shape pins = body[body.length - 1];
+ g2d.setColor(METAL_COLOR);
+ g2d.fill(pins);
+ g2d.setColor(METAL_COLOR.darker());
+ g2d.draw(pins);
+
+ Color finalLabelColor;
+ if (outlineMode) {
+ Theme theme = (Theme) ConfigurationManager.getInstance().readObject(
+ IPlugInPort.THEME_KEY, Constants.DEFAULT_THEME);
+ finalLabelColor = componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? LABEL_COLOR_SELECTED : theme
+ .getOutlineColor();
+ } else {
+ finalLabelColor = componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? LABEL_COLOR_SELECTED
+ : LABEL_COLOR;
+ }
+ g2d.setColor(finalLabelColor);
+ g2d.setFont(LABEL_FONT);
+ int centerX = (controlPoints[0].x + controlPoints[3].x) / 2;
+ int centerY = (controlPoints[0].y + controlPoints[3].y) / 2;
+ drawCenteredText(g2d, name, centerX, centerY, HorizontalAlignment.CENTER,
+ VerticalAlignment.CENTER);
+ }
+
+ @Override
+ public void drawIcon(Graphics2D g2d, int width, int height) {
+ int bodyWidth = getClosestOdd(width * 3 / 5);
+ int tailWidth = getClosestOdd(width * 3 / 9);
+
+ g2d.setColor(BODY_COLOR);
+ g2d.fillRoundRect((width - tailWidth) / 2, height / 2, tailWidth, height / 2 - 2 * 32
+ / height, 4 * 32 / width, 4 * 32 / width);
+ g2d.setColor(BORDER_COLOR);
+ g2d.drawRoundRect((width - tailWidth) / 2, height / 2, tailWidth, height / 2 - 2 * 32
+ / height, 4 * 32 / width, 4 * 32 / width);
+
+ g2d.setColor(NUT_COLOR);
+ g2d.fillRoundRect((width - tailWidth) / 2, 2 * 32 / height, tailWidth, height / 2,
+ 4 * 32 / width, 4 * 32 / width);
+ g2d.setColor(BORDER_COLOR);
+ g2d.drawRoundRect((width - tailWidth) / 2, 2 * 32 / height, tailWidth, height / 2,
+ 4 * 32 / width, 4 * 32 / width);
+
+ g2d.setColor(BODY_COLOR);
+ g2d.fillRect((width - bodyWidth) / 2, height / 7 + 1, bodyWidth, height * 5 / 7);
+ g2d.setColor(BORDER_COLOR);
+ g2d.drawRect((width - bodyWidth) / 2, height / 7 + 1, bodyWidth, height * 5 / 7);
+
+ g2d.setColor(METAL_COLOR);
+ int pinX1 = getClosestOdd((width - bodyWidth * 3 / 4) / 2);
+ int pinX2 = getClosestOdd((width + bodyWidth * 3 / 4) / 2) - 1;
+ g2d.drawLine(pinX1, width * 2 / 8, pinX1, width * 3 / 8);
+ g2d.drawLine(pinX1, width * 5 / 8, pinX1, width * 6 / 8);
+ g2d.drawLine(pinX2, width * 2 / 8, pinX2, width * 3 / 8);
+ g2d.drawLine(pinX2, width * 5 / 8, pinX2, width * 6 / 8);
+ }
+
+ @Override
+ public int getControlPointCount() {
+ return controlPoints.length;
+ }
+
+ @Override
+ public Point getControlPoint(int index) {
+ return controlPoints[index];
+ }
+
+ @Override
+ public void setControlPoint(Point point, int index) {
+ controlPoints[index].setLocation(point);
+ body = null;
+ }
+
+ @Override
+ public boolean isControlPointSticky(int index) {
+ return true;
+ }
+
+ @Override
+ public VisibilityPolicy getControlPointVisibilityPolicy(int index) {
+ return VisibilityPolicy.NEVER;
+ }
+
+ @EditableProperty
+ @Override
+ public String getValue() {
+ return value;
+ }
+
+ @Override
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ @EditableProperty
+ public JackType getType() {
+ return type;
+ }
+
+ public void setType(JackType type) {
+ this.type = type;
+ updateControlPoints();
+ }
+
+ @EditableProperty
+ public Orientation getOrientation() {
+ return orientation;
+ }
+
+ public void setOrientation(Orientation orientation) {
+ this.orientation = orientation;
+ updateControlPoints();
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/electromechanical/ClosedJack1_4.java b/diylc-library/src/org/diylc/components/electromechanical/ClosedJack1_4.java
new file mode 100644
index 00000000..fc32c2fa
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/electromechanical/ClosedJack1_4.java
@@ -0,0 +1,338 @@
+package org.diylc.components.electromechanical;
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Composite;
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Area;
+import java.awt.geom.Ellipse2D;
+import java.awt.geom.GeneralPath;
+
+import org.diylc.appframework.miscutils.ConfigurationManager;
+import org.diylc.common.HorizontalAlignment;
+import org.diylc.common.IPlugInPort;
+import org.diylc.common.ObjectCache;
+import org.diylc.common.Orientation;
+import org.diylc.common.VerticalAlignment;
+import org.diylc.components.AbstractTransparentComponent;
+import org.diylc.core.ComponentState;
+import org.diylc.core.IDIYComponent;
+import org.diylc.core.IDrawingObserver;
+import org.diylc.core.Project;
+import org.diylc.core.Theme;
+import org.diylc.core.VisibilityPolicy;
+import org.diylc.core.annotations.ComponentDescriptor;
+import org.diylc.core.annotations.EditableProperty;
+import org.diylc.core.measures.Size;
+import org.diylc.core.measures.SizeUnit;
+import org.diylc.utils.Constants;
+
+@ComponentDescriptor(name = "Closed 1/4\" Jack", category = "Electromechanical", author = "Branislav Stojkovic", description = "Enclosed panel mount 1/4\" phono jack", stretchable = false, zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "J")
+public class ClosedJack1_4 extends AbstractTransparentComponent {
+
+ private static final long serialVersionUID = 1L;
+
+ private static Size SPACING = new Size(0.1d, SizeUnit.in);
+ private static Size LUG_WIDTH = new Size(0.1d, SizeUnit.in);
+ private static Size LUG_LENGTH = new Size(0.12d, SizeUnit.in);
+ private static Size LUG_HOLE_SIZE = new Size(1d, SizeUnit.mm);
+ private static Color BODY_COLOR = Color.decode("#666666");
+ private static Color SHAFT_COLOR = Color.decode("#AAAAAA");
+ private static Size SHAFT_LENGTH = new Size(0.25d, SizeUnit.in);
+ private static Size SHAFT_WIDTH = new Size(3d / 8, SizeUnit.in);
+ private static Color BORDER_COLOR = Color.black;
+ private static Color LABEL_COLOR = Color.white;
+ private static Size BODY_WIDTH = new Size(0.65d, SizeUnit.in);
+ private static Size BODY_LENGTH = new Size(0.8d, SizeUnit.in);
+
+ private Point[] controlPoints = new Point[] { new Point(0, 0) };
+ private JackType type = JackType.MONO;
+ private Orientation orientation = Orientation.DEFAULT;
+ transient private Shape[] body;
+ private String value = "";
+
+ public ClosedJack1_4() {
+ super();
+ updateControlPoints();
+ }
+
+ private void updateControlPoints() {
+ // invalidate body shape
+ body = null;
+ int x = controlPoints[0].x;
+ int y = controlPoints[0].y;
+ int spacing = (int) SPACING.convertToPixels();
+ int bodyLength = (int) BODY_LENGTH.convertToPixels();
+ controlPoints = new Point[type == JackType.STEREO ? 3 : 2];
+
+ controlPoints[0] = new Point(x, y);
+ controlPoints[1] = new Point(x + bodyLength, y);
+ if (type == JackType.STEREO) {
+ controlPoints[2] = new Point(x, y + 2 * spacing);
+ }
+
+ // Apply rotation if necessary
+ double angle = getAngle();
+ if (angle != 0) {
+ AffineTransform rotation = AffineTransform.getRotateInstance(angle, x, y);
+ for (int i = 1; i < controlPoints.length; i++) {
+ rotation.transform(controlPoints[i], controlPoints[i]);
+ }
+ }
+ }
+
+ private double getAngle() {
+ // Apply rotation if necessary
+ double angle;
+ switch (orientation) {
+ case _90:
+ angle = Math.PI / 2;
+ break;
+ case _180:
+ angle = Math.PI;
+ break;
+ case _270:
+ angle = Math.PI * 3 / 2;
+ break;
+ default:
+ angle = 0;
+ }
+
+ return angle;
+ }
+
+ public Shape[] getBody() {
+ if (body == null) {
+ body = new Shape[5];
+
+ // Create body.
+ int x = controlPoints[0].x;
+ int y = controlPoints[0].y;
+ int lugWidth = (int) LUG_WIDTH.convertToPixels();
+ int lugLength = (int) LUG_LENGTH.convertToPixels();
+ int lugHoleSize = (int) LUG_HOLE_SIZE.convertToPixels();
+ int bodyLength = (int) BODY_LENGTH.convertToPixels();
+ int bodyWidth = (int) BODY_WIDTH.convertToPixels();
+ body[0] = new Area(new Rectangle(x + lugLength, y - bodyWidth / 2, bodyLength,
+ bodyWidth));
+
+ int shaftLength = (int) SHAFT_LENGTH.convertToPixels();
+ int shaftWidth = (int) SHAFT_WIDTH.convertToPixels();
+ Area shaft = new Area(new Rectangle(x + lugLength + bodyLength, y - shaftWidth / 2,
+ shaftLength, shaftWidth));
+ body[1] = shaft;
+
+ double angle = getAngle();
+ AffineTransform rotation = null;
+ if (angle != 0) {
+ rotation = AffineTransform.getRotateInstance(angle, x, y);
+ }
+
+ GeneralPath path = new GeneralPath();
+ int step = 4;
+ for (int i = x + lugLength + bodyLength + step; i <= x + lugLength + bodyLength
+ + shaftLength; i += step) {
+ Point p = new Point(i, y - shaftWidth / 2 + 1);
+ if (rotation != null) {
+ rotation.transform(p, p);
+ }
+ path.moveTo(p.x, p.y);
+ p = new Point(i - step, y + shaftWidth / 2 - 1);
+ if (rotation != null) {
+ rotation.transform(p, p);
+ }
+ path.lineTo(p.x, p.y);
+ }
+ Area pathArea = new Area(path);
+ pathArea.intersect(shaft);
+ body[2] = path;
+
+ // Create lugs.
+ Area lugs = new Area();
+
+ int spacing = (int) SPACING.convertToPixels();
+ Point[] untransformedControlPoints = new Point[type == JackType.STEREO ? 3 : 2];
+
+ untransformedControlPoints[0] = new Point(x, y);
+ untransformedControlPoints[1] = new Point(x + bodyLength, y);
+ if (type == JackType.STEREO) {
+ untransformedControlPoints[2] = new Point(x, y + 2 * spacing);
+ }
+
+ for (int i = 0; i < untransformedControlPoints.length; i++) {
+ Point point = untransformedControlPoints[i];
+ Area lug = new Area(new Ellipse2D.Double(point.x - lugWidth / 2, point.y - lugWidth
+ / 2, lugWidth, lugWidth));
+ lug.add(new Area(
+ new Rectangle(point.x, point.y - lugWidth / 2, lugLength, lugWidth)));
+ lug.subtract(new Area(new Ellipse2D.Double(point.x - lugHoleSize / 2, point.y
+ - lugHoleSize / 2, lugHoleSize, lugHoleSize)));
+ lugs.add(lug);
+ }
+
+ body[3] = lugs;
+
+ // Rotate everything that's of Area type, e.g. everything but lines.
+ if (rotation != null) {
+ for (Shape shape : body) {
+ if (shape instanceof Area) {
+ Area area = (Area) shape;
+ area.transform(rotation);
+ }
+ }
+ }
+ }
+ return body;
+ }
+
+ @Override
+ public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode,
+ Project project, IDrawingObserver drawingObserver) {
+ Shape[] body = getBody();
+
+ // Rectangle bounds = body.getBounds();
+
+ g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
+ if (componentState != ComponentState.DRAGGING) {
+ Composite oldComposite = g2d.getComposite();
+ if (alpha < MAX_ALPHA) {
+ g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha
+ / MAX_ALPHA));
+ }
+ g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : BODY_COLOR);
+ g2d.fill(body[0]);
+ g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : SHAFT_COLOR);
+ g2d.fill(body[1]);
+ g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : METAL_COLOR);
+ g2d.fill(body[3]);
+ g2d.setComposite(oldComposite);
+ }
+
+ Color finalBorderColor;
+ if (outlineMode) {
+ Theme theme = (Theme) ConfigurationManager.getInstance().readObject(
+ IPlugInPort.THEME_KEY, Constants.DEFAULT_THEME);
+ finalBorderColor = componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? SELECTION_COLOR : theme
+ .getOutlineColor();
+ } else {
+ finalBorderColor = componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? SELECTION_COLOR : BORDER_COLOR;
+ }
+
+ g2d.setColor(finalBorderColor);
+ g2d.draw(body[0]);
+ g2d.draw(body[1]);
+ if (!outlineMode) {
+ g2d.setColor(SHAFT_COLOR.darker());
+ g2d.fill(body[2]);
+ g2d.draw(body[2]);
+ }
+
+ // Pins are the last piece.
+ g2d.setColor(outlineMode ? finalBorderColor : METAL_COLOR.darker());
+ g2d.draw(body[3]);
+
+ Color finalLabelColor;
+ if (outlineMode) {
+ Theme theme = (Theme) ConfigurationManager.getInstance().readObject(
+ IPlugInPort.THEME_KEY, Constants.DEFAULT_THEME);
+ finalLabelColor = componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? LABEL_COLOR_SELECTED : theme
+ .getOutlineColor();
+ } else {
+ finalLabelColor = componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? LABEL_COLOR_SELECTED
+ : LABEL_COLOR;
+ }
+ g2d.setColor(finalLabelColor);
+ g2d.setFont(LABEL_FONT);
+ Rectangle bounds = body[0].getBounds();
+ int centerX = bounds.x + bounds.width / 2;
+ int centerY = bounds.y + bounds.height / 2;
+ drawCenteredText(g2d, name, centerX, centerY, HorizontalAlignment.CENTER,
+ VerticalAlignment.CENTER);
+ }
+
+ @Override
+ public void drawIcon(Graphics2D g2d, int width, int height) {
+ int bodyWidth = getClosestOdd(width * 3 / 5);
+ int tailWidth = getClosestOdd(width * 3 / 10);
+
+ g2d.setColor(SHAFT_COLOR);
+ g2d.fillRect((width - tailWidth) / 2, 1, tailWidth, height / 2);
+ g2d.setColor(BORDER_COLOR);
+ g2d.drawRect((width - tailWidth) / 2, 1, tailWidth, height / 2);
+
+ g2d.setColor(BODY_COLOR);
+ g2d.fillRect((width - bodyWidth) / 2, height / 7 + 1, bodyWidth, height * 5 / 7);
+ g2d.setColor(BORDER_COLOR);
+ g2d.drawRect((width - bodyWidth) / 2, height / 7 + 1, bodyWidth, height * 5 / 7);
+
+ g2d.setColor(METAL_COLOR);
+
+ g2d.fillRect(width * 7 / 16, height * 6 / 7 + 1, width / 8, height / 7 - 1);
+ g2d.fillRect(width * 7 / 16, height / 7 + 2, width / 8, height / 7 - 1);
+ }
+
+ @Override
+ public int getControlPointCount() {
+ return controlPoints.length;
+ }
+
+ @Override
+ public Point getControlPoint(int index) {
+ return controlPoints[index];
+ }
+
+ @Override
+ public void setControlPoint(Point point, int index) {
+ controlPoints[index].setLocation(point);
+ body = null;
+ }
+
+ @Override
+ public boolean isControlPointSticky(int index) {
+ return true;
+ }
+
+ @Override
+ public VisibilityPolicy getControlPointVisibilityPolicy(int index) {
+ return VisibilityPolicy.NEVER;
+ }
+
+ @EditableProperty
+ @Override
+ public String getValue() {
+ return value;
+ }
+
+ @Override
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ @EditableProperty
+ public JackType getType() {
+ return type;
+ }
+
+ public void setType(JackType type) {
+ this.type = type;
+ updateControlPoints();
+ }
+
+ @EditableProperty
+ public Orientation getOrientation() {
+ return orientation;
+ }
+
+ public void setOrientation(Orientation orientation) {
+ this.orientation = orientation;
+ updateControlPoints();
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/electromechanical/DCPolarity.java b/diylc-library/src/org/diylc/components/electromechanical/DCPolarity.java
new file mode 100644
index 00000000..0f69e82b
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/electromechanical/DCPolarity.java
@@ -0,0 +1,17 @@
+package org.diylc.components.electromechanical;
+
+public enum DCPolarity {
+
+ NONE("None"), CENTER_POSITIVE("Center Positive"), CENTER_NEGATIVE("Center Negative");
+
+ private String title;
+
+ private DCPolarity(String title) {
+ this.title = title;
+ }
+
+ @Override
+ public String toString() {
+ return title;
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/electromechanical/JackType.java b/diylc-library/src/org/diylc/components/electromechanical/JackType.java
new file mode 100644
index 00000000..babf975a
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/electromechanical/JackType.java
@@ -0,0 +1,11 @@
+package org.diylc.components.electromechanical;
+
+public enum JackType {
+
+ MONO, STEREO;
+
+ @Override
+ public String toString() {
+ return name().substring(0, 1) + name().substring(1).toLowerCase();
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/electromechanical/MiniToggleSwitch.java b/diylc-library/src/org/diylc/components/electromechanical/MiniToggleSwitch.java
new file mode 100644
index 00000000..1efdac51
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/electromechanical/MiniToggleSwitch.java
@@ -0,0 +1,271 @@
+package org.diylc.components.electromechanical;
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Composite;
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.Shape;
+import java.awt.geom.RoundRectangle2D;
+
+import org.diylc.appframework.miscutils.ConfigurationManager;
+import org.diylc.common.IPlugInPort;
+import org.diylc.common.ObjectCache;
+import org.diylc.components.AbstractTransparentComponent;
+import org.diylc.core.ComponentState;
+import org.diylc.core.IDIYComponent;
+import org.diylc.core.IDrawingObserver;
+import org.diylc.core.Project;
+import org.diylc.core.Theme;
+import org.diylc.core.VisibilityPolicy;
+import org.diylc.core.annotations.ComponentDescriptor;
+import org.diylc.core.annotations.EditableProperty;
+import org.diylc.core.measures.Size;
+import org.diylc.core.measures.SizeUnit;
+import org.diylc.utils.Constants;
+
+@ComponentDescriptor(name = "Mini Toggle Switch", category = "Electromechanical", author = "Branislav Stojkovic", description = "Panel mounted mini toggle switch", stretchable = false, zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "SW")
+public class MiniToggleSwitch extends AbstractTransparentComponent {
+
+ private static final long serialVersionUID = 1L;
+
+ private static Size SPACING = new Size(0.2d, SizeUnit.in);
+ private static Size MARGIN = new Size(0.08d, SizeUnit.in);
+ private static Size CIRCLE_SIZE = new Size(0.09d, SizeUnit.in);
+ private static Size LUG_WIDTH = new Size(0.060d, SizeUnit.in);
+ private static Size LUG_THICKNESS = new Size(0.02d, SizeUnit.in);
+
+ private static Color BODY_COLOR = Color.decode("#3299CC");
+ private static Color BORDER_COLOR = BODY_COLOR.darker();
+ private static Color CIRCLE_COLOR = Color.decode("#FFFFAA");
+
+ protected Point[] controlPoints = new Point[] { new Point(0, 0) };
+ transient protected RoundRectangle2D body;
+ protected String name;
+ protected ToggleSwitchType switchType = ToggleSwitchType.DPDT;
+
+ public MiniToggleSwitch() {
+ super();
+ updateControlPoints();
+ }
+
+ private void updateControlPoints() {
+ Point firstPoint = controlPoints[0];
+ int spacing = (int) SPACING.convertToPixels();
+ switch (switchType) {
+ case SPST:
+ controlPoints = new Point[] { firstPoint,
+ new Point(firstPoint.x, firstPoint.y + spacing) };
+ break;
+ case SPDT:
+ controlPoints = new Point[] { firstPoint,
+ new Point(firstPoint.x, firstPoint.y + spacing),
+ new Point(firstPoint.x, firstPoint.y + 2 * spacing) };
+ break;
+ case DPDT:
+ controlPoints = new Point[] { firstPoint,
+ new Point(firstPoint.x, firstPoint.y + spacing),
+ new Point(firstPoint.x, firstPoint.y + 2 * spacing),
+ new Point(firstPoint.x + spacing, firstPoint.y),
+ new Point(firstPoint.x + spacing, firstPoint.y + spacing),
+ new Point(firstPoint.x + spacing, firstPoint.y + 2 * spacing) };
+ break;
+ case _3PDT:
+ controlPoints = new Point[] { firstPoint,
+ new Point(firstPoint.x, firstPoint.y + spacing),
+ new Point(firstPoint.x, firstPoint.y + 2 * spacing),
+ new Point(firstPoint.x + spacing, firstPoint.y),
+ new Point(firstPoint.x + spacing, firstPoint.y + spacing),
+ new Point(firstPoint.x + spacing, firstPoint.y + 2 * spacing),
+ new Point(firstPoint.x + 2 * spacing, firstPoint.y),
+ new Point(firstPoint.x + 2 * spacing, firstPoint.y + spacing),
+ new Point(firstPoint.x + 2 * spacing, firstPoint.y + 2 * spacing) };
+ break;
+ case _4PDT:
+ controlPoints = new Point[] { firstPoint,
+ new Point(firstPoint.x, firstPoint.y + spacing),
+ new Point(firstPoint.x, firstPoint.y + 2 * spacing),
+ new Point(firstPoint.x + spacing, firstPoint.y),
+ new Point(firstPoint.x + spacing, firstPoint.y + spacing),
+ new Point(firstPoint.x + spacing, firstPoint.y + 2 * spacing),
+ new Point(firstPoint.x + 2 * spacing, firstPoint.y),
+ new Point(firstPoint.x + 2 * spacing, firstPoint.y + spacing),
+ new Point(firstPoint.x + 2 * spacing, firstPoint.y + 2 * spacing),
+ new Point(firstPoint.x + 3 * spacing, firstPoint.y),
+ new Point(firstPoint.x + 3 * spacing, firstPoint.y + spacing),
+ new Point(firstPoint.x + 3 * spacing, firstPoint.y + 2 * spacing) };
+ break;
+ case _5PDT:
+ controlPoints = new Point[] { firstPoint,
+ new Point(firstPoint.x, firstPoint.y + spacing),
+ new Point(firstPoint.x, firstPoint.y + 2 * spacing),
+ new Point(firstPoint.x + spacing, firstPoint.y),
+ new Point(firstPoint.x + spacing, firstPoint.y + spacing),
+ new Point(firstPoint.x + spacing, firstPoint.y + 2 * spacing),
+ new Point(firstPoint.x + 2 * spacing, firstPoint.y),
+ new Point(firstPoint.x + 2 * spacing, firstPoint.y + spacing),
+ new Point(firstPoint.x + 2 * spacing, firstPoint.y + 2 * spacing),
+ new Point(firstPoint.x + 3 * spacing, firstPoint.y),
+ new Point(firstPoint.x + 3 * spacing, firstPoint.y + spacing),
+ new Point(firstPoint.x + 3 * spacing, firstPoint.y + 2 * spacing),
+ new Point(firstPoint.x + 4 * spacing, firstPoint.y),
+ new Point(firstPoint.x + 4 * spacing, firstPoint.y + spacing),
+ new Point(firstPoint.x + 4 * spacing, firstPoint.y + 2 * spacing) };
+ break;
+ }
+ }
+
+ @Override
+ public Point getControlPoint(int index) {
+ return controlPoints[index];
+ }
+
+ @Override
+ public boolean isControlPointSticky(int index) {
+ return true;
+ }
+
+ @Override
+ public VisibilityPolicy getControlPointVisibilityPolicy(int index) {
+ return VisibilityPolicy.NEVER;
+ }
+
+ @Override
+ public int getControlPointCount() {
+ return controlPoints.length;
+ }
+
+ @Override
+ public void setControlPoint(Point point, int index) {
+ controlPoints[index].setLocation(point);
+ // Reset body shape.
+ body = null;
+ }
+
+ @EditableProperty
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @EditableProperty(name = "Type")
+ @Override
+ public ToggleSwitchType getValue() {
+ return switchType;
+ }
+
+ @Override
+ public void setValue(ToggleSwitchType value) {
+ this.switchType = value;
+ updateControlPoints();
+ // Reset body shape.
+ body = null;
+ }
+
+ @Override
+ public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode,
+ Project project, IDrawingObserver drawingObserver) {
+ if (checkPointsClipped(g2d.getClip())) {
+ return;
+ }
+ Shape body = getBody();
+ // Draw body if available.
+ if (body != null) {
+ Composite oldComposite = g2d.getComposite();
+ if (alpha < MAX_ALPHA) {
+ g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha
+ / MAX_ALPHA));
+ }
+ g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : BODY_COLOR);
+ g2d.fill(body);
+ g2d.setComposite(oldComposite);
+ g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
+ Color finalBorderColor;
+ if (outlineMode) {
+ Theme theme = (Theme) ConfigurationManager.getInstance().readObject(
+ IPlugInPort.THEME_KEY, Constants.DEFAULT_THEME);
+ finalBorderColor = componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? SELECTION_COLOR : theme
+ .getOutlineColor();
+ } else {
+ finalBorderColor = componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? SELECTION_COLOR
+ : BORDER_COLOR;
+ }
+ g2d.setColor(finalBorderColor);
+ g2d.draw(body);
+ }
+ // Do not track these changes because the whole switch has been tracked
+ // so far.
+ drawingObserver.stopTracking();
+ // Draw lugs.
+ int circleDiameter = getClosestOdd((int) CIRCLE_SIZE.convertToPixels());
+ int lugWidth = getClosestOdd((int) LUG_WIDTH.convertToPixels());
+ int lugHeight = getClosestOdd((int) LUG_THICKNESS.convertToPixels());
+ for (Point p : controlPoints) {
+ if (!outlineMode) {
+ g2d.setColor(CIRCLE_COLOR);
+ g2d.fillOval(p.x - circleDiameter / 2, p.y - circleDiameter / 2, circleDiameter,
+ circleDiameter);
+ }
+ g2d.setColor(METAL_COLOR);
+ g2d.fillRect(p.x - lugWidth / 2, p.y - lugHeight / 2, lugWidth, lugHeight);
+ }
+ }
+
+ public RoundRectangle2D getBody() {
+ if (body == null) {
+ Point firstPoint = controlPoints[0];
+ int margin = (int) MARGIN.convertToPixels();
+ int spacing = (int) SPACING.convertToPixels();
+ switch (switchType) {
+ case SPST:
+ body = new RoundRectangle2D.Double(firstPoint.x - margin, firstPoint.y - margin,
+ 2 * margin, 2 * margin + spacing, margin, margin);
+ break;
+ case SPDT:
+ body = new RoundRectangle2D.Double(firstPoint.x - margin, firstPoint.y - margin,
+ 2 * margin, 2 * margin + 2 * spacing, margin, margin);
+ break;
+ case DPDT:
+ body = new RoundRectangle2D.Double(firstPoint.x - margin, firstPoint.y - margin, 2
+ * margin + spacing, 2 * margin + 2 * spacing, margin, margin);
+ break;
+ case _3PDT:
+ body = new RoundRectangle2D.Double(firstPoint.x - margin, firstPoint.y - margin, 2
+ * margin + 2 * spacing, 2 * margin + 2 * spacing, margin, margin);
+ break;
+ case _4PDT:
+ body = new RoundRectangle2D.Double(firstPoint.x - margin, firstPoint.y - margin, 2
+ * margin + 3 * spacing, 2 * margin + 2 * spacing, margin, margin);
+ break;
+ case _5PDT:
+ body = new RoundRectangle2D.Double(firstPoint.x - margin, firstPoint.y - margin, 2
+ * margin + 4 * spacing, 2 * margin + 2 * spacing, margin, margin);
+ break;
+ }
+ }
+ return body;
+ }
+
+ @Override
+ public void drawIcon(Graphics2D g2d, int width, int height) {
+ int circleSize = 5 * width / 32;
+ g2d.setColor(BODY_COLOR);
+ g2d.fillRoundRect(width / 4, 1, width / 2, height - 2, circleSize, circleSize);
+ g2d.setColor(BORDER_COLOR);
+ g2d.drawRoundRect(width / 4, 1, width / 2, height - 2, circleSize, circleSize);
+ for (int i = 1; i <= 3; i++) {
+ g2d.setColor(CIRCLE_COLOR);
+ g2d.fillOval(width / 2 - circleSize / 2, i * height / 4 - 3, circleSize, circleSize);
+ g2d.setColor(METAL_COLOR);
+ g2d.drawLine(width / 2 - circleSize / 2 + 1, i * height / 4 - 1, width / 2 + circleSize
+ / 2 - 1, i * height / 4 - 1);
+ }
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/electromechanical/PlasticDCJack.java b/diylc-library/src/org/diylc/components/electromechanical/PlasticDCJack.java
new file mode 100644
index 00000000..9cc1944c
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/electromechanical/PlasticDCJack.java
@@ -0,0 +1,247 @@
+package org.diylc.components.electromechanical;
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Composite;
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.awt.geom.Area;
+import java.awt.geom.Ellipse2D;
+
+import org.diylc.appframework.miscutils.ConfigurationManager;
+import org.diylc.common.HorizontalAlignment;
+import org.diylc.common.IPlugInPort;
+import org.diylc.common.ObjectCache;
+import org.diylc.common.VerticalAlignment;
+import org.diylc.components.AbstractTransparentComponent;
+import org.diylc.core.ComponentState;
+import org.diylc.core.IDIYComponent;
+import org.diylc.core.IDrawingObserver;
+import org.diylc.core.Project;
+import org.diylc.core.Theme;
+import org.diylc.core.VisibilityPolicy;
+import org.diylc.core.annotations.ComponentDescriptor;
+import org.diylc.core.annotations.EditableProperty;
+import org.diylc.core.measures.Size;
+import org.diylc.core.measures.SizeUnit;
+import org.diylc.utils.Constants;
+
+@ComponentDescriptor(name = "Plastic DC Jack", category = "Electromechanical", author = "Branislav Stojkovic", description = "Panel mount plastic DC jack", stretchable = false, zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "J")
+public class PlasticDCJack extends AbstractTransparentComponent {
+
+ private static final long serialVersionUID = 1L;
+
+ private static Size LUG_WIDTH = new Size(0.08d, SizeUnit.in);
+ private static Size LUG_THICKNESS = new Size(0.02d, SizeUnit.in);
+ private static Size SPACING = new Size(0.1d, SizeUnit.in);
+ private static Size DIAMETER = new Size(0.5d, SizeUnit.in);
+ private static Color BODY_COLOR = Color.decode("#666666");
+ private static Color PHENOLIC_COLOR = Color.decode("#CD8500");
+ private static Color BORDER_COLOR = Color.black;
+ private static Color MARKING_COLOR = Color.lightGray;
+
+ private Point[] controlPoints = new Point[] { new Point(0, 0), new Point(0, 0), new Point(0, 0) };
+ private String value = "";
+ private DCPolarity polarity = DCPolarity.CENTER_NEGATIVE;
+ transient private Shape[] body;
+
+ public PlasticDCJack() {
+ updateControlPoints();
+ }
+
+ private void updateControlPoints() {
+ // invalidate body shape
+ body = null;
+
+ int x = controlPoints[0].x;
+ int y = controlPoints[0].y;
+
+ int spacing = (int) SPACING.convertToPixels();
+ controlPoints[1] = new Point(x + spacing, y + spacing);
+ controlPoints[2] = new Point(x - spacing, y + spacing * 2);
+ }
+
+ public Shape[] getBody() {
+ if (body == null) {
+ body = new Shape[4];
+
+ int x = controlPoints[0].x;
+ int y = controlPoints[0].y;
+ int spacing = (int) SPACING.convertToPixels();
+ int diameter = getClosestOdd(DIAMETER.convertToPixels());
+ body[0] = new Ellipse2D.Double(x - diameter / 2, y + spacing - diameter / 2, diameter,
+ diameter);
+
+ int rectWidth = (int) (diameter / Math.sqrt(2)) - 2;
+ body[1] = new Rectangle(x - rectWidth / 2, y + spacing - rectWidth / 2, rectWidth,
+ rectWidth);
+
+ int lugWidth = getClosestOdd(LUG_WIDTH.convertToPixels());
+ int lugThickness = getClosestOdd(LUG_THICKNESS.convertToPixels());
+
+ Point groundPoint = controlPoints[controlPoints.length - 1];
+ Area groundLug = new Area(new Ellipse2D.Double(groundPoint.x + spacing - lugWidth / 2,
+ groundPoint.y - lugWidth / 2, lugWidth, lugWidth));
+ groundLug.add(new Area(new Rectangle(groundPoint.x, groundPoint.y - lugWidth / 2,
+ spacing, lugWidth)));
+ groundLug.subtract(new Area(new Ellipse2D.Double(
+ groundPoint.x + spacing - lugWidth / 6, groundPoint.y - lugWidth / 6,
+ lugWidth / 3, lugWidth / 3)));
+ body[2] = groundLug;
+
+ Area lugArea = new Area();
+ for (int i = 0; i < controlPoints.length; i++) {
+ Point point = controlPoints[i];
+ if (i == getControlPointCount() - 1) {
+ lugArea.add(new Area(new Rectangle(point.x - lugThickness / 2, point.y
+ - lugWidth / 2, lugThickness, lugWidth)));
+ } else {
+ lugArea.add(new Area(new Rectangle(point.x - lugWidth / 2, point.y
+ - lugThickness / 2, lugWidth, lugThickness)));
+ }
+ }
+ body[3] = lugArea;
+ }
+ return body;
+ }
+
+ @Override
+ public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode,
+ Project project, IDrawingObserver drawingObserver) {
+ Shape[] body = getBody();
+
+ g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
+ if (componentState != ComponentState.DRAGGING) {
+ Composite oldComposite = g2d.getComposite();
+ if (alpha < MAX_ALPHA) {
+ g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha
+ / MAX_ALPHA));
+ }
+ g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : BODY_COLOR);
+ g2d.fill(body[0]);
+ if (!outlineMode) {
+ g2d.setColor(PHENOLIC_COLOR);
+ g2d.fill(body[1]);
+ }
+ g2d.setComposite(oldComposite);
+ }
+
+ Color finalBorderColor;
+ if (outlineMode) {
+ Theme theme = (Theme) ConfigurationManager.getInstance().readObject(
+ IPlugInPort.THEME_KEY, Constants.DEFAULT_THEME);
+ finalBorderColor = componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? SELECTION_COLOR : theme
+ .getOutlineColor();
+ } else {
+ finalBorderColor = componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? SELECTION_COLOR : BORDER_COLOR;
+ }
+
+ g2d.setColor(finalBorderColor);
+ g2d.draw(body[0]);
+ if (!outlineMode) {
+ g2d.setColor(PHENOLIC_COLOR.darker());
+ g2d.draw(body[1]);
+
+ g2d.setColor(METAL_COLOR);
+ g2d.fill(body[2]);
+ g2d.setColor(METAL_COLOR.darker());
+ g2d.draw(body[2]);
+ }
+
+ g2d.setColor(METAL_COLOR);
+ g2d.fill(body[3]);
+ g2d.setColor(METAL_COLOR.darker());
+ g2d.draw(body[3]);
+
+ if (!outlineMode && getPolarity() != DCPolarity.NONE) {
+ int spacing = (int) SPACING.convertToPixels();
+ g2d.setColor(MARKING_COLOR);
+ g2d.setFont(LABEL_FONT.deriveFont(12f));
+ drawCenteredText(g2d, getPolarity() == DCPolarity.CENTER_NEGATIVE ? "+" : "-",
+ controlPoints[0].x, controlPoints[0].y - spacing * 7 / 16,
+ HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
+ drawCenteredText(g2d, getPolarity() == DCPolarity.CENTER_NEGATIVE ? "_" : "+",
+ controlPoints[2].x, controlPoints[2].y - spacing * 3 / 4,
+ HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
+ }
+ }
+
+ @Override
+ public void drawIcon(Graphics2D g2d, int width, int height) {
+ int margin = 2 * 32 / width;
+ int diameter = getClosestOdd(width - margin);
+ g2d.setColor(BODY_COLOR);
+ g2d.fillOval((width - diameter) / 2, (height - diameter) / 2, diameter, diameter);
+ g2d.setColor(BORDER_COLOR);
+ g2d.drawOval((width - diameter) / 2, (height - diameter) / 2, diameter, diameter);
+ int rectWidth = getClosestOdd(((width - 2 * margin) / Math.sqrt(2)) - margin / 2);
+ g2d.setColor(PHENOLIC_COLOR);
+ g2d.fillRect((width - rectWidth) / 2, (height - rectWidth) / 2, rectWidth, rectWidth);
+ g2d.setColor(PHENOLIC_COLOR.darker());
+ g2d.drawRect((width - rectWidth) / 2, (height - rectWidth) / 2, rectWidth, rectWidth);
+ int lugWidth = 4 * 32 / width;
+ g2d.setColor(METAL_COLOR);
+ g2d.drawLine((width - lugWidth) / 2, height / 3, (width + lugWidth) / 2, height / 3);
+ g2d
+ .drawLine(width * 2 / 3, (height - lugWidth) / 2, width * 2 / 3,
+ (height + lugWidth) / 2);
+ g2d.fillOval((width - lugWidth) / 2, height * 2 / 3 - lugWidth / 2, lugWidth, lugWidth);
+ g2d.fillRect(width / 2 - lugWidth * 3 / 2, height * 2 / 3 - lugWidth / 2, lugWidth * 3 / 2,
+ lugWidth);
+ g2d.setColor(PHENOLIC_COLOR);
+ g2d.fillOval((width - margin) / 2, height * 2 / 3 - margin / 2, margin, margin);
+ }
+
+ @Override
+ public int getControlPointCount() {
+ return controlPoints.length;
+ }
+
+ @Override
+ public Point getControlPoint(int index) {
+ return controlPoints[index];
+ }
+
+ @Override
+ public void setControlPoint(Point point, int index) {
+ controlPoints[index].setLocation(point);
+ this.body = null;
+ }
+
+ @Override
+ public VisibilityPolicy getControlPointVisibilityPolicy(int index) {
+ return VisibilityPolicy.NEVER;
+ }
+
+ @Override
+ public boolean isControlPointSticky(int index) {
+ return true;
+ }
+
+ @EditableProperty
+ @Override
+ public String getValue() {
+ return value;
+ }
+
+ @Override
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ @EditableProperty
+ public DCPolarity getPolarity() {
+ if (polarity == null) {
+ polarity = DCPolarity.CENTER_NEGATIVE;
+ }
+ return polarity;
+ }
+
+ public void setPolarity(DCPolarity polarity) {
+ this.polarity = polarity;
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/electromechanical/ToggleSwitchType.java b/diylc-library/src/org/diylc/components/electromechanical/ToggleSwitchType.java
new file mode 100644
index 00000000..6a9e0c2e
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/electromechanical/ToggleSwitchType.java
@@ -0,0 +1,11 @@
+package org.diylc.components.electromechanical;
+
+public enum ToggleSwitchType {
+
+ SPST, SPDT, DPDT, _3PDT, _4PDT, _5PDT;
+
+ @Override
+ public String toString() {
+ return name().replace("_", "");
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/guitar/HumbuckerPickup.java b/diylc-library/src/org/diylc/components/guitar/HumbuckerPickup.java
new file mode 100644
index 00000000..41c91200
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/guitar/HumbuckerPickup.java
@@ -0,0 +1,268 @@
+package org.diylc.components.guitar;
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Composite;
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Area;
+import java.awt.geom.Ellipse2D;
+import java.awt.geom.RoundRectangle2D;
+
+import org.diylc.appframework.miscutils.ConfigurationManager;
+import org.diylc.common.HorizontalAlignment;
+import org.diylc.common.IPlugInPort;
+import org.diylc.common.ObjectCache;
+import org.diylc.common.Orientation;
+import org.diylc.common.VerticalAlignment;
+import org.diylc.components.AbstractTransparentComponent;
+import org.diylc.core.ComponentState;
+import org.diylc.core.IDIYComponent;
+import org.diylc.core.IDrawingObserver;
+import org.diylc.core.Project;
+import org.diylc.core.Theme;
+import org.diylc.core.VisibilityPolicy;
+import org.diylc.core.annotations.ComponentDescriptor;
+import org.diylc.core.annotations.EditableProperty;
+import org.diylc.core.measures.Size;
+import org.diylc.core.measures.SizeUnit;
+import org.diylc.utils.Constants;
+
+@ComponentDescriptor(name = "Humbucker Pickup", category = "Guitar", author = "Branislav Stojkovic", description = "PAF-style humbucker guitar pickup", stretchable = false, zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "PKP")
+public class HumbuckerPickup extends AbstractTransparentComponent {
+
+ private static final long serialVersionUID = 1L;
+
+ private static Color BODY_COLOR = Color.lightGray;
+ private static Color POINT_COLOR = Color.darkGray;
+ private static Size WIDTH = new Size(36.5d, SizeUnit.mm);
+ private static Size LENGTH = new Size(68.58d, SizeUnit.mm);
+ private static Size LIP_WIDTH = new Size(12.7d, SizeUnit.mm);
+ private static Size LIP_LENGTH = new Size(7.9d, SizeUnit.mm);
+ private static Size EDGE_RADIUS = new Size(4d, SizeUnit.mm);
+ private static Size POINT_MARGIN = new Size(5d, SizeUnit.mm);
+ private static Size POINT_SIZE = new Size(3d, SizeUnit.mm);
+ private static Size LIP_HOLE_SIZE = new Size(1.5d, SizeUnit.mm);
+ private static Size POLE_SIZE = new Size(3d, SizeUnit.mm);
+ private static Size POLE_SPACING = new Size(10.1d, SizeUnit.mm);
+ private static Size COIL_SPACING = new Size(18d, SizeUnit.mm);
+
+ private String value = "";
+ private Point controlPoint = new Point(0, 0);
+ transient Shape[] body;
+ private Orientation orientation = Orientation.DEFAULT;
+ private Color color = BODY_COLOR;
+
+ @Override
+ public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode,
+ Project project, IDrawingObserver drawingObserver) {
+ Shape[] body = getBody();
+
+ g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
+ if (componentState != ComponentState.DRAGGING) {
+ Composite oldComposite = g2d.getComposite();
+ if (alpha < MAX_ALPHA) {
+ g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha
+ / MAX_ALPHA));
+ }
+ g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : color);
+ g2d.fill(body[0]);
+ g2d.fill(body[1]);
+ g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : POINT_COLOR);
+ g2d.fill(body[2]);
+ g2d.setComposite(oldComposite);
+ }
+
+ Color finalBorderColor;
+ if (outlineMode) {
+ Theme theme = (Theme) ConfigurationManager.getInstance().readObject(
+ IPlugInPort.THEME_KEY, Constants.DEFAULT_THEME);
+ finalBorderColor = componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? SELECTION_COLOR : theme
+ .getOutlineColor();
+ } else {
+ finalBorderColor = componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? SELECTION_COLOR : color.darker();
+ }
+
+ g2d.setColor(finalBorderColor);
+ g2d.draw(body[0]);
+ g2d.draw(body[1]);
+ if (!outlineMode) {
+ g2d.setColor(color.darker());
+ g2d.draw(body[3]);
+ }
+
+ Color finalLabelColor;
+ if (outlineMode) {
+ Theme theme = (Theme) ConfigurationManager.getInstance().readObject(
+ IPlugInPort.THEME_KEY, Constants.DEFAULT_THEME);
+ finalLabelColor = componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? LABEL_COLOR_SELECTED : theme
+ .getOutlineColor();
+ } else {
+ finalLabelColor = componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? LABEL_COLOR_SELECTED
+ : LABEL_COLOR;
+ }
+ g2d.setColor(finalLabelColor);
+ g2d.setFont(LABEL_FONT);
+ Rectangle bounds = body[0].getBounds();
+ drawCenteredText(g2d, value, bounds.x + bounds.width / 2, bounds.y + bounds.height / 2,
+ HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
+ }
+
+ public Shape[] getBody() {
+ if (body == null) {
+ body = new Shape[4];
+
+ int x = controlPoint.x;
+ int y = controlPoint.y;
+ int width = (int) WIDTH.convertToPixels();
+ int length = (int) LENGTH.convertToPixels();
+ int lipWidth = (int) LIP_WIDTH.convertToPixels();
+ int lipLength = (int) LIP_LENGTH.convertToPixels();
+ int edgeRadius = (int) EDGE_RADIUS.convertToPixels();
+ int pointMargin = (int) POINT_MARGIN.convertToPixels();
+ int pointSize = getClosestOdd(POINT_SIZE.convertToPixels());
+ int lipHoleSize = getClosestOdd(LIP_HOLE_SIZE.convertToPixels());
+
+ body[0] = new Area(new RoundRectangle2D.Double(x + pointMargin - length, y
+ - pointMargin, length, width, edgeRadius, edgeRadius));
+ body[1] = new Area(new RoundRectangle2D.Double(x + pointMargin - length - lipLength, y
+ - pointMargin + width / 2 - lipWidth / 2, length + 2 * lipLength, lipWidth,
+ edgeRadius / 2, edgeRadius / 2));
+ Area lipArea = new Area(body[1]);
+ lipArea.subtract(new Area(body[0]));
+ lipArea.subtract(new Area(new Ellipse2D.Double(
+ x + pointMargin - length - lipLength / 2, y - pointMargin + width / 2
+ - lipHoleSize / 2, lipHoleSize, lipHoleSize)));
+ lipArea.subtract(new Area(new Ellipse2D.Double(x + pointMargin + lipLength / 2, y
+ - pointMargin + width / 2 - lipHoleSize / 2, lipHoleSize, lipHoleSize)));
+ body[1] = lipArea;
+ body[2] = new Area(new Ellipse2D.Double(x - pointSize / 2, y - pointSize / 2,
+ pointSize, pointSize));
+
+ int poleSize = (int) POLE_SIZE.convertToPixels();
+ int poleSpacing = (int) POLE_SPACING.convertToPixels();
+ int coilSpacing = (int) COIL_SPACING.convertToPixels();
+ int coilMargin = (width - coilSpacing) / 2;
+ int poleMargin = (length - poleSpacing * 5) / 2;
+ Area poleArea = new Area();
+ for (int i = 0; i < 6; i++) {
+ Ellipse2D pole = new Ellipse2D.Double(x + pointMargin - length + poleMargin + i
+ * poleSpacing - poleSize / 2, y - pointMargin + coilMargin - poleSize / 2,
+ poleSize, poleSize);
+ poleArea.add(new Area(pole));
+ pole = new Ellipse2D.Double(x + pointMargin - length + poleMargin + i * poleSpacing
+ - poleSize / 2, y - pointMargin + width - coilMargin - poleSize / 2,
+ poleSize, poleSize);
+ poleArea.add(new Area(pole));
+ }
+ body[3] = poleArea;
+
+ // Rotate if needed
+ if (orientation != Orientation.DEFAULT) {
+ double theta = 0;
+ switch (orientation) {
+ case _90:
+ theta = Math.PI / 2;
+ break;
+ case _180:
+ theta = Math.PI;
+ break;
+ case _270:
+ theta = Math.PI * 3 / 2;
+ break;
+ }
+ AffineTransform rotation = AffineTransform.getRotateInstance(theta, x, y);
+ for (Shape shape : body) {
+ Area area = (Area) shape;
+ area.transform(rotation);
+ }
+ }
+ }
+ return body;
+ }
+
+ @Override
+ public void drawIcon(Graphics2D g2d, int width, int height) {
+ int baseWidth = 16 * width / 32;
+ int baseLength = 27 * width / 32;
+
+ g2d.setColor(BODY_COLOR);
+ g2d.fillRoundRect((width - baseWidth / 4) / 2, 0, baseWidth / 4, height - 1,
+ 2 * width / 32, 2 * width / 32);
+ g2d.setColor(BODY_COLOR.darker());
+ g2d.drawRoundRect((width - baseWidth / 4) / 2, 0, baseWidth / 4, height - 1,
+ 2 * width / 32, 2 * width / 32);
+
+ g2d.setColor(BODY_COLOR);
+ g2d.fillRoundRect((width - baseWidth) / 2, (height - baseLength) / 2, baseWidth,
+ baseLength, 4 * width / 32, 4 * width / 32);
+ g2d.setColor(BODY_COLOR.darker());
+ g2d.drawRoundRect((width - baseWidth) / 2, (height - baseLength) / 2, baseWidth,
+ baseLength, 4 * width / 32, 4 * width / 32);
+ }
+
+ @Override
+ public int getControlPointCount() {
+ return 1;
+ }
+
+ @Override
+ public VisibilityPolicy getControlPointVisibilityPolicy(int index) {
+ return VisibilityPolicy.WHEN_SELECTED;
+ }
+
+ @Override
+ public boolean isControlPointSticky(int index) {
+ return true;
+ }
+
+ @Override
+ public Point getControlPoint(int index) {
+ return controlPoint;
+ }
+
+ @Override
+ public void setControlPoint(Point point, int index) {
+ this.controlPoint.setLocation(point);
+ // Invalidate the body
+ body = null;
+ }
+
+ @EditableProperty(name = "Model")
+ @Override
+ public String getValue() {
+ return value;
+ }
+
+ @Override
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ @EditableProperty
+ public Orientation getOrientation() {
+ return orientation;
+ }
+
+ public void setOrientation(Orientation orientation) {
+ this.orientation = orientation;
+ // Invalidate the body
+ body = null;
+ }
+
+ @EditableProperty
+ public Color getColor() {
+ return color;
+ }
+
+ public void setColor(Color color) {
+ this.color = color;
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/guitar/LeverSwitch.java b/diylc-library/src/org/diylc/components/guitar/LeverSwitch.java
new file mode 100644
index 00000000..fe5e368e
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/guitar/LeverSwitch.java
@@ -0,0 +1,370 @@
+package org.diylc.components.guitar;
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Composite;
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.Shape;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Area;
+import java.awt.geom.Ellipse2D;
+import java.awt.geom.Rectangle2D;
+import java.awt.geom.RoundRectangle2D;
+
+import org.diylc.appframework.miscutils.ConfigurationManager;
+import org.diylc.common.IPlugInPort;
+import org.diylc.common.ObjectCache;
+import org.diylc.common.Orientation;
+import org.diylc.components.AbstractTransparentComponent;
+import org.diylc.core.ComponentState;
+import org.diylc.core.IDIYComponent;
+import org.diylc.core.IDrawingObserver;
+import org.diylc.core.Project;
+import org.diylc.core.Theme;
+import org.diylc.core.VisibilityPolicy;
+import org.diylc.core.annotations.ComponentDescriptor;
+import org.diylc.core.annotations.EditableProperty;
+import org.diylc.core.measures.Size;
+import org.diylc.core.measures.SizeUnit;
+import org.diylc.utils.Constants;
+
+@ComponentDescriptor(name = "Lever Switch", category = "Guitar", author = "Branislav Stojkovic", description = "Strat-style lever switch", stretchable = false, zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "SW")
+public class LeverSwitch extends AbstractTransparentComponent {
+
+ private static final long serialVersionUID = 1L;
+
+ private static Color BASE_COLOR = Color.lightGray;
+ private static Color WAFER_COLOR = Color.decode("#CD8500");
+
+ private static Size BASE_WIDTH = new Size(10d, SizeUnit.mm);
+ private static Size BASE_LENGTH = new Size(47.5d, SizeUnit.mm);
+ private static Size WAFER_LENGTH = new Size(40d, SizeUnit.mm);
+ private static Size WAFER_SPACING = new Size(7.62d, SizeUnit.mm);
+ private static Size WAFER_THICKNESS = new Size(1.27d, SizeUnit.mm);
+ private static Size HOLE_SIZE = new Size(2d, SizeUnit.mm);
+ private static Size HOLE_SPACING = new Size(41.2d, SizeUnit.mm);
+ private static Size TERMINAL_WIDTH = new Size(2d, SizeUnit.mm);
+ private static Size TERMINAL_LENGTH = new Size(0.1d, SizeUnit.in);
+ private static Size TERMINAL_SPACING = new Size(0.1d, SizeUnit.in);
+
+ private String value = "";
+ private Point[] controlPoints = new Point[] { new Point(0, 0) };
+ transient Shape[] body;
+ private Orientation orientation = Orientation.DEFAULT;
+ private LeverSwitchType type = LeverSwitchType.DP3T;
+
+ public LeverSwitch() {
+ super();
+ updateControlPoints();
+ }
+
+ @Override
+ public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode,
+ Project project, IDrawingObserver drawingObserver) {
+ Shape[] body = getBody();
+
+ g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
+ if (componentState != ComponentState.DRAGGING) {
+ Composite oldComposite = g2d.getComposite();
+ if (alpha < MAX_ALPHA) {
+ g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha
+ / MAX_ALPHA));
+ }
+ g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : BASE_COLOR);
+ g2d.fill(body[0]);
+ g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : WAFER_COLOR);
+ g2d.fill(body[1]);
+ g2d.setComposite(oldComposite);
+ }
+
+ Color finalBorderColor;
+ if (outlineMode) {
+ Theme theme = (Theme) ConfigurationManager.getInstance().readObject(
+ IPlugInPort.THEME_KEY, Constants.DEFAULT_THEME);
+ finalBorderColor = componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? SELECTION_COLOR : theme
+ .getOutlineColor();
+ } else {
+ finalBorderColor = componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? SELECTION_COLOR : BASE_COLOR
+ .darker();
+ }
+
+ g2d.setColor(finalBorderColor);
+ g2d.draw(body[0]);
+
+ if (outlineMode) {
+ Theme theme = (Theme) ConfigurationManager.getInstance().readObject(
+ IPlugInPort.THEME_KEY, Constants.DEFAULT_THEME);
+ finalBorderColor = componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? SELECTION_COLOR : theme
+ .getOutlineColor();
+ } else {
+ finalBorderColor = componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? SELECTION_COLOR : WAFER_COLOR
+ .darker();
+ }
+
+ g2d.draw(body[1]);
+
+ g2d.setColor(METAL_COLOR);
+ g2d.fill(body[2]);
+ g2d.setColor(METAL_COLOR.darker());
+ g2d.draw(body[2]);
+ }
+
+ public Shape[] getBody() {
+ if (body == null) {
+ body = new Shape[3];
+
+ int x = controlPoints[0].x;
+ int y = controlPoints[0].y;
+ int baseWidth = (int) BASE_WIDTH.convertToPixels();
+ int baseLength = (int) BASE_LENGTH.convertToPixels();
+ int holeSize = getClosestOdd(HOLE_SIZE.convertToPixels());
+ int holeSpacing = (int) HOLE_SPACING.convertToPixels();
+ int waferLength = (int) WAFER_LENGTH.convertToPixels();
+ int waferSpacing = (int) WAFER_SPACING.convertToPixels();
+ int waferThickness = (int) WAFER_THICKNESS.convertToPixels();
+ int terminalSpacing = (int) TERMINAL_SPACING.convertToPixels();
+ int terminalLength = getClosestOdd(TERMINAL_LENGTH.convertToPixels());
+ int terminalWidth = getClosestOdd(TERMINAL_WIDTH.convertToPixels());
+
+ int baseX = x - terminalLength / 2 - waferSpacing;
+ int baseY = y
+ - (baseLength - terminalSpacing * (type == LeverSwitchType.DP3T ? 7 : 12)) / 2;
+ Area baseArea = new Area(new Rectangle2D.Double(baseX, baseY, baseWidth, baseLength));
+ baseArea.subtract(new Area(new Ellipse2D.Double(baseX + baseWidth / 2 - holeSize / 2,
+ baseY + (baseLength - holeSpacing) / 2 - holeSize / 2, holeSize, holeSize)));
+ baseArea.subtract(new Area(new Ellipse2D.Double(baseX + baseWidth / 2 - holeSize / 2,
+ baseY + (baseLength - holeSpacing) / 2 - holeSize / 2 + holeSpacing, holeSize,
+ holeSize)));
+ body[0] = baseArea;
+
+ Area waferArea = new Area(new Rectangle2D.Double(x - terminalLength / 2
+ - waferThickness / 2,
+ y - (waferLength - terminalSpacing * (type == LeverSwitchType.DP3T ? 7 : 12))
+ / 2, waferThickness, waferLength));
+
+ if (type == LeverSwitchType._4P5T) {
+ waferArea.add(new Area(new Rectangle2D.Double(x - terminalLength / 2
+ - waferThickness / 2 + waferSpacing, y
+ - (waferLength - terminalSpacing * (type == LeverSwitchType.DP3T ? 7 : 12))
+ / 2, waferThickness, waferLength)));
+ }
+ body[1] = waferArea;
+
+ double theta = 0;
+ // Rotate if needed
+ if (orientation != Orientation.DEFAULT) {
+ switch (orientation) {
+ case _90:
+ theta = Math.PI / 2;
+ break;
+ case _180:
+ theta = Math.PI;
+ break;
+ case _270:
+ theta = Math.PI * 3 / 2;
+ break;
+ }
+ }
+
+ Area terminalArea = new Area();
+ for (Point point : controlPoints) {
+ Area terminal = new Area(new RoundRectangle2D.Double(point.x - terminalLength / 2,
+ point.y - terminalWidth / 2, terminalLength, terminalWidth,
+ terminalWidth / 2, terminalWidth / 2));
+ terminal.subtract(new Area(
+ new RoundRectangle2D.Double(point.x - terminalLength / 4, point.y
+ - terminalWidth / 4, terminalLength / 2, terminalWidth / 2,
+ terminalWidth / 2, terminalWidth / 2)));
+ // Rotate the terminal if needed
+ if (theta != 0) {
+ AffineTransform rotation = AffineTransform.getRotateInstance(theta, point.x,
+ point.y);
+ terminal.transform(rotation);
+ }
+ terminalArea.add(terminal);
+ }
+ body[2] = terminalArea;
+
+ // Rotate if needed
+ if (theta != 0) {
+ AffineTransform rotation = AffineTransform.getRotateInstance(theta, x, y);
+ // Skip the last one because it's already rotated
+ for (int i = 0; i < body.length - 1; i++) {
+ Shape shape = body[i];
+ Area area = (Area) shape;
+ area.transform(rotation);
+ }
+ }
+ }
+ return body;
+ }
+
+ private void updateControlPoints() {
+ int x = controlPoints[0].x;
+ int y = controlPoints[0].y;
+ int waferSpacing = (int) WAFER_SPACING.convertToPixels();
+ int terminalSpacing = (int) TERMINAL_SPACING.convertToPixels();
+ int terminalLength = (int) TERMINAL_LENGTH.convertToPixels();
+
+ switch (type) {
+ case DP3T:
+ controlPoints = new Point[8];
+ for (int i = 0; i < 8; i++) {
+ controlPoints[i] = new Point(x - (i % 2 == 1 ? terminalLength : 0), y + i
+ * terminalSpacing);
+ }
+ break;
+ case DP5T:
+ controlPoints = new Point[12];
+ for (int i = 0; i < 12; i++) {
+ controlPoints[i] = new Point(x, y + i * terminalSpacing
+ + (i >= 6 ? terminalSpacing : 0));
+ }
+ break;
+ case _4P5T:
+ controlPoints = new Point[24];
+ for (int i = 0; i < 12; i++) {
+ controlPoints[i] = new Point(x, y + i * terminalSpacing
+ + (i >= 6 ? terminalSpacing : 0));
+ controlPoints[i + 12] = new Point(x + waferSpacing, y + i * terminalSpacing
+ + (i >= 6 ? terminalSpacing : 0));
+ }
+ break;
+ }
+
+ // Rotate if needed
+ if (orientation != Orientation.DEFAULT) {
+ double theta = 0;
+ switch (orientation) {
+ case _90:
+ theta = Math.PI / 2;
+ break;
+ case _180:
+ theta = Math.PI;
+ break;
+ case _270:
+ theta = Math.PI * 3 / 2;
+ break;
+ }
+ AffineTransform rotation = AffineTransform.getRotateInstance(theta, x, y);
+ for (Point point : controlPoints) {
+ rotation.transform(point, point);
+ }
+ }
+ }
+
+ @Override
+ public void drawIcon(Graphics2D g2d, int width, int height) {
+ g2d.setClip(width / 32, width / 32, width, height);
+ g2d.setColor(BASE_COLOR);
+ g2d.fillRect(0, 0, width * 2 / 3, height);
+ g2d.setColor(BASE_COLOR.darker());
+ g2d.drawRect(0, 0, width * 2 / 3, height);
+ g2d.setColor(WAFER_COLOR);
+ g2d.fillRect(width / 8 * 3, 0, width / 8, height);
+ g2d.setColor(WAFER_COLOR.darker());
+ g2d.drawRect(width / 8 * 3, 0, width / 8, height);
+ Area terminals = new Area();
+ int terminalLength = getClosestOdd(11 * width / 32);
+ int terminalWidth = getClosestOdd(7 * width / 32);
+ Area terminal = new Area(new RoundRectangle2D.Double(width / 16 * 7, 4 * width / 32,
+ terminalLength, terminalWidth, terminalWidth / 2, terminalWidth / 2));
+ terminal.subtract(new Area(new RoundRectangle2D.Double(width / 16 * 7 + terminalLength / 4
+ + 1, 4 * width / 32 + terminalWidth / 4 + 1, terminalLength / 2, terminalWidth / 2,
+ terminalWidth / 4, terminalWidth / 4)));
+ terminals.add(terminal);
+ terminal = new Area(terminal);
+ terminal.transform(AffineTransform.getTranslateInstance(-terminalLength, terminalWidth + 2
+ * width / 32));
+ terminals.add(terminal);
+ terminal = new Area(terminal);
+ terminal.transform(AffineTransform.getTranslateInstance(terminalLength, terminalWidth + 2
+ * width / 32));
+ terminals.add(terminal);
+ g2d.setColor(METAL_COLOR);
+ g2d.fill(terminals);
+ g2d.setColor(METAL_COLOR.darker());
+ g2d.draw(terminals);
+ }
+
+ @Override
+ public int getControlPointCount() {
+ return controlPoints.length;
+ }
+
+ @Override
+ public VisibilityPolicy getControlPointVisibilityPolicy(int index) {
+ return VisibilityPolicy.NEVER;
+ }
+
+ @Override
+ public boolean isControlPointSticky(int index) {
+ return true;
+ }
+
+ @Override
+ public Point getControlPoint(int index) {
+ return controlPoints[index];
+ }
+
+ @Override
+ public void setControlPoint(Point point, int index) {
+ this.controlPoints[index].setLocation(point);
+ // Invalidate the body
+ body = null;
+ }
+
+ @Override
+ public String getValue() {
+ return value;
+ }
+
+ @Override
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ @EditableProperty
+ public Orientation getOrientation() {
+ return orientation;
+ }
+
+ @EditableProperty
+ public LeverSwitchType getType() {
+ return type;
+ }
+
+ public void setType(LeverSwitchType type) {
+ this.type = type;
+ updateControlPoints();
+ // Invalidate body
+ this.body = null;
+ }
+
+ public void setOrientation(Orientation orientation) {
+ this.orientation = orientation;
+ updateControlPoints();
+ // Invalidate the body
+ body = null;
+ }
+
+ public enum LeverSwitchType {
+ DP3T("DP3T (Standard Strat)"), _4P5T("4P5T (Super/Mega)"), DP5T("DP5T");
+
+ private String title;
+
+ private LeverSwitchType(String title) {
+ this.title = title;
+ }
+
+ @Override
+ public String toString() {
+ return title;
+ }
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/guitar/SingleCoilPickup.java b/diylc-library/src/org/diylc/components/guitar/SingleCoilPickup.java
new file mode 100644
index 00000000..b428a50f
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/guitar/SingleCoilPickup.java
@@ -0,0 +1,254 @@
+package org.diylc.components.guitar;
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Composite;
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.Polygon;
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Area;
+import java.awt.geom.Ellipse2D;
+import java.awt.geom.RoundRectangle2D;
+
+import org.diylc.appframework.miscutils.ConfigurationManager;
+import org.diylc.common.HorizontalAlignment;
+import org.diylc.common.IPlugInPort;
+import org.diylc.common.ObjectCache;
+import org.diylc.common.Orientation;
+import org.diylc.common.VerticalAlignment;
+import org.diylc.components.AbstractTransparentComponent;
+import org.diylc.core.ComponentState;
+import org.diylc.core.IDIYComponent;
+import org.diylc.core.IDrawingObserver;
+import org.diylc.core.Project;
+import org.diylc.core.Theme;
+import org.diylc.core.VisibilityPolicy;
+import org.diylc.core.annotations.ComponentDescriptor;
+import org.diylc.core.annotations.EditableProperty;
+import org.diylc.core.measures.Size;
+import org.diylc.core.measures.SizeUnit;
+import org.diylc.utils.Constants;
+
+@ComponentDescriptor(name = "Strat Single Coil Pickup", category = "Guitar", author = "Branislav Stojkovic", description = "Strat-style single coil guitar pickup", stretchable = false, zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "PKP")
+public class SingleCoilPickup extends AbstractTransparentComponent {
+
+ private static final long serialVersionUID = 1L;
+
+ private static Color BODY_COLOR = Color.darkGray;
+ private static Color POINT_COLOR = Color.lightGray;
+ private static Size WIDTH = new Size(15.5d, SizeUnit.mm);
+ private static Size LENGTH = new Size(83.82d, SizeUnit.mm);
+ private static Size LIP_WIDTH = new Size(5d, SizeUnit.mm);
+ private static Size LIP_LENGTH = new Size(20d, SizeUnit.mm);
+ private static Size POINT_SIZE = new Size(3d, SizeUnit.mm);
+ private static Size HOLE_SIZE = new Size(2d, SizeUnit.mm);
+ private static Size HOLE_MARGIN = new Size(4d, SizeUnit.mm);
+ private static Size POLE_SIZE = new Size(3d, SizeUnit.mm);
+ private static Size POLE_SPACING = new Size(11.68d, SizeUnit.mm);
+
+ private String value = "";
+ private Point controlPoint = new Point(0, 0);
+ transient Shape[] body;
+ private Orientation orientation = Orientation.DEFAULT;
+ private Color color = BODY_COLOR;
+
+ @Override
+ public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode,
+ Project project, IDrawingObserver drawingObserver) {
+ Shape[] body = getBody();
+
+ g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
+ if (componentState != ComponentState.DRAGGING) {
+ Composite oldComposite = g2d.getComposite();
+ if (alpha < MAX_ALPHA) {
+ g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha
+ / MAX_ALPHA));
+ }
+ g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : color);
+ g2d.fill(body[0]);
+ g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : POINT_COLOR);
+ g2d.fill(body[1]);
+ g2d.setComposite(oldComposite);
+ }
+
+ Color finalBorderColor;
+ if (outlineMode) {
+ Theme theme = (Theme) ConfigurationManager.getInstance().readObject(
+ IPlugInPort.THEME_KEY, Constants.DEFAULT_THEME);
+ finalBorderColor = componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? SELECTION_COLOR : theme
+ .getOutlineColor();
+ } else {
+ finalBorderColor = componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? SELECTION_COLOR : color
+ .brighter();
+ }
+
+ g2d.setColor(finalBorderColor);
+ g2d.draw(body[0]);
+ if (!outlineMode) {
+ g2d.draw(body[2]);
+ }
+
+ Color finalLabelColor;
+ if (outlineMode) {
+ Theme theme = (Theme) ConfigurationManager.getInstance().readObject(
+ IPlugInPort.THEME_KEY, Constants.DEFAULT_THEME);
+ finalLabelColor = componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? LABEL_COLOR_SELECTED : theme
+ .getOutlineColor();
+ } else {
+ finalLabelColor = componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? LABEL_COLOR_SELECTED
+ : LABEL_COLOR;
+ }
+ g2d.setColor(finalLabelColor);
+ g2d.setFont(LABEL_FONT);
+ Rectangle bounds = body[0].getBounds();
+ drawCenteredText(g2d, value, bounds.x + bounds.width / 2, bounds.y + bounds.height / 2,
+ HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
+ }
+
+ public Shape[] getBody() {
+ if (body == null) {
+ body = new Shape[3];
+
+ int x = controlPoint.x;
+ int y = controlPoint.y;
+ int width = (int) WIDTH.convertToPixels();
+ int length = (int) LENGTH.convertToPixels();
+ int lipWidth = (int) LIP_WIDTH.convertToPixels();
+ int lipLength = (int) LIP_LENGTH.convertToPixels();
+ int pointSize = getClosestOdd(POINT_SIZE.convertToPixels());
+ int holeSize = getClosestOdd(HOLE_SIZE.convertToPixels());
+ int holeMargin = getClosestOdd(HOLE_MARGIN.convertToPixels());
+
+ Area mainArea = new Area(new RoundRectangle2D.Double(x - length / 2, y - lipWidth / 2
+ - width, length, width, width, width));
+ mainArea.add(new Area(new Polygon(new int[] { x - length / 2 + width / 2,
+ x + length / 2 - width / 2, x + lipLength / 2, x - lipLength / 2 }, new int[] {
+ y - lipWidth / 2, y - lipWidth / 2, y + lipWidth / 2, y + lipWidth / 2 }, 4)));
+ // Cutout holes
+ mainArea.subtract(new Area(new Ellipse2D.Double(x - length / 2 + holeMargin - holeSize
+ / 2, y - lipWidth / 2 - width / 2 - holeSize / 2, holeSize, holeSize)));
+ mainArea.subtract(new Area(new Ellipse2D.Double(x + length / 2 - holeMargin - holeSize
+ / 2, y - lipWidth / 2 - width / 2 - holeSize / 2, holeSize, holeSize)));
+
+ body[0] = mainArea;
+
+ body[1] = new Area(new Ellipse2D.Double(x - pointSize / 2, y - pointSize / 2,
+ pointSize, pointSize));
+
+ int poleSize = (int) POLE_SIZE.convertToPixels();
+ int poleSpacing = (int) POLE_SPACING.convertToPixels();
+ int poleMargin = (length - poleSpacing * 5) / 2;
+ Area poleArea = new Area();
+ for (int i = 0; i < 6; i++) {
+ Ellipse2D pole = new Ellipse2D.Double(
+ x - length / 2 + poleMargin + i * poleSpacing, y - lipWidth / 2 - width / 2
+ - poleSize / 2, poleSize, poleSize);
+ poleArea.add(new Area(pole));
+ }
+ body[2] = poleArea;
+
+ // Rotate if needed
+ if (orientation != Orientation.DEFAULT) {
+ double theta = 0;
+ switch (orientation) {
+ case _90:
+ theta = Math.PI / 2;
+ break;
+ case _180:
+ theta = Math.PI;
+ break;
+ case _270:
+ theta = Math.PI * 3 / 2;
+ break;
+ }
+ AffineTransform rotation = AffineTransform.getRotateInstance(theta, x, y);
+ for (Shape shape : body) {
+ Area area = (Area) shape;
+ area.transform(rotation);
+ }
+ }
+ }
+ return body;
+ }
+
+ @Override
+ public void drawIcon(Graphics2D g2d, int width, int height) {
+ int bodyWidth = 8 * width / 32;
+ int bodyLength = 30 * width / 32;
+ g2d.setColor(BODY_COLOR);
+ g2d.fillRoundRect((width - bodyWidth) / 2, (height - bodyLength) / 2, bodyWidth,
+ bodyLength, bodyWidth, bodyWidth);
+ g2d.fillPolygon(new int[] { width * 9 / 16, width * 9 / 16, width * 11 / 16, width * 11 / 16 },
+ new int[] { (height - bodyLength) / 2, (height + bodyLength) / 2, height * 5 / 8,
+ height * 3 / 8 }, 4);
+
+ g2d.setColor(Color.lightGray);
+ g2d.drawLine(width / 2, 4 * width / 32, width / 2, 4 * width / 32);
+ g2d.drawLine(width / 2, height - 4 * width / 32, width / 2, height - 4 * width / 32);
+ }
+
+ @Override
+ public int getControlPointCount() {
+ return 1;
+ }
+
+ @Override
+ public VisibilityPolicy getControlPointVisibilityPolicy(int index) {
+ return VisibilityPolicy.WHEN_SELECTED;
+ }
+
+ @Override
+ public boolean isControlPointSticky(int index) {
+ return true;
+ }
+
+ @Override
+ public Point getControlPoint(int index) {
+ return controlPoint;
+ }
+
+ @Override
+ public void setControlPoint(Point point, int index) {
+ this.controlPoint.setLocation(point);
+ // Invalidate the body
+ body = null;
+ }
+
+ @EditableProperty(name = "Model")
+ @Override
+ public String getValue() {
+ return value;
+ }
+
+ @Override
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ @EditableProperty
+ public Orientation getOrientation() {
+ return orientation;
+ }
+
+ public void setOrientation(Orientation orientation) {
+ this.orientation = orientation;
+ // Invalidate the body
+ body = null;
+ }
+
+ @EditableProperty
+ public Color getColor() {
+ return color;
+ }
+
+ public void setColor(Color color) {
+ this.color = color;
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/misc/BOM.java b/diylc-library/src/org/diylc/components/misc/BOM.java
new file mode 100644
index 00000000..d84f0153
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/misc/BOM.java
@@ -0,0 +1,186 @@
+package org.diylc.components.misc;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.geom.Rectangle2D;
+import java.util.Iterator;
+import java.util.List;
+
+import org.diylc.components.AbstractComponent;
+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.measures.Size;
+import org.diylc.core.measures.SizeUnit;
+import org.diylc.utils.BomEntry;
+import org.diylc.utils.BomMaker;
+
+@ComponentDescriptor(name = "Bill of Materials", author = "Branislav Stojkovic", category = "Misc", description = "", instanceNamePrefix = "BOM", zOrder = IDIYComponent.TEXT, stretchable = false, bomPolicy = BomPolicy.NEVER_SHOW)
+public class BOM extends AbstractComponent {
+
+ public static Size DEFAULT_SIZE = new Size(10d, SizeUnit.cm);
+ public static Size SPACING = new Size(0.1d, SizeUnit.in);
+ public static Color COLOR = Color.black;
+ public static String DEFAULT_TEXT = "No components to show in the Bill of Materials";
+
+ private static final long serialVersionUID = 1L;
+ private Size size = DEFAULT_SIZE;
+
+ private Point point = new Point(0, 0);
+
+ @Override
+ public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode,
+ Project project, IDrawingObserver drawingObserver) {
+ List bom = BomMaker.getInstance().createBom(project.getComponents());
+ // Cleanup entries that do not have a value set.
+ Iterator iterator = bom.iterator();
+ while (iterator.hasNext()) {
+ BomEntry entry = iterator.next();
+ if (entry.getValue() == null || entry.getName().toLowerCase().contains("bom")) {
+ iterator.remove();
+ }
+ }
+ g2d.setFont(LABEL_FONT);
+ g2d.setColor(componentState == ComponentState.DRAGGING
+ || componentState == ComponentState.SELECTED ? SELECTION_COLOR : COLOR);
+ // Determine maximum name length and maximum value length to calculate
+ // number of columns.
+ FontMetrics fontMetrics = g2d.getFontMetrics();
+ int maxNameWidth = 0;
+ int maxValueWidth = 0;
+ int maxHeight = 0;
+ for (BomEntry entry : bom) {
+ String valueStr;
+ if (entry.getValue() == null || entry.getValue().trim().isEmpty()) {
+ valueStr = "qty " + entry.getQuantity().toString();
+ } else {
+ valueStr = entry.getValue().toString();
+ }
+ Rectangle2D stringBounds = fontMetrics.getStringBounds(entry.getName(), g2d);
+ if (stringBounds.getWidth() > maxNameWidth) {
+ maxNameWidth = (int) stringBounds.getWidth();
+ }
+ if (stringBounds.getHeight() > maxHeight) {
+ maxHeight = (int) stringBounds.getHeight();
+ }
+ stringBounds = fontMetrics.getStringBounds(valueStr, g2d);
+ if (stringBounds.getWidth() > maxValueWidth) {
+ maxValueWidth = (int) stringBounds.getWidth();
+ }
+ if (stringBounds.getHeight() > maxHeight) {
+ maxHeight = (int) stringBounds.getHeight();
+ }
+ }
+ // Calculate maximum entry size.
+ int maxEntrySize = maxNameWidth + maxValueWidth + 2 * (int) SPACING.convertToPixels();
+ int columnCount = (int) size.convertToPixels() / maxEntrySize;
+ if (columnCount == 0)
+ columnCount = 1;
+ int columnWidth = (int) size.convertToPixels() / columnCount;
+ int entriesPerColumn = (int) Math.ceil(1.d * bom.size() / columnCount);
+ if (entriesPerColumn == 0) {
+ g2d.drawString(DEFAULT_TEXT, point.x, point.y);
+ return;
+ }
+ for (int i = 0; i < bom.size(); i++) {
+ String valueStr;
+ BomEntry entry = bom.get(i);
+ if (entry.getValue() == null || entry.getValue().trim().isEmpty()) {
+ valueStr = "qty " + entry.getQuantity().toString();
+ } else {
+ valueStr = entry.getValue().toString();
+ }
+ int columnIndex = i / entriesPerColumn;
+ int rowIndex = i % entriesPerColumn;
+ int x = point.x + columnIndex * columnWidth;
+ int y = point.y + rowIndex * maxHeight;
+ g2d.drawString(entry.getName(), x, y);
+ x += maxNameWidth + SPACING.convertToPixels();
+ g2d.drawString(valueStr, x, y);
+ }
+ }
+
+ @Override
+ public void drawIcon(Graphics2D g2d, int width, int height) {
+ g2d.setColor(Color.white);
+ g2d.fillRect(width / 8, 0, 6 * width / 8, height - 1);
+ g2d.setColor(Color.black);
+ g2d.drawRect(width / 8, 0, 6 * width / 8, height - 1);
+ g2d.setFont(LABEL_FONT.deriveFont(1f * 9 * width / 32).deriveFont(Font.PLAIN));
+
+ FontMetrics fontMetrics = g2d.getFontMetrics();
+ Rectangle2D rect = fontMetrics.getStringBounds("BOM", g2d);
+
+ int textHeight = (int) (rect.getHeight());
+ int textWidth = (int) (rect.getWidth());
+
+ // Center text horizontally and vertically.
+ int x = (width - textWidth) / 2 + 1;
+ int y = textHeight + 2;
+
+ g2d.drawString("BOM", x, y);
+
+ g2d.setFont(g2d.getFont().deriveFont(1f * 5 * width / 32));
+
+ fontMetrics = g2d.getFontMetrics();
+ rect = fontMetrics.getStringBounds("resistors", g2d);
+ x = (width - textWidth) / 2 + 1;
+ y = height / 2 + 2;
+ g2d.drawString("resistors", x, y);
+ y += rect.getHeight() - 1;
+ g2d.drawString("tubes", x, y);
+ y += rect.getHeight() - 1;
+ g2d.drawString("diodes", x, y);
+ }
+
+ @Override
+ public int getControlPointCount() {
+ return 1;
+ }
+
+ @Override
+ public Point getControlPoint(int index) {
+ return point;
+ }
+
+ @Override
+ public boolean isControlPointSticky(int index) {
+ return false;
+ }
+
+ @Override
+ public VisibilityPolicy getControlPointVisibilityPolicy(int index) {
+ return VisibilityPolicy.NEVER;
+ }
+
+ @Override
+ public void setControlPoint(Point point, int index) {
+ this.point.setLocation(point);
+ }
+
+ @EditableProperty(name = "Width")
+ public Size getSize() {
+ return size;
+ }
+
+ public void setSize(Size size) {
+ this.size = size;
+ }
+
+ @Override
+ public Void getValue() {
+ return null;
+ }
+
+ @Override
+ public void setValue(Void value) {
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/misc/GroundSymbol.java b/diylc-library/src/org/diylc/components/misc/GroundSymbol.java
new file mode 100644
index 00000000..b889d7f3
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/misc/GroundSymbol.java
@@ -0,0 +1,149 @@
+package org.diylc.components.misc;
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.Polygon;
+
+import org.diylc.common.ObjectCache;
+import org.diylc.components.AbstractComponent;
+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.measures.Size;
+import org.diylc.core.measures.SizeUnit;
+
+@ComponentDescriptor(name = "Ground Symbol", author = "Branislav Stojkovic", category = "Misc", instanceNamePrefix = "GND", description = "Ground schematic symbol", stretchable = false, zOrder = IDIYComponent.COMPONENT, bomPolicy = BomPolicy.NEVER_SHOW)
+public class GroundSymbol extends AbstractComponent {
+
+ private static final long serialVersionUID = 1L;
+
+ public static Color COLOR = Color.black;
+ public static Size SIZE = new Size(0.15d, SizeUnit.in);
+
+ private Point point = new Point(0, 0);
+ private Color color = COLOR;
+ private Size size = SIZE;
+ private GroundSymbolType type = GroundSymbolType.DEFAULT;
+
+ @Override
+ public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode,
+ Project project, IDrawingObserver drawingObserver) {
+ int sizePx = (int) size.convertToPixels();
+ g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
+ g2d.setColor(color);
+ int x = point.x;
+ int y = point.y;
+ g2d.drawLine(x, y, x, y + sizePx / 6);
+ if (type == GroundSymbolType.DEFAULT) {
+ int delta = sizePx / 7;
+ for (int i = 0; i < 5; i++) {
+ g2d.drawLine(x - sizePx / 2 + delta * i, y + sizePx / 6 * (i + 1), x + sizePx / 2
+ - delta * i, y + sizePx / 6 * (i + 1));
+ }
+ } else {
+ Polygon poly = new Polygon(new int[] { x - sizePx / 2, x + sizePx / 2, x }, new int[] {
+ y + sizePx / 6, y + sizePx / 6, y + sizePx }, 3);
+ g2d.draw(poly);
+ }
+ }
+
+ @Override
+ public void drawIcon(Graphics2D g2d, int width, int height) {
+ int margin = 3 * width / 32;
+ g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
+ g2d.setColor(COLOR);
+ g2d.drawLine(width / 2, margin * 2, width / 2, margin * 3 + height / 5);
+ for (int i = 0; i < 5; i++) {
+ g2d.drawLine(margin * (i + 1), margin * (3 + i) + height / 5, width - margin * (i + 1),
+ margin * (3 + i) + height / 5);
+ }
+ }
+
+ @EditableProperty(name = "Style")
+ public GroundSymbolType getType() {
+ return type;
+ }
+
+ public void setType(GroundSymbolType type) {
+ this.type = type;
+ }
+
+ @Override
+ public Point getControlPoint(int index) {
+ return point;
+ }
+
+ @EditableProperty
+ public Color getColor() {
+ return color;
+ }
+
+ public void setColor(Color color) {
+ this.color = color;
+ }
+
+ @EditableProperty
+ public Size getSize() {
+ return size;
+ }
+
+ public void setSize(Size size) {
+ this.size = size;
+ }
+
+ @Override
+ public int getControlPointCount() {
+ return 1;
+ }
+
+ @Override
+ public boolean isControlPointSticky(int index) {
+ return true;
+ }
+
+ @Override
+ public void setControlPoint(Point point, int index) {
+ this.point = point;
+ }
+
+ @Override
+ public VisibilityPolicy getControlPointVisibilityPolicy(int index) {
+ return VisibilityPolicy.WHEN_SELECTED;
+ }
+
+ @Deprecated
+ @Override
+ public Void getValue() {
+ return null;
+ }
+
+ @Deprecated
+ @Override
+ public void setValue(Void value) {
+ }
+
+ public static enum GroundSymbolType {
+ DEFAULT("Default"), TRIANGLE("Triangle");
+
+ private String title;
+
+ private GroundSymbolType(String title) {
+ this.title = title;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ @Override
+ public String toString() {
+ return title;
+ }
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/misc/IconImageConverter.java b/diylc-library/src/org/diylc/components/misc/IconImageConverter.java
new file mode 100644
index 00000000..6df78527
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/misc/IconImageConverter.java
@@ -0,0 +1,102 @@
+package org.diylc.components.misc;
+
+import java.awt.Image;
+import java.awt.Toolkit;
+import java.awt.image.BufferedImage;
+import java.awt.image.ColorModel;
+import java.awt.image.ImageObserver;
+import java.awt.image.MemoryImageSource;
+import java.awt.image.PixelGrabber;
+import java.io.File;
+import java.io.IOException;
+
+import javax.imageio.ImageIO;
+import javax.swing.ImageIcon;
+
+import org.apache.log4j.Logger;
+
+import com.thoughtworks.xstream.converters.Converter;
+import com.thoughtworks.xstream.converters.MarshallingContext;
+import com.thoughtworks.xstream.converters.UnmarshallingContext;
+import com.thoughtworks.xstream.io.HierarchicalStreamReader;
+import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
+
+public class IconImageConverter implements Converter {
+
+ private static final Logger LOG = Logger.getLogger(IconImageConverter.class);
+
+ @Override
+ public void marshal(Object object, HierarchicalStreamWriter writer, MarshallingContext context) {
+ ImageIcon image = (ImageIcon) object;
+ int width = image.getIconWidth();
+ int height = image.getIconHeight();
+ int[] pixels = new int[width * height];
+
+ if (image != null) {
+ try {
+ PixelGrabber pg = new PixelGrabber(image.getImage(), 0, 0, width, height, pixels,
+ 0, width);
+ pg.grabPixels();
+ if ((pg.getStatus() & ImageObserver.ABORT) != 0) {
+ LOG.error("Failed to load image contents");
+ }
+ } catch (InterruptedException e) {
+ LOG.error("Image load interrupted");
+ }
+ }
+ LOG.debug("Writing image to file: " + width + "x" + height);
+ writer.addAttribute("width", Integer.toString(width));
+ writer.addAttribute("height", Integer.toString(height));
+ StringBuilder dataBuilder = new StringBuilder();
+ int n = 0;
+ for (int pixel : pixels) {
+ dataBuilder.append(Integer.toString(pixel, 16));
+ dataBuilder.append(",");
+ n++;
+ if (n % 16 == 0) {
+ dataBuilder.append("\n");
+ }
+ }
+ writer.addAttribute("data", dataBuilder.toString());
+ }
+
+ @Override
+ public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
+ int width = Integer.parseInt(reader.getAttribute("width"));
+ int height = Integer.parseInt(reader.getAttribute("height"));
+ LOG.debug("Reading image from file: " + width + "x" + height);
+ int[] pixels = new int[width * height];
+ String data = reader.getAttribute("data").replace("\n", "");
+ String[] splitData = data.split(",");
+ for (int i = 0; i < Math.min(splitData.length, pixels.length); i++) {
+ if (!splitData[i].trim().isEmpty()) {
+ try {
+ pixels[i] = Integer.parseInt(splitData[i].trim(), 16);
+ } catch (Exception e) {
+ pixels[i] = 0;
+ }
+ }
+ }
+
+ Toolkit toolkit = Toolkit.getDefaultToolkit();
+ ColorModel colorModel = ColorModel.getRGBdefault();
+ Image image = toolkit.createImage(new MemoryImageSource(width, height, colorModel, pixels,
+ 0, width));
+
+ BufferedImage bimg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
+ bimg.setRGB(0, 0, width, height, pixels, 0, width);
+ File outputfile = new File("c:\\saved.png");
+ try {
+ ImageIO.write(bimg, "png", outputfile);
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ return new ImageIcon(image);
+ }
+
+ @Override
+ public boolean canConvert(Class clazz) {
+ return ImageIcon.class.isAssignableFrom(clazz);
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/misc/Image.java b/diylc-library/src/org/diylc/components/misc/Image.java
new file mode 100644
index 00000000..7ea90c1d
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/misc/Image.java
@@ -0,0 +1,132 @@
+package org.diylc.components.misc;
+
+import java.awt.AlphaComposite;
+import java.awt.Composite;
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.Shape;
+
+import javax.swing.ImageIcon;
+
+import org.diylc.components.AbstractTransparentComponent;
+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 com.thoughtworks.xstream.annotations.XStreamConverter;
+
+@ComponentDescriptor(name = "Image", author = "Branislav Stojkovic", category = "Misc", description = "User defined image", instanceNamePrefix = "Img", zOrder = IDIYComponent.TEXT, stretchable = false, bomPolicy = BomPolicy.NEVER_SHOW)
+public class Image extends AbstractTransparentComponent {
+
+ private static final long serialVersionUID = 1L;
+ public static String DEFAULT_TEXT = "Double click to edit text";
+ private static ImageIcon ICON;
+ private static byte DEFAULT_SCALE = 50;
+
+ static {
+ String name = "image.png";
+ java.net.URL imgURL = Image.class.getResource(name);
+ if (imgURL != null) {
+ ICON = new ImageIcon(imgURL, name);
+ }
+ }
+
+ private Point point = new Point(0, 0);
+ @XStreamConverter(IconImageConverter.class)
+ private ImageIcon image = ICON;
+ private byte scale = 50;
+
+ @Override
+ public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode,
+ Project project, IDrawingObserver drawingObserver) {
+ double s = 1d * scale / DEFAULT_SCALE;
+ Point endPoint = new Point((int) (image.getIconWidth() * s),
+ (int) (image.getIconHeight() * s));
+ Shape clip = g2d.getClip();
+ if (!clip.contains(point) && !clip.contains(endPoint)
+ && !clip.contains(point.x, endPoint.y) && !clip.contains(endPoint.x, point.y)) {
+ return;
+ }
+ Composite oldComposite = g2d.getComposite();
+ if (alpha < MAX_ALPHA) {
+ g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha
+ / MAX_ALPHA));
+ }
+
+ g2d.scale(s, s);
+ g2d.drawImage(image.getImage(), (int) (point.x / s), (int) (point.y / s), null);
+ if (componentState == ComponentState.SELECTED) {
+ g2d.setComposite(oldComposite);
+ g2d.scale(1 / s, 1 / s);
+ g2d.setColor(SELECTION_COLOR);
+ g2d.drawRect(point.x, point.y, endPoint.x, endPoint.y);
+ }
+ }
+
+ @Override
+ public void drawIcon(Graphics2D g2d, int width, int height) {
+ g2d.drawImage(ICON.getImage(), point.x, point.y, null);
+ }
+
+ @Override
+ public int getControlPointCount() {
+ return 1;
+ }
+
+ @Override
+ public Point getControlPoint(int index) {
+ return point;
+ }
+
+ @Override
+ public boolean isControlPointSticky(int index) {
+ return false;
+ }
+
+ @Override
+ public VisibilityPolicy getControlPointVisibilityPolicy(int index) {
+ return VisibilityPolicy.NEVER;
+ }
+
+ @Override
+ public void setControlPoint(Point point, int index) {
+ this.point.setLocation(point);
+ }
+
+ @EditableProperty(defaultable = false)
+ public ImageIcon getImage() {
+ return image;
+ }
+
+ public void setImage(ImageIcon image) {
+ this.image = image;
+ }
+
+ @EditableProperty(defaultable = false)
+ public byte getScale() {
+ return scale;
+ }
+
+ public void setScale(byte scale) {
+ this.scale = scale;
+ }
+
+ @Override
+ public String getName() {
+ return super.getName();
+ }
+
+ @Override
+ public Void getValue() {
+ return null;
+ }
+
+ @Override
+ public void setValue(Void value) {
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/misc/Label.java b/diylc-library/src/org/diylc/components/misc/Label.java
new file mode 100644
index 00000000..0406aa8c
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/misc/Label.java
@@ -0,0 +1,203 @@
+package org.diylc.components.misc;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.geom.Rectangle2D;
+
+import org.diylc.components.AbstractComponent;
+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;
+
+@ComponentDescriptor(name = "Label", author = "Branislav Stojkovic", category = "Misc", description = "User defined label", instanceNamePrefix = "L", zOrder = IDIYComponent.TEXT, stretchable = false, bomPolicy = BomPolicy.NEVER_SHOW)
+public class Label extends AbstractComponent {
+
+ public static String DEFAULT_TEXT = "Double click to edit text";
+
+ private static final long serialVersionUID = 1L;
+
+ private Point point = new Point(0, 0);
+ private String text = DEFAULT_TEXT;
+ private Font font = LABEL_FONT;
+ private Color color = LABEL_COLOR;
+ private boolean center = true;
+
+ @Override
+ public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode,
+ Project project, IDrawingObserver drawingObserver) {
+ g2d.setColor(componentState == ComponentState.SELECTED ? LABEL_COLOR_SELECTED : color);
+ g2d.setFont(font);
+ FontMetrics fontMetrics = g2d.getFontMetrics();
+ Rectangle2D rect = fontMetrics.getStringBounds(text, g2d);
+
+ int textHeight = (int) rect.getHeight();
+ int textWidth = (int) rect.getWidth();
+
+ int x;
+ int y;
+ if (center) {
+ // Center text horizontally and vertically
+ x = point.x - textWidth / 2;
+ y = point.y - textHeight / 2 + fontMetrics.getAscent();
+ } else {
+ x = point.x;
+ y = point.y - textHeight;
+ }
+
+ g2d.drawString(text, x, y);
+ }
+
+ @Override
+ public void drawIcon(Graphics2D g2d, int width, int height) {
+ g2d.setColor(LABEL_COLOR);
+ g2d.setFont(LABEL_FONT.deriveFont(13f * width / 32).deriveFont(Font.PLAIN));
+
+ FontMetrics fontMetrics = g2d.getFontMetrics();
+ Rectangle2D rect = fontMetrics.getStringBounds("Abc", g2d);
+
+ int textHeight = (int) (rect.getHeight());
+ int textWidth = (int) (rect.getWidth());
+
+ // Center text horizontally and vertically.
+ int x = (width - textWidth) / 2 + 1;
+ int y = (height - textHeight) / 2 + fontMetrics.getAscent();
+
+ g2d.drawString("Abc", x, y);
+ }
+
+ @EditableProperty
+ public Font getFont() {
+ return font;
+ }
+
+ public void setFont(Font font) {
+ this.font = font;
+ }
+
+ // Bold and italic fields are named to be alphabetically after Font. This is
+ // important!
+
+ @EditableProperty(name = "Font Bold")
+ public boolean getBold() {
+ return font.isBold();
+ }
+
+ public void setBold(boolean bold) {
+ if (bold) {
+ if (font.isItalic()) {
+ font = font.deriveFont(Font.BOLD + Font.ITALIC);
+ } else {
+ font = font.deriveFont(Font.BOLD);
+ }
+ } else {
+ if (font.isItalic()) {
+ font = font.deriveFont(Font.ITALIC);
+ } else {
+ font = font.deriveFont(Font.PLAIN);
+ }
+ }
+ }
+
+ @EditableProperty(name = "Font Italic")
+ public boolean getItalic() {
+ return font.isItalic();
+ }
+
+ public void setItalic(boolean italic) {
+ if (italic) {
+ if (font.isBold()) {
+ font = font.deriveFont(Font.BOLD + Font.ITALIC);
+ } else {
+ font = font.deriveFont(Font.ITALIC);
+ }
+ } else {
+ if (font.isBold()) {
+ font = font.deriveFont(Font.BOLD);
+ } else {
+ font = font.deriveFont(Font.PLAIN);
+ }
+ }
+ }
+
+ @EditableProperty(name = "Font Size")
+ public int getFontSize() {
+ return font.getSize();
+ }
+
+ public void setFontSize(int size) {
+ font = font.deriveFont((float) size);
+ }
+
+ @Override
+ public int getControlPointCount() {
+ return 1;
+ }
+
+ @Override
+ public Point getControlPoint(int index) {
+ return point;
+ }
+
+ @Override
+ public boolean isControlPointSticky(int index) {
+ return false;
+ }
+
+ @Override
+ public VisibilityPolicy getControlPointVisibilityPolicy(int index) {
+ return VisibilityPolicy.NEVER;
+ }
+
+ @Override
+ public void setControlPoint(Point point, int index) {
+ this.point.setLocation(point);
+ }
+
+ @EditableProperty(defaultable = false)
+ public String getText() {
+ return text;
+ }
+
+ public void setText(String text) {
+ this.text = text;
+ }
+
+ @EditableProperty
+ public Color getColor() {
+ return color;
+ }
+
+ public void setColor(Color color) {
+ this.color = color;
+ }
+
+ public boolean getCenter() {
+ return center;
+ }
+
+ public void setCenter(boolean center) {
+ this.center = center;
+ }
+
+ @Override
+ public String getName() {
+ return super.getName();
+ }
+
+ @Override
+ public Void getValue() {
+ return null;
+ }
+
+ @Override
+ public void setValue(Void value) {
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/misc/image.png b/diylc-library/src/org/diylc/components/misc/image.png
new file mode 100644
index 00000000..b663869f
Binary files /dev/null and b/diylc-library/src/org/diylc/components/misc/image.png differ
diff --git a/diylc-library/src/org/diylc/components/passive/AbstractPotentiometer.java b/diylc-library/src/org/diylc/components/passive/AbstractPotentiometer.java
new file mode 100644
index 00000000..b6151fbb
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/passive/AbstractPotentiometer.java
@@ -0,0 +1,85 @@
+package org.diylc.components.passive;
+
+import java.awt.Point;
+
+import org.diylc.common.Orientation;
+import org.diylc.components.AbstractTransparentComponent;
+import org.diylc.core.VisibilityPolicy;
+import org.diylc.core.annotations.EditableProperty;
+import org.diylc.core.measures.Resistance;
+import org.diylc.core.measures.ResistanceUnit;
+
+public abstract class AbstractPotentiometer extends
+ AbstractTransparentComponent {
+
+ private static final long serialVersionUID = 1L;
+
+ protected Point[] controlPoints;
+
+ protected Resistance resistance = new Resistance(100d, ResistanceUnit.K);
+ protected Orientation orientation = Orientation.DEFAULT;
+ protected Taper taper = Taper.LIN;
+
+ @Override
+ public int getControlPointCount() {
+ return controlPoints.length;
+ }
+
+ @Override
+ public Point getControlPoint(int index) {
+ return controlPoints[index];
+ }
+
+ @Override
+ public void setControlPoint(Point point, int index) {
+ controlPoints[index].setLocation(point);
+ }
+
+ @Override
+ public VisibilityPolicy getControlPointVisibilityPolicy(int index) {
+ return VisibilityPolicy.NEVER;
+ }
+
+ @Override
+ public boolean isControlPointSticky(int index) {
+ return true;
+ }
+
+ @EditableProperty
+ public Resistance getResistance() {
+ return resistance;
+ }
+
+ public void setResistance(Resistance resistance) {
+ this.resistance = resistance;
+ }
+
+ @Override
+ public PotentiometerValue getValue() {
+ return new PotentiometerValue(resistance, taper);
+ }
+
+ @Override
+ public void setValue(PotentiometerValue value) {
+ this.resistance = value.getResistance();
+ this.taper = value.getTaper();
+ }
+
+ @EditableProperty
+ public Taper getTaper() {
+ return taper;
+ }
+
+ public void setTaper(Taper taper) {
+ this.taper = taper;
+ }
+
+ @EditableProperty
+ public Orientation getOrientation() {
+ return orientation;
+ }
+
+ public void setOrientation(Orientation orientation) {
+ this.orientation = orientation;
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/passive/AxialElectrolyticCapacitor.java b/diylc-library/src/org/diylc/components/passive/AxialElectrolyticCapacitor.java
new file mode 100644
index 00000000..327113c6
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/passive/AxialElectrolyticCapacitor.java
@@ -0,0 +1,168 @@
+package org.diylc.components.passive;
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Shape;
+import java.awt.geom.Rectangle2D;
+
+import org.diylc.appframework.miscutils.ConfigurationManager;
+import org.diylc.common.IPlugInPort;
+import org.diylc.common.ObjectCache;
+import org.diylc.components.AbstractLeadedComponent;
+import org.diylc.core.CreationMethod;
+import org.diylc.core.IDIYComponent;
+import org.diylc.core.Theme;
+import org.diylc.core.annotations.ComponentDescriptor;
+import org.diylc.core.annotations.EditableProperty;
+import org.diylc.core.measures.Capacitance;
+import org.diylc.core.measures.CapacitanceUnit;
+import org.diylc.core.measures.Size;
+import org.diylc.core.measures.SizeUnit;
+import org.diylc.core.measures.VoltageUnit;
+import org.diylc.utils.Constants;
+
+@ComponentDescriptor(name = "Electrolytic Capacitor (axial)", author = "Branislav Stojkovic", category = "Passive", creationMethod = CreationMethod.POINT_BY_POINT, instanceNamePrefix = "C", description = "Axial electrolytic capacitor, similar to Sprague Atom, F&T, etc", zOrder = IDIYComponent.COMPONENT)
+public class AxialElectrolyticCapacitor extends AbstractLeadedComponent {
+
+ private static final long serialVersionUID = 1L;
+
+ public static Size DEFAULT_WIDTH = new Size(1d / 2, SizeUnit.in);
+ public static Size DEFAULT_HEIGHT = new Size(1d / 8, SizeUnit.in);
+ public static Color BODY_COLOR = Color.decode("#EAADEA");
+ public static Color BORDER_COLOR = BODY_COLOR.darker();
+ public static Color MARKER_COLOR = Color.gray;
+ public static Color TICK_COLOR = Color.white;
+
+ private Capacitance value = new Capacitance(1d, CapacitanceUnit.uF);
+ @Deprecated
+ private Voltage voltage = Voltage._63V;
+ private org.diylc.core.measures.Voltage voltageNew = new org.diylc.core.measures.Voltage(63d,
+ VoltageUnit.V);
+
+ private Color markerColor = MARKER_COLOR;
+ private Color tickColor = TICK_COLOR;
+ private boolean polarized = true;
+
+ public AxialElectrolyticCapacitor() {
+ super();
+ this.bodyColor = BODY_COLOR;
+ this.borderColor = BORDER_COLOR;
+ }
+
+ @EditableProperty
+ public Capacitance getValue() {
+ return value;
+ }
+
+ public void setValue(Capacitance value) {
+ this.value = value;
+ }
+
+ @Deprecated
+ public Voltage getVoltage() {
+ return voltage;
+ }
+
+ @Deprecated
+ public void setVoltage(Voltage voltage) {
+ this.voltage = voltage;
+ }
+
+ @EditableProperty(name = "Voltage")
+ public org.diylc.core.measures.Voltage getVoltageNew() {
+ // Backward comptibility
+ if (voltageNew == null) {
+ voltageNew = voltage.convertToNewFormat();
+ voltage = null;
+ // Clear old value, don't need it anymore
+ }
+ return voltageNew;
+ }
+
+ public void setVoltageNew(org.diylc.core.measures.Voltage voltageNew) {
+ this.voltageNew = voltageNew;
+ }
+
+ public void drawIcon(Graphics2D g2d, int width, int height) {
+ g2d.rotate(-Math.PI / 4, width / 2, height / 2);
+ g2d.setColor(LEAD_COLOR);
+ g2d.drawLine(0, height / 2, width, height / 2);
+ g2d.setColor(BODY_COLOR);
+ g2d.fillRect(4, height / 2 - 3, width - 8, 6);
+ g2d.setColor(MARKER_COLOR);
+ g2d.fillRect(width - 9, height / 2 - 3, 5, 6);
+ g2d.setColor(TICK_COLOR);
+ g2d.drawLine(width - 6, height / 2 - 1, width - 6, height / 2 + 1);
+ g2d.setColor(BORDER_COLOR);
+ g2d.drawRect(4, height / 2 - 3, width - 8, 6);
+ }
+
+ @Override
+ protected Size getDefaultWidth() {
+ return DEFAULT_HEIGHT;
+ }
+
+ @Override
+ protected Size getDefaultLength() {
+ return DEFAULT_WIDTH;
+ }
+
+ @EditableProperty(name = "Marker")
+ public Color getMarkerColor() {
+ return markerColor;
+ }
+
+ public void setMarkerColor(Color coverColor) {
+ this.markerColor = coverColor;
+ }
+
+ @EditableProperty(name = "Tick")
+ public Color getTickColor() {
+ return tickColor;
+ }
+
+ public void setTickColor(Color tickColor) {
+ this.tickColor = tickColor;
+ }
+
+ @EditableProperty(name = "Polarized")
+ public boolean getPolarized() {
+ return polarized;
+ }
+
+ public void setPolarized(boolean polarized) {
+ this.polarized = polarized;
+ }
+
+ @Override
+ protected Shape getBodyShape() {
+ return new Rectangle2D.Double(0f, 0f, getLength().convertToPixels(),
+ getClosestOdd(getWidth().convertToPixels()));
+ }
+
+ @Override
+ protected void decorateComponentBody(Graphics2D g2d, boolean outlineMode) {
+ if (polarized) {
+ int width = getClosestOdd(getWidth().convertToPixels());
+ int markerLength = (int) (getLength().convertToPixels() * 0.2);
+ if (!outlineMode) {
+ g2d.setColor(markerColor);
+ g2d.fillRect((int) getLength().convertToPixels() - markerLength, 0, markerLength,
+ width);
+ }
+ Color finalTickColor;
+ if (outlineMode) {
+ Theme theme = (Theme) ConfigurationManager.getInstance().readObject(
+ IPlugInPort.THEME_KEY, Constants.DEFAULT_THEME);
+ finalTickColor = theme.getOutlineColor();
+ } else {
+ finalTickColor = tickColor;
+ }
+ g2d.setColor(finalTickColor);
+ g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(2));
+ g2d.drawLine((int) getLength().convertToPixels() - markerLength / 2,
+ (int) (width / 2 - width * 0.15), (int) getLength().convertToPixels()
+ - markerLength / 2, (int) (width / 2 + width * 0.15));
+ }
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/passive/AxialFilmCapacitor.java b/diylc-library/src/org/diylc/components/passive/AxialFilmCapacitor.java
new file mode 100644
index 00000000..b8e8ed9f
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/passive/AxialFilmCapacitor.java
@@ -0,0 +1,105 @@
+package org.diylc.components.passive;
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Shape;
+import java.awt.geom.Rectangle2D;
+
+import org.diylc.components.AbstractLeadedComponent;
+import org.diylc.core.CreationMethod;
+import org.diylc.core.IDIYComponent;
+import org.diylc.core.annotations.ComponentDescriptor;
+import org.diylc.core.annotations.EditableProperty;
+import org.diylc.core.measures.Capacitance;
+import org.diylc.core.measures.CapacitanceUnit;
+import org.diylc.core.measures.Size;
+import org.diylc.core.measures.SizeUnit;
+import org.diylc.core.measures.VoltageUnit;
+
+@ComponentDescriptor(name = "Film Capacitor (axial)", author = "Branislav Stojkovic", category = "Passive", creationMethod = CreationMethod.POINT_BY_POINT, instanceNamePrefix = "C", description = "Axial film capacitor, similar to Mallory 150s", zOrder = IDIYComponent.COMPONENT)
+public class AxialFilmCapacitor extends AbstractLeadedComponent {
+
+ private static final long serialVersionUID = 1L;
+
+ public static Size DEFAULT_WIDTH = new Size(1d / 2, SizeUnit.in);
+ public static Size DEFAULT_HEIGHT = new Size(1d / 8, SizeUnit.in);
+ public static Color BODY_COLOR = Color.decode("#FFE303");
+ public static Color BORDER_COLOR = BODY_COLOR.darker();
+
+ private Capacitance value = new Capacitance(22d, CapacitanceUnit.nF);
+ @Deprecated
+ private Voltage voltage = Voltage._63V;
+ private org.diylc.core.measures.Voltage voltageNew = new org.diylc.core.measures.Voltage(63d,
+ VoltageUnit.V);
+
+ public AxialFilmCapacitor() {
+ super();
+ this.bodyColor = BODY_COLOR;
+ this.borderColor = BORDER_COLOR;
+ }
+
+ @Override
+ protected boolean supportsStandingMode() {
+ return true;
+ }
+
+ @EditableProperty
+ public Capacitance getValue() {
+ return value;
+ }
+
+ public void setValue(Capacitance value) {
+ this.value = value;
+ }
+
+ @Deprecated
+ public Voltage getVoltage() {
+ return voltage;
+ }
+
+ @Deprecated
+ public void setVoltage(Voltage voltage) {
+ this.voltage = voltage;
+ }
+
+ @EditableProperty(name = "Voltage")
+ public org.diylc.core.measures.Voltage getVoltageNew() {
+ // Backward comptibility
+ if (voltageNew == null) {
+ voltageNew = voltage.convertToNewFormat();
+ voltage = null;
+ // Clear old value, don't need it anymore
+ }
+ return voltageNew;
+ }
+
+ public void setVoltageNew(org.diylc.core.measures.Voltage voltageNew) {
+ this.voltageNew = voltageNew;
+ }
+
+ public void drawIcon(Graphics2D g2d, int width, int height) {
+ g2d.rotate(-Math.PI / 4, width / 2, height / 2);
+ g2d.setColor(LEAD_COLOR);
+ g2d.drawLine(0, height / 2, width, height / 2);
+ g2d.setColor(BODY_COLOR);
+ g2d.fillRect(4, height / 2 - 3, width - 8, 6);
+ g2d.setColor(BORDER_COLOR);
+ g2d.drawRect(4, height / 2 - 3, width - 8, 6);
+ }
+
+ @Override
+ protected Size getDefaultWidth() {
+ return DEFAULT_HEIGHT;
+ }
+
+ @Override
+ protected Size getDefaultLength() {
+ return DEFAULT_WIDTH;
+ }
+
+ @Override
+ protected Shape getBodyShape() {
+ return new Rectangle2D.Double(0f, 0f, getLength().convertToPixels(),
+ getClosestOdd(getWidth().convertToPixels()));
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/passive/CapacitorSymbol.java b/diylc-library/src/org/diylc/components/passive/CapacitorSymbol.java
new file mode 100644
index 00000000..8f9c3689
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/passive/CapacitorSymbol.java
@@ -0,0 +1,120 @@
+package org.diylc.components.passive;
+
+import java.awt.Graphics2D;
+import java.awt.Shape;
+import java.awt.geom.GeneralPath;
+
+import org.diylc.components.AbstractSchematicLeadedSymbol;
+import org.diylc.core.CreationMethod;
+import org.diylc.core.IDIYComponent;
+import org.diylc.core.annotations.ComponentDescriptor;
+import org.diylc.core.annotations.EditableProperty;
+import org.diylc.core.measures.Capacitance;
+import org.diylc.core.measures.CapacitanceUnit;
+import org.diylc.core.measures.Size;
+import org.diylc.core.measures.SizeUnit;
+import org.diylc.core.measures.VoltageUnit;
+
+@ComponentDescriptor(name = "Capacitor (schematic symbol)", author = "Branislav Stojkovic", category = "Passive", creationMethod = CreationMethod.POINT_BY_POINT, instanceNamePrefix = "C", description = "Capacitor schematic symbol with an optional polarity sign", zOrder = IDIYComponent.COMPONENT)
+public class CapacitorSymbol extends AbstractSchematicLeadedSymbol {
+
+ private static final long serialVersionUID = 1L;
+
+ public static Size DEFAULT_LENGTH = new Size(0.05, SizeUnit.in);
+ public static Size DEFAULT_WIDTH = new Size(0.15, SizeUnit.in);
+
+ private Capacitance value = new Capacitance(22d, CapacitanceUnit.nF);
+ @Deprecated
+ private Voltage voltage = Voltage._63V;
+ private org.diylc.core.measures.Voltage voltageNew = new org.diylc.core.measures.Voltage(63d,
+ VoltageUnit.V);
+ private boolean polarized = false;
+
+ @EditableProperty
+ public Capacitance getValue() {
+ return value;
+ }
+
+ public void setValue(Capacitance value) {
+ this.value = value;
+ }
+
+ @Deprecated
+ public Voltage getVoltage() {
+ return voltage;
+ }
+
+ @Deprecated
+ public void setVoltage(Voltage voltage) {
+ this.voltage = voltage;
+ }
+
+ @EditableProperty(name = "Voltage")
+ public org.diylc.core.measures.Voltage getVoltageNew() {
+ // Backward compatibility
+ if (voltageNew == null) {
+ voltageNew = voltage.convertToNewFormat();
+ voltage = null;
+ // Clear old value, don't need it anymore
+ }
+ return voltageNew;
+ }
+
+ public void setVoltageNew(org.diylc.core.measures.Voltage voltageNew) {
+ this.voltageNew = voltageNew;
+ }
+
+ @EditableProperty
+ public boolean getPolarized() {
+ return polarized;
+ }
+
+ public void setPolarized(boolean polarized) {
+ this.polarized = polarized;
+ }
+
+ public void drawIcon(Graphics2D g2d, int width, int height) {
+ g2d.rotate(-Math.PI / 4, width / 2, height / 2);
+ g2d.setColor(LEAD_COLOR);
+ g2d.drawLine(0, height / 2, 13, height / 2);
+ g2d.drawLine(width - 13, height / 2, width, height / 2);
+ g2d.setColor(COLOR);
+ g2d.drawLine(14, height / 2 - 6, 14, height / 2 + 6);
+ g2d.drawLine(width - 14, height / 2 - 6, width - 14, height / 2 + 6);
+ }
+
+ @Override
+ protected Size getDefaultWidth() {
+ return DEFAULT_WIDTH;
+ }
+
+ @Override
+ protected Size getDefaultLength() {
+ return DEFAULT_LENGTH;
+ }
+
+ @Override
+ protected Shape getBodyShape() {
+ GeneralPath polyline = new GeneralPath();
+ double length = getLength().convertToPixels();
+ double width = getWidth().convertToPixels();
+ polyline.moveTo(0, 0);
+ polyline.lineTo(0, width);
+ polyline.moveTo(length, 0);
+ polyline.lineTo(length, width);
+ return polyline;
+ }
+
+ @Override
+ protected void decorateComponentBody(Graphics2D g2d, boolean outlineMode) {
+ if (polarized) {
+ // Draw + sign.
+ g2d.setColor(getBorderColor());
+ int plusSize = getClosestOdd(getWidth().convertToPixels() / 4);
+ int x = -plusSize;
+ int y = plusSize;
+ g2d.drawLine(x - plusSize / 2, y, x + plusSize / 2, y);
+ g2d.drawLine(x, y - plusSize / 2, x, y + plusSize / 2);
+ }
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/passive/PotentiometerPanel.java b/diylc-library/src/org/diylc/components/passive/PotentiometerPanel.java
new file mode 100644
index 00000000..1ee92aa8
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/passive/PotentiometerPanel.java
@@ -0,0 +1,278 @@
+package org.diylc.components.passive;
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Composite;
+import java.awt.FontMetrics;
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.geom.Area;
+import java.awt.geom.Ellipse2D;
+import java.awt.geom.Rectangle2D;
+
+import org.diylc.common.ObjectCache;
+import org.diylc.common.Orientation;
+import org.diylc.core.ComponentState;
+import org.diylc.core.CreationMethod;
+import org.diylc.core.IDIYComponent;
+import org.diylc.core.IDrawingObserver;
+import org.diylc.core.Project;
+import org.diylc.core.annotations.ComponentDescriptor;
+import org.diylc.core.annotations.EditableProperty;
+import org.diylc.core.measures.Size;
+import org.diylc.core.measures.SizeUnit;
+
+@ComponentDescriptor(name = "Potentiometer (panel mount)", author = "Branislav Stojkovic", category = "Passive", creationMethod = CreationMethod.SINGLE_CLICK, instanceNamePrefix = "VR", description = "Panel mount potentiometer with solder lugs", zOrder = IDIYComponent.COMPONENT, stretchable = false)
+public class PotentiometerPanel extends AbstractPotentiometer {
+
+ private static final long serialVersionUID = 1L;
+
+ protected static Size BODY_DIAMETER = new Size(1d, SizeUnit.in);
+ protected static Size SPACING = new Size(0.3d, SizeUnit.in);
+ protected static Size LUG_DIAMETER = new Size(0.15d, SizeUnit.in);
+ protected static Color BODY_COLOR = Color.gray.brighter();
+ protected static Color BORDER_COLOR = Color.gray;
+
+ protected Size bodyDiameter = BODY_DIAMETER;
+ protected Size spacing = SPACING;
+ protected Size lugDiameter = LUG_DIAMETER;
+ protected Color bodyColor = BODY_COLOR;
+ protected Color borderColor = BORDER_COLOR;
+ // Array of 7 elements: 3 lug connectors, 1 pot body and 3 lugs
+ transient protected Area[] body = null;
+
+ public PotentiometerPanel() {
+ controlPoints = new Point[] { new Point(0, 0), new Point(0, 0), new Point(0, 0) };
+ updateControlPoints();
+ }
+
+ protected void updateControlPoints() {
+ int spacing = (int) this.spacing.convertToPixels();
+ switch (orientation) {
+ case DEFAULT:
+ controlPoints[1].setLocation(controlPoints[0].x + spacing, controlPoints[0].y);
+ controlPoints[2].setLocation(controlPoints[0].x + 2 * spacing, controlPoints[0].y);
+ break;
+ case _90:
+ controlPoints[1].setLocation(controlPoints[0].x, controlPoints[0].y + spacing);
+ controlPoints[2].setLocation(controlPoints[0].x, controlPoints[0].y + 2 * spacing);
+ break;
+ case _180:
+ controlPoints[1].setLocation(controlPoints[0].x - spacing, controlPoints[0].y);
+ controlPoints[2].setLocation(controlPoints[0].x - 2 * spacing, controlPoints[0].y);
+ break;
+ case _270:
+ controlPoints[1].setLocation(controlPoints[0].x, controlPoints[0].y - spacing);
+ controlPoints[2].setLocation(controlPoints[0].x, controlPoints[0].y - 2 * spacing);
+ break;
+ default:
+ break;
+ }
+ }
+
+ public Area[] getBody() {
+ int spacing = (int) this.spacing.convertToPixels();
+ int diameter = getClosestOdd(bodyDiameter.convertToPixels());
+ if (body == null) {
+ body = new Area[7];
+
+ // Add lugs.
+ int lugDiameter = getClosestOdd(this.lugDiameter.convertToPixels());
+ int holeDiameter = getClosestOdd(this.lugDiameter.convertToPixels() / 2);
+ for (int i = 0; i < 3; i++) {
+ Area area = new Area(new Ellipse2D.Double(controlPoints[i].x - lugDiameter / 2,
+ controlPoints[i].y - lugDiameter / 2, lugDiameter, lugDiameter));
+ body[4 + i] = area;
+ }
+
+ switch (orientation) {
+ case DEFAULT:
+ body[3] = new Area(new Ellipse2D.Double(
+ controlPoints[0].x + spacing - diameter / 2, controlPoints[0].y - spacing
+ / 2 - diameter, diameter, diameter));
+ for (int i = 0; i < 3; i++) {
+ body[i] = new Area(new Rectangle2D.Double(
+ controlPoints[i].x - holeDiameter / 2, controlPoints[i].y
+ - (spacing + diameter) / 2, holeDiameter,
+ (spacing + diameter) / 2));
+ }
+ break;
+ case _90:
+ body[3] = new Area(new Ellipse2D.Double(controlPoints[0].x + spacing / 2,
+ controlPoints[0].y + spacing - diameter / 2, diameter, diameter));
+ for (int i = 0; i < 3; i++) {
+ body[i] = new Area(new Rectangle2D.Double(controlPoints[i].x,
+ controlPoints[i].y - holeDiameter / 2, (spacing + diameter) / 2,
+ holeDiameter));
+ }
+ break;
+ case _180:
+ body[3] = new Area(new Ellipse2D.Double(
+ controlPoints[0].x - spacing - diameter / 2, controlPoints[0].y + spacing
+ / 2, diameter, diameter));
+ for (int i = 0; i < 3; i++) {
+ body[i] = new Area(new Rectangle2D.Double(
+ controlPoints[i].x - holeDiameter / 2, controlPoints[i].y,
+ holeDiameter, (spacing + diameter) / 2));
+ }
+ break;
+ case _270:
+ body[3] = new Area(new Ellipse2D.Double(
+ controlPoints[0].x - spacing / 2 - diameter, controlPoints[0].y - spacing
+ - diameter / 2, diameter, diameter));
+ for (int i = 0; i < 3; i++) {
+ body[i] = new Area(new Rectangle2D.Double(controlPoints[i].x
+ - (spacing + diameter) / 2, controlPoints[i].y - holeDiameter / 2,
+ (spacing + diameter) / 2, holeDiameter));
+ }
+ break;
+ default:
+ break;
+ }
+ for (int i = 0; i < 3; i++) {
+ for (int j = 3; j < 7; j++) {
+ body[i].subtract(body[j]);
+ }
+ }
+ // Make holes in the lugs.
+ for (int i = 0; i < 3; i++) {
+ body[4 + i].subtract(new Area(new Ellipse2D.Double(controlPoints[i].x
+ - holeDiameter / 2, controlPoints[i].y - holeDiameter / 2, holeDiameter,
+ holeDiameter)));
+ }
+ }
+ return body;
+ }
+
+ @Override
+ public void setControlPoint(Point point, int index) {
+ super.setControlPoint(point, index);
+ body = null;
+ }
+
+ @Override
+ public void setOrientation(Orientation orientation) {
+ super.setOrientation(orientation);
+ updateControlPoints();
+ body = null;
+ }
+
+ @Override
+ public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode,
+ Project project, IDrawingObserver drawingObserver) {
+ g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
+ for (Area shape : getBody()) {
+ if (shape != null) {
+ g2d.setColor(bodyColor);
+ Composite oldComposite = g2d.getComposite();
+ if (alpha < MAX_ALPHA) {
+ g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha
+ / MAX_ALPHA));
+ }
+ g2d.fill(shape);
+ g2d.setComposite(oldComposite);
+ g2d.setColor(componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? SELECTION_COLOR
+ : borderColor);
+ g2d.draw(shape);
+ }
+ }
+ // Draw caption.
+ g2d.setFont(LABEL_FONT);
+ g2d
+ .setColor(componentState == ComponentState.SELECTED ? LABEL_COLOR_SELECTED
+ : LABEL_COLOR);
+ FontMetrics fontMetrics = g2d.getFontMetrics();
+ Rectangle2D bodyRect = getBody()[3].getBounds2D();
+ Rectangle2D rect = fontMetrics.getStringBounds(getName(), g2d);
+
+ int textHeight = (int) rect.getHeight();
+ int textWidth = (int) rect.getWidth();
+ int panelHeight = (int) bodyRect.getHeight();
+ int panelWidth = (int) bodyRect.getWidth();
+
+ int x = (panelWidth - textWidth) / 2;
+ int y = panelHeight / 2 - textHeight + fontMetrics.getAscent();
+
+ g2d.drawString(getName(), (int) (bodyRect.getX() + x), (int) (bodyRect.getY() + y));
+
+ // Draw value.
+ rect = fontMetrics.getStringBounds(getValue().toString(), g2d);
+
+ textHeight = (int) rect.getHeight();
+ textWidth = (int) rect.getWidth();
+
+ x = (panelWidth - textWidth) / 2;
+ y = panelHeight / 2 + fontMetrics.getAscent();
+
+ g2d.drawString(getValue().toString(), (int) (bodyRect.getX() + x),
+ (int) (bodyRect.getY() + y));
+ }
+
+ @Override
+ public void drawIcon(Graphics2D g2d, int width, int height) {
+ int margin = 4 * width / 32;
+ int spacing = width / 3 - 1;
+ g2d.setColor(BORDER_COLOR);
+ g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(2 * width / 32));
+ g2d.drawLine(width / 2 - spacing, height / 2, width / 2 - spacing, height - margin);
+ g2d.drawLine(width / 2 + spacing, height / 2, width / 2 + spacing, height - margin);
+ g2d.drawLine(width / 2, height / 2, width / 2, height - margin);
+ g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(4 * width / 32));
+ g2d.drawLine(width / 2 - spacing, height - margin, width / 2 - spacing, height - margin);
+ g2d.drawLine(width / 2 + spacing, height - margin, width / 2 + spacing, height - margin);
+ g2d.drawLine(width / 2, height - margin, width / 2, height - margin);
+ g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
+ g2d.setColor(BODY_COLOR);
+ g2d.fillOval(margin, margin / 2, width - 2 * margin, height - 2 * margin);
+ g2d.setColor(BORDER_COLOR);
+ g2d.drawOval(margin, margin / 2, width - 2 * margin, height - 2 * margin);
+ }
+
+ @EditableProperty
+ public Size getSpacing() {
+ return spacing;
+ }
+
+ public void setSpacing(Size spacing) {
+ this.spacing = spacing;
+ updateControlPoints();
+ body = null;
+ }
+
+ @EditableProperty(name = "Diameter")
+ public Size getBodyDiameter() {
+ return bodyDiameter;
+ }
+
+ public void setBodyDiameter(Size bodyDiameter) {
+ this.bodyDiameter = bodyDiameter;
+ }
+
+ @EditableProperty(name = "Lug size")
+ public Size getLugDiameter() {
+ return lugDiameter;
+ }
+
+ public void setLugDiameter(Size lugDiameter) {
+ this.lugDiameter = lugDiameter;
+ }
+
+ @EditableProperty(name = "Body")
+ public Color getBodyColor() {
+ return bodyColor;
+ }
+
+ public void setBodyColor(Color bodyColor) {
+ this.bodyColor = bodyColor;
+ }
+
+ @EditableProperty(name = "Border")
+ public Color getBorderColor() {
+ return borderColor;
+ }
+
+ public void setBorderColor(Color borderColor) {
+ this.borderColor = borderColor;
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/passive/PotentiometerValue.java b/diylc-library/src/org/diylc/components/passive/PotentiometerValue.java
new file mode 100644
index 00000000..26f3caee
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/passive/PotentiometerValue.java
@@ -0,0 +1,28 @@
+package org.diylc.components.passive;
+
+import org.diylc.core.measures.Resistance;
+
+public class PotentiometerValue {
+
+ private Resistance resistance;
+ private Taper taper;
+
+ public PotentiometerValue(Resistance resistance, Taper taper) {
+ super();
+ this.resistance = resistance;
+ this.taper = taper;
+ }
+
+ public Resistance getResistance() {
+ return resistance;
+ }
+
+ public Taper getTaper() {
+ return taper;
+ }
+
+ @Override
+ public String toString() {
+ return resistance.toString() + " " + taper.toString();
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/passive/Power.java b/diylc-library/src/org/diylc/components/passive/Power.java
new file mode 100644
index 00000000..f925e2cb
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/passive/Power.java
@@ -0,0 +1,56 @@
+package org.diylc.components.passive;
+
+import org.diylc.core.measures.PowerUnit;
+
+public enum Power {
+
+ EIGHT("1/8W"), QUARTER("1/4W"), HALF("1/2W"), ONE("1W"), TWO("2W"), FIVE("5W"), TEN("10W"), FIFTY(
+ "50W"), HUNDRED("100W");
+
+ String label;
+
+ Power(String label) {
+ this.label = label;
+ }
+
+ @Override
+ public String toString() {
+ return label;
+ }
+
+ public org.diylc.core.measures.Power convertToNewFormat() {
+ org.diylc.core.measures.Power powerNew;
+ switch (this) {
+ case EIGHT:
+ powerNew = new org.diylc.core.measures.Power(1 / 8d, PowerUnit.W);
+ break;
+ case FIVE:
+ powerNew = new org.diylc.core.measures.Power(5d, PowerUnit.W);
+ break;
+ case FIFTY:
+ powerNew = new org.diylc.core.measures.Power(50d, PowerUnit.W);
+ break;
+ case HALF:
+ powerNew = new org.diylc.core.measures.Power(1 / 2d, PowerUnit.W);
+ break;
+ case HUNDRED:
+ powerNew = new org.diylc.core.measures.Power(100d, PowerUnit.W);
+ break;
+ case ONE:
+ powerNew = new org.diylc.core.measures.Power(1d, PowerUnit.W);
+ break;
+ case QUARTER:
+ powerNew = new org.diylc.core.measures.Power(1 / 4d, PowerUnit.W);
+ break;
+ case TEN:
+ powerNew = new org.diylc.core.measures.Power(10d, PowerUnit.W);
+ break;
+ case TWO:
+ powerNew = new org.diylc.core.measures.Power(2d, PowerUnit.W);
+ break;
+ default:
+ powerNew = new org.diylc.core.measures.Power(1 / 2d, PowerUnit.W);
+ }
+ return powerNew;
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/passive/RadialCeramicDiskCapacitor.java b/diylc-library/src/org/diylc/components/passive/RadialCeramicDiskCapacitor.java
new file mode 100644
index 00000000..876a800d
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/passive/RadialCeramicDiskCapacitor.java
@@ -0,0 +1,80 @@
+package org.diylc.components.passive;
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Shape;
+import java.awt.geom.Ellipse2D;
+
+import org.diylc.components.AbstractLeadedComponent;
+import org.diylc.core.CreationMethod;
+import org.diylc.core.IDIYComponent;
+import org.diylc.core.annotations.ComponentDescriptor;
+import org.diylc.core.annotations.EditableProperty;
+import org.diylc.core.measures.Capacitance;
+import org.diylc.core.measures.CapacitanceUnit;
+import org.diylc.core.measures.Size;
+import org.diylc.core.measures.SizeUnit;
+
+@ComponentDescriptor(name = "Ceramic Capacitor (radial)", author = "Branislav Stojkovic", category = "Passive", creationMethod = CreationMethod.POINT_BY_POINT, instanceNamePrefix = "C", description = "Standard radial ceramic capacitor", zOrder = IDIYComponent.COMPONENT)
+public class RadialCeramicDiskCapacitor extends AbstractLeadedComponent {
+
+ private static final long serialVersionUID = 1L;
+
+ public static Size DEFAULT_WIDTH = new Size(1d / 2, SizeUnit.in);
+ public static Size DEFAULT_HEIGHT = new Size(1d / 8, SizeUnit.in);
+ public static Color BODY_COLOR = Color.decode("#F0E68C");
+ public static Color BORDER_COLOR = BODY_COLOR.darker();
+
+ private Capacitance value = new Capacitance(470d, CapacitanceUnit.pF);
+ private Voltage voltage = Voltage._63V;
+
+ public RadialCeramicDiskCapacitor() {
+ super();
+ this.bodyColor = BODY_COLOR;
+ this.borderColor = BORDER_COLOR;
+ }
+
+ @EditableProperty
+ public Capacitance getValue() {
+ return value;
+ }
+
+ public void setValue(Capacitance value) {
+ this.value = value;
+ }
+
+ @EditableProperty
+ public Voltage getVoltage() {
+ return voltage;
+ }
+
+ public void setVoltage(Voltage voltage) {
+ this.voltage = voltage;
+ }
+
+ public void drawIcon(Graphics2D g2d, int width, int height) {
+ g2d.rotate(-Math.PI / 4, width / 2, height / 2);
+ g2d.setColor(LEAD_COLOR);
+ g2d.drawLine(0, height / 2, width, height / 2);
+ g2d.setColor(BODY_COLOR);
+ g2d.fillOval(4, height / 2 - 3, width - 8, 6);
+ g2d.setColor(BORDER_COLOR);
+ g2d.drawOval(4, height / 2 - 3, width - 8, 6);
+ }
+
+ @Override
+ protected Size getDefaultWidth() {
+ return DEFAULT_HEIGHT;
+ }
+
+ @Override
+ protected Size getDefaultLength() {
+ return DEFAULT_WIDTH;
+ }
+
+ @Override
+ protected Shape getBodyShape() {
+ return new Ellipse2D.Double(0f, 0f, getLength().convertToPixels(), getClosestOdd(getWidth()
+ .convertToPixels()));
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/passive/RadialElectrolytic.java b/diylc-library/src/org/diylc/components/passive/RadialElectrolytic.java
new file mode 100644
index 00000000..4ca98002
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/passive/RadialElectrolytic.java
@@ -0,0 +1,203 @@
+package org.diylc.components.passive;
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Shape;
+import java.awt.geom.Area;
+import java.awt.geom.Ellipse2D;
+import java.awt.geom.Rectangle2D;
+
+import org.diylc.appframework.miscutils.ConfigurationManager;
+import org.diylc.common.IPlugInPort;
+import org.diylc.common.ObjectCache;
+import org.diylc.components.AbstractLeadedComponent;
+import org.diylc.core.CreationMethod;
+import org.diylc.core.IDIYComponent;
+import org.diylc.core.Theme;
+import org.diylc.core.annotations.ComponentDescriptor;
+import org.diylc.core.annotations.EditableProperty;
+import org.diylc.core.measures.Capacitance;
+import org.diylc.core.measures.CapacitanceUnit;
+import org.diylc.core.measures.Size;
+import org.diylc.core.measures.SizeUnit;
+import org.diylc.core.measures.VoltageUnit;
+import org.diylc.utils.Constants;
+
+@ComponentDescriptor(name = "Electrolytic Capacitor", author = "Branislav Stojkovic", category = "Passive", creationMethod = CreationMethod.POINT_BY_POINT, instanceNamePrefix = "C", description = "Vertical mounted electrolytic capacitor, polarized or bipolar", zOrder = IDIYComponent.COMPONENT)
+public class RadialElectrolytic extends AbstractLeadedComponent {
+
+ private static final long serialVersionUID = 1L;
+
+ public static Size DEFAULT_SIZE = new Size(1d / 2, SizeUnit.in);
+ public static Color BODY_COLOR = Color.decode("#EAADEA");
+ public static Color BORDER_COLOR = BODY_COLOR.darker();
+ public static Color MARKER_COLOR = Color.gray;
+ public static Color TICK_COLOR = Color.white;
+
+ private Capacitance value = new Capacitance(1d, CapacitanceUnit.uF);
+ @Deprecated
+ private Voltage voltage = Voltage._63V;
+ private org.diylc.core.measures.Voltage voltageNew = new org.diylc.core.measures.Voltage(63d,
+ VoltageUnit.V);
+
+ private Color markerColor = MARKER_COLOR;
+ private Color tickColor = TICK_COLOR;
+ private boolean polarized = true;
+
+ public RadialElectrolytic() {
+ super();
+ this.bodyColor = BODY_COLOR;
+ this.borderColor = BORDER_COLOR;
+ }
+
+ @EditableProperty
+ public Capacitance getValue() {
+ return value;
+ }
+
+ public void setValue(Capacitance value) {
+ this.value = value;
+ }
+
+ @Deprecated
+ public Voltage getVoltage() {
+ return voltage;
+ }
+
+ @Deprecated
+ public void setVoltage(Voltage voltage) {
+ this.voltage = voltage;
+ }
+
+ @EditableProperty(name = "Voltage")
+ public org.diylc.core.measures.Voltage getVoltageNew() {
+ // Backward comptibility
+ if (voltageNew == null) {
+ voltageNew = voltage.convertToNewFormat();
+ voltage = null;
+ // Clear old value, don't need it anymore
+ }
+ return voltageNew;
+ }
+
+ public void setVoltageNew(org.diylc.core.measures.Voltage voltageNew) {
+ this.voltageNew = voltageNew;
+ }
+
+ public void drawIcon(Graphics2D g2d, int width, int height) {
+ g2d.setColor(BODY_COLOR);
+ int margin = 3;
+ Ellipse2D body = new Ellipse2D.Double(margin, margin, getClosestOdd(width - 2 * margin),
+ getClosestOdd(width - 2 * margin));
+ g2d.fill(body);
+ Area marker = new Area(body);
+ marker.subtract(new Area(new Rectangle2D.Double(margin, margin, width - 4 * margin,
+ getClosestOdd(width - 2 * margin))));
+ g2d.setColor(MARKER_COLOR);
+ g2d.fill(marker);
+ g2d.setColor(TICK_COLOR);
+ g2d.drawLine(width - 2 * margin, height / 2 - 2, width - 2 * margin, height / 2 + 2);
+ g2d.setColor(BORDER_COLOR);
+ g2d.draw(body);
+ // g2d.setColor(COVER_COLOR.darker());
+ // g2d.drawLine(innerMargin + 2, innerMargin + 2, width - innerMargin -
+ // 2, width - innerMargin
+ // - 2);
+ // g2d.drawLine(innerMargin + 2, width - innerMargin - 2, width -
+ // innerMargin - 2,
+ // innerMargin + 2);
+ }
+
+ @Override
+ protected void decorateComponentBody(Graphics2D g2d, boolean outlineMode) {
+ if (polarized) {
+ int totalDiameter = getClosestOdd(getLength().convertToPixels());
+ if (!outlineMode) {
+ Area area = new Area(getBodyShape());
+ area.subtract(new Area(new Rectangle2D.Double(0, 0, totalDiameter * 0.8,
+ totalDiameter)));
+ g2d.setColor(markerColor);
+ g2d.fill(area);
+ }
+ Color finalTickColor;
+ if (outlineMode) {
+ Theme theme = (Theme) ConfigurationManager.getInstance().readObject(
+ IPlugInPort.THEME_KEY, Constants.DEFAULT_THEME);
+ finalTickColor = theme.getOutlineColor();
+ } else {
+ finalTickColor = tickColor;
+ }
+ g2d.setColor(finalTickColor);
+ g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(2));
+ g2d.drawLine((int) (totalDiameter * 0.9), totalDiameter / 2
+ - (int) (totalDiameter * 0.05), (int) (totalDiameter * 0.9), totalDiameter / 2
+ + (int) (totalDiameter * 0.05));
+ }
+ // int coverDiameter = getClosestOdd(totalDiameter * 3 / 4);
+ // g2d.setColor(coverColor);
+ // int position = (totalDiameter - coverDiameter) / 2;
+ // g2d.fillOval(position, position, coverDiameter, coverDiameter);
+ // g2d.setColor(coverColor.darker());
+ // g2d.drawLine(position + coverDiameter / 5, position + coverDiameter /
+ // 5, position + 4
+ // * coverDiameter / 5, position + 4 * coverDiameter / 5);
+ // g2d.drawLine(position + coverDiameter / 5, position + 4 *
+ // coverDiameter / 5, position + 4
+ // * coverDiameter / 5, position + coverDiameter / 5);
+ }
+
+ @Override
+ protected Size getDefaultWidth() {
+ return null;
+ }
+
+ @Override
+ public Size getWidth() {
+ return super.getWidth();
+ }
+
+ @Override
+ protected Size getDefaultLength() {
+ // We'll reuse width property to set the diameter.
+ return DEFAULT_SIZE;
+ }
+
+ @EditableProperty(name = "Diameter")
+ @Override
+ public Size getLength() {
+ return super.getLength();
+ }
+
+ @EditableProperty(name = "Marker")
+ public Color getMarkerColor() {
+ return markerColor;
+ }
+
+ public void setMarkerColor(Color coverColor) {
+ this.markerColor = coverColor;
+ }
+
+ @EditableProperty(name = "Tick")
+ public Color getTickColor() {
+ return tickColor;
+ }
+
+ public void setTickColor(Color tickColor) {
+ this.tickColor = tickColor;
+ }
+
+ @EditableProperty(name = "Polarized")
+ public boolean getPolarized() {
+ return polarized;
+ }
+
+ public void setPolarized(boolean polarized) {
+ this.polarized = polarized;
+ }
+
+ @Override
+ protected Shape getBodyShape() {
+ return new Ellipse2D.Double(0f, 0f, getClosestOdd(getLength().convertToPixels()),
+ getClosestOdd(getLength().convertToPixels()));
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/passive/RadialFilmCapacitor.java b/diylc-library/src/org/diylc/components/passive/RadialFilmCapacitor.java
new file mode 100644
index 00000000..5da8ff4b
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/passive/RadialFilmCapacitor.java
@@ -0,0 +1,101 @@
+package org.diylc.components.passive;
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Shape;
+import java.awt.geom.RoundRectangle2D;
+
+import org.diylc.components.AbstractLeadedComponent;
+import org.diylc.core.CreationMethod;
+import org.diylc.core.IDIYComponent;
+import org.diylc.core.annotations.ComponentDescriptor;
+import org.diylc.core.annotations.EditableProperty;
+import org.diylc.core.measures.Capacitance;
+import org.diylc.core.measures.CapacitanceUnit;
+import org.diylc.core.measures.Size;
+import org.diylc.core.measures.SizeUnit;
+import org.diylc.core.measures.VoltageUnit;
+
+@ComponentDescriptor(name = "Film Capacitor (radial)", author = "Branislav Stojkovic", category = "Passive", creationMethod = CreationMethod.POINT_BY_POINT, instanceNamePrefix = "C", description = "Radial film capacitor, similar to Sprague Orange Drop", zOrder = IDIYComponent.COMPONENT)
+public class RadialFilmCapacitor extends AbstractLeadedComponent {
+
+ private static final long serialVersionUID = 1L;
+
+ public static Size DEFAULT_WIDTH = new Size(1d, SizeUnit.in);
+ public static Size DEFAULT_HEIGHT = new Size(1d / 4, SizeUnit.in);
+ public static Color BODY_COLOR = Color.decode("#FF8000");
+ public static Color BORDER_COLOR = BODY_COLOR.darker();
+
+ private Capacitance value = new Capacitance(123d, CapacitanceUnit.nF);
+ @Deprecated
+ private Voltage voltage = Voltage._63V;
+ private org.diylc.core.measures.Voltage voltageNew = new org.diylc.core.measures.Voltage(63d,
+ VoltageUnit.V);
+
+ public RadialFilmCapacitor() {
+ super();
+ this.bodyColor = BODY_COLOR;
+ this.borderColor = BORDER_COLOR;
+ }
+
+ @EditableProperty
+ public Capacitance getValue() {
+ return value;
+ }
+
+ public void setValue(Capacitance value) {
+ this.value = value;
+ }
+
+ @Deprecated
+ public Voltage getVoltage() {
+ return voltage;
+ }
+
+ @Deprecated
+ public void setVoltage(Voltage voltage) {
+ this.voltage = voltage;
+ }
+
+ @EditableProperty(name = "Voltage")
+ public org.diylc.core.measures.Voltage getVoltageNew() {
+ // Backward comptibility
+ if (voltageNew == null) {
+ voltageNew = voltage.convertToNewFormat();
+ voltage = null;
+ // Clear old value, don't need it anymore
+ }
+ return voltageNew;
+ }
+
+ public void setVoltageNew(org.diylc.core.measures.Voltage voltageNew) {
+ this.voltageNew = voltageNew;
+ }
+
+ public void drawIcon(Graphics2D g2d, int width, int height) {
+ g2d.rotate(-Math.PI / 4, width / 2, height / 2);
+ g2d.setColor(LEAD_COLOR);
+ g2d.drawLine(0, height / 2, width, height / 2);
+ g2d.setColor(BODY_COLOR);
+ g2d.fillRoundRect(4, height / 2 - 3, width - 8, 6, 5, 5);
+ g2d.setColor(BORDER_COLOR);
+ g2d.drawRoundRect(4, height / 2 - 3, width - 8, 6, 5, 5);
+ }
+
+ @Override
+ protected Size getDefaultWidth() {
+ return DEFAULT_HEIGHT;
+ }
+
+ @Override
+ protected Size getDefaultLength() {
+ return DEFAULT_WIDTH;
+ }
+
+ @Override
+ protected Shape getBodyShape() {
+ double radius = getWidth().convertToPixels() * 0.7;
+ return new RoundRectangle2D.Double(0f, 0f, getLength().convertToPixels(),
+ getClosestOdd(getWidth().convertToPixels()), radius, radius);
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/passive/Resistor.java b/diylc-library/src/org/diylc/components/passive/Resistor.java
new file mode 100644
index 00000000..a13adbe3
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/passive/Resistor.java
@@ -0,0 +1,153 @@
+package org.diylc.components.passive;
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Shape;
+import java.awt.geom.Rectangle2D;
+
+import org.diylc.common.ObjectCache;
+import org.diylc.common.ResistorColorCode;
+import org.diylc.components.AbstractLeadedComponent;
+import org.diylc.core.CreationMethod;
+import org.diylc.core.IDIYComponent;
+import org.diylc.core.annotations.ComponentDescriptor;
+import org.diylc.core.annotations.EditableProperty;
+import org.diylc.core.annotations.PositiveMeasureValidator;
+import org.diylc.core.measures.PowerUnit;
+import org.diylc.core.measures.Resistance;
+import org.diylc.core.measures.ResistanceUnit;
+import org.diylc.core.measures.Size;
+import org.diylc.core.measures.SizeUnit;
+
+@ComponentDescriptor(name = "Resistor", author = "Branislav Stojkovic", category = "Passive", creationMethod = CreationMethod.POINT_BY_POINT, instanceNamePrefix = "R", description = "", zOrder = IDIYComponent.COMPONENT)
+public class Resistor extends AbstractLeadedComponent {
+
+ private static final long serialVersionUID = 1L;
+
+ public static Size DEFAULT_WIDTH = new Size(1d / 2, SizeUnit.in);
+ public static Size DEFAULT_HEIGHT = new Size(1d / 8, SizeUnit.in);
+ public static Color BODY_COLOR = Color.decode("#82CFFD");
+ public static Color BORDER_COLOR = BODY_COLOR.darker();
+ public static int BAND_SPACING = 5;
+ public static int FIRST_BAND = 4;
+
+ private Resistance value = new Resistance(100d, ResistanceUnit.K);
+ @Deprecated
+ private Power power = Power.HALF;
+ private org.diylc.core.measures.Power powerNew = new org.diylc.core.measures.Power(0.5,
+ PowerUnit.W);
+ private ResistorColorCode colorCode = ResistorColorCode._5_BAND;
+
+ public Resistor() {
+ super();
+ this.bodyColor = BODY_COLOR;
+ this.borderColor = BORDER_COLOR;
+ }
+
+ @Override
+ protected boolean supportsStandingMode() {
+ return true;
+ }
+
+ @EditableProperty(validatorClass = PositiveMeasureValidator.class)
+ public Resistance getValue() {
+ return value;
+ }
+
+ public void setValue(Resistance value) {
+ this.value = value;
+ }
+
+ @Deprecated
+ public Power getPower() {
+ return power;
+ }
+
+ @Deprecated
+ public void setPower(Power power) {
+ this.power = power;
+ }
+
+ @EditableProperty(name = "Power rating")
+ public org.diylc.core.measures.Power getPowerNew() {
+ // Backward compatibility
+ if (powerNew == null) {
+ powerNew = power.convertToNewFormat();
+ // Clear old value, don't need it anymore
+ power = null;
+ }
+ return powerNew;
+ }
+
+ public void setPowerNew(org.diylc.core.measures.Power powerNew) {
+ this.powerNew = powerNew;
+ }
+
+ public void drawIcon(Graphics2D g2d, int width, int height) {
+ g2d.rotate(-Math.PI / 4, width / 2, height / 2);
+ g2d.setColor(LEAD_COLOR);
+ g2d.drawLine(0, height / 2, width, height / 2);
+ g2d.setColor(BODY_COLOR);
+ g2d.fillRect(4, height / 2 - 3, width - 8, 6);
+ g2d.setColor(Color.red);
+ g2d.drawLine(7, height / 2 - 3, 7, height / 2 + 3);
+ g2d.setColor(Color.orange);
+ g2d.drawLine(11, height / 2 - 3, 11, height / 2 + 3);
+ g2d.setColor(Color.black);
+ g2d.drawLine(15, height / 2 - 3, 15, height / 2 + 3);
+ g2d.setColor(BORDER_COLOR);
+ g2d.drawRect(4, height / 2 - 3, width - 8, 6);
+ }
+
+ @Override
+ protected Size getDefaultWidth() {
+ return DEFAULT_HEIGHT;
+ }
+
+ @Override
+ protected Size getDefaultLength() {
+ return DEFAULT_WIDTH;
+ }
+
+ @EditableProperty(name = "Color code")
+ public ResistorColorCode getColorCode() {
+ return colorCode;
+ }
+
+ public void setColorCode(ResistorColorCode colorCode) {
+ this.colorCode = colorCode;
+ }
+
+ @Override
+ protected Shape getBodyShape() {
+ return new Rectangle2D.Double(0f, 0f, getLength().convertToPixels(),
+ getClosestOdd(getWidth().convertToPixels()));
+ }
+
+ @Override
+ protected void decorateComponentBody(Graphics2D g2d, boolean outlineMode) {
+ // int width = getWidth().convertToPixels();
+ if (colorCode == ResistorColorCode.NONE || outlineMode) {
+ return;
+ }
+ int height = getClosestOdd(getWidth().convertToPixels());
+ Color[] bands = value.getColorCode(colorCode);
+ int x = FIRST_BAND;
+ g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(2));
+ for (int i = 0; i < bands.length; i++) {
+ g2d.setColor(bands[i]);
+ g2d.drawLine(x, 1, x, height - 1);
+ x += BAND_SPACING;
+ }
+ }
+
+ @Override
+ protected int getLabelOffset(int bodyWidth, int labelWidth) {
+ Color[] bands = value.getColorCode(colorCode);
+ int bandArea = FIRST_BAND + BAND_SPACING * (bands.length - 1);
+ // Only offset the label if overlaping with the band area.
+ if (labelWidth > bodyWidth - 2 * bandArea)
+ return bandArea / 2;
+ return 0;
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/passive/ResistorSymbol.java b/diylc-library/src/org/diylc/components/passive/ResistorSymbol.java
new file mode 100644
index 00000000..2ed5d7ba
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/passive/ResistorSymbol.java
@@ -0,0 +1,105 @@
+package org.diylc.components.passive;
+
+import java.awt.Graphics2D;
+import java.awt.Shape;
+import java.awt.geom.GeneralPath;
+
+import org.diylc.components.AbstractSchematicLeadedSymbol;
+import org.diylc.core.CreationMethod;
+import org.diylc.core.IDIYComponent;
+import org.diylc.core.annotations.ComponentDescriptor;
+import org.diylc.core.annotations.EditableProperty;
+import org.diylc.core.measures.PowerUnit;
+import org.diylc.core.measures.Resistance;
+import org.diylc.core.measures.ResistanceUnit;
+import org.diylc.core.measures.Size;
+import org.diylc.core.measures.SizeUnit;
+
+@ComponentDescriptor(name = "Resistor (schematic symbol)", author = "Branislav Stojkovic", category = "Passive", creationMethod = CreationMethod.POINT_BY_POINT, instanceNamePrefix = "R", description = "Resistor schematic symbol", zOrder = IDIYComponent.COMPONENT)
+public class ResistorSymbol extends AbstractSchematicLeadedSymbol {
+
+ private static final long serialVersionUID = 1L;
+
+ public static Size DEFAULT_LENGTH = new Size(0.3, SizeUnit.in);
+ public static Size DEFAULT_WIDTH = new Size(0.08, SizeUnit.in);
+
+ private Resistance value = new Resistance(100d, ResistanceUnit.K);
+ @Deprecated
+ private Power power = Power.HALF;
+ private org.diylc.core.measures.Power powerNew = new org.diylc.core.measures.Power(0.5,
+ PowerUnit.W);
+
+ @EditableProperty
+ public Resistance getValue() {
+ return value;
+ }
+
+ public void setValue(Resistance value) {
+ this.value = value;
+ }
+
+ @Deprecated
+ public Power getPower() {
+ return power;
+ }
+
+ @Deprecated
+ public void setPower(Power power) {
+ this.power = power;
+ }
+
+ @EditableProperty(name = "Power rating")
+ public org.diylc.core.measures.Power getPowerNew() {
+ // Backward compatibility
+ if (powerNew == null) {
+ powerNew = power.convertToNewFormat();
+ // Clear old value, don't need it anymore
+ power = null;
+ }
+ return powerNew;
+ }
+
+ public void setPowerNew(org.diylc.core.measures.Power powerNew) {
+ this.powerNew = powerNew;
+ }
+
+
+ public void drawIcon(Graphics2D g2d, int width, int height) {
+ g2d.rotate(-Math.PI / 4, width / 2, height / 2);
+ g2d.setColor(LEAD_COLOR);
+ g2d.drawLine(0, height / 2, 4, height / 2);
+ g2d.drawLine(width - 4, height / 2, width, height / 2);
+ g2d.setColor(COLOR);
+ g2d.drawPolyline(new int[] { 4, 6, 10, 14, 18, 22, 26, 28 }, new int[] { height / 2,
+ height / 2 + 2, height / 2 - 2, height / 2 + 2, height / 2 - 2, height / 2 + 2,
+ height / 2 - 2, height / 2 }, 8);
+ }
+
+ @Override
+ protected Size getDefaultWidth() {
+ return DEFAULT_WIDTH;
+ }
+
+ @Override
+ protected Size getDefaultLength() {
+ return DEFAULT_LENGTH;
+ }
+
+ @Override
+ protected Shape getBodyShape() {
+ GeneralPath polyline = new GeneralPath();
+ double length = getLength().convertToPixels();
+ double width = getWidth().convertToPixels();
+ polyline.moveTo(0, width / 2);
+ polyline.lineTo(length / 16, width);
+ polyline.lineTo(3 * length / 16, 0);
+ polyline.lineTo(5 * length / 16, width);
+ polyline.lineTo(7 * length / 16, 0);
+ polyline.lineTo(9 * length / 16, width);
+ polyline.lineTo(11 * length / 16, 0);
+ polyline.lineTo(13 * length / 16, width);
+ polyline.lineTo(15 * length / 16, 0);
+ polyline.lineTo(length, width / 2);
+ return polyline;
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/passive/ResistorValue.java b/diylc-library/src/org/diylc/components/passive/ResistorValue.java
new file mode 100644
index 00000000..4fbe5c80
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/passive/ResistorValue.java
@@ -0,0 +1,28 @@
+package org.diylc.components.passive;
+
+import org.diylc.core.measures.Resistance;
+
+public class ResistorValue {
+
+ private Resistance resistance;
+ private Power power;
+
+ public ResistorValue(Resistance resistance, Power power) {
+ super();
+ this.resistance = resistance;
+ this.power = power;
+ }
+
+ public Resistance getResistance() {
+ return resistance;
+ }
+
+ public Power getPower() {
+ return power;
+ }
+
+ @Override
+ public String toString() {
+ return resistance.toString() + power.toString();
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/passive/Taper.java b/diylc-library/src/org/diylc/components/passive/Taper.java
new file mode 100644
index 00000000..5dcd8f93
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/passive/Taper.java
@@ -0,0 +1,12 @@
+package org.diylc.components.passive;
+
+public enum Taper {
+
+ LIN, LOG, REV_LOG, W, S;
+
+ @Override
+ public String toString() {
+ return name().substring(0, 1).toUpperCase()
+ + name().substring(1).toLowerCase().replace("_", " ");
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/passive/TrimmerPotentiometer.java b/diylc-library/src/org/diylc/components/passive/TrimmerPotentiometer.java
new file mode 100644
index 00000000..f117ccf7
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/passive/TrimmerPotentiometer.java
@@ -0,0 +1,375 @@
+package org.diylc.components.passive;
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Composite;
+import java.awt.FontMetrics;
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.Shape;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Area;
+import java.awt.geom.Ellipse2D;
+import java.awt.geom.Rectangle2D;
+
+import org.diylc.common.ObjectCache;
+import org.diylc.common.Orientation;
+import org.diylc.core.ComponentState;
+import org.diylc.core.CreationMethod;
+import org.diylc.core.IDIYComponent;
+import org.diylc.core.IDrawingObserver;
+import org.diylc.core.Project;
+import org.diylc.core.annotations.ComponentDescriptor;
+import org.diylc.core.annotations.EditableProperty;
+import org.diylc.core.measures.Size;
+import org.diylc.core.measures.SizeUnit;
+
+@ComponentDescriptor(name = "Trimmer Potentiometer", author = "Branislav Stojkovic", category = "Passive", creationMethod = CreationMethod.SINGLE_CLICK, instanceNamePrefix = "VR", description = "Various types of board mounted trimmer potentiometers", zOrder = IDIYComponent.COMPONENT, stretchable = false)
+public class TrimmerPotentiometer extends AbstractPotentiometer {
+
+ private static final long serialVersionUID = 1L;
+
+ protected static Size FLAT_BODY_SIZE = new Size(9.5d, SizeUnit.mm);
+ protected static Size FLAT_SHAFT_SIZE = new Size(4d, SizeUnit.mm);
+ protected static Size VERTICAL_BODY_LENGTH = new Size(9.5d, SizeUnit.mm);
+ protected static Size VERTICAL_BODY_WIDTH = new Size(4.5d, SizeUnit.mm);
+ protected static Size SPACING = new Size(0.1d, SizeUnit.in);
+ private static Color BODY_COLOR = Color.decode("#FFFFE0");
+ private static Color BORDER_COLOR = Color.decode("#8E8E38");
+ private static Color SHAFT_COLOR = Color.decode("#FFFFE0");
+ private static Color SHAFT_BORDER_COLOR = Color.decode("#8E8E38");
+ public static Color PIN_COLOR = Color.decode("#00B2EE");
+ public static Color PIN_BORDER_COLOR = PIN_COLOR.darker();
+ public static Size PIN_SIZE = new Size(0.03d, SizeUnit.in);
+
+ protected Color bodyColor = BODY_COLOR;
+ protected Color borderColor = BORDER_COLOR;
+ // Array of 7 elements: 3 lug connectors, 1 pot body and 3 lugs
+ transient protected Shape[] body = null;
+
+ protected TrimmerType type = TrimmerType.FLAT_SMALL;
+
+ public TrimmerPotentiometer() {
+ controlPoints = new Point[] { new Point(0, 0), new Point(0, 0), new Point(0, 0) };
+ updateControlPoints();
+ }
+
+ protected void updateControlPoints() {
+ int spacing = (int) SPACING.convertToPixels();
+ int dx1 = 0;
+ int dy1 = 0;
+ int dx2 = 0;
+ int dy2 = 0;
+ switch (orientation) {
+ case DEFAULT:
+ switch (type) {
+ case FLAT_SMALL:
+ dx1 = 2 * spacing;
+ dy1 = spacing;
+ dx2 = 0;
+ dy2 = 2 * spacing;
+ break;
+ case FLAT_LARGE:
+ dx1 = 4 * spacing;
+ dy1 = spacing;
+ dx2 = 0;
+ dy2 = 2 * spacing;
+ break;
+ case VERTICAL_INLINE:
+ dx1 = 0;
+ dy1 = spacing;
+ dx2 = 0;
+ dy2 = 2 * spacing;
+ break;
+ case VERTICAL_OFFSET:
+ dx1 = spacing;
+ dy1 = spacing;
+ dx2 = 0;
+ dy2 = 2 * spacing;
+ break;
+ }
+ break;
+ case _90:
+ switch (type) {
+ case FLAT_SMALL:
+ dx1 = -spacing;
+ dy1 = 2 * spacing;
+ dx2 = -2 * spacing;
+ dy2 = 0;
+ break;
+ case FLAT_LARGE:
+ dx1 = -spacing;
+ dy1 = 4 * spacing;
+ dx2 = -2 * spacing;
+ dy2 = 0;
+ break;
+ case VERTICAL_INLINE:
+ dx1 = -spacing;
+ dy1 = 0;
+ dx2 = -2 * spacing;
+ dy2 = 0;
+ break;
+ case VERTICAL_OFFSET:
+ dx1 = -spacing;
+ dy1 = spacing;
+ dx2 = -2 * spacing;
+ dy2 = 0;
+ break;
+ }
+ break;
+ case _180:
+ switch (type) {
+ case FLAT_SMALL:
+ dx1 = -2 * spacing;
+ dy1 = -spacing;
+ dx2 = 0;
+ dy2 = -2 * spacing;
+ break;
+ case FLAT_LARGE:
+ dx1 = -4 * spacing;
+ dy1 = -spacing;
+ dx2 = 0;
+ dy2 = -2 * spacing;
+ break;
+ case VERTICAL_INLINE:
+ dx1 = 0;
+ dy1 = -spacing;
+ dx2 = 0;
+ dy2 = -2 * spacing;
+ break;
+ case VERTICAL_OFFSET:
+ dx1 = -spacing;
+ dy1 = -spacing;
+ dx2 = 0;
+ dy2 = -2 * spacing;
+ break;
+ }
+ break;
+ case _270:
+ switch (type) {
+ case FLAT_SMALL:
+ dx1 = spacing;
+ dy1 = -2 * spacing;
+ dx2 = 2 * spacing;
+ dy2 = 0;
+ break;
+ case FLAT_LARGE:
+ dx1 = spacing;
+ dy1 = -4 * spacing;
+ dx2 = 2 * spacing;
+ dy2 = 0;
+ break;
+ case VERTICAL_INLINE:
+ dx1 = spacing;
+ dy1 = 0;
+ dx2 = 2 * spacing;
+ dy2 = 0;
+ break;
+ case VERTICAL_OFFSET:
+ dx1 = spacing;
+ dy1 = -spacing;
+ dx2 = 2 * spacing;
+ dy2 = 0;
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ controlPoints[1].setLocation(controlPoints[0].x + dx1, controlPoints[0].y + dy1);
+ controlPoints[2].setLocation(controlPoints[0].x + dx2, controlPoints[0].y + dy2);
+ }
+
+ public Shape[] getBody() {
+ if (body == null) {
+ body = new Shape[2];
+
+ // Calculate the center point as center of the minimum bounding
+ // rectangle.
+ int centerX = (Math.max(Math.max(controlPoints[0].x, controlPoints[1].x),
+ controlPoints[2].x) + Math.min(
+ Math.min(controlPoints[0].x, controlPoints[1].x), controlPoints[2].x)) / 2;
+ int centerY = (Math.max(Math.max(controlPoints[0].y, controlPoints[1].y),
+ controlPoints[2].y) + Math.min(
+ Math.min(controlPoints[0].y, controlPoints[1].y), controlPoints[2].y)) / 2;
+
+ // Calculate body dimensions based on the selected type.
+ int length = 0;
+ int width = 0;
+ switch (type) {
+ case FLAT_LARGE:
+ case FLAT_SMALL:
+ length = getClosestOdd(FLAT_BODY_SIZE.convertToPixels());
+ width = length;
+ int shaftSize = getClosestOdd(FLAT_SHAFT_SIZE.convertToPixels());
+ Area shaft = new Area(new Ellipse2D.Double(centerX - shaftSize / 2, centerY
+ - shaftSize / 2, shaftSize, shaftSize));
+ Area slot = new Area(new Rectangle2D.Double(centerX - shaftSize / 2, centerY
+ - shaftSize / 8, shaftSize, shaftSize / 4));
+ slot.transform(AffineTransform.getRotateInstance(Math.PI / 4, centerX, centerY));
+ shaft.subtract(slot);
+ body[1] = shaft;
+ break;
+ case VERTICAL_INLINE:
+ case VERTICAL_OFFSET:
+ length = getClosestOdd(VERTICAL_BODY_LENGTH.convertToPixels());
+ width = getClosestOdd(VERTICAL_BODY_WIDTH.convertToPixels());
+ break;
+ }
+ if (orientation == Orientation.DEFAULT || orientation == Orientation._180) {
+ int p = length;
+ length = width;
+ width = p;
+ }
+ body[0] = new Rectangle2D.Double(centerX - length / 2, centerY - width / 2, length,
+ width);
+ }
+ return body;
+ }
+
+ @Override
+ public void setControlPoint(Point point, int index) {
+ super.setControlPoint(point, index);
+ body = null;
+ }
+
+ @Override
+ public void setOrientation(Orientation orientation) {
+ super.setOrientation(orientation);
+ updateControlPoints();
+ body = null;
+ }
+
+ @Override
+ public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode,
+ Project project, IDrawingObserver drawingObserver) {
+ if (checkPointsClipped(g2d.getClip())) {
+ return;
+ }
+ g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
+ Shape mainShape = getBody()[0];
+ Shape shaftShape = getBody()[1];
+ if (mainShape != null) {
+ g2d.setColor(bodyColor);
+ Composite oldComposite = g2d.getComposite();
+ if (alpha < MAX_ALPHA) {
+ g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha
+ / MAX_ALPHA));
+ }
+ g2d.fill(mainShape);
+ if (shaftShape != null) {
+ g2d.setColor(SHAFT_COLOR);
+ g2d.fill(shaftShape);
+ g2d.setColor(SHAFT_BORDER_COLOR);
+ g2d.draw(shaftShape);
+ }
+ g2d.setComposite(oldComposite);
+ g2d.setColor(componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? SELECTION_COLOR : borderColor);
+ g2d.draw(mainShape);
+ }
+
+ // Draw pins.
+ int pinSize = getClosestOdd(PIN_SIZE.convertToPixels());
+ for (Point point : controlPoints) {
+ g2d.setColor(PIN_COLOR);
+ g2d.fillOval(point.x - pinSize / 2, point.y - pinSize / 2, pinSize, pinSize);
+ g2d.setColor(PIN_BORDER_COLOR);
+ g2d.drawOval(point.x - pinSize / 2, point.y - pinSize / 2, pinSize, pinSize);
+ }
+
+ // Draw label.
+ g2d.setFont(LABEL_FONT);
+ g2d
+ .setColor(componentState == ComponentState.SELECTED ? LABEL_COLOR_SELECTED
+ : LABEL_COLOR);
+ FontMetrics fontMetrics = g2d.getFontMetrics();
+ Rectangle2D bodyRect = getBody()[0].getBounds2D();
+ Rectangle2D rect = fontMetrics.getStringBounds(getName(), g2d);
+
+ int textHeight = (int) rect.getHeight();
+ int textWidth = (int) rect.getWidth();
+ int panelHeight = (int) bodyRect.getHeight();
+ int panelWidth = (int) bodyRect.getWidth();
+
+ int x = (panelWidth - textWidth) / 2;
+ int y = (panelHeight - textHeight) / 2 + fontMetrics.getAscent();
+
+ g2d.drawString(getName(), (int) (bodyRect.getX() + x), (int) (bodyRect.getY() + y));
+ }
+
+ @Override
+ public void drawIcon(Graphics2D g2d, int width, int height) {
+ int margin = 4;
+ g2d.setColor(BODY_COLOR);
+ g2d.fillRect(margin, margin, width - 2 * margin, width - 2 * margin);
+ g2d.setColor(BORDER_COLOR);
+ g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
+ g2d.drawRect(margin, margin, width - 2 * margin, width - 2 * margin);
+ int shaftSize = 11;
+ int slotSize = 2;
+ Area area = new Area(new Ellipse2D.Double(width / 2 - shaftSize / 2, width / 2 - shaftSize
+ / 2, shaftSize, shaftSize));
+ Area slot = new Area(new Rectangle2D.Double(0, width / 2 - slotSize / 2, width, slotSize));
+ slot.transform(AffineTransform.getRotateInstance(Math.PI / 4, width / 2, width / 2));
+ area.subtract(slot);
+ g2d.setColor(SHAFT_COLOR);
+ g2d.fill(area);
+ g2d.setColor(SHAFT_BORDER_COLOR);
+ g2d.draw(area);
+
+ int pinSize = 3;
+ g2d.setColor(PIN_COLOR);
+ g2d.fillOval(margin - pinSize / 2, 10 - pinSize / 2, pinSize, pinSize);
+ g2d.setColor(PIN_BORDER_COLOR);
+ g2d.drawOval(margin - pinSize / 2, 10 - pinSize / 2, pinSize, pinSize);
+
+ g2d.setColor(PIN_COLOR);
+ g2d.fillOval(margin - pinSize / 2, 21 - pinSize / 2, pinSize, pinSize);
+ g2d.setColor(PIN_BORDER_COLOR);
+ g2d.drawOval(margin - pinSize / 2, 21 - pinSize / 2, pinSize, pinSize);
+
+ g2d.setColor(PIN_COLOR);
+ g2d.fillOval(width - margin - pinSize / 2, width / 2 - pinSize / 2, pinSize, pinSize);
+ g2d.setColor(PIN_BORDER_COLOR);
+ g2d.drawOval(width - margin - pinSize / 2, width / 2 - pinSize / 2, pinSize, pinSize);
+ }
+
+ @EditableProperty(name = "Body")
+ public Color getBodyColor() {
+ return bodyColor;
+ }
+
+ public void setBodyColor(Color bodyColor) {
+ this.bodyColor = bodyColor;
+ }
+
+ @EditableProperty(name = "Border")
+ public Color getBorderColor() {
+ return borderColor;
+ }
+
+ public void setBorderColor(Color borderColor) {
+ this.borderColor = borderColor;
+ }
+
+ @EditableProperty
+ public TrimmerType getType() {
+ return type;
+ }
+
+ public void setType(TrimmerType type) {
+ this.type = type;
+ updateControlPoints();
+ body = null;
+ }
+
+ public static enum TrimmerType {
+ FLAT_LARGE, FLAT_SMALL, VERTICAL_INLINE, VERTICAL_OFFSET;
+
+ @Override
+ public String toString() {
+ return name().substring(0, 1) + name().substring(1).toLowerCase().replace("_", " ");
+ }
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/passive/Voltage.java b/diylc-library/src/org/diylc/components/passive/Voltage.java
new file mode 100644
index 00000000..e27b308a
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/passive/Voltage.java
@@ -0,0 +1,58 @@
+package org.diylc.components.passive;
+
+import org.diylc.core.measures.VoltageUnit;
+
+public enum Voltage {
+
+ _16V, _25V, _63V, _100V, _160V, _250V, _300V, _350V, _400V, _500V, _630V, _1KV;
+
+ @Override
+ public String toString() {
+ return name().replace("_", "");
+ }
+
+ public org.diylc.core.measures.Voltage convertToNewFormat() {
+ org.diylc.core.measures.Voltage voltageNew;
+ switch (this) {
+ case _100V:
+ voltageNew = new org.diylc.core.measures.Voltage(100d, VoltageUnit.V);
+ break;
+ case _160V:
+ voltageNew = new org.diylc.core.measures.Voltage(160d, VoltageUnit.V);
+ break;
+ case _16V:
+ voltageNew = new org.diylc.core.measures.Voltage(16d, VoltageUnit.V);
+ break;
+ case _1KV:
+ voltageNew = new org.diylc.core.measures.Voltage(1d, VoltageUnit.KV);
+ break;
+ case _250V:
+ voltageNew = new org.diylc.core.measures.Voltage(250d, VoltageUnit.V);
+ break;
+ case _25V:
+ voltageNew = new org.diylc.core.measures.Voltage(25d, VoltageUnit.V);
+ break;
+ case _300V:
+ voltageNew = new org.diylc.core.measures.Voltage(300d, VoltageUnit.V);
+ break;
+ case _350V:
+ voltageNew = new org.diylc.core.measures.Voltage(350d, VoltageUnit.V);
+ break;
+ case _400V:
+ voltageNew = new org.diylc.core.measures.Voltage(400d, VoltageUnit.V);
+ break;
+ case _500V:
+ voltageNew = new org.diylc.core.measures.Voltage(500d, VoltageUnit.V);
+ break;
+ case _630V:
+ voltageNew = new org.diylc.core.measures.Voltage(630d, VoltageUnit.V);
+ break;
+ case _63V:
+ voltageNew = new org.diylc.core.measures.Voltage(63d, VoltageUnit.V);
+ break;
+ default:
+ voltageNew = new org.diylc.core.measures.Voltage(100d, VoltageUnit.V);
+ }
+ return voltageNew;
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/semiconductors/AbstractDiodeSymbol.java b/diylc-library/src/org/diylc/components/semiconductors/AbstractDiodeSymbol.java
new file mode 100644
index 00000000..2fe93373
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/semiconductors/AbstractDiodeSymbol.java
@@ -0,0 +1,20 @@
+package org.diylc.components.semiconductors;
+
+import java.awt.Shape;
+
+import org.diylc.components.AbstractLeadedComponent;
+
+public abstract class AbstractDiodeSymbol extends AbstractLeadedComponent {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected boolean shouldShadeLeads() {
+ return false;
+ }
+
+ @Override
+ protected Shape getBodyShape() {
+ return null;
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/semiconductors/AbstractTransistorSymbol.java b/diylc-library/src/org/diylc/components/semiconductors/AbstractTransistorSymbol.java
new file mode 100644
index 00000000..7114e7b4
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/semiconductors/AbstractTransistorSymbol.java
@@ -0,0 +1,173 @@
+package org.diylc.components.semiconductors;
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.Shape;
+
+import org.diylc.appframework.miscutils.ConfigurationManager;
+import org.diylc.common.Display;
+import org.diylc.common.HorizontalAlignment;
+import org.diylc.common.IPlugInPort;
+import org.diylc.common.ObjectCache;
+import org.diylc.common.VerticalAlignment;
+import org.diylc.components.AbstractComponent;
+import org.diylc.core.ComponentState;
+import org.diylc.core.IDrawingObserver;
+import org.diylc.core.Project;
+import org.diylc.core.Theme;
+import org.diylc.core.VisibilityPolicy;
+import org.diylc.core.annotations.EditableProperty;
+import org.diylc.core.measures.Size;
+import org.diylc.core.measures.SizeUnit;
+import org.diylc.utils.Constants;
+
+public abstract class AbstractTransistorSymbol extends AbstractComponent {
+
+ private static final long serialVersionUID = 1L;
+
+ public static Size PIN_SPACING = new Size(0.1d, SizeUnit.in);
+ public static Color COLOR = Color.black;
+
+ protected String value = "";
+ protected Point[] controlPoints = new Point[] { new Point(0, 0), new Point(0, 0),
+ new Point(0, 0) };
+ protected Color color = COLOR;
+ protected Display display = Display.NAME;
+ transient protected Shape[] body;
+
+ public AbstractTransistorSymbol() {
+ super();
+ updateControlPoints();
+ }
+
+ @Override
+ public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode,
+ Project project, IDrawingObserver drawingObserver) {
+ if (checkPointsClipped(g2d.getClip())) {
+ return;
+ }
+ int pinSpacing = (int) PIN_SPACING.convertToPixels();
+ Color finalColor;
+ if (componentState == ComponentState.SELECTED) {
+ finalColor = SELECTION_COLOR;
+ } else if (outlineMode) {
+ Theme theme = (Theme) ConfigurationManager.getInstance().readObject(
+ IPlugInPort.THEME_KEY, Constants.DEFAULT_THEME);
+ finalColor = theme.getOutlineColor();
+ } else {
+ finalColor = color;
+ }
+ g2d.setColor(finalColor);
+
+ // Draw transistor
+
+ int x = controlPoints[0].x;
+ int y = controlPoints[0].y;
+
+ Shape[] body = getBody();
+
+ g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(2));
+ g2d.draw(body[0]);
+
+ g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
+ g2d.draw(body[1]);
+
+ g2d.fill(body[2]);
+
+ // Draw label
+ g2d.setFont(LABEL_FONT);
+ Color finalLabelColor;
+ if (outlineMode) {
+ Theme theme = (Theme) ConfigurationManager.getInstance().readObject(
+ IPlugInPort.THEME_KEY, Constants.DEFAULT_THEME);
+ finalLabelColor = componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? LABEL_COLOR_SELECTED : theme
+ .getOutlineColor();
+ } else {
+ finalLabelColor = componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? LABEL_COLOR_SELECTED
+ : LABEL_COLOR;
+ }
+ g2d.setColor(finalLabelColor);
+ drawCenteredText(g2d, display == Display.VALUE ? getValue() : getName(),
+ x + pinSpacing * 2, y, HorizontalAlignment.LEFT, VerticalAlignment.CENTER);
+ }
+
+ @Override
+ public Point getControlPoint(int index) {
+ return controlPoints[index];
+ }
+
+ @Override
+ public int getControlPointCount() {
+ return controlPoints.length;
+ }
+
+ private void updateControlPoints() {
+ int pinSpacing = (int) PIN_SPACING.convertToPixels();
+ // Update control points.
+ int x = controlPoints[0].x;
+ int y = controlPoints[0].y;
+
+ controlPoints[1].x = x + pinSpacing * 2;
+ controlPoints[1].y = y - pinSpacing * 2;
+
+ controlPoints[2].x = x + pinSpacing * 2;
+ controlPoints[2].y = y + pinSpacing * 2;
+ }
+
+ @Override
+ public VisibilityPolicy getControlPointVisibilityPolicy(int index) {
+ return VisibilityPolicy.WHEN_SELECTED;
+ }
+
+ @EditableProperty
+ @Override
+ public String getValue() {
+ return this.value;
+ }
+
+ @Override
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ @Override
+ public boolean isControlPointSticky(int index) {
+ return true;
+ }
+
+ @Override
+ public void setControlPoint(Point point, int index) {
+ controlPoints[index].setLocation(point);
+ // Invalidate body
+ body = null;
+ }
+
+ @EditableProperty
+ public Color getColor() {
+ return color;
+ }
+
+ public void setColor(Color color) {
+ this.color = color;
+ }
+
+ @EditableProperty
+ public Display getDisplay() {
+ return display;
+ }
+
+ public void setDisplay(Display display) {
+ this.display = display;
+ }
+
+ /**
+ * Returns transistor shape consisting of 3 parts, in this order: main body,
+ * connectors, polarity arrow.
+ *
+ * @return
+ */
+ protected abstract Shape[] getBody();
+}
diff --git a/diylc-library/src/org/diylc/components/semiconductors/BJTPolarity.java b/diylc-library/src/org/diylc/components/semiconductors/BJTPolarity.java
new file mode 100644
index 00000000..15590148
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/semiconductors/BJTPolarity.java
@@ -0,0 +1,11 @@
+package org.diylc.components.semiconductors;
+
+public enum BJTPolarity {
+
+ NPN, PNP;
+
+ @Override
+ public String toString() {
+ return name();
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/semiconductors/BJTSymbol.java b/diylc-library/src/org/diylc/components/semiconductors/BJTSymbol.java
new file mode 100644
index 00000000..95cabfc4
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/semiconductors/BJTSymbol.java
@@ -0,0 +1,98 @@
+package org.diylc.components.semiconductors;
+
+import java.awt.Graphics2D;
+import java.awt.Polygon;
+import java.awt.Shape;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Area;
+import java.awt.geom.GeneralPath;
+
+import org.diylc.common.ObjectCache;
+import org.diylc.core.IDIYComponent;
+import org.diylc.core.annotations.ComponentDescriptor;
+import org.diylc.core.annotations.EditableProperty;
+
+@ComponentDescriptor(name = "BJT Symbol", author = "Branislav Stojkovic", category = "Semiconductors", instanceNamePrefix = "Q", description = "Bipolar junction transistor schematic symbol", stretchable = false, zOrder = IDIYComponent.COMPONENT)
+public class BJTSymbol extends AbstractTransistorSymbol {
+
+ private static final long serialVersionUID = 1L;
+
+ protected BJTPolarity polarity = BJTPolarity.NPN;
+
+ public Shape[] getBody() {
+ if (body == null) {
+ body = new Shape[3];
+ int x = controlPoints[0].x;
+ int y = controlPoints[0].y;
+ int pinSpacing = (int) PIN_SPACING.convertToPixels();
+
+ GeneralPath polyline = new GeneralPath();
+
+ polyline.moveTo(x + pinSpacing / 2, y - pinSpacing);
+ polyline.lineTo(x + pinSpacing / 2, y + pinSpacing);
+
+ body[0] = polyline;
+
+ polyline = new GeneralPath();
+
+ polyline.moveTo(x, y);
+ polyline.lineTo(x + pinSpacing / 2, y);
+ polyline.moveTo(x + pinSpacing / 2, y - pinSpacing / 2);
+ polyline.lineTo(x + pinSpacing * 2, y - pinSpacing);
+ polyline.lineTo(x + pinSpacing * 2, y - pinSpacing * 2);
+ polyline.moveTo(x + pinSpacing / 2, y + pinSpacing / 2);
+ polyline.lineTo(x + pinSpacing * 2, y + pinSpacing);
+ polyline.lineTo(x + pinSpacing * 2, y + pinSpacing * 2);
+ body[1] = polyline;
+
+ Area arrow;
+ double theta;
+ if (polarity == BJTPolarity.NPN) {
+ theta = Math.atan(1.0 / 3);
+ arrow = new Area(new Polygon(new int[] { x + pinSpacing, x + pinSpacing,
+ x + pinSpacing * 10 / 6 }, new int[] { y - pinSpacing / 5 + pinSpacing / 2,
+ y + pinSpacing / 5 + pinSpacing / 2, y + pinSpacing / 2 }, 3));
+ arrow.transform(AffineTransform.getRotateInstance(theta, x + pinSpacing / 2, y
+ + pinSpacing / 2));
+ } else {
+ theta = -Math.atan(1.0 / 3);
+ arrow = new Area(new Polygon(new int[] { x + pinSpacing, x + pinSpacing * 10 / 6,
+ x + pinSpacing * 10 / 6 }, new int[] { y - pinSpacing / 2,
+ y - pinSpacing / 5 - pinSpacing / 2, y + pinSpacing / 5 - pinSpacing / 2 },
+ 3));
+ arrow.transform(AffineTransform.getRotateInstance(theta, x + pinSpacing / 2, y
+ - pinSpacing / 2));
+ }
+ body[2] = arrow;
+ }
+ return body;
+ }
+
+ @Override
+ public void drawIcon(Graphics2D g2d, int width, int height) {
+ g2d.setColor(COLOR);
+
+ g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(2));
+ g2d.drawLine(width / 3, height / 5, width / 3, height * 4 / 5);
+
+ g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
+ g2d.drawLine(width / 8, height / 2, width / 3, height / 2);
+
+ g2d.drawLine(width * 3 / 4, 1, width * 3 / 4, height / 4);
+ g2d.drawLine(width / 3, height / 3 + 1, width * 3 / 4, height / 4);
+
+ g2d.drawLine(width * 3 / 4, height - 1, width * 3 / 4, height * 3 / 4);
+ g2d.drawLine(width / 3, height * 2 / 3 - 1, width * 3 / 4, height * 3 / 4);
+ }
+
+ @EditableProperty(name = "Polarity")
+ public BJTPolarity getPolarity() {
+ return polarity;
+ }
+
+ public void setPolarity(BJTPolarity polarity) {
+ this.polarity = polarity;
+ // Invalidate body
+ body = null;
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/semiconductors/DIL_IC.java b/diylc-library/src/org/diylc/components/semiconductors/DIL_IC.java
new file mode 100644
index 00000000..2ba52f9e
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/semiconductors/DIL_IC.java
@@ -0,0 +1,372 @@
+package org.diylc.components.semiconductors;
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Composite;
+import java.awt.FontMetrics;
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.geom.Area;
+import java.awt.geom.Ellipse2D;
+import java.awt.geom.Rectangle2D;
+import java.awt.geom.RoundRectangle2D;
+
+import org.diylc.appframework.miscutils.ConfigurationManager;
+import org.diylc.common.Display;
+import org.diylc.common.IPlugInPort;
+import org.diylc.common.ObjectCache;
+import org.diylc.common.Orientation;
+import org.diylc.components.AbstractTransparentComponent;
+import org.diylc.core.ComponentState;
+import org.diylc.core.IDIYComponent;
+import org.diylc.core.IDrawingObserver;
+import org.diylc.core.Project;
+import org.diylc.core.Theme;
+import org.diylc.core.VisibilityPolicy;
+import org.diylc.core.annotations.ComponentDescriptor;
+import org.diylc.core.annotations.EditableProperty;
+import org.diylc.core.measures.Size;
+import org.diylc.core.measures.SizeUnit;
+import org.diylc.utils.Constants;
+
+@ComponentDescriptor(name = "DIL IC", author = "Branislav Stojkovic", category = "Semiconductors", instanceNamePrefix = "IC", description = "IC with rectangular housing and two parallel rows of pins", stretchable = false, zOrder = IDIYComponent.COMPONENT)
+public class DIL_IC extends AbstractTransparentComponent {
+
+ private static final long serialVersionUID = 1L;
+
+ public static Color BODY_COLOR = Color.gray;
+ public static Color BORDER_COLOR = Color.gray.darker();
+ public static Color PIN_COLOR = Color.decode("#00B2EE");
+ public static Color PIN_BORDER_COLOR = PIN_COLOR.darker();
+ public static Color INDENT_COLOR = Color.gray.darker();
+ public static Color LABEL_COLOR = Color.white;
+ public static int EDGE_RADIUS = 6;
+ public static Size PIN_SIZE = new Size(0.04d, SizeUnit.in);
+ public static Size INDENT_SIZE = new Size(0.07d, SizeUnit.in);
+
+ private String value = "";
+ private Orientation orientation = Orientation.DEFAULT;
+ private PinCount pinCount = PinCount._8;
+ private Size pinSpacing = new Size(0.1d, SizeUnit.in);
+ private Size rowSpacing = new Size(0.3d, SizeUnit.in);
+ private Point[] controlPoints = new Point[] { new Point(0, 0) };
+ protected Display display = Display.NAME;
+ // new Point(0, pinSpacing.convertToPixels()),
+ // new Point(0, 2 * pinSpacing.convertToPixels()),
+ // new Point(0, 3 * pinSpacing.convertToPixels()),
+ // new Point(3 * pinSpacing.convertToPixels(), 0),
+ // new Point(3 * pinSpacing.convertToPixels(),
+ // pinSpacing.convertToPixels()),
+ // new Point(3 * pinSpacing.convertToPixels(), 2 *
+ // pinSpacing.convertToPixels()),
+ // new Point(3 * pinSpacing.convertToPixels(), 3 *
+ // pinSpacing.convertToPixels()) };
+ transient private Area[] body;
+
+ public DIL_IC() {
+ super();
+ updateControlPoints();
+ }
+
+ @EditableProperty
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ @EditableProperty
+ public Orientation getOrientation() {
+ return orientation;
+ }
+
+ public void setOrientation(Orientation orientation) {
+ this.orientation = orientation;
+ updateControlPoints();
+ // Reset body shape.
+ body = null;
+ }
+
+ @EditableProperty(name = "Pins")
+ public PinCount getPinCount() {
+ return pinCount;
+ }
+
+ public void setPinCount(PinCount pinCount) {
+ this.pinCount = pinCount;
+ updateControlPoints();
+ // Reset body shape;
+ body = null;
+ }
+
+ @EditableProperty(name = "Pin spacing")
+ public Size getPinSpacing() {
+ return pinSpacing;
+ }
+
+ public void setPinSpacing(Size pinSpacing) {
+ this.pinSpacing = pinSpacing;
+ }
+
+ @EditableProperty(name = "Row spacing")
+ public Size getRowSpacing() {
+ return rowSpacing;
+ }
+
+ public void setRowSpacing(Size rowSpacing) {
+ this.rowSpacing = rowSpacing;
+ updateControlPoints();
+ // Reset body shape;
+ body = null;
+ }
+
+ @EditableProperty
+ public Display getDisplay() {
+ if (display == null) {
+ display = Display.VALUE;
+ }
+ return display;
+ }
+
+ public void setDisplay(Display display) {
+ this.display = display;
+ }
+
+ @Override
+ public int getControlPointCount() {
+ return controlPoints.length;
+ }
+
+ @Override
+ public Point getControlPoint(int index) {
+ return controlPoints[index];
+ }
+
+ @Override
+ public boolean isControlPointSticky(int index) {
+ return true;
+ }
+
+ @Override
+ public VisibilityPolicy getControlPointVisibilityPolicy(int index) {
+ return VisibilityPolicy.NEVER;
+ }
+
+ @Override
+ public void setControlPoint(Point point, int index) {
+ controlPoints[index].setLocation(point);
+ body = null;
+ }
+
+ private void updateControlPoints() {
+ Point firstPoint = controlPoints[0];
+ controlPoints = new Point[pinCount.getValue()];
+ controlPoints[0] = firstPoint;
+ int pinSpacing = (int) this.pinSpacing.convertToPixels();
+ int rowSpacing = (int) this.rowSpacing.convertToPixels();
+ // Update control points.
+ int dx1;
+ int dy1;
+ int dx2;
+ int dy2;
+ for (int i = 0; i < pinCount.getValue() / 2; i++) {
+ switch (orientation) {
+ case DEFAULT:
+ dx1 = 0;
+ dy1 = i * pinSpacing;
+ dx2 = rowSpacing;
+ dy2 = i * pinSpacing;
+ break;
+ case _90:
+ dx1 = -i * pinSpacing;
+ dy1 = 0;
+ dx2 = -i * pinSpacing;
+ dy2 = rowSpacing;
+ break;
+ case _180:
+ dx1 = 0;
+ dy1 = -i * pinSpacing;
+ dx2 = -rowSpacing;
+ dy2 = -i * pinSpacing;
+ break;
+ case _270:
+ dx1 = i * pinSpacing;
+ dy1 = 0;
+ dx2 = i * pinSpacing;
+ dy2 = -rowSpacing;
+ break;
+ default:
+ throw new RuntimeException("Unexpected orientation: " + orientation);
+ }
+ controlPoints[i] = new Point(firstPoint.x + dx1, firstPoint.y + dy1);
+ controlPoints[i + pinCount.getValue() / 2] = new Point(firstPoint.x + dx2, firstPoint.y
+ + dy2);
+ }
+ }
+
+ public Area[] getBody() {
+ if (body == null) {
+ body = new Area[2];
+ int x = controlPoints[0].x;
+ int y = controlPoints[0].y;
+ int width;
+ int height;
+ int pinSize = (int) PIN_SIZE.convertToPixels();
+ int pinSpacing = (int) this.pinSpacing.convertToPixels();
+ int rowSpacing = (int) this.rowSpacing.convertToPixels();
+ Area indentation = null;
+ int indentationSize = getClosestOdd(INDENT_SIZE.convertToPixels());
+ switch (orientation) {
+ case DEFAULT:
+ width = rowSpacing - pinSize;
+ height = (pinCount.getValue() / 2) * pinSpacing;
+ x += pinSize / 2;
+ y -= pinSpacing / 2;
+ indentation = new Area(new Ellipse2D.Double(x + width / 2 - indentationSize / 2, y
+ - indentationSize / 2, indentationSize, indentationSize));
+ break;
+ case _90:
+ width = (pinCount.getValue() / 2) * pinSpacing;
+ height = rowSpacing - pinSize;
+ x -= (pinSpacing / 2) + width - pinSpacing;
+ y += pinSize / 2;
+ indentation = new Area(new Ellipse2D.Double(x + width - indentationSize / 2, y
+ + height / 2 - indentationSize / 2, indentationSize, indentationSize));
+ break;
+ case _180:
+ width = rowSpacing - pinSize;
+ height = (pinCount.getValue() / 2) * pinSpacing;
+ x -= rowSpacing - pinSize / 2;
+ y -= (pinSpacing / 2) + height - pinSpacing;
+ indentation = new Area(new Ellipse2D.Double(x + width / 2 - indentationSize / 2, y
+ + height - indentationSize / 2, indentationSize, indentationSize));
+ break;
+ case _270:
+ width = (pinCount.getValue() / 2) * pinSpacing;
+ height = rowSpacing - pinSize;
+ x -= pinSpacing / 2;
+ y += pinSize / 2 - rowSpacing;
+ indentation = new Area(new Ellipse2D.Double(x - indentationSize / 2, y + height / 2
+ - indentationSize / 2, indentationSize, indentationSize));
+ break;
+ default:
+ throw new RuntimeException("Unexpected orientation: " + orientation);
+ }
+ body[0] = new Area(new RoundRectangle2D.Double(x, y, width, height, EDGE_RADIUS,
+ EDGE_RADIUS));
+ body[1] = indentation;
+ if (indentation != null) {
+ indentation.intersect(body[0]);
+ }
+ }
+ return body;
+ }
+
+ @Override
+ public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode,
+ Project project, IDrawingObserver drawingObserver) {
+ if (checkPointsClipped(g2d.getClip())) {
+ return;
+ }
+ Area mainArea = getBody()[0];
+ if (!outlineMode) {
+ int pinSize = (int) PIN_SIZE.convertToPixels() / 2 * 2;
+ for (Point point : controlPoints) {
+ g2d.setColor(PIN_COLOR);
+ g2d.fillRect(point.x - pinSize / 2, point.y - pinSize / 2, pinSize, pinSize);
+ g2d.setColor(PIN_BORDER_COLOR);
+ g2d.drawRect(point.x - pinSize / 2, point.y - pinSize / 2, pinSize, pinSize);
+ }
+ }
+ Composite oldComposite = g2d.getComposite();
+ if (alpha < MAX_ALPHA) {
+ g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha
+ / MAX_ALPHA));
+ }
+ g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : BODY_COLOR);
+ g2d.fill(mainArea);
+ g2d.setComposite(oldComposite);
+
+ Color finalBorderColor;
+ if (outlineMode) {
+ Theme theme = (Theme) ConfigurationManager.getInstance().readObject(
+ IPlugInPort.THEME_KEY, Constants.DEFAULT_THEME);
+ finalBorderColor = componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? SELECTION_COLOR : theme
+ .getOutlineColor();
+ } else {
+ finalBorderColor = componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? SELECTION_COLOR : BORDER_COLOR;
+ }
+ g2d.setColor(finalBorderColor);
+ g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
+ if (outlineMode) {
+ Area area = new Area(mainArea);
+ area.subtract(getBody()[1]);
+ g2d.draw(area);
+ } else {
+ g2d.draw(mainArea);
+ if (getBody()[1] != null) {
+ g2d.setColor(INDENT_COLOR);
+ g2d.fill(getBody()[1]);
+ }
+ }
+ // Draw label.
+ g2d.setFont(LABEL_FONT);
+ Color finalLabelColor;
+ if (outlineMode) {
+ Theme theme = (Theme) ConfigurationManager.getInstance().readObject(
+ IPlugInPort.THEME_KEY, Constants.DEFAULT_THEME);
+ finalLabelColor = componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? LABEL_COLOR_SELECTED : theme
+ .getOutlineColor();
+ } else {
+ finalLabelColor = componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? LABEL_COLOR_SELECTED
+ : LABEL_COLOR;
+ }
+ g2d.setColor(finalLabelColor);
+ FontMetrics fontMetrics = g2d.getFontMetrics(g2d.getFont());
+ String label = display == Display.NAME ? getName() : getValue();
+ Rectangle2D rect = fontMetrics.getStringBounds(label, g2d);
+ int textHeight = (int) (rect.getHeight());
+ int textWidth = (int) (rect.getWidth());
+ // Center text horizontally and vertically
+ Rectangle bounds = mainArea.getBounds();
+ int x = bounds.x + (bounds.width - textWidth) / 2;
+ int y = bounds.y + (bounds.height - textHeight) / 2 + fontMetrics.getAscent();
+ g2d.drawString(label, x, y);
+ }
+
+ @Override
+ public void drawIcon(Graphics2D g2d, int width, int height) {
+ int radius = 6 * width / 32;
+ g2d.setColor(BODY_COLOR);
+ g2d.fillRoundRect(width / 6, 1, 4 * width / 6, height - 4, radius, radius);
+ g2d.setColor(BORDER_COLOR);
+ g2d.drawRoundRect(width / 6, 1, 4 * width / 6, height - 4, radius, radius);
+ int pinSize = 2 * width / 32;
+ g2d.setColor(PIN_COLOR);
+ for (int i = 0; i < 4; i++) {
+ g2d.fillRect(width / 6 - pinSize, (height / 5) * (i + 1) - 1, pinSize, pinSize);
+ g2d.fillRect(5 * width / 6 + 1, (height / 5) * (i + 1) - 1, pinSize, pinSize);
+ }
+ }
+
+ 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;
+
+ @Override
+ public String toString() {
+ return name().replace("_", "");
+ }
+
+ public int getValue() {
+ return Integer.parseInt(toString());
+ }
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/semiconductors/DiodePlastic.java b/diylc-library/src/org/diylc/components/semiconductors/DiodePlastic.java
new file mode 100644
index 00000000..bd7d4e2f
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/semiconductors/DiodePlastic.java
@@ -0,0 +1,111 @@
+package org.diylc.components.semiconductors;
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Shape;
+import java.awt.geom.Rectangle2D;
+
+import org.diylc.appframework.miscutils.ConfigurationManager;
+import org.diylc.common.IPlugInPort;
+import org.diylc.components.AbstractLeadedComponent;
+import org.diylc.core.CreationMethod;
+import org.diylc.core.IDIYComponent;
+import org.diylc.core.Theme;
+import org.diylc.core.annotations.ComponentDescriptor;
+import org.diylc.core.annotations.EditableProperty;
+import org.diylc.core.measures.Size;
+import org.diylc.core.measures.SizeUnit;
+import org.diylc.utils.Constants;
+
+@ComponentDescriptor(name = "Diode (plastic)", author = "Branislav Stojkovic", category = "Semiconductors", creationMethod = CreationMethod.POINT_BY_POINT, instanceNamePrefix = "D", description = "Plastic diode, like most rectifier, zener, schottky, etc.", zOrder = IDIYComponent.COMPONENT)
+public class DiodePlastic extends AbstractLeadedComponent {
+
+ private static final long serialVersionUID = 1L;
+
+ public static Size DEFAULT_WIDTH = new Size(1d / 4, SizeUnit.in);
+ public static Size DEFAULT_HEIGHT = new Size(1d / 8, SizeUnit.in);
+ public static Size MARKER_WIDTH = new Size(1d, SizeUnit.mm);
+ public static Color BODY_COLOR = Color.darkGray;
+ public static Color MARKER_COLOR = Color.decode("#DDDDDD");
+ public static Color LABEL_COLOR = Color.white;
+ public static Color BORDER_COLOR = BODY_COLOR.darker();
+
+ private String value = "";
+ private Color markerColor = MARKER_COLOR;
+
+ public DiodePlastic() {
+ super();
+ this.labelColor = LABEL_COLOR;
+ this.bodyColor = BODY_COLOR;
+ this.borderColor = BORDER_COLOR;
+ }
+
+ @Override
+ protected boolean supportsStandingMode() {
+ return true;
+ }
+
+ @EditableProperty
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ public void drawIcon(Graphics2D g2d, int width, int height) {
+ g2d.rotate(-Math.PI / 4, width / 2, height / 2);
+ g2d.setColor(LEAD_COLOR);
+ g2d.drawLine(0, height / 2, width, height / 2);
+ g2d.setColor(BODY_COLOR);
+ g2d.fillRect(6, height / 2 - 3, width - 12, 6);
+ g2d.setColor(MARKER_COLOR);
+ int markerWidth = 4 * width / 32;
+ g2d.fillRect(width - 6 - markerWidth, height / 2 - 3, markerWidth, 6);
+ g2d.setColor(BORDER_COLOR);
+ g2d.drawRect(6, height / 2 - 3, width - 12, 6);
+ }
+
+ @Override
+ protected Size getDefaultWidth() {
+ return DEFAULT_HEIGHT;
+ }
+
+ @Override
+ protected Size getDefaultLength() {
+ return DEFAULT_WIDTH;
+ }
+
+ @Override
+ protected Shape getBodyShape() {
+ return new Rectangle2D.Double(0f, 0f, getLength().convertToPixels(),
+ getClosestOdd(getWidth().convertToPixels()));
+ }
+
+ @Override
+ protected void decorateComponentBody(Graphics2D g2d, boolean outlineMode) {
+ Color finalMarkerColor;
+ if (outlineMode) {
+ Theme theme = (Theme) ConfigurationManager.getInstance().readObject(
+ IPlugInPort.THEME_KEY, Constants.DEFAULT_THEME);
+ finalMarkerColor = theme.getOutlineColor();
+ } else {
+ finalMarkerColor = markerColor;
+ }
+ g2d.setColor(finalMarkerColor);
+ int width = (int) getLength().convertToPixels();
+ int markerWidth = (int) MARKER_WIDTH.convertToPixels();
+ g2d.fillRect(width - markerWidth, 0, markerWidth, getClosestOdd(getWidth()
+ .convertToPixels()));
+ }
+
+ @EditableProperty(name = "Marker")
+ public Color getMarkerColor() {
+ return markerColor;
+ }
+
+ public void setMarkerColor(Color markerColor) {
+ this.markerColor = markerColor;
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/semiconductors/FETPolarity.java b/diylc-library/src/org/diylc/components/semiconductors/FETPolarity.java
new file mode 100644
index 00000000..f5cc48bb
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/semiconductors/FETPolarity.java
@@ -0,0 +1,11 @@
+package org.diylc.components.semiconductors;
+
+public enum FETPolarity {
+
+ NEGATIVE, POSITIVE;
+
+ @Override
+ public String toString() {
+ return name().substring(0, 1) + name().substring(1).toLowerCase();
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/semiconductors/ICPointCount.java b/diylc-library/src/org/diylc/components/semiconductors/ICPointCount.java
new file mode 100644
index 00000000..acca6be6
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/semiconductors/ICPointCount.java
@@ -0,0 +1,22 @@
+package org.diylc.components.semiconductors;
+
+public enum ICPointCount {
+ _3("3", 3), _5("5", 5);
+
+ private String title;
+ private int value;
+
+ private ICPointCount(String title, int value) {
+ this.title = title;
+ this.value = value;
+ }
+
+ @Override
+ public String toString() {
+ return title;
+ }
+
+ public int getValue() {
+ return value;
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/semiconductors/ICSymbol.java b/diylc-library/src/org/diylc/components/semiconductors/ICSymbol.java
new file mode 100644
index 00000000..962ae81d
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/semiconductors/ICSymbol.java
@@ -0,0 +1,258 @@
+package org.diylc.components.semiconductors;
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Composite;
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.Polygon;
+import java.awt.Shape;
+import java.awt.geom.Area;
+import java.awt.geom.GeneralPath;
+import java.awt.geom.Rectangle2D;
+
+import org.diylc.appframework.miscutils.ConfigurationManager;
+import org.diylc.common.Display;
+import org.diylc.common.HorizontalAlignment;
+import org.diylc.common.IPlugInPort;
+import org.diylc.common.ObjectCache;
+import org.diylc.common.VerticalAlignment;
+import org.diylc.components.AbstractTransparentComponent;
+import org.diylc.core.ComponentState;
+import org.diylc.core.IDIYComponent;
+import org.diylc.core.IDrawingObserver;
+import org.diylc.core.Project;
+import org.diylc.core.Theme;
+import org.diylc.core.VisibilityPolicy;
+import org.diylc.core.annotations.ComponentDescriptor;
+import org.diylc.core.annotations.EditableProperty;
+import org.diylc.core.measures.Size;
+import org.diylc.core.measures.SizeUnit;
+import org.diylc.utils.Constants;
+
+@ComponentDescriptor(name = "IC Symbol", author = "Branislav Stojkovic", category = "Semiconductors", instanceNamePrefix = "IC", description = "IC symbol with 3 or 5 contacts", stretchable = false, zOrder = IDIYComponent.COMPONENT)
+public class ICSymbol extends AbstractTransparentComponent {
+
+ private static final long serialVersionUID = 1L;
+
+ public static Size PIN_SPACING = new Size(0.1d, SizeUnit.in);
+ public static Color BODY_COLOR = Color.white;
+ public static Color BORDER_COLOR = Color.black;
+
+ protected ICPointCount icPointCount = ICPointCount._5;
+ protected String value = "";
+ protected Point[] controlPoints = new Point[] { new Point(0, 0), new Point(0, 0),
+ new Point(0, 0), new Point(0, 0), new Point(0, 0) };
+ protected Color bodyColor = BODY_COLOR;
+ protected Color borderColor = BORDER_COLOR;
+ protected Display display = Display.NAME;
+ transient private Shape[] body;
+
+ public ICSymbol() {
+ super();
+ updateControlPoints();
+ }
+
+ @Override
+ public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode,
+ Project project, IDrawingObserver drawingObserver) {
+ if (checkPointsClipped(g2d.getClip())) {
+ return;
+ }
+ int pinSpacing = (int) PIN_SPACING.convertToPixels();
+ Composite oldComposite = g2d.getComposite();
+ if (alpha < MAX_ALPHA) {
+ g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha
+ / MAX_ALPHA));
+ }
+
+ Shape[] body = getBody();
+
+ g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : bodyColor);
+ g2d.fill(body[0]);
+ g2d.setComposite(oldComposite);
+ Color finalBorderColor;
+ if (outlineMode) {
+ Theme theme = (Theme) ConfigurationManager.getInstance().readObject(
+ IPlugInPort.THEME_KEY, Constants.DEFAULT_THEME);
+ finalBorderColor = theme.getOutlineColor();
+ } else {
+ finalBorderColor = borderColor;
+ }
+ g2d.setColor(finalBorderColor);
+ // Draw contacts
+ g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
+ g2d.draw(body[1]);
+ // Draw triangle
+ g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(2));
+ g2d.draw(body[0]);
+ // Draw label
+ g2d.setFont(LABEL_FONT);
+ Color finalLabelColor;
+ if (outlineMode) {
+ Theme theme = (Theme) ConfigurationManager.getInstance().readObject(
+ IPlugInPort.THEME_KEY, Constants.DEFAULT_THEME);
+ finalLabelColor = componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? LABEL_COLOR_SELECTED : theme
+ .getOutlineColor();
+ } else {
+ finalLabelColor = componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? LABEL_COLOR_SELECTED
+ : LABEL_COLOR;
+ }
+ g2d.setColor(finalLabelColor);
+ int x = (controlPoints[0].x + controlPoints[2].x) / 2;
+ drawCenteredText(g2d, display == Display.VALUE ? getValue() : getName(), x,
+ controlPoints[0].y + pinSpacing, HorizontalAlignment.CENTER,
+ VerticalAlignment.CENTER);
+ // Draw +/- markers
+ drawCenteredText(g2d, "-", controlPoints[0].x + pinSpacing, controlPoints[0].y,
+ HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
+ drawCenteredText(g2d, "+", controlPoints[1].x + pinSpacing, controlPoints[1].y,
+ HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
+ }
+
+ @Override
+ public void drawIcon(Graphics2D g2d, int width, int height) {
+ int margin = 3 * width / 32;
+ Area area = new Area(new Polygon(new int[] { margin, margin, width - margin }, new int[] {
+ margin, height - margin, height / 2 }, 3));
+ // area.subtract(new Area(new Rectangle2D.Double(0, 0, 2 * margin,
+ // height)));
+ area.intersect(new Area(new Rectangle2D.Double(2 * margin, 0, width, height)));
+ g2d.setColor(BODY_COLOR);
+ g2d.fill(area);
+ g2d.setColor(BORDER_COLOR);
+ g2d.setFont(LABEL_FONT.deriveFont(8f));
+ drawCenteredText(g2d, "-", 3 * margin, height / 3, HorizontalAlignment.CENTER,
+ VerticalAlignment.CENTER);
+ drawCenteredText(g2d, "+", 3 * margin + 1, height * 2 / 3, HorizontalAlignment.CENTER,
+ VerticalAlignment.CENTER);
+ // g2d.setStroke(new BasicStroke(1f, BasicStroke.CAP_ROUND,
+ // BasicStroke.JOIN_ROUND));
+ g2d.draw(area);
+ }
+
+ @Override
+ public Point getControlPoint(int index) {
+ return controlPoints[index];
+ }
+
+ @Override
+ public int getControlPointCount() {
+ return icPointCount.getValue();
+ }
+
+ private void updateControlPoints() {
+ int pinSpacing = (int) PIN_SPACING.convertToPixels();
+ // Update control points.
+ int x = controlPoints[0].x;
+ int y = controlPoints[0].y;
+
+ controlPoints[1].x = x;
+ controlPoints[1].y = y + pinSpacing * 2;
+
+ controlPoints[2].x = x + pinSpacing * 6;
+ controlPoints[2].y = y + pinSpacing;
+
+ controlPoints[3].x = x + pinSpacing * 3;
+ controlPoints[3].y = y - pinSpacing;
+
+ controlPoints[4].x = x + pinSpacing * 3;
+ controlPoints[4].y = y + pinSpacing * 3;
+ }
+
+ public Shape[] getBody() {
+ if (body == null) {
+ body = new Shape[2];
+ int pinSpacing = (int) PIN_SPACING.convertToPixels();
+ int x = controlPoints[0].x;
+ int y = controlPoints[0].y;
+ Shape triangle = new Polygon(new int[] { x + pinSpacing / 2, x + pinSpacing * 11 / 2,
+ x + pinSpacing / 2 }, new int[] { y - pinSpacing * 3 / 2, y + pinSpacing,
+ y + pinSpacing * 7 / 2 }, 3);
+ body[0] = triangle;
+
+ GeneralPath polyline = new GeneralPath();
+ polyline.moveTo(controlPoints[0].x, controlPoints[0].y);
+ polyline.lineTo(controlPoints[0].x + pinSpacing / 2, controlPoints[0].y);
+ polyline.moveTo(controlPoints[1].x, controlPoints[1].y);
+ polyline.lineTo(controlPoints[1].x + pinSpacing / 2, controlPoints[1].y);
+ polyline.moveTo(controlPoints[2].x, controlPoints[2].y);
+ polyline.lineTo(controlPoints[2].x - pinSpacing / 2, controlPoints[2].y);
+ if (icPointCount == ICPointCount._5) {
+ polyline.moveTo(controlPoints[3].x, controlPoints[3].y);
+ polyline.lineTo(controlPoints[3].x, controlPoints[3].y + pinSpacing * 3 / 4);
+ polyline.moveTo(controlPoints[4].x, controlPoints[4].y);
+ polyline.lineTo(controlPoints[4].x, controlPoints[4].y - pinSpacing * 3 / 4);
+ }
+ body[1] = polyline;
+ }
+ return body;
+ }
+
+ @Override
+ public VisibilityPolicy getControlPointVisibilityPolicy(int index) {
+ return VisibilityPolicy.WHEN_SELECTED;
+ }
+
+ @EditableProperty
+ @Override
+ public String getValue() {
+ return this.value;
+ }
+
+ @Override
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ @Override
+ public boolean isControlPointSticky(int index) {
+ return true;
+ }
+
+ @Override
+ public void setControlPoint(Point point, int index) {
+ controlPoints[index].setLocation(point);
+ body = null;
+ }
+
+ @EditableProperty(name = "Contacts")
+ public ICPointCount getIcPointCount() {
+ return icPointCount;
+ }
+
+ public void setIcPointCount(ICPointCount icPointCount) {
+ this.icPointCount = icPointCount;
+ updateControlPoints();
+ body = null;
+ }
+
+ @EditableProperty(name = "Body")
+ public Color getBodyColor() {
+ return bodyColor;
+ }
+
+ public void setBodyColor(Color bodyColor) {
+ this.bodyColor = bodyColor;
+ }
+
+ @EditableProperty(name = "Border")
+ public Color getBorderColor() {
+ return borderColor;
+ }
+
+ public void setBorderColor(Color borderColor) {
+ this.borderColor = borderColor;
+ }
+
+ @EditableProperty
+ public Display getDisplay() {
+ return display;
+ }
+
+ public void setDisplay(Display display) {
+ this.display = display;
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/semiconductors/JFETSymbol.java b/diylc-library/src/org/diylc/components/semiconductors/JFETSymbol.java
new file mode 100644
index 00000000..3f48f003
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/semiconductors/JFETSymbol.java
@@ -0,0 +1,88 @@
+package org.diylc.components.semiconductors;
+
+import java.awt.Graphics2D;
+import java.awt.Polygon;
+import java.awt.Shape;
+import java.awt.geom.GeneralPath;
+
+import org.diylc.common.ObjectCache;
+import org.diylc.core.IDIYComponent;
+import org.diylc.core.annotations.ComponentDescriptor;
+import org.diylc.core.annotations.EditableProperty;
+
+@ComponentDescriptor(name = "JFET Symbol", author = "Branislav Stojkovic", category = "Semiconductors", instanceNamePrefix = "Q", description = "JFET transistor schematic symbol", stretchable = false, zOrder = IDIYComponent.COMPONENT)
+public class JFETSymbol extends AbstractTransistorSymbol {
+
+ private static final long serialVersionUID = 1L;
+
+ protected FETPolarity polarity = FETPolarity.NEGATIVE;
+
+ public Shape[] getBody() {
+ if (body == null) {
+ body = new Shape[3];
+ int x = controlPoints[0].x;
+ int y = controlPoints[0].y;
+ int pinSpacing = (int) PIN_SPACING.convertToPixels();
+
+ GeneralPath polyline = new GeneralPath();
+
+ polyline.moveTo(x + pinSpacing, y - pinSpacing);
+ polyline.lineTo(x + pinSpacing, y + pinSpacing);
+
+ body[0] = polyline;
+
+ polyline = new GeneralPath();
+
+ polyline.moveTo(x, y);
+ polyline.lineTo(x + pinSpacing, y);
+ polyline.moveTo(x + pinSpacing, y - pinSpacing * 7 / 8);
+ polyline.lineTo(x + pinSpacing * 2, y - pinSpacing * 7 / 8);
+ polyline.lineTo(x + pinSpacing * 2, y - pinSpacing * 2);
+ polyline.moveTo(x + pinSpacing, y + pinSpacing * 7 / 8);
+ polyline.lineTo(x + pinSpacing * 2, y + pinSpacing * 7 / 8);
+ polyline.lineTo(x + pinSpacing * 2, y + pinSpacing * 2);
+ body[1] = polyline;
+
+ Polygon arrow;
+ if (polarity == FETPolarity.NEGATIVE) {
+ arrow = new Polygon(new int[] { x + pinSpacing * 2 / 6, x + pinSpacing * 2 / 6,
+ x + pinSpacing * 6 / 6 }, new int[] { y - pinSpacing / 5,
+ y + pinSpacing / 5, y }, 3);
+ } else {
+ arrow = new Polygon(new int[] { x + pinSpacing / 6, x + pinSpacing * 5 / 6,
+ x + pinSpacing * 5 / 6 }, new int[] { y, y + pinSpacing / 5,
+ y - pinSpacing / 5 }, 3);
+ }
+ body[2] = arrow;
+ }
+ return body;
+ }
+
+ @Override
+ public void drawIcon(Graphics2D g2d, int width, int height) {
+ g2d.setColor(COLOR);
+
+ g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(2));
+ g2d.drawLine(width / 2, height / 5, width / 2, height * 4 / 5);
+
+ g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
+ g2d.drawLine(width / 5, height / 2, width / 2, height / 2);
+
+ g2d.drawLine(width * 3 / 4, 1, width * 3 / 4, height / 4);
+ g2d.drawLine(width / 2, height / 4, width * 3 / 4, height / 4);
+
+ g2d.drawLine(width * 3 / 4, height - 1, width * 3 / 4, height * 3 / 4);
+ g2d.drawLine(width / 2, height * 3 / 4, width * 3 / 4, height * 3 / 4);
+ }
+
+ @EditableProperty(name = "Channel")
+ public FETPolarity getPolarity() {
+ return polarity;
+ }
+
+ public void setPolarity(FETPolarity polarity) {
+ this.polarity = polarity;
+ // Invalidate body
+ body = null;
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/semiconductors/LED.java b/diylc-library/src/org/diylc/components/semiconductors/LED.java
new file mode 100644
index 00000000..e58abe2a
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/semiconductors/LED.java
@@ -0,0 +1,106 @@
+package org.diylc.components.semiconductors;
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Shape;
+import java.awt.geom.Area;
+import java.awt.geom.Ellipse2D;
+import java.awt.geom.Rectangle2D;
+
+import org.diylc.common.ObjectCache;
+import org.diylc.components.AbstractLeadedComponent;
+import org.diylc.core.CreationMethod;
+import org.diylc.core.IDIYComponent;
+import org.diylc.core.annotations.ComponentDescriptor;
+import org.diylc.core.annotations.EditableProperty;
+import org.diylc.core.measures.Size;
+import org.diylc.core.measures.SizeUnit;
+
+@ComponentDescriptor(name = "LED", author = "Branislav Stojkovic", category = "Semiconductors", creationMethod = CreationMethod.POINT_BY_POINT, instanceNamePrefix = "D", description = "Light Emitting Diode", zOrder = IDIYComponent.COMPONENT)
+public class LED extends AbstractLeadedComponent {
+
+ private static final long serialVersionUID = 1L;
+
+ public static Size DEFAULT_SIZE = new Size(5d, SizeUnit.mm);
+ public static Color BODY_COLOR = Color.decode("#5DFC0A");
+ public static Color BORDER_COLOR = BODY_COLOR.darker();
+
+ private String value = "";
+
+ public LED() {
+ super();
+ this.bodyColor = BODY_COLOR;
+ this.borderColor = BORDER_COLOR;
+ }
+
+ @EditableProperty
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ public void drawIcon(Graphics2D g2d, int width, int height) {
+ g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
+ g2d.rotate(-Math.PI / 4, width / 2, height / 2);
+ g2d.setColor(LEAD_COLOR);
+ g2d.drawLine(0, height / 2, width, height / 2);
+
+ int margin = 4 * width / 32;
+ Area area = new Area(new Ellipse2D.Double(margin, margin, width - 2 * margin, width - 2
+ * margin));
+ area.intersect(new Area(new Rectangle2D.Double(margin, margin, width - 5 * margin / 2,
+ width - 2 * margin)));
+ g2d.setColor(BODY_COLOR);
+ g2d.fill(area);
+ g2d.setColor(BORDER_COLOR);
+ g2d.draw(area);
+ g2d
+ .drawOval(margin * 2 - 1, margin * 2 - 1, width - 4 * margin + 2, width - 4
+ * margin + 2);
+ }
+
+ @Override
+ protected Size getDefaultWidth() {
+ return DEFAULT_SIZE;
+ }
+
+ @Override
+ protected Size getDefaultLength() {
+ return DEFAULT_SIZE;
+ }
+
+ @Override
+ protected Shape getBodyShape() {
+ int size = getClosestOdd((int) (getLength().convertToPixels() * 1.2));
+ Area area = new Area(new Ellipse2D.Double(0, 0, size, size));
+ area.intersect(new Area(new Rectangle2D.Double(0, 0, getLength().convertToPixels() * 1.15,
+ size)));
+ return area;
+ }
+
+ @Override
+ protected void decorateComponentBody(Graphics2D g2d, boolean outlineMode) {
+ if (!outlineMode) {
+ int size = getClosestOdd((int) (getLength().convertToPixels() * 1.2));
+ int innerSize = getClosestOdd(getLength().convertToPixels());
+ int x = (size - innerSize) / 2;
+ Shape s = new Ellipse2D.Double(x, x, innerSize, innerSize);
+ g2d.setColor(getBorderColor());
+ g2d.draw(s);
+ }
+ }
+
+ @EditableProperty(name = "Size")
+ @Override
+ public Size getLength() {
+ return super.getLength();
+ }
+
+ @Override
+ public Size getWidth() {
+ return super.getWidth();
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/semiconductors/MOSFETSymbol.java b/diylc-library/src/org/diylc/components/semiconductors/MOSFETSymbol.java
new file mode 100644
index 00000000..562ea206
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/semiconductors/MOSFETSymbol.java
@@ -0,0 +1,91 @@
+package org.diylc.components.semiconductors;
+
+import java.awt.Graphics2D;
+import java.awt.Polygon;
+import java.awt.Shape;
+import java.awt.geom.GeneralPath;
+
+import org.diylc.common.ObjectCache;
+import org.diylc.core.IDIYComponent;
+import org.diylc.core.annotations.ComponentDescriptor;
+import org.diylc.core.annotations.EditableProperty;
+
+@ComponentDescriptor(name = "MOSFET Symbol", author = "Branislav Stojkovic", category = "Semiconductors", instanceNamePrefix = "Q", description = "MOSFET transistor schematic symbol", stretchable = false, zOrder = IDIYComponent.COMPONENT)
+public class MOSFETSymbol extends AbstractTransistorSymbol {
+
+ private static final long serialVersionUID = 1L;
+
+ protected FETPolarity polarity = FETPolarity.NEGATIVE;
+
+ public Shape[] getBody() {
+ if (body == null) {
+ body = new Shape[3];
+ int x = controlPoints[0].x;
+ int y = controlPoints[0].y;
+ int pinSpacing = (int) PIN_SPACING.convertToPixels();
+
+ GeneralPath polyline = new GeneralPath();
+
+ polyline.moveTo(x + pinSpacing / 2, y - pinSpacing + 1);
+ polyline.lineTo(x + pinSpacing / 2, y + pinSpacing - 1);
+ polyline.moveTo(x + pinSpacing, y - pinSpacing + 1);
+ polyline.lineTo(x + pinSpacing, y + pinSpacing - 1);
+ body[0] = polyline;
+
+ polyline = new GeneralPath();
+
+ polyline.moveTo(x + pinSpacing, y - pinSpacing);
+ polyline.lineTo(x + pinSpacing * 2, y - pinSpacing);
+ polyline.moveTo(x + pinSpacing, y + pinSpacing);
+ polyline.lineTo(x + pinSpacing * 2, y + pinSpacing);
+ polyline.moveTo(x, y);
+ polyline.lineTo(x + pinSpacing / 2, y);
+ polyline.moveTo(x + pinSpacing * 2, y - pinSpacing * 2);
+ polyline.lineTo(x + pinSpacing * 2, y - pinSpacing);
+ polyline.moveTo(x + pinSpacing * 2, y + pinSpacing * 2);
+ polyline.lineTo(x + pinSpacing * 2, y + pinSpacing);
+ body[1] = polyline;
+
+ Polygon arrow;
+ if (polarity == FETPolarity.NEGATIVE) {
+ arrow = new Polygon(new int[] { x + pinSpacing * 8 / 6, x + pinSpacing * 8 / 6,
+ x + pinSpacing * 12 / 6 }, new int[] { y + pinSpacing * 6 / 5,
+ y + pinSpacing * 4 / 5, y + pinSpacing }, 3);
+ } else {
+ arrow = new Polygon(new int[] { x + pinSpacing * 7 / 6, x + pinSpacing * 11 / 6,
+ x + pinSpacing * 11 / 6 }, new int[] { y - pinSpacing,
+ y - pinSpacing * 6 / 5, y - pinSpacing * 4 / 5 }, 3);
+ }
+ body[2] = arrow;
+ }
+ return body;
+ }
+
+ @Override
+ public void drawIcon(Graphics2D g2d, int width, int height) {
+ g2d.setColor(COLOR);
+ g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(2));
+ g2d.drawLine(width * 2 / 5, height / 4 + 1, width * 2 / 5, height * 3 / 4 - 1);
+ g2d.drawLine(width * 3 / 5, height / 4 + 1, width * 3 / 5, height * 3 / 4 - 1);
+
+ g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
+ g2d.drawLine(width / 5, height / 2, width * 2 / 5, height / 2);
+
+ g2d.drawLine(width * 4 / 5, 1, width * 4 / 5, height / 4);
+ g2d.drawLine(width * 4 / 5, height / 4, width * 3 / 5, height / 4);
+
+ g2d.drawLine(width * 4 / 5, height - 1, width * 4 / 5, height * 3 / 4);
+ g2d.drawLine(width * 4 / 5, height * 3 / 4, width * 3 / 5, height * 3 / 4);
+ }
+
+ @EditableProperty(name = "Channel")
+ public FETPolarity getPolarity() {
+ return polarity;
+ }
+
+ public void setPolarity(FETPolarity polarity) {
+ this.polarity = polarity;
+ // Invalidate body
+ body = null;
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/semiconductors/TransistorTO220.java b/diylc-library/src/org/diylc/components/semiconductors/TransistorTO220.java
new file mode 100644
index 00000000..c29f5cef
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/semiconductors/TransistorTO220.java
@@ -0,0 +1,322 @@
+package org.diylc.components.semiconductors;
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Composite;
+import java.awt.FontMetrics;
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.awt.geom.Rectangle2D;
+
+import org.diylc.appframework.miscutils.ConfigurationManager;
+import org.diylc.common.Display;
+import org.diylc.common.IPlugInPort;
+import org.diylc.common.ObjectCache;
+import org.diylc.common.Orientation;
+import org.diylc.components.AbstractTransparentComponent;
+import org.diylc.core.ComponentState;
+import org.diylc.core.IDIYComponent;
+import org.diylc.core.IDrawingObserver;
+import org.diylc.core.Project;
+import org.diylc.core.Theme;
+import org.diylc.core.VisibilityPolicy;
+import org.diylc.core.annotations.ComponentDescriptor;
+import org.diylc.core.annotations.EditableProperty;
+import org.diylc.core.measures.Size;
+import org.diylc.core.measures.SizeUnit;
+import org.diylc.utils.Constants;
+
+@ComponentDescriptor(name = "Transistor (TO-220 package)", author = "Branislav Stojkovic", category = "Semiconductors", instanceNamePrefix = "Q", description = "Transistors with metal tab for heat sink mounting", stretchable = false, zOrder = IDIYComponent.COMPONENT)
+public class TransistorTO220 extends AbstractTransparentComponent {
+
+ private static final long serialVersionUID = 1L;
+
+ public static Color BODY_COLOR = Color.gray;
+ public static Color BORDER_COLOR = Color.gray.darker();
+ public static Color PIN_COLOR = Color.decode("#00B2EE");
+ public static Color PIN_BORDER_COLOR = PIN_COLOR.darker();
+ public static Color TAB_COLOR = Color.decode("#C3E4ED");
+ public static Color TAB_BORDER_COLOR = TAB_COLOR.darker();
+ public static Color LABEL_COLOR = Color.white;
+ public static Size PIN_SIZE = new Size(0.03d, SizeUnit.in);
+ public static Size PIN_SPACING = new Size(0.1d, SizeUnit.in);
+ public static Size BODY_WIDTH = new Size(0.4d, SizeUnit.in);
+ public static Size BODY_HEIGHT = new Size(4.5d, SizeUnit.mm);
+ public static Size TAB_THICKNESS = new Size(1d, SizeUnit.mm);
+
+ private String value = "";
+ private Orientation orientation = Orientation.DEFAULT;
+ private Point[] controlPoints = new Point[] { new Point(0, 0), new Point(0, 0), new Point(0, 0) };
+ transient private Shape[] body;
+ private Color bodyColor = BODY_COLOR;
+ private Color borderColor = BORDER_COLOR;
+ private Color tabColor = TAB_COLOR;
+ private Color tabBorderColor = TAB_BORDER_COLOR;
+ private Display display = Display.NAME;
+
+ public TransistorTO220() {
+ super();
+ updateControlPoints();
+ }
+
+ @EditableProperty
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ @EditableProperty
+ public Orientation getOrientation() {
+ return orientation;
+ }
+
+ public void setOrientation(Orientation orientation) {
+ this.orientation = orientation;
+ updateControlPoints();
+ // Reset body shape;
+ body = null;
+ }
+
+ @Override
+ public int getControlPointCount() {
+ return controlPoints.length;
+ }
+
+ @Override
+ public Point getControlPoint(int index) {
+ return controlPoints[index];
+ }
+
+ @Override
+ public boolean isControlPointSticky(int index) {
+ return true;
+ }
+
+ @Override
+ public VisibilityPolicy getControlPointVisibilityPolicy(int index) {
+ return VisibilityPolicy.NEVER;
+ }
+
+ @Override
+ public void setControlPoint(Point point, int index) {
+ controlPoints[index].setLocation(point);
+ body = null;
+ }
+
+ private void updateControlPoints() {
+ int pinSpacing = (int) PIN_SPACING.convertToPixels();
+ // Update control points.
+ int x = controlPoints[0].x;
+ int y = controlPoints[0].y;
+ switch (orientation) {
+ case DEFAULT:
+ controlPoints[1].setLocation(x, y + pinSpacing);
+ controlPoints[2].setLocation(x, y + 2 * pinSpacing);
+ break;
+ case _90:
+ controlPoints[1].setLocation(x - pinSpacing, y);
+ controlPoints[2].setLocation(x - 2 * pinSpacing, y);
+ break;
+ case _180:
+ controlPoints[1].setLocation(x, y - pinSpacing);
+ controlPoints[2].setLocation(x, y - 2 * pinSpacing);
+ break;
+ case _270:
+ controlPoints[1].setLocation(x + pinSpacing, y);
+ controlPoints[2].setLocation(x + 2 * pinSpacing, y);
+ break;
+ default:
+ throw new RuntimeException("Unexpected orientation: " + orientation);
+ }
+ }
+
+ public Shape[] getBody() {
+ if (body == null) {
+ body = new Shape[2];
+ int x = controlPoints[0].x;
+ int y = controlPoints[0].y;
+ int pinSpacing = (int) PIN_SPACING.convertToPixels();
+ int bodyWidth = getClosestOdd(BODY_WIDTH.convertToPixels());
+ int bodyHeight = getClosestOdd(BODY_HEIGHT.convertToPixels());
+ int tabThickness = (int) TAB_THICKNESS.convertToPixels();
+
+ switch (orientation) {
+ case DEFAULT:
+ body[0] = new Rectangle2D.Double(x - bodyHeight / 2,
+ y + pinSpacing - bodyWidth / 2, bodyHeight, bodyWidth);
+ body[1] = new Rectangle2D.Double(x + bodyHeight / 2 - tabThickness, y + pinSpacing
+ - bodyWidth / 2, tabThickness, bodyWidth);
+ break;
+ case _90:
+ body[0] = new Rectangle2D.Double(x - pinSpacing - bodyWidth / 2,
+ y - bodyHeight / 2, bodyWidth, bodyHeight);
+ body[1] = new Rectangle2D.Double(x - pinSpacing - bodyWidth / 2, y + bodyHeight / 2
+ - tabThickness, bodyWidth, tabThickness);
+ break;
+ case _180:
+ body[0] = new Rectangle2D.Double(x - bodyHeight / 2,
+ y - pinSpacing - bodyWidth / 2, bodyHeight, bodyWidth);
+ body[1] = new Rectangle2D.Double(x - bodyHeight / 2,
+ y - pinSpacing - bodyWidth / 2, tabThickness, bodyWidth);
+ break;
+ case _270:
+ body[0] = new Rectangle2D.Double(x + pinSpacing - bodyWidth / 2,
+ y - bodyHeight / 2, bodyWidth, bodyHeight);
+ body[1] = new Rectangle2D.Double(x + pinSpacing - bodyWidth / 2,
+ y - bodyHeight / 2, bodyWidth, tabThickness);
+ break;
+ default:
+ throw new RuntimeException("Unexpected orientation: " + orientation);
+ }
+ }
+ return body;
+ }
+
+ @Override
+ public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode,
+ Project project, IDrawingObserver drawingObserver) {
+ if (checkPointsClipped(g2d.getClip())) {
+ return;
+ }
+ int pinSize = (int) PIN_SIZE.convertToPixels() / 2 * 2;
+ Shape mainArea = getBody()[0];
+ Shape tabArea = getBody()[1];
+ Composite oldComposite = g2d.getComposite();
+ if (alpha < MAX_ALPHA) {
+ g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha
+ / MAX_ALPHA));
+ }
+ g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : bodyColor);
+ g2d.fill(mainArea);
+ Color finalTabColor;
+ if (outlineMode) {
+ Theme theme = (Theme) ConfigurationManager.getInstance().readObject(
+ IPlugInPort.THEME_KEY, Constants.DEFAULT_THEME);
+ finalTabColor = theme.getOutlineColor();
+ } else {
+ finalTabColor = tabColor;
+ }
+ g2d.setColor(finalTabColor);
+ g2d.fill(tabArea);
+ g2d.setComposite(oldComposite);
+ if (!outlineMode) {
+ g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
+ g2d.setColor(tabBorderColor);
+ g2d.draw(tabArea);
+ }
+ Color finalBorderColor;
+ if (outlineMode) {
+ Theme theme = (Theme) ConfigurationManager.getInstance().readObject(
+ IPlugInPort.THEME_KEY, Constants.DEFAULT_THEME);
+ finalBorderColor = componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? SELECTION_COLOR : theme
+ .getOutlineColor();
+ } else {
+ finalBorderColor = componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? SELECTION_COLOR : borderColor;
+ }
+ g2d.setColor(finalBorderColor);
+ g2d.draw(mainArea);
+
+ // Draw pins.
+ if (!outlineMode) {
+ for (Point point : controlPoints) {
+ g2d.setColor(PIN_COLOR);
+ g2d.fillOval(point.x - pinSize / 2, point.y - pinSize / 2, pinSize, pinSize);
+ g2d.setColor(PIN_BORDER_COLOR);
+ g2d.drawOval(point.x - pinSize / 2, point.y - pinSize / 2, pinSize, pinSize);
+ }
+ }
+
+ // Draw label.
+ g2d.setFont(LABEL_FONT);
+ Color finalLabelColor;
+ if (outlineMode) {
+ Theme theme = (Theme) ConfigurationManager.getInstance().readObject(
+ IPlugInPort.THEME_KEY, Constants.DEFAULT_THEME);
+ finalLabelColor = componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? LABEL_COLOR_SELECTED : theme
+ .getOutlineColor();
+ } else {
+ finalLabelColor = componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? LABEL_COLOR_SELECTED
+ : LABEL_COLOR;
+ }
+ g2d.setColor(finalLabelColor);
+ String label = (getDisplay() == Display.NAME) ? getName() : getValue();
+ FontMetrics fontMetrics = g2d.getFontMetrics(g2d.getFont());
+ Rectangle2D rect = fontMetrics.getStringBounds(label, g2d);
+ int textHeight = (int) (rect.getHeight());
+ int textWidth = (int) (rect.getWidth());
+ // Center text horizontally and vertically
+ Rectangle bounds = mainArea.getBounds();
+ int x = bounds.x + (bounds.width - textWidth) / 2;
+ int y = bounds.y + (bounds.height - textHeight) / 2 + fontMetrics.getAscent();
+ g2d.drawString(label, x, y);
+ }
+
+ @Override
+ public void drawIcon(Graphics2D g2d, int width, int height) {
+ g2d.setColor(BODY_COLOR);
+ int margin = 2 * width / 32;
+ int thickness = 7 * width / 32;
+ g2d.fillRect(margin, (height - thickness) / 2, width - margin * 2, thickness);
+ g2d.setColor(TAB_COLOR);
+ g2d.fillRect(margin, (height + thickness) / 2 - margin - 1, width - margin * 2, margin);
+ g2d.setColor(BORDER_COLOR);
+ g2d.drawRect(margin, (height - thickness) / 2, width - margin * 2, thickness);
+ }
+
+ @EditableProperty(name = "Body")
+ public Color getBodyColor() {
+ return bodyColor;
+ }
+
+ public void setBodyColor(Color bodyColor) {
+ this.bodyColor = bodyColor;
+ }
+
+ @EditableProperty(name = "Border")
+ public Color getBorderColor() {
+ return borderColor;
+ }
+
+ public void setBorderColor(Color borderColor) {
+ this.borderColor = borderColor;
+ }
+
+ @EditableProperty(name = "Tab")
+ public Color getTabColor() {
+ return tabColor;
+ }
+
+ public void setTabColor(Color tabColor) {
+ this.tabColor = tabColor;
+ }
+
+ @EditableProperty(name = "Tab border")
+ public Color getTabBorderColor() {
+ return tabBorderColor;
+ }
+
+ public void setTabBorderColor(Color tabBorderColor) {
+ this.tabBorderColor = tabBorderColor;
+ }
+
+ @EditableProperty
+ public Display getDisplay() {
+ if (display == null) {
+ display = Display.NAME;
+ }
+ return display;
+ }
+
+ public void setDisplay(Display display) {
+ this.display = display;
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/semiconductors/TransistorTO92.java b/diylc-library/src/org/diylc/components/semiconductors/TransistorTO92.java
new file mode 100644
index 00000000..033bcc41
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/semiconductors/TransistorTO92.java
@@ -0,0 +1,282 @@
+package org.diylc.components.semiconductors;
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Composite;
+import java.awt.FontMetrics;
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.geom.Area;
+import java.awt.geom.Ellipse2D;
+import java.awt.geom.Rectangle2D;
+
+import org.diylc.appframework.miscutils.ConfigurationManager;
+import org.diylc.common.Display;
+import org.diylc.common.IPlugInPort;
+import org.diylc.common.ObjectCache;
+import org.diylc.common.Orientation;
+import org.diylc.components.AbstractTransparentComponent;
+import org.diylc.core.ComponentState;
+import org.diylc.core.IDIYComponent;
+import org.diylc.core.IDrawingObserver;
+import org.diylc.core.Project;
+import org.diylc.core.Theme;
+import org.diylc.core.VisibilityPolicy;
+import org.diylc.core.annotations.ComponentDescriptor;
+import org.diylc.core.annotations.EditableProperty;
+import org.diylc.core.measures.Size;
+import org.diylc.core.measures.SizeUnit;
+import org.diylc.utils.Constants;
+
+@ComponentDescriptor(name = "Transistor (TO-92 package)", author = "Branislav Stojkovic", category = "Semiconductors", instanceNamePrefix = "Q", description = "Transistor with small plastic or epoxy body", stretchable = false, zOrder = IDIYComponent.COMPONENT)
+public class TransistorTO92 extends AbstractTransparentComponent {
+
+ private static final long serialVersionUID = 1L;
+
+ public static Color BODY_COLOR = Color.gray;
+ public static Color BORDER_COLOR = Color.gray.darker();
+ public static Color PIN_COLOR = Color.decode("#00B2EE");
+ public static Color PIN_BORDER_COLOR = PIN_COLOR.darker();
+ public static Color LABEL_COLOR = Color.white;
+ public static Size PIN_SIZE = new Size(0.03d, SizeUnit.in);
+ public static Size PIN_SPACING = new Size(0.1d, SizeUnit.in);
+ public static Size BODY_DIAMETER = new Size(0.2d, SizeUnit.in);
+
+ private String value = "";
+ private Orientation orientation = Orientation.DEFAULT;
+ private Point[] controlPoints = new Point[] { new Point(0, 0), new Point(0, 0), new Point(0, 0) };
+ transient private Area body;
+ private Color bodyColor = BODY_COLOR;
+ private Color borderColor = BORDER_COLOR;
+ protected Display display = Display.NAME;
+
+ public TransistorTO92() {
+ super();
+ updateControlPoints();
+ }
+
+ @EditableProperty
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ @EditableProperty
+ public Orientation getOrientation() {
+ return orientation;
+ }
+
+ public void setOrientation(Orientation orientation) {
+ this.orientation = orientation;
+ updateControlPoints();
+ // Reset body shape;
+ body = null;
+ }
+
+ @Override
+ public int getControlPointCount() {
+ return controlPoints.length;
+ }
+
+ @Override
+ public Point getControlPoint(int index) {
+ return controlPoints[index];
+ }
+
+ @Override
+ public boolean isControlPointSticky(int index) {
+ return true;
+ }
+
+ @Override
+ public VisibilityPolicy getControlPointVisibilityPolicy(int index) {
+ return VisibilityPolicy.NEVER;
+ }
+
+ @Override
+ public void setControlPoint(Point point, int index) {
+ controlPoints[index].setLocation(point);
+ body = null;
+ }
+
+ private void updateControlPoints() {
+ int pinSpacing = (int) PIN_SPACING.convertToPixels();
+ // Update control points.
+ int x = controlPoints[0].x;
+ int y = controlPoints[0].y;
+ switch (orientation) {
+ case DEFAULT:
+ controlPoints[1].setLocation(x, y + pinSpacing);
+ controlPoints[2].setLocation(x, y + 2 * pinSpacing);
+ break;
+ case _90:
+ controlPoints[1].setLocation(x - pinSpacing, y);
+ controlPoints[2].setLocation(x - 2 * pinSpacing, y);
+ break;
+ case _180:
+ controlPoints[1].setLocation(x, y - pinSpacing);
+ controlPoints[2].setLocation(x, y - 2 * pinSpacing);
+ break;
+ case _270:
+ controlPoints[1].setLocation(x + pinSpacing, y);
+ controlPoints[2].setLocation(x + 2 * pinSpacing, y);
+ break;
+ default:
+ throw new RuntimeException("Unexpected orientation: " + orientation);
+ }
+ }
+
+ public Area getBody() {
+ if (body == null) {
+ int x = controlPoints[0].x;
+ int y = controlPoints[0].y;
+ int pinSpacing = (int) PIN_SPACING.convertToPixels();
+ int bodyDiameter = getClosestOdd(BODY_DIAMETER.convertToPixels());
+
+ switch (orientation) {
+ case DEFAULT:
+ body = new Area(new Ellipse2D.Double(x - bodyDiameter / 2, y + pinSpacing
+ - bodyDiameter / 2, bodyDiameter, bodyDiameter));
+ body.subtract(new Area(new Rectangle2D.Double(x - bodyDiameter, y + pinSpacing
+ - bodyDiameter / 2, 3 * bodyDiameter / 4, bodyDiameter)));
+ break;
+ case _90:
+ body = new Area(new Ellipse2D.Double(x - pinSpacing - bodyDiameter / 2, y
+ - bodyDiameter / 2, bodyDiameter, bodyDiameter));
+ body.subtract(new Area(new Rectangle2D.Double(x - pinSpacing - bodyDiameter / 2, y
+ - bodyDiameter, bodyDiameter, 3 * bodyDiameter / 4)));
+ break;
+ case _180:
+ body = new Area(new Ellipse2D.Double(x - bodyDiameter / 2, y - pinSpacing
+ - bodyDiameter / 2, bodyDiameter, bodyDiameter));
+ body.subtract(new Area(new Rectangle2D.Double(x + bodyDiameter / 4, y - pinSpacing
+ - bodyDiameter / 2, 3 * bodyDiameter / 4, bodyDiameter)));
+ break;
+ case _270:
+ body = new Area(new Ellipse2D.Double(x + pinSpacing - bodyDiameter / 2, y
+ - bodyDiameter / 2, bodyDiameter, bodyDiameter));
+ body.subtract(new Area(new Rectangle2D.Double(x + pinSpacing - bodyDiameter / 2, y
+ + bodyDiameter / 4, bodyDiameter, 3 * bodyDiameter / 4)));
+ break;
+ default:
+ throw new RuntimeException("Unexpected orientation: " + orientation);
+ }
+ }
+ return body;
+ }
+
+ @Override
+ public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode,
+ Project project, IDrawingObserver drawingObserver) {
+ if (checkPointsClipped(g2d.getClip())) {
+ return;
+ }
+ int pinSize = (int) PIN_SIZE.convertToPixels() / 2 * 2;
+ Area mainArea = getBody();
+ Composite oldComposite = g2d.getComposite();
+ if (alpha < MAX_ALPHA) {
+ g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha
+ / MAX_ALPHA));
+ }
+ g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : bodyColor);
+ g2d.fill(mainArea);
+ g2d.setComposite(oldComposite);
+ Color finalBorderColor;
+ if (outlineMode) {
+ Theme theme = (Theme) ConfigurationManager.getInstance().readObject(
+ IPlugInPort.THEME_KEY, Constants.DEFAULT_THEME);
+ finalBorderColor = componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? SELECTION_COLOR : theme
+ .getOutlineColor();
+ } else {
+ finalBorderColor = componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? SELECTION_COLOR : borderColor;
+ }
+ g2d.setColor(finalBorderColor);
+ g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
+ g2d.draw(mainArea);
+
+ if (!outlineMode) {
+ for (Point point : controlPoints) {
+ g2d.setColor(PIN_COLOR);
+ g2d.fillOval(point.x - pinSize / 2, point.y - pinSize / 2, pinSize, pinSize);
+ g2d.setColor(PIN_BORDER_COLOR);
+ g2d.drawOval(point.x - pinSize / 2, point.y - pinSize / 2, pinSize, pinSize);
+ }
+ }
+
+ // Draw label.
+ g2d.setFont(LABEL_FONT);
+ Color finalLabelColor;
+ if (outlineMode) {
+ Theme theme = (Theme) ConfigurationManager.getInstance().readObject(
+ IPlugInPort.THEME_KEY, Constants.DEFAULT_THEME);
+ finalLabelColor = componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? LABEL_COLOR_SELECTED : theme
+ .getOutlineColor();
+ } else {
+ finalLabelColor = componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? LABEL_COLOR_SELECTED
+ : LABEL_COLOR;
+ }
+ g2d.setColor(finalLabelColor);
+ String label = (getDisplay() == Display.NAME) ? getName() : getValue();
+ FontMetrics fontMetrics = g2d.getFontMetrics(g2d.getFont());
+ Rectangle2D rect = fontMetrics.getStringBounds(label, g2d);
+ int textHeight = (int) (rect.getHeight());
+ int textWidth = (int) (rect.getWidth());
+ // Center text horizontally and vertically
+ Rectangle bounds = mainArea.getBounds();
+ int x = bounds.x + (bounds.width - textWidth) / 2;
+ int y = bounds.y + (bounds.height - textHeight) / 2 + fontMetrics.getAscent();
+ g2d.drawString(label, x, y);
+ }
+
+ @Override
+ public void drawIcon(Graphics2D g2d, int width, int height) {
+ int margin = 3 * width / 32;
+ Area area = new Area(new Ellipse2D.Double(margin / 2, margin, width - 2 * margin, width - 2
+ * margin));
+ // area.subtract(new Area(new Rectangle2D.Double(0, 0, 2 * margin,
+ // height)));
+ area.intersect(new Area(new Rectangle2D.Double(2 * margin, 0, width, height)));
+ g2d.setColor(BODY_COLOR);
+ g2d.fill(area);
+ g2d.setColor(BORDER_COLOR);
+ g2d.draw(area);
+ }
+
+ @EditableProperty(name = "Body")
+ public Color getBodyColor() {
+ return bodyColor;
+ }
+
+ public void setBodyColor(Color bodyColor) {
+ this.bodyColor = bodyColor;
+ }
+
+ @EditableProperty(name = "Border")
+ public Color getBorderColor() {
+ return borderColor;
+ }
+
+ public void setBorderColor(Color borderColor) {
+ this.borderColor = borderColor;
+ }
+
+ @EditableProperty
+ public Display getDisplay() {
+ if (display == null) {
+ display = Display.NAME;
+ }
+ return display;
+ }
+
+ public void setDisplay(Display display) {
+ this.display = display;
+ }
+}
diff --git a/diylc-library/src/org/diylc/components/tube/TubeSocket.java b/diylc-library/src/org/diylc/components/tube/TubeSocket.java
new file mode 100644
index 00000000..52c5ca47
--- /dev/null
+++ b/diylc-library/src/org/diylc/components/tube/TubeSocket.java
@@ -0,0 +1,327 @@
+package org.diylc.components.tube;
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Composite;
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.Shape;
+import java.awt.geom.Area;
+import java.awt.geom.Ellipse2D;
+
+import org.diylc.appframework.miscutils.ConfigurationManager;
+import org.diylc.common.IPlugInPort;
+import org.diylc.common.ObjectCache;
+import org.diylc.common.Orientation;
+import org.diylc.components.AbstractTransparentComponent;
+import org.diylc.core.ComponentState;
+import org.diylc.core.IDIYComponent;
+import org.diylc.core.IDrawingObserver;
+import org.diylc.core.Project;
+import org.diylc.core.Theme;
+import org.diylc.core.VisibilityPolicy;
+import org.diylc.core.annotations.ComponentDescriptor;
+import org.diylc.core.annotations.EditableProperty;
+import org.diylc.core.measures.Size;
+import org.diylc.core.measures.SizeUnit;
+import org.diylc.utils.Constants;
+
+@ComponentDescriptor(name = "Tube Socket", author = "Branislav Stojkovic", category = "Tubes", instanceNamePrefix = "V", description = "Various types of tube/valve sockets", stretchable = false, zOrder = IDIYComponent.COMPONENT)
+public class TubeSocket extends AbstractTransparentComponent {
+
+ private static final long serialVersionUID = 1L;
+
+ private static Color BODY_COLOR = Color.decode("#FFFFE0");
+ private static Color BORDER_COLOR = Color.decode("#8E8E38");
+ public static Color PIN_COLOR = Color.decode("#00B2EE");
+ public static Color PIN_BORDER_COLOR = PIN_COLOR.darker();
+ public static Size PIN_SIZE = new Size(1d, SizeUnit.mm);
+ public static Size HOLE_SIZE = new Size(5d, SizeUnit.mm);
+ public static Size TICK_SIZE = new Size(2d, SizeUnit.mm);
+
+ private Base base = Base.B9A;
+ private String type = "";
+ private Orientation orientation = Orientation.DEFAULT;
+ // private Mount mount = Mount.CHASSIS;
+
+ private Point[] controlPoints = new Point[] { new Point(0, 0) };
+
+ transient private Shape body;
+
+ public TubeSocket() {
+ super();
+ updateControlPoints();
+ }
+
+ @EditableProperty
+ public Base getBase() {
+ return base;
+ }
+
+ public void setBase(Base base) {
+ this.base = base;
+ updateControlPoints();
+ // Reset body shape
+ body = null;
+ }
+
+ @EditableProperty
+ public Orientation getOrientation() {
+ return orientation;
+ }
+
+ public void setOrientation(Orientation orientation) {
+ this.orientation = orientation;
+ updateControlPoints();
+ // Reset body shape
+ body = null;
+ }
+
+ // @EditableProperty
+ // public Mount getMount() {
+ // return mount;
+ // }
+ //
+ // public void setMount(Mount mount) {
+ // this.mount = mount;
+ // }
+
+ private void updateControlPoints() {
+ Point firstPoint = controlPoints[0];
+ int pinCount;
+ int pinCircleDiameter;
+ boolean hasEmptySpace;
+ switch (base) {
+ case B7G:
+ pinCount = 7;
+ pinCircleDiameter = getClosestOdd(new Size(12d, SizeUnit.mm).convertToPixels());
+ hasEmptySpace = true;
+ break;
+ case OCTAL:
+ pinCount = 8;
+ pinCircleDiameter = getClosestOdd(new Size(17.5d, SizeUnit.mm).convertToPixels());
+ hasEmptySpace = false;
+ break;
+ case B9A:
+ pinCount = 9;
+ pinCircleDiameter = getClosestOdd(new Size(21d, SizeUnit.mm).convertToPixels());
+ hasEmptySpace = true;
+ break;
+ default:
+ throw new RuntimeException("Unexpected base: " + base);
+ }
+ double angleIncrement = Math.PI * 2 / (hasEmptySpace ? (pinCount + 1) : pinCount);
+ double initialAngleOffset = hasEmptySpace ? angleIncrement : (angleIncrement / 2);
+ double initialAngle;
+ switch (orientation) {
+ case DEFAULT:
+ initialAngle = Math.PI / 2 + initialAngleOffset;
+ break;
+ case _90:
+ initialAngle = Math.PI + initialAngleOffset;
+ break;
+ case _180:
+ initialAngle = 3 * Math.PI / 2 + initialAngleOffset;
+ break;
+ case _270:
+ initialAngle = initialAngleOffset;
+ break;
+ default:
+ throw new RuntimeException("Unexpected orientation: " + orientation);
+ }
+ controlPoints = new Point[pinCount + 1];
+ double angle = initialAngle;
+ controlPoints[0] = firstPoint;
+ for (int i = 0; i < pinCount; i++) {
+ controlPoints[i + 1] = new Point((int) (firstPoint.getX() + Math.cos(angle)
+ * pinCircleDiameter / 2), (int) (firstPoint.getY() + Math.sin(angle)
+ * pinCircleDiameter / 2));
+ angle += angleIncrement;
+ }
+ }
+
+ public Shape getBody() {
+ if (body == null) {
+ int bodyDiameter;
+ switch (base) {
+ case B7G:
+ bodyDiameter = getClosestOdd(new Size(17d, SizeUnit.mm).convertToPixels());
+ break;
+ case B9A:
+ bodyDiameter = getClosestOdd(new Size(19d, SizeUnit.mm).convertToPixels());
+ break;
+ case OCTAL:
+ bodyDiameter = getClosestOdd(new Size(24.5d, SizeUnit.mm).convertToPixels());
+ break;
+ default:
+ throw new RuntimeException("Unexpected base: " + base);
+ }
+ body = new Ellipse2D.Double(controlPoints[0].x - bodyDiameter / 2, controlPoints[0].y
+ - bodyDiameter / 2, bodyDiameter, bodyDiameter);
+ Area bodyArea = new Area(body);
+ int holeSize = getClosestOdd(HOLE_SIZE.convertToPixels());
+ bodyArea.subtract(new Area(new Ellipse2D.Double(controlPoints[0].x - holeSize / 2,
+ controlPoints[0].y - holeSize / 2, holeSize, holeSize)));
+ if (base == Base.OCTAL) {
+ int tickSize = getClosestOdd(TICK_SIZE.convertToPixels());
+ double angle = 0;
+ switch (orientation) {
+ case DEFAULT:
+ angle = Math.PI / 2;
+ break;
+ case _90:
+ angle = Math.PI;
+ break;
+ case _180:
+ angle = 3 * Math.PI / 2;
+ break;
+ case _270:
+ angle = 0;
+ break;
+ }
+ int centerX = (int) (controlPoints[0].x + Math.cos(angle) * holeSize / 2);
+ int centerY = (int) (controlPoints[0].y + Math.sin(angle) * holeSize / 2);
+ bodyArea.subtract(new Area(new Ellipse2D.Double(centerX - tickSize / 2, centerY
+ - tickSize / 2, tickSize, tickSize)));
+ }
+ body = bodyArea;
+ }
+ return body;
+ }
+
+ @Override
+ public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode,
+ Project project, IDrawingObserver drawingObserver) {
+ // g2d.setColor(Color.black);
+ g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
+ // for (int i = 0; i < controlPoints.length; i++) {
+ // g2d.drawString(Integer.toString(i), controlPoints[i].x,
+ // controlPoints[i].y);
+ // }
+ // Draw body
+ Shape body = getBody();
+ Composite oldComposite = g2d.getComposite();
+ if (alpha < MAX_ALPHA) {
+ g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha
+ / MAX_ALPHA));
+ }
+ if (componentState != ComponentState.DRAGGING) {
+ g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : BODY_COLOR);
+ g2d.fill(body);
+ }
+ g2d.setComposite(oldComposite);
+ Color finalBorderColor;
+ if (outlineMode) {
+ Theme theme = (Theme) ConfigurationManager.getInstance().readObject(
+ IPlugInPort.THEME_KEY, Constants.DEFAULT_THEME);
+ finalBorderColor = componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? SELECTION_COLOR : theme
+ .getOutlineColor();
+ } else {
+ finalBorderColor = componentState == ComponentState.SELECTED
+ || componentState == ComponentState.DRAGGING ? SELECTION_COLOR : BORDER_COLOR;
+ }
+ g2d.setColor(finalBorderColor);
+ g2d.draw(body);
+ // Draw pins
+ if (!outlineMode) {
+ int pinSize = getClosestOdd(PIN_SIZE.convertToPixels());
+ for (int i = 1; i < controlPoints.length; i++) {
+ g2d.setColor(PIN_COLOR);
+ g2d.fillOval(controlPoints[i].x - pinSize / 2, controlPoints[i].y - pinSize / 2,
+ pinSize, pinSize);
+ g2d.setColor(PIN_BORDER_COLOR);
+ g2d.drawOval(controlPoints[i].x - pinSize / 2, controlPoints[i].y - pinSize / 2,
+ pinSize, pinSize);
+ }
+ }
+ }
+
+ @Override
+ public void drawIcon(Graphics2D g2d, int width, int height) {
+ Area area = new Area(new Ellipse2D.Double(1, 1, width - 2, width - 2));
+ int center = width / 2;
+ area.subtract(new Area(new Ellipse2D.Double(center - 2, center - 2, 5, 5)));
+ g2d.setColor(BODY_COLOR);
+ g2d.fill(area);
+ g2d.setColor(BORDER_COLOR);
+ g2d.draw(area);
+
+ int radius = width / 2 - 6;
+ for (int i = 0; i < 8; i++) {
+ int x = (int) (center + Math.cos(i * Math.PI / 4) * radius);
+ int y = (int) (center + Math.sin(i * Math.PI / 4) * radius);
+ g2d.setColor(PIN_COLOR);
+ g2d.fillOval(x - 1, y - 1, 3, 3);
+ g2d.setColor(PIN_BORDER_COLOR);
+ g2d.drawOval(x - 1, y - 1, 3, 3);
+ }
+ }
+
+ @Override
+ public Point getControlPoint(int index) {
+ return controlPoints[index];
+ }
+
+ @Override
+ public int getControlPointCount() {
+ return controlPoints.length;
+ }
+
+ @Override
+ public VisibilityPolicy getControlPointVisibilityPolicy(int index) {
+ return VisibilityPolicy.NEVER;
+ }
+
+ @Override
+ @EditableProperty(name = "Type")
+ public String getValue() {
+ return type;
+ }
+
+ @Override
+ public void setValue(String value) {
+ this.type = value;
+ }
+
+ @Override
+ public boolean isControlPointSticky(int index) {
+ return index > 0;
+ }
+
+ @Override
+ public void setControlPoint(Point point, int index) {
+ controlPoints[index].setLocation(point);
+ body = null;
+ }
+
+ static enum Base {
+ B9A("Noval B9A"), OCTAL("Octal"), B7G("Small-button B7G");
+
+ String name;
+
+ private Base(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+ }
+
+ static enum Mount {
+ CHASSIS("Chassis"), PCB("PCB");
+
+ String name;
+
+ private Mount(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+ }
+}