saving JSON plus PNG preview

This commit is contained in:
Patrick Haßel 2024-05-23 11:17:04 +02:00
parent ad7562e3d2
commit 0eb53de222
6 changed files with 151 additions and 108 deletions

View File

@ -1,5 +1,6 @@
package de.ph87.electro; package de.ph87.electro;
import de.ph87.electro.circuit.CircuitIOService;
import de.ph87.electro.circuit.CircuitPanel; import de.ph87.electro.circuit.CircuitPanel;
import de.ph87.electro.sidebar.Sidebar; import de.ph87.electro.sidebar.Sidebar;
@ -17,7 +18,7 @@ public class Window extends JFrame {
setExtendedState(MAXIMIZED_BOTH); setExtendedState(MAXIMIZED_BOTH);
final CircuitPanel circuitPanel = new CircuitPanel(); 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); final JSplitPane splitter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, sidebar, circuitPanel);
sidebar.setRepaintCallback(circuitPanel::repaint); sidebar.setRepaintCallback(circuitPanel::repaint);

View File

@ -9,11 +9,9 @@ import de.ph87.electro.circuit.wire.Wire;
import de.ph87.electro.circuit.wire.WireDto; import de.ph87.electro.circuit.wire.WireDto;
import lombok.Getter; import lombok.Getter;
import lombok.NonNull; import lombok.NonNull;
import lombok.extern.slf4j.Slf4j; import lombok.Setter;
import java.io.File; import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.ArrayList; import java.util.ArrayList;
@ -21,9 +19,6 @@ import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Stream; import java.util.stream.Stream;
import static de.ph87.electro.circuit.CircuitIOService.write;
@Slf4j
public class Circuit { public class Circuit {
@Getter @Getter
@ -34,13 +29,19 @@ public class Circuit {
private final List<Wire> wires = new ArrayList<>(); private final List<Wire> wires = new ArrayList<>();
@Setter
@Getter
private boolean dirty = false; private boolean dirty = false;
@Setter
@Getter
private File file = null;
public Circuit() { public Circuit() {
this.created = ZonedDateTime.now().format(DateTimeFormatter.ISO_OFFSET_DATE_TIME); 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(); created = dto.getCreated();
for (PartDto partDto : dto.getParts()) { for (PartDto partDto : dto.getParts()) {
final Part part = Part.of(this, partDto); final Part part = Part.of(this, partDto);
@ -96,22 +97,6 @@ public class Circuit {
parts.forEach(Part::render); 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) { private void verifyFree(final Position position) {
if (parts.stream().anyMatch(part -> part.getPosition().equals(position))) { if (parts.stream().anyMatch(part -> part.getPosition().equals(position))) {
throw new RuntimeException(); throw new RuntimeException();
@ -146,4 +131,11 @@ public class Circuit {
return wires.stream().filter(wire -> wire.intersects(position)).findFirst(); 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"));
}
} }

View File

@ -1,21 +1,55 @@
package de.ph87.electro.circuit; package de.ph87.electro.circuit;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException; import javax.imageio.ImageIO;
import java.io.InputStream; import java.awt.image.BufferedImage;
import java.io.OutputStream; import java.io.*;
import static de.ph87.electro.circuit.CircuitPainter.paintCircuit;
@Slf4j
public class CircuitIOService { public class CircuitIOService {
private static final ObjectMapper objectMapper = new ObjectMapper().findAndRegisterModules(); 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)); objectMapper.writeValue(stream, new CircuitDto(circuit));
} }
public static Circuit read(final InputStream stream) throws IOException {
return new Circuit(objectMapper.readValue(stream, CircuitDto.class));
}
} }

View File

@ -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);
}
}
}
}
}

View File

@ -1,7 +1,5 @@
package de.ph87.electro.circuit; 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.Battery;
import de.ph87.electro.circuit.part.parts.Light; import de.ph87.electro.circuit.part.parts.Light;
import lombok.Getter; import lombok.Getter;
@ -9,9 +7,8 @@ import lombok.extern.slf4j.Slf4j;
import javax.swing.*; import javax.swing.*;
import java.awt.*; 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; import static de.ph87.electro.circuit.part.Position.RST;
@Slf4j @Slf4j
@ -34,81 +31,14 @@ public class CircuitPanel extends JPanel {
@Override @Override
public void paint(final Graphics _g) { public void paint(final Graphics _g) {
final Graphics2D g = (Graphics2D) _g; final Graphics2D g = (Graphics2D) _g;
final int w = getWidth(); paintCircuit(circuit, g, getWidth(), getHeight());
final int h = getHeight();
drawBack(g, w, h);
drawParts(g);
drawRaster(g, w, h);
drawWires(g);
drawVoltages(g);
mouseAdapter.drawHover(g); mouseAdapter.drawHover(g);
mouseAdapter.drawDrag(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() { public void newCircuit() {
circuit = new Circuit(); circuit = new Circuit();
repaint(); repaint();
} }
public void save() {
circuit.save();
}
} }

View File

@ -1,6 +1,7 @@
package de.ph87.electro.circuit.io; package de.ph87.electro.circuit.io;
import de.ph87.electro.circuit.Circuit; import de.ph87.electro.circuit.Circuit;
import de.ph87.electro.circuit.CircuitIOService;
import de.ph87.electro.circuit.part.Part; import de.ph87.electro.circuit.part.Part;
import de.ph87.electro.circuit.part.junction.Junction; import de.ph87.electro.circuit.part.junction.Junction;
import de.ph87.electro.circuit.part.parts.Battery; import de.ph87.electro.circuit.part.parts.Battery;
@ -12,8 +13,7 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import static de.ph87.electro.circuit.CircuitIOService.read; import static de.ph87.electro.circuit.CircuitIOService.serialize;
import static de.ph87.electro.circuit.CircuitIOService.write;
import static de.ph87.electro.circuit.part.Position.RST; import static de.ph87.electro.circuit.part.Position.RST;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
@ -31,12 +31,12 @@ class CircuitIOServiceTest {
private void check(final Circuit original) throws IOException { private void check(final Circuit original) throws IOException {
final ByteArrayOutputStream output = new ByteArrayOutputStream(); final ByteArrayOutputStream output = new ByteArrayOutputStream();
write(original, output); serialize(original, output);
System.out.println(output.toString(StandardCharsets.UTF_8)); System.out.println(output.toString(StandardCharsets.UTF_8));
final ByteArrayInputStream input = new ByteArrayInputStream(output.toByteArray()); 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.getCreated(), reloaded.getCreated());
assertEquals(original.getPartCount(), reloaded.getPartCount()); assertEquals(original.getPartCount(), reloaded.getPartCount());