slide switch highlighting

This commit is contained in:
Bane Stojkovic
2025-03-16 23:22:34 +01:00
parent 896a623c7c
commit a86af6384b
10 changed files with 252 additions and 179 deletions
@@ -0,0 +1,34 @@
package org.diylc.core;
import org.diylc.common.PropertyWrapper;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public abstract class AbstractDynamicPropertyValueSource implements IDynamicPropertySource {
protected List<PropertyWrapper> properties;
protected IDIYComponent<?> component;
@Override
public void setProperties(List<PropertyWrapper> properties) {
this.properties = properties;
}
@Override
public void setComponent(IDIYComponent<?> component) {
this.component = component;
}
@Override
public String getDisplayValue(Object value) {
if (value == null) {
return "None";
}
if (component instanceof ISwitch sw) {
return sw.getPositionName((Integer)value);
}
return "#" + (((Integer)value) + 1);
}
}
@@ -27,63 +27,68 @@ import java.util.*;
public class SwitchUtils {
public static List<Set<Integer>> getConnectedTerminals(ISwitch switchComponent, int numPoints) {
List<Set<Integer>> connectedGroups = new ArrayList<>();
public static List<Set<Integer>> getConnectedTerminals(ISwitch switchComponent, int numPoints) {
// Only process if we have a selected position and should render connections
if (switchComponent.getSelectedPosition() == null || !Boolean.TRUE.equals(switchComponent.getHighlightConnectedTerminals())) {
return connectedGroups;
}
// First, collect all direct connections
List<int[]> connections = new ArrayList<>();
for (int i = 0; i < numPoints; i++) {
for (int j = i + 1; j < numPoints; j++) {
if (switchComponent.arePointsConnected(i, j, switchComponent.getSelectedPosition())) {
connections.add(new int[]{i, j});
}
}
}
// Create a map to track which points are in which group
Map<Integer, Set<Integer>> pointToGroup = new HashMap<>();
// Process each connection
for (int[] connection : connections) {
int p1 = connection[0];
int p2 = connection[1];
Set<Integer> group1 = pointToGroup.get(p1);
Set<Integer> group2 = pointToGroup.get(p2);
if (group1 == null && group2 == null) {
// Create new group
Set<Integer> newGroup = new HashSet<>();
newGroup.add(p1);
newGroup.add(p2);
pointToGroup.put(p1, newGroup);
pointToGroup.put(p2, newGroup);
} else if (group1 == null) {
// Add p1 to p2's group
group2.add(p1);
pointToGroup.put(p1, group2);
} else if (group2 == null) {
// Add p2 to p1's group
group1.add(p2);
pointToGroup.put(p2, group1);
} else if (group1 != group2) {
// Merge groups
group1.addAll(group2);
for (Integer p : group2) {
pointToGroup.put(p, group1);
}
}
}
// Collect unique groups
Set<Set<Integer>> uniqueGroups = new HashSet<>(pointToGroup.values());
connectedGroups.addAll(uniqueGroups);
return connectedGroups;
if (!Boolean.TRUE.equals(switchComponent.getHighlightConnectedTerminals())) {
return List.of();
}
List<Set<Integer>> connectedGroups = new ArrayList<>();
// Only process if we have a selected position and should render connections
if (switchComponent.getSelectedPosition() == null || !Boolean.TRUE.equals(
switchComponent.getHighlightConnectedTerminals())) {
return connectedGroups;
}
// First, collect all direct connections
List<int[]> connections = new ArrayList<>();
for (int i = 0; i < numPoints; i++) {
for (int j = i + 1; j < numPoints; j++) {
if (switchComponent.arePointsConnected(i, j, switchComponent.getSelectedPosition())) {
connections.add(new int[] {i, j});
}
}
}
// Create a map to track which points are in which group
Map<Integer, Set<Integer>> pointToGroup = new HashMap<>();
// Process each connection
for (int[] connection : connections) {
int p1 = connection[0];
int p2 = connection[1];
Set<Integer> group1 = pointToGroup.get(p1);
Set<Integer> group2 = pointToGroup.get(p2);
if (group1 == null && group2 == null) {
// Create new group
Set<Integer> newGroup = new HashSet<>();
newGroup.add(p1);
newGroup.add(p2);
pointToGroup.put(p1, newGroup);
pointToGroup.put(p2, newGroup);
} else if (group1 == null) {
// Add p1 to p2's group
group2.add(p1);
pointToGroup.put(p1, group2);
} else if (group2 == null) {
// Add p2 to p1's group
group1.add(p2);
pointToGroup.put(p2, group1);
} else if (group1 != group2) {
// Merge groups
group1.addAll(group2);
for (Integer p : group2) {
pointToGroup.put(p, group1);
}
}
}
// Collect unique groups
Set<Set<Integer>> uniqueGroups = new HashSet<>(pointToGroup.values());
connectedGroups.addAll(uniqueGroups);
return connectedGroups;
}
}
@@ -29,12 +29,17 @@ import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import java.util.List;
import java.util.Set;
import java.util.stream.IntStream;
import org.diylc.appframework.miscutils.ConfigurationManager;
import org.diylc.common.IPlugInPort;
import org.diylc.common.ObjectCache;
import org.diylc.common.OrientationHV;
import org.diylc.components.AbstractTransparentComponent;
import org.diylc.components.guitar.Freeway3x4_03SwitchPositionPropertyValueSource;
import org.diylc.components.transform.MiniToggleSwitchTransformer;
import org.diylc.core.ComponentState;
import org.diylc.core.IDIYComponent;
@@ -44,6 +49,7 @@ 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.DynamicEditableProperty;
import org.diylc.core.annotations.EditableProperty;
import org.diylc.core.annotations.PositiveNonZeroMeasureValidator;
import org.diylc.core.gerber.IGerberComponentSimple;
@@ -51,6 +57,8 @@ import org.diylc.core.measures.Size;
import org.diylc.core.measures.SizeUnit;
import org.diylc.utils.Constants;
import static org.diylc.utils.SwitchUtils.getConnectedTerminals;
@ComponentDescriptor(name = "Slide Switch", category = "Electro-Mechanical",
author = "Branislav Stojkovic", description = "Panel mounted slide switch",
zOrder = IDIYComponent.COMPONENT, instanceNamePrefix = "SW", autoEdit = false,
@@ -85,6 +93,9 @@ public class SlideSwitch extends AbstractTransparentComponent<SlideSwitchType> i
private Color bracketColor = BRACKET_COLOR;
private Boolean showBracket = true;
private Integer selectedPosition;
private Boolean highlightConnectedTerminals;
public SlideSwitch() {
super();
updateControlPoints();
@@ -273,39 +284,36 @@ public class SlideSwitch extends AbstractTransparentComponent<SlideSwitchType> i
lugHeight = getClosestOdd((int) LUG_THICKNESS.convertToPixels());
}
List<Set<Integer>> connectedTerminals = getConnectedTerminals(this, controlPoints.length);
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
for (Point2D p : controlPoints) {
for (int i = 0; i < controlPoints.length; i++) {
Point2D p = controlPoints[i];
if (outlineMode) {
g2d.setColor(theme.getOutlineColor());
g2d.drawRect((int) (p.getX() - lugWidth / 2), (int) (p.getY() - lugHeight / 2), lugWidth,
lugHeight);
} else {
g2d.setColor(TERMINAL_COLOR);
int finalI = i;
int groupIndex = IntStream.range(0, connectedTerminals.size())
.filter(j -> connectedTerminals.get(j).contains(finalI))
.findFirst().orElse(-1);
if (groupIndex < 0) {
g2d.setColor(TERMINAL_COLOR);
} else {
g2d.setColor(ISwitch.POLE_COLORS[groupIndex]);
}
g2d.fillRect((int) (p.getX() - lugWidth / 2), (int) (p.getY() - lugHeight / 2), lugWidth,
lugHeight);
g2d.setColor(TERMINAL_COLOR.darker());
if (groupIndex < 0) {
g2d.setColor(TERMINAL_COLOR.darker());
} else {
g2d.setColor(ISwitch.POLE_COLORS[groupIndex].darker());
}
g2d.drawRect((int) (p.getX() - lugWidth / 2), (int) (p.getY() - lugHeight / 2), lugWidth,
lugHeight);
}
}
// g2d.setFont(project.getFont());
// g2d.setColor(getLabelColor());
//
// double labelOffsetX = 0;
// double labelOffsetY = 0;
// if (getValue() == SlideSwitchType.SPDT) {
// int rowSpacing = (int) getRowSpacing().convertToPixels();
// if (getOrientation() == OrientationHV.VERTICAL) {
// labelOffsetY = -rowSpacing / 2;
// } else {
// labelOffsetX = -rowSpacing / 2;
// }
// }
//
// Rectangle bounds = body[0].getBounds();
// StringUtils.drawCenteredText(g2d, getName(), bounds.getCenterX() + labelOffsetX,
// bounds.getCenterY() + labelOffsetY, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
}
public Shape[] getBody() {
@@ -411,14 +419,7 @@ public class SlideSwitch extends AbstractTransparentComponent<SlideSwitchType> i
@Override
public int getPositionCount() {
switch (switchType) {
case SPDT:
case DPDT:
return 2;
case DP3T:
return 3;
}
return 2;
return switchType.getPositionCount();
}
@Override
@@ -428,12 +429,43 @@ public class SlideSwitch extends AbstractTransparentComponent<SlideSwitchType> i
@Override
public boolean arePointsConnected(int index1, int index2, int position) {
// Method assumes index1 < index2
switch (switchType) {
case SPDT:
return (index1 == position) && (index2 - index1 == 1);
// For SPDT, index 1 is the common (center) terminal
// Position 1: connects terminals 0-1
// Position 2: connects terminals 1-2
if (position == 0) {
return index1 == 0 && index2 == 1;
} else if (position == 1) {
return index1 == 1 && index2 == 2;
}
return false;
case DPDT:
// For DPDT, indices 1 and 4 are the common (center) terminals
// Position 1: connects terminals 0-1 and 3-4
// Position 2: connects terminals 1-2 and 4-5
if (position == 0) {
return (index1 == 0 && index2 == 1) || (index1 == 3 && index2 == 4);
} else if (position == 1) {
return (index1 == 1 && index2 == 2) || (index1 == 4 && index2 == 5);
}
return false;
case DP3T:
return (index1 == position * 2 || index1 == position * 2 + 1) && (index2 - index1 == 2);
// For DP3T, following the schematic:
// Position 1: A1-A2 and B1-B2 (0-1 and 4-5)
// Position 2: A2-A3 and B2-B3 (1-2 and 5-6)
// Position 3: A3-A4 and B3-B4 (2-3 and 6-7)
if (position == 0) { // Position 1
return (index1 == 0 && index2 == 1) || // A1-A2
(index1 == 4 && index2 == 5); // B1-B2
} else if (position == 1) { // Position 2
return (index1 == 1 && index2 == 2) || // A2-A3
(index1 == 5 && index2 == 6); // B2-B3
} else if (position == 2) { // Position 3
return (index1 == 2 && index2 == 3) || // A3-A4
(index1 == 6 && index2 == 7); // B3-B4
}
}
return false;
}
@@ -465,6 +497,30 @@ public class SlideSwitch extends AbstractTransparentComponent<SlideSwitchType> i
this.bracketColor = bracketColor;
}
@DynamicEditableProperty(source = SlideSwitchPositionPropertyValueSource.class)
@EditableProperty(name = "Selected Position")
@Override
public Integer getSelectedPosition() {
return selectedPosition;
}
public void setSelectedPosition(Integer selectedPosition) {
this.selectedPosition = selectedPosition;
}
@EditableProperty(name = "Highlight Connected")
@Override
public Boolean getHighlightConnectedTerminals() {
if (highlightConnectedTerminals == null) {
highlightConnectedTerminals = false;
}
return highlightConnectedTerminals;
}
public void setHighlightConnectedTerminals(Boolean highlightConnectedTerminals) {
this.highlightConnectedTerminals = highlightConnectedTerminals;
}
@Override
public Rectangle2D getCachingBounds() {
int margin = 20;
@@ -0,0 +1,41 @@
package org.diylc.components.electromechanical;
import org.diylc.common.PropertyWrapper;
import org.diylc.components.guitar.LeverSwitch;
import org.diylc.core.AbstractDynamicPropertyValueSource;
import org.diylc.core.IDIYComponent;
import org.diylc.core.IDynamicPropertySource;
import org.diylc.core.ISwitch;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public class SlideSwitchPositionPropertyValueSource extends AbstractDynamicPropertyValueSource {
@Override
public List<Object> getAvailableValues() {
if (properties == null) {
return List.of();
}
Optional<PropertyWrapper> optionalPropertyWrapper = this.properties.stream().filter(p -> {
try {
return p.getGetter().getName().equals("getValue");
} catch (NoSuchMethodException e) {
return false;
}
}).findFirst();
if (optionalPropertyWrapper.isEmpty()) {
return List.of();
}
SlideSwitchType
switchType = (SlideSwitchType) optionalPropertyWrapper.get().getValue();
int positionCount = switchType.getPositionCount();
List<Object> res = new ArrayList<>();
res.add(null);
for (int i = 0; i < positionCount; i++) {
res.add(i);
}
return res;
}
}
@@ -2,16 +2,15 @@ package org.diylc.components.electromechanical;
public enum SlideSwitchType {
SPDT, DPDT, DP3T;
SPDT(2), DPDT(2), DP3T(3);
@Override
public String toString() {
String name = name();
if (name.startsWith("_"))
name = name.substring(1);
name = name.replace("_", " ");
name = name.replace("mustang", "");
name = name.replace("off", " (Center OFF)");
return name;
private final int positionCount;
SlideSwitchType(int positionCount) {
this.positionCount = positionCount;
}
public int getPositionCount() {
return positionCount;
}
}
@@ -129,8 +129,6 @@ public class Freeway3x3_03 extends AbstractTransparentComponent<Void> implements
double[] xOffsets = getXOffsetsPx();
double[] yOffsets = getYOffsetsPx();
Rectangle2D rect = new Rectangle2D.Double(x - xOffsets[0], y - yOffsets[0], bounds2d.getWidth(),
bounds2d.getHeight());
@@ -1,6 +1,7 @@
package org.diylc.components.guitar;
import org.diylc.common.PropertyWrapper;
import org.diylc.core.AbstractDynamicPropertyValueSource;
import org.diylc.core.IDIYComponent;
import org.diylc.core.IDynamicPropertySource;
import org.diylc.core.ISwitch;
@@ -9,20 +10,8 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public class Freeway3x4_03SwitchPositionPropertyValueSource implements IDynamicPropertySource {
private List<PropertyWrapper> properties;
private IDIYComponent<?> component;
@Override
public void setProperties(List<PropertyWrapper> properties) {
this.properties = properties;
}
@Override
public void setComponent(IDIYComponent<?> component) {
this.component = component;
}
public class Freeway3x4_03SwitchPositionPropertyValueSource extends
AbstractDynamicPropertyValueSource {
@Override
public List<Object> getAvailableValues() {
@@ -34,15 +23,4 @@ public class Freeway3x4_03SwitchPositionPropertyValueSource implements IDynamicP
}
return res;
}
@Override
public String getDisplayValue(Object value) {
if (value == null) {
return "None";
}
if (component instanceof ISwitch sw) {
return sw.getPositionName((Integer)value);
}
return "#" + (((Integer)value) + 1);
}
}
@@ -1,26 +1,14 @@
package org.diylc.components.guitar;
import org.diylc.common.PropertyWrapper;
import org.diylc.core.AbstractDynamicPropertyValueSource;
import org.diylc.core.IDIYComponent;
import org.diylc.core.IDynamicPropertySource;
import org.diylc.core.ISwitch;
import java.util.*;
public class LeverSwitchPositionPropertyValueSource implements IDynamicPropertySource {
private List<PropertyWrapper> properties;
private IDIYComponent<?> component;
@Override
public void setProperties(List<PropertyWrapper> properties) {
this.properties = properties;
}
@Override
public void setComponent(IDIYComponent<?> component) {
this.component = component;
}
public class LeverSwitchPositionPropertyValueSource extends AbstractDynamicPropertyValueSource {
@Override
public List<Object> getAvailableValues() {
@@ -47,15 +35,4 @@ public class LeverSwitchPositionPropertyValueSource implements IDynamicPropertyS
}
return res;
}
@Override
public String getDisplayValue(Object value) {
if (value == null) {
return "None";
}
if (component instanceof ISwitch sw) {
return sw.getPositionName((Integer)value);
}
return "#" + (((Integer)value) + 1);
}
}
@@ -1,6 +1,7 @@
package org.diylc.components.guitar;
import org.diylc.common.PropertyWrapper;
import org.diylc.core.AbstractDynamicPropertyValueSource;
import org.diylc.core.IDIYComponent;
import org.diylc.core.IDynamicPropertySource;
import org.diylc.core.ISwitch;
@@ -9,20 +10,8 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public class SchallerMegaSwitchPositionPropertyValueSource implements IDynamicPropertySource {
private List<PropertyWrapper> properties;
private IDIYComponent<?> component;
@Override
public void setProperties(List<PropertyWrapper> properties) {
this.properties = properties;
}
@Override
public void setComponent(IDIYComponent<?> component) {
this.component = component;
}
public class SchallerMegaSwitchPositionPropertyValueSource extends
AbstractDynamicPropertyValueSource {
@Override
public List<Object> getAvailableValues() {
@@ -49,15 +38,4 @@ public class SchallerMegaSwitchPositionPropertyValueSource implements IDynamicPr
}
return res;
}
@Override
public String getDisplayValue(Object value) {
if (value == null) {
return "None";
}
if (component instanceof ISwitch sw) {
return sw.getPositionName((Integer)value);
}
return "#" + (((Integer)value) + 1);
}
}
@@ -5,7 +5,7 @@ import org.junit.Test;
import org.diylc.components.electromechanical.SlideSwitch;
import org.diylc.components.electromechanical.SlideSwitchType;
public abstract class SlideSwitchTests extends AbstractSwitchTests {
public class SlideSwitchTests extends AbstractSwitchTests {
@Test
public void testSlideSwitchSPDT() {
@@ -19,7 +19,10 @@ public abstract class SlideSwitchTests extends AbstractSwitchTests {
public void testSlideSwitchDPDT() {
SlideSwitch slideSwitch = new SlideSwitch();
slideSwitch.setValue(SlideSwitchType.DPDT);
String[] validCombinations = new String[] {"0,0,2", "0,1,3", "1,2,4", "1,3,5"};
String[] validCombinations = new String[] {
"0,0,1", "0,3,4", // Position 1 connections
"1,1,2", "1,4,5" // Position 2 connections
};
testSwitch(slideSwitch, validCombinations);
}
@@ -27,7 +30,11 @@ public abstract class SlideSwitchTests extends AbstractSwitchTests {
public void testSlideSwitchDP3T() {
SlideSwitch slideSwitch = new SlideSwitch();
slideSwitch.setValue(SlideSwitchType.DP3T);
String[] validCombinations = new String[] {"0,0,2", "0,1,3", "1,2,4", "1,3,5", "2,4,6", "2,5,7"};
String[] validCombinations = new String[] {
"0,0,1", "0,4,5", // Position 1 connections
"1,1,2", "1,5,6", // Position 2 connections
"2,2,3", "2,6,7" // Position 3 connections
};
testSwitch(slideSwitch, validCombinations);
}
}