Compare commits

...

10 Commits

50 changed files with 954 additions and 885 deletions

View File

@ -4,18 +4,25 @@ import java.awt.*;
import static java.lang.Math.round;
@SuppressWarnings("unused")
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;
public static boolean SHOW_NODE_VOLTAGES = false;
public static boolean SHOW_JUNCTION_NAMES = false;
public static boolean SHOW_NODE_NAMES = false;
public static final double VOLTAGE_HIGH_MIN = 0.1;
public static final int RASTER = 200;
public static final int P03 = (int) round(0.03 * RASTER);
public static final int P05 = (int) round(0.05 * RASTER);
public static final int P10 = (int) round(0.1 * RASTER);
@ -24,21 +31,27 @@ public class CONFIG {
public static final int P50 = (int) round(0.5 * RASTER);
public static final int P65 = (int) round(0.65 * RASTER);
public static final int P75 = (int) round(0.75 * RASTER);
public static final int P70 = (int) round(0.7 * RASTER);
public static final int P80 = (int) round(0.8 * RASTER);
public static final int P90 = (int) round(0.9 * RASTER);
public static final int P95 = (int) round(0.95 * RASTER);
public static final int JUNCTION_RADIUS = (int) round(0.09 * RASTER);
public static final int NODE_RADIUS = (int) round(0.09 * RASTER);
public static final int JUNCTION_RADIUS_HOVER = (int) round(1.5 * JUNCTION_RADIUS);
public static final int NODE_RADIUS_HOVER = (int) round(1.5 * NODE_RADIUS);
public static final Color PART_BACKGROUND = new Color(224, 224, 224);
public static final Color PART_BACK_COLOR = new Color(224, 224, 224);
public static final Color PART_HOVER_COLOR = new Color(192, 192, 192, 128);
public static final Color JUNCTION_HOVER_BORDER_COLOR = Color.BLACK;
public static final Color NODE_HOVER_BORDER_COLOR = Color.BLACK;
public static final Color RASTER_COLOR = Color.gray;
@ -54,7 +67,7 @@ public class CONFIG {
public static final BasicStroke RASTER_STROKE = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 1, new float[]{5}, 0);
public static final BasicStroke JUNCTION_STROKE = new BasicStroke(1);
public static final BasicStroke NODE_STROKE = new BasicStroke(1);
public static final BasicStroke HOVER_STROKE = new BasicStroke(2);
@ -68,4 +81,12 @@ public class CONFIG {
public static final BasicStroke SWITCH_STROKE = new BasicStroke(15);
public static Point ALIGN(final Point position) {
return new Point(position.x / RASTER * RASTER, position.y / RASTER * RASTER);
}
public static Point RASTER(final int x, final int y) {
return new Point(x * RASTER, y * RASTER);
}
}

View File

@ -1,7 +1,8 @@
package de.ph87.electro;
import de.ph87.electro.circuit.CircuitIOService;
import de.ph87.electro.circuit.Circuit;
import de.ph87.electro.circuit.CircuitPanel;
import de.ph87.electro.circuit.demo.Demos;
import de.ph87.electro.sidebar.Sidebar;
import javax.swing.*;
@ -11,17 +12,17 @@ import static de.ph87.electro.CONFIG.RASTER;
public class Window extends JFrame {
private final CircuitPanel circuitPanel = new CircuitPanel();
public Window() {
positionOnRightMostScreen();
setDefaultCloseOperation(EXIT_ON_CLOSE);
setPreferredSize(new Dimension(1200, 900));
setExtendedState(MAXIMIZED_BOTH);
final CircuitPanel circuitPanel = new CircuitPanel();
final Sidebar sidebar = new Sidebar(circuitPanel::newCircuit, () -> CircuitIOService.save(circuitPanel.getCircuit()));
final Sidebar sidebar = new Sidebar(() -> setCircuit(new Circuit()));
final JSplitPane splitter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, sidebar, circuitPanel);
sidebar.setRepaintCallback(circuitPanel::repaint);
sidebar.setPreferredSize(new Dimension(calcWidth(3), 0));
sidebar.setMinimumSize(new Dimension(calcWidth(1), 0));
@ -35,6 +36,10 @@ public class Window extends JFrame {
setVisible(true);
}
public void setCircuit(final Circuit circuit) {
circuitPanel.setCircuit(circuit);
}
private int calcWidth(final int raster) {
return raster * (RASTER + 5) + 5;
}
@ -47,7 +52,8 @@ public class Window extends JFrame {
}
public static void main(String[] args) {
new Window();
final Window window = new Window();
window.setCircuit(Demos.potiAndVoltmeter());
}
}

View File

@ -3,14 +3,14 @@ package de.ph87.electro.circuit;
import de.ph87.electro.circuit.calculation.Calculation;
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.node.Node;
import de.ph87.electro.circuit.wire.Wire;
import de.ph87.electro.circuit.wire.WireDto;
import lombok.Getter;
import lombok.NonNull;
import lombok.Setter;
import java.awt.*;
import java.io.File;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
@ -19,6 +19,8 @@ import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
import static de.ph87.electro.CONFIG.ALIGN;
public class Circuit {
@Getter
@ -29,10 +31,6 @@ public class Circuit {
private final List<Wire> wires = new ArrayList<>();
@Setter
@Getter
private boolean dirty = false;
@Setter
@Getter
private File file = null;
@ -41,24 +39,25 @@ public class Circuit {
this.created = ZonedDateTime.now().format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);
}
@SuppressWarnings("unused")
public Circuit(final File file, final CircuitDto dto) {
created = dto.getCreated();
this.created = dto.getCreated();
for (PartDto partDto : dto.getParts()) {
final Part part = Part.of(this, partDto);
final Part part = Part.fromDto(partDto);
verifyFree(part.getPosition());
parts.add(part);
}
for (final WireDto wire : dto.getWires()) {
final Junction a = findJunctionByUuid(wire.getA()).orElseThrow();
final Junction b = findJunctionByUuid(wire.getB()).orElseThrow();
final Node a = findNodeByUuid(wire.getA()).orElseThrow();
final Node b = findNodeByUuid(wire.getB()).orElseThrow();
wires.add(new Wire(a, b));
}
evaluate();
}
public void connect(final Junction a, final Junction b) {
wires.add(new Wire(a, b));
evaluate();
public Wire connect(final Node a, final Node b) {
final Wire wire = new Wire(a, b);
wires.add(wire);
return wire;
}
public void disconnect(final Wire wire) {
@ -68,7 +67,6 @@ public class Circuit {
wires.remove(wire);
wire.getA().getWires().remove(wire);
wire.getB().getWires().remove(wire);
evaluate();
}
public <T extends Part> T addPart(final T part) {
@ -77,32 +75,28 @@ public class Circuit {
}
verifyFree(part.getPosition());
parts.add(part);
part.render();
evaluate();
return part;
}
public void movePart(final Part part, final Position position) {
if (!parts.contains(part)) {
public void removePart(final Part 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)
} else {
throw new RuntimeException();
}
verifyFree(position);
part.setPosition(position);
dirty = true;
}
public void evaluate() {
dirty = true;
Calculation.calculate(this);
parts.forEach(Part::render);
}
private void verifyFree(final Position position) {
if (parts.stream().anyMatch(part -> part.getPosition().equals(position))) {
public void verifyFree(final Point position) {
if (isOccupied(position)) {
throw new RuntimeException();
}
}
public boolean isOccupied(final Point position) {
final Point aligned = ALIGN(position);
return parts.stream().anyMatch(part -> part.getPosition().equals(aligned));
}
public Stream<Part> streamParts() {
return parts.stream();
}
@ -115,19 +109,16 @@ public class Circuit {
return parts.size();
}
public Optional<Part> findPartByPosition(final Position position) {
return streamParts().filter(p -> p.getPosition().raster.equals(position.raster)).findFirst();
public Optional<Part> findPartByPosition(final Point position) {
final Point aligned = ALIGN(position);
return streamParts().filter(p -> p.getPosition().equals(aligned)).findFirst();
}
public Optional<Junction> findJunctionByAbsolute(final Position position) {
return findPartByPosition(position).flatMap(part -> part.findJunctionByPosition(position));
public Optional<Node> findNodeByUuid(@NonNull final String nodeUuid) {
return parts.stream().map(part -> part.findNodeByUuid(nodeUuid)).filter(Optional::isPresent).map(Optional::get).findFirst();
}
public Optional<Junction> findJunctionByUuid(@NonNull final String junctionUuid) {
return parts.stream().map(part -> part.findJunctionByUuid(junctionUuid)).filter(Optional::isPresent).map(Optional::get).findFirst();
}
public Optional<Wire> findWireByPosition(final Position position) {
public Optional<Wire> findWireByPosition(final Point position) {
return wires.stream().filter(wire -> wire.intersects(position)).findFirst();
}
@ -138,4 +129,8 @@ public class Circuit {
return new File(file.getAbsolutePath().replaceAll("\\.json$", ".png"));
}
public void evaluate() {
Calculation.calculate(this);
}
}

View File

@ -7,17 +7,15 @@ import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import static de.ph87.electro.circuit.CircuitPainter.paintCircuit;
import static de.ph87.electro.circuit.CircuitPainter.draw;
@Slf4j
@SuppressWarnings("unused")
public class CircuitIOService {
private static final ObjectMapper objectMapper = new ObjectMapper().findAndRegisterModules();
public static void save(final Circuit circuit) {
if (!circuit.isDirty()) {
return;
}
if (circuit.getFile() == null) {
circuit.setFile(new File("./data/%s.json".formatted(circuit.getCreated())));
}
@ -26,12 +24,11 @@ public class CircuitIOService {
log.info("Directory created: {}", circuit.getFile().getParent());
}
CircuitIOService.serialize(circuit, new FileOutputStream(circuit.getFile()));
circuit.setDirty(false);
} catch (IOException e) {
log.error(e.toString());
}
final BufferedImage img = paintCircuit(circuit, 1920, 1080);
final BufferedImage img = draw(circuit, 1920, 1080);
try {
ImageIO.write(img, "PNG", circuit.getPreviewFile());
} catch (IOException e) {

View File

@ -1,26 +1,31 @@
package de.ph87.electro.circuit;
import de.ph87.electro.circuit.part.Orientation;
import de.ph87.electro.circuit.part.Part;
import de.ph87.electro.circuit.part.junction.Junction;
import de.ph87.electro.circuit.part.node.Node;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import static de.ph87.electro.CONFIG.*;
import static java.lang.Math.round;
public class CircuitPainter {
public static BufferedImage paintCircuit(final Circuit circuit, final int w, final int h) {
public static BufferedImage draw(final Circuit circuit, final int w, final int h) {
final BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
final Graphics2D g = img.createGraphics();
paintCircuit(circuit, g, w, h);
draw(g, circuit, w, h);
return img;
}
public static void paintCircuit(final Circuit circuit, final Graphics2D g, final int w, final int h) {
public static void draw(final Graphics2D g, final Circuit circuit, final int w, final int h) {
g.setTransform(new AffineTransform());
drawBack(g, w, h);
drawParts(circuit, g);
g.setTransform(new AffineTransform());
drawRaster(g, w, h);
drawWires(circuit, g);
drawVoltages(circuit, g);
@ -32,7 +37,10 @@ public class CircuitPainter {
}
private static void drawParts(final Circuit circuit, final Graphics2D g) {
circuit.streamParts().forEach(part -> part.paint(g));
circuit.streamParts().forEach(part -> {
g.setTransform(part.getTransform());
part.draw(g);
});
}
private static void drawRaster(final Graphics2D g, final int w, final int h) {
@ -51,7 +59,7 @@ public class CircuitPainter {
}
private static void drawVoltages(final Circuit circuit, final Graphics2D g) {
if (!SHOW_JUNCTION_VOLTAGES && !SHOW_JUNCTION_NAMES) {
if (!SHOW_NODE_VOLTAGES && !SHOW_NODE_NAMES) {
return;
}
@ -60,27 +68,84 @@ public class CircuitPainter {
final int third = g.getFont().getSize() / 3;
for (Part part : circuit.getParts()) {
for (final Junction junction : part.getJunctions()) {
if (SHOW_JUNCTION_NAMES) {
for (final Node node : part.getNodes()) {
final Point absolute = node.getAbsolute();
if (SHOW_NODE_NAMES) {
int offsetY = third;
if (SHOW_JUNCTION_VOLTAGES) {
if (SHOW_NODE_VOLTAGES) {
offsetY -= third;
}
final String string = junction.getName();
final String string = node.getName();
final Rectangle2D bounds = g.getFontMetrics().getStringBounds(string, g);
g.drawString(string, junction.getPosition().absolute.x - (int) (bounds.getWidth() / 2), junction.getPosition().absolute.y + offsetY);
g.drawString(string, absolute.x - (int) (bounds.getWidth() / 2), absolute.y + offsetY);
}
if (SHOW_JUNCTION_VOLTAGES) {
if (SHOW_NODE_VOLTAGES) {
int offsetY = third;
if (SHOW_JUNCTION_NAMES) {
if (SHOW_NODE_NAMES) {
offsetY += 2 * third;
}
final String string = "%.1fV".formatted(junction.getVoltage());
final String string = "%.1fV".formatted(node.getVoltage());
final Rectangle2D bounds = g.getFontMetrics().getStringBounds(string, g);
g.drawString(string, junction.getPosition().absolute.x - (int) (bounds.getWidth() / 2), junction.getPosition().absolute.y + offsetY);
g.drawString(string, absolute.x - (int) (bounds.getWidth() / 2), absolute.y + offsetY);
}
}
}
}
public static void drawLine(final Graphics2D g, final Node a, final Node b, final Color color, final Stroke stroke) {
drawLine(g, a.getInside(), b.getInside(), color, stroke);
}
public static void drawLine(final Graphics2D g, final Node a, final Point b, final Color color, final Stroke stroke) {
drawLine(g, a.getInside(), b, color, stroke);
}
public static void drawLine(final Graphics2D g, final Point a, final Point b, final Color color, final Stroke stroke) {
drawLine(g, a.x, a.y, b.x, b.y, color, stroke);
}
public static void drawLine(final Graphics2D g, final int x0, final int y0, final int x1, final int y1, final Color color, final Stroke stroke) {
g.setColor(color);
g.setStroke(stroke);
g.drawLine(x0, y0, x1, y1);
}
public static void fillRect(final Graphics2D g, final int x0, final int y0, final int x1, final int y1, final Color color) {
g.setColor(color);
g.fillRect(x0, y0, x1, y1);
}
public static void drawCircle(final Graphics2D g, final Point center, final int radius, final double degreesOffset, final double degreesRange, final Color border, final Stroke stroke, final Color fill) {
drawCircle(g, center.x, center.y, radius, border, stroke, fill, degreesOffset, degreesRange);
}
public static void drawCircle(final Graphics2D g, final int x, final int y, final int radius, final Color border, final Stroke stroke, final Color fill, final double degreesOffset, final double degreesRange) {
if (fill != null) {
g.setColor(fill);
g.fillArc(x - radius, y - radius, 2 * radius, 2 * radius, (int) round(degreesOffset), (int) round(degreesRange));
}
if (border != null && stroke != null) {
g.setColor(border);
g.setStroke(stroke);
g.drawArc(x - radius, y - radius, 2 * radius, 2 * radius, (int) round(degreesOffset), (int) round(degreesRange));
}
}
public static void drawText(final Graphics2D g, final Font font, final String text, final int x, int y, final Color color, final Orientation orientation) {
final AffineTransform transformBackup = g.getTransform();
if (orientation == Orientation.R180) {
g.rotate(orientation.getRadians(), P50, P50);
y = RASTER - y;
}
g.setFont(font);
g.setColor(color);
final Rectangle2D bounds = g.getFontMetrics().getStringBounds(text, g);
final int xx = (int) round(x - bounds.getWidth() / 2);
final int yy = (int) round(y - bounds.getHeight() / 2 + font.getSize());
g.drawString(text, xx, yy);
g.setTransform(transformBackup);
}
}

View File

@ -1,44 +1,47 @@
package de.ph87.electro.circuit;
import de.ph87.electro.circuit.part.parts.Battery;
import de.ph87.electro.circuit.part.parts.Light;
import lombok.Getter;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import javax.swing.*;
import java.awt.*;
import static de.ph87.electro.circuit.CircuitPainter.paintCircuit;
import static de.ph87.electro.circuit.part.Position.RST;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
@Slf4j
public class CircuitPanel extends JPanel {
@Getter
private Circuit circuit = new Circuit();
private final CircuitPanelMouseAdapter mouseAdapter = new CircuitPanelMouseAdapter(this);
@Getter
@NonNull
private Circuit circuit = new Circuit();
public CircuitPanel() {
new CircuitPanelDropTarget(this);
final Battery b0 = circuit.addPart(new Battery(circuit, RST(0, 0)));
final Light l = circuit.addPart(new Light(circuit, RST(2, 0)));
final Battery b1 = circuit.addPart(new Battery(circuit, RST(4, 0)));
circuit.connect(b0.getPlus(), l.getA());
circuit.connect(l.getB(), b1.getMinus());
addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(final ComponentEvent e) {
repaint();
}
});
}
public void setCircuit(@NonNull final Circuit circuit) {
this.circuit = circuit;
this.circuit.evaluate();
repaint();
}
@Override
public void paint(final Graphics _g) {
final Graphics2D g = (Graphics2D) _g;
paintCircuit(circuit, g, getWidth(), getHeight());
public void paint(final Graphics graphics) {
final Graphics2D g = (Graphics2D) graphics;
CircuitPainter.draw(g, circuit, getWidth(), getHeight());
mouseAdapter.drawHover(g);
mouseAdapter.drawDrag(g);
}
public void newCircuit() {
circuit = new Circuit();
repaint();
}
}

View File

@ -1,11 +1,12 @@
package de.ph87.electro.circuit;
import de.ph87.electro.circuit.part.Position;
import de.ph87.electro.circuit.part.parts.*;
import de.ph87.electro.common.AbstractDropTarget;
import java.awt.*;
import static de.ph87.electro.CONFIG.ALIGN;
public class CircuitPanelDropTarget extends AbstractDropTarget {
private final CircuitPanel circuitPanel;
@ -18,29 +19,30 @@ public class CircuitPanelDropTarget extends AbstractDropTarget {
@Override
protected boolean drop(final Point point, final String data) {
final Position position = new Position(point);
final Point aligned = ALIGN(point);
if (data.equals(Battery.class.getSimpleName())) {
circuitPanel.getCircuit().addPart(new Battery(circuitPanel.getCircuit(), position));
circuitPanel.getCircuit().addPart(new Battery(aligned));
} else if (data.equals(ConnectorCorner.class.getSimpleName())) {
circuitPanel.getCircuit().addPart(new ConnectorCorner(circuitPanel.getCircuit(), position));
circuitPanel.getCircuit().addPart(new ConnectorCorner(aligned));
} else if (data.equals(ConnectorEdge.class.getSimpleName())) {
circuitPanel.getCircuit().addPart(new ConnectorEdge(circuitPanel.getCircuit(), position));
circuitPanel.getCircuit().addPart(new ConnectorEdge(aligned));
} else if (data.equals(ConnectorMiddle.class.getSimpleName())) {
circuitPanel.getCircuit().addPart(new ConnectorMiddle(circuitPanel.getCircuit(), position));
circuitPanel.getCircuit().addPart(new ConnectorMiddle(aligned));
} else if (data.equals(Light.class.getSimpleName())) {
circuitPanel.getCircuit().addPart(new Light(circuitPanel.getCircuit(), position));
circuitPanel.getCircuit().addPart(new Light(aligned));
} else if (data.equals(Switch1x1.class.getSimpleName())) {
circuitPanel.getCircuit().addPart(new Switch1x1(circuitPanel.getCircuit(), position));
circuitPanel.getCircuit().addPart(new Switch1x1(aligned));
} else if (data.equals(Switch1x2.class.getSimpleName())) {
circuitPanel.getCircuit().addPart(new Switch1x2(circuitPanel.getCircuit(), position));
circuitPanel.getCircuit().addPart(new Switch1x2(aligned));
} else if (data.equals(SwitchCross.class.getSimpleName())) {
circuitPanel.getCircuit().addPart(new SwitchCross(circuitPanel.getCircuit(), position));
circuitPanel.getCircuit().addPart(new SwitchCross(aligned));
} else if (data.equals(Poti.class.getSimpleName())) {
circuitPanel.getCircuit().addPart(new Poti(circuitPanel.getCircuit(), position));
circuitPanel.getCircuit().addPart(new Poti(aligned));
} else if (data.equals(Voltmeter.class.getSimpleName())) {
circuitPanel.getCircuit().addPart(new Voltmeter(aligned));
} else {
throw new RuntimeException();
}
circuitPanel.repaint();
return true;
}

View File

@ -1,34 +1,29 @@
package de.ph87.electro.circuit;
import de.ph87.electro.circuit.part.Part;
import de.ph87.electro.circuit.part.Position;
import de.ph87.electro.circuit.part.junction.Junction;
import de.ph87.electro.circuit.part.node.Node;
import de.ph87.electro.circuit.wire.Wire;
import lombok.extern.slf4j.Slf4j;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Optional;
import static de.ph87.electro.CONFIG.*;
import static java.awt.event.MouseEvent.BUTTON1;
import static java.awt.event.MouseEvent.BUTTON3;
import static java.awt.event.MouseEvent.*;
@Slf4j
class CircuitPanelMouseAdapter extends MouseAdapter {
private final CircuitPanel circuitPanel;
private Part partHover = null;
private Part part = null;
private Junction junctionHover = null;
private Node node = null;
private Wire wireHover = null;
private Wire wire = null;
private Part partDrag = null;
private Junction junctionDrag = null;
private Position dragPosition = null;
private Point dragging = null;
CircuitPanelMouseAdapter(final CircuitPanel circuitPanel) {
this.circuitPanel = circuitPanel;
@ -38,129 +33,141 @@ class CircuitPanelMouseAdapter extends MouseAdapter {
@Override
public void mouseClicked(final MouseEvent event) {
final Position position = new Position(event);
hoverUpdate(event);
if (event.getButton() == BUTTON3) {
final Optional<Wire> wireOptional = circuitPanel.getCircuit().findWireByPosition(position);
if (wireOptional.isPresent()) {
circuitPanel.getCircuit().disconnect(wireOptional.get());
circuitPanel.repaint();
return;
if (wire != null) {
switch (event.getButton()) {
case BUTTON3:
circuitPanel.getCircuit().disconnect(wire);
circuitPanel.getCircuit().evaluate();
wire = null;
break;
}
}
circuitPanel.getCircuit().findPartByPosition(position).ifPresent(part -> {
if (part != null) {
switch (event.getButton()) {
case BUTTON1:
part.action();
circuitPanel.repaint();
circuitPanel.getCircuit().evaluate();
break;
case BUTTON2:
part.rotate();
break;
case BUTTON3:
part.rotate();
circuitPanel.repaint();
circuitPanel.getCircuit().removePart(part);
circuitPanel.getCircuit().evaluate();
part = null;
break;
}
});
}
circuitPanel.repaint();
}
@Override
public void mouseMoved(final MouseEvent event) {
findHover(new Position(event));
hoverUpdate(event);
circuitPanel.repaint();
}
@Override
public void mousePressed(final MouseEvent event) {
final Position position = new Position(event);
circuitPanel.getCircuit().findPartByPosition(position).ifPresent(part -> startPartOrJunction(part, position));
}
private void startPartOrJunction(final Part part, final Position position) {
part.findJunctionByPosition(position).ifPresentOrElse(junction -> {
partDrag = null;
junctionDrag = junction;
}, () -> {
partDrag = part;
junctionDrag = null;
});
}
@Override
public void mouseDragged(final MouseEvent event) {
final Position position = new Position(event);
findHover(position);
if (partDrag != null || junctionDrag != null) {
dragPosition = position;
if (dragging == null) {
hoverUpdate(event);
}
dragging = event.getPoint();
circuitPanel.repaint();
}
private void findHover(final Position position) {
partHover = circuitPanel.getCircuit().findPartByPosition(position).orElse(null);
junctionHover = partHover != null ? partHover.findJunctionByPosition(position).orElse(null) : null;
if (junctionHover != null) {
partHover = null;
wireHover = null;
return;
}
wireHover = circuitPanel.getCircuit().findWireByPosition(position).orElse(null);
if (wireHover != null) {
partHover = null;
}
}
@Override
public void mouseReleased(final MouseEvent event) {
final Position position = new Position(event);
if (partDrag != null) {
circuitPanel.getCircuit().movePart(partDrag, position);
if (dragging == null) {
return;
}
if (junctionDrag != null) {
circuitPanel.getCircuit().findJunctionByAbsolute(position).filter(destination -> destination != junctionDrag).ifPresent(destination -> circuitPanel.getCircuit().connect(junctionDrag, destination));
if (part != null) {
final Point aligned = ALIGN(event.getPoint());
if (!circuitPanel.getCircuit().isOccupied(aligned)) {
part.setPosition(aligned);
log.info("Dropped Part at {}: {}", aligned, part);
} else {
log.info("Cell already occupied at: {}", aligned);
}
}
partDrag = null;
junctionDrag = null;
dragPosition = null;
if (node != null) {
final Node source = node;
hoverUpdate(event);
if (node != null) {
final Wire wire = circuitPanel.getCircuit().connect(source, node);
log.info("Wire CREATED: {}", wire);
circuitPanel.getCircuit().evaluate();
} else {
log.info("No Wire created: No destination node found!");
}
}
dragging = null;
circuitPanel.repaint();
}
public void drawHover(final Graphics2D g) {
if (partHover != null) {
g.setColor(PART_HOVER_COLOR);
g.setStroke(HOVER_STROKE);
g.drawRect(partHover.getPosition().absolute.x, partHover.getPosition().absolute.y, RASTER, RASTER);
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;
}
}
if (junctionHover != null) {
g.setColor(junctionHover.getColor());
g.fillArc(junctionHover.getPosition().absolute.x - JUNCTION_RADIUS_HOVER, junctionHover.getPosition().absolute.y - JUNCTION_RADIUS_HOVER, 2 * JUNCTION_RADIUS_HOVER, 2 * JUNCTION_RADIUS_HOVER, 0, 360);
g.setColor(JUNCTION_HOVER_BORDER_COLOR);
public void drawHover(final Graphics2D g) {
if (part != null) {
g.setColor(NODE_HOVER_BORDER_COLOR);
g.setStroke(HOVER_STROKE);
g.drawArc(junctionHover.getPosition().absolute.x - JUNCTION_RADIUS_HOVER, junctionHover.getPosition().absolute.y - JUNCTION_RADIUS_HOVER, 2 * JUNCTION_RADIUS_HOVER, 2 * JUNCTION_RADIUS_HOVER, 0, 360);
g.drawRect(part.getPosition().x, part.getPosition().y, RASTER, RASTER);
}
if (wireHover != null) {
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(wireHover.getA().getPosition().absolute.x, wireHover.getA().getPosition().absolute.y, wireHover.getB().getPosition().absolute.x, wireHover.getB().getPosition().absolute.y);
g.drawLine(aa.x, aa.y, bb.x, bb.y);
g.setColor(wireHover.getA().getColor());
g.setColor(wire.getA().getColor());
g.setStroke(WIRE_HOVER_STROKE);
g.drawLine(wireHover.getA().getPosition().absolute.x, wireHover.getA().getPosition().absolute.y, wireHover.getB().getPosition().absolute.x, wireHover.getB().getPosition().absolute.y);
g.drawLine(aa.x, aa.y, bb.x, bb.y);
}
}
public void drawDrag(final Graphics2D g) {
if (dragPosition != null) {
if (partDrag != null) {
if (dragging != null) {
if (part != null) {
g.setColor(PART_HOVER_COLOR);
g.fillRect(dragPosition.absolute.x - P50, dragPosition.absolute.y - P50, RASTER, RASTER);
g.fillRect(dragging.x - P50, dragging.y - P50, RASTER, RASTER);
}
if (junctionDrag != null) {
g.setColor(junctionDrag.getColor());
if (node != null) {
g.setColor(node.getColor());
g.setStroke(WIRE_STROKE);
g.drawLine(junctionDrag.getPosition().absolute.x, junctionDrag.getPosition().absolute.y, dragPosition.absolute.x, dragPosition.absolute.y);
final Point absolute = node.getAbsolute();
g.drawLine(absolute.x, absolute.y, dragging.x, dragging.y);
}
}
}

View File

@ -1,9 +1,10 @@
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;
import de.ph87.electro.circuit.part.junction.Junction;
import de.ph87.electro.circuit.part.node.Node;
import de.ph87.electro.circuit.part.parts.Battery;
import de.ph87.electro.circuit.wire.Wire;
import lombok.Getter;
@ -22,11 +23,7 @@ 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<Junction> junctions = new ArrayList<>();
private final List<Node> nodes = new ArrayList<>();
private final Set<Part> parts = new HashSet<>();
@ -43,11 +40,11 @@ public class Calculation {
final List<Battery> batteries = new ArrayList<>(circuit.streamParts().flatMap(Battery::filterCast).toList());
while (!batteries.isEmpty()) {
final Battery pivot = batteries.removeFirst();
final Set<Junction> connectedJunctions = new HashSet<>();
pivot.getPlus().collectConnectedJunctions(connectedJunctions);
pivot.getMinus().collectConnectedJunctions(connectedJunctions);
connectedJunctions.stream().map(Junction::getOwner).flatMap(Battery::filterCast).forEach(batteries::remove);
calculations.add(new Calculation(connectedJunctions, pivot));
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;
}
@ -57,18 +54,18 @@ public class Calculation {
currents = new ArrayRealVector(numNodes);
}
private Calculation(final Set<Junction> connectedJunctions, final Battery pivot) {
for (final Junction junction : connectedJunctions) {
parts.add(junction.getOwner());
if (!junctions.contains(junction) && junction != pivot.getMinus()) {
private Calculation(final Set<Node> connectedNodes, final Battery pivot) {
for (final Node node : connectedNodes) {
parts.add(node.getOwner());
if (!nodes.contains(node) && node != pivot.getMinus()) {
// pivot.minus is GND and cannot be part of the matrix (linear dependency)
this.junctions.add(junction);
this.nodes.add(node);
}
wires.addAll(junction.getWires());
wires.addAll(node.getWires());
}
matrix = new Array2DRowRealMatrix(this.junctions.size(), this.junctions.size());
currents = new ArrayRealVector(this.junctions.size());
matrix = new Array2DRowRealMatrix(this.nodes.size(), this.nodes.size());
currents = new ArrayRealVector(this.nodes.size());
fromSchematic();
solve();
toSchematic();
@ -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()) {
@ -84,8 +81,8 @@ public class Calculation {
}
if (part instanceof final Battery battery) {
final double current = battery.getVoltage() / battery.getResistance();
final int indexMinus = getJunctionIndex(battery.getMinus());
final int indexPlus = getJunctionIndex(battery.getPlus());
final int indexMinus = getNodeIndex(battery.getMinus());
final int indexPlus = getNodeIndex(battery.getPlus());
addCurrentSource(indexMinus, indexPlus, current);
}
});
@ -94,13 +91,13 @@ public class Calculation {
private void toSchematic() {
wires.forEach(wire -> wire.setCurrent(getCurrent(wire)));
parts.forEach(part -> {
part.getJunctions().forEach(junction -> junction.setVoltage(getPotential(junction)));
part.getNodes().forEach(node -> node.setVoltage(getPotential(node)));
part.postCalculate();
});
}
private double getPotential(final @NonNull Junction junction) {
final int index = getJunctionIndex(junction);
private double getPotential(final @NonNull Node node) {
final int index = getNodeIndex(node);
if (index < 0) {
return 0; // per definition
}
@ -111,25 +108,25 @@ 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 int indexA = getNodeIndex(wire.getA());
final int indexB = getNodeIndex(wire.getB());
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;
}
private int getJunctionIndex(@NonNull final Junction junction) {
for (int i = 0; i < junctions.size(); i++) {
if (junctions.get(i) == junction) {
private int getNodeIndex(@NonNull final Node node) {
for (int i = 0; i < nodes.size(); i++) {
if (nodes.get(i) == node) {
return i;
}
}
return -1;
}
public void addResistor(final Junction a, final Junction b, final double resistance) {
final int indexA = getJunctionIndex(a);
final int indexB = getJunctionIndex(b);
public void addResistor(final Node a, final Node b, final double resistance) {
final int indexA = getNodeIndex(a);
final int indexB = getNodeIndex(b);
addResistor(indexA, indexB, resistance);
}

View File

@ -0,0 +1,25 @@
package de.ph87.electro.circuit.demo;
import de.ph87.electro.circuit.Circuit;
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 static de.ph87.electro.CONFIG.RASTER;
@SuppressWarnings("unused")
public class Demos {
public static Circuit potiAndVoltmeter() {
final Circuit circuit = new Circuit();
final Battery battery = circuit.addPart(new Battery(RASTER(1, 0)));
final Poti poti = circuit.addPart(new Poti(RASTER(1, 2)));
final Voltmeter voltmeter = circuit.addPart(new Voltmeter(RASTER(1, 4)));
circuit.connect(battery.getMinus(), poti.getCommon());
circuit.connect(battery.getPlus(), poti.getEnd());
circuit.connect(poti.getCommon(), voltmeter.getA());
circuit.connect(poti.getMiddle(), voltmeter.getB());
return circuit;
}
}

View File

@ -1,27 +1,27 @@
package de.ph87.electro.circuit.part;
import de.ph87.electro.circuit.part.junction.Junction;
import de.ph87.electro.circuit.part.node.Node;
import java.util.stream.Stream;
public class InnerConnection {
public final Junction a;
public final Node a;
public final Junction b;
public final Node b;
public final double resistance;
public InnerConnection(final Junction a, final Junction b, final double resistance) {
public InnerConnection(final Node a, final Node b, final double resistance) {
this.a = a;
this.b = b;
this.resistance = resistance;
}
public Stream<Junction> filter(final Junction junction) {
if (a == junction) {
public Stream<Node> filter(final Node node) {
if (a == node) {
return Stream.of(b);
} else if (b == junction) {
} else if (b == node) {
return Stream.of(a);
}
return Stream.empty();

View File

@ -25,8 +25,8 @@ public enum Orientation {
this.map = rotate;
}
public Point rotate(final Point p) {
return map.apply(p);
public Point rotate(final Point inside) {
return map.apply(inside);
}
public Orientation clockwise() {

View File

@ -1,151 +1,140 @@
package de.ph87.electro.circuit.part;
import de.ph87.electro.circuit.Circuit;
import de.ph87.electro.circuit.part.junction.Junction;
import de.ph87.electro.circuit.part.junction.JunctionDto;
import de.ph87.electro.circuit.part.node.Node;
import de.ph87.electro.circuit.part.node.NodeDto;
import de.ph87.electro.circuit.part.parts.*;
import lombok.Getter;
import lombok.NonNull;
import lombok.Setter;
import lombok.ToString;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.util.List;
import java.util.*;
import static de.ph87.electro.CONFIG.PART_BACKGROUND;
import static de.ph87.electro.CONFIG.RASTER;
import static de.ph87.electro.circuit.part.Position.ZERO;
import static de.ph87.electro.CONFIG.*;
import static java.lang.Math.round;
@Getter
@ToString(onlyExplicitlyIncluded = true)
public abstract class Part {
private final Circuit circuit;
@Getter
@ToString.Include
private final String uuid;
@Getter
@Setter
@ToString.Include
private String name;
@Getter
private Position position;
private Point position;
@Getter
private Orientation orientation = Orientation.R0;
protected Orientation orientation = Orientation.R0;
@Getter
private final List<Junction> junctions = new ArrayList<>();
private final List<Node> nodes = new ArrayList<>();
protected final Render render = new Render();
@NonNull
private AffineTransform transform;
protected Part(final Circuit circuit, final String name, final Position position) {
this.circuit = circuit;
protected Part(final String name, final Point position) {
this.uuid = UUID.randomUUID().toString();
this.name = name;
setPosition(position);
this.position = position;
this.transform = newTransform();
}
protected Part(final Circuit circuit, final PartDto dto) {
this.circuit = circuit;
protected Part(final PartDto dto) {
this.uuid = dto.getUuid();
this.name = dto.getName();
this.orientation = dto.getOrientation();
setPosition(new Position(dto.getPosition()));
this.position = dto.getPosition();
this.transform = newTransform();
}
protected Junction addJunction(final String name, final int x, final int y) {
return addJunction(new Junction(this, name, new Point(x, y)));
private AffineTransform newTransform() {
final AffineTransform transform = new AffineTransform();
transform.translate(position.x, position.y);
transform.rotate(orientation.getRadians(), P50, P50);
return transform;
}
protected Junction addJunction(final JunctionDto dto, final int x, final int y) {
return addJunction(new Junction(this, dto, new Point(x, y)));
public Point transform(final Point point) {
final Point2D result = transform.transform(point, new Point2D.Double());
return new Point((int) round(result.getX()), (int) round(result.getY()));
}
private Junction addJunction(final Junction junction) {
junctions.add(junction);
return junction;
protected Node addNode(final String name, final int x, final int y) {
return addNode(new Node(this, name, new Point(x, y)));
}
public void setPosition(final Position position) {
this.position = position.alignToRaster();
junctions.forEach(Junction::updatePosition);
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.transform = newTransform();
}
public void rotate() {
orientation = orientation.clockwise();
junctions.forEach(Junction::updatePosition);
render();
this.orientation = orientation.clockwise();
this.transform = newTransform();
}
public void render() {
render.rect(ZERO, RASTER, RASTER, null, null, PART_BACKGROUND);
_labels();
render.resetTransform();
_render();
render.resetTransform();
junctions.forEach(junction -> junction.render(render));
render.resetTransform();
public final void draw(final Graphics2D g) {
g.setColor(PART_BACK_COLOR);
g.fillRect(0, 0, RASTER, RASTER);
_render(g);
nodes.forEach(node -> node.draw(g));
_labels(g);
}
public void paint(final Graphics2D g) {
g.drawImage(render.getImage(), position.absolute.x, position.absolute.y, null);
protected void _render(final Graphics2D g) {
// -
}
public Optional<Junction> findJunctionByUuid(final String junctionUuid) {
return junctions.stream().filter(junction -> junction.getUuid().equals(junctionUuid)).findFirst();
protected void _labels(final Graphics2D g) {
// -
}
public Optional<Junction> findJunctionByPosition(final Position position) {
return junctions.stream().filter(junction -> junction.intersects(position)).findFirst();
}
public final void action() {
_action();
evaluate();
}
public void _action() {
// may be overwritten
public void action() {
// -
}
public void postCalculate() {
// may be overwritten
}
protected void _render() {
// may be overwritten
}
protected void _labels() {
// may be overwritten
}
protected void evaluate() {
if (circuit != null) {
circuit.evaluate();
}
// -
}
public List<InnerConnection> getInnerConnections() {
return Collections.emptyList();
}
public static Part of(final Circuit circuit, final PartDto abstractDto) {
public Optional<Node> findNodeByUuid(final String nodeUuid) {
return nodes.stream().filter(node -> node.getUuid().equals(nodeUuid)).findFirst();
}
public Optional<Node> findNodeByPosition(final Point position) {
return nodes.stream().filter(node -> node.intersects(position)).findFirst();
}
public static Part fromDto(final PartDto abstractDto) {
return switch (abstractDto) {
case final BatteryDto dto -> new Battery(circuit, dto);
case final ConnectorCornerDto dto -> new ConnectorCorner(circuit, dto);
case final ConnectorEdgeDto dto -> new ConnectorEdge(circuit, dto);
case final ConnectorMiddleDto dto -> new ConnectorMiddle(circuit, dto);
case final LightDto dto -> new Light(circuit, dto);
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 BatteryDto dto -> new Battery(dto);
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

@ -25,7 +25,7 @@ public abstract class PartDto {
protected PartDto(final Part part) {
this.uuid = part.getUuid();
this.name = part.getName();
this.position = part.getPosition().absolute;
this.position = part.getPosition();
this.orientation = part.getOrientation();
}
@ -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();
};
}

View File

@ -1,89 +0,0 @@
package de.ph87.electro.circuit.part;
import lombok.Getter;
import lombok.ToString;
import java.awt.*;
import java.awt.event.MouseEvent;
import static de.ph87.electro.CONFIG.RASTER;
import static de.ph87.electro.CONFIG.WIRE_HOVER_STROKE_BACK;
import static java.lang.Math.*;
@Getter
@ToString
public final class Position {
public static final Position ZERO = new Position(0, 0);
public final Point absolute;
public final Point inside;
public final Point raster;
public static Position ABS(final double absoluteX, final double absoluteY) {
return new Position((int) round(absoluteX), (int) round(absoluteY));
}
public static Position RST(final int x, final int y) {
return ABS(x * RASTER, y * RASTER);
}
private Position(final int absoluteX, final int absoluteY) {
this.absolute = new Point(absoluteX, absoluteY);
this.raster = new Point(absoluteX / RASTER, absoluteY / RASTER);
this.inside = new Point(absoluteX % RASTER, absoluteY % RASTER);
}
@Override
public boolean equals(final Object obj) {
if (!(obj instanceof final Position other)) {
return false;
}
return absolute.equals(other.absolute);
}
@Override
public int hashCode() {
return absolute.hashCode();
}
public Position(final Point point) {
this(point.x, point.y);
}
public Position(final MouseEvent event) {
this(event.getX(), event.getY());
}
public Position plus(final Point vector) {
return new Position(absolute.x + vector.x, absolute.y + vector.y);
}
public double distance(final Position position) {
return sqrt(pow(position.absolute.x - absolute.x, 2) + pow(position.absolute.y - absolute.y, 2));
}
public boolean distanceToLine(final Position lineStart, final Position lineEnd) {
double dx = lineEnd.absolute.x - lineStart.absolute.x;
double dy = lineEnd.absolute.y - lineStart.absolute.y;
double lineLength = dx * dx + dy * dy;
if (lineLength == 0) {
return distance(lineStart) <= WIRE_HOVER_STROKE_BACK.getLineWidth();
}
double t = ((absolute.x - lineStart.absolute.x) * dx + (absolute.y - lineStart.absolute.y) * dy) / lineLength;
t = Math.max(0, Math.min(1, t));
double closestX = lineStart.absolute.x + t * dx;
double closestY = lineStart.absolute.y + t * dy;
final double distance = sqrt(pow(closestX - absolute.x, 2) + pow(closestY - absolute.y, 2));
return distance <= WIRE_HOVER_STROKE_BACK.getLineWidth();
}
public Position alignToRaster() {
return new Position(raster.x * RASTER, raster.y * RASTER);
}
}

View File

@ -1,82 +0,0 @@
package de.ph87.electro.circuit.part;
import de.ph87.electro.circuit.part.junction.Junction;
import lombok.Getter;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import static de.ph87.electro.CONFIG.P50;
import static de.ph87.electro.CONFIG.RASTER;
import static java.lang.Math.round;
public class Render {
@Getter
private final BufferedImage image = new BufferedImage(RASTER, RASTER, BufferedImage.TYPE_INT_ARGB);
private final Graphics2D g = image.createGraphics();
public void line(final Junction junction0, final Junction junction1, final Color color, final BasicStroke stroke) {
line(junction0.getPosition(), junction1.getPosition(), color, stroke);
}
public void line(final Position p0, final Position p1, final Color color, final BasicStroke stroke) {
if (color != null) {
g.setColor(color);
}
if (stroke != null) {
g.setStroke(stroke);
}
g.drawLine(p0.inside.x, p0.inside.y, p1.inside.x, p1.inside.y);
}
public void rect(final Position p, final double w, final double h, final Color border, final Stroke stroke, final Color fill) {
final int _w = (int) round(w);
final int _h = (int) round(h);
if (fill != null) {
g.setColor(fill);
g.fillRect(p.inside.x, p.inside.y, _w, _h);
}
if (border != null && stroke != null) {
g.setColor(border);
g.setStroke(stroke);
g.drawRect(p.inside.x, p.inside.y, _w, _h);
}
}
public void circle(final Position center, final double radius, final Color border, final Stroke stroke, final Color fill) {
final int _x = (int) round(center.inside.x - radius);
final int _y = (int) round(center.inside.y - radius);
final int diameter = (int) round(radius * 2);
if (fill != null) {
g.setColor(fill);
g.fillArc(_x, _y, diameter, diameter, 0, 360);
}
if (border != null && stroke != null) {
g.setColor(border);
g.setStroke(stroke);
g.drawArc(_x, _y, diameter, diameter, 0, 360);
}
}
public void textCenter(final Font font, final String string, final int innerX, final int innerY, final Color color) {
g.setFont(font);
g.setColor(color);
final Rectangle2D bounds = g.getFontMetrics().getStringBounds(string, g);
final int x = innerX - (int) round(bounds.getWidth()) / 2;
final int y = innerY + (int) round(bounds.getHeight()) / 2;
g.drawString(string, x, y);
}
public void clockwise(final Orientation orientation) {
g.rotate(orientation.getRadians(), P50, P50);
}
public void resetTransform() {
g.setTransform(new AffineTransform());
}
}

View File

@ -1,21 +0,0 @@
package de.ph87.electro.circuit.part.junction;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
@Getter
@ToString
@NoArgsConstructor
public class JunctionDto {
private String uuid;
private String name;
public JunctionDto(final Junction junction) {
this.uuid = junction.getUuid();
this.name = junction.getName();
}
}

View File

@ -1,8 +1,6 @@
package de.ph87.electro.circuit.part.junction;
package de.ph87.electro.circuit.part.node;
import de.ph87.electro.circuit.part.Part;
import de.ph87.electro.circuit.part.Position;
import de.ph87.electro.circuit.part.Render;
import de.ph87.electro.circuit.wire.Wire;
import lombok.Getter;
import lombok.ToString;
@ -17,7 +15,7 @@ import static de.ph87.electro.CONFIG.*;
@Getter
@ToString(onlyExplicitlyIncluded = true)
public class Junction {
public class Node {
@ToString.Include
private final String uuid;
@ -27,9 +25,7 @@ public class Junction {
@ToString.Include
private final String name;
private final Point relative;
private Position position;
private final Point inside;
private final Set<Wire> wires = new HashSet<>();
@ -41,23 +37,21 @@ public class Junction {
@ToString.Include
@SuppressWarnings("unused") // lombok toString
public List<String> destinations() {
return wires.stream().map(wire -> wire.getOpposite(this)).map(Junction::getUuid).toList();
return wires.stream().map(wire -> wire.getOpposite(this)).map(Node::getUuid).toList();
}
public Junction(final Part owner, final String name, final Point relative) {
public Node(final Part owner, final String name, final Point inside) {
this.owner = owner;
this.uuid = UUID.randomUUID().toString();
this.name = name;
this.relative = relative;
updatePosition();
this.inside = inside;
}
public Junction(final Part owner, final JunctionDto dto, final Point relative) {
public Node(final Part owner, final NodeDto dto, final Point inside) {
this.owner = owner;
this.uuid = dto.getUuid();
this.name = dto.getName();
this.relative = relative;
updatePosition();
this.inside = inside;
}
public void setVoltage(final double voltage) {
@ -65,25 +59,30 @@ public class Junction {
this.color = Double.isNaN(voltage) ? VOLTAGE_UNKNOWN_COLOR : ((voltage >= VOLTAGE_HIGH_MIN) ? VOLTAGE_HIGH_COLOR : VOLTAGE_LOW_COLOR);
}
public void render(final Render render) {
render.circle(position, JUNCTION_RADIUS, Color.BLACK, JUNCTION_STROKE, color);
public void draw(final Graphics2D g) {
g.setColor(color);
g.fillArc(inside.x - NODE_RADIUS, inside.y - NODE_RADIUS, 2 * NODE_RADIUS, 2 * NODE_RADIUS, 0, 360);
g.setColor(Color.BLACK);
g.setStroke(NODE_STROKE);
g.drawArc(inside.x - NODE_RADIUS, inside.y - NODE_RADIUS, 2 * NODE_RADIUS, 2 * NODE_RADIUS, 0, 360);
}
public void updatePosition() {
position = owner.getPosition().plus(owner.getOrientation().rotate(relative));
public boolean intersects(final Point position) {
return getAbsolute().distance(position) <= NODE_RADIUS_HOVER;
}
public boolean intersects(final Position position) {
return this.position.distance(position) <= JUNCTION_RADIUS_HOVER;
}
public void collectConnectedJunctions(final Set<Junction> connected) {
public void collectConnectedNodes(final Set<Node> connected) {
if (connected.contains(this)) {
return;
}
connected.add(this);
wires.forEach(wire -> wire.getOpposite(this).collectConnectedJunctions(connected));
owner.getInnerConnections().stream().flatMap(innerConnection -> innerConnection.filter(this)).forEach(opposite -> opposite.collectConnectedJunctions(connected));
wires.forEach(wire -> wire.getOpposite(this).collectConnectedNodes(connected));
owner.getInnerConnections().stream().flatMap(innerConnection -> innerConnection.filter(this)).forEach(opposite -> opposite.collectConnectedNodes(connected));
}
public Point getAbsolute() {
return owner.transform(inside);
}
}

View File

@ -0,0 +1,21 @@
package de.ph87.electro.circuit.part.node;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
@Getter
@ToString
@NoArgsConstructor
public class NodeDto {
private String uuid;
private String name;
public NodeDto(final Node node) {
this.uuid = node.getUuid();
this.name = node.getName();
}
}

View File

@ -1,11 +1,8 @@
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.Orientation;
import de.ph87.electro.circuit.part.Part;
import de.ph87.electro.circuit.part.Position;
import de.ph87.electro.circuit.part.junction.Junction;
import de.ph87.electro.circuit.part.node.Node;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@ -15,25 +12,26 @@ import java.util.List;
import java.util.stream.Stream;
import static de.ph87.electro.CONFIG.*;
import static de.ph87.electro.circuit.part.Position.ABS;
import static de.ph87.electro.circuit.CircuitPainter.*;
import static java.lang.Math.round;
@Getter
@ToString(callSuper = true, onlyExplicitlyIncluded = true)
public class Battery extends Part {
private static final double MINUS_W = 0.1 * RASTER;
private static final int MINUS_W = (int) round(0.1 * RASTER);
private static final double MINUS_H = 0.3 * RASTER;
private static final int MINUS_H = (int) round(0.3 * RASTER);
private static final double GAP = 0.05 * RASTER;
private static final int GAP = (int) round(0.05 * RASTER);
private static final double PLUS_W = 0.02 * RASTER;
private static final int PLUS_W = (int) round(0.02 * RASTER);
private static final double PLUS_H = 0.6 * RASTER;
private static final int PLUS_H = (int) round(0.6 * RASTER);
private final Junction minus;
private final Node minus;
private final Junction plus;
private final Node plus;
@Setter
@ToString.Include
@ -43,41 +41,19 @@ public class Battery extends Part {
@ToString.Include
private double resistance = 0.05;
public Battery(final Circuit circuit, final Position position) {
super(circuit, "Batterie", position);
minus = addJunction("MINUS", P10, P50);
plus = addJunction("PLUS", P90, P50);
public Battery(final Point position) {
super("Batterie", position);
minus = addNode("MINUS", P10, P50);
plus = addNode("PLUS", P90, P50);
}
public Battery(final Circuit circuit, final BatteryDto dto) {
super(circuit, dto);
minus = addJunction(dto.getMinus(), P10, P50);
plus = addJunction(dto.getPlus(), P90, P50);
public Battery(final BatteryDto dto) {
super(dto);
minus = addNode(dto.getMinus(), P10, P50);
plus = addNode(dto.getPlus(), P90, P50);
voltage = dto.getVoltage();
}
@Override
protected void _render() {
render.clockwise(getOrientation());
render.line(ABS(P10, P50), ABS(P50 - GAP / 2 - MINUS_W / 2, P50), Color.BLACK, SYMBOL_STROKE);
render.line(ABS(P50 + GAP / 2 + PLUS_W / 2, P50), ABS(P90, P50), Color.BLACK, SYMBOL_STROKE);
render.rect(ABS(P50 - MINUS_W - GAP / 2, P50 - MINUS_H / 2), MINUS_W, MINUS_H, null, null, Color.BLACK);
render.rect(ABS(P50 + GAP / 2, P50 - PLUS_H / 2), PLUS_W, PLUS_H, null, null, Color.BLACK);
}
@Override
protected void _labels() {
final int x = RASTER / 2;
final int y0;
if (getOrientation() == Orientation.R180) {
y0 = P95 - LABEL_FONT.getSize();
} else {
render.clockwise(getOrientation());
y0 = P05;
}
render.textCenter(LABEL_FONT, "%.1fV".formatted(voltage), x, y0, Color.BLACK);
}
public static Stream<Battery> filterCast(final Part part) {
if (part instanceof final Battery battery) {
return Stream.of(battery);
@ -90,4 +66,17 @@ public class Battery extends Part {
return List.of(new InnerConnection(minus, plus, resistance));
}
@Override
protected void _render(final Graphics2D g) {
drawLine(g, P10, P50, P50 - GAP / 2 - MINUS_W / 2, P50, Color.BLACK, SYMBOL_STROKE);
drawLine(g, P50 + GAP / 2 + PLUS_W / 2, P50, P90, P50, Color.BLACK, SYMBOL_STROKE);
fillRect(g, P50 - MINUS_W - GAP / 2, P50 - MINUS_H / 2, MINUS_W, MINUS_H, Color.BLACK);
fillRect(g, P50 + GAP / 2, P50 - PLUS_H / 2, PLUS_W, PLUS_H, Color.BLACK);
}
@Override
protected void _labels(final Graphics2D g) {
drawText(g, LABEL_FONT, "%.1fV".formatted(voltage), P50, P10, Color.BLACK, orientation);
}
}

View File

@ -1,7 +1,7 @@
package de.ph87.electro.circuit.part.parts;
import de.ph87.electro.circuit.part.PartDto;
import de.ph87.electro.circuit.part.junction.JunctionDto;
import de.ph87.electro.circuit.part.node.NodeDto;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
@ -11,16 +11,16 @@ import lombok.ToString;
@NoArgsConstructor
public class BatteryDto extends PartDto {
private JunctionDto minus;
private NodeDto minus;
private JunctionDto plus;
private NodeDto plus;
private double voltage;
public BatteryDto(final Battery battery) {
super(battery);
this.minus = new JunctionDto(battery.getMinus());
this.plus = new JunctionDto(battery.getPlus());
this.minus = new NodeDto(battery.getMinus());
this.plus = new NodeDto(battery.getPlus());
this.voltage = battery.getVoltage();
}

View File

@ -1,12 +1,12 @@
package de.ph87.electro.circuit.part.parts;
import de.ph87.electro.circuit.Circuit;
import de.ph87.electro.circuit.part.Part;
import de.ph87.electro.circuit.part.Position;
import de.ph87.electro.circuit.part.junction.Junction;
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;
@ -14,20 +14,20 @@ import static de.ph87.electro.CONFIG.P90;
@ToString(callSuper = true)
public class ConnectorCorner extends Part {
private final Junction j0;
private final Node j0;
private final Junction j1;
private final Node j1;
public ConnectorCorner(final Circuit circuit, final Position position) {
super(circuit, "", position);
j0 = addJunction("J0", P10, P10);
j1 = addJunction("J1", P90, P90);
public ConnectorCorner(final Point position) {
super("", position);
j0 = addNode("J0", P10, P10);
j1 = addNode("J1", P90, P90);
}
public ConnectorCorner(final Circuit circuit, final ConnectorCornerDto dto) {
super(circuit, dto);
j0 = addJunction(dto.getJ0(), P10, P10);
j1 = addJunction(dto.getJ1(), P90, P90);
public ConnectorCorner(final ConnectorCornerDto dto) {
super(dto);
j0 = addNode(dto.getJ0(), P10, P10);
j1 = addNode(dto.getJ1(), P90, P90);
}
}

View File

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

View File

@ -1,12 +1,12 @@
package de.ph87.electro.circuit.part.parts;
import de.ph87.electro.circuit.Circuit;
import de.ph87.electro.circuit.part.Part;
import de.ph87.electro.circuit.part.Position;
import de.ph87.electro.circuit.part.junction.Junction;
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;
@ -14,20 +14,20 @@ import static de.ph87.electro.CONFIG.P50;
@ToString(callSuper = true)
public class ConnectorEdge extends Part {
private final Junction j0;
private final Node j0;
private final Junction j1;
private final Node j1;
public ConnectorEdge(final Circuit circuit, final Position position) {
super(circuit, "", position);
j0 = addJunction("J0", P10, P50);
j1 = addJunction("J1", P50, P10);
public ConnectorEdge(final Point position) {
super("", position);
j0 = addNode("J0", P10, P50);
j1 = addNode("J1", P50, P10);
}
public ConnectorEdge(final Circuit circuit, final ConnectorEdgeDto dto) {
super(circuit, dto);
j0 = addJunction(dto.getJ0(), P10, P50);
j1 = addJunction(dto.getJ1(), P50, P10);
public ConnectorEdge(final ConnectorEdgeDto dto) {
super(dto);
j0 = addNode(dto.getJ0(), P10, P50);
j1 = addNode(dto.getJ1(), P50, P10);
}
}

View File

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

View File

@ -1,28 +1,28 @@
package de.ph87.electro.circuit.part.parts;
import de.ph87.electro.circuit.Circuit;
import de.ph87.electro.circuit.part.Part;
import de.ph87.electro.circuit.part.Position;
import de.ph87.electro.circuit.part.junction.Junction;
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 Junction junction;
private final Node node;
public ConnectorMiddle(final Circuit circuit, final Position position) {
super(circuit, "", position);
junction = addJunction("J", P50, P50);
public ConnectorMiddle(final Point position) {
super("", position);
node = addNode("J", P50, P50);
}
public ConnectorMiddle(final Circuit circuit, final ConnectorMiddleDto dto) {
super(circuit, dto);
junction = addJunction(dto.getJunction(), P50, P50);
public ConnectorMiddle(final ConnectorMiddleDto dto) {
super(dto);
node = addNode(dto.getNode(), P50, P50);
}
}

View File

@ -1,7 +1,7 @@
package de.ph87.electro.circuit.part.parts;
import de.ph87.electro.circuit.part.PartDto;
import de.ph87.electro.circuit.part.junction.JunctionDto;
import de.ph87.electro.circuit.part.node.NodeDto;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
@ -11,10 +11,10 @@ import lombok.ToString;
@NoArgsConstructor
public class ConnectorMiddleDto extends PartDto {
private JunctionDto junction;
private NodeDto node;
public ConnectorMiddleDto(final ConnectorMiddle part) {
junction = new JunctionDto(part.getJunction());
node = new NodeDto(part.getNode());
}
}

View File

@ -1,11 +1,9 @@
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.Orientation;
import de.ph87.electro.circuit.part.Part;
import de.ph87.electro.circuit.part.Position;
import de.ph87.electro.circuit.part.junction.Junction;
import de.ph87.electro.circuit.part.node.Node;
import lombok.Getter;
import lombok.ToString;
@ -14,7 +12,7 @@ import java.util.Collections;
import java.util.List;
import static de.ph87.electro.CONFIG.*;
import static de.ph87.electro.circuit.part.Position.ABS;
import static de.ph87.electro.circuit.CircuitPainter.*;
import static java.lang.Math.abs;
import static java.lang.Math.round;
@ -38,9 +36,9 @@ public class Light extends Part {
private static final Color BULB_OFF_COLOR = Color.DARK_GRAY;
private final Junction a;
private final Node a;
private final Junction b;
private final Node b;
private double resistance = 15;
@ -52,23 +50,23 @@ public class Light extends Part {
private Color color = BULB_OFF_COLOR;
public Light(final Circuit circuit, final Position position) {
super(circuit, "Licht", position);
a = addJunction("A", P10, P50);
b = addJunction("B", P90, P50);
public Light(final Point position) {
super("Licht", position);
a = addNode("A", P10, P50);
b = addNode("B", P90, P50);
}
public Light(final Circuit circuit, final LightDto dto) {
super(circuit, dto);
a = addJunction(dto.getB(), P10, P50);
b = addJunction(dto.getA(), P90, P50);
public Light(final LightDto dto) {
super(dto);
a = addNode(dto.getB(), P10, P50);
b = addNode(dto.getA(), P90, P50);
resistance = dto.getResistance();
maxVoltage = dto.getMaxVoltage();
defect = dto.isDefect();
}
@Override
public void _action() {
public void action() {
defect = false;
}
@ -85,17 +83,17 @@ public class Light extends Part {
}
@Override
protected void _render() {
render.line(a, b, Color.BLACK, SYMBOL_STROKE);
render.circle(ABS(P50, P50), BULB_RADIUS, Color.BLACK, SYMBOL_STROKE, color);
protected void _render(final Graphics2D g) {
drawLine(g, a, b, Color.BLACK, SYMBOL_STROKE);
drawCircle(g, P50, P50, BULB_RADIUS, Color.BLACK, SYMBOL_STROKE, color, 0, 360);
final double diag = 0.33 * RASTER;
render.line(ABS(diag, diag), ABS(RASTER - diag, RASTER - diag), Color.BLACK, SYMBOL_STROKE);
render.line(ABS(diag, RASTER - diag), ABS(RASTER - diag, diag), Color.BLACK, SYMBOL_STROKE);
final int diag = (int) round(0.33 * RASTER);
drawLine(g, RASTER - diag, RASTER - diag, diag, diag, Color.BLACK, SYMBOL_STROKE);
drawLine(g, RASTER - diag, diag, diag, RASTER - diag, Color.BLACK, SYMBOL_STROKE);
}
@Override
protected void _labels() {
protected void _labels(final Graphics2D g) {
final int x = RASTER / 2;
final int y0;
final int y1;
@ -103,13 +101,13 @@ public class Light extends Part {
y0 = P95 - LABEL_FONT.getSize();
y1 = P05;
} else {
render.clockwise(getOrientation());
g.rotate(getOrientation().getRadians(), P50, P50);
y0 = P05;
y1 = P95 - LABEL_FONT.getSize();
}
render.textCenter(LABEL_FONT, "%.1fV (max)".formatted(maxVoltage), x, y0, Color.BLACK);
drawText(g, LABEL_FONT, "%.1fV (max)".formatted(maxVoltage), x, y0, Color.BLACK, orientation);
if (defect) {
render.textCenter(LABEL_FONT, "DEFEKT", x, y1, Color.RED);
drawText(g, LABEL_FONT, "DEFEKT", x, y1, Color.RED, orientation);
}
}

View File

@ -1,7 +1,7 @@
package de.ph87.electro.circuit.part.parts;
import de.ph87.electro.circuit.part.PartDto;
import de.ph87.electro.circuit.part.junction.JunctionDto;
import de.ph87.electro.circuit.part.node.NodeDto;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
@ -11,9 +11,9 @@ import lombok.ToString;
@NoArgsConstructor
public class LightDto extends PartDto {
private JunctionDto a;
private NodeDto a;
private JunctionDto b;
private NodeDto b;
private double resistance;
@ -23,8 +23,8 @@ public class LightDto extends PartDto {
public LightDto(final Light light) {
super(light);
this.a = new JunctionDto(light.getB());
this.b = new JunctionDto(light.getA());
this.a = new NodeDto(light.getB());
this.b = new NodeDto(light.getA());
this.defect = light.isDefect();
this.resistance = light.getResistance();
this.maxVoltage = light.getMaxVoltage();

View File

@ -1,10 +1,8 @@
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.Position;
import de.ph87.electro.circuit.part.junction.Junction;
import de.ph87.electro.circuit.part.node.Node;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@ -13,45 +11,40 @@ 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.circuit.CircuitPainter.drawText;
import static java.lang.Math.max;
@Getter
@ToString(callSuper = true)
public class Poti extends Part {
private final Junction common;
private final Node common;
private final Junction middle;
private final Node middle;
private final Junction end;
private final Node end;
@Setter
private double resistance = 10;
private double ratio = 0.0;
public Poti(final Circuit circuit, final Position position) {
super(circuit, "Poti", position);
common = addJunction("C", P10, P50);
middle = addJunction("M", P50, P10);
end = addJunction("E", P90, P50);
public Poti(final Point position) {
super("Poti", position);
common = addNode("C", P10, P50);
middle = addNode("M", P50, P10);
end = addNode("E", P90, P50);
}
protected Poti(final Circuit circuit, final PotiDto dto) {
super(circuit, dto);
common = addJunction(dto.getCommon().getName(), P10, P50);
middle = addJunction(dto.getMiddle().getName(), P50, P10);
end = addJunction(dto.getEnd().getName(), P90, P50);
}
public void setRatio(final double ratio) {
this.ratio = ratio;
evaluate();
public Poti(final PotiDto dto) {
super(dto);
common = addNode(dto.getCommon().getName(), P10, P50);
middle = addNode(dto.getMiddle().getName(), P50, P10);
end = addNode(dto.getEnd().getName(), P90, P50);
}
@Override
public void _action() {
public void action() {
if (ratio > 0.95) {
ratio = 0;
} else {
@ -60,15 +53,14 @@ public class Poti extends Part {
}
@Override
protected void _labels() {
render.textCenter(LABEL_FONT, "%3.0f%%".formatted(ratio * 100), P50, P50, Color.BLACK);
protected void _labels(final Graphics2D g) {
drawText(g, LABEL_FONT, "%3.0f%%".formatted(ratio * 100), P50, P50, Color.BLACK, orientation);
}
@Override
public List<InnerConnection> getInnerConnections() {
return List.of(
new InnerConnection(common, middle, max(NO_RESISTANCE, resistance * ratio)),
// new InnerConnection(common, end, max(NO_RESISTANCE, resistance)),
new InnerConnection(middle, end, max(NO_RESISTANCE, resistance * (1 - ratio)))
);
}

View File

@ -1,7 +1,7 @@
package de.ph87.electro.circuit.part.parts;
import de.ph87.electro.circuit.part.PartDto;
import de.ph87.electro.circuit.part.junction.JunctionDto;
import de.ph87.electro.circuit.part.node.NodeDto;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@ -12,11 +12,11 @@ import lombok.ToString;
@NoArgsConstructor
public class PotiDto extends PartDto {
private JunctionDto common;
private NodeDto common;
private JunctionDto middle;
private NodeDto middle;
private JunctionDto end;
private NodeDto end;
@Setter
private double resistance;
@ -25,9 +25,9 @@ public class PotiDto extends PartDto {
private double ratio;
public PotiDto(final Poti poti) {
common = new JunctionDto(poti.getCommon());
middle = new JunctionDto(poti.getMiddle());
end = new JunctionDto(poti.getEnd());
common = new NodeDto(poti.getCommon());
middle = new NodeDto(poti.getMiddle());
end = new NodeDto(poti.getEnd());
resistance = poti.getResistance();
ratio = poti.getRatio();
}

View File

@ -0,0 +1,21 @@
package de.ph87.electro.circuit.part.parts;
import org.apache.commons.math3.linear.Array2DRowRealMatrix;
import org.apache.commons.math3.linear.RealMatrix;
import org.apache.commons.math3.linear.RealVector;
import static java.lang.Math.cos;
import static java.lang.Math.sin;
public class RotationMatrix extends Array2DRowRealMatrix {
public static RealVector rotateDegrees(final RealVector finger, final double degrees) {
final double radians = degrees * Math.PI / 180.0;
final RealMatrix matrix = new Array2DRowRealMatrix(new double[][]{
{cos(radians), -sin(radians)},
{sin(radians), cos(radians)},
});
return matrix.operate(finger);
}
}

View File

@ -1,10 +1,8 @@
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.Position;
import de.ph87.electro.circuit.part.junction.Junction;
import de.ph87.electro.circuit.part.node.Node;
import lombok.Getter;
import lombok.ToString;
@ -12,48 +10,49 @@ 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.circuit.CircuitPainter.drawLine;
@Getter
@ToString(callSuper = true)
public class Switch1x1 extends Part {
private final Junction common;
private static final Point END = new Point(P90, P25);
private final Junction output;
private final Node common;
private final Node output;
private boolean state = false;
public Switch1x1(final Circuit circuit, final Position position) {
super(circuit, "Ausschalter", position);
common = addJunction("C", P10, P50);
output = addJunction("O", P90, P50);
public Switch1x1(final Point position) {
super("Ausschalter", position);
common = addNode("C", P10, P50);
output = addNode("O", P90, P50);
}
public Switch1x1(final Circuit circuit, final Switch1x1Dto dto) {
super(circuit, dto);
common = addJunction(dto.getCommon(), P10, P50);
output = addJunction(dto.getOutput(), P90, P50);
public Switch1x1(final Switch1x1Dto dto) {
super(dto);
common = addNode(dto.getCommon(), P10, P50);
output = addNode(dto.getOutput(), P90, P50);
state = dto.isState();
}
public void setState(final boolean state) {
this.state = state;
evaluate();
}
@Override
public void _action() {
public void action() {
state = !state;
}
@Override
protected void _render() {
protected void _render(final Graphics2D g) {
if (!state) {
final Point end = getOrientation().rotate(new Point(P90, P25));
render.line(common.getPosition(), new Position(end), common.getColor(), SWITCH_STROKE);
final Point end = getOrientation().rotate(END);
drawLine(g, common.getAbsolute(), end, common.getColor(), SWITCH_STROKE);
} else {
render.line(common, output, common.getColor(), SWITCH_STROKE);
drawLine(g, common, output, common.getColor(), SWITCH_STROKE);
}
}

View File

@ -1,7 +1,7 @@
package de.ph87.electro.circuit.part.parts;
import de.ph87.electro.circuit.part.PartDto;
import de.ph87.electro.circuit.part.junction.JunctionDto;
import de.ph87.electro.circuit.part.node.NodeDto;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
@ -11,16 +11,16 @@ import lombok.ToString;
@NoArgsConstructor
public class Switch1x1Dto extends PartDto {
private JunctionDto common;
private NodeDto common;
private JunctionDto output;
private NodeDto output;
private boolean state;
public Switch1x1Dto(final Switch1x1 switch1X1) {
super(switch1X1);
this.common = new JunctionDto(switch1X1.getCommon());
this.output = new JunctionDto(switch1X1.getOutput());
this.common = new NodeDto(switch1X1.getCommon());
this.output = new NodeDto(switch1X1.getOutput());
this.state = switch1X1.isState();
}

View File

@ -1,61 +1,59 @@
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.Position;
import de.ph87.electro.circuit.part.junction.Junction;
import de.ph87.electro.circuit.part.node.Node;
import lombok.Getter;
import lombok.ToString;
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.circuit.CircuitPainter.drawLine;
@Getter
@ToString(callSuper = true)
public class Switch1x2 extends Part {
private final Junction common;
private final Node common;
private final Junction output0;
private final Node output0;
private final Junction output1;
private final Node output1;
private boolean state = false;
public Switch1x2(final Circuit circuit, final Position position) {
super(circuit, "Wechselschalter", position);
common = addJunction("C", P10, P50);
output0 = addJunction("O0", P90, P25);
output1 = addJunction("O1", P90, P75);
public Switch1x2(final Point position) {
super("Wechselschalter", position);
common = addNode("C", P10, P50);
output0 = addNode("O0", P90, P25);
output1 = addNode("O1", P90, P75);
}
public Switch1x2(final Circuit circuit, final Switch1x2Dto dto) {
super(circuit, dto);
common = addJunction(dto.getCommon(), P10, P50);
output0 = addJunction(dto.getOutput0(), P90, P25);
output1 = addJunction(dto.getOutput1(), P90, P75);
public Switch1x2(final Switch1x2Dto dto) {
super(dto);
common = addNode(dto.getCommon(), P10, P50);
output0 = addNode(dto.getOutput0(), P90, P25);
output1 = addNode(dto.getOutput1(), P90, P75);
state = dto.isState();
}
public void setState(final boolean state) {
this.state = state;
evaluate();
}
@Override
public void _action() {
public void action() {
state = !state;
}
@Override
protected void _render() {
protected void _render(final Graphics2D g) {
if (!state) {
render.line(common, output0, common.getColor(), SWITCH_STROKE);
drawLine(g, common, output0, common.getColor(), SWITCH_STROKE);
} else {
render.line(common, output1, common.getColor(), SWITCH_STROKE);
drawLine(g, common, output1, common.getColor(), SWITCH_STROKE);
}
}

View File

@ -1,7 +1,7 @@
package de.ph87.electro.circuit.part.parts;
import de.ph87.electro.circuit.part.PartDto;
import de.ph87.electro.circuit.part.junction.JunctionDto;
import de.ph87.electro.circuit.part.node.NodeDto;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
@ -11,19 +11,19 @@ import lombok.ToString;
@NoArgsConstructor
public class Switch1x2Dto extends PartDto {
private JunctionDto common;
private NodeDto common;
private JunctionDto output0;
private NodeDto output0;
private JunctionDto output1;
private NodeDto output1;
private boolean state;
public Switch1x2Dto(final Switch1x2 switch1X2) {
super(switch1X2);
this.common = new JunctionDto(switch1X2.getCommon());
this.output0 = new JunctionDto(switch1X2.getOutput0());
this.output1 = new JunctionDto(switch1X2.getOutput1());
this.common = new NodeDto(switch1X2.getCommon());
this.output0 = new NodeDto(switch1X2.getOutput0());
this.output1 = new NodeDto(switch1X2.getOutput1());
this.state = switch1X2.isState();
}

View File

@ -1,67 +1,65 @@
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.Position;
import de.ph87.electro.circuit.part.junction.Junction;
import de.ph87.electro.circuit.part.node.Node;
import lombok.Getter;
import lombok.ToString;
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.circuit.CircuitPainter.drawLine;
@Getter
@ToString(callSuper = true)
public class SwitchCross extends Part {
private final Junction common0;
private final Node common0;
private final Junction common1;
private final Node common1;
private final Junction output0;
private final Node output0;
private final Junction output1;
private final Node output1;
private boolean state = false;
public SwitchCross(final Circuit circuit, final Position position) {
super(circuit, "Kreuzschalter", position);
common0 = addJunction("C0", P10, P25);
common1 = addJunction("C1", P10, P75);
output0 = addJunction("O0", P90, P25);
output1 = addJunction("O1", P90, P75);
public SwitchCross(final Point position) {
super("Kreuzschalter", position);
common0 = addNode("C0", P10, P25);
common1 = addNode("C1", P10, P75);
output0 = addNode("O0", P90, P25);
output1 = addNode("O1", P90, P75);
}
public SwitchCross(final Circuit circuit, final SwitchCrossDto dto) {
super(circuit, dto);
common0 = addJunction(dto.getCommon0(), P10, P25);
common1 = addJunction(dto.getCommon1(), P10, P75);
output0 = addJunction(dto.getOutput0(), P90, P25);
output1 = addJunction(dto.getOutput1(), P90, P75);
public SwitchCross(final SwitchCrossDto dto) {
super(dto);
common0 = addNode(dto.getCommon0(), P10, P25);
common1 = addNode(dto.getCommon1(), P10, P75);
output0 = addNode(dto.getOutput0(), P90, P25);
output1 = addNode(dto.getOutput1(), P90, P75);
state = dto.isState();
}
public void setState(final boolean state) {
this.state = state;
evaluate();
}
@Override
public void _action() {
public void action() {
state = !state;
}
@Override
protected void _render() {
protected void _render(final Graphics2D g) {
if (!state) {
render.line(common0, output0, common0.getColor(), SWITCH_STROKE);
render.line(common1, output1, common1.getColor(), SWITCH_STROKE);
drawLine(g, common0, output0, common0.getColor(), SWITCH_STROKE);
drawLine(g, common1, output1, common1.getColor(), SWITCH_STROKE);
} else {
render.line(common0, output1, common0.getColor(), SWITCH_STROKE);
render.line(common1, output0, common1.getColor(), SWITCH_STROKE);
drawLine(g, common0, output1, common0.getColor(), SWITCH_STROKE);
drawLine(g, common1, output0, common1.getColor(), SWITCH_STROKE);
}
}

View File

@ -1,7 +1,7 @@
package de.ph87.electro.circuit.part.parts;
import de.ph87.electro.circuit.part.PartDto;
import de.ph87.electro.circuit.part.junction.JunctionDto;
import de.ph87.electro.circuit.part.node.NodeDto;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
@ -11,22 +11,22 @@ import lombok.ToString;
@NoArgsConstructor
public class SwitchCrossDto extends PartDto {
private JunctionDto common0;
private NodeDto common0;
private JunctionDto common1;
private NodeDto common1;
private JunctionDto output0;
private NodeDto output0;
private JunctionDto output1;
private NodeDto output1;
private boolean state;
public SwitchCrossDto(final SwitchCross switchCross) {
super(switchCross);
this.common0 = new JunctionDto(switchCross.getCommon0());
this.common1 = new JunctionDto(switchCross.getCommon1());
this.output0 = new JunctionDto(switchCross.getOutput0());
this.output1 = new JunctionDto(switchCross.getOutput1());
this.common0 = new NodeDto(switchCross.getCommon0());
this.common1 = new NodeDto(switchCross.getCommon1());
this.output0 = new NodeDto(switchCross.getOutput0());
this.output1 = new NodeDto(switchCross.getOutput1());
this.state = switchCross.isState();
}

View File

@ -0,0 +1,95 @@
package de.ph87.electro.circuit.part.parts;
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.node.Node;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.math3.linear.ArrayRealVector;
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.CircuitPainter.*;
import static java.lang.Math.round;
@Getter
public class Voltmeter extends Part {
private static final int FINGER_LENGTH = P65;
private static final RealVector ANCHOR = new ArrayRealVector(new double[]{P50, P75});
private static final RealVector FINGER = new ArrayRealVector(new double[]{FINGER_LENGTH - P05, 0});
private static final Stroke SCALE_STROKE = new BasicStroke(1);
private static final double DEGREES_RANGE = 90;
private final Node a;
private final Node b;
@Setter
private double min = -3;
@Setter
private double max = 3;
private RealVector tip;
public Voltmeter(final Point position) {
super("Voltmeter", position);
a = addNode("A", P10, P90);
b = addNode("B", P90, P90);
postCalculate(); // TODO remove
}
public Voltmeter(final PartDto dto) {
super(dto);
a = addNode("A", P10, P90);
b = addNode("B", P90, P90);
postCalculate(); // TODO remove
}
double x = 0;
@Override
public void _render(final Graphics2D g) {
// scale
final double DEGREES_OFFSET = 45;
drawCircle(g, P(ANCHOR), FINGER_LENGTH, DEGREES_OFFSET, DEGREES_RANGE, Color.BLACK, SCALE_STROKE, Color.white);
// finger
final double ratio = (getVoltage() - min) / (max - min);
final double degrees = -(DEGREES_OFFSET + DEGREES_RANGE * (1 - ratio));
final RealVector finger = RotationMatrix.rotateDegrees(FINGER, degrees);
tip = ANCHOR.add(finger);
drawLine(g, P(ANCHOR), P(tip), Color.BLACK, SCALE_STROKE);
// anchor
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() {
return !Double.isNaN(b.getVoltage()) && !Double.isNaN(a.getVoltage()) ? b.getVoltage() - a.getVoltage() : 0.0;
}
@Override
protected void _labels(final Graphics2D g) {
drawText(g, LABEL_FONT, "%.2f V".formatted(getVoltage()), P50, P90, Color.BLACK, orientation);
}
@Override
public List<InnerConnection> getInnerConnections() {
return List.of(new InnerConnection(a, b, MAX_RESISTANCE));
}
}

View File

@ -0,0 +1,32 @@
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.Setter;
import lombok.ToString;
@Getter
@ToString
@NoArgsConstructor
public class VoltmeterDto extends PartDto {
private NodeDto a;
private NodeDto b;
@Setter
private double min;
@Setter
private double max;
public VoltmeterDto(final Voltmeter voltmeter) {
a = new NodeDto(voltmeter.getA());
b = new NodeDto(voltmeter.getB());
min = voltmeter.getMin();
max = voltmeter.getMax();
}
}

View File

@ -1,7 +1,6 @@
package de.ph87.electro.circuit.wire;
import de.ph87.electro.circuit.part.Position;
import de.ph87.electro.circuit.part.junction.Junction;
import de.ph87.electro.circuit.part.node.Node;
import lombok.Getter;
import lombok.NonNull;
import lombok.Setter;
@ -11,51 +10,67 @@ import java.awt.*;
import java.awt.geom.Rectangle2D;
import static de.ph87.electro.CONFIG.*;
import static java.lang.Math.abs;
import static java.lang.Math.round;
import static java.lang.Math.*;
@ToString
public class Wire {
@Getter
@NonNull
private final Junction a;
private final Node a;
@Getter
@NonNull
private final Junction b;
private final Node b;
@Setter
private double current = Double.NaN;
public Wire(@NonNull final Junction a, @NonNull final Junction b) {
public Wire(@NonNull final Node a, @NonNull final Node b) {
this.a = a;
this.b = b;
a.getWires().add(this);
b.getWires().add(this);
}
public boolean intersects(final Position position) {
return position.distanceToLine(a.getPosition(), b.getPosition());
public boolean intersects(final Point position) {
return distanceToLine(position, a.getAbsolute(), b.getAbsolute());
}
public Junction getOpposite(final Junction junction) {
if (a == junction) {
public boolean distanceToLine(final Point point, final Point lineStart, final Point lineEnd) {
double dx = lineEnd.x - lineStart.x;
double dy = lineEnd.y - lineStart.y;
double lineLength = dx * dx + dy * dy;
if (lineLength == 0) {
return sqrt(pow(lineStart.x - point.x, 2) + pow(lineStart.y - point.y, 2)) <= WIRE_HOVER_STROKE_BACK.getLineWidth();
}
double t = ((point.x - lineStart.x) * dx + (point.y - lineStart.y) * dy) / lineLength;
t = Math.max(0, Math.min(1, t));
double closestX = lineStart.x + t * dx;
double closestY = lineStart.y + t * dy;
final double distance = sqrt(pow(closestX - point.x, 2) + pow(closestY - point.y, 2));
return distance <= WIRE_HOVER_STROKE_BACK.getLineWidth();
}
public Node getOpposite(final Node node) {
if (a == node) {
return b;
} else if (b == junction) {
} else if (b == node) {
return a;
}
throw new RuntimeException();
}
public double getCurrent(final Junction junction) {
return junction == a ? current : -current;
public double getCurrent(final Node node) {
return node == a ? current : -current;
}
public void draw(final Graphics2D g) {
g.setColor(a.getColor());
g.setStroke(WIRE_STROKE);
g.drawLine(a.getPosition().absolute.x, a.getPosition().absolute.y, b.getPosition().absolute.x, b.getPosition().absolute.y);
g.drawLine(a.getAbsolute().x, a.getAbsolute().y, b.getAbsolute().x, b.getAbsolute().y);
if (SHOW_WIRE_DETAILS) {
drawValues(g, "%.2f A".formatted(abs(current)), -0.5);
@ -66,8 +81,11 @@ public class Wire {
private void drawValues(final Graphics2D g, final String string, final double offset) {
g.setFont(LABEL_FONT);
final Rectangle2D bounds = g.getFontMetrics().getStringBounds(string, g);
final int mx = (a.getPosition().absolute.x + b.getPosition().absolute.x) / 2;
final int my = (a.getPosition().absolute.y + b.getPosition().absolute.y) / 2;
final Point aa = a.getAbsolute();
final Point bb = b.getAbsolute();
final int mx = (aa.x + bb.x) / 2;
final int my = (aa.y + bb.y) / 2;
final int bx = (int) round((mx - bounds.getWidth() / 2)) - 2;
final int by = (int) round((my - bounds.getHeight() / 2) + offset * bounds.getHeight()) - 2;

View File

@ -1,9 +1,7 @@
package de.ph87.electro.sidebar;
import de.ph87.electro.circuit.part.Part;
import de.ph87.electro.circuit.part.Position;
import de.ph87.electro.circuit.part.parts.*;
import lombok.Setter;
import javax.swing.*;
import java.awt.*;
@ -14,29 +12,26 @@ import static de.ph87.electro.CONFIG.*;
public class Sidebar extends JPanel {
@Setter
private Runnable repaintCallback = null;
public Sidebar(final Runnable newCircuit, final Runnable doSave) {
public Sidebar(final Runnable newCircuit) {
setPreferredSize(new Dimension(0, 200));
addButton("Neu", null, newCircuit, new Color(128, 202, 255));
addButton("Öffnen", null, doSave, new Color(128, 202, 255));
addButton("Speichern", null, doSave, new Color(128, 202, 255));
addToggle("Details", () -> SHOW_WIRE_DETAILS, v -> SHOW_WIRE_DETAILS = v);
addToggle("Namen", () -> SHOW_JUNCTION_NAMES, v -> SHOW_JUNCTION_NAMES = v);
addToggle("Spannungen", () -> SHOW_JUNCTION_VOLTAGES, v -> SHOW_JUNCTION_VOLTAGES = v);
addToggle("Namen", () -> SHOW_NODE_NAMES, v -> SHOW_NODE_NAMES = v);
addToggle("Spannungen", () -> SHOW_NODE_VOLTAGES, v -> SHOW_NODE_VOLTAGES = v);
addPart(new Battery(null, Position.ZERO));
addPart(new ConnectorCorner(null, Position.ZERO));
addPart(new ConnectorEdge(null, Position.ZERO));
addPart(new ConnectorMiddle(null, Position.ZERO));
addPart(new Light(null, Position.ZERO));
addPart(new Switch1x1(null, Position.ZERO));
addPart(new Switch1x2(null, Position.ZERO));
addPart(new SwitchCross(null, Position.ZERO));
addPart(new Poti(null, Position.ZERO));
final Point ZERO = new Point();
addPart(new Battery(ZERO));
addPart(new ConnectorCorner(ZERO));
addPart(new ConnectorEdge(ZERO));
addPart(new ConnectorMiddle(ZERO));
addPart(new Light(ZERO));
addPart(new Switch1x1(ZERO));
addPart(new Switch1x2(ZERO));
addPart(new SwitchCross(ZERO));
addPart(new Poti(ZERO));
addPart(new Voltmeter(ZERO));
}
@SuppressWarnings("UnusedReturnValue")
@ -48,10 +43,7 @@ public class Sidebar extends JPanel {
button.setBackground(new Color(255, 128, 128));
}
};
final Runnable click = () -> {
set.accept(!get.get());
triggerRepaint();
};
final Runnable click = () -> set.accept(!get.get());
return addButton(label, refresh, click, null);
}
@ -72,12 +64,6 @@ public class Sidebar extends JPanel {
return button;
}
private void triggerRepaint() {
if (repaintCallback != null) {
repaintCallback.run();
}
}
private void addPart(final Part part) {
final SidebarPart entry = new SidebarPart(part);
add(entry);

View File

@ -30,12 +30,11 @@ public class SidebarPart extends JPanel {
DnDConstants.ACTION_COPY_OR_MOVE,
event -> dragSource.startDrag(event, DragSource.DefaultCopyDrop, new StringSelection(part.getClass().getSimpleName()), null)
);
part.render();
}
@Override
public void paint(final Graphics g) {
part.paint((Graphics2D) g);
part.draw((Graphics2D) g);
g.setColor(Color.black);
g.drawRect(0, 0, RASTER - 1, RASTER - 1);
}

View File

@ -1,14 +1,14 @@
package de.ph87.electro.circuit;
import de.ph87.electro.circuit.calculation.Calculation;
import de.ph87.electro.circuit.part.junction.Junction;
import de.ph87.electro.circuit.part.node.Node;
import de.ph87.electro.circuit.part.parts.Battery;
import de.ph87.electro.circuit.part.parts.Light;
import de.ph87.electro.circuit.wire.Wire;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import static de.ph87.electro.circuit.part.Position.RST;
import static de.ph87.electro.CONFIG.RASTER;
@Slf4j
class CalculationServiceTest {
@ -16,8 +16,8 @@ class CalculationServiceTest {
@Test
void test() {
final Circuit circuit = new Circuit();
final Battery battery = circuit.addPart(new Battery(circuit, RST(0, 0)));
final Light light = circuit.addPart(new Light(circuit, RST(0, 1)));
final Battery battery = circuit.addPart(new Battery(RASTER(0, 0)));
final Light light = circuit.addPart(new Light(RASTER(0, 1)));
circuit.connect(battery.getMinus(), light.getA());
circuit.connect(battery.getPlus(), light.getB());
@ -26,11 +26,11 @@ class CalculationServiceTest {
circuit.streamParts().forEach(part -> {
log.info("");
log.info(part.toString());
for (final Junction junction : part.getJunctions()) {
log.info(" \"%s\" = %.2f V".formatted(junction.getName(), junction.getVoltage()));
for (final Wire wire : junction.getWires()) {
final Junction opposite = wire.getOpposite(junction);
log.info(" -> %s.%s = %.2f A".formatted(opposite.getOwner().getName(), opposite.getName(), wire.getCurrent(junction)));
for (final Node node : part.getNodes()) {
log.info(" \"%s\" = %.2f V".formatted(node.getName(), node.getVoltage()));
for (final Wire wire : node.getWires()) {
final Node opposite = wire.getOpposite(node);
log.info(" -> %s.%s = %.2f A".formatted(opposite.getOwner().getName(), opposite.getName(), wire.getCurrent(node)));
}
}
});

View File

@ -3,7 +3,7 @@ package de.ph87.electro.circuit.io;
import de.ph87.electro.circuit.Circuit;
import de.ph87.electro.circuit.CircuitIOService;
import de.ph87.electro.circuit.part.Part;
import de.ph87.electro.circuit.part.junction.Junction;
import de.ph87.electro.circuit.part.node.Node;
import de.ph87.electro.circuit.part.parts.Battery;
import de.ph87.electro.circuit.part.parts.Light;
import org.junit.jupiter.api.Test;
@ -13,8 +13,8 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import static de.ph87.electro.CONFIG.RASTER;
import static de.ph87.electro.circuit.CircuitIOService.serialize;
import static de.ph87.electro.circuit.part.Position.RST;
import static org.junit.jupiter.api.Assertions.assertEquals;
class CircuitIOServiceTest {
@ -22,8 +22,8 @@ class CircuitIOServiceTest {
@Test
void serialization() throws IOException {
final Circuit circuit = new Circuit();
final Battery battery = circuit.addPart(new Battery(circuit, RST(0, 0)));
final Light light = circuit.addPart(new Light(circuit, RST(0, 1)));
final Battery battery = circuit.addPart(new Battery(RASTER(0, 0)));
final Light light = circuit.addPart(new Light(RASTER(0, 1)));
circuit.connect(battery.getPlus(), light.getB());
circuit.connect(light.getA(), battery.getMinus());
check(circuit);
@ -38,6 +38,9 @@ class CircuitIOServiceTest {
final ByteArrayInputStream input = new ByteArrayInputStream(output.toByteArray());
final Circuit reloaded = CircuitIOService.load(null, input);
original.evaluate();
reloaded.evaluate();
assertEquals(original.getCreated(), reloaded.getCreated());
assertEquals(original.getPartCount(), reloaded.getPartCount());
original.streamParts().forEach(originalPart -> {
@ -47,20 +50,19 @@ class CircuitIOServiceTest {
assertEquals(originalPart.getName(), reloadedPart.getName());
assertEquals(originalPart.getPosition(), reloadedPart.getPosition());
assertEquals(originalPart.getOrientation(), reloadedPart.getOrientation());
assertEquals(originalPart.getJunctions().size(), reloadedPart.getJunctions().size());
for (final Junction originalJunction : originalPart.getJunctions()) {
System.out.printf(" - Junction: %s\n", originalJunction.getUuid());
final Junction reloadedJunction = reloadedPart.getJunctions().stream().filter(junction -> junction.getUuid().equals(originalJunction.getUuid())).findFirst().orElseThrow();
assertEquals(originalJunction.getUuid(), reloadedJunction.getUuid());
assertEquals(originalJunction.getName(), reloadedJunction.getName());
assertEquals(originalJunction.getRelative(), reloadedJunction.getRelative());
assertEquals(originalJunction.getPosition(), reloadedJunction.getPosition());
assertEquals(originalJunction.getWires().size(), reloadedJunction.getWires().size());
originalJunction.getWires().stream()
.map(originalWire -> originalWire.getOpposite(originalJunction))
assertEquals(originalPart.getNodes().size(), reloadedPart.getNodes().size());
for (final Node originalNode : originalPart.getNodes()) {
System.out.printf(" - Node: %s\n", originalNode.getUuid());
final Node reloadedNode = reloadedPart.getNodes().stream().filter(node -> node.getUuid().equals(originalNode.getUuid())).findFirst().orElseThrow();
assertEquals(originalNode.getUuid(), reloadedNode.getUuid());
assertEquals(originalNode.getName(), reloadedNode.getName());
assertEquals(originalNode.getAbsolute(), reloadedNode.getAbsolute());
assertEquals(originalNode.getWires().size(), reloadedNode.getWires().size());
originalNode.getWires().stream()
.map(originalWire -> originalWire.getOpposite(originalNode))
.forEach(originalDestination -> {
System.out.printf(" - Destinations: %s\n", originalDestination.getUuid());
final Junction reloadedDestination = reloadedJunction.getWires().stream().map(wire -> wire.getOpposite(reloadedJunction)).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.getOwner().getUuid(), reloadedDestination.getOwner().getUuid());
}

View File

@ -4,8 +4,8 @@ import de.ph87.electro.circuit.Circuit;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static de.ph87.electro.CONFIG.RASTER;
import static de.ph87.electro.circuit.AssertHelper.assertVoltage;
import static de.ph87.electro.circuit.part.Position.RST;
import static org.junit.jupiter.api.Assertions.assertFalse;
public class BatteryLightTest {
@ -14,9 +14,9 @@ public class BatteryLightTest {
private static final Circuit CIRCUIT = new Circuit();
private static final Battery battery = CIRCUIT.addPart(new Battery(CIRCUIT, RST(0, 0)));
private static final Battery battery = CIRCUIT.addPart(new Battery(RASTER(0, 0)));
private static final Light light = CIRCUIT.addPart(new Light(CIRCUIT, RST(0, 1)));
private static final Light light = CIRCUIT.addPart(new Light(RASTER(0, 1)));
@BeforeAll
public static void setUp() {
@ -26,6 +26,8 @@ public class BatteryLightTest {
@Test
void test() {
CIRCUIT.evaluate();
assertVoltage(VOLTAGE, battery.getPlus().getVoltage());
assertVoltage(VOLTAGE, light.getB().getVoltage());
assertVoltage(VOLTAGE, light.getPotentialDifference());

View File

@ -4,8 +4,8 @@ import de.ph87.electro.circuit.Circuit;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static de.ph87.electro.CONFIG.RASTER;
import static de.ph87.electro.circuit.AssertHelper.assertVoltage;
import static de.ph87.electro.circuit.part.Position.RST;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
@ -15,11 +15,11 @@ public class BatterySwitcher1x1Test {
private static final Circuit CIRCUIT = new Circuit();
private static final Battery battery = CIRCUIT.addPart(new Battery(CIRCUIT, RST(0, 0)));
private static final Battery battery = CIRCUIT.addPart(new Battery(RASTER(0, 0)));
private static final Switch1x1 switcher = CIRCUIT.addPart(new Switch1x1(CIRCUIT, RST(0, 1)));
private static final Switch1x1 switcher = CIRCUIT.addPart(new Switch1x1(RASTER(0, 1)));
private static final Light light = CIRCUIT.addPart(new Light(CIRCUIT, RST(1, 0)));
private static final Light light = CIRCUIT.addPart(new Light(RASTER(1, 0)));
@BeforeAll
public static void setUp() {
@ -42,6 +42,7 @@ public class BatterySwitcher1x1Test {
final double voltage = state ? VOLTAGE : 0.0;
switcher.setState(state);
CIRCUIT.evaluate();
assertEquals(state, switcher.isState());

View File

@ -4,8 +4,8 @@ import de.ph87.electro.circuit.Circuit;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static de.ph87.electro.CONFIG.RASTER;
import static de.ph87.electro.circuit.AssertHelper.assertVoltage;
import static de.ph87.electro.circuit.part.Position.RST;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
@ -15,13 +15,13 @@ public class BatterySwitcher1x2Test {
private static final Circuit CIRCUIT = new Circuit();
private static final Battery battery = CIRCUIT.addPart(new Battery(CIRCUIT, RST(0, 0)));
private static final Battery battery = CIRCUIT.addPart(new Battery(RASTER(0, 0)));
private static final Switch1x2 switcher = CIRCUIT.addPart(new Switch1x2(CIRCUIT, RST(0, 2)));
private static final Switch1x2 switcher = CIRCUIT.addPart(new Switch1x2(RASTER(0, 2)));
private static final Light light0 = CIRCUIT.addPart(new Light(CIRCUIT, RST(1, 1)));
private static final Light light0 = CIRCUIT.addPart(new Light(RASTER(1, 1)));
private static final Light light1 = CIRCUIT.addPart(new Light(CIRCUIT, RST(1, 3)));
private static final Light light1 = CIRCUIT.addPart(new Light(RASTER(1, 3)));
@BeforeAll
public static void setUp() {
@ -47,6 +47,7 @@ public class BatterySwitcher1x2Test {
final double voltage1 = state ? VOLTAGE : 0.0;
switcher.setState(state);
CIRCUIT.evaluate();
assertEquals(state, switcher.isState());

View File

@ -4,8 +4,8 @@ import de.ph87.electro.circuit.Circuit;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static de.ph87.electro.CONFIG.RASTER;
import static de.ph87.electro.circuit.AssertHelper.assertVoltage;
import static de.ph87.electro.circuit.part.Position.RST;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
@ -15,13 +15,13 @@ public class BatterySwitcher2x2Test {
private static final Circuit CIRCUIT = new Circuit();
private static final Battery battery = CIRCUIT.addPart(new Battery(CIRCUIT, RST(0, 0)));
private static final Battery battery = CIRCUIT.addPart(new Battery(RASTER(0, 0)));
private static final Switch1x2 switcher0 = CIRCUIT.addPart(new Switch1x2(CIRCUIT, RST(0, 1)));
private static final Switch1x2 switcher0 = CIRCUIT.addPart(new Switch1x2(RASTER(0, 1)));
private static final Switch1x2 switcher1 = CIRCUIT.addPart(new Switch1x2(CIRCUIT, RST(1, 1)));
private static final Switch1x2 switcher1 = CIRCUIT.addPart(new Switch1x2(RASTER(1, 1)));
private static final Light light = CIRCUIT.addPart(new Light(CIRCUIT, RST(1, 0)));
private static final Light light = CIRCUIT.addPart(new Light(RASTER(1, 0)));
@BeforeAll
public static void setUp() {
@ -59,6 +59,7 @@ public class BatterySwitcher2x2Test {
switcher0.setState(state0);
switcher1.setState(state1);
CIRCUIT.evaluate();
assertEquals(state0, switcher0.isState());
assertEquals(state1, switcher1.isState());

View File

@ -4,8 +4,8 @@ import de.ph87.electro.circuit.Circuit;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static de.ph87.electro.CONFIG.RASTER;
import static de.ph87.electro.circuit.AssertHelper.assertVoltage;
import static de.ph87.electro.circuit.part.Position.RST;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
@ -15,15 +15,15 @@ public class BatterySwitcherCrossTest {
private static final Circuit CIRCUIT = new Circuit();
private static final Battery battery = CIRCUIT.addPart(new Battery(CIRCUIT, RST(0, 0)));
private static final Battery battery = CIRCUIT.addPart(new Battery(RASTER(0, 0)));
private static final Switch1x2 switcher0 = CIRCUIT.addPart(new Switch1x2(CIRCUIT, RST(0, 1)));
private static final Switch1x2 switcher0 = CIRCUIT.addPart(new Switch1x2(RASTER(0, 1)));
private static final SwitchCross switcherX = CIRCUIT.addPart(new SwitchCross(CIRCUIT, RST(1, 1)));
private static final SwitchCross switcherX = CIRCUIT.addPart(new SwitchCross(RASTER(1, 1)));
private static final Switch1x2 switcher1 = CIRCUIT.addPart(new Switch1x2(CIRCUIT, RST(2, 1)));
private static final Switch1x2 switcher1 = CIRCUIT.addPart(new Switch1x2(RASTER(2, 1)));
private static final Light light = CIRCUIT.addPart(new Light(CIRCUIT, RST(2, 0)));
private static final Light light = CIRCUIT.addPart(new Light(RASTER(2, 0)));
@BeforeAll
public static void setUp() {
@ -86,6 +86,7 @@ public class BatterySwitcherCrossTest {
switcher0.setState(state0);
switcherX.setState(stateX);
switcher1.setState(state1);
CIRCUIT.evaluate();
assertEquals(state0, switcher0.isState());
assertEquals(stateX, switcherX.isState());