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
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 {
}
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -278,6 +278,18 @@ public class SvgImporterTest extends TestCase {
.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 {
CustomShapeDescription custom = new SvgImporter(
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<>();
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++) {