added a simple SVG export

This commit is contained in:
hneemann 2016-03-29 21:38:49 +02:00
parent e502cc2817
commit b77bf92c03
8 changed files with 305 additions and 2 deletions

View File

@ -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) {

View 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;
}

View File

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

View 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("&amp;");
break;
case '<':
sb.append("&lt;");
break;
case '>':
sb.append("&gt;");
break;
case '"':
sb.append("&quot;");
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;
}
}

View File

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

View File

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

View File

@ -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?

View File

@ -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?