fixes bug if invalid arc radii are used

This commit is contained in:
hneemann 2018-12-01 18:54:25 +01:00
parent c3b43b753a
commit c83cb41210
4 changed files with 86 additions and 48 deletions

View File

@ -255,6 +255,20 @@ public class PolygonParser {
*/ */
//CHECKSTYLE.OFF: ParameterNumberCheck //CHECKSTYLE.OFF: ParameterNumberCheck
private void addArc(Polygon p, VectorInterface current, float rx, float ry, float rot, boolean large, boolean sweep, VectorFloat pos) { 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; Transform tr = Transform.IDENTITY;
if (rx != ry) if (rx != ry)
tr = TransformMatrix.scale(1, rx / ry); tr = TransformMatrix.scale(1, rx / ry);
@ -270,6 +284,11 @@ public class PolygonParser {
// ellipse is transformed to a circle with radius r // ellipse is transformed to a circle with radius r
float r = rx; 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 x1 = p1.getXFloat();
double y1 = p1.getYFloat(); double y1 = p1.getYFloat();
double x2 = p2.getXFloat(); double x2 = p2.getXFloat();
@ -281,6 +300,7 @@ public class PolygonParser {
double y2q = y2 * y2; double y2q = y2 * y2;
double rq = r * r; double rq = r * r;
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 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 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 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)));
@ -325,6 +345,10 @@ public class PolygonParser {
start += delta; start += delta;
} }
addArcPoint(p, lastStart, end, x0, y0, r, invert); addArcPoint(p, lastStart, end, x0, y0, r, invert);
} catch (SqrtException e) {
p.add(pos);
}
} }
//CHECKSTYLE.ON: ParameterNumberCheck //CHECKSTYLE.ON: ParameterNumberCheck
@ -336,8 +360,13 @@ public class PolygonParser {
p.add(c.transform(tr), p1.transform(tr)); p.add(c.transform(tr), p1.transform(tr));
} }
private static double sqrt(double x) { private static double sqrt(double x) throws SqrtException {
if (x > 0)
return Math.sqrt(x); return Math.sqrt(x);
if (x > -1e-6)
return 0;
throw new SqrtException();
} }
private static double sign(double x) { private static double sign(double x) {
@ -394,4 +423,6 @@ public class PolygonParser {
return p; return p;
} }
private static class SqrtException extends Exception {
}
} }

View File

@ -42,6 +42,7 @@ class Context {
tr = Transform.IDENTITY; tr = Transform.IDENTITY;
thickness = 1; thickness = 1;
color = Color.BLACK; color = Color.BLACK;
fill = Color.BLACK;
} }
private Context(Context parent) { private Context(Context parent) {
@ -50,6 +51,7 @@ class Context {
color = parent.color; color = parent.color;
thickness = parent.thickness; thickness = parent.thickness;
fontSize = parent.fontSize; fontSize = parent.fontSize;
textAnchor = parent.textAnchor;
fillRuleEvenOdd = parent.fillRuleEvenOdd; fillRuleEvenOdd = parent.fillRuleEvenOdd;
} }
@ -128,7 +130,6 @@ class Context {
return fontSize; return fontSize;
} }
private interface AttrParser { private interface AttrParser {
void parse(Context c, String value) throws SvgException; void parse(Context c, String value) throws SvgException;
} }

View File

@ -135,7 +135,7 @@ public class SvgImporter {
} }
private void drawPolygon(CustomShapeDescription csd, Context c, Polygon polygon) { 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); csd.addPolygon(polygon.transform(c.getTransform()), c.getThickness(), c.getFilled(), true);
if (c.getColor() != null) if (c.getColor() != null)
csd.addPolygon(polygon.transform(c.getTransform()), c.getThickness(), c.getColor(), false); 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 + width, y + height))
.add(c.v(x, y + height)); .add(c.v(x, y + height));
if (c.getFilled() != null) drawPolygon(csd, c, polygon);
csd.addPolygon(polygon, c.getThickness(), c.getFilled(), true);
if (c.getColor() != null)
csd.addPolygon(polygon, c.getThickness(), c.getColor(), false);
} }
private void drawCircle(CustomShapeDescription csd, Element element, Context c) { 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 + 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 - cx, y - ry), c.v(x - rx, y - cy), c.v(x - rx, y));
if (c.getFilled() != null) drawPolygon(csd, c, poly);
csd.addPolygon(poly, c.getThickness(), c.getFilled(), true);
if (c.getColor() != null)
csd.addPolygon(poly, c.getThickness(), c.getColor(), false);
} }
} }

View File

@ -278,6 +278,18 @@ public class SvgImporterTest extends TestCase {
.check(); .check();
} }
public void testInvalidArcRadii() throws IOException, SvgException, PolygonParser.ParserException, PinException {
CustomShapeDescription custom = new SvgImporter(
in("<svg viewBox=\"0 0 200 100\" xmlns=\"http://www.w3.org/2000/svg\">\n" +
"<path fill=\"none\" stroke=\"black\" stroke-width=\"3\"\n" +
" d=\"M 0,0 L 40,0 A 10,20,0,1,1,100,0 L 140,0\"/>\n" +
"</svg>")).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 { public void testInkscape1() throws IOException, SvgException, PolygonParser.ParserException, PinException {
CustomShapeDescription custom = new SvgImporter( CustomShapeDescription custom = new SvgImporter(
in("<svg viewBox=\"0 0 200 100\" xmlns=\"http://www.w3.org/2000/svg\">\n" + in("<svg viewBox=\"0 0 200 100\" xmlns=\"http://www.w3.org/2000/svg\">\n" +
@ -429,7 +441,7 @@ public class SvgImporterTest extends TestCase {
ArrayList<VectorInterface> isPoints = new ArrayList<>(); ArrayList<VectorInterface> isPoints = new ArrayList<>();
polygon.traverse(isPoints::add); polygon.traverse(isPoints::add);
//System.out.println(polygon); System.out.println(polygon);
assertEquals("not the correct polygon size", shouldPoints.size(), isPoints.size()); assertEquals("not the correct polygon size", shouldPoints.size(), isPoints.size());
for (int i = 0; i < shouldPoints.size(); i++) { for (int i = 0; i < shouldPoints.size(); i++) {