You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@poi.apache.org by fa...@apache.org on 2021/11/02 19:35:25 UTC

svn commit: r1894696 - in /poi/trunk/poi-ooxml/src: main/java/org/apache/poi/xdgf/usermodel/section/geometry/ArcTo.java test/java/org/apache/poi/xdgf/usermodel/section/geometry/ test/java/org/apache/poi/xdgf/usermodel/section/geometry/TestArcTo.java

Author: fanningpj
Date: Tue Nov  2 19:35:25 2021
New Revision: 1894696

URL: http://svn.apache.org/viewvc?rev=1894696&view=rev
Log:
[github-270] Draw correct XDGF circular arc. Thanks to Dmitry Komarov. This closes #270

Added:
    poi/trunk/poi-ooxml/src/test/java/org/apache/poi/xdgf/usermodel/section/geometry/
    poi/trunk/poi-ooxml/src/test/java/org/apache/poi/xdgf/usermodel/section/geometry/TestArcTo.java   (with props)
Modified:
    poi/trunk/poi-ooxml/src/main/java/org/apache/poi/xdgf/usermodel/section/geometry/ArcTo.java

Modified: poi/trunk/poi-ooxml/src/main/java/org/apache/poi/xdgf/usermodel/section/geometry/ArcTo.java
URL: http://svn.apache.org/viewvc/poi/trunk/poi-ooxml/src/main/java/org/apache/poi/xdgf/usermodel/section/geometry/ArcTo.java?rev=1894696&r1=1894695&r2=1894696&view=diff
==============================================================================
--- poi/trunk/poi-ooxml/src/main/java/org/apache/poi/xdgf/usermodel/section/geometry/ArcTo.java (original)
+++ poi/trunk/poi-ooxml/src/main/java/org/apache/poi/xdgf/usermodel/section/geometry/ArcTo.java Tue Nov  2 19:35:25 2021
@@ -17,8 +17,6 @@
 
 package org.apache.poi.xdgf.usermodel.section.geometry;
 
-import java.awt.geom.AffineTransform;
-import java.awt.geom.Arc2D;
 import java.awt.geom.Path2D;
 import java.awt.geom.Point2D;
 
@@ -112,20 +110,21 @@ public class ArcTo implements GeometryRo
         double x0 = last.getX();
         double y0 = last.getY();
 
-        double chordLength = Math.hypot(y - y0, x - x0);
-        double radius = (4 * a * a + chordLength * chordLength)
-                / (8 * Math.abs(a));
+        // Find a normal to the chord of the circle.
+        double nx = y - y0;
+        double ny = x0 - x;
+        double nLength = Math.sqrt(nx * nx + ny * ny);
+
+        // Follow the normal with the height of the arc to get the third point on the circle.
+        double x1 = (x0 + x) / 2 + a * nx / nLength;
+        double y1 = (y0 + y) / 2 + a * ny / nLength;
 
-        // center point
-        double cx = x0 + (x - x0) / 2.0;
-        double cy = y0 + (y - y0) / 2.0;
-
-        double rotate = Math.atan2(y - cy, x - cx);
-
-        Arc2D arc = new Arc2D.Double(x0, y0 - radius, chordLength, 2 * radius,
-                180, x0 < x ? 180 : -180, Arc2D.OPEN);
+        // Add an elliptical arc with rx / ry = 1 to the path because it's a circle.
+        EllipticalArcTo.createEllipticalArc(x, y, x1, y1, 0.0, 1.0, path);
+    }
 
-        path.append(AffineTransform.getRotateInstance(rotate, x0, y0)
-                .createTransformedShape(arc), true);
+    @Override
+    public String toString() {
+        return String.format("ArcTo: x=%f; y=%f; a=%f", x, y, a);
     }
 }

Added: poi/trunk/poi-ooxml/src/test/java/org/apache/poi/xdgf/usermodel/section/geometry/TestArcTo.java
URL: http://svn.apache.org/viewvc/poi/trunk/poi-ooxml/src/test/java/org/apache/poi/xdgf/usermodel/section/geometry/TestArcTo.java?rev=1894696&view=auto
==============================================================================
--- poi/trunk/poi-ooxml/src/test/java/org/apache/poi/xdgf/usermodel/section/geometry/TestArcTo.java (added)
+++ poi/trunk/poi-ooxml/src/test/java/org/apache/poi/xdgf/usermodel/section/geometry/TestArcTo.java Tue Nov  2 19:35:25 2021
@@ -0,0 +1,179 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.xdgf.usermodel.section.geometry;
+
+import com.microsoft.schemas.office.visio.x2012.main.CellType;
+import com.microsoft.schemas.office.visio.x2012.main.RowType;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.awt.geom.Path2D;
+import java.awt.geom.PathIterator;
+import java.util.Arrays;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class TestArcTo {
+
+    private static final double EPS = 0.000001;
+
+    // We draw a circular arc with radius 100 from (0, 0) to (100, 100)
+    private static final double X0 = 0.0;
+    private static final double Y0 = 0.0;
+    private static final double X = 100.0;
+    private static final double Y = 100.0;
+    private static final double A = 29.289322; // a = radius - sqrt(((x + x0) / 2) ^ 2 + ((y + y0) / 2) ^2)
+
+    @Test
+    public void shouldDrawCircularArcWhenArcHeightMoreThanZero() {
+        ArcTo arcTo = createArcTo(A);
+
+        Path2D.Double actualPath = new Path2D.Double();
+        actualPath.moveTo(X0, Y0);
+
+        // Shape isn't used while creating a circular arc
+        arcTo.addToPath(actualPath, null);
+
+        // This path can be used to draw a curve that approximates calculated arc.
+        Path2D.Double expectedPath = new Path2D.Double();
+        expectedPath.moveTo(X0, Y0);
+        expectedPath.curveTo(26.521649, 0.0, 51.957040, 10.535684, 70.710678, 29.289321);
+        expectedPath.curveTo(89.464316, 48.042960, 100.000000, 73.478351, X, Y);
+
+        assertPath(expectedPath, actualPath);
+    }
+
+    @Test
+    public void shouldDrawCircularArcWhenArcHeightLessThanZero() {
+        ArcTo arcTo = createArcTo(-A);
+
+        Path2D.Double actualPath = new Path2D.Double();
+        actualPath.moveTo(X0, Y0);
+
+        // Shape isn't used while creating a circular arc
+        arcTo.addToPath(actualPath, null);
+
+        // This path can be used to draw a curve that approximates calculated arc.
+        Path2D.Double expectedPath = new Path2D.Double();
+        expectedPath.moveTo(X0, Y0);
+        expectedPath.curveTo(0.0, 26.521649, 10.535684, 51.957040, 29.289321, 70.710678);
+        expectedPath.curveTo(48.042960, 89.464316,  73.478351, 100.000000,  X, Y);
+
+        assertPath(expectedPath, actualPath);
+    }
+
+    @Test
+    public void shouldDrawLineInsteadOfArcWhenArcHeightIsZero() {
+        ArcTo arcTo = createArcTo(0.0);
+
+        Path2D.Double actualPath = new Path2D.Double();
+        actualPath.moveTo(X0, Y0);
+
+        // Shape isn't used while creating a circular arc
+        arcTo.addToPath(actualPath, null);
+
+        // This path can be used to draw a curve that approximates calculated arc.
+        Path2D.Double expectedPath = new Path2D.Double();
+        expectedPath.moveTo(X0, Y0);
+        expectedPath.lineTo(X, Y);
+
+        assertPath(expectedPath, actualPath);
+    }
+
+    @Test
+    public void shouldNotDrawAnythingWhenArcIsDeleted() {
+        RowType row = RowType.Factory.newInstance();
+        row.setIX(0L);
+        row.setDel(true);
+
+        ArcTo arcTo = new ArcTo(row);
+
+        Path2D.Double actualPath = new Path2D.Double();
+        actualPath.moveTo(X0, Y0);
+
+        // Shape isn't used while creating a circular arc
+        arcTo.addToPath(actualPath, null);
+
+        // This path can be used to draw a curve that approximates calculated arc.
+        Path2D.Double expectedPath = new Path2D.Double();
+        expectedPath.moveTo(X0, Y0);
+
+        assertPath(expectedPath, actualPath);
+    }
+
+    private static ArcTo createArcTo(double a) {
+        RowType row = RowType.Factory.newInstance();
+        row.setIX(0L);
+        row.setDel(false);
+
+        CellType xCell = CellType.Factory.newInstance();
+        xCell.setN("X");
+        xCell.setV(Double.toString(X));
+
+        CellType yCell = CellType.Factory.newInstance();
+        yCell.setN("Y");
+        yCell.setV(Double.toString(Y));
+
+
+        CellType aCell = CellType.Factory.newInstance();
+        aCell.setN("A");
+        aCell.setV(Double.toString(a));
+
+        CellType[] cells = new CellType[] { xCell , yCell, aCell };
+        row.setCellArray(cells);
+
+        return new ArcTo(row);
+    }
+
+    private static void assertPath(Path2D expected, Path2D actual) {
+        PathIterator expectedIterator = expected.getPathIterator(null);
+        PathIterator actualIterator = actual.getPathIterator(null);
+
+        double[] expectedCoordinates = new double[6];
+        double[] actualCoordinates = new double[6];
+        while (!expectedIterator.isDone() && !actualIterator.isDone()) {
+            int expectedSegmentType = expectedIterator.currentSegment(expectedCoordinates);
+            int actualSegmentType = actualIterator.currentSegment(actualCoordinates);
+
+            assertEquals(expectedSegmentType, actualSegmentType);
+            assertCoordinates(expectedCoordinates, actualCoordinates);
+
+            expectedIterator.next();
+            actualIterator.next();
+        }
+
+        if (!expectedIterator.isDone() || !actualIterator.isDone()) {
+            Assertions.fail("Path iterators have different number of segments");
+        }
+    }
+
+    private static void assertCoordinates(double[] expected, double[] actual) {
+        if (expected.length != actual.length) {
+            Assertions.fail(String.format("Given coordinates arrays have different length: expected=%s, actual=%s",
+                    Arrays.toString(expected), Arrays.toString(actual)));
+        }
+        for (int i = 0; i < expected.length; i++) {
+            double e = expected[i];
+            double a = actual[i];
+
+            if (Math.abs(e - a) > EPS) {
+                Assertions.fail(String.format("expected <%f> but found <%f>", e, a));
+            }
+        }
+    }
+}

Propchange: poi/trunk/poi-ooxml/src/test/java/org/apache/poi/xdgf/usermodel/section/geometry/TestArcTo.java
------------------------------------------------------------------------------
    svn:eol-style = native



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@poi.apache.org
For additional commands, e-mail: commits-help@poi.apache.org