From 02a24b43e71bde63d46ec061c752e49f21fe42b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20Ha=C3=9Fel?= Date: Thu, 23 May 2024 15:20:42 +0200 Subject: [PATCH] Voltmeter --- src/main/java/de/ph87/electro/CONFIG.java | 4 + .../java/de/ph87/electro/circuit/Circuit.java | 7 +- .../circuit/CircuitPanelDropTarget.java | 2 + .../circuit/calculation/Calculation.java | 9 +- .../de/ph87/electro/circuit/part/Part.java | 6 +- .../de/ph87/electro/circuit/part/PartDto.java | 1 + .../ph87/electro/circuit/part/Position.java | 5 + .../de/ph87/electro/circuit/part/Render.java | 4 +- .../ph87/electro/circuit/part/parts/Poti.java | 3 +- .../circuit/part/parts/RotationMatrix.java | 23 +++++ .../electro/circuit/part/parts/Switch1x1.java | 2 +- .../electro/circuit/part/parts/Switch1x2.java | 2 +- .../circuit/part/parts/SwitchCross.java | 2 +- .../electro/circuit/part/parts/Voltmeter.java | 95 +++++++++++++++++++ .../circuit/part/parts/VoltmeterDto.java | 32 +++++++ .../java/de/ph87/electro/sidebar/Sidebar.java | 1 + 16 files changed, 182 insertions(+), 16 deletions(-) create mode 100644 src/main/java/de/ph87/electro/circuit/part/parts/RotationMatrix.java create mode 100644 src/main/java/de/ph87/electro/circuit/part/parts/Voltmeter.java create mode 100644 src/main/java/de/ph87/electro/circuit/part/parts/VoltmeterDto.java diff --git a/src/main/java/de/ph87/electro/CONFIG.java b/src/main/java/de/ph87/electro/CONFIG.java index c447852..4e6ad6a 100644 --- a/src/main/java/de/ph87/electro/CONFIG.java +++ b/src/main/java/de/ph87/electro/CONFIG.java @@ -6,6 +6,10 @@ import static java.lang.Math.round; public class CONFIG { + public static final double NO_RESISTANCE = 1e-12; + + public static final double MAX_RESISTANCE = 1 / NO_RESISTANCE; + public static boolean SHOW_WIRE_DETAILS = true; public static boolean SHOW_JUNCTION_VOLTAGES = false; diff --git a/src/main/java/de/ph87/electro/circuit/Circuit.java b/src/main/java/de/ph87/electro/circuit/Circuit.java index b96c2eb..d003f4e 100644 --- a/src/main/java/de/ph87/electro/circuit/Circuit.java +++ b/src/main/java/de/ph87/electro/circuit/Circuit.java @@ -5,6 +5,9 @@ import de.ph87.electro.circuit.part.Part; import de.ph87.electro.circuit.part.PartDto; import de.ph87.electro.circuit.part.Position; import de.ph87.electro.circuit.part.junction.Junction; +import de.ph87.electro.circuit.part.parts.Battery; +import de.ph87.electro.circuit.part.parts.Poti; +import de.ph87.electro.circuit.part.parts.Voltmeter; import de.ph87.electro.circuit.wire.Wire; import de.ph87.electro.circuit.wire.WireDto; import lombok.Getter; @@ -19,6 +22,8 @@ import java.util.List; import java.util.Optional; import java.util.stream.Stream; +import static de.ph87.electro.circuit.part.Position.RST; + public class Circuit { @Getter @@ -40,7 +45,7 @@ public class Circuit { public Circuit(final File file, final CircuitDto dto) { created = dto.getCreated(); for (PartDto partDto : dto.getParts()) { - final Part part = Part.of(this, partDto); + final Part part = Part.fromDto(this, partDto); verifyFree(part.getPosition()); parts.add(part); } diff --git a/src/main/java/de/ph87/electro/circuit/CircuitPanelDropTarget.java b/src/main/java/de/ph87/electro/circuit/CircuitPanelDropTarget.java index 8c476f5..86d0971 100644 --- a/src/main/java/de/ph87/electro/circuit/CircuitPanelDropTarget.java +++ b/src/main/java/de/ph87/electro/circuit/CircuitPanelDropTarget.java @@ -37,6 +37,8 @@ public class CircuitPanelDropTarget extends AbstractDropTarget { circuitPanel.getCircuit().addPart(new SwitchCross(circuitPanel.getCircuit(), position)); } else if (data.equals(Poti.class.getSimpleName())) { circuitPanel.getCircuit().addPart(new Poti(circuitPanel.getCircuit(), position)); + } else if (data.equals(Voltmeter.class.getSimpleName())) { + circuitPanel.getCircuit().addPart(new Voltmeter(circuitPanel.getCircuit(), position)); } else { throw new RuntimeException(); } diff --git a/src/main/java/de/ph87/electro/circuit/calculation/Calculation.java b/src/main/java/de/ph87/electro/circuit/calculation/Calculation.java index ee327ea..d49fc66 100644 --- a/src/main/java/de/ph87/electro/circuit/calculation/Calculation.java +++ b/src/main/java/de/ph87/electro/circuit/calculation/Calculation.java @@ -1,5 +1,6 @@ package de.ph87.electro.circuit.calculation; +import de.ph87.electro.CONFIG; import de.ph87.electro.circuit.Circuit; import de.ph87.electro.circuit.part.InnerConnection; import de.ph87.electro.circuit.part.Part; @@ -22,10 +23,6 @@ import static java.lang.Math.max; @Getter public class Calculation { - public static final double NO_RESISTANCE = 1e-12; - - private static final double FULL_ADMITTANCE = 1 / NO_RESISTANCE; - private final List junctions = new ArrayList<>(); private final Set parts = new HashSet<>(); @@ -76,7 +73,7 @@ public class Calculation { private void fromSchematic() { wires.forEach( - wire -> addResistor(wire.getA(), wire.getB(), NO_RESISTANCE) + wire -> addResistor(wire.getA(), wire.getB(), CONFIG.NO_RESISTANCE) ); parts.forEach(part -> { for (final InnerConnection innerConnection : part.getInnerConnections()) { @@ -113,7 +110,7 @@ public class Calculation { private double getCurrent(final Wire wire) { final int indexA = getJunctionIndex(wire.getA()); final int indexB = getJunctionIndex(wire.getB()); - final double conductance = indexA >= 0 && indexB >= 0 ? matrix.getEntry(indexA, indexB) : FULL_ADMITTANCE; + final double conductance = indexA >= 0 && indexB >= 0 ? matrix.getEntry(indexA, indexB) : 1 / CONFIG.NO_RESISTANCE; final double potentialDifference = getPotential(wire.getB()) - getPotential(wire.getA()); return conductance * potentialDifference; } diff --git a/src/main/java/de/ph87/electro/circuit/part/Part.java b/src/main/java/de/ph87/electro/circuit/part/Part.java index c938d3a..14b0281 100644 --- a/src/main/java/de/ph87/electro/circuit/part/Part.java +++ b/src/main/java/de/ph87/electro/circuit/part/Part.java @@ -80,7 +80,7 @@ public abstract class Part { evaluate(); } - public void render() { + public final void render() { render.rect(ZERO, RASTER, RASTER, null, null, PART_BACKGROUND); _labels(); @@ -136,7 +136,7 @@ public abstract class Part { return Collections.emptyList(); } - public static Part of(final Circuit circuit, final PartDto abstractDto) { + public static Part fromDto(final Circuit circuit, final PartDto abstractDto) { return switch (abstractDto) { case final BatteryDto dto -> new Battery(circuit, dto); case final ConnectorCornerDto dto -> new ConnectorCorner(circuit, dto); @@ -146,6 +146,8 @@ public abstract class Part { case final Switch1x1Dto dto -> new Switch1x1(circuit, dto); case final Switch1x2Dto dto -> new Switch1x2(circuit, dto); case final SwitchCrossDto dto -> new SwitchCross(circuit, dto); + case final PotiDto dto -> new Poti(circuit, dto); + case final VoltmeterDto dto -> new Voltmeter(circuit, dto); case null, default -> throw new RuntimeException(); }; } diff --git a/src/main/java/de/ph87/electro/circuit/part/PartDto.java b/src/main/java/de/ph87/electro/circuit/part/PartDto.java index 9e9eae0..e009672 100644 --- a/src/main/java/de/ph87/electro/circuit/part/PartDto.java +++ b/src/main/java/de/ph87/electro/circuit/part/PartDto.java @@ -40,6 +40,7 @@ public abstract class PartDto { case final Switch1x2 part -> new Switch1x2Dto(part); case final SwitchCross part -> new SwitchCrossDto(part); case final Poti part -> new PotiDto(part); + case final Voltmeter part -> new VoltmeterDto(part); case null, default -> throw new RuntimeException(); }; } diff --git a/src/main/java/de/ph87/electro/circuit/part/Position.java b/src/main/java/de/ph87/electro/circuit/part/Position.java index 6e13918..770f2fe 100644 --- a/src/main/java/de/ph87/electro/circuit/part/Position.java +++ b/src/main/java/de/ph87/electro/circuit/part/Position.java @@ -2,6 +2,7 @@ package de.ph87.electro.circuit.part; import lombok.Getter; import lombok.ToString; +import org.apache.commons.math3.linear.RealVector; import java.awt.*; import java.awt.event.MouseEvent; @@ -22,6 +23,10 @@ public final class Position { public final Point raster; + public static Position ABS(final RealVector vector) { + return ABS(vector.getEntry(0), vector.getEntry(1)); + } + public static Position ABS(final double absoluteX, final double absoluteY) { return new Position((int) round(absoluteX), (int) round(absoluteY)); } diff --git a/src/main/java/de/ph87/electro/circuit/part/Render.java b/src/main/java/de/ph87/electro/circuit/part/Render.java index 07a0e90..91525a7 100644 --- a/src/main/java/de/ph87/electro/circuit/part/Render.java +++ b/src/main/java/de/ph87/electro/circuit/part/Render.java @@ -19,11 +19,11 @@ public class Render { private final Graphics2D g = image.createGraphics(); - public void line(final Junction junction0, final Junction junction1, final Color color, final BasicStroke stroke) { + public void line(final Junction junction0, final Junction junction1, final Color color, final Stroke stroke) { line(junction0.getPosition(), junction1.getPosition(), color, stroke); } - public void line(final Position p0, final Position p1, final Color color, final BasicStroke stroke) { + public void line(final Position p0, final Position p1, final Color color, final Stroke stroke) { if (color != null) { g.setColor(color); } diff --git a/src/main/java/de/ph87/electro/circuit/part/parts/Poti.java b/src/main/java/de/ph87/electro/circuit/part/parts/Poti.java index 3978355..e3e54c1 100644 --- a/src/main/java/de/ph87/electro/circuit/part/parts/Poti.java +++ b/src/main/java/de/ph87/electro/circuit/part/parts/Poti.java @@ -13,7 +13,6 @@ import java.awt.*; import java.util.List; import static de.ph87.electro.CONFIG.*; -import static de.ph87.electro.circuit.calculation.Calculation.NO_RESISTANCE; import static java.lang.Math.max; @Getter @@ -38,7 +37,7 @@ public class Poti extends Part { end = addJunction("E", P90, P50); } - protected Poti(final Circuit circuit, final PotiDto dto) { + public Poti(final Circuit circuit, final PotiDto dto) { super(circuit, dto); common = addJunction(dto.getCommon().getName(), P10, P50); middle = addJunction(dto.getMiddle().getName(), P50, P10); diff --git a/src/main/java/de/ph87/electro/circuit/part/parts/RotationMatrix.java b/src/main/java/de/ph87/electro/circuit/part/parts/RotationMatrix.java new file mode 100644 index 0000000..b5a198d --- /dev/null +++ b/src/main/java/de/ph87/electro/circuit/part/parts/RotationMatrix.java @@ -0,0 +1,23 @@ +package de.ph87.electro.circuit.part.parts; + +import org.apache.commons.math3.linear.Array2DRowRealMatrix; +import org.apache.commons.math3.linear.RealMatrix; + +import static java.lang.Math.cos; +import static java.lang.Math.sin; + +public class RotationMatrix extends Array2DRowRealMatrix { + + public static RealMatrix ofDegrees(final double degrees) { + final double radians = degrees * Math.PI / 180.0; + return new RotationMatrix(radians); + } + + private RotationMatrix(final double radians) { + super(new double[][]{ + {cos(radians), -sin(radians)}, + {sin(radians), cos(radians)}, + }); + } + +} diff --git a/src/main/java/de/ph87/electro/circuit/part/parts/Switch1x1.java b/src/main/java/de/ph87/electro/circuit/part/parts/Switch1x1.java index 6c3895d..3c81d5f 100644 --- a/src/main/java/de/ph87/electro/circuit/part/parts/Switch1x1.java +++ b/src/main/java/de/ph87/electro/circuit/part/parts/Switch1x1.java @@ -12,7 +12,7 @@ import java.awt.*; import java.util.List; import static de.ph87.electro.CONFIG.*; -import static de.ph87.electro.circuit.calculation.Calculation.NO_RESISTANCE; +import static de.ph87.electro.CONFIG.NO_RESISTANCE; @Getter @ToString(callSuper = true) diff --git a/src/main/java/de/ph87/electro/circuit/part/parts/Switch1x2.java b/src/main/java/de/ph87/electro/circuit/part/parts/Switch1x2.java index 413dbdc..6524b30 100644 --- a/src/main/java/de/ph87/electro/circuit/part/parts/Switch1x2.java +++ b/src/main/java/de/ph87/electro/circuit/part/parts/Switch1x2.java @@ -11,7 +11,7 @@ import lombok.ToString; import java.util.List; import static de.ph87.electro.CONFIG.*; -import static de.ph87.electro.circuit.calculation.Calculation.NO_RESISTANCE; +import static de.ph87.electro.CONFIG.NO_RESISTANCE; @Getter @ToString(callSuper = true) diff --git a/src/main/java/de/ph87/electro/circuit/part/parts/SwitchCross.java b/src/main/java/de/ph87/electro/circuit/part/parts/SwitchCross.java index 6f4825a..f5e59a0 100644 --- a/src/main/java/de/ph87/electro/circuit/part/parts/SwitchCross.java +++ b/src/main/java/de/ph87/electro/circuit/part/parts/SwitchCross.java @@ -11,7 +11,7 @@ import lombok.ToString; import java.util.List; import static de.ph87.electro.CONFIG.*; -import static de.ph87.electro.circuit.calculation.Calculation.NO_RESISTANCE; +import static de.ph87.electro.CONFIG.NO_RESISTANCE; @Getter @ToString(callSuper = true) diff --git a/src/main/java/de/ph87/electro/circuit/part/parts/Voltmeter.java b/src/main/java/de/ph87/electro/circuit/part/parts/Voltmeter.java new file mode 100644 index 0000000..a9ff6c6 --- /dev/null +++ b/src/main/java/de/ph87/electro/circuit/part/parts/Voltmeter.java @@ -0,0 +1,95 @@ +package de.ph87.electro.circuit.part.parts; + +import de.ph87.electro.circuit.Circuit; +import de.ph87.electro.circuit.part.InnerConnection; +import de.ph87.electro.circuit.part.Part; +import de.ph87.electro.circuit.part.PartDto; +import de.ph87.electro.circuit.part.Position; +import de.ph87.electro.circuit.part.junction.Junction; +import lombok.Getter; +import org.apache.commons.math3.linear.ArrayRealVector; +import org.apache.commons.math3.linear.RealMatrix; +import org.apache.commons.math3.linear.RealVector; + +import java.awt.*; +import java.util.List; + +import static de.ph87.electro.CONFIG.*; +import static de.ph87.electro.circuit.part.Position.ABS; + +@Getter +public class Voltmeter extends Part { + + private static final RealVector ANCHOR = new ArrayRealVector(new double[]{P50, P50}); + + private static final RealVector FINGER = new ArrayRealVector(new double[]{P90, P50}).subtract(ANCHOR); + + private static final Stroke SCALE_STROKE = new BasicStroke(1); + + private static final double DEGREES_TOTAL = 90.0; + + private static final double DEGREES_ROTATE = -135.0; + + private final Junction a; + + private final Junction b; + + private double min = -3; + + private double max = +3; + + private double voltage; + + private RealVector tip; + + public Voltmeter(final Circuit circuit, final Position position) { + super(circuit, "Voltmeter", position); + a = addJunction("A", P10, P50); + b = addJunction("B", P90, P50); + postCalculate(); // TODO remove + } + + public Voltmeter(final Circuit circuit, final PartDto dto) { + super(circuit, dto); + a = addJunction("A", P10, P50); + b = addJunction("B", P90, P50); + postCalculate(); // TODO remove + } + + public void setMin(final double min) { + this.min = min; + evaluate(); + } + + public void setMax(final double max) { + this.max = max; + evaluate(); + } + + @Override + public void postCalculate() { + voltage = !Double.isNaN(b.getVoltage()) && !Double.isNaN(a.getVoltage()) ? b.getVoltage() - a.getVoltage() : 0.0; + final double ratio = (voltage - min) / (max - min); + final double degrees = DEGREES_TOTAL * ratio + DEGREES_ROTATE; + final RealMatrix rotate = RotationMatrix.ofDegrees(degrees); + final RealVector rotatedFinger = rotate.operate(FINGER); + tip = ANCHOR.add(rotatedFinger); + } + + @Override + public void _render() { + render.clockwise(getOrientation()); + render.line(ABS(ANCHOR), ABS(tip), Color.BLACK, SCALE_STROKE); + } + + @Override + protected void _labels() { + render.textCenter(LABEL_FONT, "%.2f V".formatted(voltage), P50, P75, Color.BLACK); + } + + @Override + public List getInnerConnections() { + return List.of(new InnerConnection(a, b, MAX_RESISTANCE)); + } + +} diff --git a/src/main/java/de/ph87/electro/circuit/part/parts/VoltmeterDto.java b/src/main/java/de/ph87/electro/circuit/part/parts/VoltmeterDto.java new file mode 100644 index 0000000..784c1f3 --- /dev/null +++ b/src/main/java/de/ph87/electro/circuit/part/parts/VoltmeterDto.java @@ -0,0 +1,32 @@ +package de.ph87.electro.circuit.part.parts; + +import de.ph87.electro.circuit.part.PartDto; +import de.ph87.electro.circuit.part.junction.JunctionDto; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +@Getter +@ToString +@NoArgsConstructor +public class VoltmeterDto extends PartDto { + + private JunctionDto a; + + private JunctionDto b; + + @Setter + private double min; + + @Setter + private double max; + + public VoltmeterDto(final Voltmeter voltmeter) { + a = new JunctionDto(voltmeter.getA()); + b = new JunctionDto(voltmeter.getB()); + min = voltmeter.getMin(); + max = voltmeter.getMax(); + } + +} diff --git a/src/main/java/de/ph87/electro/sidebar/Sidebar.java b/src/main/java/de/ph87/electro/sidebar/Sidebar.java index a402d66..7ab860b 100644 --- a/src/main/java/de/ph87/electro/sidebar/Sidebar.java +++ b/src/main/java/de/ph87/electro/sidebar/Sidebar.java @@ -44,6 +44,7 @@ public class Sidebar extends JPanel { addPart(new Switch1x2(null, Position.ZERO)); addPart(new SwitchCross(null, Position.ZERO)); addPart(new Poti(null, Position.ZERO)); + addPart(new Voltmeter(null, Position.ZERO)); } @SuppressWarnings("UnusedReturnValue")