Add rotary switches

This commit is contained in:
Bane Stojkovic
2022-10-22 18:44:49 +02:00
parent 6100309b3b
commit c30c8da267
34 changed files with 883 additions and 33 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 155 KiB

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 155 KiB

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 155 KiB

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 206 KiB

After

Width:  |  Height:  |  Size: 204 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 210 KiB

After

Width:  |  Height:  |  Size: 208 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 277 KiB

After

Width:  |  Height:  |  Size: 276 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 118 KiB

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 MiB

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 244 KiB

After

Width:  |  Height:  |  Size: 245 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 261 KiB

After

Width:  |  Height:  |  Size: 264 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 131 KiB

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 200 KiB

After

Width:  |  Height:  |  Size: 202 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 475 KiB

After

Width:  |  Height:  |  Size: 478 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 KiB

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 852 KiB

After

Width:  |  Height:  |  Size: 857 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 379 KiB

After

Width:  |  Height:  |  Size: 371 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 421 KiB

After

Width:  |  Height:  |  Size: 420 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 446 KiB

After

Width:  |  Height:  |  Size: 445 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 KiB

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 158 KiB

After

Width:  |  Height:  |  Size: 160 KiB

@@ -15,6 +15,10 @@ public class Angle {
public int getValue() {
return value;
}
public double getValueRad() {
return Math.toRadians(value);
}
@Override
public int hashCode() {
@@ -205,6 +205,9 @@ public class StringUtils {
public static void drawCenteredText(Graphics2D g2d, String text, double x, double y, HorizontalAlignment horizontalAlignment,
VerticalAlignment verticalAlignment) {
if (text == null)
return;
String[] parts = text.split("\\<br\\>");
if (parts.length > 1) {
FontMetrics fontMetrics = g2d.getFontMetrics();
@@ -0,0 +1,693 @@
/*
*
* DIY Layout Creator (DIYLC). Copyright (c) 2009-2018 held jointly by the individual authors.
*
* This file is part of DIYLC.
*
* DIYLC is free software: you can redistribute it and/or modify it under the terms of the GNU
* General Public License as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* DIYLC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with DIYLC. If not, see
* <http://www.gnu.org/licenses/>.
*
*/
package org.diylc.components.electromechanical;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import java.util.ArrayList;
import java.util.List;
import org.diylc.appframework.miscutils.ConfigurationManager;
import org.diylc.awt.StringUtils;
import org.diylc.awt.TwoCircleTangent;
import org.diylc.common.HorizontalAlignment;
import org.diylc.common.IPlugInPort;
import org.diylc.common.ObjectCache;
import org.diylc.common.VerticalAlignment;
import org.diylc.components.AbstractAngledComponent;
import org.diylc.components.transform.AngledComponentTransformer;
import org.diylc.core.Angle;
import org.diylc.core.ComponentState;
import org.diylc.core.IDIYComponent;
import org.diylc.core.IDrawingObserver;
import org.diylc.core.ISwitch;
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.annotations.KeywordPolicy;
import org.diylc.core.measures.Size;
import org.diylc.core.measures.SizeUnit;
import org.diylc.presenter.AreaUtils;
import org.diylc.utils.Constants;
@ComponentDescriptor(name = "Rotary Switch (Open)", author = "Branislav Stojkovic",
category = "Electro-Mechanical", instanceNamePrefix = "SW",
description = "Open rotary switch in several different switching configurations",
zOrder = IDIYComponent.COMPONENT, keywordPolicy = KeywordPolicy.SHOW_VALUE, enableCache = true,
transformer = AngledComponentTransformer.class)
public class RotarySwitchOpen extends AbstractAngledComponent<RotarySwitchOpenType>
implements ISwitch {
private static final int ANGLE_OFFSET = -15;
private static final int INNER_COMMON_ANGLE_OFFSET = -90;
public static Size LARGE_DIAMETER = new Size(25d, SizeUnit.mm);
public static Size SMALL_DIAMETER = new Size(11d, SizeUnit.mm);
public static Size SMALL_DISTANCE = new Size(20d, SizeUnit.mm);
public static Size HOLE_DISTANCE = new Size(26d, SizeUnit.mm);
public static Size BODY_HOLE_SIZE = new Size(3d, SizeUnit.mm);
public static Size TERMINAL_HOLE_SIZE = new Size(1d, SizeUnit.mm);
private static final Size INNER_PIN_SPACING = new Size(20d, SizeUnit.mm);
private static final Size OUTER_PIN_SPACING = new Size(30d, SizeUnit.mm);
private static final Size INNER_COMMON_PIN_SPACING = new Size(11d, SizeUnit.mm);
private static final Size OUTER_COMMON_PIN_SPACING = new Size(32d, SizeUnit.mm);
private static final long serialVersionUID = 1L;
public static Size PIN_DIAMETER = new Size(1d, SizeUnit.mm);
private static Size PIN_WIDTH = new Size(0.08d, SizeUnit.in);
private static Size PIN_THICKNESS = new Size(0.02d, SizeUnit.in);
private RotarySwitchOpenType configuration = RotarySwitchOpenType._2P6T;
private Color color = PHENOLIC_DARK_COLOR;
private SwitchTiming timing;
private Color labelColor = Color.white;
private Color pinColor = METAL_COLOR;
private boolean showMarkers = true;
private Angle angleOffset = Angle.of(ANGLE_OFFSET);
private Point2D[] controlPoints = new Point2D[] {new Point2D.Double(0, 0)};
private double[] pointAngles;
public RotarySwitchOpen() {
super();
updateControlPoints();
}
@EditableProperty(name = "Type")
public RotarySwitchOpenType getValue() {
return configuration;
}
public void setValue(RotarySwitchOpenType configuration) {
this.configuration = configuration;
updateControlPoints();
// Reset body shape
body = null;
}
@EditableProperty
public Color getColor() {
return color;
}
public void setColor(Color color) {
this.color = color;
}
@Override
protected void updateControlPoints() {
Point2D firstPoint = controlPoints[0];
int innerPinCount = 2 * configuration.getPositionCount();
int commonPinCount = 2;
int innerPinSpacing = getClosestOdd(INNER_PIN_SPACING.convertToPixels());
int innerCommonPinSpacing = getClosestOdd(INNER_COMMON_PIN_SPACING.convertToPixels());
int outerPinSpacing = getClosestOdd(OUTER_PIN_SPACING.convertToPixels());
int outerCommonPinSpacing = getClosestOdd(OUTER_COMMON_PIN_SPACING.convertToPixels());
double angleIncrement = -Math.PI / 6;
double commonAngleIncrement = -Math.PI;
double angleOffset = this.angleOffset.getValueRad();
double innerCommonAngleOffset = Math.toRadians(INNER_COMMON_ANGLE_OFFSET);
double outerCommonAngleOffset = Math.toRadians(INNER_COMMON_ANGLE_OFFSET);
double angleRad = Math.toRadians(getAngle().getValue());
controlPoints =
new Point2D[1 + (innerPinCount + commonPinCount) * configuration.getPoleCount() / 2];
pointAngles = new double[controlPoints.length];
controlPoints[0] = firstPoint;
double theta = angleOffset + angleRad;
for (int i = 0; i < configuration.getPositionCount(); i++) {
controlPoints[i + 1] =
new Point2D.Double(firstPoint.getX() + Math.cos(theta) * innerPinSpacing / 2,
firstPoint.getY() + Math.sin(theta) * innerPinSpacing / 2);
pointAngles[i + 1] = theta;
theta += angleIncrement;
}
theta = angleOffset + angleRad - Math.PI;
for (int i = 0; i < configuration.getPositionCount(); i++) {
controlPoints[i + configuration.getPositionCount() + 1] =
new Point2D.Double(firstPoint.getX() + Math.cos(theta) * innerPinSpacing / 2,
firstPoint.getY() + Math.sin(theta) * innerPinSpacing / 2);
pointAngles[i + configuration.getPositionCount() + 1] = theta;
theta += angleIncrement;
}
theta = innerCommonAngleOffset + angleRad;
for (int i = 0; i < commonPinCount; i++) {
controlPoints[i + innerPinCount + 1] =
new Point2D.Double(firstPoint.getX() + Math.cos(theta) * innerCommonPinSpacing / 2,
firstPoint.getY() + Math.sin(theta) * innerCommonPinSpacing / 2);
pointAngles[i + innerPinCount + 1] = theta;
theta += commonAngleIncrement;
}
if (configuration.getNeedsSecondLevel()) {
int secondLevelStart = commonPinCount + innerPinCount + 1;
theta = angleOffset + angleRad;
if (configuration.getPositionCount() == 5) {
theta += angleIncrement;
}
for (int i = 0; i < configuration.getPositionCount(); i++) {
controlPoints[secondLevelStart + i] =
new Point2D.Double(firstPoint.getX() + Math.cos(theta) * outerPinSpacing / 2,
firstPoint.getY() + Math.sin(theta) * outerPinSpacing / 2);
pointAngles[secondLevelStart + i] = theta;
theta += angleIncrement;
}
theta = angleOffset + angleRad - Math.PI;
for (int i = 0; i < configuration.getPositionCount(); i++) {
controlPoints[secondLevelStart + i + configuration.getPositionCount()] =
new Point2D.Double(firstPoint.getX() + Math.cos(theta) * outerPinSpacing / 2,
firstPoint.getY() + Math.sin(theta) * outerPinSpacing / 2);
pointAngles[secondLevelStart + i + configuration.getPositionCount()] = theta;
theta += angleIncrement;
}
theta = outerCommonAngleOffset + angleRad;
for (int i = 0; i < commonPinCount; i++) {
controlPoints[secondLevelStart + i + innerPinCount] =
new Point2D.Double(firstPoint.getX() + Math.cos(theta) * outerCommonPinSpacing / 2,
firstPoint.getY() + Math.sin(theta) * outerCommonPinSpacing / 2);
pointAngles[secondLevelStart + i + innerPinCount] = theta;
theta += commonAngleIncrement;
}
}
}
public Shape[] getBody() {
if (body == null) {
List<Area> bodyList = new ArrayList<Area>();
int largeDiameter = getClosestOdd(LARGE_DIAMETER.convertToPixels());
int smallDiameter = getClosestOdd(SMALL_DIAMETER.convertToPixels());
int smallDistance = getClosestOdd(SMALL_DISTANCE.convertToPixels());
int holeDistance = getClosestOdd(HOLE_DISTANCE.convertToPixels());
int holeSize = getClosestOdd(BODY_HOLE_SIZE.convertToPixels());
double x = controlPoints[0].getX();
double y = controlPoints[0].getY();
TwoCircleTangent left = new TwoCircleTangent(new Point2D.Double(x, y),
new Point2D.Double(x - smallDistance / 2, y), largeDiameter / 2, smallDiameter / 2);
TwoCircleTangent right = new TwoCircleTangent(new Point2D.Double(x, y),
new Point2D.Double(x + smallDistance / 2, y), largeDiameter / 2, smallDiameter / 2);
Area bodyArea = new Area(left);
bodyArea.add(right);
bodyArea.subtract(new Area(new Ellipse2D.Double(x - holeDistance / 2 - holeSize / 2,
y - holeSize / 2, holeSize, holeSize)));
bodyArea.subtract(new Area(new Ellipse2D.Double(x + holeDistance / 2 - holeSize / 2,
y - holeSize / 2, holeSize, holeSize)));
if (angle != 0) {
AffineTransform tx = AffineTransform.getRotateInstance(Math.toRadians(angle), x, y);
bodyArea.transform(tx);
}
bodyList.add(bodyArea);
int pinWidth = getClosestOdd(PIN_WIDTH.convertToPixels());
int pinThickness = getClosestOdd(PIN_THICKNESS.convertToPixels());
// inner terminals and commons
for (int i = 1; i < Math
.ceil(controlPoints.length / (configuration.getNeedsSecondLevel() ? 2.0 : 1.0)); i++) {
Shape pinShape;
pinShape = new Rectangle2D.Double(controlPoints[i].getX() - pinWidth / 2,
controlPoints[i].getY() - pinThickness / 2, pinWidth, pinThickness);
double theta = Math.atan2(controlPoints[i].getY() - controlPoints[0].getY(),
controlPoints[i].getX() - controlPoints[0].getX()) + Math.PI / 2;
Area rotatedPin = new Area(pinShape);
rotatedPin.transform(AffineTransform.getRotateInstance(theta, controlPoints[i].getX(),
controlPoints[i].getY()));
bodyList.add(rotatedPin);
}
if (configuration.getNeedsSecondLevel()) {
int innerPinCount = 2 * configuration.getPositionCount();
int commonPinCount = 2;
double outerCommonAngleOffset = Math.toRadians(INNER_COMMON_ANGLE_OFFSET);
int outerPinSpacing = getClosestOdd(OUTER_PIN_SPACING);
int outerCommonPinSpacing = getClosestOdd(OUTER_COMMON_PIN_SPACING);
int terminalHoleSize = getClosestOdd(TERMINAL_HOLE_SIZE);
int secondLevelStart = commonPinCount + innerPinCount + 1;
// outer terminals
for (int i = 0; i < configuration.getPositionCount() * 2; i++) {
Area terminalArea = new Area(new RoundRectangle2D.Double(x, y - pinWidth / 2,
outerPinSpacing / 2 + pinWidth / 2, pinWidth, pinWidth / 2, pinWidth / 2));
AffineTransform tx =
AffineTransform.getRotateInstance(pointAngles[secondLevelStart + i], x, y);
terminalArea.transform(tx);
terminalArea.subtract(bodyArea);
terminalArea.subtract(new Area(new Ellipse2D.Double(
controlPoints[secondLevelStart + i].getX() - terminalHoleSize / 2,
controlPoints[secondLevelStart + i].getY() - terminalHoleSize / 2, terminalHoleSize,
terminalHoleSize)));
bodyList.add(terminalArea);
}
// outer common terminals
Area terminalArea =
new Area(new RoundRectangle2D.Double(x - outerCommonPinSpacing / 2 - pinWidth / 2,
y - pinWidth / 2, outerCommonPinSpacing + pinWidth, pinWidth, pinWidth / 2,
pinWidth / 2));
AffineTransform tx = AffineTransform.getRotateInstance(outerCommonAngleOffset, x, y);
terminalArea.transform(tx);
terminalArea.subtract(bodyArea);
terminalArea.subtract(new Area(new Ellipse2D.Double(
controlPoints[controlPoints.length - 1].getX() - terminalHoleSize / 2,
controlPoints[controlPoints.length - 1].getY() - terminalHoleSize / 2, terminalHoleSize,
terminalHoleSize)));
terminalArea.subtract(new Area(new Ellipse2D.Double(
controlPoints[controlPoints.length - 2].getX() - terminalHoleSize / 2,
controlPoints[controlPoints.length - 2].getY() - terminalHoleSize / 2, terminalHoleSize,
terminalHoleSize)));
bodyList.addAll(AreaUtils.tryAreaBreakout(terminalArea));
}
body = bodyList.toArray(new Shape[0]);
}
return body;
}
@Override
public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode,
Project project, IDrawingObserver drawingObserver) {
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
Shape[] body = getBody();
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
: getColor().darker();
}
Composite oldComposite = g2d.getComposite();
if (alpha < MAX_ALPHA) {
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha / MAX_ALPHA));
}
int commonPinCount = 2;
int secondLevelStart = 2 * configuration.getPositionCount() + commonPinCount;
// Draw outer pins
if (configuration.getNeedsSecondLevel()) {
drawingObserver.startTrackingContinuityArea(true);
for (int i = secondLevelStart; i < body.length; i++) {
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : pinColor);
g2d.fill(body[i]);
g2d.setColor(outlineMode ? finalBorderColor : pinColor.darker());
g2d.draw(body[i]);
}
drawingObserver.stopTrackingContinuityArea();
}
// Draw body
if (componentState != ComponentState.DRAGGING) {
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : getColor());
g2d.fill(body[0]);
}
g2d.setColor(finalBorderColor);
g2d.draw(body[0]);
drawingObserver.stopTracking();
// Draw pins
for (int i = 1; i <= secondLevelStart; i++) {
drawingObserver.startTrackingContinuityArea(true);
g2d.setColor(outlineMode ? Constants.TRANSPARENT_COLOR : pinColor);
g2d.fill(body[i]);
drawingObserver.stopTrackingContinuityArea();
if (!outlineMode) {
g2d.setColor(outlineMode ? finalBorderColor : pinColor.darker());
g2d.draw(body[i]);
}
}
g2d.setComposite(oldComposite);
// draw labels
// StringUtils.drawCenteredText(g2d, name, controlPoints[0].getX(), controlPoints[0].getY(),
// HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
if (showMarkers) {
g2d.setColor(labelColor);
int innerPinSpacing = getClosestOdd(INNER_PIN_SPACING.convertToPixels());
int innerCommonPinSpacing = getClosestOdd(INNER_COMMON_PIN_SPACING.convertToPixels());
int outerPinCount = 2 * configuration.getPositionCount();
double relativeLabelLocation = 0.85;
Point2D firstPoint = controlPoints[0];
for (int i = 0; i < outerPinCount; i++) {
int x = (int) (firstPoint.getX()
+ Math.cos(pointAngles[i + 1]) * relativeLabelLocation * innerPinSpacing / 2);
int y = (int) (firstPoint.getY()
+ Math.sin(pointAngles[i + 1]) * relativeLabelLocation * innerPinSpacing / 2);
StringUtils.drawCenteredText(g2d, getControlPointNodeNameForRender(i + 1), x, y,
HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
}
relativeLabelLocation = 0.65;
for (int i = 0; i < commonPinCount; i++) {
int x = (int) (firstPoint.getX() + Math.cos(pointAngles[i + outerPinCount + 1])
* relativeLabelLocation * innerCommonPinSpacing / 2);
int y = (int) (firstPoint.getY() + Math.sin(pointAngles[i + outerPinCount + 1])
* relativeLabelLocation * innerCommonPinSpacing / 2);
StringUtils.drawCenteredText(g2d, getControlPointNodeNameForRender(i + outerPinCount + 1),
x, y, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
}
if (configuration.getNeedsSecondLevel()) {
relativeLabelLocation = 0.90;
int outerPinSpacing = getClosestOdd(OUTER_PIN_SPACING.convertToPixels());
int outerCommonPinSpacing = getClosestOdd(OUTER_COMMON_PIN_SPACING.convertToPixels());
for (int i = 0; i < outerPinCount; i++) {
int x = (int) (firstPoint.getX() + Math.cos(pointAngles[i + 1 + secondLevelStart])
* relativeLabelLocation * outerPinSpacing / 2);
int y = (int) (firstPoint.getY() + Math.sin(pointAngles[i + 1 + secondLevelStart])
* relativeLabelLocation * outerPinSpacing / 2);
StringUtils.drawCenteredText(g2d,
getControlPointNodeNameForRender(i + 1 + secondLevelStart), x, y,
HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
}
// relativeLabelLocation = 0.90;
for (int i = 0; i < commonPinCount; i++) {
int x = (int) (firstPoint.getX()
+ Math.cos(pointAngles[i + outerPinCount + 1 + secondLevelStart])
* relativeLabelLocation * outerCommonPinSpacing / 2);
int y = (int) (firstPoint.getY()
+ Math.sin(pointAngles[i + outerPinCount + 1 + secondLevelStart])
* relativeLabelLocation * outerCommonPinSpacing / 2);
StringUtils.drawCenteredText(g2d,
getControlPointNodeNameForRender(i + outerPinCount + 1 + secondLevelStart), x, y,
HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
}
}
}
}
@Override
public void drawIcon(Graphics2D g2d, int width, int height) {
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
int largeR = getClosestOdd(width * 3d / 8);
int smallR = getClosestOdd(width / 6d);
int hole = 4 * width / 32;
Area area = new TwoCircleTangent(new Point2D.Double(width * 0.5, height * 0.5),
new Point2D.Double(width / 2, height / 8d), largeR, smallR);
area.add((Area) new TwoCircleTangent(new Point2D.Double(width * 0.5, height * 0.5),
new Point2D.Double(width / 2, height * 7 / 8d), largeR, smallR));
area.subtract(
new Area(new Ellipse2D.Double((width - hole) / 2, height / 8 - hole / 2, hole, hole)));
area.subtract(
new Area(new Ellipse2D.Double((width - hole) / 2, height * 7 / 8 - hole / 2, hole, hole)));
area.transform(AffineTransform.getRotateInstance(Math.PI / 4, width / 2, height / 2));
g2d.setColor(color);
g2d.fill(area);
g2d.setColor(color.darker());
g2d.draw(area);
int center = width / 2 + 1;
int radius = width / 2 - 7;
for (int i = 0; i < 10; i++) {
int x = (int) (center + Math.cos(Math.PI / 10 + i * Math.PI / 5) * radius);
int y = (int) (center + Math.sin(Math.PI / 10 + i * Math.PI / 5) * radius);
g2d.setColor(pinColor);
g2d.fillOval(x - 1, y - 1, 2, 2);
g2d.setColor(pinColor.darker());
g2d.drawOval(x - 1, y - 1, 2, 2);
}
}
@Override
public Point2D getControlPoint(int index) {
return controlPoints[index];
}
@Override
public int getControlPointCount() {
return controlPoints.length;
}
@Override
public VisibilityPolicy getControlPointVisibilityPolicy(int index) {
return VisibilityPolicy.NEVER;
}
@Override
public boolean isControlPointSticky(int index) {
return index > 0;
}
@Override
public void setControlPoint(Point2D point, int index) {
controlPoints[index].setLocation(point);
body = null;
}
@Override
public boolean canPointMoveFreely(int pointIndex) {
return false;
}
@EditableProperty(name = "Label")
public Color getLabelColor() {
if (labelColor == null)
labelColor = LABEL_COLOR;
return labelColor;
}
public void setLabelColor(Color labelColor) {
this.labelColor = labelColor;
}
@EditableProperty
public SwitchTiming getTiming() {
return timing;
}
public void setTiming(SwitchTiming timing) {
this.timing = timing;
}
@EditableProperty(name = "Terminals")
public Color getPinColor() {
return pinColor;
}
public void setPinColor(Color pinColor) {
this.pinColor = pinColor;
}
@EditableProperty(name = "Markers")
public boolean getShowMarkers() {
return showMarkers;
}
public void setShowMarkers(boolean showMarkers) {
this.showMarkers = showMarkers;
}
@EditableProperty(name = "Angle Offset")
public Angle getAngleOffset() {
return angleOffset;
}
public void setAngleOffset(Angle angleOffset) {
this.angleOffset = angleOffset;
updateControlPoints();
// Reset body shape
body = null;
}
@Override
public Rectangle2D getCachingBounds() {
Shape[] body = getBody();
double minX = Double.MAX_VALUE;
double maxX = Double.MIN_VALUE;
double minY = Double.MAX_VALUE;
double maxY = Double.MIN_VALUE;
for (Shape shape : body) {
Rectangle2D rect = shape.getBounds2D();
minX = Math.min(rect.getMinX(), minX);
maxX = Math.max(rect.getMaxX(), maxX);
minY = Math.min(rect.getMinY(), minY);
maxY = Math.max(rect.getMaxY(), maxY);
}
int margin = 40; // to catch the pins that are outside the body, if needed
return new Rectangle2D.Double(minX - margin, minY - margin, maxX - minX + 2 * margin,
maxY - minY + 2 * margin);
}
public static enum Mount {
CHASSIS("Chassis"), PCB("PCB");
String name;
private Mount(String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}
}
@Override
public int getPositionCount() {
return configuration.getPositionCount();
}
@Override
public String getPositionName(int position) {
return Integer.toString(position + 1);
}
public String getControlPointNodeNameForRender(int index) {
int commonPinCount = 2;
int positionCount = configuration.getPositionCount();
if (index <= positionCount) {
return Integer.toString(positionCount + 1 - index);
}
if (index <= 2 * positionCount) {
return Integer.toString(2 * positionCount + 1 - index);
}
if (index <= 2 * positionCount + commonPinCount) {
return Character.toString((char) ('A' + (index - 2 * positionCount - 1)));
}
int secondLevelStart = 2 * positionCount + commonPinCount;
if (index <= positionCount + secondLevelStart) {
return Integer.toString(positionCount + 1 - index + secondLevelStart);
}
if (index <= 2 * positionCount + secondLevelStart) {
return Integer.toString(2 * positionCount + 1 - index + secondLevelStart);
}
if (index <= 2 * positionCount + commonPinCount + secondLevelStart) {
return Character.toString((char) ('C' + (index - 2 * positionCount - 1 - secondLevelStart)));
}
return null;
}
@Override
public String getControlPointNodeName(int index) {
int commonPinCount = 2;
int positionCount = configuration.getPositionCount();
if (index <= positionCount) {
return "A" + (positionCount + 1 - index);
}
if (index <= 2 * positionCount) {
return "B" + (2 * positionCount + 1 - index);
}
if (index <= 2 * positionCount + commonPinCount) {
return Character.toString((char) ('A' + (index - 2 * positionCount - 1)));
}
int secondLevelStart = 2 * positionCount + commonPinCount;
if (index <= positionCount + secondLevelStart) {
return "C" + (positionCount + 1 - index + secondLevelStart);
}
if (index <= 2 * positionCount + secondLevelStart) {
return "D" + (2 * positionCount + 1 - index + secondLevelStart);
}
if (index <= 2 * positionCount + commonPinCount + secondLevelStart) {
return Character.toString((char) ('C' + (index - 2 * positionCount - 1 - secondLevelStart)));
}
return null;
}
@Override
public boolean arePointsConnected(int index1, int index2, int position) {
if (index1 == 0)
return false;
int commonPinCount = 2;
int positionCount = configuration.getPositionCount();
if (index1 <= positionCount) {
return index1 == positionCount - position && index2 == 2 * positionCount + 1;
}
if (index1 <= 2 * positionCount) {
return index1 == 2 * positionCount - position && index2 == 2 * positionCount + 2;
}
if (index1 <= 2 * positionCount + commonPinCount) {
return false;
}
if (configuration.getNeedsSecondLevel()) {
int secondLevelStart = 2 * positionCount + commonPinCount;
if (index1 <= positionCount + secondLevelStart) {
return index1 == secondLevelStart + positionCount - position
&& index2 == 2 * positionCount + 1 + secondLevelStart;
}
if (index2 <= 2 * positionCount + secondLevelStart + commonPinCount) {
return index1 == secondLevelStart + 2 * positionCount - position
&& index2 == 2 * positionCount + 2 + secondLevelStart;
}
}
return false;
}
}
@@ -0,0 +1,55 @@
/*
DIY Layout Creator (DIYLC).
Copyright (c) 2009-2018 held jointly by the individual authors.
This file is part of DIYLC.
DIYLC is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
DIYLC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*/
package org.diylc.components.electromechanical;
public enum RotarySwitchOpenType {
_2P5T("2P5T", 2, 5, false), _2P6T("2P6T", 2, 6, false), _4P5T("4P5T", 4, 5, true), _4P6T("4P6T", 4, 6, true);
private String name;
private int positionCount;
private int poleCount;
private boolean needsSecondLevel;
private RotarySwitchOpenType(String name, int poleCount, int positionCount, boolean needsSecondLevel) {
this.name = name;
this.poleCount = poleCount;
this.positionCount = positionCount;
this.needsSecondLevel = needsSecondLevel;
}
@Override
public String toString() {
return name;
}
public int getPoleCount() {
return poleCount;
}
public int getPositionCount() {
return positionCount;
}
public boolean getNeedsSecondLevel() {
return needsSecondLevel;
}
}
@@ -70,8 +70,7 @@ public class RotarySwitchSealed extends AbstractAngledComponent<RotarySwitchSeal
private static Color BODY_COLOR = Color.decode("#F7F7EF");
private static Color LABEL_COLOR = BODY_COLOR.darker();
public static Color PIN_COLOR = Color.decode("#00B2EE");
public static Color PIN_BORDER_COLOR = PIN_COLOR.darker();
public static Size PIN_DIAMETER = new Size(1d, SizeUnit.mm);
private static Size PIN_WIDTH = new Size(0.08d, SizeUnit.in);
private static Size PIN_THICKNESS = new Size(0.02d, SizeUnit.in);
@@ -81,6 +80,7 @@ public class RotarySwitchSealed extends AbstractAngledComponent<RotarySwitchSeal
private Mount mount = Mount.CHASSIS;
private SwitchTiming timing;
private Color labelColor = LABEL_COLOR;
private Color pinColor = METAL_COLOR;
private Point2D[] controlPoints = new Point2D[] {new Point2D.Double(0, 0)};
private double[] pointAngles;
@@ -130,7 +130,7 @@ public class RotarySwitchSealed extends AbstractAngledComponent<RotarySwitchSeal
controlPoints = new Point2D[outerPinCount + innerPinCount + 1];
pointAngles = new double[controlPoints.length];
double theta = outerAngleOffset + Math.toRadians(getAngle().getValue());
double theta = outerAngleOffset + getAngle().getValueRad();
controlPoints[0] = firstPoint;
for (int i = 0; i < outerPinCount; i++) {
@@ -141,7 +141,7 @@ public class RotarySwitchSealed extends AbstractAngledComponent<RotarySwitchSeal
theta += outerAngleIncrement;
}
theta = innerAngleOffset + Math.toRadians(getAngle().getValue());
theta = innerAngleOffset + getAngle().getValueRad();
for (int i = 0; i < innerPinCount; i++) {
controlPoints[i + outerPinCount + 1] =
new Point2D.Double(firstPoint.getX() + Math.cos(theta) * innerPinDiameter / 2,
@@ -214,10 +214,13 @@ public class RotarySwitchSealed extends AbstractAngledComponent<RotarySwitchSeal
rotatedPin.transform(AffineTransform.getRotateInstance(theta, controlPoints[i].getX(), controlPoints[i].getY()));
pinShape = rotatedPin;
}
g2d.setColor(PIN_COLOR);
g2d.fill(pinShape);
g2d.setColor(PIN_BORDER_COLOR);
g2d.setColor(pinColor);
if (getMount() == Mount.CHASSIS)
drawingObserver.startTrackingContinuityArea(true);
g2d.fill(pinShape);
drawingObserver.stopTrackingContinuityArea();
g2d.setColor(pinColor.darker());
g2d.draw(pinShape);
}
}
@@ -231,17 +234,14 @@ public class RotarySwitchSealed extends AbstractAngledComponent<RotarySwitchSeal
for (int i = 0; i < outerPinCount; i++) {
int x = (int) (firstPoint.getX() + Math.cos(pointAngles[i + 1]) * relativeLabelLocation * outerPinDiameter / 2);
int y = (int) (firstPoint.getY() + Math.sin(pointAngles[i + 1]) * relativeLabelLocation * outerPinDiameter / 2);
StringUtils.drawCenteredText(g2d, Integer.toString(i + 1), x, y, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
StringUtils.drawCenteredText(g2d, getControlPointNodeName(i + 1), x, y, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
}
relativeLabelLocation = 0.65;
int labelStep = 1;
if (configuration.getPoleCount() == 2) {
labelStep = 2;
}
for (int i = 0; i < innerPinCount; i++) {
int x = (int) (firstPoint.getX() + Math.cos(pointAngles[i + outerPinCount + 1]) * relativeLabelLocation * innerPinDiameter / 2);
int y = (int) (firstPoint.getY() + Math.sin(pointAngles[i + outerPinCount + 1]) * relativeLabelLocation * innerPinDiameter / 2);
StringUtils.drawCenteredText(g2d, Character.toString((char)('A' + labelStep * i)), x, y,
StringUtils.drawCenteredText(g2d, getControlPointNodeName(i + outerPinCount + 1), x, y,
HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
}
}
@@ -255,18 +255,18 @@ public class RotarySwitchSealed extends AbstractAngledComponent<RotarySwitchSeal
int commonX = (int) (width - 8 * width / 32d);
int commonY = (int) (height - 8 * width / 32d);
g2d.setColor(PIN_COLOR);
g2d.setColor(pinColor);
g2d.fillOval(commonX - 1, commonY - 1, 3, 3);
g2d.setColor(PIN_BORDER_COLOR);
g2d.setColor(pinColor.darker());
g2d.drawOval(commonX - 1, commonY - 1, 3, 3);
int radius = (int) (width - 6 * width / 32d);
for (int i = 0; i < 4; i++) {
int x = (int) (width + Math.cos(1.1 * Math.PI + i * Math.PI / 6) * radius);
int y = (int) (height + Math.sin(1.1 * Math.PI + i * Math.PI / 6) * radius);
g2d.setColor(PIN_COLOR);
g2d.setColor(pinColor);
g2d.fillOval(x - 1, y - 1, 3, 3);
g2d.setColor(PIN_BORDER_COLOR);
g2d.setColor(pinColor.darker());
g2d.drawOval(x - 1, y - 1, 3, 3);
}
@@ -342,6 +342,15 @@ public class RotarySwitchSealed extends AbstractAngledComponent<RotarySwitchSeal
this.timing = timing;
}
@EditableProperty(name = "Terminals")
public Color getPinColor() {
return pinColor;
}
public void setPinColor(Color pinColor) {
this.pinColor = pinColor;
}
@Override
public Rectangle2D getCachingBounds() {
Rectangle2D rect = getBody()[0].getBounds2D();
@@ -373,6 +382,22 @@ public class RotarySwitchSealed extends AbstractAngledComponent<RotarySwitchSeal
public String getPositionName(int position) {
return Integer.toString(position + 1);
}
@Override
public String getControlPointNodeName(int index) {
int outerPinCount = 12;
if (index <= outerPinCount) {
return Integer.toString(index);
}
int labelStep = 1;
if (configuration.getPoleCount() == 2) {
labelStep = 2;
}
return Character.toString((char)('A' + labelStep * (index - outerPinCount - 1)));
}
@Override
public boolean arePointsConnected(int index1, int index2, int position) {
@@ -205,7 +205,7 @@ public class LeverSwitch extends AbstractAngledComponent<LeverSwitchType> implem
}
body[1] = waferArea;
double theta = Math.toRadians(getAngle().getValue());
double theta = getAngle().getValueRad();
Area terminalArea = new Area();
Area commonTerminalArea = new Area();
@@ -299,9 +299,9 @@ public class LeverSwitch extends AbstractAngledComponent<LeverSwitchType> implem
break;
}
// Rotate if needed
if (getAngle().getValue() != 0) {
double theta = Math.toRadians(getAngle().getValue());
// Rotate if needed
double theta = getAngle().getValueRad();
if (theta != 0) {
AffineTransform rotation = AffineTransform.getRotateInstance(theta, x, y);
for (Point2D point : controlPoints) {
rotation.transform(point, point);
@@ -188,7 +188,7 @@ public class SchallerMegaSwitch extends AbstractAngledComponent<String> implemen
double y =
(controlPoints[0].getY() + controlPoints[lastPointIdx].getY()) / 2 + getLabelDy() * 2.25;
double theta = Math.toRadians(getAngle().getValue()) - Math.PI / 2;
double theta = getAngle().getValueRad() - Math.PI / 2;
if ((theta >= Math.PI / 2 && theta <= Math.PI) || (theta < -Math.PI / 2 && theta > -Math.PI)) {
theta += Math.PI;
@@ -248,7 +248,7 @@ public class SchallerMegaSwitch extends AbstractAngledComponent<String> implemen
body[1] = waferArea;
double theta = Math.toRadians(getAngle().getValue());
double theta = getAngle().getValueRad();
Area terminalArea = new Area();
for (int i = 0; i < controlPoints.length; i++) {
@@ -326,8 +326,8 @@ public class SchallerMegaSwitch extends AbstractAngledComponent<String> implemen
}
// Rotate if needed
if (getAngle().getValue() != 0) {
double theta = Math.toRadians(getAngle().getValue());
double theta = getAngle().getValueRad();
if (theta != 0) {
AffineTransform rotation = AffineTransform.getRotateInstance(theta, x, y);
for (Point2D point : controlPoints) {
rotation.transform(point, point);
@@ -343,7 +343,7 @@ public class SchallerMegaSwitch extends AbstractAngledComponent<String> implemen
private void updateLabelPositions() {
double labelOffset = LABEL_OFFSET.convertToPixels();
double theta = Math.toRadians(getAngle().getValue()) - Math.PI;
double theta = getAngle().getValueRad() - Math.PI;
labelDx = Math.cos(theta) * labelOffset;
labelDy = Math.sin(theta) * labelOffset;
}
@@ -110,7 +110,7 @@ public class Jumbo4PinTubeSocket extends AbstractAngledComponent<String> {
double initialAngleOffset = angleIncrement / 2;
controlPoints = new Point2D[pinCount + 1];
double theta = initialAngleOffset + Math.toRadians(getAngle().getValue());
double theta = initialAngleOffset + getAngle().getValueRad();
controlPoints[0] = firstPoint;
for (int i = 0; i < pinCount; i++) {
controlPoints[i + 1] =
@@ -138,7 +138,7 @@ public class Jumbo4PinTubeSocket extends AbstractAngledComponent<String> {
double mountingHoleSize = getClosestOdd(JUMBO_MOUNT_HOLE_SIZE.convertToPixels());
double mountingHoleSpacing = getClosestOdd(JUMBO_MOUNT_HOLE_SPACING.convertToPixels());
double theta = Math.toRadians(getAngle().getValue());
double theta = getAngle().getValueRad();
int centerX = (int) (controlPoints[0].getX()
+ Math.cos(theta) * (mountingHoleSpacing / 2 - mountingHoleSize / 2));
int centerY = (int) (controlPoints[0].getY()
@@ -159,7 +159,7 @@ public class Jumbo4PinTubeSocket extends AbstractAngledComponent<String> {
Area terminalArea = new Area();
double alpha = initialAngleOffset + Math.toRadians(getAngle().getValue());
double alpha = initialAngleOffset + getAngle().getValueRad();
double terminalLength = JUMBO_TERMINAL_LENGTH.convertToPixels();
int terminalWidth = getClosestOdd(JUMBO_TERMINAL_WIDTH.convertToPixels());
int terminalHoleDiameter = getClosestOdd(JUMBO_TERMINAL_HOLE_SIZE.convertToPixels());
@@ -167,7 +167,7 @@ public class TubeSocket extends AbstractAngledComponent<String> {
double initialAngleOffset = hasEmptySpace ? angleIncrement : (angleIncrement / 2);
controlPoints = new Point2D[pinCount + 1];
double theta = initialAngleOffset + Math.toRadians(getAngle().getValue());
double theta = initialAngleOffset + getAngle().getValueRad();
controlPoints[0] = firstPoint;
for (int i = 0; i < pinCount; i++) {
controlPoints[i + 1] =
@@ -207,7 +207,7 @@ public class TubeSocket extends AbstractAngledComponent<String> {
if (base == Base.OCTAL) {
int tickSize = getClosestOdd(OCTAL_TICK_SIZE.convertToPixels());
double theta = Math.toRadians(getAngle().getValue());
double theta = getAngle().getValueRad();
int centerX = (int) (controlPoints[0].getX() + Math.cos(theta) * holeSize / 2);
int centerY = (int) (controlPoints[0].getY() + Math.sin(theta) * holeSize / 2);
bodyArea.subtract(new Area(new Ellipse2D.Double(centerX - tickSize / 2, centerY - tickSize / 2, tickSize,
@@ -0,0 +1,70 @@
package org.diylc.netlist;
import org.diylc.components.electromechanical.RotarySwitchOpen;
import org.diylc.components.electromechanical.RotarySwitchOpenType;
import org.junit.Test;
public class RotarySwitchOpenTests extends AbstractSwitchTests {
@Test
public void testRotarySwitch2P5T() {
RotarySwitchOpen sw = new RotarySwitchOpen();
String[] validCombinations = new String[] {
"0,5,11", "0,10,12",
"1,4,11", "1,9,12",
"2,3,11", "2,8,12",
"3,2,11", "3,7,12",
"4,1,11", "4,6,12"};
sw.setValue(RotarySwitchOpenType._2P5T);
testSwitch(sw, validCombinations);
}
@Test
public void testRotarySwitch2P6T() {
RotarySwitchOpen sw = new RotarySwitchOpen();
String[] validCombinations = new String[] {
"0,6,13", "0,12,14",
"1,5,13", "1,11,14",
"2,4,13", "2,10,14",
"3,3,13", "3,9,14",
"4,2,13", "4,8,14",
"5,1,13", "5,7,14"};
sw.setValue(RotarySwitchOpenType._2P6T);
testSwitch(sw, validCombinations);
}
@Test
public void testRotarySwitch4P5T() {
RotarySwitchOpen sw = new RotarySwitchOpen();
String[] validCombinations = new String[] {
"0,5,11", "0,10,12", "0,17,23", "0,22,24",
"1,4,11", "1,9,12", "1,16,23", "1,21,24",
"2,3,11", "2,8,12", "2,15,23", "2,20,24",
"3,2,11", "3,7,12", "3,14,23", "3,19,24",
"4,1,11", "4,6,12", "4,13,23", "4,18,24"};
sw.setValue(RotarySwitchOpenType._4P5T);
testSwitch(sw, validCombinations);
}
@Test
public void testRotarySwitch4P6T() {
RotarySwitchOpen sw = new RotarySwitchOpen();
String[] validCombinations = new String[] {
"0,6,13", "0,12,14", "0,20,27", "0,26,28",
"1,5,13", "1,11,14", "1,19,27", "1,25,28",
"2,4,13", "2,10,14", "2,18,27", "2,24,28",
"3,3,13", "3,9,14", "3,17,27", "3,23,28",
"4,2,13", "4,8,14", "4,16,27", "4,22,28",
"5,1,13", "5,7,14", "5,15,27", "5,21,28",};
sw.setValue(RotarySwitchOpenType._4P6T);
testSwitch(sw, validCombinations);
}
}
@@ -4,7 +4,7 @@ import org.diylc.components.electromechanical.RotarySwitchSealed;
import org.diylc.components.electromechanical.RotarySwitchSealedType;
import org.junit.Test;
public abstract class RotarySwitchTests extends AbstractSwitchTests {
public abstract class RotarySwitchSealedTests extends AbstractSwitchTests {
@Test
public void testRotarySwitchSP12T() {
@@ -25,7 +25,7 @@ public abstract class RotarySwitchTests extends AbstractSwitchTests {
"0,1,13", "1,2,13", "2,3,13", "3,4,13", "4,5,13", "5,6,13",
"0,7,14", "1,8,14", "2,9,14", "3,10,14", "4,11,14", "5,12,14"};
sw.setValue(RotarySwitchSealedType.DP6T);
sw.setValue(RotarySwitchSealedType._2P6T);
testSwitch(sw, validCombinations);
}