mirror of
https://github.com/hneemann/Digital.git
synced 2025-09-17 08:55:05 -04:00
added a simple SVG export
This commit is contained in:
parent
e502cc2817
commit
b77bf92c03
@ -99,10 +99,15 @@ public class Circuit implements Drawable {
|
|||||||
dotsPresent = true;
|
dotsPresent = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
graphic.openGroup();
|
||||||
for (Wire w : wires)
|
for (Wire w : wires)
|
||||||
w.drawTo(graphic);
|
w.drawTo(graphic);
|
||||||
for (VisualElement p : visualElements)
|
graphic.closeGroup();
|
||||||
|
for (VisualElement p : visualElements) {
|
||||||
|
graphic.openGroup();
|
||||||
p.drawTo(graphic);
|
p.drawTo(graphic);
|
||||||
|
graphic.closeGroup();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void add(VisualElement visualElement) {
|
public void add(VisualElement visualElement) {
|
||||||
|
11
src/main/java/de/neemann/digital/draw/graphics/Exporter.java
Normal file
11
src/main/java/de/neemann/digital/draw/graphics/Exporter.java
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package de.neemann.digital.draw.graphics;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author hneemann
|
||||||
|
*/
|
||||||
|
public interface Exporter {
|
||||||
|
Graphic create(File file, Vector min, Vector max) throws IOException;
|
||||||
|
}
|
@ -12,4 +12,12 @@ public interface Graphic {
|
|||||||
void drawCircle(Vector p1, Vector p2, Style style);
|
void drawCircle(Vector p1, Vector p2, Style style);
|
||||||
|
|
||||||
void drawText(Vector p1, Vector p2, String text, Orientation orientation, Style style);
|
void drawText(Vector p1, Vector p2, String text, Orientation orientation, Style style);
|
||||||
|
|
||||||
|
default void openGroup() {
|
||||||
|
}
|
||||||
|
|
||||||
|
default void closeGroup() {
|
||||||
|
}
|
||||||
|
|
||||||
|
;
|
||||||
}
|
}
|
||||||
|
212
src/main/java/de/neemann/digital/draw/graphics/GraphicSVG.java
Normal file
212
src/main/java/de/neemann/digital/draw/graphics/GraphicSVG.java
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
package de.neemann.digital.draw.graphics;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author hneemann
|
||||||
|
*/
|
||||||
|
public class GraphicSVG implements Graphic, Closeable {
|
||||||
|
private static final int TEXTSIZE = 20;
|
||||||
|
private static final int DEF_SCALE = 100;
|
||||||
|
private final BufferedWriter w;
|
||||||
|
|
||||||
|
public GraphicSVG(File file, Vector min, Vector max) throws IOException {
|
||||||
|
this(file, min, max, null, DEF_SCALE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GraphicSVG(File file, Vector min, Vector max, File source) throws IOException {
|
||||||
|
this(file, min, max, source, DEF_SCALE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GraphicSVG(File file, Vector min, Vector max, File source, int svgScale) throws IOException {
|
||||||
|
w = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "utf-8"));
|
||||||
|
w.write("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n" +
|
||||||
|
"<!-- Created with Digital by H.Neemann -->\n");
|
||||||
|
w.write("<!-- created: " + new Date() + " -->\n");
|
||||||
|
if (source != null) {
|
||||||
|
w.write("<!-- source: " + source.getPath() + " -->\n");
|
||||||
|
}
|
||||||
|
w.write("\n" +
|
||||||
|
"<svg\n" +
|
||||||
|
" xmlns:svg=\"http://www.w3.org/2000/svg\"\n" +
|
||||||
|
" xmlns=\"http://www.w3.org/2000/svg\"\n");
|
||||||
|
|
||||||
|
double width = (max.x - min.x) * svgScale / 900;
|
||||||
|
double height = (max.y - min.y) * svgScale / 900;
|
||||||
|
|
||||||
|
w.write(" width=\"" + width + "mm\"\n" +
|
||||||
|
" height=\"" + height + "mm\"\n" +
|
||||||
|
" viewBox=\"" + min.x + " " + min.y + " " + (max.x - min.x) + " " + (max.y - min.y) + "\">\n");
|
||||||
|
w.write("<g>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
w.write("</g>\n");
|
||||||
|
w.write("</svg>");
|
||||||
|
w.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drawLine(Vector p1, Vector p2, Style style) {
|
||||||
|
try {
|
||||||
|
w.write("<line x1=\"" + p1.x + "\" y1=\"" + p1.y + "\" x2=\"" + p2.x + "\" y2=\"" + p2.y + "\" stroke=\"black\" stroke-linecap=\"square\" stroke-width=\"" + getStrokeWidth(style) + "\"");
|
||||||
|
// if (style.isDashed())
|
||||||
|
// addStrokeDash(w, style.getDashArray());
|
||||||
|
w.write(" />\n");
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drawPolygon(Polygon p, Style style) {
|
||||||
|
try {
|
||||||
|
w.write("<path d=\"M " + str(p.get(0)));
|
||||||
|
for (int i = 1; i < p.size(); i++)
|
||||||
|
w.write(" L " + str(p.get(i)));
|
||||||
|
|
||||||
|
if (p.isClosed())
|
||||||
|
w.write(" Z");
|
||||||
|
|
||||||
|
w.write("\"");
|
||||||
|
// if (style.isDashed())
|
||||||
|
// addStrokeDash(w, style.getDashArray());
|
||||||
|
w.write(" stroke=\"black\" stroke-width=\"" + getStrokeWidth(style) + "\" fill=\"none\"/>\n");
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double getStrokeWidth(Style style) {
|
||||||
|
return style.getThickness() * 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drawCircle(Vector p1, Vector p2, Style style) {
|
||||||
|
try {
|
||||||
|
Vector c = p1.add(p2).div(2);
|
||||||
|
double r = Math.abs(p2.sub(p1).x) / 2;
|
||||||
|
if (style.isFilled())
|
||||||
|
w.write("<circle cx=\"" + c.x + "\" cy=\"" + c.y + "\" r=\"" + r + "\" stroke=\"black\" stroke-width=\"" + getStrokeWidth(style) + "\" fill=\"black\" />\n");
|
||||||
|
else {
|
||||||
|
w.write("<circle cx=\"" + c.x + "\" cy=\"" + c.y + "\" r=\"" + r + "\" stroke=\"black\" stroke-width=\"" + getStrokeWidth(style) + "\" fill=\"none\"");
|
||||||
|
// if (style.isDashed())
|
||||||
|
// addStrokeDash(w, style.getDashArray());
|
||||||
|
w.write(" />\n");
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drawText(Vector p1, Vector p2, String text, Orientation orientation, Style style) {
|
||||||
|
if (text != null && text.length() > 0)
|
||||||
|
try {
|
||||||
|
text = escapeXML(text);
|
||||||
|
|
||||||
|
boolean rotateText = false;
|
||||||
|
if (p1.y == p2.y) { // 0 and 180 deg
|
||||||
|
if (p1.x > p2.x) // 180
|
||||||
|
orientation = orientation.rot(2);
|
||||||
|
} else {
|
||||||
|
if (p1.y < p2.y) // 270
|
||||||
|
orientation = orientation.rot(2);
|
||||||
|
else // 90
|
||||||
|
orientation = orientation.rot(0);
|
||||||
|
rotateText = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector p = new Vector(p1);
|
||||||
|
switch (orientation.getY()) {
|
||||||
|
case 1:
|
||||||
|
p = p.add(0, style.getFontSize() / 2 - style.getFontSize() / 8);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
p = p.add(0, style.getFontSize() * 3 / 4);
|
||||||
|
break;
|
||||||
|
case 0:
|
||||||
|
//p = p.add(0, -style.getFontSize() / 4);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rotateText)
|
||||||
|
w.write("<text text-anchor=\"" + getAchor(orientation.getX()) + "\" x=\"" + p.x + "\" y=\"" + p.y + "\" fill=\"black\" style=\"font-size:" + style.getFontSize() + "\" transform=\"rotate(-90," + str(p1) + ")\" >" + text + "</text>\n");
|
||||||
|
else
|
||||||
|
w.write("<text text-anchor=\"" + getAchor(orientation.getX()) + "\" x=\"" + p.x + "\" y=\"" + p.y + "\" fill=\"black\" style=\"font-size:" + style.getFontSize() + "\">" + text + "</text>\n");
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String escapeXML(String text) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (int i = 0; i < text.length(); i++) {
|
||||||
|
char c = text.charAt(i);
|
||||||
|
switch (c) {
|
||||||
|
case '&':
|
||||||
|
sb.append("&");
|
||||||
|
break;
|
||||||
|
case '<':
|
||||||
|
sb.append("<");
|
||||||
|
break;
|
||||||
|
case '>':
|
||||||
|
sb.append(">");
|
||||||
|
break;
|
||||||
|
case '"':
|
||||||
|
sb.append(""");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
sb.append(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getAchor(int x) {
|
||||||
|
switch (x) {
|
||||||
|
case 1:
|
||||||
|
return "middle";
|
||||||
|
case 2:
|
||||||
|
return "end";
|
||||||
|
default:
|
||||||
|
return "start";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void openGroup() {
|
||||||
|
try {
|
||||||
|
w.write("<g>\n");
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void closeGroup() {
|
||||||
|
try {
|
||||||
|
w.write("</g>\n");
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static void addStrokeDash(Writer w, int[] dashArray) throws IOException {
|
||||||
|
w.write(" stroke-dasharray=\"");
|
||||||
|
for (int i = 0; i < dashArray.length; i++) {
|
||||||
|
if (i != 0) w.write(',');
|
||||||
|
w.write(Integer.toString(dashArray[i]));
|
||||||
|
}
|
||||||
|
w.write('"');
|
||||||
|
}
|
||||||
|
|
||||||
|
private String str(Vector p) {
|
||||||
|
return p.x + "," + p.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -43,4 +43,13 @@ public class Polygon {
|
|||||||
public ArrayList<Vector> getPoints() {
|
public ArrayList<Vector> getPoints() {
|
||||||
return points;
|
return points;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Vector get(int i) {
|
||||||
|
return points.get(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int size() {
|
||||||
|
return points.size();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,10 @@ import de.neemann.digital.core.wiring.Clock;
|
|||||||
import de.neemann.digital.draw.elements.Circuit;
|
import de.neemann.digital.draw.elements.Circuit;
|
||||||
import de.neemann.digital.draw.elements.PinException;
|
import de.neemann.digital.draw.elements.PinException;
|
||||||
import de.neemann.digital.draw.elements.PinOrder;
|
import de.neemann.digital.draw.elements.PinOrder;
|
||||||
|
import de.neemann.digital.draw.graphics.Exporter;
|
||||||
|
import de.neemann.digital.draw.graphics.Graphic;
|
||||||
|
import de.neemann.digital.draw.graphics.GraphicMinMax;
|
||||||
|
import de.neemann.digital.draw.graphics.GraphicSVG;
|
||||||
import de.neemann.digital.draw.library.ElementLibrary;
|
import de.neemann.digital.draw.library.ElementLibrary;
|
||||||
import de.neemann.digital.draw.model.ModelDescription;
|
import de.neemann.digital.draw.model.ModelDescription;
|
||||||
import de.neemann.digital.draw.model.RealTimeClock;
|
import de.neemann.digital.draw.model.RealTimeClock;
|
||||||
@ -21,6 +25,7 @@ import javax.swing.*;
|
|||||||
import javax.swing.filechooser.FileNameExtensionFilter;
|
import javax.swing.filechooser.FileNameExtensionFilter;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.io.Closeable;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.prefs.Preferences;
|
import java.util.prefs.Preferences;
|
||||||
@ -151,6 +156,11 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
JMenu export = new JMenu(Lang.get("menu_export"));
|
||||||
|
export.add(new ExportAction(Lang.get("menu_exportSVG"), "svg", GraphicSVG::new));
|
||||||
|
|
||||||
|
|
||||||
JMenu file = new JMenu(Lang.get("menu_file"));
|
JMenu file = new JMenu(Lang.get("menu_file"));
|
||||||
bar.add(file);
|
bar.add(file);
|
||||||
file.add(newFile);
|
file.add(newFile);
|
||||||
@ -158,6 +168,7 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave {
|
|||||||
file.add(openWin);
|
file.add(openWin);
|
||||||
file.add(save);
|
file.add(save);
|
||||||
file.add(saveas);
|
file.add(saveas);
|
||||||
|
file.add(export);
|
||||||
|
|
||||||
JMenu edit = new JMenu(Lang.get("menu_edit"));
|
JMenu edit = new JMenu(Lang.get("menu_edit"));
|
||||||
bar.add(edit);
|
bar.add(edit);
|
||||||
@ -483,4 +494,48 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave {
|
|||||||
doStep.setEnabled(model.needsUpdate());
|
doStep.setEnabled(model.needsUpdate());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class ExportAction extends ToolTipAction {
|
||||||
|
private final String name;
|
||||||
|
private final String suffix;
|
||||||
|
private final Exporter exporter;
|
||||||
|
|
||||||
|
public ExportAction(String name, String suffix, Exporter exporter) {
|
||||||
|
super(name);
|
||||||
|
this.name = name;
|
||||||
|
this.suffix = suffix;
|
||||||
|
this.exporter = exporter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
JFileChooser fc = new JFileChooser();
|
||||||
|
if (filename != null) {
|
||||||
|
String name = filename.getName();
|
||||||
|
int p = name.lastIndexOf('.');
|
||||||
|
if (p >= 0)
|
||||||
|
name = name.substring(0, p);
|
||||||
|
File f = new File(filename.getParentFile(), name + "." + suffix);
|
||||||
|
fc.setSelectedFile(f);
|
||||||
|
}
|
||||||
|
fc.addChoosableFileFilter(new FileNameExtensionFilter(name, suffix));
|
||||||
|
if (fc.showSaveDialog(Main.this) == JFileChooser.APPROVE_OPTION) {
|
||||||
|
Circuit circuit = circuitComponent.getCircuit();
|
||||||
|
GraphicMinMax minMax = new GraphicMinMax();
|
||||||
|
circuit.drawTo(minMax);
|
||||||
|
try {
|
||||||
|
Graphic gr = null;
|
||||||
|
try {
|
||||||
|
gr = exporter.create(fc.getSelectedFile(), minMax.getMin(), minMax.getMax());
|
||||||
|
circuit.drawTo(gr);
|
||||||
|
} finally {
|
||||||
|
if (gr instanceof Closeable)
|
||||||
|
((Closeable) gr).close();
|
||||||
|
}
|
||||||
|
} catch (IOException e1) {
|
||||||
|
new ErrorMessage(Lang.get("msg_errorWritingFile")).addCause(e1).show(Main.this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -123,7 +123,8 @@ menu_editAttributes=Model Attribute Bearbeiten
|
|||||||
menu_editAttributes_tt=Diese Attribute beeinflussen das Modell, wenn es in andere Modelle eingebettet wird.
|
menu_editAttributes_tt=Diese Attribute beeinflussen das Modell, wenn es in andere Modelle eingebettet wird.
|
||||||
menu_fast=Schneller Lauf
|
menu_fast=Schneller Lauf
|
||||||
menu_fast_tt=F\u00FChrt das Modell aus, bis ein Stopsignal \u00FCber ein BRK-Element detektiert wird.
|
menu_fast_tt=F\u00FChrt das Modell aus, bis ein Stopsignal \u00FCber ein BRK-Element detektiert wird.
|
||||||
|
menu_export=Export
|
||||||
|
menu_exportSVG=Export SVG
|
||||||
|
|
||||||
menu_about=\u00DCber Digital
|
menu_about=\u00DCber Digital
|
||||||
win_saveChanges=Ver\u00E4nderungen speichern?
|
win_saveChanges=Ver\u00E4nderungen speichern?
|
||||||
|
@ -124,6 +124,8 @@ menu_editAttributes=Edit Models Attributes
|
|||||||
menu_editAttributes_tt=These attributes effect the behavior is the model is included in other models.
|
menu_editAttributes_tt=These attributes effect the behavior is the model is included in other models.
|
||||||
menu_fast=Run Fast
|
menu_fast=Run Fast
|
||||||
menu_fast_tt=Runs the model until a break is detected by the BRK element.
|
menu_fast_tt=Runs the model until a break is detected by the BRK element.
|
||||||
|
menu_export=Export
|
||||||
|
menu_exportSVG=Export SVG
|
||||||
|
|
||||||
|
|
||||||
win_saveChanges=Save Changes?
|
win_saveChanges=Save Changes?
|
||||||
|
Loading…
x
Reference in New Issue
Block a user