From e9c620953e1f76b78f34d18f3a894fbb168aefaa Mon Sep 17 00:00:00 2001 From: hneemann Date: Fri, 30 Nov 2018 12:08:45 +0100 Subject: [PATCH] added more test cases for the svg parser --- .../shapes/custom/CustomShapeDescription.java | 14 ++ .../draw/shapes/custom/svg/Context.java | 10 ++ .../draw/shapes/custom/svg/SvgImporter.java | 77 +++++----- src/main/resources/lang/lang_de.xml | 1 + src/main/resources/lang/lang_en.xml | 1 + .../draw/shapes/custom/SvgImporterTest.java | 131 +++++++++++++++++- 6 files changed, 198 insertions(+), 36 deletions(-) diff --git a/src/main/java/de/neemann/digital/draw/shapes/custom/CustomShapeDescription.java b/src/main/java/de/neemann/digital/draw/shapes/custom/CustomShapeDescription.java index 69e491742..cf6a55371 100644 --- a/src/main/java/de/neemann/digital/draw/shapes/custom/CustomShapeDescription.java +++ b/src/main/java/de/neemann/digital/draw/shapes/custom/CustomShapeDescription.java @@ -174,6 +174,20 @@ public class CustomShapeDescription implements Iterable { public void drawTo(Graphic graphic, Style highLight) { graphic.drawLine(p1, p2, Style.NORMAL.deriveStyle(thickness, false, color)); } + + /** + * @return first coordinate + */ + public Vector getP1() { + return p1; + } + + /** + * @return second coordinate + */ + public Vector getP2() { + return p2; + } } /** diff --git a/src/main/java/de/neemann/digital/draw/shapes/custom/svg/Context.java b/src/main/java/de/neemann/digital/draw/shapes/custom/svg/Context.java index 2a3e515b1..eb82da17c 100644 --- a/src/main/java/de/neemann/digital/draw/shapes/custom/svg/Context.java +++ b/src/main/java/de/neemann/digital/draw/shapes/custom/svg/Context.java @@ -114,6 +114,16 @@ class Context { return vector.transform(tr); } + public VectorInterface v(float x, float y) { + return new VectorFloat(x, y).transform(tr); + } + + public VectorInterface v(String xStr, String yStr) { + float x = xStr.isEmpty() ? 0 : Float.parseFloat(xStr); + float y = yStr.isEmpty() ? 0 : Float.parseFloat(yStr); + return v(x, y); + } + public float getFontSize() { return fontSize; } diff --git a/src/main/java/de/neemann/digital/draw/shapes/custom/svg/SvgImporter.java b/src/main/java/de/neemann/digital/draw/shapes/custom/svg/SvgImporter.java index c30304c72..9138d5b46 100644 --- a/src/main/java/de/neemann/digital/draw/shapes/custom/svg/SvgImporter.java +++ b/src/main/java/de/neemann/digital/draw/shapes/custom/svg/SvgImporter.java @@ -90,8 +90,8 @@ public class SvgImporter { break; case "line": csd.addLine( - vec(element.getAttribute("x1"), element.getAttribute("y1"), c).round(), - vec(element.getAttribute("x2"), element.getAttribute("y2"), c).round(), + c.v(element.getAttribute("x1"), element.getAttribute("y1")).round(), + c.v(element.getAttribute("x2"), element.getAttribute("y2")).round(), c.getThickness(), c.getColor()); break; case "rect": @@ -139,22 +139,12 @@ public class SvgImporter { csd.addPolygon(polygon.transform(c.getTransform()), c.getThickness(), c.getColor(), false); } - private VectorFloat vec(String xStr, String yStr, Context c) { - return c.getTransform().transform(vec(xStr, yStr)); - } - private VectorFloat vec(String xStr, String yStr) { float x = xStr.isEmpty() ? 0 : Float.parseFloat(xStr); float y = yStr.isEmpty() ? 0 : Float.parseFloat(yStr); return new VectorFloat(x, y); } - private VectorFloat vecD(String xStr, String yStr, Context c) { - float x = xStr.isEmpty() ? 0 : Float.parseFloat(xStr); - float y = yStr.isEmpty() ? 0 : Float.parseFloat(yStr); - return c.getTransform().getMatrix().transformDirection(new VectorFloat(x, y)); - } - private void drawRect(CustomShapeDescription csd, Element element, Context c) { VectorInterface size = vec(element.getAttribute("width"), element.getAttribute("height")); VectorInterface pos = vec(element.getAttribute("x"), element.getAttribute("y")); @@ -165,31 +155,35 @@ public class SvgImporter { float width = size.getXFloat(); float height = size.getYFloat(); if (rad.getXFloat() * rad.getYFloat() != 0) { - float w = size.getXFloat() - 2 * rad.getXFloat(); - float h = size.getYFloat() - 2 * rad.getYFloat(); float rx = rad.getXFloat(); float ry = rad.getYFloat(); + float w = size.getXFloat() - 2 * rx; + float h = size.getYFloat() - 2 * ry; + + double f = 4 * (Math.sqrt(2) - 1) / 3; + float cx = (float) (f * rx); + float cy = (float) (f * ry); + csd.addPolygon(new Polygon(true) - .add(c.tr(new VectorFloat(x + rx, y))) - .add(c.tr(new VectorFloat(x + rx + w, y))) - .add(c.tr(new VectorFloat(x + width, y + ry))) - .add(c.tr(new VectorFloat(x + width, y + ry + h))) - .add(c.tr(new VectorFloat(x + rx + w, y + height))) - .add(c.tr(new VectorFloat(x + rx, y + height))) - .add(c.tr(new VectorFloat(x, y + ry + h))) - .add(c.tr(new VectorFloat(x, y + ry))), c.getThickness(), c.getColor(), false); + .add(c.v(x + rx + w, y)) + .add(c.v(x + rx + w + cx, y), c.v(x + width, y + ry - cy), c.v(x + width, y + ry)) + .add(c.v(x + width, y + ry + h)) + .add(c.v(x + width, y + ry + h + cy), c.v(x + rx + w + cx, y + height), c.v(x + rx + w, y + height)) + .add(c.v(x + rx, y + height)) + .add(c.v(x + rx - cx, y + height), c.v(x, y + ry + h + cy), c.v(x, y + ry + h)) + .add(c.v(x, y + ry)) + .add(c.v(x, y + ry - cy), c.v(x + rx - cx, y), c.v(x + rx, y)), c.getThickness(), c.getColor(), false); } else csd.addPolygon(new Polygon(true) - .add(c.tr(new VectorFloat(x, y))) - .add(c.tr(new VectorFloat(x + width, y))) - .add(c.tr(new VectorFloat(x + width, y + height))) - .add(c.tr(new VectorFloat(x, y + height))), c.getThickness(), c.getColor(), false); + .add(c.v(x, y)) + .add(c.v(x + width, y)) + .add(c.v(x + width, y + height)) + .add(c.v(x, y + height)), c.getThickness(), c.getColor(), false); } private void drawCircle(CustomShapeDescription csd, Element element, Context c) { - if (element.hasAttribute("id")) { - VectorFloat pos = vec(element.getAttribute("cx"), element.getAttribute("cy"), c); + VectorInterface pos = c.v(element.getAttribute("cx"), element.getAttribute("cy")); String id = element.getAttribute("id"); if (id.startsWith("pin:")) { csd.addPin(id.substring(4).trim(), pos.round(), false); @@ -203,20 +197,33 @@ public class SvgImporter { VectorFloat r = null; if (element.hasAttribute("r")) { final String rad = element.getAttribute("r"); - r = vecD(rad, rad, c); + r = vec(rad, rad); } if (element.hasAttribute("rx")) { - r = vecD(element.getAttribute("rx"), element.getAttribute("ry"), c); + r = vec(element.getAttribute("rx"), element.getAttribute("ry")); } if (r != null) { - VectorFloat pos = vec(element.getAttribute("cx"), element.getAttribute("cy"), c); - VectorFloat oben = pos.sub(r); - VectorFloat unten = pos.add(r); + VectorFloat pos = vec(element.getAttribute("cx"), element.getAttribute("cy")); + float x = pos.getXFloat(); + float y = pos.getYFloat(); + float rx = r.getXFloat(); + float ry = r.getYFloat(); + + double f = 4 * (Math.sqrt(2) - 1) / 3; + float cx = (float) (f * rx); + float cy = (float) (f * ry); + + Polygon poly = new Polygon(true) + .add(c.v(x - rx, y)) + .add(c.v(x - rx, y + cy), c.v(x - cx, y + ry), c.v(x, y + ry)) + .add(c.v(x + cx, y + ry), c.v(x + rx, y + cy), c.v(x + rx, y)) + .add(c.v(x + rx, y - cy), c.v(x + cx, y - ry), c.v(x, y - ry)) + .add(c.v(x - cx, y - ry), c.v(x - rx, y - cy), c.v(x - rx, y)); if (c.getFilled() != null) - csd.addCircle(oben.round(), unten.round(), c.getThickness(), c.getFilled(), true); + csd.addPolygon(poly, c.getThickness(), c.getFilled(), true); if (c.getColor() != null) - csd.addCircle(oben.round(), unten.round(), c.getThickness(), c.getColor(), false); + csd.addPolygon(poly, c.getThickness(), c.getColor(), false); } } diff --git a/src/main/resources/lang/lang_de.xml b/src/main/resources/lang/lang_de.xml index 7757128b8..b8e2791c0 100644 --- a/src/main/resources/lang/lang_de.xml +++ b/src/main/resources/lang/lang_de.xml @@ -957,6 +957,7 @@ Sind evtl. die Namen der Variablen nicht eindeutig? Kein Programmspeicher im Modell gefunden! Ein Programmspeicher muss gewählt werden! Mehr als einen Programmspeicher gefunden. Es darf nur einen Programmspeicher geben. Fehler beim Laden des Programmspeichers. + Fehler beim Laden der SVG-Datei. Adress-Bits Anzahl der Adress-Bits, die verwendet werden. diff --git a/src/main/resources/lang/lang_en.xml b/src/main/resources/lang/lang_en.xml index acd44f84a..28cd10882 100644 --- a/src/main/resources/lang/lang_en.xml +++ b/src/main/resources/lang/lang_en.xml @@ -954,6 +954,7 @@ No program memory found! The program memory needs to be flagged as such. More then one program memories found! Only one program memory must be flages as such. Error loading the program memory. + Error while reading the SVG file. Address Bits Number of address bits used. diff --git a/src/test/java/de/neemann/digital/draw/shapes/custom/SvgImporterTest.java b/src/test/java/de/neemann/digital/draw/shapes/custom/SvgImporterTest.java index dc3d7929d..aff0e2b81 100644 --- a/src/test/java/de/neemann/digital/draw/shapes/custom/SvgImporterTest.java +++ b/src/test/java/de/neemann/digital/draw/shapes/custom/SvgImporterTest.java @@ -1,8 +1,14 @@ +/* + * Copyright (c) 2018 Helmut Neemann. + * Use of this source code is governed by the GPL v3 license + * that can be found in the LICENSE file. + */ package de.neemann.digital.draw.shapes.custom; import de.neemann.digital.draw.elements.PinException; import de.neemann.digital.draw.graphics.Polygon; import de.neemann.digital.draw.graphics.PolygonParser; +import de.neemann.digital.draw.graphics.VectorInterface; import de.neemann.digital.draw.shapes.Drawable; import de.neemann.digital.draw.shapes.custom.svg.SvgException; import de.neemann.digital.draw.shapes.custom.svg.SvgImporter; @@ -130,6 +136,100 @@ public class SvgImporterTest extends TestCase { .check(); } + public void testPolygonRotate() throws IOException, SvgException, PolygonParser.ParserException, PinException { + CustomShapeDescription custom = new SvgImporter( + in("\n" + + "\n" + + "\n" + + "\n" + + "")).create(); + + new CSDChecker(custom) + .checkPolygon("M 0,0 L -50,86.60254 L 36.60254,136.60254 L 86.60254,50 Z") + .check(); + } + + public void testPolygonRotate2() throws IOException, SvgException, PolygonParser.ParserException, PinException { + CustomShapeDescription custom = new SvgImporter( + in("\n" + + "\n" + + "\n" + + "\n" + + "")).create(); + + new CSDChecker(custom) + .checkPolygon("M 50,-20.710678 L -20.710678,50 L 50,120.71068 L 120.71068,50 Z") + .check(); + } + + public void testPolygonMatrix() throws IOException, SvgException, PolygonParser.ParserException, PinException { + CustomShapeDescription custom = new SvgImporter( + in("\n" + + "\n" + + "\n" + + "\n" + + "")).create(); + + new CSDChecker(custom) + .checkPolygon("M 5,10 L 15,100 L 95,120 L 85,30 Z") + .check(); + } + + public void testRect() throws IOException, SvgException, PolygonParser.ParserException, PinException { + CustomShapeDescription custom = new SvgImporter( + in("\n" + + "\n" + + "")).create(); + + new CSDChecker(custom) + .checkPolygon("M 10,20 L 80,20 L 80,100 L 10,100 Z") + .check(); + } + + public void testRectRound() throws IOException, SvgException, PolygonParser.ParserException, PinException { + CustomShapeDescription custom = new SvgImporter( + in("\n" + + "\n" + + "")).create(); + + new CSDChecker(custom) + .checkPolygon("M 70,20 C 75.52285,20 80,28.954304 80,40 L 80,80 C 80,91.04569 75.52285,100 70,100 L 20,100 C 14.477152,100 10,91.04569 10,80 L 10,40 C 10,28.954304 14.477152,20 20,20 Z") + .check(); + } + + public void testCircle() throws IOException, SvgException, PolygonParser.ParserException, PinException { + CustomShapeDescription custom = new SvgImporter( + in("\n" + + "\n" + + "\n" + + "\n" + + "")).create(); + + new CSDChecker(custom) + .checkPolygon("M 27,68 C 28.656855,82.91169 40.745167,97.686295 54,101 C 67.25484,104.313705 76.65685,94.91169 75,80 C 73.34315,65.08831 61.254833,50.31371 48,47 C 34.745167,43.68629 25.343145,53.08831 27,68 Z") + .check(); + } + + public void testScale() throws IOException, SvgException, PolygonParser.ParserException, PinException { + CustomShapeDescription custom = new SvgImporter( + in("\n" + + "\n" + + "\n" + + "\n" + + "")).create(); + + new CSDChecker(custom) + .checkLine(20, 60, 160, 210) + .check(); + } + //***************************************************************************************************** @@ -191,6 +291,21 @@ public class SvgImporterTest extends TestCase { return this; } + public CSDChecker checkLine(int x1, int y1, int x2, int y2) { + checker.add(new Checker() { + @Override + public void check(Drawable d) { + assertTrue(d instanceof CustomShapeDescription.LineHolder); + CustomShapeDescription.LineHolder l = (CustomShapeDescription.LineHolder) d; + assertEquals(x1, l.getP1().x); + assertEquals(y1, l.getP1().y); + assertEquals(x2, l.getP2().x); + assertEquals(y2, l.getP2().y); + } + }); + return this; + } + private static class TestPin { private final int x; private final int y; @@ -222,8 +337,22 @@ public class SvgImporterTest extends TestCase { public void check(Drawable d) { assertTrue("no polygon found", d instanceof CustomShapeDescription.PolygonHolder); final Polygon polygon = ((CustomShapeDescription.PolygonHolder) d).getPolygon(); - assertEquals("wrong polygon shape", should.toString(), polygon.toString()); assertEquals("wrong evanOdd mode", should.getEvenOdd(), polygon.getEvenOdd()); + + ArrayList shouldPoints = new ArrayList<>(); + should.traverse(shouldPoints::add); + ArrayList isPoints = new ArrayList<>(); + polygon.traverse(isPoints::add); + + //System.out.println(polygon); + + assertEquals("not the correct polygon size", shouldPoints.size(), isPoints.size()); + for (int i = 0; i < shouldPoints.size(); i++) { + VectorInterface sh = shouldPoints.get(i); + VectorInterface is = isPoints.get(i); + assertEquals("x coordinate " + i, sh.getXFloat(), is.getXFloat(), 1e-4); + assertEquals("y coordinate " + i, sh.getYFloat(), is.getYFloat(), 1e-4); + } } }