refactoring of graphics creation

This commit is contained in:
hneemann 2017-06-25 10:49:37 +02:00
parent a85001282b
commit 85177b2570
19 changed files with 145 additions and 94 deletions

View File

@ -204,14 +204,14 @@ public class VisualElement implements Drawable, Movable, AttributeListener {
public GraphicMinMax getMinMax(boolean includeText) { public GraphicMinMax getMinMax(boolean includeText) {
if (includeText) { if (includeText) {
if (minMaxText == null) { if (minMaxText == null) {
GraphicMinMax mm = new GraphicMinMax(true); GraphicMinMax mm = new GraphicMinMax(true, null);
drawShape(mm, null); drawShape(mm, null);
minMaxText = mm; minMaxText = mm;
} }
return minMaxText; return minMaxText;
} else { } else {
if (minMax == null) { if (minMax == null) {
GraphicMinMax mm = new GraphicMinMax(false); GraphicMinMax mm = new GraphicMinMax(false, null);
drawShape(mm, null); drawShape(mm, null);
minMax = mm; minMax = mm;
} }

View File

@ -128,7 +128,8 @@ public class GifExporter extends JDialog implements ModelStateObserver, ModelMod
} }
private BufferedImage createBufferedImage() throws IOException { private BufferedImage createBufferedImage() throws IOException {
GraphicsImage gri = GraphicsImage.create(null, minMax.getMin(), minMax.getMax(), "gif", 1); GraphicsImage gri = new GraphicsImage(null, "gif", 1);
gri.setBoundingBox(minMax.getMin(), minMax.getMax());
BufferedImage bi = gri.getBufferedImage(); BufferedImage bi = gri.getBufferedImage();
Graphics gr = bi.getGraphics(); Graphics gr = bi.getGraphics();
gr.setColor(Color.WHITE); gr.setColor(Color.WHITE);

View File

@ -36,10 +36,13 @@ public class Export {
* @throws IOException IOException * @throws IOException IOException
*/ */
public void export(OutputStream out) throws IOException { public void export(OutputStream out) throws IOException {
GraphicMinMax minMax = new GraphicMinMax(); Graphic gr = factory.create(out);
GraphicMinMax minMax = new GraphicMinMax(gr);
circuit.drawTo(minMax); circuit.drawTo(minMax);
Graphic gr = factory.create(out, minMax.getMin(), minMax.getMax()); gr.setBoundingBox(minMax.getMin(), minMax.getMax());
try { try {
GraphicLineCollector glc = new GraphicLineCollector(); GraphicLineCollector glc = new GraphicLineCollector();

View File

@ -13,10 +13,8 @@ public interface ExportFactory {
* Creates a {@link Graphic} instance * Creates a {@link Graphic} instance
* *
* @param out the stream to write the graphic to * @param out the stream to write the graphic to
* @param min upper right corner of the circuit
* @param max lower left corner of the circuit
* @return the {@link Graphic} instance to use * @return the {@link Graphic} instance to use
* @throws IOException IOException * @throws IOException IOException
*/ */
Graphic create(OutputStream out, Vector min, Vector max) throws IOException; Graphic create(OutputStream out) throws IOException;
} }

View File

@ -9,6 +9,19 @@ package de.neemann.digital.draw.graphics;
*/ */
public interface Graphic { public interface Graphic {
/**
* Sets the bounding box of the future usage of this instance
* Instances that create a file will use this bounding box th write a header.
* So this method needs to be called before a draw-Method is called.
*
* @param min upper left corner
* @param max lower right corner
* @return this for chained calls
*/
default Graphic setBoundingBox(Vector min, Vector max) {
return this;
}
/** /**
* Draws a line * Draws a line
* *

View File

@ -15,6 +15,7 @@ import static de.neemann.digital.core.element.ElementAttributes.cleanLabel;
public class GraphicMinMax implements Graphic { public class GraphicMinMax implements Graphic {
private final boolean includeText; private final boolean includeText;
private final Graphic parent;
private Vector min; private Vector min;
private Vector max; private Vector max;
@ -22,16 +23,27 @@ public class GraphicMinMax implements Graphic {
* Creates a new instance * Creates a new instance
*/ */
public GraphicMinMax() { public GraphicMinMax() {
this(true); this(true, null);
}
/**
* Creates a new instance
*
* @param parent oly used to provide the flags
*/
public GraphicMinMax(Graphic parent) {
this(true, parent);
} }
/** /**
* Creates a new instance * Creates a new instance
* *
* @param includeText true if text is included in measurement * @param includeText true if text is included in measurement
* @param parent oly used to provide the flags
*/ */
public GraphicMinMax(boolean includeText) { public GraphicMinMax(boolean includeText, Graphic parent) {
this.includeText = includeText; this.includeText = includeText;
this.parent = parent;
} }
@Override @Override
@ -135,4 +147,12 @@ public class GraphicMinMax implements Graphic {
public Vector getMax() { public Vector getMax() {
return max; return max;
} }
@Override
public boolean isFlagSet(String name) {
if (parent == null)
return false;
else
return parent.isFlagSet(name);
}
} }

View File

@ -3,6 +3,8 @@ package de.neemann.digital.draw.graphics;
import java.io.*; import java.io.*;
import java.util.Date; import java.util.Date;
import static java.lang.System.out;
/** /**
* Used to create a SVG representation of the circuit. * Used to create a SVG representation of the circuit.
* Don't use this implementation directly. Use {@link GraphicSVGIndex} to create plain SVG or * Don't use this implementation directly. Use {@link GraphicSVGIndex} to create plain SVG or
@ -12,65 +14,74 @@ import java.util.Date;
*/ */
public class GraphicSVG implements Graphic, Closeable { public class GraphicSVG implements Graphic, Closeable {
private static final int DEF_SCALE = 15; private static final int DEF_SCALE = 15;
private final BufferedWriter w; private final OutputStream out;
private final File source;
private final int svgScale;
private BufferedWriter w;
/** /**
* Creates a new instance. * Creates a new instance.
* *
* @param out the stream * @param out the stream
* @param min upper left corner
* @param max lower right corner
* @throws IOException IOException * @throws IOException IOException
*/ */
public GraphicSVG(OutputStream out, Vector min, Vector max) throws IOException { public GraphicSVG(OutputStream out) throws IOException {
this(out, min, max, null, DEF_SCALE); this(out, null, DEF_SCALE);
} }
/** /**
* Creates a new instance. * Creates a new instance.
* *
* @param file the file * @param file the file
* @param min upper left corner
* @param max lower right corner
* @param source source file, only used to create a comment in the SVG file * @param source source file, only used to create a comment in the SVG file
* @param svgScale the scaling * @param svgScale the scaling
* @throws IOException IOException * @throws IOException IOException
*/ */
public GraphicSVG(File file, Vector min, Vector max, File source, int svgScale) throws IOException { public GraphicSVG(File file, File source, int svgScale) throws IOException {
this(new FileOutputStream(file), min, max, source, svgScale); this(new FileOutputStream(file), source, svgScale);
} }
/** /**
* Creates a new instance. * Creates a new instance.
* *
* @param out the stream to write the file to * @param out the stream to write the file to
* @param min upper left corner
* @param max lower right corner
* @param source source file, only used to create a comment in the SVG file * @param source source file, only used to create a comment in the SVG file
* @param svgScale the scaling * @param svgScale the scaling
* @throws IOException IOException * @throws IOException IOException
*/ */
public GraphicSVG(OutputStream out, Vector min, Vector max, File source, int svgScale) throws IOException { public GraphicSVG(OutputStream out, File source, int svgScale) throws IOException {
w = new BufferedWriter(new OutputStreamWriter(out, "utf-8")); this.out = out;
w.write("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n" this.source = source;
+ "<!-- Created with Digital by H.Neemann -->\n"); this.svgScale = svgScale;
w.write("<!-- created: " + new Date() + " -->\n"); }
if (source != null) {
w.write("<!-- source: " + source.getPath() + " -->\n"); @Override
public Graphic setBoundingBox(Vector min, Vector max) {
try {
w = new BufferedWriter(new OutputStreamWriter(out, "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 + Style.MAXLINETHICK) * svgScale / 100.0;
double height = (max.y - min.y + Style.MAXLINETHICK) * svgScale / 100.0;
final int lineCorr = Style.MAXLINETHICK / 2;
w.write(" width=\"" + width + "mm\"\n"
+ " height=\"" + height + "mm\"\n"
+ " viewBox=\"" + (min.x - lineCorr) + " " + (min.y - lineCorr) + " " + (max.x - min.x + Style.MAXLINETHICK) + " " + (max.y - min.y + Style.MAXLINETHICK) + "\">\n");
w.write("<g stroke-linecap=\"square\">\n");
return this;
} catch (IOException e) {
throw new RuntimeException(e);
} }
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 + Style.MAXLINETHICK) * svgScale / 100.0;
double height = (max.y - min.y + Style.MAXLINETHICK) * svgScale / 100.0;
final int lineCorr = Style.MAXLINETHICK / 2;
w.write(" width=\"" + width + "mm\"\n"
+ " height=\"" + height + "mm\"\n"
+ " viewBox=\"" + (min.x - lineCorr) + " " + (min.y - lineCorr) + " " + (max.x - min.x + Style.MAXLINETHICK) + " " + (max.y - min.y + Style.MAXLINETHICK) + "\">\n");
w.write("<g stroke-linecap=\"square\">\n");
} }
@Override @Override

View File

@ -18,26 +18,22 @@ public class GraphicSVGIndex extends GraphicSVG {
* Creates new instance * Creates new instance
* *
* @param out the file * @param out the file
* @param min upper left corner
* @param max lower right corner
* @throws IOException IOException * @throws IOException IOException
*/ */
public GraphicSVGIndex(OutputStream out, Vector min, Vector max) throws IOException { public GraphicSVGIndex(OutputStream out) throws IOException {
super(out, min, max); super(out);
} }
/** /**
* Creates new instance * Creates new instance
* *
* @param out the output stream to use * @param out the output stream to use
* @param min upper left corner
* @param max lower right corner
* @param source source file, only used to create a comment in the SVG file * @param source source file, only used to create a comment in the SVG file
* @param svgScale the scaling * @param svgScale the scaling
* @throws IOException IOException * @throws IOException IOException
*/ */
public GraphicSVGIndex(OutputStream out, Vector min, Vector max, File source, int svgScale) throws IOException { public GraphicSVGIndex(OutputStream out, File source, int svgScale) throws IOException {
super(out, min, max, source, svgScale); super(out, source, svgScale);
} }
@Override @Override

View File

@ -22,26 +22,22 @@ public class GraphicSVGLaTeX extends GraphicSVG {
* Creates new instance * Creates new instance
* *
* @param out the file * @param out the file
* @param min upper left corner
* @param max lower right corner
* @throws IOException IOException * @throws IOException IOException
*/ */
public GraphicSVGLaTeX(OutputStream out, Vector min, Vector max) throws IOException { public GraphicSVGLaTeX(OutputStream out) throws IOException {
super(out, min, max); super(out);
} }
/** /**
* Creates new instance * Creates new instance
* *
* @param out the output stream to use * @param out the output stream to use
* @param min upper left corner
* @param max lower right corner
* @param source source file, only used to create a comment in the SVG file * @param source source file, only used to create a comment in the SVG file
* @param svgScale the scaling * @param svgScale the scaling
* @throws IOException IOException * @throws IOException IOException
*/ */
public GraphicSVGLaTeX(OutputStream out, Vector min, Vector max, File source, int svgScale) throws IOException { public GraphicSVGLaTeX(OutputStream out, File source, int svgScale) throws IOException {
super(out, min, max, source, svgScale); super(out, source, svgScale);
} }
@Override @Override

View File

@ -14,10 +14,10 @@ import static de.neemann.digital.core.element.ElementAttributes.cleanLabel;
*/ */
public class GraphicSwing implements Graphic { public class GraphicSwing implements Graphic {
private final Graphics2D gr;
private final int minFontSize; private final int minFontSize;
private int pixelSize; private int pixelSize;
private Style lastStyle; private Style lastStyle;
private Graphics2D gr;
/** /**
* Creates a new instance * Creates a new instance
@ -38,7 +38,17 @@ public class GraphicSwing implements Graphic {
this.gr = gr; this.gr = gr;
this.pixelSize = pixelSize; this.pixelSize = pixelSize;
this.minFontSize = pixelSize * 3; this.minFontSize = pixelSize * 3;
gr.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); if (gr != null)
gr.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
}
/**
* Set the graphics instance to use
*
* @param gr the Graphics2D to draw to
*/
protected void setGraphics2D(Graphics2D gr) {
this.gr = gr;
} }
@Override @Override

View File

@ -14,20 +14,29 @@ import java.io.OutputStream;
*/ */
public final class GraphicsImage extends GraphicSwing implements Closeable { public final class GraphicsImage extends GraphicSwing implements Closeable {
private final OutputStream out;
private final String format;
private final float scale;
private BufferedImage bi;
/** /**
* Creates a new instance of this class * Creates a new instance
* *
* @param out the OutputStream to write the image to * @param out the output stream
* @param min upper left corner
* @param max lower right corner
* @param format the format to write * @param format the format to write
* @param scale the scaling of the created image * @param scale the scaling
* @return the {@link Graphic} instance
*/ */
public static GraphicsImage create(OutputStream out, Vector min, Vector max, String format, float scale) { public GraphicsImage(OutputStream out, String format, float scale) {
super(null);
this.out = out;
this.format = format;
this.scale = scale;
}
@Override
public Graphic setBoundingBox(Vector min, Vector max) {
int thickness = Style.MAXLINETHICK; int thickness = Style.MAXLINETHICK;
BufferedImage bi bi = new BufferedImage(
= new BufferedImage(
Math.round((max.x - min.x + thickness * 2) * scale), Math.round((max.x - min.x + thickness * 2) * scale),
Math.round((max.y - min.y + thickness * 2) * scale), Math.round((max.y - min.y + thickness * 2) * scale),
BufferedImage.TYPE_INT_ARGB); BufferedImage.TYPE_INT_ARGB);
@ -42,19 +51,8 @@ public final class GraphicsImage extends GraphicSwing implements Closeable {
gr.scale(scale, scale); gr.scale(scale, scale);
gr.translate(thickness - min.x, thickness - min.y); gr.translate(thickness - min.x, thickness - min.y);
setGraphics2D(gr);
return new GraphicsImage(out, gr, bi, format); return this;
}
private final OutputStream out;
private final BufferedImage bi;
private final String format;
private GraphicsImage(OutputStream out, Graphics2D gr, BufferedImage bi, String format) {
super(gr);
this.out = out;
this.bi = bi;
this.format = format;
} }
@Override @Override

View File

@ -477,8 +477,8 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS
JMenu export = new JMenu(Lang.get("menu_export")); JMenu export = new JMenu(Lang.get("menu_export"));
export.add(new ExportAction(Lang.get("menu_exportSVG"), "svg", GraphicSVGIndex::new)); export.add(new ExportAction(Lang.get("menu_exportSVG"), "svg", GraphicSVGIndex::new));
export.add(new ExportAction(Lang.get("menu_exportSVGLaTex"), "svg", GraphicSVGLaTeX::new)); export.add(new ExportAction(Lang.get("menu_exportSVGLaTex"), "svg", GraphicSVGLaTeX::new));
export.add(new ExportAction(Lang.get("menu_exportPNGSmall"), "png", (out, min, max) -> GraphicsImage.create(out, min, max, "PNG", 1))); export.add(new ExportAction(Lang.get("menu_exportPNGSmall"), "png", (out) -> new GraphicsImage(out, "PNG", 1)));
export.add(new ExportAction(Lang.get("menu_exportPNGLarge"), "png", (out, min, max) -> GraphicsImage.create(out, min, max, "PNG", 2))); export.add(new ExportAction(Lang.get("menu_exportPNGLarge"), "png", (out) -> new GraphicsImage(out, "PNG", 2)));
if (enableExperimental()) if (enableExperimental())
export.add(new ExportGifAction(Lang.get("menu_exportAnimatedGIF"))); export.add(new ExportGifAction(Lang.get("menu_exportAnimatedGIF")));

View File

@ -116,10 +116,11 @@ public class DocuTest extends TestCase {
} }
private void writeSVG(File imageFile, VisualElement ve) throws IOException { private void writeSVG(File imageFile, VisualElement ve) throws IOException {
GraphicMinMax minMax = new GraphicMinMax(true);
ve.drawTo(minMax, null);
try (FileOutputStream out = new FileOutputStream(imageFile)) { try (FileOutputStream out = new FileOutputStream(imageFile)) {
try (GraphicSVG svg = new GraphicSVG(out, minMax.getMin(), minMax.getMax(), null, 20)) { try (GraphicSVG svg = new GraphicSVG(out, null, 20)) {
GraphicMinMax minMax = new GraphicMinMax(true, svg);
ve.drawTo(minMax, null);
svg.setBoundingBox(minMax.getMin(), minMax.getMax());
ve.drawTo(svg, null); ve.drawTo(svg, null);
} }
} }

View File

@ -7,7 +7,8 @@ import junit.framework.TestCase;
*/ */
public class GraphicSVGIndexTest extends TestCase { public class GraphicSVGIndexTest extends TestCase {
public void testFormatText() throws Exception { public void testFormatText() throws Exception {
GraphicSVGIndex gs = new GraphicSVGIndex(System.out, new Vector(0, 0), new Vector(30, 30), null, 30); GraphicSVGIndex gs = new GraphicSVGIndex(System.out, null, 30);
gs.setBoundingBox(new Vector(0, 0), new Vector(30, 30));
assertEquals("Z<tspan style=\"font-size:80%;baseline-shift:sub\">0</tspan>", gs.formatText("Z_0", 0)); assertEquals("Z<tspan style=\"font-size:80%;baseline-shift:sub\">0</tspan>", gs.formatText("Z_0", 0));
assertEquals("&lt;a&gt;", gs.formatText("<a>", 0)); assertEquals("&lt;a&gt;", gs.formatText("<a>", 0));

View File

@ -7,7 +7,8 @@ import junit.framework.TestCase;
*/ */
public class GraphicSVGLaTeXTest extends TestCase { public class GraphicSVGLaTeXTest extends TestCase {
public void testFormatText() throws Exception { public void testFormatText() throws Exception {
GraphicSVGLaTeX gs = new GraphicSVGLaTeX(System.out, new Vector(0, 0), new Vector(30, 30), null, 30); GraphicSVGLaTeX gs = new GraphicSVGLaTeX(System.out, null, 30);
gs.setBoundingBox(new Vector(0, 0), new Vector(30, 30));
assertEquals("$Z_{0}$", gs.formatText("Z_0", Style.NORMAL.getFontSize())); assertEquals("$Z_{0}$", gs.formatText("Z_0", Style.NORMAL.getFontSize()));
assertEquals("\\&amp;", gs.formatText("&", Style.NORMAL.getFontSize())); assertEquals("\\&amp;", gs.formatText("&", Style.NORMAL.getFontSize()));

View File

@ -7,7 +7,8 @@ import junit.framework.TestCase;
*/ */
public class GraphicSVGTest extends TestCase { public class GraphicSVGTest extends TestCase {
public void testFormatText() throws Exception { public void testFormatText() throws Exception {
GraphicSVG gs = new GraphicSVG(System.out, new Vector(0, 0), new Vector(30, 30), null, 30); GraphicSVG gs = new GraphicSVG(System.out, null, 30);
gs.setBoundingBox(new Vector(0, 0), new Vector(30, 30));
assertEquals("Z0", gs.formatText("Z0", 0)); assertEquals("Z0", gs.formatText("Z0", 0));
assertEquals("&lt;a&gt;", gs.formatText("<a>", 0)); assertEquals("&lt;a&gt;", gs.formatText("<a>", 0));

View File

@ -1,6 +1,7 @@
package de.neemann.digital.integration; package de.neemann.digital.integration;
import de.neemann.digital.draw.graphics.Export; import de.neemann.digital.draw.graphics.Export;
import de.neemann.digital.draw.graphics.GraphicTransform;
import de.neemann.digital.draw.graphics.GraphicsImage; import de.neemann.digital.draw.graphics.GraphicsImage;
import de.neemann.digital.gui.components.data.DataSample; import de.neemann.digital.gui.components.data.DataSample;
import de.neemann.digital.gui.components.data.DataSet; import de.neemann.digital.gui.components.data.DataSet;
@ -45,7 +46,7 @@ public class TestData extends TestCase {
// try to write data to graphics instance // try to write data to graphics instance
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
new Export(toBreakRunner.getCircuit(), new Export(toBreakRunner.getCircuit(),
(out, min, max) -> GraphicsImage.create(out, min, max, "PNG", 1)) (out) -> new GraphicsImage(out, "PNG", 1))
.export(baos); .export(baos);
assertTrue(baos.size() > 15000); assertTrue(baos.size() > 15000);

View File

@ -28,7 +28,7 @@ public class TestExport extends TestCase {
public void testSVGExport() throws NodeException, PinException, IOException, ElementNotFoundException { public void testSVGExport() throws NodeException, PinException, IOException, ElementNotFoundException {
ByteArrayOutputStream baos ByteArrayOutputStream baos
= export("../../main/dig/processor/Processor.dig", = export("../../main/dig/processor/Processor.dig",
(out, min, max) -> new GraphicSVGIndex(out, min, max, null, 15)); (out) -> new GraphicSVGIndex(out, null, 15));
assertTrue(baos.size() > 20000); assertTrue(baos.size() > 20000);
} }
@ -36,7 +36,7 @@ public class TestExport extends TestCase {
public void testSVGExportLaTeX() throws NodeException, PinException, IOException, ElementNotFoundException { public void testSVGExportLaTeX() throws NodeException, PinException, IOException, ElementNotFoundException {
ByteArrayOutputStream baos ByteArrayOutputStream baos
= export("../../main/dig/processor/Processor.dig", = export("../../main/dig/processor/Processor.dig",
(out, min, max) -> new GraphicSVGLaTeX(out, min, max, null, 15)); (out) -> new GraphicSVGLaTeX(out, null, 15));
assertTrue(baos.size() > 15000); assertTrue(baos.size() > 15000);
} }
@ -44,7 +44,7 @@ public class TestExport extends TestCase {
public void testPNGExport() throws NodeException, PinException, IOException, ElementNotFoundException { public void testPNGExport() throws NodeException, PinException, IOException, ElementNotFoundException {
ByteArrayOutputStream baos ByteArrayOutputStream baos
= export("../../main/dig/processor/Processor.dig", = export("../../main/dig/processor/Processor.dig",
(out, min, max) -> GraphicsImage.create(out, min, max, "PNG", 1)); (out) -> new GraphicsImage(out, "PNG", 1));
assertTrue(baos.size() > 45000); assertTrue(baos.size() > 45000);
} }

View File

@ -39,7 +39,7 @@ public class TestShapes extends TestCase {
// try to write circuit to graphics instance // try to write circuit to graphics instance
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
new Export(circuit, new Export(circuit,
(out, min, max) -> GraphicsImage.create(out, min, max, "PNG", 1)) (out) -> new GraphicsImage(out, "PNG", 1))
.export(baos); .export(baos);
assertTrue(baos.size() > 30000); assertTrue(baos.size() > 30000);