mirror of
https://github.com/hneemann/Digital.git
synced 2025-09-13 06:49:36 -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;
|
||||
}
|
||||
|
||||
graphic.openGroup();
|
||||
for (Wire w : wires)
|
||||
w.drawTo(graphic);
|
||||
for (VisualElement p : visualElements)
|
||||
graphic.closeGroup();
|
||||
for (VisualElement p : visualElements) {
|
||||
graphic.openGroup();
|
||||
p.drawTo(graphic);
|
||||
graphic.closeGroup();
|
||||
}
|
||||
}
|
||||
|
||||
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 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() {
|
||||
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.PinException;
|
||||
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.model.ModelDescription;
|
||||
import de.neemann.digital.draw.model.RealTimeClock;
|
||||
@ -21,6 +25,7 @@ import javax.swing.*;
|
||||
import javax.swing.filechooser.FileNameExtensionFilter;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
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"));
|
||||
bar.add(file);
|
||||
file.add(newFile);
|
||||
@ -158,6 +168,7 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave {
|
||||
file.add(openWin);
|
||||
file.add(save);
|
||||
file.add(saveas);
|
||||
file.add(export);
|
||||
|
||||
JMenu edit = new JMenu(Lang.get("menu_edit"));
|
||||
bar.add(edit);
|
||||
@ -483,4 +494,48 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave {
|
||||
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_fast=Schneller Lauf
|
||||
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
|
||||
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_fast=Run Fast
|
||||
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?
|
||||
|
Loading…
x
Reference in New Issue
Block a user