Compare commits

...

11 Commits

30 changed files with 548 additions and 440 deletions

View File

@ -11,16 +11,12 @@ public class CONFIG {
public static final double MAX_RESISTANCE = 1 / NO_RESISTANCE; public static final double MAX_RESISTANCE = 1 / NO_RESISTANCE;
public static boolean SHOW_WIRE_DETAILS = true;
public static boolean SHOW_NODE_VOLTAGES = false;
public static boolean SHOW_NODE_NAMES = false;
public static final double VOLTAGE_HIGH_MIN = 0.1; public static final double VOLTAGE_HIGH_MIN = 0.1;
public static final int RASTER = 200; public static final int RASTER = 200;
public static final int SUB_RASTER = RASTER / 5;
public static final int P03 = (int) round(0.03 * RASTER); public static final int P03 = (int) round(0.03 * RASTER);
public static final int P05 = (int) round(0.05 * RASTER); public static final int P05 = (int) round(0.05 * RASTER);
@ -81,6 +77,12 @@ public class CONFIG {
public static final BasicStroke SWITCH_STROKE = new BasicStroke(15); public static final BasicStroke SWITCH_STROKE = new BasicStroke(15);
public static boolean SHOW_WIRE_DETAILS = false;
public static boolean SHOW_NODE_VOLTAGES = false;
public static boolean SHOW_NODE_NAMES = false;
public static Point ALIGN(final Point position) { public static Point ALIGN(final Point position) {
return new Point(position.x / RASTER * RASTER, position.y / RASTER * RASTER); return new Point(position.x / RASTER * RASTER, position.y / RASTER * RASTER);
} }

View File

@ -36,6 +36,11 @@ public class Window extends JFrame {
setVisible(true); setVisible(true);
} }
public static void main(String[] args) {
final Window window = new Window();
window.setCircuit(Demos.potiAndVoltmeter());
}
public void setCircuit(final Circuit circuit) { public void setCircuit(final Circuit circuit) {
circuitPanel.setCircuit(circuit); circuitPanel.setCircuit(circuit);
} }
@ -51,9 +56,4 @@ public class Window extends JFrame {
setLocation(screenBounds.x, screenBounds.y); setLocation(screenBounds.x, screenBounds.y);
} }
public static void main(String[] args) {
final Window window = new Window();
window.setCircuit(Demos.potiAndVoltmeter());
}
} }

View File

@ -9,38 +9,42 @@ import de.ph87.electro.circuit.wire.WireDto;
import lombok.Getter; import lombok.Getter;
import lombok.NonNull; import lombok.NonNull;
import lombok.Setter; import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import java.awt.*; import java.awt.*;
import java.io.File; import java.io.File;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.*;
import java.util.stream.Stream; import java.util.stream.Stream;
import static de.ph87.electro.CONFIG.ALIGN; import static de.ph87.electro.CONFIG.ALIGN;
@Slf4j
public class Circuit { public class Circuit {
@Getter
private final String created;
@Getter
private final List<Part> parts = new ArrayList<>();
private final List<Wire> wires = new ArrayList<>();
@Setter @Setter
@Getter @Getter
private File file = null; private File file = null;
@NonNull
@Getter
private final String created;
@NonNull
@Getter
private final List<Part> parts = new ArrayList<>();
@NonNull
private final List<Wire> wires = new ArrayList<>();
public Circuit() { public Circuit() {
this.created = ZonedDateTime.now().format(DateTimeFormatter.ISO_OFFSET_DATE_TIME); this.created = ZonedDateTime.now().format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
public Circuit(final File file, final CircuitDto dto) { public Circuit(@NonNull final File file, @NonNull final CircuitDto dto) {
this.created = dto.getCreated(); this.created = dto.getCreated();
for (PartDto partDto : dto.getParts()) { for (PartDto partDto : dto.getParts()) {
final Part part = Part.fromDto(partDto); final Part part = Part.fromDto(partDto);
@ -54,13 +58,23 @@ public class Circuit {
} }
} }
public Wire connect(final Node a, final Node b) { public void connect(@NonNull final Node a, @NonNull final Node b) {
if (a == b) {
log.warn("No Wire created: Nodes are identical: {}", a);
return;
}
final Optional<Wire> duplicate = a.getWires().stream().filter(w -> w.getOpposite(a) == b).findFirst();
if (duplicate.isPresent()) {
log.warn("No Wire created: Between these nodes, a wire already exists: {}", duplicate);
return;
}
final Wire wire = new Wire(a, b); final Wire wire = new Wire(a, b);
wires.add(wire); wires.add(wire);
return wire; log.info("Wire CREATED: {}", wire);
evaluate();
} }
public void disconnect(final Wire wire) { public void disconnect(@NonNull final Wire wire) {
if (!wires.contains(wire)) { if (!wires.contains(wire)) {
throw new RuntimeException(); throw new RuntimeException();
} }
@ -69,7 +83,7 @@ public class Circuit {
wire.getB().getWires().remove(wire); wire.getB().getWires().remove(wire);
} }
public <T extends Part> T addPart(final T part) { public <T extends Part> T addPart(@NonNull final T part) {
if (parts.contains(part)) { if (parts.contains(part)) {
throw new RuntimeException(); throw new RuntimeException();
} }
@ -78,7 +92,21 @@ public class Circuit {
return part; return part;
} }
public void removePart(final Part part) { public void removePart(@NonNull final Part part) {
for (final Node node : part.getNodes()) {
Node origin = null;
Set<Node> destinations = new HashSet<>();
for (final Wire wire : node.getWires()) {
if (origin == null) {
origin = wire.getOpposite(node);
} else {
destinations.add(wire.getOpposite(node));
}
}
for (final Node destination : destinations) {
connect(origin, destination);
}
}
if (parts.remove(part)) { if (parts.remove(part)) {
part.getNodes().stream().flatMap(node -> node.getWires().stream()).toList().forEach(this::disconnect); // jep, first toList(), then forEach (due to concurrent modification) part.getNodes().stream().flatMap(node -> node.getWires().stream()).toList().forEach(this::disconnect); // jep, first toList(), then forEach (due to concurrent modification)
} else { } else {
@ -86,13 +114,13 @@ public class Circuit {
} }
} }
public void verifyFree(final Point position) { public void verifyFree(@NonNull final Point position) {
if (isOccupied(position)) { if (isOccupied(position)) {
throw new RuntimeException(); throw new RuntimeException();
} }
} }
public boolean isOccupied(final Point position) { public boolean isOccupied(@NonNull final Point position) {
final Point aligned = ALIGN(position); final Point aligned = ALIGN(position);
return parts.stream().anyMatch(part -> part.getPosition().equals(aligned)); return parts.stream().anyMatch(part -> part.getPosition().equals(aligned));
} }
@ -109,16 +137,15 @@ public class Circuit {
return parts.size(); return parts.size();
} }
public Optional<Part> findPartByPosition(final Point position) { public Optional<Part> findPartByPosition(@NonNull final Point position) {
final Point aligned = ALIGN(position); return streamParts().filter(p -> p.intersects(position)).findFirst();
return streamParts().filter(p -> p.getPosition().equals(aligned)).findFirst();
} }
public Optional<Node> findNodeByUuid(@NonNull final String nodeUuid) { public Optional<? extends Node> findNodeByUuid(@NonNull final String nodeUuid) {
return parts.stream().map(part -> part.findNodeByUuid(nodeUuid)).filter(Optional::isPresent).map(Optional::get).findFirst(); return parts.stream().map(part -> part.findNodeByUuid(nodeUuid)).filter(Optional::isPresent).map(Optional::get).findFirst();
} }
public Optional<Wire> findWireByPosition(final Point position) { public Optional<Wire> findWireByPosition(@NonNull final Point position) {
return wires.stream().filter(wire -> wire.intersects(position)).findFirst(); return wires.stream().filter(wire -> wire.intersects(position)).findFirst();
} }

View File

@ -69,7 +69,7 @@ public class CircuitPainter {
for (Part part : circuit.getParts()) { for (Part part : circuit.getParts()) {
for (final Node node : part.getNodes()) { for (final Node node : part.getNodes()) {
final Point absolute = node.getAbsolute(); final Point absolute = node.getPosition();
if (SHOW_NODE_NAMES) { if (SHOW_NODE_NAMES) {
int offsetY = third; int offsetY = third;
if (SHOW_NODE_VOLTAGES) { if (SHOW_NODE_VOLTAGES) {

View File

@ -40,7 +40,7 @@ public class CircuitPanel extends JPanel {
public void paint(final Graphics graphics) { public void paint(final Graphics graphics) {
final Graphics2D g = (Graphics2D) graphics; final Graphics2D g = (Graphics2D) graphics;
CircuitPainter.draw(g, circuit, getWidth(), getHeight()); CircuitPainter.draw(g, circuit, getWidth(), getHeight());
mouseAdapter.drawHover(g); mouseAdapter.drawHovers(g);
mouseAdapter.drawDrag(g); mouseAdapter.drawDrag(g);
} }

View File

@ -22,12 +22,6 @@ public class CircuitPanelDropTarget extends AbstractDropTarget {
final Point aligned = ALIGN(point); final Point aligned = ALIGN(point);
if (data.equals(Battery.class.getSimpleName())) { if (data.equals(Battery.class.getSimpleName())) {
circuitPanel.getCircuit().addPart(new Battery(aligned)); circuitPanel.getCircuit().addPart(new Battery(aligned));
} else if (data.equals(ConnectorCorner.class.getSimpleName())) {
circuitPanel.getCircuit().addPart(new ConnectorCorner(aligned));
} else if (data.equals(ConnectorEdge.class.getSimpleName())) {
circuitPanel.getCircuit().addPart(new ConnectorEdge(aligned));
} else if (data.equals(ConnectorMiddle.class.getSimpleName())) {
circuitPanel.getCircuit().addPart(new ConnectorMiddle(aligned));
} else if (data.equals(Light.class.getSimpleName())) { } else if (data.equals(Light.class.getSimpleName())) {
circuitPanel.getCircuit().addPart(new Light(aligned)); circuitPanel.getCircuit().addPart(new Light(aligned));
} else if (data.equals(Switch1x1.class.getSimpleName())) { } else if (data.equals(Switch1x1.class.getSimpleName())) {

View File

@ -2,7 +2,9 @@ package de.ph87.electro.circuit;
import de.ph87.electro.circuit.part.Part; import de.ph87.electro.circuit.part.Part;
import de.ph87.electro.circuit.part.node.Node; import de.ph87.electro.circuit.part.node.Node;
import de.ph87.electro.circuit.part.parts.ConnectorSub;
import de.ph87.electro.circuit.wire.Wire; import de.ph87.electro.circuit.wire.Wire;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import java.awt.*; import java.awt.*;
@ -25,66 +27,112 @@ class CircuitPanelMouseAdapter extends MouseAdapter {
private Point dragging = null; private Point dragging = null;
CircuitPanelMouseAdapter(final CircuitPanel circuitPanel) { private Part part2 = null;
private Node node2 = null;
private Point draggingSub;
CircuitPanelMouseAdapter(@NonNull final CircuitPanel circuitPanel) {
this.circuitPanel = circuitPanel; this.circuitPanel = circuitPanel;
circuitPanel.addMouseListener(this); circuitPanel.addMouseListener(this);
circuitPanel.addMouseMotionListener(this); circuitPanel.addMouseMotionListener(this);
} }
@Override /* HOVER ---------------------------------------------------------------------------------------- */
public void mouseClicked(final MouseEvent event) {
hoverUpdate(event);
if (wire != null) { @Override
switch (event.getButton()) { public void mouseMoved(@NonNull final MouseEvent event) {
case BUTTON3: findHovers(event);
circuitPanel.getCircuit().disconnect(wire); circuitPanel.repaint();
circuitPanel.getCircuit().evaluate(); }
wire = null;
break; private void findHovers(@NonNull final MouseEvent event) {
final Point position = event.getPoint();
part = circuitPanel.getCircuit().findPartByPosition(position).orElse(null);
node = part != null ? part.findNodeByPosition(position).orElse(null) : null;
if (node != null) {
part = null;
if (wire != null) {
wire.setGhost(false);
} }
} wire = null;
if (part != null) {
switch (event.getButton()) {
case BUTTON1:
part.action();
circuitPanel.getCircuit().evaluate();
break;
case BUTTON2:
part.rotate();
break;
case BUTTON3:
circuitPanel.getCircuit().removePart(part);
circuitPanel.getCircuit().evaluate();
part = null;
break;
}
}
circuitPanel.repaint();
}
@Override
public void mouseMoved(final MouseEvent event) {
hoverUpdate(event);
circuitPanel.repaint();
}
@Override
public void mouseDragged(final MouseEvent event) {
if (dragging == null) {
hoverUpdate(event);
}
dragging = event.getPoint();
circuitPanel.repaint();
}
@Override
public void mouseReleased(final MouseEvent event) {
if (dragging == null) {
return; return;
} }
wire = circuitPanel.getCircuit().findWireByPosition(position).orElse(null);
if (wire != null) {
part = null;
}
}
public void drawHovers(@NonNull final Graphics2D g) {
drawPartHover(g);
drawNodeHover(g, node);
drawNodeHover(g, node2);
drawWireHover(g);
}
private void drawPartHover(@NonNull final Graphics2D g) {
if (part == null) {
return;
}
g.setColor(NODE_HOVER_BORDER_COLOR);
g.setStroke(HOVER_STROKE);
g.drawRect(part.getPosition().x, part.getPosition().y, RASTER, RASTER);
}
private void drawNodeHover(@NonNull final Graphics2D g, final Node n) {
if (n == null) {
return;
}
final Point absolute = n.getPosition();
g.setColor(n.getColor());
g.fillArc(absolute.x - NODE_RADIUS_HOVER, absolute.y - NODE_RADIUS_HOVER, 2 * NODE_RADIUS_HOVER, 2 * NODE_RADIUS_HOVER, 0, 360);
g.setColor(NODE_HOVER_BORDER_COLOR);
g.setStroke(HOVER_STROKE);
g.drawArc(absolute.x - NODE_RADIUS_HOVER, absolute.y - NODE_RADIUS_HOVER, 2 * NODE_RADIUS_HOVER, 2 * NODE_RADIUS_HOVER, 0, 360);
}
private void drawWireHover(@NonNull final Graphics2D g) {
if (wire == null || wire.isGhost()) {
return;
}
final Point aa = wire.getA().getPosition();
final Point bb = wire.getB().getPosition();
g.setColor(WIRE_HOVER_COLOR_BACK);
g.setStroke(WIRE_HOVER_STROKE_BACK);
g.drawLine(aa.x, aa.y, bb.x, bb.y);
g.setColor(wire.getA().getColor());
g.setStroke(WIRE_HOVER_STROKE);
g.drawLine(aa.x, aa.y, bb.x, bb.y);
}
/* DRAG ----------------------------------------------------------------------------------------- */
@Override
public void mouseDragged(@NonNull final MouseEvent event) {
if (dragging == null) {
findHovers(event);
if (wire != null) {
wire.setGhost(true);
}
} else {
part2 = circuitPanel.getCircuit().findPartByPosition(dragging).orElse(null);
node2 = part2 == null ? null : part2.findNodeByPosition(dragging).orElse(null);
}
dragging = event.getPoint();
draggingSub = new Point(
dragging.x / SUB_RASTER * SUB_RASTER,
dragging.y / SUB_RASTER * SUB_RASTER
);
if (part != null) { if (part != null) {
final Point aligned = ALIGN(event.getPoint()); final Point aligned = ALIGN(event.getPoint());
@ -96,80 +144,184 @@ class CircuitPanelMouseAdapter extends MouseAdapter {
} }
} }
circuitPanel.repaint();
}
@Override
public void mouseReleased(@NonNull final MouseEvent event) {
if (dragging == null) {
return;
}
if (node != null) { if (node != null) {
final Node source = node; final Node source = node;
hoverUpdate(event); findHovers(event);
if (node != null) { if (node != null) {
final Wire wire = circuitPanel.getCircuit().connect(source, node); circuitPanel.getCircuit().connect(source, node);
log.info("Wire CREATED: {}", wire);
circuitPanel.getCircuit().evaluate(); circuitPanel.getCircuit().evaluate();
} else { } else {
log.info("No Wire created: No destination node found!"); log.info("No Wire created: No destination node found!");
} }
} }
if (wire != null) {
wireBend();
}
terminateMouseAction();
}
private void wireBend() {
if (node2 != null) {
wireBendToExistingNode();
} else {
wireBendToNewConnector();
}
}
private void wireBendToExistingNode() {
if (wire == null) {
throw new RuntimeException();
}
if (node2 == wire.getA() || node2 == wire.getB()) {
log.info("Wire-bending-destination node is already part of that wire: wire={}, node={}", wire, node2);
return;
}
connectIntermediate(node2);
}
private void wireBendToNewConnector() {
final ConnectorSub newConnector = circuitPanel.getCircuit().addPart(new ConnectorSub(draggingSub));
connectIntermediate(newConnector.getNode());
}
private void connectIntermediate(@NonNull final Node intermediate) {
circuitPanel.getCircuit().connect(wire.getA(), intermediate);
circuitPanel.getCircuit().connect(intermediate, wire.getB());
circuitPanel.getCircuit().disconnect(wire);
circuitPanel.getCircuit().evaluate();
}
public void drawDrag(@NonNull final Graphics2D g) {
drawPartDrag(g);
drawNodeDrag(g);
drawWireDrag(g);
}
private void drawWireDrag(@NonNull final Graphics2D g) {
if (dragging == null || wire == null) {
return;
}
final Point vertex;
if (part2 != null) {
if (node2 == null) {
// hovering part, but not a node
vertex = dragging;
} else {
// hovering existing node
vertex = node2.getPosition();
}
} else {
// hovering free space (will create a new connector in sub-raster)
g.setColor(PART_HOVER_COLOR);
g.fillRect(draggingSub.x, draggingSub.y, SUB_RASTER, SUB_RASTER);
vertex = new Point(
draggingSub.x + SUB_RASTER / 2,
draggingSub.y + SUB_RASTER / 2
);
}
// wire
g.setColor(wire.getA().getColor());
g.setStroke(WIRE_STROKE);
g.drawLine(wire.getA().getPosition().x, wire.getA().getPosition().y, vertex.x, vertex.y);
g.drawLine(vertex.x, vertex.y, wire.getB().getPosition().x, wire.getB().getPosition().y);
}
private void drawNodeDrag(@NonNull final Graphics2D g) {
if (dragging == null || node == null) {
return;
}
g.setColor(node.getColor());
g.setStroke(WIRE_STROKE);
final Point absolute = node.getPosition();
g.drawLine(absolute.x, absolute.y, dragging.x, dragging.y);
}
private void drawPartDrag(@NonNull final Graphics2D g) {
if (dragging == null || part == null) {
return;
}
g.setColor(PART_HOVER_COLOR);
g.fillRect(dragging.x - P50, dragging.y - P50, RASTER, RASTER);
}
/* CLICK ---------------------------------------------------------------------------------------- */
@Override
public void mouseClicked(@NonNull final MouseEvent event) {
findHovers(event);
if (mouseClickedAction(event)) {
terminateMouseAction();
}
}
private boolean mouseClickedAction(@NonNull final MouseEvent event) {
if (node != null && node.getPart() instanceof ConnectorSub) {
switch (event.getButton()) {
case BUTTON3:
circuitPanel.getCircuit().removePart(node.getPart());
circuitPanel.getCircuit().evaluate();
return true;
}
}
if (wire != null) {
switch (event.getButton()) {
case BUTTON3:
circuitPanel.getCircuit().disconnect(wire);
circuitPanel.getCircuit().evaluate();
return true;
}
}
if (part != null) {
switch (event.getButton()) {
case BUTTON1:
part.action();
circuitPanel.getCircuit().evaluate();
return true;
case BUTTON2:
part.rotate();
return true;
case BUTTON3:
circuitPanel.getCircuit().removePart(part);
circuitPanel.getCircuit().evaluate();
part = null;
return true;
}
}
return false;
}
/* COMMON --------------------------------------------------------------------------------------- */
private void terminateMouseAction() {
if (wire != null) {
wire.setGhost(false);
}
part = null;
node = null;
wire = null;
part2 = null;
node2 = null;
dragging = null; dragging = null;
draggingSub = null;
circuitPanel.repaint(); circuitPanel.repaint();
} }
private void hoverUpdate(final MouseEvent event) {
final Point position = event.getPoint();
part = circuitPanel.getCircuit().findPartByPosition(position).orElse(null);
node = part != null ? part.findNodeByPosition(position).orElse(null) : null;
if (node != null) {
part = null;
wire = null;
return;
}
wire = circuitPanel.getCircuit().findWireByPosition(position).orElse(null);
if (wire != null) {
part = null;
}
}
public void drawHover(final Graphics2D g) {
if (part != null) {
g.setColor(NODE_HOVER_BORDER_COLOR);
g.setStroke(HOVER_STROKE);
g.drawRect(part.getPosition().x, part.getPosition().y, RASTER, RASTER);
}
if (node != null) {
final Point absolute = node.getAbsolute();
g.setColor(node.getColor());
g.fillArc(absolute.x - NODE_RADIUS_HOVER, absolute.y - NODE_RADIUS_HOVER, 2 * NODE_RADIUS_HOVER, 2 * NODE_RADIUS_HOVER, 0, 360);
g.setColor(NODE_HOVER_BORDER_COLOR);
g.setStroke(HOVER_STROKE);
g.drawArc(absolute.x - NODE_RADIUS_HOVER, absolute.y - NODE_RADIUS_HOVER, 2 * NODE_RADIUS_HOVER, 2 * NODE_RADIUS_HOVER, 0, 360);
}
if (wire != null) {
final Point aa = wire.getA().getAbsolute();
final Point bb = wire.getB().getAbsolute();
g.setColor(WIRE_HOVER_COLOR_BACK);
g.setStroke(WIRE_HOVER_STROKE_BACK);
g.drawLine(aa.x, aa.y, bb.x, bb.y);
g.setColor(wire.getA().getColor());
g.setStroke(WIRE_HOVER_STROKE);
g.drawLine(aa.x, aa.y, bb.x, bb.y);
}
}
public void drawDrag(final Graphics2D g) {
if (dragging != null) {
if (part != null) {
g.setColor(PART_HOVER_COLOR);
g.fillRect(dragging.x - P50, dragging.y - P50, RASTER, RASTER);
}
if (node != null) {
g.setColor(node.getColor());
g.setStroke(WIRE_STROKE);
final Point absolute = node.getAbsolute();
g.drawLine(absolute.x, absolute.y, dragging.x, dragging.y);
}
}
}
} }

View File

@ -12,10 +12,7 @@ import lombok.NonNull;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.math3.linear.*; import org.apache.commons.math3.linear.*;
import java.util.ArrayList; import java.util.*;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static java.lang.Math.max; import static java.lang.Math.max;
@ -35,20 +32,6 @@ public class Calculation {
private RealVector potentials = null; private RealVector potentials = null;
public static List<Calculation> calculate(final Circuit circuit) {
final List<Calculation> calculations = new ArrayList<>();
final List<Battery> batteries = new ArrayList<>(circuit.streamParts().flatMap(Battery::filterCast).toList());
while (!batteries.isEmpty()) {
final Battery pivot = batteries.removeFirst();
final Set<Node> connectedNodes = new HashSet<>();
pivot.getPlus().collectConnectedNodes(connectedNodes);
pivot.getMinus().collectConnectedNodes(connectedNodes);
connectedNodes.stream().map(Node::getOwner).flatMap(Battery::filterCast).forEach(batteries::remove);
calculations.add(new Calculation(connectedNodes, pivot));
}
return calculations;
}
public Calculation(final int numNodes) { public Calculation(final int numNodes) {
matrix = new Array2DRowRealMatrix(numNodes, numNodes); matrix = new Array2DRowRealMatrix(numNodes, numNodes);
currents = new ArrayRealVector(numNodes); currents = new ArrayRealVector(numNodes);
@ -56,7 +39,9 @@ public class Calculation {
private Calculation(final Set<Node> connectedNodes, final Battery pivot) { private Calculation(final Set<Node> connectedNodes, final Battery pivot) {
for (final Node node : connectedNodes) { for (final Node node : connectedNodes) {
parts.add(node.getOwner()); if (node instanceof final Node partNode) {
parts.add(partNode.getPart());
}
if (!nodes.contains(node) && node != pivot.getMinus()) { if (!nodes.contains(node) && node != pivot.getMinus()) {
// pivot.minus is GND and cannot be part of the matrix (linear dependency) // pivot.minus is GND and cannot be part of the matrix (linear dependency)
this.nodes.add(node); this.nodes.add(node);
@ -71,6 +56,20 @@ public class Calculation {
toSchematic(); toSchematic();
} }
public static List<Calculation> calculate(final Circuit circuit) {
final List<Calculation> calculations = new ArrayList<>();
final List<Battery> batteries = new ArrayList<>(circuit.streamParts().flatMap(Battery::filterCast).toList());
while (!batteries.isEmpty()) {
final Battery pivot = batteries.removeFirst();
final Set<Node> connectedNodes = new HashSet<>();
pivot.getPlus().collectConnectedNodes(connectedNodes);
pivot.getMinus().collectConnectedNodes(connectedNodes);
connectedNodes.stream().map(Node::getPart).flatMap(Battery::filterCast).forEach(batteries::remove);
calculations.add(new Calculation(connectedNodes, pivot));
}
return calculations;
}
private void fromSchematic() { private void fromSchematic() {
wires.forEach( wires.forEach(
wire -> addResistor(wire.getA(), wire.getB(), CONFIG.NO_RESISTANCE) wire -> addResistor(wire.getA(), wire.getB(), CONFIG.NO_RESISTANCE)
@ -191,12 +190,12 @@ public class Calculation {
@Getter @Getter
private static final class Format { private static final class Format {
private final String format;
private int integer; private int integer;
private int decimal; private int decimal;
private final String format;
public Format(final RealMatrix matrix) { public Format(final RealMatrix matrix) {
for (int r = 0; r < matrix.getRowDimension(); r++) { for (int r = 0; r < matrix.getRowDimension(); r++) {
for (int c = 0; c < matrix.getColumnDimension(); c++) { for (int c = 0; c < matrix.getColumnDimension(); c++) {

View File

@ -14,92 +14,121 @@ import java.awt.geom.Point2D;
import java.util.List; import java.util.List;
import java.util.*; import java.util.*;
import static de.ph87.electro.CONFIG.*; import static de.ph87.electro.CONFIG.P50;
import static de.ph87.electro.CONFIG.PART_BACK_COLOR;
import static java.lang.Math.round; import static java.lang.Math.round;
@Getter @Getter
@ToString(onlyExplicitlyIncluded = true) @ToString(onlyExplicitlyIncluded = true)
public abstract class Part { public abstract class Part {
@NonNull
@ToString.Include @ToString.Include
private final String uuid; private final String uuid;
@NonNull
private final List<Node> nodes = new ArrayList<>();
@NonNull
protected Orientation orientation = Orientation.R0;
@NonNull
@Setter @Setter
@ToString.Include @ToString.Include
private String name; private String name;
@NonNull
private Point position; private Point position;
protected Orientation orientation = Orientation.R0;
private final List<Node> nodes = new ArrayList<>();
@NonNull @NonNull
private AffineTransform transform; private AffineTransform transform = new AffineTransform();
protected Part(final String name, final Point position) { protected Part(@NonNull final String name, @NonNull final Point position) {
this.uuid = UUID.randomUUID().toString(); this.uuid = UUID.randomUUID().toString();
this.name = name; this.name = name;
this.position = position; this.position = position;
this.transform = newTransform(); updateTransform();
} }
protected Part(final PartDto dto) { protected Part(@NonNull final PartDto dto) {
this.uuid = dto.getUuid(); this.uuid = dto.getUuid();
this.name = dto.getName(); this.name = dto.getName();
this.orientation = dto.getOrientation(); this.orientation = dto.getOrientation();
this.position = dto.getPosition(); this.position = dto.getPosition();
this.transform = newTransform(); updateTransform();
} }
private AffineTransform newTransform() { @NonNull
final AffineTransform transform = new AffineTransform(); public static Part fromDto(@NonNull final PartDto abstractDto) {
transform.translate(position.x, position.y); return switch (abstractDto) {
transform.rotate(orientation.getRadians(), P50, P50); case final BatteryDto dto -> new Battery(dto);
return transform; case final LightDto dto -> new Light(dto);
case final ConnectorSubDto dto -> new ConnectorSub(dto);
case final Switch1x1Dto dto -> new Switch1x1(dto);
case final Switch1x2Dto dto -> new Switch1x2(dto);
case final SwitchCrossDto dto -> new SwitchCross(dto);
case final PotiDto dto -> new Poti(dto);
case final VoltmeterDto dto -> new Voltmeter(dto);
case null, default -> throw new RuntimeException();
};
} }
public Point transform(final Point point) { @NonNull
final Point2D result = transform.transform(point, new Point2D.Double()); public abstract Point getSize();
return new Point((int) round(result.getX()), (int) round(result.getY()));
}
protected Node addNode(final String name, final int x, final int y) { public void setPosition(@NonNull final Point position) {
return addNode(new Node(this, name, new Point(x, y)));
}
protected Node addNode(final NodeDto dto, final int x, final int y) {
return addNode(new Node(this, dto, new Point(x, y)));
}
private Node addNode(final Node node) {
nodes.add(node);
return node;
}
public void setPosition(final Point position) {
this.position = position; this.position = position;
this.transform = newTransform(); updateTransform();
} }
public void rotate() { public void rotate() {
this.orientation = orientation.clockwise(); this.orientation = orientation.clockwise();
this.transform = newTransform(); updateTransform();
} }
public final void draw(final Graphics2D g) { @NonNull
private void updateTransform() {
transform = new AffineTransform();
transform.translate(position.x, position.y);
transform.rotate(orientation.getRadians(), P50, P50);
nodes.forEach(Node::positionChanged);
}
@NonNull
public Point transform(@NonNull final Point point) {
final Point2D result = transform.transform(point, new Point2D.Double());
return new Point((int) round(result.getX()), (int) round(result.getY()));
}
@NonNull
protected Node addNode(@NonNull final String name, final int x, final int y) {
return addNode(new Node(this, name, new Point(x, y)));
}
@NonNull
protected Node addNode(@NonNull final NodeDto dto, final int x, final int y) {
return addNode(new Node(this, dto, new Point(x, y)));
}
@NonNull
private Node addNode(@NonNull final Node node) {
nodes.add(node);
return node;
}
public final void draw(@NonNull final Graphics2D g) {
g.setColor(PART_BACK_COLOR); g.setColor(PART_BACK_COLOR);
g.fillRect(0, 0, RASTER, RASTER); g.fillRect(0, 0, getSize().x, getSize().y);
_render(g); _render(g);
nodes.forEach(node -> node.draw(g)); nodes.forEach(node -> node.draw(g));
_labels(g); _labels(g);
} }
protected void _render(final Graphics2D g) { protected void _render(@NonNull final Graphics2D g) {
// - // -
} }
protected void _labels(final Graphics2D g) { protected void _labels(@NonNull final Graphics2D g) {
// - // -
} }
@ -111,32 +140,24 @@ public abstract class Part {
// - // -
} }
@NonNull
public List<InnerConnection> getInnerConnections() { public List<InnerConnection> getInnerConnections() {
return Collections.emptyList(); return Collections.emptyList();
} }
public Optional<Node> findNodeByUuid(final String nodeUuid) { @NonNull
public Optional<Node> findNodeByUuid(@NonNull final String nodeUuid) {
return nodes.stream().filter(node -> node.getUuid().equals(nodeUuid)).findFirst(); return nodes.stream().filter(node -> node.getUuid().equals(nodeUuid)).findFirst();
} }
public Optional<Node> findNodeByPosition(final Point position) { @NonNull
public Optional<Node> findNodeByPosition(@NonNull final Point position) {
return nodes.stream().filter(node -> node.intersects(position)).findFirst(); return nodes.stream().filter(node -> node.intersects(position)).findFirst();
} }
public static Part fromDto(final PartDto abstractDto) { public boolean intersects(@NonNull final Point point) {
return switch (abstractDto) { return this.position.x <= point.x && point.x < this.position.x + this.getSize().x
case final BatteryDto dto -> new Battery(dto); && this.position.y <= point.y && point.y < this.position.y + this.getSize().y;
case final ConnectorCornerDto dto -> new ConnectorCorner(dto);
case final ConnectorEdgeDto dto -> new ConnectorEdge(dto);
case final ConnectorMiddleDto dto -> new ConnectorMiddle(dto);
case final LightDto dto -> new Light(dto);
case final Switch1x1Dto dto -> new Switch1x1(dto);
case final Switch1x2Dto dto -> new Switch1x2(dto);
case final SwitchCrossDto dto -> new SwitchCross(dto);
case final PotiDto dto -> new Poti(dto);
case final VoltmeterDto dto -> new Voltmeter(dto);
case null, default -> throw new RuntimeException();
};
} }
} }

View File

@ -32,10 +32,8 @@ public abstract class PartDto {
public static PartDto of(final Part abstractPart) { public static PartDto of(final Part abstractPart) {
return switch (abstractPart) { return switch (abstractPart) {
case final Battery part -> new BatteryDto(part); case final Battery part -> new BatteryDto(part);
case final ConnectorCorner part -> new ConnectorCornerDto(part);
case final ConnectorEdge part -> new ConnectorEdgeDto(part);
case final ConnectorMiddle part -> new ConnectorMiddleDto(part);
case final Light part -> new LightDto(part); case final Light part -> new LightDto(part);
case final ConnectorSub part -> new ConnectorSubDto(part);
case final Switch1x1 part -> new Switch1x1Dto(part); case final Switch1x1 part -> new Switch1x1Dto(part);
case final Switch1x2 part -> new Switch1x2Dto(part); case final Switch1x2 part -> new Switch1x2Dto(part);
case final SwitchCross part -> new SwitchCrossDto(part); case final SwitchCross part -> new SwitchCrossDto(part);

View File

@ -3,6 +3,7 @@ package de.ph87.electro.circuit.part.node;
import de.ph87.electro.circuit.part.Part; import de.ph87.electro.circuit.part.Part;
import de.ph87.electro.circuit.wire.Wire; import de.ph87.electro.circuit.wire.Wire;
import lombok.Getter; import lombok.Getter;
import lombok.NonNull;
import lombok.ToString; import lombok.ToString;
import java.awt.*; import java.awt.*;
@ -17,50 +18,67 @@ import static de.ph87.electro.CONFIG.*;
@ToString(onlyExplicitlyIncluded = true) @ToString(onlyExplicitlyIncluded = true)
public class Node { public class Node {
@NonNull
@ToString.Include @ToString.Include
private final String uuid; private final String uuid;
private final Part owner; @NonNull
@ToString.Include @ToString.Include
private final String name; private final String name;
@NonNull
private final Part part;
@NonNull
private final Point inside; private final Point inside;
@NonNull
private final Set<Wire> wires = new HashSet<>(); private final Set<Wire> wires = new HashSet<>();
@NonNull
protected Point position;
@NonNull
@ToString.Include @ToString.Include
private double voltage = Double.NaN; private double voltage = Double.NaN;
@NonNull
private Color color = VOLTAGE_UNKNOWN_COLOR; private Color color = VOLTAGE_UNKNOWN_COLOR;
public Node(@NonNull final Part part, @NonNull final String name, @NonNull final Point inside) {
this.part = part;
this.inside = inside;
this.uuid = UUID.randomUUID().toString();
this.name = name;
this.position = part.transform(inside);
}
public Node(@NonNull final Part part, @NonNull final NodeDto dto, @NonNull final Point inside) {
this.uuid = dto.getUuid();
this.name = dto.getName();
this.part = part;
this.inside = inside;
this.position = part.transform(inside);
}
@NonNull
@ToString.Include @ToString.Include
@SuppressWarnings("unused") // lombok toString @SuppressWarnings("unused") // lombok toString
public List<String> destinations() { public List<String> destinations() {
return wires.stream().map(wire -> wire.getOpposite(this)).map(Node::getUuid).toList(); return wires.stream().map(wire -> wire.getOpposite(this)).map(Node::getUuid).toList();
} }
public Node(final Part owner, final String name, final Point inside) {
this.owner = owner;
this.uuid = UUID.randomUUID().toString();
this.name = name;
this.inside = inside;
}
public Node(final Part owner, final NodeDto dto, final Point inside) {
this.owner = owner;
this.uuid = dto.getUuid();
this.name = dto.getName();
this.inside = inside;
}
public void setVoltage(final double voltage) { public void setVoltage(final double voltage) {
this.voltage = voltage; this.voltage = voltage;
this.color = Double.isNaN(voltage) ? VOLTAGE_UNKNOWN_COLOR : ((voltage >= VOLTAGE_HIGH_MIN) ? VOLTAGE_HIGH_COLOR : VOLTAGE_LOW_COLOR); this.color = Double.isNaN(voltage) ? VOLTAGE_UNKNOWN_COLOR : ((voltage >= VOLTAGE_HIGH_MIN) ? VOLTAGE_HIGH_COLOR : VOLTAGE_LOW_COLOR);
} }
public void draw(final Graphics2D g) { public void positionChanged() {
g.setColor(color); position = part.transform(inside);
}
public void draw(@NonNull final Graphics2D g) {
g.setColor(getColor());
g.fillArc(inside.x - NODE_RADIUS, inside.y - NODE_RADIUS, 2 * NODE_RADIUS, 2 * NODE_RADIUS, 0, 360); g.fillArc(inside.x - NODE_RADIUS, inside.y - NODE_RADIUS, 2 * NODE_RADIUS, 2 * NODE_RADIUS, 0, 360);
g.setColor(Color.BLACK); g.setColor(Color.BLACK);
@ -68,21 +86,17 @@ public class Node {
g.drawArc(inside.x - NODE_RADIUS, inside.y - NODE_RADIUS, 2 * NODE_RADIUS, 2 * NODE_RADIUS, 0, 360); g.drawArc(inside.x - NODE_RADIUS, inside.y - NODE_RADIUS, 2 * NODE_RADIUS, 2 * NODE_RADIUS, 0, 360);
} }
public boolean intersects(final Point position) { public boolean intersects(@NonNull final Point position) {
return getAbsolute().distance(position) <= NODE_RADIUS_HOVER; return this.position.distance(position) <= NODE_RADIUS_HOVER;
} }
public void collectConnectedNodes(final Set<Node> connected) { public void collectConnectedNodes(@NonNull final Set<Node> connected) {
if (connected.contains(this)) { if (connected.contains(this)) {
return; return;
} }
connected.add(this); connected.add(this);
wires.forEach(wire -> wire.getOpposite(this).collectConnectedNodes(connected)); wires.forEach(wire -> wire.getOpposite(this).collectConnectedNodes(connected));
owner.getInnerConnections().stream().flatMap(innerConnection -> innerConnection.filter(this)).forEach(opposite -> opposite.collectConnectedNodes(connected)); part.getInnerConnections().stream().flatMap(innerConnection -> innerConnection.filter(this)).forEach(opposite -> opposite.collectConnectedNodes(connected));
}
public Point getAbsolute() {
return owner.transform(inside);
} }
} }

View File

@ -29,6 +29,8 @@ public class Battery extends Part {
private static final int PLUS_H = (int) round(0.6 * RASTER); private static final int PLUS_H = (int) round(0.6 * RASTER);
private final Point size = new Point(RASTER, RASTER);
private final Node minus; private final Node minus;
private final Node plus; private final Node plus;

View File

@ -1,33 +0,0 @@
package de.ph87.electro.circuit.part.parts;
import de.ph87.electro.circuit.part.Part;
import de.ph87.electro.circuit.part.node.Node;
import lombok.Getter;
import lombok.ToString;
import java.awt.*;
import static de.ph87.electro.CONFIG.P10;
import static de.ph87.electro.CONFIG.P90;
@Getter
@ToString(callSuper = true)
public class ConnectorCorner extends Part {
private final Node j0;
private final Node j1;
public ConnectorCorner(final Point position) {
super("", position);
j0 = addNode("J0", P10, P10);
j1 = addNode("J1", P90, P90);
}
public ConnectorCorner(final ConnectorCornerDto dto) {
super(dto);
j0 = addNode(dto.getJ0(), P10, P10);
j1 = addNode(dto.getJ1(), P90, P90);
}
}

View File

@ -1,23 +0,0 @@
package de.ph87.electro.circuit.part.parts;
import de.ph87.electro.circuit.part.PartDto;
import de.ph87.electro.circuit.part.node.NodeDto;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
@Getter
@ToString
@NoArgsConstructor
public class ConnectorCornerDto extends PartDto {
private NodeDto j0;
private NodeDto j1;
public ConnectorCornerDto(final ConnectorCorner part) {
j0 = new NodeDto(part.getJ0());
j1 = new NodeDto(part.getJ1());
}
}

View File

@ -1,33 +0,0 @@
package de.ph87.electro.circuit.part.parts;
import de.ph87.electro.circuit.part.Part;
import de.ph87.electro.circuit.part.node.Node;
import lombok.Getter;
import lombok.ToString;
import java.awt.*;
import static de.ph87.electro.CONFIG.P10;
import static de.ph87.electro.CONFIG.P50;
@Getter
@ToString(callSuper = true)
public class ConnectorEdge extends Part {
private final Node j0;
private final Node j1;
public ConnectorEdge(final Point position) {
super("", position);
j0 = addNode("J0", P10, P50);
j1 = addNode("J1", P50, P10);
}
public ConnectorEdge(final ConnectorEdgeDto dto) {
super(dto);
j0 = addNode(dto.getJ0(), P10, P50);
j1 = addNode(dto.getJ1(), P50, P10);
}
}

View File

@ -1,23 +0,0 @@
package de.ph87.electro.circuit.part.parts;
import de.ph87.electro.circuit.part.PartDto;
import de.ph87.electro.circuit.part.node.NodeDto;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
@Getter
@ToString
@NoArgsConstructor
public class ConnectorEdgeDto extends PartDto {
private NodeDto j0;
private NodeDto j1;
public ConnectorEdgeDto(final ConnectorEdge part) {
j0 = new NodeDto(part.getJ0());
j1 = new NodeDto(part.getJ1());
}
}

View File

@ -1,28 +0,0 @@
package de.ph87.electro.circuit.part.parts;
import de.ph87.electro.circuit.part.Part;
import de.ph87.electro.circuit.part.node.Node;
import lombok.Getter;
import lombok.ToString;
import java.awt.*;
import static de.ph87.electro.CONFIG.P50;
@Getter
@ToString(callSuper = true)
public class ConnectorMiddle extends Part {
private final Node node;
public ConnectorMiddle(final Point position) {
super("", position);
node = addNode("J", P50, P50);
}
public ConnectorMiddle(final ConnectorMiddleDto dto) {
super(dto);
node = addNode(dto.getNode(), P50, P50);
}
}

View File

@ -0,0 +1,30 @@
package de.ph87.electro.circuit.part.parts;
import de.ph87.electro.circuit.part.Part;
import de.ph87.electro.circuit.part.node.Node;
import lombok.Getter;
import lombok.ToString;
import java.awt.*;
import static de.ph87.electro.CONFIG.SUB_RASTER;
@Getter
@ToString(callSuper = true)
public class ConnectorSub extends Part {
private final Node node;
private final Point size = new Point(SUB_RASTER, SUB_RASTER);
public ConnectorSub(final Point position) {
super("", position);
node = addNode("", SUB_RASTER / 2, SUB_RASTER / 2);
}
public ConnectorSub(final ConnectorSubDto dto) {
super(dto);
node = addNode(dto.getNode(), SUB_RASTER / 2, SUB_RASTER / 2);
}
}

View File

@ -9,11 +9,11 @@ import lombok.ToString;
@Getter @Getter
@ToString @ToString
@NoArgsConstructor @NoArgsConstructor
public class ConnectorMiddleDto extends PartDto { public class ConnectorSubDto extends PartDto {
private NodeDto node; private NodeDto node;
public ConnectorMiddleDto(final ConnectorMiddle part) { public ConnectorSubDto(final ConnectorSub part) {
node = new NodeDto(part.getNode()); node = new NodeDto(part.getNode());
} }

View File

@ -20,22 +20,12 @@ import static java.lang.Math.round;
@ToString(callSuper = true) @ToString(callSuper = true)
public class Light extends Part { public class Light extends Part {
private static final Color COLOR_DEFECT = new Color(255, 0, 234);
private static final int MINUS_W = (int) round(0.1 * RASTER);
private static final int MINUS_H = (int) round(0.3 * RASTER);
private static final int GAP = (int) round(0.05 * RASTER);
private static final int PLUS_W = (int) round(0.02 * RASTER);
private static final int PLUS_H = (int) round(0.6 * RASTER);
private static final int BULB_RADIUS = (int) round(0.25 * RASTER); private static final int BULB_RADIUS = (int) round(0.25 * RASTER);
private static final Color BULB_OFF_COLOR = Color.DARK_GRAY; private static final Color BULB_OFF_COLOR = Color.DARK_GRAY;
private final Point size = new Point(RASTER, RASTER);
private final Node a; private final Node a;
private final Node b; private final Node b;

View File

@ -4,6 +4,7 @@ import de.ph87.electro.circuit.part.InnerConnection;
import de.ph87.electro.circuit.part.Part; import de.ph87.electro.circuit.part.Part;
import de.ph87.electro.circuit.part.node.Node; import de.ph87.electro.circuit.part.node.Node;
import lombok.Getter; import lombok.Getter;
import lombok.NonNull;
import lombok.Setter; import lombok.Setter;
import lombok.ToString; import lombok.ToString;
@ -18,25 +19,30 @@ import static java.lang.Math.max;
@ToString(callSuper = true) @ToString(callSuper = true)
public class Poti extends Part { public class Poti extends Part {
@NonNull
private final Node common; private final Node common;
@NonNull
private final Node middle; private final Node middle;
@NonNull
private final Node end; private final Node end;
private final Point size = new Point(RASTER, RASTER);
@Setter @Setter
private double resistance = 10; private double resistance = 10;
private double ratio = 0.0; private double ratio = 0.0;
public Poti(final Point position) { public Poti(@NonNull final Point position) {
super("Poti", position); super("Poti", position);
common = addNode("C", P10, P50); common = addNode("C", P10, P50);
middle = addNode("M", P50, P10); middle = addNode("M", P50, P10);
end = addNode("E", P90, P50); end = addNode("E", P90, P50);
} }
public Poti(final PotiDto dto) { public Poti(@NonNull final PotiDto dto) {
super(dto); super(dto);
common = addNode(dto.getCommon().getName(), P10, P50); common = addNode(dto.getCommon().getName(), P10, P50);
middle = addNode(dto.getMiddle().getName(), P50, P10); middle = addNode(dto.getMiddle().getName(), P50, P10);
@ -53,7 +59,7 @@ public class Poti extends Part {
} }
@Override @Override
protected void _labels(final Graphics2D g) { protected void _labels(@NonNull final Graphics2D g) {
drawText(g, LABEL_FONT, "%3.0f%%".formatted(ratio * 100), P50, P50, Color.BLACK, orientation); drawText(g, LABEL_FONT, "%3.0f%%".formatted(ratio * 100), P50, P50, Color.BLACK, orientation);
} }

View File

@ -18,6 +18,8 @@ public class Switch1x1 extends Part {
private static final Point END = new Point(P90, P25); private static final Point END = new Point(P90, P25);
private final Point size = new Point(RASTER, RASTER);
private final Node common; private final Node common;
private final Node output; private final Node output;
@ -49,8 +51,7 @@ public class Switch1x1 extends Part {
@Override @Override
protected void _render(final Graphics2D g) { protected void _render(final Graphics2D g) {
if (!state) { if (!state) {
final Point end = getOrientation().rotate(END); drawLine(g, common, END, common.getColor(), SWITCH_STROKE);
drawLine(g, common.getAbsolute(), end, common.getColor(), SWITCH_STROKE);
} else { } else {
drawLine(g, common, output, common.getColor(), SWITCH_STROKE); drawLine(g, common, output, common.getColor(), SWITCH_STROKE);
} }

View File

@ -16,6 +16,8 @@ import static de.ph87.electro.circuit.CircuitPainter.drawLine;
@ToString(callSuper = true) @ToString(callSuper = true)
public class Switch1x2 extends Part { public class Switch1x2 extends Part {
private final Point size = new Point(RASTER, RASTER);
private final Node common; private final Node common;
private final Node output0; private final Node output0;

View File

@ -16,6 +16,8 @@ import static de.ph87.electro.circuit.CircuitPainter.drawLine;
@ToString(callSuper = true) @ToString(callSuper = true)
public class SwitchCross extends Part { public class SwitchCross extends Part {
private final Point size = new Point(RASTER, RASTER);
private final Node common0; private final Node common0;
private final Node common1; private final Node common1;

View File

@ -4,6 +4,7 @@ import de.ph87.electro.circuit.part.InnerConnection;
import de.ph87.electro.circuit.part.Part; import de.ph87.electro.circuit.part.Part;
import de.ph87.electro.circuit.part.PartDto; import de.ph87.electro.circuit.part.PartDto;
import de.ph87.electro.circuit.part.node.Node; import de.ph87.electro.circuit.part.node.Node;
import de.ph87.electro.common.RotationMatrix;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import org.apache.commons.math3.linear.ArrayRealVector; import org.apache.commons.math3.linear.ArrayRealVector;
@ -29,10 +30,14 @@ public class Voltmeter extends Part {
private static final double DEGREES_RANGE = 90; private static final double DEGREES_RANGE = 90;
private final Point size = new Point(RASTER, RASTER);
private final Node a; private final Node a;
private final Node b; private final Node b;
double x = 0;
@Setter @Setter
private double min = -3; private double min = -3;
@ -55,7 +60,9 @@ public class Voltmeter extends Part {
postCalculate(); // TODO remove postCalculate(); // TODO remove
} }
double x = 0; public static Point P(final RealVector anchor) {
return new Point((int) round(anchor.getEntry(0)), (int) round(anchor.getEntry(1)));
}
@Override @Override
public void _render(final Graphics2D g) { public void _render(final Graphics2D g) {
@ -74,10 +81,6 @@ public class Voltmeter extends Part {
drawCircle(g, P(ANCHOR), P03, 0, 360, null, null, Color.black); drawCircle(g, P(ANCHOR), P03, 0, 360, null, null, Color.black);
} }
public static Point P(final RealVector anchor) {
return new Point((int) round(anchor.getEntry(0)), (int) round(anchor.getEntry(1)));
}
private double getVoltage() { private double getVoltage() {
return !Double.isNaN(b.getVoltage()) && !Double.isNaN(a.getVoltage()) ? b.getVoltage() - a.getVoltage() : 0.0; return !Double.isNaN(b.getVoltage()) && !Double.isNaN(a.getVoltage()) ? b.getVoltage() - a.getVoltage() : 0.0;
} }

View File

@ -12,20 +12,22 @@ import java.awt.geom.Rectangle2D;
import static de.ph87.electro.CONFIG.*; import static de.ph87.electro.CONFIG.*;
import static java.lang.Math.*; import static java.lang.Math.*;
@Getter
@ToString @ToString
public class Wire { public class Wire {
@Getter
@NonNull @NonNull
private final Node a; private final Node a;
@Getter
@NonNull @NonNull
private final Node b; private final Node b;
@Setter @Setter
private double current = Double.NaN; private double current = Double.NaN;
@Setter
private boolean ghost = false;
public Wire(@NonNull final Node a, @NonNull final Node b) { public Wire(@NonNull final Node a, @NonNull final Node b) {
this.a = a; this.a = a;
this.b = b; this.b = b;
@ -34,7 +36,7 @@ public class Wire {
} }
public boolean intersects(final Point position) { public boolean intersects(final Point position) {
return distanceToLine(position, a.getAbsolute(), b.getAbsolute()); return distanceToLine(position, a.getPosition(), b.getPosition());
} }
public boolean distanceToLine(final Point point, final Point lineStart, final Point lineEnd) { public boolean distanceToLine(final Point point, final Point lineStart, final Point lineEnd) {
@ -68,9 +70,11 @@ public class Wire {
} }
public void draw(final Graphics2D g) { public void draw(final Graphics2D g) {
g.setColor(a.getColor()); final Color color = ghost ? new Color(a.getColor().getRed(), a.getColor().getGreen(), a.getColor().getBlue(), 64) : a.getColor();
g.setColor(color);
g.setStroke(WIRE_STROKE); g.setStroke(WIRE_STROKE);
g.drawLine(a.getAbsolute().x, a.getAbsolute().y, b.getAbsolute().x, b.getAbsolute().y); g.drawLine(a.getPosition().x, a.getPosition().y, b.getPosition().x, b.getPosition().y);
if (SHOW_WIRE_DETAILS) { if (SHOW_WIRE_DETAILS) {
drawValues(g, "%.2f A".formatted(abs(current)), -0.5); drawValues(g, "%.2f A".formatted(abs(current)), -0.5);
@ -81,8 +85,8 @@ public class Wire {
private void drawValues(final Graphics2D g, final String string, final double offset) { private void drawValues(final Graphics2D g, final String string, final double offset) {
g.setFont(LABEL_FONT); g.setFont(LABEL_FONT);
final Rectangle2D bounds = g.getFontMetrics().getStringBounds(string, g); final Rectangle2D bounds = g.getFontMetrics().getStringBounds(string, g);
final Point aa = a.getAbsolute(); final Point aa = a.getPosition();
final Point bb = b.getAbsolute(); final Point bb = b.getPosition();
final int mx = (aa.x + bb.x) / 2; final int mx = (aa.x + bb.x) / 2;
final int my = (aa.y + bb.y) / 2; final int my = (aa.y + bb.y) / 2;

View File

@ -1,4 +1,4 @@
package de.ph87.electro.circuit.part.parts; package de.ph87.electro.common;
import org.apache.commons.math3.linear.Array2DRowRealMatrix; import org.apache.commons.math3.linear.Array2DRowRealMatrix;
import org.apache.commons.math3.linear.RealMatrix; import org.apache.commons.math3.linear.RealMatrix;

View File

@ -23,9 +23,6 @@ public class Sidebar extends JPanel {
final Point ZERO = new Point(); final Point ZERO = new Point();
addPart(new Battery(ZERO)); addPart(new Battery(ZERO));
addPart(new ConnectorCorner(ZERO));
addPart(new ConnectorEdge(ZERO));
addPart(new ConnectorMiddle(ZERO));
addPart(new Light(ZERO)); addPart(new Light(ZERO));
addPart(new Switch1x1(ZERO)); addPart(new Switch1x1(ZERO));
addPart(new Switch1x2(ZERO)); addPart(new Switch1x2(ZERO));

View File

@ -30,7 +30,8 @@ class CalculationServiceTest {
log.info(" \"%s\" = %.2f V".formatted(node.getName(), node.getVoltage())); log.info(" \"%s\" = %.2f V".formatted(node.getName(), node.getVoltage()));
for (final Wire wire : node.getWires()) { for (final Wire wire : node.getWires()) {
final Node opposite = wire.getOpposite(node); final Node opposite = wire.getOpposite(node);
log.info(" -> %s.%s = %.2f A".formatted(opposite.getOwner().getName(), opposite.getName(), wire.getCurrent(node))); final String partName = opposite instanceof final Node partNode ? partNode.getPart().getName() : "";
log.info(" -> %s.%s = %.2f A".formatted(partName, opposite.getName(), wire.getCurrent(node)));
} }
} }
}); });

View File

@ -2,8 +2,8 @@ package de.ph87.electro.circuit.io;
import de.ph87.electro.circuit.Circuit; import de.ph87.electro.circuit.Circuit;
import de.ph87.electro.circuit.CircuitIOService; import de.ph87.electro.circuit.CircuitIOService;
import de.ph87.electro.circuit.part.Part;
import de.ph87.electro.circuit.part.node.Node; import de.ph87.electro.circuit.part.node.Node;
import de.ph87.electro.circuit.part.Part;
import de.ph87.electro.circuit.part.parts.Battery; import de.ph87.electro.circuit.part.parts.Battery;
import de.ph87.electro.circuit.part.parts.Light; import de.ph87.electro.circuit.part.parts.Light;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -16,6 +16,7 @@ import java.nio.charset.StandardCharsets;
import static de.ph87.electro.CONFIG.RASTER; import static de.ph87.electro.CONFIG.RASTER;
import static de.ph87.electro.circuit.CircuitIOService.serialize; import static de.ph87.electro.circuit.CircuitIOService.serialize;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
class CircuitIOServiceTest { class CircuitIOServiceTest {
@ -56,7 +57,7 @@ class CircuitIOServiceTest {
final Node reloadedNode = reloadedPart.getNodes().stream().filter(node -> node.getUuid().equals(originalNode.getUuid())).findFirst().orElseThrow(); final Node reloadedNode = reloadedPart.getNodes().stream().filter(node -> node.getUuid().equals(originalNode.getUuid())).findFirst().orElseThrow();
assertEquals(originalNode.getUuid(), reloadedNode.getUuid()); assertEquals(originalNode.getUuid(), reloadedNode.getUuid());
assertEquals(originalNode.getName(), reloadedNode.getName()); assertEquals(originalNode.getName(), reloadedNode.getName());
assertEquals(originalNode.getAbsolute(), reloadedNode.getAbsolute()); assertEquals(originalNode.getPosition(), reloadedNode.getPosition());
assertEquals(originalNode.getWires().size(), reloadedNode.getWires().size()); assertEquals(originalNode.getWires().size(), reloadedNode.getWires().size());
originalNode.getWires().stream() originalNode.getWires().stream()
.map(originalWire -> originalWire.getOpposite(originalNode)) .map(originalWire -> originalWire.getOpposite(originalNode))
@ -64,7 +65,9 @@ class CircuitIOServiceTest {
System.out.printf(" - Destinations: %s\n", originalDestination.getUuid()); System.out.printf(" - Destinations: %s\n", originalDestination.getUuid());
final Node reloadedDestination = reloadedNode.getWires().stream().map(wire -> wire.getOpposite(reloadedNode)).filter(destination -> destination.getUuid().equals(originalDestination.getUuid())).findFirst().orElseThrow(); final Node reloadedDestination = reloadedNode.getWires().stream().map(wire -> wire.getOpposite(reloadedNode)).filter(destination -> destination.getUuid().equals(originalDestination.getUuid())).findFirst().orElseThrow();
assertEquals(originalDestination.getUuid(), reloadedDestination.getUuid()); assertEquals(originalDestination.getUuid(), reloadedDestination.getUuid());
assertEquals(originalDestination.getOwner().getUuid(), reloadedDestination.getOwner().getUuid()); assertInstanceOf(Node.class, originalDestination);
assertInstanceOf(Node.class, reloadedDestination);
assertEquals(originalDestination.getPart().getUuid(), reloadedDestination.getPart().getUuid());
} }
); );
} }