diff --git a/src/main/java/de/ph87/electro/Window.java b/src/main/java/de/ph87/electro/Window.java index 8ccbcac..fb8f2a2 100644 --- a/src/main/java/de/ph87/electro/Window.java +++ b/src/main/java/de/ph87/electro/Window.java @@ -1,5 +1,6 @@ package de.ph87.electro; +import de.ph87.electro.circuit.CircuitIOService; import de.ph87.electro.circuit.CircuitPanel; import de.ph87.electro.sidebar.Sidebar; @@ -17,7 +18,7 @@ public class Window extends JFrame { setExtendedState(MAXIMIZED_BOTH); final CircuitPanel circuitPanel = new CircuitPanel(); - final Sidebar sidebar = new Sidebar(circuitPanel::newCircuit, circuitPanel::save); + final Sidebar sidebar = new Sidebar(circuitPanel::newCircuit, () -> CircuitIOService.save(circuitPanel.getCircuit())); final JSplitPane splitter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, sidebar, circuitPanel); sidebar.setRepaintCallback(circuitPanel::repaint); diff --git a/src/main/java/de/ph87/electro/circuit/Circuit.java b/src/main/java/de/ph87/electro/circuit/Circuit.java index 581c4c5..e71f245 100644 --- a/src/main/java/de/ph87/electro/circuit/Circuit.java +++ b/src/main/java/de/ph87/electro/circuit/Circuit.java @@ -9,11 +9,9 @@ import de.ph87.electro.circuit.wire.Wire; import de.ph87.electro.circuit.wire.WireDto; import lombok.Getter; import lombok.NonNull; -import lombok.extern.slf4j.Slf4j; +import lombok.Setter; import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; @@ -21,9 +19,6 @@ import java.util.List; import java.util.Optional; import java.util.stream.Stream; -import static de.ph87.electro.circuit.CircuitIOService.write; - -@Slf4j public class Circuit { @Getter @@ -34,13 +29,19 @@ public class Circuit { private final List wires = new ArrayList<>(); + @Setter + @Getter private boolean dirty = false; + @Setter + @Getter + private File file = null; + public Circuit() { this.created = ZonedDateTime.now().format(DateTimeFormatter.ISO_OFFSET_DATE_TIME); } - public Circuit(final CircuitDto dto) { + public Circuit(final File file, final CircuitDto dto) { created = dto.getCreated(); for (PartDto partDto : dto.getParts()) { final Part part = Part.of(this, partDto); @@ -96,22 +97,6 @@ public class Circuit { parts.forEach(Part::render); } - public void save() { - if (!dirty) { - return; - } - try { - final File file = new File("./data/%s.json".formatted(created)); - if (file.getParentFile().mkdirs()) { - log.info("Directory created: {}", file.getParent()); - } - write(this, new FileOutputStream(file)); - dirty = false; - } catch (IOException e) { - log.error(e.toString()); - } - } - private void verifyFree(final Position position) { if (parts.stream().anyMatch(part -> part.getPosition().equals(position))) { throw new RuntimeException(); @@ -146,4 +131,11 @@ public class Circuit { return wires.stream().filter(wire -> wire.intersects(position)).findFirst(); } + public File getPreviewFile() { + if (file == null) { + return null; + } + return new File(file.getAbsolutePath().replaceAll("\\.json$", ".png")); + } + } diff --git a/src/main/java/de/ph87/electro/circuit/CircuitIOService.java b/src/main/java/de/ph87/electro/circuit/CircuitIOService.java index 1460a42..2fbb40e 100644 --- a/src/main/java/de/ph87/electro/circuit/CircuitIOService.java +++ b/src/main/java/de/ph87/electro/circuit/CircuitIOService.java @@ -1,21 +1,55 @@ package de.ph87.electro.circuit; import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.*; +import static de.ph87.electro.circuit.CircuitPainter.paintCircuit; + +@Slf4j public class CircuitIOService { private static final ObjectMapper objectMapper = new ObjectMapper().findAndRegisterModules(); - public static void write(final Circuit circuit, final OutputStream stream) throws IOException { + 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()))); + } + try { + if (circuit.getFile().getParentFile().mkdirs()) { + 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); + try { + ImageIO.write(img, "PNG", circuit.getPreviewFile()); + } catch (IOException e) { + log.error(e.toString()); + } + } + + public static Circuit load(final File file) throws IOException { + return load(null, new FileInputStream(file)); + } + + public static Circuit load(final File file, final InputStream stream) throws IOException { + final CircuitDto dto = objectMapper.readValue(stream, CircuitDto.class); + return new Circuit(file, dto); + } + + public static void serialize(final Circuit circuit, final OutputStream stream) throws IOException { objectMapper.writeValue(stream, new CircuitDto(circuit)); } - public static Circuit read(final InputStream stream) throws IOException { - return new Circuit(objectMapper.readValue(stream, CircuitDto.class)); - } - } diff --git a/src/main/java/de/ph87/electro/circuit/CircuitPainter.java b/src/main/java/de/ph87/electro/circuit/CircuitPainter.java new file mode 100644 index 0000000..da38444 --- /dev/null +++ b/src/main/java/de/ph87/electro/circuit/CircuitPainter.java @@ -0,0 +1,86 @@ +package de.ph87.electro.circuit; + +import de.ph87.electro.circuit.part.Part; +import de.ph87.electro.circuit.part.junction.Junction; + +import java.awt.*; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; + +import static de.ph87.electro.CONFIG.*; + +public class CircuitPainter { + + public static BufferedImage paintCircuit(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); + return img; + } + + public static void paintCircuit(final Circuit circuit, final Graphics2D g, final int w, final int h) { + drawBack(g, w, h); + drawParts(circuit, g); + drawRaster(g, w, h); + drawWires(circuit, g); + drawVoltages(circuit, g); + } + + private static void drawBack(final Graphics2D g, final int w, final int h) { + g.setColor(Color.white); + g.fillRect(0, 0, w, h); + } + + private static void drawParts(final Circuit circuit, final Graphics2D g) { + circuit.streamParts().forEach(part -> part.paint(g)); + } + + private static void drawRaster(final Graphics2D g, final int w, final int h) { + g.setStroke(RASTER_STROKE); + g.setColor(RASTER_COLOR); + for (int x = 0; x < w; x += RASTER) { + g.drawLine(x, 0, x, h); + } + for (int y = 0; y < h; y += RASTER) { + g.drawLine(0, y, w, y); + } + } + + private static void drawWires(final Circuit circuit, final Graphics2D g) { + circuit.streamWires().forEach(wire -> wire.draw(g)); + } + + private static void drawVoltages(final Circuit circuit, final Graphics2D g) { + if (!SHOW_JUNCTION_VOLTAGES && !SHOW_JUNCTION_NAMES) { + return; + } + + g.setColor(Color.BLACK); + g.setFont(LABEL_FONT); + final int third = g.getFont().getSize() / 3; + + for (Part part : circuit.getParts()) { + for (final Junction junction : part.getJunctions()) { + if (SHOW_JUNCTION_NAMES) { + int offsetY = third; + if (SHOW_JUNCTION_VOLTAGES) { + offsetY -= third; + } + final String string = junction.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); + } + if (SHOW_JUNCTION_VOLTAGES) { + int offsetY = third; + if (SHOW_JUNCTION_NAMES) { + offsetY += 2 * third; + } + final String string = "%.1fV".formatted(junction.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); + } + } + } + } + +} diff --git a/src/main/java/de/ph87/electro/circuit/CircuitPanel.java b/src/main/java/de/ph87/electro/circuit/CircuitPanel.java index ef83389..1f662f1 100644 --- a/src/main/java/de/ph87/electro/circuit/CircuitPanel.java +++ b/src/main/java/de/ph87/electro/circuit/CircuitPanel.java @@ -1,7 +1,5 @@ package de.ph87.electro.circuit; -import de.ph87.electro.circuit.part.Part; -import de.ph87.electro.circuit.part.junction.Junction; import de.ph87.electro.circuit.part.parts.Battery; import de.ph87.electro.circuit.part.parts.Light; import lombok.Getter; @@ -9,9 +7,8 @@ import lombok.extern.slf4j.Slf4j; import javax.swing.*; import java.awt.*; -import java.awt.geom.Rectangle2D; -import static de.ph87.electro.CONFIG.*; +import static de.ph87.electro.circuit.CircuitPainter.paintCircuit; import static de.ph87.electro.circuit.part.Position.RST; @Slf4j @@ -34,81 +31,14 @@ public class CircuitPanel extends JPanel { @Override public void paint(final Graphics _g) { final Graphics2D g = (Graphics2D) _g; - final int w = getWidth(); - final int h = getHeight(); - drawBack(g, w, h); - drawParts(g); - drawRaster(g, w, h); - drawWires(g); - drawVoltages(g); + paintCircuit(circuit, g, getWidth(), getHeight()); mouseAdapter.drawHover(g); mouseAdapter.drawDrag(g); } - private void drawBack(final Graphics2D g, final int w, final int h) { - g.setColor(Color.white); - g.fillRect(0, 0, w, h); - } - - private void drawParts(final Graphics2D g) { - circuit.streamParts().forEach(part -> part.paint(g)); - } - - private void drawRaster(final Graphics2D g, final int w, final int h) { - g.setStroke(RASTER_STROKE); - g.setColor(RASTER_COLOR); - for (int x = 0; x < w; x += RASTER) { - g.drawLine(x, 0, x, h); - } - for (int y = 0; y < h; y += RASTER) { - g.drawLine(0, y, w, y); - } - } - - private void drawWires(final Graphics2D g) { - circuit.streamWires().forEach(wire -> wire.draw(g)); - } - - private void drawVoltages(final Graphics2D g) { - if (!SHOW_JUNCTION_VOLTAGES && !SHOW_JUNCTION_NAMES) { - return; - } - - g.setColor(Color.BLACK); - g.setFont(LABEL_FONT); - final int third = g.getFont().getSize() / 3; - - for (Part part : circuit.getParts()) { - for (final Junction junction : part.getJunctions()) { - if (SHOW_JUNCTION_NAMES) { - int offsetY = third; - if (SHOW_JUNCTION_VOLTAGES) { - offsetY -= third; - } - final String string = junction.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); - } - if (SHOW_JUNCTION_VOLTAGES) { - int offsetY = third; - if (SHOW_JUNCTION_NAMES) { - offsetY += 2 * third; - } - final String string = "%.1fV".formatted(junction.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); - } - } - } - } - public void newCircuit() { circuit = new Circuit(); repaint(); } - public void save() { - circuit.save(); - } - } diff --git a/src/test/java/de/ph87/electro/circuit/io/CircuitIOServiceTest.java b/src/test/java/de/ph87/electro/circuit/io/CircuitIOServiceTest.java index 829d40b..3d42974 100644 --- a/src/test/java/de/ph87/electro/circuit/io/CircuitIOServiceTest.java +++ b/src/test/java/de/ph87/electro/circuit/io/CircuitIOServiceTest.java @@ -1,6 +1,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.parts.Battery; @@ -12,8 +13,7 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; -import static de.ph87.electro.circuit.CircuitIOService.read; -import static de.ph87.electro.circuit.CircuitIOService.write; +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; @@ -31,12 +31,12 @@ class CircuitIOServiceTest { private void check(final Circuit original) throws IOException { final ByteArrayOutputStream output = new ByteArrayOutputStream(); - write(original, output); + serialize(original, output); System.out.println(output.toString(StandardCharsets.UTF_8)); final ByteArrayInputStream input = new ByteArrayInputStream(output.toByteArray()); - final Circuit reloaded = read(input); + final Circuit reloaded = CircuitIOService.load(null, input); assertEquals(original.getCreated(), reloaded.getCreated()); assertEquals(original.getPartCount(), reloaded.getPartCount());