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) {
if (includeText) {
if (minMaxText == null) {
GraphicMinMax mm = new GraphicMinMax(true);
GraphicMinMax mm = new GraphicMinMax(true, null);
drawShape(mm, null);
minMaxText = mm;
}
return minMaxText;
} else {
if (minMax == null) {
GraphicMinMax mm = new GraphicMinMax(false);
GraphicMinMax mm = new GraphicMinMax(false, null);
drawShape(mm, null);
minMax = mm;
}

View File

@ -128,7 +128,8 @@ public class GifExporter extends JDialog implements ModelStateObserver, ModelMod
}
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();
Graphics gr = bi.getGraphics();
gr.setColor(Color.WHITE);

View File

@ -36,10 +36,13 @@ public class Export {
* @throws IOException 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);
Graphic gr = factory.create(out, minMax.getMin(), minMax.getMax());
gr.setBoundingBox(minMax.getMin(), minMax.getMax());
try {
GraphicLineCollector glc = new GraphicLineCollector();

View File

@ -13,10 +13,8 @@ public interface ExportFactory {
* Creates a {@link Graphic} instance
*
* @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
* @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 {
/**
* 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
*

View File

@ -15,6 +15,7 @@ import static de.neemann.digital.core.element.ElementAttributes.cleanLabel;
public class GraphicMinMax implements Graphic {
private final boolean includeText;
private final Graphic parent;
private Vector min;
private Vector max;
@ -22,16 +23,27 @@ public class GraphicMinMax implements Graphic {
* Creates a new instance
*/
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
*
* @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.parent = parent;
}
@Override
@ -135,4 +147,12 @@ public class GraphicMinMax implements Graphic {
public Vector getMax() {
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.util.Date;
import static java.lang.System.out;
/**
* Used to create a SVG representation of the circuit.
* 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 {
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.
*
* @param out the stream
* @param min upper left corner
* @param max lower right corner
* @throws IOException IOException
*/
public GraphicSVG(OutputStream out, Vector min, Vector max) throws IOException {
this(out, min, max, null, DEF_SCALE);
public GraphicSVG(OutputStream out) throws IOException {
this(out, null, DEF_SCALE);
}
/**
* Creates a new instance.
*
* @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 svgScale the scaling
* @throws IOException IOException
*/
public GraphicSVG(File file, Vector min, Vector max, File source, int svgScale) throws IOException {
this(new FileOutputStream(file), min, max, source, svgScale);
public GraphicSVG(File file, File source, int svgScale) throws IOException {
this(new FileOutputStream(file), source, svgScale);
}
/**
* Creates a new instance.
*
* @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 svgScale the scaling
* @throws IOException IOException
*/
public GraphicSVG(OutputStream out, Vector min, Vector max, File source, int svgScale) throws IOException {
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");
public GraphicSVG(OutputStream out, File source, int svgScale) throws IOException {
this.out = out;
this.source = source;
this.svgScale = svgScale;
}
@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

View File

@ -18,26 +18,22 @@ public class GraphicSVGIndex extends GraphicSVG {
* Creates new instance
*
* @param out the file
* @param min upper left corner
* @param max lower right corner
* @throws IOException IOException
*/
public GraphicSVGIndex(OutputStream out, Vector min, Vector max) throws IOException {
super(out, min, max);
public GraphicSVGIndex(OutputStream out) throws IOException {
super(out);
}
/**
* Creates new instance
*
* @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 svgScale the scaling
* @throws IOException IOException
*/
public GraphicSVGIndex(OutputStream out, Vector min, Vector max, File source, int svgScale) throws IOException {
super(out, min, max, source, svgScale);
public GraphicSVGIndex(OutputStream out, File source, int svgScale) throws IOException {
super(out, source, svgScale);
}
@Override

View File

@ -22,26 +22,22 @@ public class GraphicSVGLaTeX extends GraphicSVG {
* Creates new instance
*
* @param out the file
* @param min upper left corner
* @param max lower right corner
* @throws IOException IOException
*/
public GraphicSVGLaTeX(OutputStream out, Vector min, Vector max) throws IOException {
super(out, min, max);
public GraphicSVGLaTeX(OutputStream out) throws IOException {
super(out);
}
/**
* Creates new instance
*
* @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 svgScale the scaling
* @throws IOException IOException
*/
public GraphicSVGLaTeX(OutputStream out, Vector min, Vector max, File source, int svgScale) throws IOException {
super(out, min, max, source, svgScale);
public GraphicSVGLaTeX(OutputStream out, File source, int svgScale) throws IOException {
super(out, source, svgScale);
}
@Override

View File

@ -14,10 +14,10 @@ import static de.neemann.digital.core.element.ElementAttributes.cleanLabel;
*/
public class GraphicSwing implements Graphic {
private final Graphics2D gr;
private final int minFontSize;
private int pixelSize;
private Style lastStyle;
private Graphics2D gr;
/**
* Creates a new instance
@ -38,7 +38,17 @@ public class GraphicSwing implements Graphic {
this.gr = gr;
this.pixelSize = pixelSize;
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

View File

@ -14,20 +14,29 @@ import java.io.OutputStream;
*/
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 min upper left corner
* @param max lower right corner
* @param out the output stream
* @param format the format to write
* @param scale the scaling of the created image
* @return the {@link Graphic} instance
* @param scale the scaling
*/
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;
BufferedImage bi
= new BufferedImage(
bi = new BufferedImage(
Math.round((max.x - min.x + thickness * 2) * scale),
Math.round((max.y - min.y + thickness * 2) * scale),
BufferedImage.TYPE_INT_ARGB);
@ -42,19 +51,8 @@ public final class GraphicsImage extends GraphicSwing implements Closeable {
gr.scale(scale, scale);
gr.translate(thickness - min.x, thickness - min.y);
return new GraphicsImage(out, gr, bi, format);
}
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;
setGraphics2D(gr);
return this;
}
@Override

View File

@ -477,8 +477,8 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS
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_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_exportPNGLarge"), "png", (out, min, max) -> GraphicsImage.create(out, min, max, "PNG", 2)));
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) -> new GraphicsImage(out, "PNG", 2)));
if (enableExperimental())
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 {
GraphicMinMax minMax = new GraphicMinMax(true);
ve.drawTo(minMax, null);
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);
}
}

View File

@ -7,7 +7,8 @@ import junit.framework.TestCase;
*/
public class GraphicSVGIndexTest extends TestCase {
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("&lt;a&gt;", gs.formatText("<a>", 0));

View File

@ -7,7 +7,8 @@ import junit.framework.TestCase;
*/
public class GraphicSVGLaTeXTest extends TestCase {
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("\\&amp;", gs.formatText("&", Style.NORMAL.getFontSize()));

View File

@ -7,7 +7,8 @@ import junit.framework.TestCase;
*/
public class GraphicSVGTest extends TestCase {
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("&lt;a&gt;", gs.formatText("<a>", 0));

View File

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

View File

@ -28,7 +28,7 @@ public class TestExport extends TestCase {
public void testSVGExport() throws NodeException, PinException, IOException, ElementNotFoundException {
ByteArrayOutputStream baos
= 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);
}
@ -36,7 +36,7 @@ public class TestExport extends TestCase {
public void testSVGExportLaTeX() throws NodeException, PinException, IOException, ElementNotFoundException {
ByteArrayOutputStream baos
= 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);
}
@ -44,7 +44,7 @@ public class TestExport extends TestCase {
public void testPNGExport() throws NodeException, PinException, IOException, ElementNotFoundException {
ByteArrayOutputStream baos
= 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);
}

View File

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