From c83cb41210706de3336a68a0849d548041a9f744 Mon Sep 17 00:00:00 2001 From: hneemann Date: Sat, 1 Dec 2018 18:54:25 +0100 Subject: [PATCH] fixes bug if invalid arc radii are used --- .../digital/draw/graphics/PolygonParser.java | 105 ++++++++++++------ .../draw/shapes/custom/svg/Context.java | 3 +- .../draw/shapes/custom/svg/SvgImporter.java | 12 +- .../draw/shapes/custom/SvgImporterTest.java | 14 ++- 4 files changed, 86 insertions(+), 48 deletions(-) diff --git a/src/main/java/de/neemann/digital/draw/graphics/PolygonParser.java b/src/main/java/de/neemann/digital/draw/graphics/PolygonParser.java index e9a2caf87..35f187e8d 100644 --- a/src/main/java/de/neemann/digital/draw/graphics/PolygonParser.java +++ b/src/main/java/de/neemann/digital/draw/graphics/PolygonParser.java @@ -255,6 +255,20 @@ public class PolygonParser { */ //CHECKSTYLE.OFF: ParameterNumberCheck private void addArc(Polygon p, VectorInterface current, float rx, float ry, float rot, boolean large, boolean sweep, VectorFloat pos) { + + // if rx=0 or ry=0 add a straight line + if (rx == 0 || ry == 0) { + p.add(pos); + return; + } + + // take the absolute value of rx, ry + if (rx < 0) + rx = -rx; + if (ry < 0) + ry = -ry; + + // transform the ellipse to a circle Transform tr = Transform.IDENTITY; if (rx != ry) tr = TransformMatrix.scale(1, rx / ry); @@ -270,6 +284,11 @@ public class PolygonParser { // ellipse is transformed to a circle with radius r float r = rx; + // correct invalid radii + final float dist = p1.sub(p2).len(); + if (dist > r * 2) + r = dist / 2; + double x1 = p1.getXFloat(); double y1 = p1.getYFloat(); double x2 = p2.getXFloat(); @@ -281,50 +300,55 @@ public class PolygonParser { double y2q = y2 * y2; double rq = r * r; - double x0A = (r * (y1 - y2) * sqrt(rq * (4 * rq - y1q + y2 * (2 * y1 - y2)) - rq * (x1q - 2 * x1 * x2 + x2q)) * sign(x1 - x2) + r * (x1 + x2) * sqrt(rq * (y1q - 2 * y1 * y2 + y2q) + rq * (x1q - 2 * x1 * x2 + x2q))) / (2 * r * sqrt(rq * (y1q - 2 * y1 * y2 + y2q) + rq * (x1q - 2 * x1 * x2 + x2q))); - double y0A = (r * (y1 + y2) * sqrt(rq * (y1q - 2 * y1 * y2 + y2q) + rq * (x1q - 2 * x1 * x2 + x2q)) - r * sqrt(rq * (4 * rq - y1q + y2 * (2 * y1 - y2)) - rq * (x1q - 2 * x1 * x2 + x2q)) * abs(x1 - x2)) / (2 * r * sqrt(rq * (y1q - 2 * y1 * y2 + y2q) + rq * (x1q - 2 * x1 * x2 + x2q))); - double x0B = (r * (x1 + x2) * sqrt(rq * (y1q - 2 * y1 * y2 + y2q) + rq * (x1q - 2 * x1 * x2 + x2q)) - r * (y1 - y2) * sqrt(rq * (4 * rq - y1q + y2 * (2 * y1 - y2)) - rq * (x1q - 2 * x1 * x2 + x2q)) * sign(x1 - x2)) / (2 * r * sqrt(rq * (y1q - 2 * y1 * y2 + y2q) + rq * (x1q - 2 * x1 * x2 + x2q))); - double y0B = (r * sqrt(rq * (4 * rq - y1q + y2 * (2 * y1 - y2)) - rq * (x1q - 2 * x1 * x2 + x2q)) * abs(x1 - x2) + r * (y1 + y2) * sqrt(rq * (y1q - 2 * y1 * y2 + y2q) + rq * (x1q - 2 * x1 * x2 + x2q))) / (2 * r * sqrt(rq * (y1q - 2 * y1 * y2 + y2q) + rq * (x1q - 2 * x1 * x2 + x2q))); + try { + double x0A = (r * (y1 - y2) * sqrt(rq * (4 * rq - y1q + y2 * (2 * y1 - y2)) - rq * (x1q - 2 * x1 * x2 + x2q)) * sign(x1 - x2) + r * (x1 + x2) * sqrt(rq * (y1q - 2 * y1 * y2 + y2q) + rq * (x1q - 2 * x1 * x2 + x2q))) / (2 * r * sqrt(rq * (y1q - 2 * y1 * y2 + y2q) + rq * (x1q - 2 * x1 * x2 + x2q))); + double y0A = (r * (y1 + y2) * sqrt(rq * (y1q - 2 * y1 * y2 + y2q) + rq * (x1q - 2 * x1 * x2 + x2q)) - r * sqrt(rq * (4 * rq - y1q + y2 * (2 * y1 - y2)) - rq * (x1q - 2 * x1 * x2 + x2q)) * abs(x1 - x2)) / (2 * r * sqrt(rq * (y1q - 2 * y1 * y2 + y2q) + rq * (x1q - 2 * x1 * x2 + x2q))); + double x0B = (r * (x1 + x2) * sqrt(rq * (y1q - 2 * y1 * y2 + y2q) + rq * (x1q - 2 * x1 * x2 + x2q)) - r * (y1 - y2) * sqrt(rq * (4 * rq - y1q + y2 * (2 * y1 - y2)) - rq * (x1q - 2 * x1 * x2 + x2q)) * sign(x1 - x2)) / (2 * r * sqrt(rq * (y1q - 2 * y1 * y2 + y2q) + rq * (x1q - 2 * x1 * x2 + x2q))); + double y0B = (r * sqrt(rq * (4 * rq - y1q + y2 * (2 * y1 - y2)) - rq * (x1q - 2 * x1 * x2 + x2q)) * abs(x1 - x2) + r * (y1 + y2) * sqrt(rq * (y1q - 2 * y1 * y2 + y2q) + rq * (x1q - 2 * x1 * x2 + x2q))) / (2 * r * sqrt(rq * (y1q - 2 * y1 * y2 + y2q) + rq * (x1q - 2 * x1 * x2 + x2q))); - double startA = Math.atan2(y1 - y0A, x1 - x0A); - double endA = Math.atan2(y2 - y0A, x2 - x0A); + double startA = Math.atan2(y1 - y0A, x1 - x0A); + double endA = Math.atan2(y2 - y0A, x2 - x0A); - double startB = Math.atan2(y1 - y0B, x1 - x0B); - double endB = Math.atan2(y2 - y0B, x2 - x0B); + double startB = Math.atan2(y1 - y0B, x1 - x0B); + double endB = Math.atan2(y2 - y0B, x2 - x0B); - double delta = 2 * Math.PI / 12; - if (!sweep) delta = -delta; + double delta = 2 * Math.PI / 12; + if (!sweep) delta = -delta; - if (delta > 0) { - if (endA < startA) endA += 2 * Math.PI; - if (endB < startB) endB += 2 * Math.PI; - } else { - if (endA > startA) endA -= 2 * Math.PI; - if (endB > startB) endB -= 2 * Math.PI; - } + if (delta > 0) { + if (endA < startA) endA += 2 * Math.PI; + if (endB < startB) endB += 2 * Math.PI; + } else { + if (endA > startA) endA -= 2 * Math.PI; + if (endB > startB) endB -= 2 * Math.PI; + } - double sizeA = Math.abs(startA - endA); - double sizeB = Math.abs(startB - endB); + double sizeA = Math.abs(startA - endA); + double sizeB = Math.abs(startB - endB); - double start = startA; - double end = endA; - double x0 = x0A; - double y0 = y0A; - if (large ^ (sizeA > sizeB)) { - start = startB; - end = endB; - x0 = x0B; - y0 = y0B; - } + double start = startA; + double end = endA; + double x0 = x0A; + double y0 = y0A; + if (large ^ (sizeA > sizeB)) { + start = startB; + end = endB; + x0 = x0B; + y0 = y0B; + } - double lastStart = start; - start += delta; - while (delta < 0 ^ start < end) { - addArcPoint(p, lastStart, start, x0, y0, r, invert); - lastStart = start; + double lastStart = start; start += delta; + while (delta < 0 ^ start < end) { + addArcPoint(p, lastStart, start, x0, y0, r, invert); + lastStart = start; + start += delta; + } + addArcPoint(p, lastStart, end, x0, y0, r, invert); + + } catch (SqrtException e) { + p.add(pos); } - addArcPoint(p, lastStart, end, x0, y0, r, invert); } //CHECKSTYLE.ON: ParameterNumberCheck @@ -336,8 +360,13 @@ public class PolygonParser { p.add(c.transform(tr), p1.transform(tr)); } - private static double sqrt(double x) { - return Math.sqrt(x); + private static double sqrt(double x) throws SqrtException { + if (x > 0) + return Math.sqrt(x); + if (x > -1e-6) + return 0; + + throw new SqrtException(); } private static double sign(double x) { @@ -394,4 +423,6 @@ public class PolygonParser { return p; } + private static class SqrtException extends Exception { + } } 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 eb82da17c..3cd28e526 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 @@ -42,6 +42,7 @@ class Context { tr = Transform.IDENTITY; thickness = 1; color = Color.BLACK; + fill = Color.BLACK; } private Context(Context parent) { @@ -50,6 +51,7 @@ class Context { color = parent.color; thickness = parent.thickness; fontSize = parent.fontSize; + textAnchor = parent.textAnchor; fillRuleEvenOdd = parent.fillRuleEvenOdd; } @@ -128,7 +130,6 @@ class Context { return fontSize; } - private interface AttrParser { void parse(Context c, String value) throws SvgException; } 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 257cc6464..c3fb31b66 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 @@ -135,7 +135,7 @@ public class SvgImporter { } private void drawPolygon(CustomShapeDescription csd, Context c, Polygon polygon) { - if (c.getFilled() != null) + if (c.getFilled() != null && polygon.isClosed()) csd.addPolygon(polygon.transform(c.getTransform()), c.getThickness(), c.getFilled(), true); if (c.getColor() != null) csd.addPolygon(polygon.transform(c.getTransform()), c.getThickness(), c.getColor(), false); @@ -184,10 +184,7 @@ public class SvgImporter { .add(c.v(x + width, y + height)) .add(c.v(x, y + height)); - if (c.getFilled() != null) - csd.addPolygon(polygon, c.getThickness(), c.getFilled(), true); - if (c.getColor() != null) - csd.addPolygon(polygon, c.getThickness(), c.getColor(), false); + drawPolygon(csd, c, polygon); } private void drawCircle(CustomShapeDescription csd, Element element, Context c) { @@ -229,10 +226,7 @@ public class SvgImporter { .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.addPolygon(poly, c.getThickness(), c.getFilled(), true); - if (c.getColor() != null) - csd.addPolygon(poly, c.getThickness(), c.getColor(), false); + drawPolygon(csd, c, poly); } } 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 4b1ff7a37..7aaac6d80 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 @@ -278,6 +278,18 @@ public class SvgImporterTest extends TestCase { .check(); } + public void testInvalidArcRadii() throws IOException, SvgException, PolygonParser.ParserException, PinException { + CustomShapeDescription custom = new SvgImporter( + in("\n" + + "\n" + + "")).create(); + + new CSDChecker(custom) + .checkPolygon("M 0,0 L 40,0 Q 40,-16.076952 44.019238,-30 Q 48.038475,-43.92305 55,-51.961525 Q 61.961525,-60 70,-60 Q 78.038475,-60 85,-51.961525 Q 91.961525,-43.92305 95.98076,-30 Q 100,-16.076952 100,-1.469576E-14 L 140,0") + .check(); + } + public void testInkscape1() throws IOException, SvgException, PolygonParser.ParserException, PinException { CustomShapeDescription custom = new SvgImporter( in("\n" + @@ -429,7 +441,7 @@ public class SvgImporterTest extends TestCase { ArrayList isPoints = new ArrayList<>(); polygon.traverse(isPoints::add); - //System.out.println(polygon); + System.out.println(polygon); assertEquals("not the correct polygon size", shouldPoints.size(), isPoints.size()); for (int i = 0; i < shouldPoints.size(); i++) {