changeset 1671:3d2641286b27

RT-6829 Transformed bounds of rotated Circle, Ellipse and Rectangle can be optimized
author Martin Sladecek <martin.sladecek@oracle.com>
date Tue, 28 Aug 2012 15:18:51 +0200
parents 737377b8ab1d
children 8b5b1c866827
files javafx-ui-common/src/javafx/scene/shape/Circle.java javafx-ui-common/src/javafx/scene/shape/Ellipse.java javafx-ui-common/src/javafx/scene/shape/Rectangle.java javafx-ui-common/test/unit/javafx/scene/shape/CircleTest.java javafx-ui-common/test/unit/javafx/scene/shape/EllipseTest.java javafx-ui-common/test/unit/javafx/scene/shape/RectangleTest.java
diffstat 6 files changed, 124 insertions(+), 25 deletions(-) [+]
line wrap: on
line diff
--- a/javafx-ui-common/src/javafx/scene/shape/Circle.java	Wed Aug 22 18:59:34 2012 -0400
+++ b/javafx-ui-common/src/javafx/scene/shape/Circle.java	Tue Aug 28 15:18:51 2012 +0200
@@ -61,11 +61,6 @@
 
     private final Ellipse2D shape = new Ellipse2D();
 
-    private static final int NON_RECTILINEAR_TYPE_MASK = ~(
-            BaseTransform.TYPE_TRANSLATION |
-            BaseTransform.TYPE_MASK_SCALE |
-            BaseTransform.TYPE_FLIP);
-
     /**
      * Creates a new instance of Circle with a specified radius.
      * @param radius the radius of the circle in pixels
@@ -286,28 +281,42 @@
         if (impl_mode == Mode.EMPTY) {
             return bounds.makeEmpty();
         }
-        if ((tx.getType() & NON_RECTILINEAR_TYPE_MASK) != 0) {
-            return computeShapeBounds(bounds, tx, impl_configShape());
+
+        final double cX = getCenterX();
+        final double cY = getCenterY();
+
+        if ((tx.getType() & ~(BaseTransform.TYPE_MASK_ROTATION | BaseTransform.TYPE_TRANSLATION)) == 0) {
+
+            double tCX = cX * tx.getMxx() + cY * tx.getMxy() + tx.getMxt();
+            double tCY = cX * tx.getMyx() + cY * tx.getMyy() + tx.getMyt();
+            double r = getRadius();
+
+            if (impl_mode != Mode.FILL && getStrokeType() != StrokeType.INSIDE) {
+                double upad = getStrokeWidth();
+                if (getStrokeType() == StrokeType.CENTERED) {
+                    upad /= 2.0f;
+                }
+                r += upad;
+            }
+
+            return bounds.deriveWithNewBounds((float) (tCX - r), (float) (tCY - r), 0,
+                    (float) (tCX + r), (float) (tCY + r), 0);
+        } else if ((tx.getType() & ~(BaseTransform.TYPE_MASK_SCALE | BaseTransform.TYPE_TRANSLATION | BaseTransform.TYPE_FLIP)) == 0) {
+            final double r = getRadius();
+            final double x = getCenterX() - r;
+            final double y = getCenterY() - r;
+            final double width = 2.0 * r;
+            final double height = width;
+            double upad;
+            if (impl_mode == Mode.FILL || getStrokeType() == StrokeType.INSIDE) {
+                upad = 0.0f;
+            } else {
+                upad = getStrokeWidth();
+            }
+            return computeBounds(bounds, tx, upad, 0, x, y, width, height);
         }
 
-        // compute the x, y, width and height of the circle
-        final double r = getRadius();
-        final double x = getCenterX() - r;
-        final double y = getCenterY() - r;
-        final double width = 2.0 * r;
-        final double height = width;
-        double upad;
-        double dpad;
-        if (impl_mode == Mode.FILL || getStrokeType() == StrokeType.INSIDE) {
-            upad = dpad = 0.0f;
-        } else {
-            upad = getStrokeWidth();
-            if (getStrokeType() == StrokeType.CENTERED) {
-                upad /= 2.0f;
-            }
-            dpad = 0.0f;
-        }
-        return computeBounds(bounds, tx, upad, dpad, x, y, width, height);
+        return computeShapeBounds(bounds, tx, impl_configShape());
     }
 
     /**
--- a/javafx-ui-common/src/javafx/scene/shape/Ellipse.java	Wed Aug 22 18:59:34 2012 -0400
+++ b/javafx-ui-common/src/javafx/scene/shape/Ellipse.java	Tue Aug 28 15:18:51 2012 +0200
@@ -58,6 +58,7 @@
 
     private static final int NON_RECTILINEAR_TYPE_MASK = ~(
             BaseTransform.TYPE_TRANSLATION |
+            BaseTransform.TYPE_QUADRANT_ROTATION |
             BaseTransform.TYPE_MASK_SCALE |
             BaseTransform.TYPE_FLIP);
 
--- a/javafx-ui-common/src/javafx/scene/shape/Rectangle.java	Wed Aug 22 18:59:34 2012 -0400
+++ b/javafx-ui-common/src/javafx/scene/shape/Rectangle.java	Tue Aug 28 15:18:51 2012 +0200
@@ -78,6 +78,7 @@
     private static final int NON_RECTILINEAR_TYPE_MASK = ~(
             BaseTransform.TYPE_TRANSLATION |
             BaseTransform.TYPE_MASK_SCALE |
+            BaseTransform.TYPE_QUADRANT_ROTATION | 
             BaseTransform.TYPE_FLIP);
 
     /**
--- a/javafx-ui-common/test/unit/javafx/scene/shape/CircleTest.java	Wed Aug 22 18:59:34 2012 -0400
+++ b/javafx-ui-common/test/unit/javafx/scene/shape/CircleTest.java	Tue Aug 28 15:18:51 2012 +0200
@@ -25,7 +25,14 @@
 
 package javafx.scene.shape;
 
+import com.sun.javafx.test.TestHelper;
+import javafx.geometry.Bounds;
 import javafx.scene.NodeTest;
+import javafx.scene.paint.Color;
+import static org.junit.Assert.*;
+import static com.sun.javafx.test.TestHelper.*;
+import javafx.scene.transform.Rotate;
+import javafx.scene.transform.Scale;
 
 import org.junit.Test;
 
@@ -72,4 +79,56 @@
                 "radius", "radius",
                 100.0);
     }
+
+    @Test
+    public void testTransformedBounds_rotation() {
+        Circle c = new Circle(50, 100, 10, Color.RED);
+        Bounds original = c.getBoundsInParent();
+        c.setRotate(15);
+        assertSimilar(original, c.getBoundsInParent());
+    }
+    
+    @Test
+    public void testTransformedBounds_rotation2() {
+        final int centerX = 50;
+        final int centerY = 200;
+        Circle c = new Circle(centerX, centerY, 10, Color.RED);
+        Bounds original = c.getBoundsInParent();
+        // Using integer isosceles triangle of (38, 181, 181)
+        Rotate r = new Rotate();
+        final double angle = Math.asin((38.0/2)/181)*2;
+        r.setAngle(Math.toDegrees(angle));
+        r.setPivotX(centerX + 181);
+        r.setPivotY(centerY);
+        c.getTransforms().add(r);
+
+        final double centerXDelta = Math.cos((Math.PI - angle)/2) * 38;
+        final double centerYDelta = - (Math.sin((Math.PI - angle)/2) * 38);
+
+        assertSimilar(TestHelper.box(original.getMinX() + centerXDelta, original.getMinY() + centerYDelta,
+                original.getWidth(), original.getHeight()), c.getBoundsInParent());
+    }
+
+    @Test
+    public void testTransformedBounds_translate() {
+        Circle c = new Circle(50, 100, 10, Color.RED);
+        Bounds original = c.getBoundsInParent();
+        c.setTranslateX(10);
+        c.setTranslateY(20);
+        assertSimilar(TestHelper.box(original.getMinX() + 10, original.getMinY() + 20,
+                original.getWidth(), original.getHeight()), c.getBoundsInParent());
+    }
+
+
+    @Test public void testTransformedBounds_scale() {
+        Circle c = new Circle(50, 100, 10, Color.RED);
+        double scalePivotX = (c.getCenterX() + c.getRadius()) / 2;
+        double scalePivotY = (c.getCenterY() + c.getRadius()) / 2;
+        Bounds original = c.getBoundsInParent();
+        Scale s = new Scale(2.0, 1.5, scalePivotX, scalePivotY);
+        c.getTransforms().setAll(s);
+        assertSimilar(TestHelper.box(2 * original.getMinX() - scalePivotX, 1.5 * original.getMinY() - 0.5 * scalePivotY,
+                2 * original.getWidth(), 1.5 * original.getHeight()), c.getBoundsInParent());
+    }
+
 }
--- a/javafx-ui-common/test/unit/javafx/scene/shape/EllipseTest.java	Wed Aug 22 18:59:34 2012 -0400
+++ b/javafx-ui-common/test/unit/javafx/scene/shape/EllipseTest.java	Tue Aug 28 15:18:51 2012 +0200
@@ -25,7 +25,11 @@
 
 package javafx.scene.shape;
 
+import com.sun.javafx.test.TestHelper;
+import javafx.geometry.Bounds;
 import javafx.scene.NodeTest;
+import static org.junit.Assert.*;
+import static com.sun.javafx.test.TestHelper.*;
 
 import org.junit.Test;
 
@@ -80,4 +84,13 @@
                 new Ellipse(300.0, 300.0, 100.0, 100.0),
                 "centerY", "centerY", 10.0);
     }
+
+    @Test
+    public void testTransformedBounds_rotation() {
+        Ellipse e = new Ellipse(50, 100, 10, 20);
+        Bounds original = e.getBoundsInParent();
+        e.setRotate(90);
+        assertSimilar(TestHelper.box(30, 90,
+                original.getHeight(), original.getWidth()), e.getBoundsInParent());
+    }
 }
--- a/javafx-ui-common/test/unit/javafx/scene/shape/RectangleTest.java	Wed Aug 22 18:59:34 2012 -0400
+++ b/javafx-ui-common/test/unit/javafx/scene/shape/RectangleTest.java	Tue Aug 28 15:18:51 2012 +0200
@@ -25,10 +25,14 @@
 
 package javafx.scene.shape;
 
+import com.sun.javafx.test.TestHelper;
+import javafx.geometry.Bounds;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertSame;
 import javafx.scene.NodeTest;
 import javafx.scene.paint.Color;
+import static org.junit.Assert.*;
+import static com.sun.javafx.test.TestHelper.*;
 
 import org.junit.Test;
 
@@ -129,4 +133,16 @@
                 new Rectangle(200.0, 100.0),
                 "arcHeight", "arcHeight", 30.0);
     }
+
+
+    @Test
+    public void testTransformedBounds_rotation() {
+        Rectangle r = new Rectangle(50, 100, 10, 20);
+        r.setArcHeight(5);
+        r.setArcWidth(10);
+        Bounds original = r.getBoundsInParent();
+        r.setRotate(90);
+        assertSimilar(TestHelper.box(45, 105,
+                original.getHeight(), original.getWidth()), r.getBoundsInParent());
+    }
 }