summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Wienand2012-06-13 18:35:27 (EDT)
committer anyssen2012-06-13 18:35:27 (EDT)
commit3843de88343b054c74990d9d76c274762e762acb (patch)
tree4ee44832d1ef0be999ae5b7b9e7a66d6f61820f9
parentc97605d908e5058e0a3c6f19af73cd2453d6d731 (diff)
downloadorg.eclipse.gef4-3843de88343b054c74990d9d76c274762e762acb.zip
org.eclipse.gef4-3843de88343b054c74990d9d76c274762e762acb.tar.gz
org.eclipse.gef4-3843de88343b054c74990d9d76c274762e762acb.tar.bz2
[355997] Finalized IPolyShape implementations and added transformation
interfaces (CQ 6545).
-rw-r--r--org.eclipse.gef4.geometry.doc/javadocOptions.txt2
-rw-r--r--org.eclipse.gef4.geometry.doc/reference/image_src/IGeometry_Planar_Abstractions.PNGbin85320 -> 0 bytes
-rw-r--r--org.eclipse.gef4.geometry.doc/reference/image_src/IGeometry_Planar_Abstractions.ucls20
-rw-r--r--org.eclipse.gef4.geometry.doc/reference/image_src/IGeometry_Planar_Overview.PNGbin74485 -> 0 bytes
-rw-r--r--org.eclipse.gef4.geometry.doc/reference/image_src/IGeometry_Planar_Overview.ucls163
-rw-r--r--org.eclipse.gef4.geometry.doc/reference/image_src/inheritance-hierarchy.ucls283
-rw-r--r--org.eclipse.gef4.geometry.doc/reference/image_src/transform-overview.ucls44
-rw-r--r--org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/demos/BezierApproximationExample.java (renamed from org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/intersection/BezierApproximationExample.java)17
-rw-r--r--org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/demos/ConvexHullExample.java (renamed from org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/intersection/ConvexHullExample.java)4
-rw-r--r--org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/demos/CubicCurveDeCasteljauExample.java (renamed from org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/intersection/CubicCurveDeCasteljauExample.java)4
-rw-r--r--org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/demos/RegionExample.java100
-rw-r--r--org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/demos/RegionOutlineExample.java113
-rw-r--r--org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/demos/RingExample.java128
-rw-r--r--org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/demos/TriangulationExample.java109
-rw-r--r--org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/intersection/AbstractIntersectionExample.java5
-rw-r--r--org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/scalerotate/CubicCurveScaleRotate.java62
-rw-r--r--org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/AllTests.java14
-rw-r--r--org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/AngleTests.java2
-rw-r--r--org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/BezierCurveTests.java345
-rw-r--r--org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/CurveUtilsTests.java4
-rw-r--r--org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/DimensionTests.java21
-rw-r--r--org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/EllipseTests.java170
-rw-r--r--org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/LineTests.java8
-rw-r--r--org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/PointTests.java4
-rw-r--r--org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/PolygonTests.java23
-rw-r--r--org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/PolylineTests.java22
-rw-r--r--org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/RectangleTests.java36
-rw-r--r--org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/RegionTests.java103
-rw-r--r--org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/RingTests.java1120
-rw-r--r--org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/RoundedRectangleTests.java214
-rw-r--r--org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/StraightTests.java4
-rw-r--r--org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/Vector3DTests.java115
-rw-r--r--org.eclipse.gef4.geometry/META-INF/MANIFEST.MF2
-rw-r--r--org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/Point.java3
-rw-r--r--org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/euclidean/Straight.java4
-rw-r--r--org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/AbstractArcBasedGeometry.java300
-rw-r--r--org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/AbstractPointListBasedGeometry.java73
-rw-r--r--org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/AbstractPolyShape.java181
-rw-r--r--org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/AbstractRectangleBasedGeometry.java96
-rw-r--r--org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Arc.java246
-rw-r--r--org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/BezierCurve.java923
-rw-r--r--org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/BezierSpline.java2
-rw-r--r--org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/CubicCurve.java99
-rw-r--r--org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Ellipse.java96
-rw-r--r--org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/IPolyShape.java26
-rw-r--r--org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Line.java47
-rw-r--r--org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Pie.java96
-rw-r--r--org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/PolyBezier.java208
-rw-r--r--org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Polygon.java409
-rw-r--r--org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Polyline.java135
-rw-r--r--org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/QuadraticCurve.java101
-rw-r--r--org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Rectangle.java345
-rw-r--r--org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Region.java359
-rw-r--r--org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Ring.java506
-rw-r--r--org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/RoundedRectangle.java221
-rw-r--r--org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/projective/Vector3D.java4
-rw-r--r--org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/transform/IRotatable.java134
-rw-r--r--org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/transform/IScalable.java207
-rw-r--r--org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/transform/ITranslatable.java81
-rw-r--r--org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/utils/CurveUtils.java263
-rw-r--r--org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/utils/PointListUtils.java126
-rw-r--r--org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/utils/PolynomCalculationUtils.java4
62 files changed, 6967 insertions, 1589 deletions
diff --git a/org.eclipse.gef4.geometry.doc/javadocOptions.txt b/org.eclipse.gef4.geometry.doc/javadocOptions.txt
index 7f77240..7118fc9 100644
--- a/org.eclipse.gef4.geometry.doc/javadocOptions.txt
+++ b/org.eclipse.gef4.geometry.doc/javadocOptions.txt
@@ -26,4 +26,4 @@ org.eclipse.gef4.geometry.euclidean
org.eclipse.gef4.geometry.planar
org.eclipse.gef4.geometry.projective
org.eclipse.gef4.geometry.transform
-org.eclipse.gef4.geometry.utils \ No newline at end of file
+org.eclipse.gef4.geometry.utils
diff --git a/org.eclipse.gef4.geometry.doc/reference/image_src/IGeometry_Planar_Abstractions.PNG b/org.eclipse.gef4.geometry.doc/reference/image_src/IGeometry_Planar_Abstractions.PNG
deleted file mode 100644
index 33e933d..0000000
--- a/org.eclipse.gef4.geometry.doc/reference/image_src/IGeometry_Planar_Abstractions.PNG
+++ /dev/null
Binary files differ
diff --git a/org.eclipse.gef4.geometry.doc/reference/image_src/IGeometry_Planar_Abstractions.ucls b/org.eclipse.gef4.geometry.doc/reference/image_src/IGeometry_Planar_Abstractions.ucls
index 39fa299..ac6f3a7 100644
--- a/org.eclipse.gef4.geometry.doc/reference/image_src/IGeometry_Planar_Abstractions.ucls
+++ b/org.eclipse.gef4.geometry.doc/reference/image_src/IGeometry_Planar_Abstractions.ucls
@@ -1,5 +1,5 @@
-<class-diagram version="1.0.7" icons="true" automaticImage="PNG" always-add-relationships="false" generalizations="true"
- realizations="true" associations="true" dependencies="false" nesting-relationships="true">
+<class-diagram version="1.0.7" icons="true" always-add-relationships="false" generalizations="true" realizations="true"
+ associations="true" dependencies="false" nesting-relationships="true">
<interface id="1" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.planar.IGeometry"
project="org.eclipse.gef4.geometry"
file="/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/IGeometry.java" binary="false">
@@ -55,24 +55,24 @@
</display>
</class>
<generalization id="7">
- <end type="SOURCE" refId="5"/>
+ <end type="SOURCE" refId="4"/>
<end type="TARGET" refId="1"/>
</generalization>
- <realization id="8">
+ <generalization id="8">
+ <end type="SOURCE" refId="3"/>
+ <end type="TARGET" refId="2"/>
+ </generalization>
+ <realization id="9">
<end type="SOURCE" refId="6"/>
<end type="TARGET" refId="1"/>
</realization>
- <generalization id="9">
- <end type="SOURCE" refId="4"/>
- <end type="TARGET" refId="1"/>
- </generalization>
<generalization id="10">
<end type="SOURCE" refId="2"/>
<end type="TARGET" refId="1"/>
</generalization>
<generalization id="11">
- <end type="SOURCE" refId="3"/>
- <end type="TARGET" refId="2"/>
+ <end type="SOURCE" refId="5"/>
+ <end type="TARGET" refId="1"/>
</generalization>
<classifier-display autosize="true" package="true" initial-value="false" signature="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true"/>
diff --git a/org.eclipse.gef4.geometry.doc/reference/image_src/IGeometry_Planar_Overview.PNG b/org.eclipse.gef4.geometry.doc/reference/image_src/IGeometry_Planar_Overview.PNG
deleted file mode 100644
index 089d141..0000000
--- a/org.eclipse.gef4.geometry.doc/reference/image_src/IGeometry_Planar_Overview.PNG
+++ /dev/null
Binary files differ
diff --git a/org.eclipse.gef4.geometry.doc/reference/image_src/IGeometry_Planar_Overview.ucls b/org.eclipse.gef4.geometry.doc/reference/image_src/IGeometry_Planar_Overview.ucls
index 9f4e067..caae498 100644
--- a/org.eclipse.gef4.geometry.doc/reference/image_src/IGeometry_Planar_Overview.ucls
+++ b/org.eclipse.gef4.geometry.doc/reference/image_src/IGeometry_Planar_Overview.ucls
@@ -1,5 +1,5 @@
-<class-diagram version="1.0.7" icons="true" automaticImage="PNG" always-add-relationships="false" generalizations="true"
- realizations="true" associations="false" dependencies="false" nesting-relationships="false">
+<class-diagram version="1.0.7" icons="true" always-add-relationships="false" generalizations="true" realizations="true"
+ associations="false" dependencies="false" nesting-relationships="false">
<interface id="1" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.planar.IGeometry"
project="org.eclipse.gef4.geometry"
file="/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/IGeometry.java" binary="false">
@@ -21,7 +21,7 @@
<interface id="3" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.planar.IPolyShape"
project="org.eclipse.gef4.geometry"
file="/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/IPolyShape.java" binary="false">
- <position height="-1" width="-1" x="932" y="403"/>
+ <position height="-1" width="-1" x="930" y="344"/>
<display autosize="true" package="true" initial-value="false" signature="true" visibility="true">
<attributes public="false" package="false" protected="false" private="false"/>
<operations public="false" package="false" protected="false" private="false"/>
@@ -72,16 +72,7 @@
<operations public="false" package="false" protected="false" private="false"/>
</display>
</class>
- <class id="9" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.planar.Ring"
- project="org.eclipse.gef4.geometry" file="/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Ring.java"
- binary="false">
- <position height="-1" width="-1" x="1132" y="366"/>
- <display autosize="true" package="true" initial-value="false" signature="true" visibility="true">
- <attributes public="false" package="false" protected="false" private="false"/>
- <operations public="false" package="false" protected="false" private="false"/>
- </display>
- </class>
- <class id="10" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.planar.RoundedRectangle"
+ <class id="9" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.planar.RoundedRectangle"
project="org.eclipse.gef4.geometry"
file="/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/RoundedRectangle.java" binary="false">
<position height="-1" width="-1" x="1139" y="281"/>
@@ -90,7 +81,7 @@
<operations public="false" package="false" protected="false" private="false"/>
</display>
</class>
- <class id="11" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.planar.QuadraticCurve"
+ <class id="10" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.planar.QuadraticCurve"
project="org.eclipse.gef4.geometry"
file="/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/QuadraticCurve.java" binary="false">
<position height="-1" width="-1" x="91" y="120"/>
@@ -99,7 +90,7 @@
<operations public="false" package="false" protected="false" private="false"/>
</display>
</class>
- <class id="12" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.planar.Polyline"
+ <class id="11" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.planar.Polyline"
project="org.eclipse.gef4.geometry"
file="/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Polyline.java" binary="false">
<position height="-1" width="-1" x="313" y="296"/>
@@ -108,7 +99,7 @@
<operations public="false" package="false" protected="false" private="false"/>
</display>
</class>
- <class id="13" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.planar.Polygon"
+ <class id="12" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.planar.Polygon"
project="org.eclipse.gef4.geometry"
file="/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Polygon.java" binary="false">
<position height="-1" width="-1" x="1138" y="228"/>
@@ -117,7 +108,7 @@
<operations public="false" package="false" protected="false" private="false"/>
</display>
</class>
- <class id="14" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.planar.Path"
+ <class id="13" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.planar.Path"
project="org.eclipse.gef4.geometry" file="/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Path.java"
binary="false">
<position height="-1" width="-1" x="723" y="259"/>
@@ -126,7 +117,7 @@
<operations public="false" package="false" protected="false" private="false"/>
</display>
</class>
- <class id="15" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.planar.Line"
+ <class id="14" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.planar.Line"
project="org.eclipse.gef4.geometry" file="/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Line.java"
binary="false">
<position height="-1" width="-1" x="91" y="64"/>
@@ -135,7 +126,7 @@
<operations public="false" package="false" protected="false" private="false"/>
</display>
</class>
- <class id="16" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.planar.CubicCurve"
+ <class id="15" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.planar.CubicCurve"
project="org.eclipse.gef4.geometry"
file="/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/CubicCurve.java" binary="false">
<position height="-1" width="-1" x="91" y="176"/>
@@ -144,7 +135,7 @@
<operations public="false" package="false" protected="false" private="false"/>
</display>
</class>
- <class id="17" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.planar.BezierSpline"
+ <class id="16" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.planar.BezierSpline"
project="org.eclipse.gef4.geometry"
file="/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/BezierSpline.java" binary="false">
<position height="-1" width="-1" x="314" y="173"/>
@@ -153,7 +144,7 @@
<operations public="false" package="false" protected="false" private="false"/>
</display>
</class>
- <class id="18" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.planar.BezierCurve"
+ <class id="17" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.planar.BezierCurve"
project="org.eclipse.gef4.geometry"
file="/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/BezierCurve.java" binary="false">
<position height="-1" width="-1" x="315" y="119"/>
@@ -162,7 +153,7 @@
<operations public="false" package="false" protected="false" private="false"/>
</display>
</class>
- <class id="19" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.planar.Arc"
+ <class id="18" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.planar.Arc"
project="org.eclipse.gef4.geometry" file="/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Arc.java"
binary="false">
<position height="-1" width="-1" x="314" y="227"/>
@@ -171,7 +162,7 @@
<operations public="false" package="false" protected="false" private="false"/>
</display>
</class>
- <class id="20" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.planar.Pie"
+ <class id="19" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.planar.Pie"
project="org.eclipse.gef4.geometry" file="/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Pie.java"
binary="false">
<position height="-1" width="-1" x="1138" y="176"/>
@@ -180,7 +171,7 @@
<operations public="false" package="false" protected="false" private="false"/>
</display>
</class>
- <class id="21" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.planar.PolyBezier"
+ <class id="20" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.planar.PolyBezier"
project="org.eclipse.gef4.geometry"
file="/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/PolyBezier.java" binary="false">
<position height="-1" width="-1" x="314" y="351"/>
@@ -189,86 +180,108 @@
<operations public="false" package="false" protected="false" private="false"/>
</display>
</class>
- <realization id="22">
- <end type="SOURCE" refId="6"/>
- <end type="TARGET" refId="4"/>
- </realization>
- <realization id="23">
+ <class id="21" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.planar.Ring"
+ project="org.eclipse.gef4.geometry" file="/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Ring.java"
+ binary="false">
+ <position height="-1" width="-1" x="1132" y="370"/>
+ <display autosize="true" package="true" initial-value="false" signature="true" visibility="true">
+ <attributes public="false" package="false" protected="false" private="false"/>
+ <operations public="false" package="false" protected="false" private="false"/>
+ </display>
+ </class>
+ <class id="22" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.planar.AbstractPolyShape"
+ project="org.eclipse.gef4.geometry"
+ file="/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/AbstractPolyShape.java" binary="false">
+ <position height="-1" width="-1" x="930" y="410"/>
+ <display autosize="true" package="true" initial-value="false" signature="true" visibility="true">
+ <attributes public="false" package="false" protected="false" private="false"/>
+ <operations public="false" package="false" protected="false" private="false"/>
+ </display>
+ </class>
+ <generalization id="23">
<end type="SOURCE" refId="8"/>
- <end type="TARGET" refId="3"/>
- </realization>
+ <end type="TARGET" refId="22"/>
+ </generalization>
<realization id="24">
- <end type="SOURCE" refId="21"/>
- <end type="TARGET" refId="2"/>
- </realization>
- <generalization id="25">
- <end type="SOURCE" refId="2"/>
+ <end type="SOURCE" refId="17"/>
<end type="TARGET" refId="5"/>
- </generalization>
- <realization id="26">
- <end type="SOURCE" refId="7"/>
- <end type="TARGET" refId="4"/>
</realization>
- <realization id="27">
- <end type="SOURCE" refId="10"/>
- <end type="TARGET" refId="4"/>
+ <realization id="25">
+ <end type="SOURCE" refId="13"/>
+ <end type="TARGET" refId="1"/>
</realization>
- <generalization id="28">
+ <generalization id="26">
<end type="SOURCE" refId="5"/>
<end type="TARGET" refId="1"/>
</generalization>
- <realization id="29">
- <end type="SOURCE" refId="13"/>
+ <realization id="27">
+ <end type="SOURCE" refId="9"/>
<end type="TARGET" refId="4"/>
</realization>
+ <realization id="28">
+ <end type="SOURCE" refId="7"/>
+ <end type="TARGET" refId="4"/>
+ </realization>
+ <generalization id="29">
+ <end type="SOURCE" refId="14"/>
+ <end type="TARGET" refId="17"/>
+ </generalization>
<realization id="30">
- <end type="SOURCE" refId="9"/>
- <end type="TARGET" refId="3"/>
+ <end type="SOURCE" refId="11"/>
+ <end type="TARGET" refId="2"/>
</realization>
<generalization id="31">
- <end type="SOURCE" refId="16"/>
- <end type="TARGET" refId="18"/>
+ <end type="SOURCE" refId="4"/>
+ <end type="TARGET" refId="1"/>
</generalization>
<realization id="32">
- <end type="SOURCE" refId="17"/>
+ <end type="SOURCE" refId="16"/>
<end type="TARGET" refId="5"/>
</realization>
- <generalization id="33">
- <end type="SOURCE" refId="4"/>
- <end type="TARGET" refId="1"/>
- </generalization>
- <generalization id="34">
- <end type="SOURCE" refId="15"/>
- <end type="TARGET" refId="18"/>
- </generalization>
+ <realization id="33">
+ <end type="SOURCE" refId="19"/>
+ <end type="TARGET" refId="4"/>
+ </realization>
+ <realization id="34">
+ <end type="SOURCE" refId="6"/>
+ <end type="TARGET" refId="4"/>
+ </realization>
<generalization id="35">
- <end type="SOURCE" refId="11"/>
- <end type="TARGET" refId="18"/>
+ <end type="SOURCE" refId="21"/>
+ <end type="TARGET" refId="22"/>
</generalization>
<generalization id="36">
- <end type="SOURCE" refId="3"/>
- <end type="TARGET" refId="1"/>
+ <end type="SOURCE" refId="10"/>
+ <end type="TARGET" refId="17"/>
</generalization>
<realization id="37">
- <end type="SOURCE" refId="20"/>
- <end type="TARGET" refId="4"/>
+ <end type="SOURCE" refId="18"/>
+ <end type="TARGET" refId="5"/>
</realization>
- <realization id="38">
- <end type="SOURCE" refId="14"/>
+ <generalization id="38">
+ <end type="SOURCE" refId="3"/>
<end type="TARGET" refId="1"/>
- </realization>
+ </generalization>
<realization id="39">
- <end type="SOURCE" refId="19"/>
- <end type="TARGET" refId="5"/>
+ <end type="SOURCE" refId="22"/>
+ <end type="TARGET" refId="3"/>
</realization>
- <realization id="40">
- <end type="SOURCE" refId="12"/>
+ <generalization id="40">
+ <end type="SOURCE" refId="2"/>
+ <end type="TARGET" refId="5"/>
+ </generalization>
+ <realization id="41">
+ <end type="SOURCE" refId="20"/>
<end type="TARGET" refId="2"/>
</realization>
- <realization id="41">
- <end type="SOURCE" refId="18"/>
- <end type="TARGET" refId="5"/>
+ <realization id="42">
+ <end type="SOURCE" refId="12"/>
+ <end type="TARGET" refId="4"/>
</realization>
+ <generalization id="43">
+ <end type="SOURCE" refId="15"/>
+ <end type="TARGET" refId="17"/>
+ </generalization>
<classifier-display autosize="true" package="true" initial-value="false" signature="true" visibility="true">
<attributes public="false" package="false" protected="false" private="false"/>
<operations public="false" package="false" protected="false" private="false"/>
diff --git a/org.eclipse.gef4.geometry.doc/reference/image_src/inheritance-hierarchy.ucls b/org.eclipse.gef4.geometry.doc/reference/image_src/inheritance-hierarchy.ucls
new file mode 100644
index 0000000..13f62da
--- /dev/null
+++ b/org.eclipse.gef4.geometry.doc/reference/image_src/inheritance-hierarchy.ucls
@@ -0,0 +1,283 @@
+<class-diagram version="1.0.7" icons="true" always-add-relationships="false" generalizations="true" realizations="true"
+ associations="true" dependencies="false" nesting-relationships="true">
+ <class id="1" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.planar.Region"
+ project="org.eclipse.gef4.geometry"
+ file="/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Region.java" binary="false">
+ <position height="-1" width="-1" x="845" y="210"/>
+ <display autosize="true" package="false" initial-value="false" signature="true" visibility="true">
+ <attributes public="false" package="false" protected="false" private="false"/>
+ <operations public="false" package="false" protected="false" private="false"/>
+ </display>
+ </class>
+ <class id="2" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.planar.PolyBezier"
+ project="org.eclipse.gef4.geometry"
+ file="/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/PolyBezier.java" binary="false">
+ <position height="-1" width="-1" x="226" y="14"/>
+ <display autosize="true" package="false" initial-value="false" signature="true" visibility="true">
+ <attributes public="false" package="false" protected="false" private="false"/>
+ <operations public="false" package="false" protected="false" private="false"/>
+ </display>
+ </class>
+ <class id="3" corner="BOTTOM_RIGHT" language="java"
+ name="org.eclipse.gef4.geometry.planar.AbstractRectangleBasedGeometry" project="org.eclipse.gef4.geometry"
+ file="/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/AbstractRectangleBasedGeometry.java"
+ binary="false">
+ <position height="-1" width="-1" x="294" y="125"/>
+ <display autosize="true" package="false" initial-value="false" signature="true" visibility="true">
+ <attributes public="false" package="false" protected="false" private="false"/>
+ <operations public="false" package="false" protected="false" private="false"/>
+ </display>
+ </class>
+ <class id="4" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.planar.QuadraticCurve"
+ project="org.eclipse.gef4.geometry"
+ file="/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/QuadraticCurve.java" binary="false">
+ <position height="-1" width="-1" x="697" y="164"/>
+ <display autosize="true" package="false" initial-value="false" signature="true" visibility="true">
+ <attributes public="false" package="false" protected="false" private="false"/>
+ <operations public="false" package="false" protected="false" private="false"/>
+ </display>
+ </class>
+ <class id="5" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.planar.BezierCurve"
+ project="org.eclipse.gef4.geometry"
+ file="/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/BezierCurve.java" binary="false">
+ <position height="-1" width="-1" x="561" y="126"/>
+ <display autosize="true" package="false" initial-value="false" signature="true" visibility="true">
+ <attributes public="false" package="false" protected="false" private="false"/>
+ <operations public="false" package="false" protected="false" private="false"/>
+ </display>
+ </class>
+ <class id="6" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.planar.AbstractGeometry"
+ project="org.eclipse.gef4.geometry"
+ file="/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/AbstractGeometry.java" binary="false">
+ <position height="-1" width="-1" x="474" y="25"/>
+ <display autosize="true" package="false" initial-value="false" signature="true" visibility="true">
+ <attributes public="false" package="false" protected="false" private="false"/>
+ <operations public="false" package="false" protected="false" private="false"/>
+ </display>
+ </class>
+ <class id="7" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.planar.RoundedRectangle"
+ project="org.eclipse.gef4.geometry"
+ file="/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/RoundedRectangle.java" binary="false">
+ <position height="-1" width="-1" x="68" y="163"/>
+ <display autosize="true" package="false" initial-value="false" signature="true" visibility="true">
+ <attributes public="false" package="false" protected="false" private="false"/>
+ <operations public="false" package="false" protected="false" private="false"/>
+ </display>
+ </class>
+ <class id="8" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.planar.Rectangle"
+ project="org.eclipse.gef4.geometry"
+ file="/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Rectangle.java" binary="false">
+ <position height="-1" width="-1" x="93" y="86"/>
+ <display autosize="true" package="false" initial-value="false" signature="true" visibility="true">
+ <attributes public="false" package="false" protected="false" private="false"/>
+ <operations public="false" package="false" protected="false" private="false"/>
+ </display>
+ </class>
+ <class id="9" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.planar.Ring"
+ project="org.eclipse.gef4.geometry" file="/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Ring.java"
+ binary="false">
+ <position height="-1" width="-1" x="932" y="210"/>
+ <display autosize="true" package="false" initial-value="false" signature="true" visibility="true">
+ <attributes public="false" package="false" protected="false" private="false"/>
+ <operations public="false" package="false" protected="false" private="false"/>
+ </display>
+ </class>
+ <class id="10" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.planar.AbstractArcBasedGeometry"
+ project="org.eclipse.gef4.geometry"
+ file="/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/AbstractArcBasedGeometry.java" binary="false">
+ <position height="-1" width="-1" x="269" y="215"/>
+ <display autosize="true" package="false" initial-value="false" signature="true" visibility="true">
+ <attributes public="false" package="false" protected="false" private="false"/>
+ <operations public="false" package="false" protected="false" private="false"/>
+ </display>
+ </class>
+ <class id="11" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.planar.Arc"
+ project="org.eclipse.gef4.geometry" file="/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Arc.java"
+ binary="false">
+ <position height="-1" width="-1" x="315" y="273"/>
+ <display autosize="true" package="false" initial-value="false" signature="true" visibility="true">
+ <attributes public="false" package="false" protected="false" private="false"/>
+ <operations public="false" package="false" protected="false" private="false"/>
+ </display>
+ </class>
+ <class id="12" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.planar.CubicCurve"
+ project="org.eclipse.gef4.geometry"
+ file="/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/CubicCurve.java" binary="false">
+ <position height="32" width="91" x="639" y="189"/>
+ <display autosize="false" package="false" initial-value="false" signature="true" visibility="true">
+ <attributes public="false" package="false" protected="false" private="false"/>
+ <operations public="false" package="false" protected="false" private="false"/>
+ </display>
+ </class>
+ <class id="13" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.planar.Ellipse"
+ project="org.eclipse.gef4.geometry"
+ file="/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Ellipse.java" binary="false">
+ <position height="-1" width="-1" x="93" y="124"/>
+ <display autosize="true" package="false" initial-value="false" signature="true" visibility="true">
+ <attributes public="false" package="false" protected="false" private="false"/>
+ <operations public="false" package="false" protected="false" private="false"/>
+ </display>
+ </class>
+ <class id="14" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.planar.Path"
+ project="org.eclipse.gef4.geometry" file="/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Path.java"
+ binary="false">
+ <position height="-1" width="-1" x="474" y="201"/>
+ <display autosize="true" package="false" initial-value="false" signature="true" visibility="true">
+ <attributes public="false" package="false" protected="false" private="false"/>
+ <operations public="false" package="false" protected="false" private="false"/>
+ </display>
+ </class>
+ <class id="15" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.planar.Pie"
+ project="org.eclipse.gef4.geometry" file="/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Pie.java"
+ binary="false">
+ <position height="-1" width="-1" x="201" y="276"/>
+ <display autosize="true" package="false" initial-value="false" signature="true" visibility="true">
+ <attributes public="false" package="false" protected="false" private="false"/>
+ <operations public="false" package="false" protected="false" private="false"/>
+ </display>
+ </class>
+ <class id="16" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.planar.BezierSpline"
+ project="org.eclipse.gef4.geometry"
+ file="/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/BezierSpline.java" binary="false">
+ <position height="-1" width="-1" x="223" y="54"/>
+ <display autosize="true" package="false" initial-value="false" signature="true" visibility="true">
+ <attributes public="false" package="false" protected="false" private="false"/>
+ <operations public="false" package="false" protected="false" private="false"/>
+ </display>
+ </class>
+ <class id="17" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.planar.Polyline"
+ project="org.eclipse.gef4.geometry"
+ file="/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Polyline.java" binary="false">
+ <position height="-1" width="-1" x="819" y="89"/>
+ <display autosize="true" package="false" initial-value="false" signature="true" visibility="true">
+ <attributes public="false" package="false" protected="false" private="false"/>
+ <operations public="false" package="false" protected="false" private="false"/>
+ </display>
+ </class>
+ <class id="18" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.planar.Polygon"
+ project="org.eclipse.gef4.geometry"
+ file="/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Polygon.java" binary="false">
+ <position height="-1" width="-1" x="905" y="89"/>
+ <display autosize="true" package="false" initial-value="false" signature="true" visibility="true">
+ <attributes public="false" package="false" protected="false" private="false"/>
+ <operations public="false" package="false" protected="false" private="false"/>
+ </display>
+ </class>
+ <class id="19" corner="BOTTOM_RIGHT" language="java"
+ name="org.eclipse.gef4.geometry.planar.AbstractPointListBasedGeometry" project="org.eclipse.gef4.geometry"
+ file="/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/AbstractPointListBasedGeometry.java"
+ binary="false">
+ <position height="-1" width="-1" x="849" y="25"/>
+ <display autosize="true" package="false" initial-value="false" signature="true" visibility="true">
+ <attributes public="false" package="false" protected="false" private="false"/>
+ <operations public="false" package="false" protected="false" private="false"/>
+ </display>
+ </class>
+ <class id="20" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.planar.AbstractPolyShape"
+ project="org.eclipse.gef4.geometry"
+ file="/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/AbstractPolyShape.java" binary="false">
+ <position height="-1" width="-1" x="889" y="149"/>
+ <display autosize="true" package="false" initial-value="false" signature="true" visibility="true">
+ <attributes public="false" package="false" protected="false" private="false"/>
+ <operations public="false" package="false" protected="false" private="false"/>
+ </display>
+ </class>
+ <class id="21" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.planar.Line"
+ project="org.eclipse.gef4.geometry" file="/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Line.java"
+ binary="false">
+ <position height="-1" width="-1" x="681" y="123"/>
+ <display autosize="true" package="false" initial-value="false" signature="true" visibility="true">
+ <attributes public="false" package="false" protected="false" private="false"/>
+ <operations public="false" package="false" protected="false" private="false"/>
+ </display>
+ </class>
+ <generalization id="22">
+ <end type="SOURCE" refId="14"/>
+ <end type="TARGET" refId="6"/>
+ </generalization>
+ <generalization id="23">
+ <end type="SOURCE" refId="2"/>
+ <end type="TARGET" refId="6"/>
+ </generalization>
+ <generalization id="24">
+ <end type="SOURCE" refId="5"/>
+ <end type="TARGET" refId="6"/>
+ </generalization>
+ <generalization id="25">
+ <bendpoint x="622" y="206"/>
+ <end type="SOURCE" refId="12"/>
+ <end type="TARGET" refId="5"/>
+ </generalization>
+ <generalization id="26">
+ <bendpoint x="616" y="163"/>
+ <end type="SOURCE" refId="4"/>
+ <end type="TARGET" refId="5"/>
+ </generalization>
+ <generalization id="27">
+ <end type="SOURCE" refId="11"/>
+ <end type="TARGET" refId="10"/>
+ </generalization>
+ <generalization id="28">
+ <end type="SOURCE" refId="1"/>
+ <end type="TARGET" refId="20"/>
+ </generalization>
+ <generalization id="29">
+ <end type="SOURCE" refId="17"/>
+ <end type="TARGET" refId="19"/>
+ </generalization>
+ <generalization id="30">
+ <end type="SOURCE" refId="9"/>
+ <end type="TARGET" refId="20"/>
+ </generalization>
+ <generalization id="31">
+ <end type="SOURCE" refId="21"/>
+ <end type="TARGET" refId="5"/>
+ </generalization>
+ <generalization id="32">
+ <end type="SOURCE" refId="18"/>
+ <end type="TARGET" refId="19"/>
+ </generalization>
+ <generalization id="33">
+ <end type="SOURCE" refId="3"/>
+ <end type="TARGET" refId="6"/>
+ </generalization>
+ <generalization id="34">
+ <end type="SOURCE" refId="16"/>
+ <end type="TARGET" refId="6"/>
+ </generalization>
+ <generalization id="35">
+ <end type="SOURCE" refId="10"/>
+ <end type="TARGET" refId="3"/>
+ </generalization>
+ <generalization id="36">
+ <end type="SOURCE" refId="13"/>
+ <end type="TARGET" refId="3"/>
+ </generalization>
+ <generalization id="37">
+ <end type="SOURCE" refId="15"/>
+ <end type="TARGET" refId="10"/>
+ </generalization>
+ <generalization id="38">
+ <end type="SOURCE" refId="20"/>
+ <end type="TARGET" refId="6"/>
+ </generalization>
+ <generalization id="39">
+ <bendpoint x="174" y="163"/>
+ <end type="SOURCE" refId="7"/>
+ <end type="TARGET" refId="3"/>
+ </generalization>
+ <generalization id="40">
+ <end type="SOURCE" refId="19"/>
+ <end type="TARGET" refId="6"/>
+ </generalization>
+ <generalization id="41">
+ <bendpoint x="180" y="86"/>
+ <end type="SOURCE" refId="8"/>
+ <end type="TARGET" refId="3"/>
+ </generalization>
+ <classifier-display autosize="true" package="false" initial-value="false" signature="true" visibility="true">
+ <attributes public="false" package="false" protected="false" private="false"/>
+ <operations public="false" package="false" protected="false" private="false"/>
+ </classifier-display>
+ <association-display labels="true" multiplicity="true"/>
+</class-diagram> \ No newline at end of file
diff --git a/org.eclipse.gef4.geometry.doc/reference/image_src/transform-overview.ucls b/org.eclipse.gef4.geometry.doc/reference/image_src/transform-overview.ucls
new file mode 100644
index 0000000..dded353
--- /dev/null
+++ b/org.eclipse.gef4.geometry.doc/reference/image_src/transform-overview.ucls
@@ -0,0 +1,44 @@
+<class-diagram version="1.0.7" icons="true" always-add-relationships="false" generalizations="true" realizations="true"
+ associations="true" dependencies="false" nesting-relationships="true">
+ <class id="1" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.transform.AffineTransform"
+ project="org.eclipse.gef4.geometry"
+ file="/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/transform/AffineTransform.java" binary="false">
+ <position height="-1" width="-1" x="169" y="127"/>
+ <display autosize="true" package="true" initial-value="false" signature="true" visibility="true">
+ <attributes public="true" package="false" protected="false" private="false"/>
+ <operations public="true" package="false" protected="false" private="false"/>
+ </display>
+ </class>
+ <interface id="2" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.transform.IRotatable"
+ project="org.eclipse.gef4.geometry"
+ file="/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/transform/IRotatable.java" binary="false">
+ <position height="-1" width="-1" x="448" y="-207"/>
+ <display autosize="true" package="true" initial-value="false" signature="true" visibility="true">
+ <attributes public="true" package="false" protected="false" private="false"/>
+ <operations public="true" package="false" protected="false" private="false"/>
+ </display>
+ </interface>
+ <interface id="3" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.transform.IScalable"
+ project="org.eclipse.gef4.geometry"
+ file="/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/transform/IScalable.java" binary="false">
+ <position height="-1" width="-1" x="671" y="-160"/>
+ <display autosize="true" package="true" initial-value="false" signature="true" visibility="true">
+ <attributes public="true" package="false" protected="false" private="false"/>
+ <operations public="true" package="false" protected="false" private="false"/>
+ </display>
+ </interface>
+ <interface id="4" corner="BOTTOM_RIGHT" language="java" name="org.eclipse.gef4.geometry.transform.ITranslatable"
+ project="org.eclipse.gef4.geometry"
+ file="/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/transform/ITranslatable.java" binary="false">
+ <position height="-1" width="-1" x="874" y="-225"/>
+ <display autosize="true" package="true" initial-value="false" signature="true" visibility="true">
+ <attributes public="true" package="false" protected="false" private="false"/>
+ <operations public="true" package="false" protected="false" private="false"/>
+ </display>
+ </interface>
+ <classifier-display autosize="true" package="true" initial-value="false" signature="true" visibility="true">
+ <attributes public="true" package="false" protected="false" private="false"/>
+ <operations public="true" package="false" protected="false" private="false"/>
+ </classifier-display>
+ <association-display labels="true" multiplicity="true"/>
+</class-diagram> \ No newline at end of file
diff --git a/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/intersection/BezierApproximationExample.java b/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/demos/BezierApproximationExample.java
index 2bf2bb9..c0beca9 100644
--- a/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/intersection/BezierApproximationExample.java
+++ b/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/demos/BezierApproximationExample.java
@@ -9,9 +9,10 @@
* Matthias Wienand (itemis AG) - initial API and implementation
*
*******************************************************************************/
-package org.eclipse.gef4.geometry.examples.intersection;
+package org.eclipse.gef4.geometry.examples.demos;
import org.eclipse.gef4.geometry.Point;
+import org.eclipse.gef4.geometry.examples.intersection.AbstractIntersectionExample;
import org.eclipse.gef4.geometry.planar.BezierCurve;
import org.eclipse.gef4.geometry.planar.IGeometry;
import org.eclipse.gef4.geometry.planar.Line;
@@ -33,13 +34,13 @@ public class BezierApproximationExample extends AbstractIntersectionExample {
return new AbstractControllableShape(canvas) {
@Override
public void createControlPoints() {
- addControlPoint(new Point(100, 100));
- addControlPoint(new Point(150, 400));
- addControlPoint(new Point(200, 300));
- addControlPoint(new Point(250, 150));
- addControlPoint(new Point(300, 250));
- addControlPoint(new Point(350, 200));
- addControlPoint(new Point(400, 350));
+ addControlPoint(new Point(100, 200));
+ addControlPoint(new Point(150, 250));
+ addControlPoint(new Point(200, 150));
+ addControlPoint(new Point(250, 250));
+ addControlPoint(new Point(300, 150));
+ addControlPoint(new Point(350, 250));
+ addControlPoint(new Point(400, 200));
}
@Override
diff --git a/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/intersection/ConvexHullExample.java b/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/demos/ConvexHullExample.java
index 4e317f6..67955f7 100644
--- a/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/intersection/ConvexHullExample.java
+++ b/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/demos/ConvexHullExample.java
@@ -9,9 +9,11 @@
* Matthias Wienand (itemis AG) - initial API and implementation
*
*******************************************************************************/
-package org.eclipse.gef4.geometry.examples.intersection;
+package org.eclipse.gef4.geometry.examples.demos;
import org.eclipse.gef4.geometry.Point;
+import org.eclipse.gef4.geometry.examples.intersection.AbstractIntersectionExample;
+import org.eclipse.gef4.geometry.examples.intersection.AbstractIntersectionExample.AbstractControllableShape;
import org.eclipse.gef4.geometry.planar.IGeometry;
import org.eclipse.gef4.geometry.planar.Line;
import org.eclipse.gef4.geometry.planar.Polygon;
diff --git a/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/intersection/CubicCurveDeCasteljauExample.java b/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/demos/CubicCurveDeCasteljauExample.java
index 380f1b2..aa9eda0 100644
--- a/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/intersection/CubicCurveDeCasteljauExample.java
+++ b/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/demos/CubicCurveDeCasteljauExample.java
@@ -9,9 +9,11 @@
* Matthias Wienand (itemis AG) - initial API and implementation
*
*******************************************************************************/
-package org.eclipse.gef4.geometry.examples.intersection;
+package org.eclipse.gef4.geometry.examples.demos;
import org.eclipse.gef4.geometry.Point;
+import org.eclipse.gef4.geometry.examples.intersection.AbstractIntersectionExample;
+import org.eclipse.gef4.geometry.examples.intersection.AbstractIntersectionExample.AbstractControllableShape;
import org.eclipse.gef4.geometry.planar.CubicCurve;
import org.eclipse.gef4.geometry.planar.IGeometry;
import org.eclipse.gef4.geometry.planar.Line;
diff --git a/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/demos/RegionExample.java b/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/demos/RegionExample.java
new file mode 100644
index 0000000..a8a9795
--- /dev/null
+++ b/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/demos/RegionExample.java
@@ -0,0 +1,100 @@
+/*******************************************************************************
+ * Copyright (c) 2011 itemis AG and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Matthias Wienand (itemis AG) - initial API and implementation
+ *
+ *******************************************************************************/
+package org.eclipse.gef4.geometry.examples.demos;
+
+import org.eclipse.gef4.geometry.Point;
+import org.eclipse.gef4.geometry.examples.intersection.AbstractIntersectionExample;
+import org.eclipse.gef4.geometry.examples.intersection.AbstractIntersectionExample.AbstractControllableShape;
+import org.eclipse.gef4.geometry.planar.IGeometry;
+import org.eclipse.gef4.geometry.planar.Line;
+import org.eclipse.gef4.geometry.planar.Rectangle;
+import org.eclipse.gef4.geometry.planar.Region;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Display;
+
+public class RegionExample extends AbstractIntersectionExample {
+ public static void main(String[] args) {
+ new RegionExample("Region Example");
+ }
+
+ public RegionExample(String title) {
+ super(title);
+ }
+
+ protected AbstractControllableShape createControllableShape1(Canvas canvas) {
+ return new AbstractControllableShape(canvas) {
+ @Override
+ public void createControlPoints() {
+ addControlPoint(new Point(100, 100));
+ addControlPoint(new Point(200, 200));
+
+ addControlPoint(new Point(150, 150));
+ addControlPoint(new Point(250, 250));
+ }
+
+ @Override
+ public Region createGeometry() {
+ Point[] cp = getControlPoints();
+ Region region = new Region(new Rectangle(cp[0], cp[1]),
+ new Rectangle(cp[2], cp[3]));
+ return region;
+ }
+
+ @Override
+ public void drawShape(GC gc) {
+ Region region = createGeometry();
+
+ gc.setClipping(region.toSWTRegion());
+
+ for (int y = 0; y < 800; y += 20) {
+ gc.drawString(
+ "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz",
+ 20, y);
+ }
+
+ gc.setClipping((org.eclipse.swt.graphics.Region) null);
+
+ gc.setAlpha(128);
+ gc.setBackground(Display.getCurrent().getSystemColor(
+ SWT.COLOR_BLUE));
+ for (Rectangle r : region.getShapes()) {
+ gc.fillRectangle(r.toSWTRectangle());
+ }
+ gc.setAlpha(255);
+ }
+ };
+ }
+
+ protected AbstractControllableShape createControllableShape2(Canvas canvas) {
+ return new AbstractControllableShape(canvas) {
+ @Override
+ public void createControlPoints() {
+ }
+
+ @Override
+ public IGeometry createGeometry() {
+ return new Line(-10, -10, -10, -10);
+ }
+
+ @Override
+ public void drawShape(GC gc) {
+ }
+ };
+ }
+
+ @Override
+ protected Point[] computeIntersections(IGeometry g1, IGeometry g2) {
+ return new Point[] {};
+ }
+}
diff --git a/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/demos/RegionOutlineExample.java b/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/demos/RegionOutlineExample.java
new file mode 100644
index 0000000..2cc7466
--- /dev/null
+++ b/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/demos/RegionOutlineExample.java
@@ -0,0 +1,113 @@
+/*******************************************************************************
+ * Copyright (c) 2011 itemis AG and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Matthias Wienand (itemis AG) - initial API and implementation
+ *
+ *******************************************************************************/
+package org.eclipse.gef4.geometry.examples.demos;
+
+import org.eclipse.gef4.geometry.Point;
+import org.eclipse.gef4.geometry.examples.intersection.AbstractIntersectionExample;
+import org.eclipse.gef4.geometry.examples.intersection.AbstractIntersectionExample.AbstractControllableShape;
+import org.eclipse.gef4.geometry.planar.IGeometry;
+import org.eclipse.gef4.geometry.planar.Line;
+import org.eclipse.gef4.geometry.planar.Rectangle;
+import org.eclipse.gef4.geometry.planar.Region;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Display;
+
+public class RegionOutlineExample extends AbstractIntersectionExample {
+ public static void main(String[] args) {
+ new RegionOutlineExample("Region Example");
+ }
+
+ public RegionOutlineExample(String title) {
+ super(title);
+ }
+
+ protected AbstractControllableShape createControllableShape1(Canvas canvas) {
+ return new AbstractControllableShape(canvas) {
+ @Override
+ public void createControlPoints() {
+ addControlPoint(new Point(100, 0));
+ addControlPoint(new Point(300, 100));
+
+ addControlPoint(new Point(250, 200));
+ addControlPoint(new Point(350, 330));
+
+ addControlPoint(new Point(100, 200));
+ addControlPoint(new Point(190, 325));
+
+ addControlPoint(new Point(150, 300));
+ addControlPoint(new Point(280, 380));
+ }
+
+ @Override
+ public Region createGeometry() {
+ Point[] cp = getControlPoints();
+ Rectangle[] rectangles = new Rectangle[cp.length / 2];
+ for (int i = 0; i < rectangles.length; i++) {
+ rectangles[i] = new Rectangle(cp[2 * i], cp[2 * i + 1]);
+ // System.out.println("R" + i + " " + cp[2 * i] + "\\"
+ // + cp[2 * i + 1]);
+ }
+ Region region = new Region(rectangles);
+ return region;
+ }
+
+ @Override
+ public void drawShape(GC gc) {
+ Region region = createGeometry();
+
+ gc.setAlpha(128);
+ gc.setBackground(Display.getCurrent().getSystemColor(
+ SWT.COLOR_BLUE));
+ for (Rectangle r : region.getShapes()) {
+ gc.fillRectangle(r.toSWTRectangle());
+ }
+
+ gc.setAlpha(255);
+ gc.setForeground(Display.getCurrent().getSystemColor(
+ SWT.COLOR_RED));
+ for (Rectangle r : region.getShapes()) {
+ gc.drawRectangle(r.toSWTRectangle());
+ }
+ gc.setForeground(Display.getCurrent().getSystemColor(
+ SWT.COLOR_BLACK));
+ for (Line l : region.getOutlineSegments()) {
+ gc.drawLine((int) (l.getX1()), (int) (l.getY1()),
+ (int) (l.getX2()), (int) (l.getY2()));
+ }
+ }
+ };
+ }
+
+ protected AbstractControllableShape createControllableShape2(Canvas canvas) {
+ return new AbstractControllableShape(canvas) {
+ @Override
+ public void createControlPoints() {
+ }
+
+ @Override
+ public IGeometry createGeometry() {
+ return new Line(-10, -10, -10, -10);
+ }
+
+ @Override
+ public void drawShape(GC gc) {
+ }
+ };
+ }
+
+ @Override
+ protected Point[] computeIntersections(IGeometry g1, IGeometry g2) {
+ return new Point[] {};
+ }
+}
diff --git a/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/demos/RingExample.java b/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/demos/RingExample.java
new file mode 100644
index 0000000..fcd180a
--- /dev/null
+++ b/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/demos/RingExample.java
@@ -0,0 +1,128 @@
+/*******************************************************************************
+ * Copyright (c) 2012 itemis AG and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Matthias Wienand (itemis AG) - initial API and implementation
+ *
+ *******************************************************************************/
+package org.eclipse.gef4.geometry.examples.demos;
+
+import org.eclipse.gef4.geometry.Point;
+import org.eclipse.gef4.geometry.examples.intersection.AbstractIntersectionExample;
+import org.eclipse.gef4.geometry.planar.IGeometry;
+import org.eclipse.gef4.geometry.planar.Line;
+import org.eclipse.gef4.geometry.planar.Polygon;
+import org.eclipse.gef4.geometry.planar.Ring;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Display;
+
+public class RingExample extends AbstractIntersectionExample {
+ public static void main(String[] args) {
+ new RingExample("Ring Example");
+ }
+
+ public RingExample(String title) {
+ super(title);
+ }
+
+ protected AbstractControllableShape createControllableShape1(Canvas canvas) {
+ return new AbstractControllableShape(canvas) {
+ @Override
+ public void createControlPoints() {
+ addControlPoint(new Point(100, 100));
+ addControlPoint(new Point(400, 100));
+ addControlPoint(new Point(400, 200));
+
+ addControlPoint(new Point(400, 100));
+ addControlPoint(new Point(400, 400));
+ addControlPoint(new Point(300, 400));
+
+ addControlPoint(new Point(400, 400));
+ addControlPoint(new Point(100, 400));
+ addControlPoint(new Point(100, 300));
+
+ addControlPoint(new Point(100, 400));
+ addControlPoint(new Point(100, 100));
+ addControlPoint(new Point(200, 100));
+ }
+
+ @Override
+ public Ring createGeometry() {
+ Point[] cp = getControlPoints();
+ Polygon[] polygons = new Polygon[cp.length / 3];
+ for (int i = 0; i < polygons.length; i++) {
+ polygons[i] = new Polygon(cp[3 * i], cp[3 * i + 1],
+ cp[3 * i + 2]);
+ // System.out.println("R" + i + " " + cp[2 * i] + "\\"
+ // + cp[2 * i + 1]);
+ }
+ Ring ring = new Ring(polygons);
+ return ring;
+ }
+
+ @Override
+ public void drawShape(GC gc) {
+ Ring ring = createGeometry();
+
+ gc.setAlpha(64);
+ gc.setBackground(Display.getCurrent().getSystemColor(
+ SWT.COLOR_BLUE));
+ for (Polygon p : ring.getShapes()) {
+ gc.fillPolygon(p.toSWTPointArray());
+ }
+
+ gc.setAlpha(255);
+ gc.setForeground(Display.getCurrent().getSystemColor(
+ SWT.COLOR_RED));
+ for (Polygon p : ring.getShapes()) {
+ gc.drawPolygon(p.toSWTPointArray());
+ }
+
+ gc.setForeground(Display.getCurrent().getSystemColor(
+ SWT.COLOR_BLACK));
+ int lineWidth = gc.getLineWidth();
+ gc.setLineWidth(lineWidth + 2);
+ for (Line l : ring.getOutlineSegments()) {
+ gc.drawLine((int) (l.getX1()), (int) (l.getY1()),
+ (int) (l.getX2()), (int) (l.getY2()));
+ }
+ gc.setLineWidth(lineWidth);
+
+ // gc.setForeground(Display.getCurrent().getSystemColor(
+ // SWT.COLOR_BLACK));
+ // for (Line l : region.getOutlineSegments()) {
+ // gc.drawLine((int) (l.getX1()), (int) (l.getY1()),
+ // (int) (l.getX2()), (int) (l.getY2()));
+ // }
+ }
+ };
+ }
+
+ protected AbstractControllableShape createControllableShape2(Canvas canvas) {
+ return new AbstractControllableShape(canvas) {
+ @Override
+ public void createControlPoints() {
+ }
+
+ @Override
+ public IGeometry createGeometry() {
+ return new Line(-10, -10, -10, -10);
+ }
+
+ @Override
+ public void drawShape(GC gc) {
+ }
+ };
+ }
+
+ @Override
+ protected Point[] computeIntersections(IGeometry g1, IGeometry g2) {
+ return new Point[] {};
+ }
+}
diff --git a/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/demos/TriangulationExample.java b/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/demos/TriangulationExample.java
new file mode 100644
index 0000000..fb052a1
--- /dev/null
+++ b/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/demos/TriangulationExample.java
@@ -0,0 +1,109 @@
+/*******************************************************************************
+ * Copyright (c) 2012 itemis AG and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Matthias Wienand (itemis AG) - initial API and implementation
+ *
+ *******************************************************************************/
+package org.eclipse.gef4.geometry.examples.demos;
+
+import org.eclipse.gef4.geometry.Point;
+import org.eclipse.gef4.geometry.examples.intersection.AbstractIntersectionExample;
+import org.eclipse.gef4.geometry.planar.IGeometry;
+import org.eclipse.gef4.geometry.planar.Line;
+import org.eclipse.gef4.geometry.planar.Polygon;
+import org.eclipse.gef4.geometry.planar.Polygon.NonSimplePolygonException;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Display;
+
+public class TriangulationExample extends AbstractIntersectionExample {
+ public static void main(String[] args) {
+ new TriangulationExample("Triangulation Example");
+ }
+
+ public TriangulationExample(String title) {
+ super(title);
+ }
+
+ protected AbstractControllableShape createControllableShape1(Canvas canvas) {
+ return new AbstractControllableShape(canvas) {
+ @Override
+ public void createControlPoints() {
+ addControlPoint(new Point(300 / 2, 100 / 2));
+ addControlPoint(new Point(100 / 2, 200 / 2));
+ addControlPoint(new Point(200 / 2, 300 / 2));
+ addControlPoint(new Point(100 / 2, 500 / 2));
+ addControlPoint(new Point(300 / 2, 400 / 2));
+ addControlPoint(new Point(500 / 2, 600 / 2));
+ addControlPoint(new Point(600 / 2, 300 / 2));
+ addControlPoint(new Point(500 / 2, 400 / 2));
+ addControlPoint(new Point(500 / 2, 200 / 2));
+ addControlPoint(new Point(300 / 2, 200 / 2));
+ }
+
+ @Override
+ public Polygon createGeometry() {
+ Point[] cp = getControlPoints();
+ Polygon p = new Polygon(getControlPoints());
+ return p;
+ }
+
+ @Override
+ public void drawShape(GC gc) {
+ Polygon p = createGeometry();
+
+ // System.out.println("p = " + p);
+
+ gc.setForeground(Display.getCurrent().getSystemColor(
+ SWT.COLOR_RED));
+
+ Polygon[] triangulation;
+ try {
+ triangulation = p.getTriangulation();
+ } catch (NonSimplePolygonException x) {
+ triangulation = new Polygon[] { p };
+ }
+ for (Polygon triangle : triangulation) {
+ gc.drawPolygon(triangle.toSWTPointArray());
+ }
+
+ int lineWidth = gc.getLineWidth();
+ gc.setLineWidth(lineWidth + 2);
+ gc.setForeground(Display.getCurrent().getSystemColor(
+ SWT.COLOR_BLACK));
+
+ gc.drawPolygon(p.toSWTPointArray());
+
+ gc.setLineWidth(lineWidth);
+ }
+ };
+ }
+
+ protected AbstractControllableShape createControllableShape2(Canvas canvas) {
+ return new AbstractControllableShape(canvas) {
+ @Override
+ public void createControlPoints() {
+ }
+
+ @Override
+ public IGeometry createGeometry() {
+ return new Line(-10, -10, -10, -10);
+ }
+
+ @Override
+ public void drawShape(GC gc) {
+ }
+ };
+ }
+
+ @Override
+ protected Point[] computeIntersections(IGeometry g1, IGeometry g2) {
+ return new Point[] {};
+ }
+}
diff --git a/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/intersection/AbstractIntersectionExample.java b/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/intersection/AbstractIntersectionExample.java
index 5137201..777fda7 100644
--- a/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/intersection/AbstractIntersectionExample.java
+++ b/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/intersection/AbstractIntersectionExample.java
@@ -333,11 +333,14 @@ public abstract class AbstractIntersectionExample implements PaintListener {
}
infoLabel.setText(infoText);
+ // open the shell before creating the controllable shapes so that their
+ // default coordinates are not changed due to the resize of their canvas
+ shell.open();
+
controllableShape1 = createControllableShape1(shell);
controllableShape2 = createControllableShape2(shell);
shell.addPaintListener(this);
- shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
diff --git a/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/scalerotate/CubicCurveScaleRotate.java b/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/scalerotate/CubicCurveScaleRotate.java
new file mode 100644
index 0000000..0fa634a
--- /dev/null
+++ b/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/scalerotate/CubicCurveScaleRotate.java
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * Copyright (c) 2011 itemis AG and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Matthias Wienand (itemis AG) - initial API and implementation
+ *
+ *******************************************************************************/
+package org.eclipse.gef4.geometry.examples.scalerotate;
+
+import org.eclipse.gef4.geometry.Point;
+import org.eclipse.gef4.geometry.planar.CubicCurve;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Display;
+
+public class CubicCurveScaleRotate extends AbstractScaleRotateExample {
+
+ public static void main(String[] args) {
+ new CubicCurveScaleRotate();
+ }
+
+ public CubicCurveScaleRotate() {
+ super("Scale/Rotate - CubicCurve");
+ }
+
+ @Override
+ protected AbstractScaleRotateShape createShape(Canvas canvas) {
+ return new AbstractScaleRotateShape(canvas) {
+ @Override
+ public boolean contains(Point p) {
+ return createGeometry().getBounds().contains(p);
+ }
+
+ @Override
+ public CubicCurve createGeometry() {
+ double w = getCanvas().getClientArea().width;
+ double h = getCanvas().getClientArea().height;
+ double padx = w / 10;
+ double pady = h / 10;
+
+ CubicCurve me = new CubicCurve(padx, pady, w + w, h, -w, h, w
+ - padx, pady);
+ me.rotateCW(getRotationAngle(), getCenter());
+ me.scale(getZoomFactor(), getCenter());
+
+ return me;
+ }
+
+ @Override
+ public void draw(GC gc) {
+ CubicCurve me = createGeometry();
+ gc.fillRectangle(me.getBounds().toSWTRectangle());
+ gc.drawPath(new org.eclipse.swt.graphics.Path(Display
+ .getCurrent(), me.toPath().toSWTPathData()));
+ }
+ };
+ }
+}
diff --git a/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/AllTests.java b/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/AllTests.java
index b7abe26..cf7c4de 100644
--- a/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/AllTests.java
+++ b/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/AllTests.java
@@ -17,12 +17,14 @@ import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
@RunWith(Suite.class)
-@SuiteClasses({ AngleTests.class, CubicCurveTests.class, CurveUtilsTests.class,
- DimensionTests.class, EllipseTests.class, LineTests.class,
- PointListUtilsTests.class, PointTests.class, PolygonTests.class,
- PolylineTests.class, PolynomCalculationUtilsTests.class,
- PrecisionUtilsTests.class, QuadraticCurveTests.class,
- RectangleTests.class, StraightTests.class, VectorTests.class })
+@SuiteClasses({ AngleTests.class, BezierCurveTests.class,
+ CubicCurveTests.class, CurveUtilsTests.class, DimensionTests.class,
+ EllipseTests.class, LineTests.class, PointListUtilsTests.class,
+ PointTests.class, PolygonTests.class, PolylineTests.class,
+ PolynomCalculationUtilsTests.class, PrecisionUtilsTests.class,
+ QuadraticCurveTests.class, RectangleTests.class, RegionTests.class,
+ RingTests.class, RoundedRectangleTests.class, StraightTests.class,
+ VectorTests.class, Vector3DTests.class })
public class AllTests {
}
diff --git a/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/AngleTests.java b/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/AngleTests.java
index 02c0278..ead4299 100644
--- a/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/AngleTests.java
+++ b/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/AngleTests.java
@@ -74,6 +74,7 @@ public class AngleTests {
assertFalse(alpha.equals(delta));
assertTrue(gamma.equals(delta));
assertTrue(delta.equals(gamma));
+ assertFalse(alpha.equals(null));
assertFalse(alpha.equals(new Point()));
alpha = new Angle(UNRECOGNIZABLE_FRACTION / 2, AngleUnit.RAD);
@@ -81,6 +82,7 @@ public class AngleTests {
AngleUnit.RAD);
assertTrue(alpha.equals(beta));
assertTrue(beta.equals(alpha));
+
}
@Test
diff --git a/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/BezierCurveTests.java b/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/BezierCurveTests.java
new file mode 100644
index 0000000..88d3679
--- /dev/null
+++ b/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/BezierCurveTests.java
@@ -0,0 +1,345 @@
+/*******************************************************************************
+ * Copyright (c) 2012 itemis AG and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Matthias Wienand (itemis AG) - initial API and implementation
+ *
+ *******************************************************************************/
+package org.eclipse.gef4.geometry.tests;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import org.eclipse.gef4.geometry.Point;
+import org.eclipse.gef4.geometry.planar.BezierCurve;
+import org.eclipse.gef4.geometry.planar.CubicCurve;
+import org.eclipse.gef4.geometry.planar.Line;
+import org.eclipse.gef4.geometry.planar.QuadraticCurve;
+import org.eclipse.gef4.geometry.planar.Rectangle;
+import org.eclipse.gef4.geometry.utils.PointListUtils;
+import org.eclipse.gef4.geometry.utils.PrecisionUtils;
+import org.junit.Test;
+
+public class BezierCurveTests {
+
+ @Test
+ public void test_equals() {
+ BezierCurve c = new BezierCurve(1, 1, 1, 10, 10, 1, 10, 10);
+ assertFalse(c.equals(null));
+ assertFalse(c.equals(new Rectangle(1, 2, 3, 4)));
+ assertEquals(c, c);
+
+ BezierCurve cr = new BezierCurve(10, 10, 10, 1, 1, 10, 1, 1);
+ assertEquals(cr, cr);
+ assertEquals(c, cr);
+ assertEquals(cr, c);
+
+ BezierCurve ce = c.getElevated();
+ BezierCurve cre = cr.getElevated();
+ assertEquals(c, ce);
+ assertEquals(ce, c);
+ assertEquals(cr, cre);
+ assertEquals(cre, cr);
+ assertEquals(c, cre);
+ assertEquals(cre, c);
+ assertEquals(cr, ce);
+ assertEquals(ce, cr);
+
+ BezierCurve c2 = new BezierCurve(1, 2, 3, 4);
+ assertFalse(c.equals(c2));
+ assertFalse(c2.equals(c));
+ c2 = new BezierCurve(1, 1, 1, 10, 2, 3);
+ assertFalse(c.equals(c2));
+ assertFalse(c2.equals(c));
+ c2 = new BezierCurve(1, 1, 1, 10, 10, 1, 2, 3);
+ assertFalse(c.equals(c2));
+ assertFalse(c2.equals(c));
+ c2 = new BezierCurve(1, 1, 2, 9, 9, 2, 10, 10);
+ assertFalse(c.equals(c2));
+ assertFalse(c2.equals(c));
+ }
+
+ @Test
+ public void test_constructors() {
+ BezierCurve c0 = new BezierCurve(1, 1, 1, 10, 10, 1, 10, 10);
+ assertEquals(c0, new BezierCurve(new Point(1, 1), new Point(1, 10),
+ new Point(10, 1), new Point(10, 10)));
+ assertEquals(c0, new BezierCurve(new CubicCurve(1, 1, 1, 10, 10, 1, 10,
+ 10)));
+ BezierCurve c1 = new BezierCurve(1, 1, 10, 1, 10, 10);
+ assertEquals(c1, new BezierCurve(new Point(1, 1), new Point(10, 1),
+ new Point(10, 10)));
+ assertEquals(c1, new BezierCurve(
+ new QuadraticCurve(1, 1, 10, 1, 10, 10)));
+
+ // getCopy()
+ BezierCurve c0copy = c0.getCopy();
+ assertEquals(c0, c0copy);
+ assertNotSame(c0, c0copy);
+
+ c0copy.setP1(new Point(100, 100));
+ assertFalse(c0.equals(c0copy));
+ }
+
+ @Test
+ public void test_contains_Point() {
+ BezierCurve c0 = new BezierCurve(1, 1, 1, 10, 10, 1, 10, 10);
+ assertFalse(c0.contains(new Point(0, 0)));
+ assertFalse(c0.contains(new Point(3, 3)));
+ assertFalse(c0.contains(new Point(3, 8)));
+ assertFalse(c0.contains(new Point(7, 3)));
+ assertFalse(c0.contains(new Point(7, 8)));
+ assertFalse(c0.contains(new Point(11, 11)));
+ assertTrue(c0.contains(new Point(1, 1)));
+ assertTrue(c0.contains(new Point(10, 10)));
+
+ // evaluate curve at some parameter values and check that the returned
+ // points are contained by the curve
+ for (double t = 0; t <= 1; t += 0.02)
+ assertTrue(c0.contains(c0.get(t)));
+ }
+
+ @Test
+ public void test_get() {
+ BezierCurve c0 = new BezierCurve(1, 1, 1, 10, 10, 1, 10, 10);
+ assertEquals(new Point(1, 1), c0.get(0));
+ assertEquals(new Point(10, 10), c0.get(1));
+ assertEquals(new Point(5.5, 5.5), c0.get(0.5));
+ }
+
+ @Test
+ public void test_getBounds() {
+ BezierCurve c0 = new BezierCurve(1, 1, 1, 10, 10, 1, 10, 10);
+ assertEquals(new Rectangle(1, 1, 9, 9), c0.getBounds());
+ }
+
+ @Test
+ public void test_getClipped() {
+ BezierCurve c0 = new BezierCurve(1, 1, 1, 10, 10, 1, 10, 10);
+ assertEquals(new BezierCurve(1, 1), c0.getClipped(0, 0));
+ assertEquals(new BezierCurve(10, 10), c0.getClipped(1, 1));
+
+ BezierCurve c1 = c0.getClipped(0, 0.5);
+ BezierCurve c2 = c0.getClipped(0.5, 1);
+ assertEquals(new Point(1, 1), c1.get(0));
+ assertEquals(new Point(5.5, 5.5), c1.get(1));
+ assertEquals(new Point(5.5, 5.5), c2.get(0));
+ assertEquals(new Point(10, 10), c2.get(1));
+ }
+
+ @Test
+ public void test_getControlBounds() {
+ BezierCurve c0 = new BezierCurve(1, 1, 1, 10, 10, 1, 10, 10);
+ assertEquals(new Rectangle(1, 1, 9, 9), c0.getControlBounds());
+
+ BezierCurve c1 = new BezierCurve(1, 5, 5, 8, 10, 1);
+ assertEquals(new Rectangle(1, 1, 9, 7), c1.getControlBounds());
+ }
+
+ @Test
+ public void test_getDerivative() {
+ BezierCurve c0 = new BezierCurve(1, 1, 1, 10, 10, 1, 10, 10);
+ BezierCurve d0 = c0.getDerivative();
+ assertEquals(3, d0.getPoints().length);
+ // TODO: check the derivative for some points on the curve
+ }
+
+ @Test
+ public void test_getOverlap() {
+ BezierCurve c0 = new BezierCurve(1, 1, 1, 10, 10, 1, 10, 10);
+ BezierCurve c1 = c0.getClipped(0, 0.5);
+ BezierCurve c2 = c0.getClipped(0.5, 1);
+
+ BezierCurve o01 = c0.getOverlap(c1);
+ assertTrue(o01 != null);
+ assertTrue(c1.contains(o01));
+ // assertEquals(c1, o01);
+
+ /*
+ * TODO: The equality check may not return true for the computed overlap
+ * and the real overlap. This is because the overlap is only
+ * approximated.
+ */
+
+ BezierCurve o02 = c0.getOverlap(c2);
+ assertTrue(o02 != null);
+ assertTrue(c2.contains(o02));
+ // assertEquals(c2, o02);
+
+ assertNull(c1.getOverlap(c2));
+ }
+
+ private void check_values_with_getters(BezierCurve c, Point... points) {
+ Point p1 = points[0];
+ assertEquals(p1, c.getP1());
+ assertTrue(PrecisionUtils.equal(p1.x, c.getX1()));
+ assertTrue(PrecisionUtils.equal(p1.y, c.getY1()));
+
+ Point p2 = points[points.length - 1];
+ assertEquals(p2, c.getP2());
+ assertTrue(PrecisionUtils.equal(p2.x, c.getX2()));
+ assertTrue(PrecisionUtils.equal(p2.y, c.getY2()));
+
+ assertTrue(PointListUtils.equals(c.getPoints(), points));
+
+ for (int i = 0; i < points.length; i++)
+ assertEquals(points[i], c.getPoint(i));
+ }
+
+ @Test
+ public void test_point_getters() {
+ BezierCurve c0 = new BezierCurve(1, 1, 1, 10, 10, 1, 10, 10);
+ check_values_with_getters(c0, new Point[] { new Point(1, 1),
+ new Point(1, 10), new Point(10, 1), new Point(10, 10) });
+ }
+
+ @Test
+ public void test_point_setters() {
+ BezierCurve c0 = new BezierCurve(1, 1, 1, 10, 10, 1, 10, 10);
+ check_values_with_getters(c0, new Point[] { new Point(1, 1),
+ new Point(1, 10), new Point(10, 1), new Point(10, 10) });
+
+ c0.setP1(new Point(-30, 5));
+ check_values_with_getters(c0, new Point[] { new Point(-30, 5),
+ new Point(1, 10), new Point(10, 1), new Point(10, 10) });
+
+ c0.setP2(new Point(31, 11));
+ check_values_with_getters(c0, new Point[] { new Point(-30, 5),
+ new Point(1, 10), new Point(10, 1), new Point(31, 11) });
+
+ c0.setPoint(1, new Point(3, -3));
+ check_values_with_getters(c0, new Point[] { new Point(-30, 5),
+ new Point(3, -3), new Point(10, 1), new Point(31, 11) });
+
+ c0.setPoint(2, new Point(-3, 3));
+ check_values_with_getters(c0, new Point[] { new Point(-30, 5),
+ new Point(3, -3), new Point(-3, 3), new Point(31, 11) });
+ }
+
+ @Test
+ public void test_getParameterAt() {
+ BezierCurve c0 = new BezierCurve(1, 1, 1, 10, 10, 1, 10, 10);
+ assertTrue(PrecisionUtils.equal(0, c0.getParameterAt(new Point(1, 1))));
+ assertTrue(PrecisionUtils
+ .equal(1, c0.getParameterAt(new Point(10, 10))));
+ assertTrue(PrecisionUtils.equal(0.5,
+ c0.getParameterAt(new Point(5.5, 5.5))));
+
+ boolean thrown = false;
+ try {
+ c0.getParameterAt(null);
+ } catch (NullPointerException x) {
+ thrown = true;
+ }
+ assertTrue(thrown);
+
+ thrown = false;
+ try {
+ c0.getParameterAt(new Point(3, 3));
+ } catch (IllegalArgumentException x) {
+ thrown = true;
+ }
+ assertTrue(thrown);
+ }
+
+ @Test
+ public void test_getTranslated() {
+ BezierCurve c0 = new BezierCurve(1, 1, 1, 10, 10, 1, 10, 10);
+ BezierCurve t0 = c0.getTranslated(new Point(-1, 4));
+ assertEquals(new BezierCurve(0, 5, 0, 14, 9, 5, 9, 14), t0);
+ }
+
+ @Test
+ public void test_overlaps() {
+ BezierCurve c0 = new BezierCurve(1, 1, 1, 10, 10, 1, 10, 10);
+ BezierCurve c1 = c0.getClipped(0, 0.5);
+ BezierCurve c2 = c0.getClipped(0.5, 1);
+ assertTrue(c0.overlaps(c1));
+ assertTrue(c1.overlaps(c0));
+ assertTrue(c0.overlaps(c2));
+ assertTrue(c2.overlaps(c0));
+ assertFalse(c1.overlaps(c2));
+ assertFalse(c2.overlaps(c1));
+ }
+
+ @Test
+ public void test_split() {
+ BezierCurve c0 = new BezierCurve(1, 1, 1, 10, 10, 1, 10, 10);
+ BezierCurve c1 = c0.getClipped(0, 0.5);
+ BezierCurve c2 = c0.getClipped(0.5, 1);
+ BezierCurve[] split = c0.split(0.5);
+ assertEquals(c1, split[0]);
+ assertEquals(c2, split[1]);
+ }
+
+ @Test
+ public void test_toBezier() {
+ BezierCurve c0 = new BezierCurve(1, 1, 1, 10, 10, 1, 10, 10);
+ BezierCurve[] beziers = c0.toBezier();
+ assertEquals(1, beziers.length);
+ assertEquals(c0, beziers[0]);
+ }
+
+ @Test
+ public void test_toCubic() {
+ BezierCurve c0 = new BezierCurve(1, 1);
+ assertNull(c0.toCubic());
+ c0 = new BezierCurve(1, 1, 1, 10);
+ assertNull(c0.toCubic());
+ c0 = new BezierCurve(1, 1, 1, 10, 10, 1);
+ assertNull(c0.toCubic());
+ c0 = new BezierCurve(1, 1, 1, 10, 10, 1, 10, 10);
+ assertEquals(new CubicCurve(1, 1, 1, 10, 10, 1, 10, 10), c0.toCubic());
+ c0 = new BezierCurve(1, 1, 1, 10, 10, 1, 67, 89, 10, 10);
+ assertEquals(new CubicCurve(1, 1, 1, 10, 10, 1, 10, 10), c0.toCubic());
+ c0 = new BezierCurve(1, 1, 1, 10, 10, 1, 10, 10, 98, 76);
+ assertEquals(new CubicCurve(1, 1, 1, 10, 10, 1, 98, 76), c0.toCubic());
+ }
+
+ @Test
+ public void test_toQuadratic() {
+ BezierCurve c0 = new BezierCurve(1, 1);
+ assertNull(c0.toQuadratic());
+ c0 = new BezierCurve(1, 1, 1, 10);
+ assertNull(c0.toQuadratic());
+ c0 = new BezierCurve(1, 1, 1, 10, 10, 1);
+ assertEquals(new QuadraticCurve(1, 1, 1, 10, 10, 1), c0.toQuadratic());
+ c0 = new BezierCurve(1, 1, 1, 10, 67, 89, 10, 1);
+ assertEquals(new QuadraticCurve(1, 1, 1, 10, 10, 1), c0.toQuadratic());
+ c0 = new BezierCurve(1, 1, 1, 10, 10, 1, 98, 76);
+ assertEquals(new QuadraticCurve(1, 1, 1, 10, 98, 76), c0.toQuadratic());
+ }
+
+ @Test
+ public void test_toLine() {
+ BezierCurve c0 = new BezierCurve(1, 1);
+ assertNull(c0.toCubic());
+ c0 = new BezierCurve(1, 1, 1, 10);
+ assertEquals(new Line(1, 1, 1, 10), c0.toLine());
+ c0 = new BezierCurve(1, 1, 1, 10, 10, 1);
+ assertEquals(new Line(1, 1, 10, 1), c0.toLine());
+ c0 = new BezierCurve(1, 1, 1, 10, 10, 1, 10, 10);
+ assertEquals(new Line(1, 1, 10, 10), c0.toLine());
+ c0 = new BezierCurve(1, 1, 1, 10, 10, 1, 10, 10, 98, 76);
+ assertEquals(new Line(1, 1, 98, 76), c0.toLine());
+ }
+
+ @Test
+ public void test_toLineStrip() {
+ BezierCurve linear = new BezierCurve(0, 0, 1, 1);
+ Line[] lines = linear.toLineStrip(1);
+ assertEquals(1, lines.length);
+ assertEquals(new Line(0, 0, 1, 1), lines[0]);
+ assertEquals(linear.toLine(), lines[0]);
+
+ // TODO: check complicated curves, too
+ }
+
+}
diff --git a/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/CurveUtilsTests.java b/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/CurveUtilsTests.java
index b81d8d8..186cfdb 100644
--- a/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/CurveUtilsTests.java
+++ b/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/CurveUtilsTests.java
@@ -154,7 +154,7 @@ public class CurveUtilsTests {
}
@Test
- public void test_clip_with_QuadraticCurve() {
+ public void test_getClipped_with_QuadraticCurve() {
final int numPoints = 4;
final double step = 0.123456789;
@@ -169,7 +169,7 @@ public class CurveUtilsTests {
QuadraticCurve c = new QuadraticCurve(points);
for (double t1 = 0; t1 <= 1; t1 += step) {
for (double t2 = 0; t2 <= 1; t2 += step) {
- QuadraticCurve cc = c.clip(t1, t2);
+ QuadraticCurve cc = c.getClipped(t1, t2);
assertEquals(c.get(t1), cc.get(0));
assertEquals(c.get(t2), cc.get(1));
}
diff --git a/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/DimensionTests.java b/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/DimensionTests.java
index 634af06..1837d62 100644
--- a/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/DimensionTests.java
+++ b/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/DimensionTests.java
@@ -45,9 +45,12 @@ public class DimensionTests {
public void test_contains() {
Dimension d1 = new Dimension(0.1, 0.1);
Dimension d2 = new Dimension(0.2, 0.2);
+ Dimension d3 = new Dimension(0.1, 0.3);
assertTrue(d2.contains(d1));
assertFalse(d1.contains(d2));
+ assertFalse(d2.contains(d3));
+ assertTrue(d3.contains(d1));
}
@Test
@@ -64,16 +67,20 @@ public class DimensionTests {
*/
@Test
public void test_equals() {
- Dimension p1 = new Dimension(0.1, 0.1);
- Dimension p2 = new Dimension(0.2, 0.2);
- assertFalse(p1.equals(p2));
+ Dimension d1 = new Dimension(0.1, 0.1);
+ Dimension d2 = new Dimension(0.2, 0.2);
+ Dimension d3 = new Dimension(0.1, 0.2);
+ Dimension d4 = new Dimension(0.2, 0.1);
+ assertFalse(d1.equals(d2));
+ assertFalse(d1.equals(d3));
+ assertFalse(d1.equals(d4));
- p1 = new Dimension(0.2, 0.2);
- assertTrue(p1.equals(p2));
+ d1 = new Dimension(0.2, 0.2);
+ assertTrue(d1.equals(d2));
// wrong type
- p1 = new Dimension(1, 1);
- assertFalse(p1.equals(new org.eclipse.swt.graphics.Point(1, 1)));
+ d1 = new Dimension(1, 1);
+ assertFalse(d1.equals(new org.eclipse.swt.graphics.Point(1, 1)));
}
@Test
diff --git a/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/EllipseTests.java b/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/EllipseTests.java
index 83d7dcf..94039e1 100644
--- a/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/EllipseTests.java
+++ b/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/EllipseTests.java
@@ -11,17 +11,19 @@
*
*******************************************************************************/
package org.eclipse.gef4.geometry.tests;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.eclipse.gef4.geometry.Point;
+import org.eclipse.gef4.geometry.planar.CubicCurve;
import org.eclipse.gef4.geometry.planar.Ellipse;
+import org.eclipse.gef4.geometry.planar.IGeometry;
import org.eclipse.gef4.geometry.planar.Line;
import org.eclipse.gef4.geometry.planar.Rectangle;
import org.junit.Test;
-
/**
* Unit tests for {@link Ellipse}.
*
@@ -34,35 +36,32 @@ public class EllipseTests {
.getPrecisionFraction();
@Test
- public void test_contains_with_Point() {
+ public void test_equals() {
+ Ellipse e = new Ellipse(0, 0, 100, 50);
+ assertFalse(e.equals(null));
+ assertFalse(e.equals(new Point()));
+ assertEquals(e, e);
+ assertEquals(e, new Ellipse(0, 0, 100, 50));
+ assertEquals(e, new Ellipse(new Rectangle(0, 0, 100, 50)));
+ assertEquals(e, e.getCopy());
+ assertFalse(e.equals(new Ellipse(0, 0, 100, 10)));
+ assertFalse(e.equals(new Ellipse(0, 0, 10, 50)));
+ assertFalse(e.equals(new Ellipse(10, 0, 100, 50)));
+ assertFalse(e.equals(new Ellipse(0, 10, 100, 50)));
+ }
+
+ @Test
+ public void test_contains_Point() {
Rectangle r = new Rectangle(34.3435, 56.458945, 123.3098, 146.578);
Ellipse e = new Ellipse(r);
- assertTrue(e.contains(r.getCentroid()));
-
- assertTrue(e.contains(r.getLeft()));
- assertTrue(e.contains(r.getLeft().getTranslated(PRECISION_FRACTION * 1,
- 0)));
- assertFalse(e.contains(r.getLeft().getTranslated(
- -PRECISION_FRACTION * 1000, 0)));
+ checkPointContainment(r, e);
+ // these things could not be tested in the general case, because of
+ // AWT's behavior
assertTrue(e.contains(r.getTop()));
- assertTrue(e.contains(r.getTop().getTranslated(0,
- PRECISION_FRACTION * 100)));
- assertFalse(e.contains(r.getTop().getTranslated(0,
- -PRECISION_FRACTION * 100)));
-
assertTrue(e.contains(r.getRight()));
- assertTrue(e.contains(r.getRight().getTranslated(
- -PRECISION_FRACTION * 100, 0)));
- assertFalse(e.contains(r.getRight().getTranslated(
- PRECISION_FRACTION * 100, 0)));
-
assertTrue(e.contains(r.getBottom()));
- assertTrue(e.contains(r.getBottom().getTranslated(0,
- -PRECISION_FRACTION * 100)));
- assertFalse(e.contains(r.getBottom().getTranslated(0,
- PRECISION_FRACTION * 100)));
for (Point p : e.getIntersections(new Line(r.getTopLeft(), r
.getBottomRight()))) {
@@ -72,10 +71,75 @@ public class EllipseTests {
.getBottomLeft()))) {
assertTrue(e.contains(p));
}
+
+ for (CubicCurve c : e.getOutlineSegments()) {
+ assertTrue(e.contains(c.get(0.5)));
+ }
+ }
+
+ private void checkPointContainment(Rectangle r, IGeometry g) {
+ assertFalse(g.contains(r.getTopLeft()));
+ assertFalse(g.contains(r.getTopRight()));
+ assertFalse(g.contains(r.getBottomLeft()));
+ assertFalse(g.contains(r.getBottomRight()));
+
+ assertTrue(g.contains(r.getCentroid()));
+
+ assertTrue(g.contains(r.getLeft()));
+ assertTrue(g.contains(r.getLeft().getTranslated(PRECISION_FRACTION * 1,
+ 0)));
+ assertFalse(g.contains(r.getLeft().getTranslated(
+ -PRECISION_FRACTION * 1000, 0)));
+
+ // due to AWT's behavior, we won't check getTop() but a point very near
+ // to it, so that the Path() will survive these tests, too
+ assertTrue(g.contains(r.getTop().getTranslated(0, 1)));
+ assertTrue(g.contains(r.getTop().getTranslated(0,
+ PRECISION_FRACTION * 100)));
+ assertFalse(g.contains(r.getTop().getTranslated(0,
+ -PRECISION_FRACTION * 100)));
+
+ // due to AWT's behavior, we won't check getRight() but a point very
+ // near to it, so that the Path() will survive these tests, too
+ assertTrue(g.contains(r.getRight().getTranslated(-1, 0)));
+ assertTrue(g.contains(r.getRight().getTranslated(
+ -PRECISION_FRACTION * 100, 0)));
+ assertFalse(g.contains(r.getRight().getTranslated(
+ PRECISION_FRACTION * 100, 0)));
+
+ // due to AWT's behavior, we won't check getBottom() but a point very
+ // near to it, so that the Path() will survive these tests, too
+ assertTrue(g.contains(r.getBottom().getTranslated(0, -1)));
+ assertTrue(g.contains(r.getBottom().getTranslated(0,
+ -PRECISION_FRACTION * 100)));
+ assertFalse(g.contains(r.getBottom().getTranslated(0,
+ PRECISION_FRACTION * 100)));
+ }
+
+ @Test
+ public void test_contains_Line() {
+ Ellipse e = new Ellipse(0, 0, 100, 50);
+ assertFalse(e.contains(new Line(-10, -10, 10, -10)));
+ assertFalse(e.contains(new Line(-10, -10, 50, 50)));
+ assertTrue(e.contains(new Line(1, 25, 99, 25)));
+ assertTrue(e.contains(new Line(0, 25, 100, 25)));
}
@Test
- public void test_intersects_with_Line() {
+ public void test_getCenter() {
+ Ellipse e = new Ellipse(0, 0, 100, 50);
+ assertEquals(new Point(50, 25), e.getCenter());
+ e.scale(2);
+ assertEquals(new Point(50, 25), e.getCenter());
+ e.scale(0.5);
+ e.scale(2, new Point());
+ assertEquals(new Point(100, 50), e.getCenter());
+ e.translate(-100, -50);
+ assertEquals(new Point(), e.getCenter());
+ }
+
+ @Test
+ public void test_intersects_Line() {
Rectangle r = new Rectangle(34.3435, 56.458945, 123.3098, 146.578);
Ellipse e = new Ellipse(r);
for (Line l : r.getOutlineSegments()) {
@@ -83,8 +147,45 @@ public class EllipseTests {
}
}
+ private void checkPoints(Point[] expected, Point[] obtained) {
+ assertEquals(expected.length, obtained.length);
+ for (Point e : expected) {
+ boolean found = false;
+ for (Point o : obtained) {
+ if (e.equals(o)) {
+ found = true;
+ break;
+ }
+ }
+ assertTrue(found);
+ }
+ }
+
@Test
- public void test_get_intersections_with_Ellipse_strict() {
+ public void test_getIntersections_Line() {
+ Ellipse e = new Ellipse(0, 0, 100, 50);
+ Line lh = new Line(0, 25, 100, 25);
+ Point[] is = e.getIntersections(lh);
+ checkPoints(new Point[] { new Point(0, 25), new Point(100, 25) }, is);
+ Line lv = new Line(50, 0, 50, 50);
+ is = e.getIntersections(lv);
+ checkPoints(new Point[] { new Point(50, 0), new Point(50, 50) }, is);
+
+ lh = lh.getTranslated(new Point(0, -25)).toLine();
+ is = e.getIntersections(lh);
+ checkPoints(new Point[] { new Point(50, 0) }, is);
+
+ lv = lv.getTranslated(new Point(-50, 0)).toLine();
+ is = e.getIntersections(lv);
+ checkPoints(new Point[] { new Point(0, 25) }, is);
+
+ Line li = new Line(-100, 100, 0, 50);
+ is = e.getIntersections(li);
+ assertEquals(0, is.length);
+ }
+
+ @Test
+ public void test_get_intersections_Ellipse_strict() {
Rectangle r = new Rectangle(34.3435, 56.458945, 123.3098, 146.578);
Ellipse e1 = new Ellipse(r);
Ellipse e2 = new Ellipse(r);
@@ -203,7 +304,7 @@ public class EllipseTests {
}
@Test
- public void test_getIntersections_with_Ellipse_tolerance() {
+ public void test_getIntersections_Ellipse_tolerance() {
Rectangle r = new Rectangle(34.3435, 56.458945, 123.3098, 146.578);
Ellipse e1 = new Ellipse(r);
Ellipse e2 = new Ellipse(r);
@@ -245,7 +346,7 @@ public class EllipseTests {
// @Ignore("This test is too strict. For a liberal test see below: test_getIntersections_with_Ellipse_Bezier_special_tolerance")
@Test
- public void test_getIntersections_with_Ellipse_Bezier_special() {
+ public void test_getIntersections_Ellipse_Bezier_special() {
// 3 nearly tangential intersections
Ellipse e1 = new Ellipse(126, 90, 378, 270);
Ellipse e2 = new Ellipse(222, 77, 200, 200);
@@ -263,7 +364,7 @@ public class EllipseTests {
}
@Test
- public void test_getIntersections_with_Ellipse_Bezier_special_tolerance() {
+ public void test_getIntersections_Ellipse_Bezier_special_tolerance() {
// 3 nearly tangential intersections
Ellipse e1 = new Ellipse(126, 90, 378, 270);
Ellipse e2 = new Ellipse(222, 77, 200, 200);
@@ -279,4 +380,17 @@ public class EllipseTests {
intersectionsTolerance(e1, e2); // TODO: find out the 3 expected points
}
+ @Test
+ public void test_toPath() {
+ Rectangle r = new Rectangle(0, 0, 100, 50);
+ Ellipse e = new Ellipse(r);
+ checkPointContainment(r, e.toPath());
+ }
+
+ @Test
+ public void test_toString() {
+ Ellipse e = new Ellipse(0, 0, 100, 50);
+ assertEquals("Ellipse (0.0, 0.0, 100.0, 50.0)", e.toString());
+ }
+
}
diff --git a/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/LineTests.java b/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/LineTests.java
index c6a4b53..f6ab42c 100644
--- a/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/LineTests.java
+++ b/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/LineTests.java
@@ -14,6 +14,7 @@ package org.eclipse.gef4.geometry.tests;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -224,6 +225,7 @@ public class LineTests {
// intersection within precision, no real intersection
Line close = new Line(new Point(-5, UNRECOGNIZABLE_FRACTION),
new Point(5, UNRECOGNIZABLE_FRACTION));
+
// parallel so we do not return an intersection point
assertNull(normal.getIntersection(close));
assertNull(close.getIntersection(normal));
@@ -254,6 +256,12 @@ public class LineTests {
Line elsewhere = new Line(new Point(-5, 1), new Point(5, 10));
assertNull(normal.getIntersection(elsewhere));
assertNull(elsewhere.getIntersection(normal));
+
+ // single end point intersection with parallel lines:
+ // X-------X-------X
+ Line l1 = new Line(400.0, 102.48618784530387, 399.99999999999994, 100.0);
+ Line l2 = new Line(400.0, 51.10497237569061, 399.99999999999994, 100.0);
+ assertNotNull(l1.getIntersection(l2));
}
@Test
diff --git a/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/PointTests.java b/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/PointTests.java
index 6d81c3e..945cf34 100644
--- a/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/PointTests.java
+++ b/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/PointTests.java
@@ -126,6 +126,10 @@ public class PointTests {
q = new Point(3, 6);
assertTrue(p.getScaled(3, 6).equals(q));
assertTrue(q.getScaled(1f / 3f, 1f / 6f).equals(p));
+
+ // scale around some other point
+ Point c = new Point(10, 10);
+ assertEquals(new Point(9, 8), q.getScaled(1d / 7d, 1d / 2d, c));
}
@Test
diff --git a/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/PolygonTests.java b/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/PolygonTests.java
index 49e40bd..e4e9ecd 100644
--- a/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/PolygonTests.java
+++ b/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/PolygonTests.java
@@ -90,6 +90,11 @@ public class PolygonTests {
assertFalse(new Polygon(new Point(), new Point(0, 5), new Point(5, 5),
new Point(5, 0), new Point(2.5, 2.5)).contains(new Line(1, 2,
4, 2)));
+
+ Polygon mouth = new Polygon(new Point(0, 5), new Point(2, 1),
+ new Point(4, 1), new Point(6, 5), new Point(4, 6), new Point(6,
+ 7), new Point(4, 10), new Point(2, 10));
+ assertFalse(mouth.contains(new Line(6, 5, 6, 7)));
}
/**
@@ -496,6 +501,24 @@ public class PolygonTests {
}
@Test
+ public void test_getTriangulation() {
+ Polygon p = new Polygon(150.0, 50.0, 50.0, 100.0, 23.0, 165.0, 50.0,
+ 250.0, 135.0, 294.0, 250.0, 300.0, 137.0, 260.0, 63.0, 168.0,
+ 113.0, 105.0, 136.0, 206.0, 150.0, 50.0);
+
+ // test that it does not throw a NullPointerException
+ p.getTriangulation();
+ assertTrue(true);
+ // TODO: test that the triangulation is correct
+
+ p = new Polygon(150.0, 50.0, 50.0, 100.0, 32.0, 168.0, 50.0, 250.0,
+ 136.0, 298.0, 250.0, 300.0, 122.0, 252.0, 67.0, 180.0, 114.0,
+ 95.0, 136.0, 194.0, 150.0, 50.0);
+ p.getTriangulation();
+ assertTrue(true);
+ }
+
+ @Test
public void test_intersects_Ellipse() {
assertTrue(RHOMB.touches(new Ellipse(0, 0, 4, 4)));
}
diff --git a/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/PolylineTests.java b/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/PolylineTests.java
index cf74fe0..0a6e498 100644
--- a/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/PolylineTests.java
+++ b/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/PolylineTests.java
@@ -12,17 +12,35 @@
*******************************************************************************/
package org.eclipse.gef4.geometry.tests;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
import org.eclipse.gef4.geometry.Point;
+import org.eclipse.gef4.geometry.planar.Line;
import org.eclipse.gef4.geometry.planar.Polyline;
import org.junit.Test;
public class PolylineTests {
- private static final Polyline POLYLINE = new Polyline(new Point[] {
- new Point(0, 0), new Point(1, 0), new Point(6, 5) });
+ private static final Point[] POINTS = new Point[] { new Point(0, 0),
+ new Point(1, 0), new Point(6, 5) };
+ private static final Polyline POLYLINE = new Polyline(POINTS);
+
+ @Test
+ public void test_equals() {
+ assertEquals(POLYLINE, POLYLINE);
+ assertFalse(POLYLINE.equals((Object) null));
+ assertFalse(POLYLINE.equals(new Line(1, 2, 3, 4)));
+
+ List<Point> points = Arrays.asList(POINTS);
+ Collections.reverse(points);
+ assertEquals(POLYLINE, new Polyline(points.toArray(new Point[] {})));
+ }
@Test
public void test_contains_with_Point() {
diff --git a/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/RectangleTests.java b/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/RectangleTests.java
index 4ac9488..5dc75a5 100644
--- a/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/RectangleTests.java
+++ b/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/RectangleTests.java
@@ -40,10 +40,6 @@ public class RectangleTests {
void action(Rectangle rect, Point tl, Point br);
}
- private interface IPairAction {
- void action(Rectangle r1, Rectangle r2);
- }
-
private static final double PRECISION_FRACTION = TestUtils
.getPrecisionFraction();
@@ -53,32 +49,6 @@ public class RectangleTests {
private static final double UNRECOGNIZABLE_FRACTION = PRECISION_FRACTION
- PRECISION_FRACTION / 10;
- private void forRectanglePairs(IPairAction action) {
- for (double x11 = -2; x11 <= 2.4; x11 += 1.1) {
- for (double y11 = -2; y11 <= 2.4; y11 += 1.1) {
- Point p11 = new Point(x11, y11);
- for (double x12 = -2; x12 <= 2.4; x12 += 1.1) {
- for (double y12 = -2; y12 <= 2.4; y12 += 1.1) {
- Point p12 = new Point(x12, y12);
- Rectangle r1 = new Rectangle(p11, p12);
- for (double x21 = -2; x21 <= 2.4; x21 += 1.1) {
- for (double y21 = -2; y21 <= 2.4; y21 += 1.1) {
- Point p21 = new Point(x21, y21);
- for (double x22 = -2; x22 <= 2.4; x22 += 1.1) {
- for (double y22 = -2; y22 <= 2.4; y22 += 1.1) {
- Point p22 = new Point(x22, y22);
- Rectangle r2 = new Rectangle(p21, p22);
- action.action(r1, r2);
- }
- }
- }
- }
- }
- }
- }
- }
- }
-
private void forRectangles(IAction action) {
for (double x1 = -2; x1 <= 2; x1 += 0.4) {
for (double y1 = -2; y1 <= 2; y1 += 0.4) {
@@ -198,6 +168,12 @@ public class RectangleTests {
assertTrue(recognizableExpanded.contains(preciseRect));
assertFalse(recognizableShrinked.contains(preciseRect));
assertFalse(recognizableShrinked.contains(recognizableExpanded));
+
+ // Regression test for a contains() bug that caused false positives for
+ // a "containing" Rectangle with smaller x and y coordinates and greater
+ // width and height as the "contained" one.
+ assertFalse(new Rectangle(0, 0, 100, 100).contains(new Rectangle(200,
+ 200, 1, 1)));
}
@Test
diff --git a/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/RegionTests.java b/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/RegionTests.java
new file mode 100644
index 0000000..e042f32
--- /dev/null
+++ b/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/RegionTests.java
@@ -0,0 +1,103 @@
+/*******************************************************************************
+ * Copyright (c) 2012 itemis AG and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Matthias Wienand (itemis AG) - initial API and implementation
+ *
+ *******************************************************************************/
+package org.eclipse.gef4.geometry.tests;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.eclipse.gef4.geometry.planar.Rectangle;
+import org.eclipse.gef4.geometry.planar.Region;
+import org.eclipse.gef4.geometry.utils.PrecisionUtils;
+import org.junit.Test;
+
+public class RegionTests {
+
+ @Test
+ public void test_constructor() {
+ Region region = new Region();
+ assertEquals(0, region.getShapes().length);
+
+ region = new Region(new Rectangle(0, 0, 100, 100));
+ assertEquals(1, region.getShapes().length);
+
+ region = new Region(region);
+ assertEquals(1, region.getShapes().length);
+ }
+
+ @Test
+ public void test_copy_semantics() {
+ Rectangle r1 = new Rectangle(0, 0, 100, 100);
+ Region a1 = new Region(r1);
+ r1.setWidth(50);
+
+ // constructor copies Rectangles:
+ // changing r1 does not change a1
+ assertTrue(PrecisionUtils.equal(100, a1.getShapes()[0].getWidth()));
+
+ Region a2 = a1.getCopy();
+ a2.getShapes()[0].setWidth(50);
+
+ // getCopy() copies Rectangles:
+ // changing a2 does not change a1
+ assertTrue(PrecisionUtils.equal(100, a1.getShapes()[0].getWidth()));
+
+ a2 = new Region(a1);
+ a2.getShapes()[0].setWidth(50);
+
+ // constructor copies Rectangles:
+ // changing a2 does not change a1
+ assertTrue(PrecisionUtils.equal(100, a1.getShapes()[0].getWidth()));
+ }
+
+ @Test
+ public void test_cover_single_rectangle() {
+ Rectangle r1 = new Rectangle(100, 100, 100, 100);
+ Region region = new Region(r1);
+
+ assertTrue(region.contains(r1));
+ assertTrue(
+ "A Region of just a single Rectangle should use this Rectangle as its only internal shape.",
+ region.getShapes()[0].equals(r1));
+
+ assertFalse(region.contains(new Rectangle(0, 0, 50, 50)));
+ assertFalse(region.contains(new Rectangle(50, 50, 100, 100)));
+ }
+
+ @Test
+ public void test_cover_two_distinct_rectangles() {
+ Rectangle r1 = new Rectangle(100, 100, 100, 100);
+ Rectangle r2 = new Rectangle(500, 100, 100, 100);
+ Region region = new Region(r1, r2);
+
+ assertTrue(region.contains(r1));
+ assertTrue(region.contains(r2));
+
+ assertFalse(region.contains(new Rectangle(0, 0, 50, 50)));
+ assertFalse(region.contains(new Rectangle(50, 50, 100, 100)));
+ }
+
+ @Test
+ public void test_cover_two_intersecting_rectangles() {
+ Rectangle r1 = new Rectangle(50, 50, 50, 200);
+ Rectangle r2 = new Rectangle(50, 200, 200, 50);
+ Region region = new Region(r1, r2);
+
+ assertTrue(region.contains(r1));
+ assertTrue(region.contains(r2));
+ assertTrue(region.contains(r1.getIntersected(r2)));
+
+ assertFalse(region.contains(new Rectangle(0, 0, 10, 10)));
+ assertFalse(region.contains(new Rectangle(25, 25, 50, 50)));
+ }
+
+}
diff --git a/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/RingTests.java b/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/RingTests.java
new file mode 100644
index 0000000..1a72166
--- /dev/null
+++ b/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/RingTests.java
@@ -0,0 +1,1120 @@
+/*******************************************************************************
+ * Copyright (c) 2012 itemis AG and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Matthias Wienand (itemis AG) - initial API and implementation
+ *
+ *******************************************************************************/
+package org.eclipse.gef4.geometry.tests;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertTrue;
+
+import java.lang.reflect.Method;
+
+import org.eclipse.gef4.geometry.Point;
+import org.eclipse.gef4.geometry.planar.Line;
+import org.eclipse.gef4.geometry.planar.Polygon;
+import org.eclipse.gef4.geometry.planar.Ring;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.experimental.runners.Enclosed;
+import org.junit.runner.RunWith;
+
+@RunWith(Enclosed.class)
+public class RingTests {
+
+ public static class TriangulateTriangleWithOutlinePoints {
+
+ private Polygon[] triangulate(Polygon p, Point p1, Point p2) {
+ try {
+ Class<?> parameterTypes[] = new Class<?>[] { Polygon.class,
+ Point.class, Point.class };
+ Method triangulate = Ring.class.getDeclaredMethod(
+ "triangulate", parameterTypes);
+ triangulate.setAccessible(true);
+ return (Polygon[]) triangulate.invoke(null, p, p1, p2);
+ } catch (Exception x) {
+ throw new IllegalStateException(x);
+ }
+ }
+
+ Polygon p;
+
+ @Before
+ public void setUp() {
+ p = new Polygon(new Point(100, 100), new Point(100, 300),
+ new Point(300, 200));
+ }
+
+ @Test
+ public void no_polygon() {
+ try {
+ triangulate(null, new Point(0, 0), new Point(1, 1));
+ } catch (IllegalStateException x) {
+ Throwable cause = x;
+ while (cause.getCause() != null)
+ cause = cause.getCause();
+
+ assertTrue(cause.getClass().equals(
+ IllegalArgumentException.class));
+ }
+ }
+
+ @Test
+ public void no_triangle() {
+ try {
+ triangulate(new Polygon(0, 0, 1, 0, 1, 1, 0, 1),
+ new Point(0, 0), new Point(1, 1));
+ } catch (IllegalStateException x) {
+ Throwable cause = x;
+ while (cause.getCause() != null)
+ cause = cause.getCause();
+
+ assertTrue(cause.getClass().equals(
+ IllegalArgumentException.class));
+ }
+ }
+
+ @Test
+ public void p1_not_on_polygon() {
+ try {
+ triangulate(new Polygon(0, 0, 1, 1, 0, 1), new Point(1, 0),
+ new Point(0, 1));
+ } catch (IllegalStateException x) {
+ Throwable cause = x;
+ while (cause.getCause() != null)
+ cause = cause.getCause();
+
+ assertTrue(cause.getClass().equals(
+ IllegalArgumentException.class));
+ }
+ }
+
+ @Test
+ public void p2_not_on_polygon() {
+ try {
+ triangulate(new Polygon(0, 0, 1, 1, 0, 1), new Point(0, 1),
+ new Point(1, 0));
+ } catch (IllegalStateException x) {
+ Throwable cause = x;
+ while (cause.getCause() != null)
+ cause = cause.getCause();
+
+ assertTrue(cause.getClass().equals(
+ IllegalArgumentException.class));
+ }
+ }
+
+ @Test
+ public void both_points_on_first_edge() {
+ // inner
+ Polygon[] r = triangulate(p, new Point(100, 150), new Point(100,
+ 250));
+ assertEquals("1 resulting polygon", 1, r.length);
+ assertEquals("result equal to original", r[0], p);
+ assertNotSame("a copy is returned", r[0], p);
+
+ r = triangulate(p, new Point(100, 250), new Point(100, 150));
+ assertEquals("1 resulting polygon", 1, r.length);
+ assertEquals("result equal to original", r[0], p);
+ assertNotSame("a copy is returned", r[0], p);
+
+ // start - inner
+ r = triangulate(p, new Point(100, 100), new Point(100, 200));
+ assertEquals("1 resulting polygon", 1, r.length);
+ assertEquals("result equal to original", r[0], p);
+ assertNotSame("a copy is returned", r[0], p);
+
+ r = triangulate(p, new Point(100, 200), new Point(100, 100));
+ assertEquals("1 resulting polygon", 1, r.length);
+ assertEquals("result equal to original", r[0], p);
+ assertNotSame("a copy is returned", r[0], p);
+
+ // end - inner
+ r = triangulate(p, new Point(100, 300), new Point(100, 200));
+ assertEquals("1 resulting polygon", 1, r.length);
+ assertEquals("result equal to original", r[0], p);
+ assertNotSame("a copy is returned", r[0], p);
+
+ r = triangulate(p, new Point(100, 200), new Point(100, 300));
+ assertEquals("1 resulting polygon", 1, r.length);
+ assertEquals("result equal to original", r[0], p);
+ assertNotSame("a copy is returned", r[0], p);
+
+ // start - end
+ r = triangulate(p, new Point(100, 100), new Point(100, 300));
+ assertEquals("1 resulting polygon", 1, r.length);
+ assertEquals("result equal to original", r[0], p);
+ assertNotSame("a copy is returned", r[0], p);
+
+ r = triangulate(p, new Point(100, 300), new Point(100, 100));
+ assertEquals("1 resulting polygon", 1, r.length);
+ assertEquals("result equal to original", r[0], p);
+ assertNotSame("a copy is returned", r[0], p);
+ }
+
+ @Test
+ public void both_points_on_second_edge() {
+ // inner
+ Polygon[] r = triangulate(p, new Point(250, 225), new Point(150,
+ 275));
+ assertEquals("1 resulting polygon", 1, r.length);
+ assertEquals("result equal to original", r[0], p);
+ assertNotSame("a copy is returned", r[0], p);
+
+ r = triangulate(p, new Point(150, 275), new Point(250, 225));
+ assertEquals("1 resulting polygon", 1, r.length);
+ assertEquals("result equal to original", r[0], p);
+ assertNotSame("a copy is returned", r[0], p);
+
+ // start - inner
+ r = triangulate(p, new Point(300, 200), new Point(200, 250));
+ assertEquals("1 resulting polygon", 1, r.length);
+ assertEquals("result equal to original", r[0], p);
+ assertNotSame("a copy is returned", r[0], p);
+
+ r = triangulate(p, new Point(200, 250), new Point(300, 200));
+ assertEquals("1 resulting polygon", 1, r.length);
+ assertEquals("result equal to original", r[0], p);
+ assertNotSame("a copy is returned", r[0], p);
+
+ // end - inner
+ r = triangulate(p, new Point(100, 300), new Point(200, 250));
+ assertEquals("1 resulting polygon", 1, r.length);
+ assertEquals("result equal to original", r[0], p);
+ assertNotSame("a copy is returned", r[0], p);
+
+ r = triangulate(p, new Point(200, 250), new Point(100, 300));
+ assertEquals("1 resulting polygon", 1, r.length);
+ assertEquals("result equal to original", r[0], p);
+ assertNotSame("a copy is returned", r[0], p);
+
+ // start - end
+ r = triangulate(p, new Point(100, 300), new Point(300, 200));
+ assertEquals("1 resulting polygon", 1, r.length);
+ assertEquals("result equal to original", r[0], p);
+ assertNotSame("a copy is returned", r[0], p);
+
+ r = triangulate(p, new Point(300, 200), new Point(100, 300));
+ assertEquals("1 resulting polygon", 1, r.length);
+ assertEquals("result equal to original", r[0], p);
+ assertNotSame("a copy is returned", r[0], p);
+ }
+
+ @Test
+ public void both_points_on_third_edge() {
+ // inner
+ Polygon[] r = triangulate(p, new Point(150, 125), new Point(250,
+ 175));
+ assertEquals("1 resulting polygon", 1, r.length);
+ assertEquals("result equal to original", r[0], p);
+ assertNotSame("a copy is returned", r[0], p);
+
+ r = triangulate(p, new Point(250, 175), new Point(150, 125));
+ assertEquals("1 resulting polygon", 1, r.length);
+ assertEquals("result equal to original", r[0], p);
+ assertNotSame("a copy is returned", r[0], p);
+
+ // start - inner
+ r = triangulate(p, new Point(100, 100), new Point(250, 175));
+ assertEquals("1 resulting polygon", 1, r.length);
+ assertEquals("result equal to original", r[0], p);
+ assertNotSame("a copy is returned", r[0], p);
+
+ r = triangulate(p, new Point(250, 175), new Point(100, 100));
+ assertEquals("1 resulting polygon", 1, r.length);
+ assertEquals("result equal to original", r[0], p);
+ assertNotSame("a copy is returned", r[0], p);
+
+ // end - inner
+ r = triangulate(p, new Point(150, 125), new Point(300, 200));
+ assertEquals("1 resulting polygon", 1, r.length);
+ assertEquals("result equal to original", r[0], p);
+ assertNotSame("a copy is returned", r[0], p);
+
+ r = triangulate(p, new Point(300, 200), new Point(150, 125));
+ assertEquals("1 resulting polygon", 1, r.length);
+ assertEquals("result equal to original", r[0], p);
+ assertNotSame("a copy is returned", r[0], p);
+
+ // start - end
+ r = triangulate(p, new Point(100, 100), new Point(300, 200));
+ assertEquals("1 resulting polygon", 1, r.length);
+ assertEquals("result equal to original", r[0], p);
+ assertNotSame("a copy is returned", r[0], p);
+
+ r = triangulate(p, new Point(300, 200), new Point(100, 100));
+ assertEquals("1 resulting polygon", 1, r.length);
+ assertEquals("result equal to original", r[0], p);
+ assertNotSame("a copy is returned", r[0], p);
+ }
+
+ private static boolean exists(Polygon[] list, Polygon item) {
+ for (Polygon p : list)
+ if (p.equals(item))
+ return true;
+ return false;
+ }
+
+ @Test
+ public void edge1_and_edge2() {
+ Polygon[] r = triangulate(p, new Point(100, 200), new Point(200,
+ 250));
+ assertEquals("3 resulting polygons", 3, r.length);
+ assertEquals(
+ "isolated-vertex polygon exists",
+ true,
+ exists(r, new Polygon(new Point(100, 300), new Point(100,
+ 200), new Point(200, 250))));
+ // TODO: test for existence of the other two pieces
+
+ r = triangulate(p, new Point(200, 250), new Point(100, 200));
+ assertEquals("3 resulting polygons", 3, r.length);
+ assertEquals(
+ "isolated-vertex polygon exists",
+ true,
+ exists(r, new Polygon(new Point(100, 300), new Point(100,
+ 200), new Point(200, 250))));
+ // TODO: test for existence of the other two pieces
+ }
+
+ @Test
+ public void edge1_and_edge3() {
+ Polygon[] r = triangulate(p, new Point(100, 200), new Point(200,
+ 150));
+ assertEquals("3 resulting polygons", 3, r.length);
+ assertEquals(
+ "isolated-vertex polygon exists",
+ true,
+ exists(r, new Polygon(new Point(100, 100), new Point(100,
+ 200), new Point(200, 150))));
+ // TODO: test for existence of the other two pieces
+
+ r = triangulate(p, new Point(200, 150), new Point(100, 200));
+ assertEquals("3 resulting polygons", 3, r.length);
+ assertEquals(
+ "isolated-vertex polygon exists",
+ true,
+ exists(r, new Polygon(new Point(100, 100), new Point(100,
+ 200), new Point(200, 150))));
+ // TODO: test for existence of the other two pieces
+ }
+
+ @Test
+ public void edge2_and_edge3() {
+ Polygon[] r = triangulate(p, new Point(200, 250), new Point(200,
+ 150));
+ assertEquals("3 resulting polygons", 3, r.length);
+ assertEquals(
+ "isolated-vertex polygon exists",
+ true,
+ exists(r, new Polygon(new Point(200, 250), new Point(200,
+ 150), new Point(300, 200))));
+ // TODO: test for existence of the other two pieces
+
+ r = triangulate(p, new Point(200, 150), new Point(200, 250));
+ assertEquals("3 resulting polygons", 3, r.length);
+ assertEquals(
+ "isolated-vertex polygon exists",
+ true,
+ exists(r, new Polygon(new Point(200, 250), new Point(200,
+ 150), new Point(300, 200))));
+ // TODO: test for existence of the other two pieces
+ }
+
+ @Test
+ public void edge1_vertex3() {
+ Polygon[] r = triangulate(p, new Point(100, 200), new Point(300,
+ 200));
+ assertEquals("2 resulting polygons", 2, r.length);
+ assertEquals("left-side polygon exists", true,
+ exists(r, new Polygon(100, 100, 100, 200, 300, 200)));
+ assertEquals("right-side polygon exists", true,
+ exists(r, new Polygon(100, 300, 100, 200, 300, 200)));
+
+ r = triangulate(p, new Point(300, 200), new Point(100, 200));
+ assertEquals("2 resulting polygons", 2, r.length);
+ assertEquals("left-side polygon exists", true,
+ exists(r, new Polygon(100, 100, 100, 200, 300, 200)));
+ assertEquals("right-side polygon exists", true,
+ exists(r, new Polygon(100, 300, 100, 200, 300, 200)));
+ }
+
+ @Test
+ public void edge2_vertex1() {
+ Polygon[] r = triangulate(p, new Point(200, 250), new Point(100,
+ 100));
+ assertEquals("2 resulting polygons", 2, r.length);
+ assertEquals("left-side polygon exists", true,
+ exists(r, new Polygon(100, 100, 200, 250, 100, 300)));
+ assertEquals("right-side polygon exists", true,
+ exists(r, new Polygon(100, 100, 200, 250, 300, 200)));
+
+ r = triangulate(p, new Point(100, 100), new Point(200, 250));
+ assertEquals("2 resulting polygons", 2, r.length);
+ assertEquals("left-side polygon exists", true,
+ exists(r, new Polygon(100, 100, 200, 250, 100, 300)));
+ assertEquals("right-side polygon exists", true,
+ exists(r, new Polygon(100, 100, 200, 250, 300, 200)));
+ }
+
+ @Test
+ public void edge3_vertex2() {
+ Polygon[] r = triangulate(p, new Point(200, 150), new Point(100,
+ 300));
+ assertEquals("2 resulting polygons", 2, r.length);
+ assertEquals("left-side polygon exists", true,
+ exists(r, new Polygon(200, 150, 100, 300, 100, 100)));
+ assertEquals("right-side polygon exists", true,
+ exists(r, new Polygon(200, 150, 100, 300, 300, 200)));
+
+ r = triangulate(p, new Point(100, 300), new Point(200, 150));
+ assertEquals("2 resulting polygons", 2, r.length);
+ assertEquals("left-side polygon exists", true,
+ exists(r, new Polygon(200, 150, 100, 300, 100, 100)));
+ assertEquals("right-side polygon exists", true,
+ exists(r, new Polygon(200, 150, 100, 300, 300, 200)));
+ }
+
+ }
+
+ /**
+ * <p>
+ * The {@link Ring#triangulate(Polygon, Line)} method is tested here.
+ * </p>
+ *
+ * <p>
+ * The test names indicate the various situations that are tested. Each
+ * situation comprises the location of the start and end point of the
+ * {@link Line} and the real and imaginary intersection {@link Point}s of
+ * the {@link Line} and the {@link Polygon}. Real {@link Point}s of
+ * intersection are the intersection {@link Point}s of the {@link Line} and
+ * the {@link Polygon}. Imaginary {@link Point}s of intersection do not lie
+ * on the {@link Line} but on its expansion to infinity in both directions.
+ * </p>
+ *
+ * <p>
+ * The first two characters indicate the location of the start and the end
+ * {@link Point} of the {@link Line} relative to the {@link Polygon}. 'o'
+ * means 'outside the polygon'. 'i' means 'inside the polygon'. 'e' means
+ * 'on an edge of the polygon'. 'v' means 'on a vertex of the polygon'.
+ * </p>
+ *
+ * <p>
+ * After that, number and type of expected intersections are stated. Real
+ * intersection {@link Point}s ('r' for 'real') are named before the
+ * imaginary ('i' for 'imaginary') intersections. The characters after 'r'
+ * or 'i' define the type of intersection. 'v' means 'the intersection point
+ * is a vertex of the polygon'. 'e' means 'the intersection point is on an
+ * edge of the polygon'.
+ * </p>
+ *
+ * <p>
+ * The postfix indicates the expected number of resulting {@link Polygon}s.
+ * 'ntd' means 'nothing to do' and therefore, it appears when a copy of the
+ * original {@link Polygon} is expected as the result. Otherwise, 's' is
+ * followed by the number of results. 'overlaps_edge' means that a
+ * {@link Polygon}s edge is overlapped by the {@link Line}.
+ * </p>
+ */
+ public static class TriangulateTriangleWithLine {
+
+ private Polygon[] triangulate(Polygon p, Line s) {
+ try {
+ Class<?> parameterTypes[] = new Class<?>[] { Polygon.class,
+ Line.class };
+ Method triangulate = Ring.class.getDeclaredMethod(
+ "triangulate", parameterTypes);
+ triangulate.setAccessible(true);
+ return (Polygon[]) triangulate.invoke(null, p, s);
+ } catch (Exception x) {
+ throw new IllegalStateException(x);
+ }
+ }
+
+ Polygon p;
+
+ @Before
+ public void setUp() {
+ p = new Polygon(new Point(100, 100), new Point(100, 300),
+ new Point(300, 200));
+ }
+
+ @Test
+ public void no_polygon() {
+ try {
+ triangulate(null, new Line(1, 2, 3, 4));
+ } catch (IllegalStateException x) {
+ Throwable cause = x;
+ while (cause.getCause() != null)
+ cause = cause.getCause();
+
+ assertTrue(cause.getClass().equals(
+ IllegalArgumentException.class));
+ }
+ }
+
+ @Test
+ public void no_line() {
+ try {
+ triangulate(p, null);
+ } catch (IllegalStateException x) {
+ Throwable cause = x;
+ while (cause.getCause() != null)
+ cause = cause.getCause();
+
+ assertTrue(cause.getClass().equals(
+ IllegalArgumentException.class));
+ }
+ }
+
+ @Test
+ public void triangulate_oo_ntd() {
+ // p1 outside, p2 outside, nothing to do
+ Polygon[] r = triangulate(p, new Line(new Point(0, 0), new Point(
+ 400, 0)));
+ assertEquals("nothing to do", 1, r.length);
+ assertEquals("polygon remains the same", p, r[0]);
+ }
+
+ @Test
+ public void triangulate_oo1rv_ntd() {
+ // p1 outside, p2 outside, 1 real intersection (vertex), nothing to
+ // do
+ Polygon[] r = triangulate(p, new Line(new Point(0, 100), new Point(
+ 200, 100)));
+ assertEquals("nothing to do", 1, r.length);
+ assertEquals("polygon remains the same", p, r[0]);
+
+ r = triangulate(p, new Line(new Point(0, 300), new Point(200, 300)));
+ assertEquals("nothing to do", 1, r.length);
+ assertEquals("polygon remains the same", p, r[0]);
+
+ r = triangulate(p, new Line(new Point(300, 100),
+ new Point(300, 300)));
+ assertEquals("nothing to do", 1, r.length);
+ assertEquals("polygon remains the same", p, r[0]);
+ }
+
+ @Test
+ public void triangulate_oo_overlaps_edge_ntd() {
+ // p1 outside, p2 outside, overlaps edge, nothing to do
+ Polygon[] r = triangulate(p, new Line(new Point(0, 50), new Point(
+ 400, 250)));
+ assertEquals("nothing to do", 1, r.length);
+ assertEquals("polygon remains the same", p, r[0]);
+
+ r = triangulate(p, new Line(new Point(0, 350), new Point(400, 150)));
+ assertEquals("nothing to do", 1, r.length);
+ assertEquals("polygon remains the same", p, r[0]);
+
+ r = triangulate(p,
+ new Line(new Point(100, 50), new Point(100, 350)));
+ assertEquals("nothing to do", 1, r.length);
+ assertEquals("polygon remains the same", p, r[0]);
+ }
+
+ @Test
+ public void triangulate_vv_overlaps_edge_ntd() {
+ // p1 on vertex, p2 on vertex, overlaps edge, nothing to do
+ Polygon[] r = triangulate(p, new Line(new Point(100, 100),
+ new Point(100, 300)));
+ assertEquals("nothing to do", 1, r.length);
+ assertEquals("polygon remains the same", p, r[0]);
+
+ r = triangulate(p, new Line(new Point(100, 100),
+ new Point(300, 200)));
+ assertEquals("nothing to do", 1, r.length);
+ assertEquals("polygon remains the same", p, r[0]);
+
+ r = triangulate(p, new Line(new Point(300, 200),
+ new Point(100, 300)));
+ assertEquals("nothing to do", 1, r.length);
+ assertEquals("polygon remains the same", p, r[0]);
+ }
+
+ @Test
+ public void triangulate_ee_overlaps_edge_ntd() {
+ // p1 on edge, p2 on edge, overlaps edge, nothing to do
+ Polygon[] r = triangulate(p, new Line(new Point(100, 150),
+ new Point(100, 250)));
+ assertEquals("nothing to do", 1, r.length);
+ assertEquals("polygon remains the same", p, r[0]);
+
+ r = triangulate(p, new Line(new Point(150, 125),
+ new Point(250, 175)));
+ assertEquals("nothing to do", 1, r.length);
+ assertEquals("polygon remains the same", p, r[0]);
+
+ r = triangulate(p, new Line(new Point(150, 275),
+ new Point(250, 225)));
+ assertEquals("nothing to do", 1, r.length);
+ assertEquals("polygon remains the same", p, r[0]);
+ }
+
+ @Test
+ public void triangulate_ev_overlaps_edge_ntd() {
+ // p1 on edge, p2 on vertex, overlaps edge, nothing to do
+ Polygon[] r = triangulate(p, new Line(new Point(100, 200),
+ new Point(100, 100)));
+ assertEquals("nothing to do", 1, r.length);
+ assertEquals("polygon remains the same", p, r[0]);
+
+ r = triangulate(p, new Line(new Point(150, 125),
+ new Point(100, 100)));
+ assertEquals("nothing to do", 1, r.length);
+ assertEquals("polygon remains the same", p, r[0]);
+
+ r = triangulate(p, new Line(new Point(150, 125),
+ new Point(300, 200)));
+ assertEquals("nothing to do", 1, r.length);
+ assertEquals("polygon remains the same", p, r[0]);
+
+ r = triangulate(p, new Line(new Point(150, 275),
+ new Point(300, 200)));
+ assertEquals("nothing to do", 1, r.length);
+ assertEquals("polygon remains the same", p, r[0]);
+
+ r = triangulate(p, new Line(new Point(150, 275),
+ new Point(100, 300)));
+ assertEquals("nothing to do", 1, r.length);
+ assertEquals("polygon remains the same", p, r[0]);
+
+ r = triangulate(p, new Line(new Point(100, 200),
+ new Point(100, 300)));
+ assertEquals("nothing to do", 1, r.length);
+ assertEquals("polygon remains the same", p, r[0]);
+ }
+
+ @Test
+ public void triangulate_ve_overlaps_edge_ntd() {
+ // p1 on vertex, p2 on edge, overlaps edge, nothing to do
+ Polygon[] r = triangulate(p, new Line(new Point(100, 100),
+ new Point(100, 200)));
+ assertEquals("nothing to do", 1, r.length);
+ assertEquals("polygon remains the same", p, r[0]);
+
+ r = triangulate(p, new Line(new Point(100, 100),
+ new Point(150, 125)));
+ assertEquals("nothing to do", 1, r.length);
+ assertEquals("polygon remains the same", p, r[0]);
+
+ r = triangulate(p, new Line(new Point(300, 200),
+ new Point(150, 125)));
+ assertEquals("nothing to do", 1, r.length);
+ assertEquals("polygon remains the same", p, r[0]);
+
+ r = triangulate(p, new Line(new Point(300, 200),
+ new Point(150, 275)));
+ assertEquals("nothing to do", 1, r.length);
+ assertEquals("polygon remains the same", p, r[0]);
+
+ r = triangulate(p, new Line(new Point(100, 300),
+ new Point(150, 275)));
+ assertEquals("nothing to do", 1, r.length);
+ assertEquals("polygon remains the same", p, r[0]);
+
+ r = triangulate(p, new Line(new Point(100, 300),
+ new Point(100, 200)));
+ assertEquals("nothing to do", 1, r.length);
+ assertEquals("polygon remains the same", p, r[0]);
+ }
+
+ @Test
+ public void triangulate_vo1rv_ntd() {
+ // p1 on vertex, p2 outside, 1 real intersection (vertex), nothing
+ // to do
+ Polygon[] r = triangulate(p, new Line(new Point(100, 100),
+ new Point(200, 100)));
+ assertEquals("nothing to do", 1, r.length);
+ assertEquals("polygon remains the same", p, r[0]);
+
+ r = triangulate(p, new Line(new Point(100, 300),
+ new Point(200, 300)));
+ assertEquals("nothing to do", 1, r.length);
+ assertEquals("polygon remains the same", p, r[0]);
+
+ r = triangulate(p, new Line(new Point(300, 200),
+ new Point(300, 300)));
+ assertEquals("nothing to do", 1, r.length);
+ assertEquals("polygon remains the same", p, r[0]);
+ }
+
+ @Test
+ public void triangulate_eo1re_ntd() {
+ // p1 on edge, p2 outside, 1 real intersection (edge), nothing to do
+ Polygon[] r = triangulate(p, new Line(new Point(100, 200),
+ new Point(100, 0)));
+ assertEquals("nothing to do", 1, r.length);
+ assertEquals("polygon remains the same", p, r[0]);
+
+ r = triangulate(p, new Line(new Point(200, 150), new Point(200, 0)));
+ assertEquals("nothing to do", 1, r.length);
+ assertEquals("polygon remains the same", p, r[0]);
+
+ r = triangulate(p, new Line(new Point(200, 250),
+ new Point(200, 300)));
+ assertEquals("nothing to do", 1, r.length);
+ assertEquals("polygon remains the same", p, r[0]);
+ }
+
+ @Test
+ public void triangulate_ov1rv_ntd() {
+ // p1 outside, p2 on vertex, 1 real intersection (vertex), nothing
+ // to do
+ Polygon[] r = triangulate(p, new Line(new Point(200, 100),
+ new Point(100, 100)));
+ assertEquals("nothing to do", 1, r.length);
+ assertEquals("polygon remains the same", p, r[0]);
+
+ r = triangulate(p, new Line(new Point(200, 300),
+ new Point(100, 300)));
+ assertEquals("nothing to do", 1, r.length);
+ assertEquals("polygon remains the same", p, r[0]);
+
+ r = triangulate(p, new Line(new Point(300, 300),
+ new Point(300, 200)));
+ assertEquals("nothing to do", 1, r.length);
+ assertEquals("polygon remains the same", p, r[0]);
+ }
+
+ @Test
+ public void triangulate_oe1re_ntd() {
+ // p1 outside, p2 on edge, 1 real intersection (edge), nothing to do
+ Polygon[] r = triangulate(p, new Line(new Point(100, 0), new Point(
+ 100, 200)));
+ assertEquals("nothing to do", 1, r.length);
+ assertEquals("polygon remains the same", p, r[0]);
+
+ r = triangulate(p, new Line(new Point(200, 0), new Point(200, 150)));
+ assertEquals("nothing to do", 1, r.length);
+ assertEquals("polygon remains the same", p, r[0]);
+
+ r = triangulate(p, new Line(new Point(200, 300),
+ new Point(200, 250)));
+ assertEquals("nothing to do", 1, r.length);
+ assertEquals("polygon remains the same", p, r[0]);
+ }
+
+ @Test
+ public void triangulate_oo2ree_s3() {
+ // p1 outside, p2 outside, 2 real intersections (edge, edge), split
+ // into
+ // 3 pieces
+ Polygon[] r = triangulate(p, new Line(new Point(200, 100),
+ new Point(200, 300)));
+ assertEquals("split into three", 3, r.length);
+ // TODO: verify that the created three polygons are those you wanted
+ // to
+ // get back
+
+ r = triangulate(p,
+ new Line(new Point(50, 150), new Point(250, 300)));
+ assertEquals("split into three", 3, r.length);
+
+ r = triangulate(p,
+ new Line(new Point(50, 250), new Point(250, 100)));
+ assertEquals("split into three", 3, r.length);
+ }
+
+ @Test
+ public void triangulate_oo2rve_s2() {
+ // p1 outside, p2 outside, 2 real intersections (vertex, edge),
+ // split into 2 pieces
+ Polygon[] r = triangulate(p, new Line(new Point(50, 200),
+ new Point(350, 200)));
+ assertEquals("split into two", 2, r.length);
+
+ r = triangulate(p, new Line(new Point(50, 50), new Point(300, 300)));
+ assertEquals("split into two", 2, r.length);
+
+ r = triangulate(p, new Line(new Point(50, 350), new Point(350, 50)));
+ assertEquals("split into two", 2, r.length);
+ }
+
+ @Test
+ public void triangulate_io1re1ie_s3() {
+ // p1 inside, p2 outside, 1 real intersection (edge), 1 imaginary
+ // intersection (edge), split into 3 pieces
+ Polygon[] r = triangulate(p, new Line(new Point(150, 200),
+ new Point(150, 50)));
+ assertEquals("split into three", 3, r.length);
+
+ r = triangulate(p, new Line(new Point(150, 200),
+ new Point(250, 100)));
+ assertEquals("split into three", 3, r.length);
+
+ r = triangulate(p, new Line(new Point(150, 200),
+ new Point(150, 350)));
+ assertEquals("split into three", 3, r.length);
+
+ r = triangulate(p, new Line(new Point(150, 200),
+ new Point(250, 300)));
+ assertEquals("split into three", 3, r.length);
+
+ r = triangulate(p,
+ new Line(new Point(150, 200), new Point(50, 300)));
+ assertEquals("split into three", 3, r.length);
+
+ r = triangulate(p,
+ new Line(new Point(150, 200), new Point(50, 100)));
+ assertEquals("split into three", 3, r.length);
+ }
+
+ @Test
+ public void triangulate_io1re1iv_s2() {
+ // p1 inside, p2 outside, 1 real intersection (edge), 1 imaginary
+ // intersection (vertex), split into 2 pieces
+ Polygon[] r = triangulate(p, new Line(new Point(150, 200),
+ new Point(200, 100)));
+ assertEquals("split into two", 2, r.length);
+
+ r = triangulate(p, new Line(new Point(150, 200),
+ new Point(200, 300)));
+ assertEquals("split into two", 2, r.length);
+
+ r = triangulate(p,
+ new Line(new Point(150, 200), new Point(50, 200)));
+ assertEquals("split into two", 2, r.length);
+ }
+
+ @Test
+ public void triangulate_io1rv1ie_s2() {
+ // p1 inside, p2 outside, 1 real intersection (vertex), 1 imaginary
+ // intersection (edge), split into 2 pieces
+ Polygon[] r = triangulate(p, new Line(new Point(150, 200),
+ new Point(50, 400)));
+ assertEquals("split into two", 2, r.length);
+
+ r = triangulate(p, new Line(new Point(150, 200), new Point(50, 0)));
+ assertEquals("split into two", 2, r.length);
+
+ r = triangulate(p, new Line(new Point(150, 200),
+ new Point(400, 200)));
+ assertEquals("split into two", 2, r.length);
+ }
+
+ @Test
+ public void triangulate_oi1re1ie_s3() {
+ // p1 outside, p2 inside, 1 real intersection (edge), 1 imaginary
+ // intersection (edge), split into 3 pieces
+ Polygon[] r = triangulate(p, new Line(new Point(150, 50),
+ new Point(150, 200)));
+ assertEquals("split into three", 3, r.length);
+
+ r = triangulate(p, new Line(new Point(250, 100),
+ new Point(150, 200)));
+ assertEquals("split into three", 3, r.length);
+
+ r = triangulate(p, new Line(new Point(150, 350),
+ new Point(150, 200)));
+ assertEquals("split into three", 3, r.length);
+
+ r = triangulate(p, new Line(new Point(250, 300),
+ new Point(150, 200)));
+ assertEquals("split into three", 3, r.length);
+
+ r = triangulate(p,
+ new Line(new Point(50, 300), new Point(150, 200)));
+ assertEquals("split into three", 3, r.length);
+
+ r = triangulate(p,
+ new Line(new Point(50, 100), new Point(150, 200)));
+ assertEquals("split into three", 3, r.length);
+ }
+
+ @Test
+ public void triangulate_oi1re1iv_s2() {
+ // p1 outside, p2 inside, 1 real intersection (edge), 1 imaginary
+ // intersection (vertex), split into 2 pieces
+ Polygon[] r = triangulate(p, new Line(new Point(200, 100),
+ new Point(150, 200)));
+ assertEquals("split into two", 2, r.length);
+
+ r = triangulate(p, new Line(new Point(200, 300),
+ new Point(150, 200)));
+ assertEquals("split into two", 2, r.length);
+
+ r = triangulate(p,
+ new Line(new Point(50, 200), new Point(150, 200)));
+ assertEquals("split into two", 2, r.length);
+ }
+
+ @Test
+ public void triangulate_oi1rv1ie_s2() {
+ // p1 outside, p2 inside, 1 real intersection (vertex), 1 imaginary
+ // intersection (edge), split into 2 pieces
+ Polygon[] r = triangulate(p, new Line(new Point(50, 400),
+ new Point(150, 200)));
+ assertEquals("split into two", 2, r.length);
+
+ r = triangulate(p, new Line(new Point(50, 0), new Point(150, 200)));
+ assertEquals("split into two", 2, r.length);
+
+ r = triangulate(p, new Line(new Point(400, 200),
+ new Point(150, 200)));
+ assertEquals("split into two", 2, r.length);
+ }
+
+ @Test
+ public void triangulate_ee2ree_s3() {
+ // p1 on edge, p2 on edge, 2 real intersections (edge, edge), split
+ // into
+ // 3 pieces
+ Polygon[] r = triangulate(p, new Line(100, 200, 200, 150));
+ assertEquals("split into three", 3, r.length);
+
+ r = triangulate(p, new Line(100, 200, 200, 250));
+ assertEquals("split into three", 3, r.length);
+
+ r = triangulate(p, new Line(200, 250, 200, 150));
+ assertEquals("split into three", 3, r.length);
+
+ // swap start and end point
+ r = triangulate(p, new Line(200, 150, 100, 200));
+ assertEquals("split into three", 3, r.length);
+
+ r = triangulate(p, new Line(200, 250, 100, 200));
+ assertEquals("split into three", 3, r.length);
+
+ r = triangulate(p, new Line(200, 150, 200, 250));
+ assertEquals("split into three", 3, r.length);
+ }
+
+ @Test
+ public void triangulate_ve2rve_s2() {
+ // p1 on vertex, p2 on edge, 2 real intersections (vertex, edge),
+ // split
+ // into 2 pieces
+ Polygon[] r = triangulate(p, new Line(100, 100, 200, 250));
+ assertEquals("split into two", 2, r.length);
+
+ r = triangulate(p, new Line(300, 200, 100, 200));
+ assertEquals("split into two", 2, r.length);
+
+ r = triangulate(p, new Line(100, 300, 200, 150));
+ assertEquals("split into two", 2, r.length);
+ }
+
+ @Test
+ public void triangulate_ev2rve_s2() {
+ // p1 on edge, p2 on vertex, 2 real intersections (vertex, edge),
+ // split
+ // into 2 pieces
+ Polygon[] r = triangulate(p, new Line(200, 250, 100, 100));
+ assertEquals("split into two", 2, r.length);
+
+ r = triangulate(p, new Line(100, 200, 300, 200));
+ assertEquals("split into two", 2, r.length);
+
+ r = triangulate(p, new Line(200, 150, 100, 300));
+ assertEquals("split into two", 2, r.length);
+ }
+
+ @Test
+ public void triangulate_ei1re1ie_s3() {
+ // p1 on edge, p2 inside, 1 real intersection (edge), 1 imaginary
+ // intersection (edge), split into 3 pieces
+ Polygon[] r = triangulate(p, new Line(100, 200, 150, 175));
+ assertEquals("split into three", 3, r.length);
+
+ r = triangulate(p, new Line(100, 200, 150, 225));
+ assertEquals("split into three", 3, r.length);
+
+ r = triangulate(p, new Line(200, 250, 200, 200));
+ assertEquals("split into three", 3, r.length);
+ }
+
+ @Test
+ public void triangulate_ie1re1ie_s3() {
+ // p1 inside, p2 on edge, 1 real intersection (edge), 1 imaginary
+ // intersection (edge), split into 3 pieces
+ Polygon[] r = triangulate(p, new Line(150, 175, 100, 200));
+ assertEquals("split into three", 3, r.length);
+
+ r = triangulate(p, new Line(150, 225, 100, 200));
+ assertEquals("split into three", 3, r.length);
+
+ r = triangulate(p, new Line(200, 200, 200, 250));
+ assertEquals("split into three", 3, r.length);
+ }
+
+ @Test
+ public void triangulate_vi1rv1ie_s2() {
+ // p1 on vertex, p2 inside, 1 real intersection (vertex), 1
+ // imaginary
+ // intersection (edge), split into 2 pieces
+ Polygon[] r = triangulate(p, new Line(100, 100, 200, 250));
+ assertEquals("split into two", 2, r.length);
+
+ r = triangulate(p, new Line(300, 200, 100, 200));
+ assertEquals("split into two", 2, r.length);
+
+ r = triangulate(p, new Line(100, 300, 200, 150));
+ assertEquals("split into two", 2, r.length);
+ }
+
+ @Test
+ public void triangulate_iv1rv1ie_s2() {
+ // p1 inside, p2 on vertex, 1 real intersection (vertex), 1
+ // imaginary
+ // intersection (edge), split into 2 pieces
+ Polygon[] r = triangulate(p, new Line(200, 250, 100, 100));
+ assertEquals("split into two", 2, r.length);
+
+ r = triangulate(p, new Line(100, 200, 300, 200));
+ assertEquals("split into two", 2, r.length);
+
+ r = triangulate(p, new Line(200, 150, 100, 300));
+ assertEquals("split into two", 2, r.length);
+ }
+
+ @Test
+ public void triangulate_ei1re1iv_s2() {
+ // p1 on edge, p2 inside, 1 real intersection (edge), 1 imaginary
+ // intersection (vertex), split into 2 pieces
+ Polygon[] r = triangulate(p, new Line(100, 200, 200, 200));
+ assertEquals("split into two", 2, r.length);
+
+ r = triangulate(p, new Line(200, 150, 150, 225));
+ assertEquals("split into two", 2, r.length);
+
+ r = triangulate(p, new Line(200, 250, 150, 175));
+ assertEquals("split into two", 2, r.length);
+ }
+
+ @Test
+ public void triangulate_ie1re1iv_s2() {
+ // p1 inside, p2 on edge, 1 real intersection (edge), 1 imaginary
+ // intersection (vertex), split into 2 pieces
+ Polygon[] r = triangulate(p, new Line(200, 200, 100, 200));
+ assertEquals("split into two", 2, r.length);
+
+ r = triangulate(p, new Line(150, 225, 200, 150));
+ assertEquals("split into two", 2, r.length);
+
+ r = triangulate(p, new Line(150, 175, 200, 250));
+ assertEquals("split into two", 2, r.length);
+ }
+
+ @Test
+ public void triangulate_ii2iee_s3() {
+ // p1 inside, p2 inside, 2 imaginary intersections (edge, edge),
+ // split
+ // into 3 pieces
+ Polygon[] r = triangulate(p, new Line(125, 200, 150, 175));
+ assertEquals("split into three", 3, r.length);
+
+ r = triangulate(p, new Line(200, 175, 200, 225));
+ assertEquals("split into three", 3, r.length);
+
+ r = triangulate(p, new Line(125, 200, 150, 225));
+ assertEquals("split into three", 3, r.length);
+
+ r = triangulate(p, new Line(150, 175, 125, 200));
+ assertEquals("split into three", 3, r.length);
+
+ r = triangulate(p, new Line(200, 225, 200, 175));
+ assertEquals("split into three", 3, r.length);
+
+ r = triangulate(p, new Line(150, 225, 125, 200));
+ assertEquals("split into three", 3, r.length);
+ }
+
+ @Test
+ public void triangulate_ii2iev_s2() {
+ // p1 inside, p2 inside, 2 imaginary intersections (edge, vertex),
+ // split
+ // into 2 pieces
+ Polygon[] r = triangulate(p, new Line(150, 200, 125, 150));
+ assertEquals("split into two", 2, r.length);
+
+ r = triangulate(p, new Line(150, 200, 200, 200));
+ assertEquals("split into two", 2, r.length);
+
+ r = triangulate(p, new Line(150, 200, 125, 250));
+ assertEquals("split into two", 2, r.length);
+
+ r = triangulate(p, new Line(125, 150, 150, 200));
+ assertEquals("split into two", 2, r.length);
+
+ r = triangulate(p, new Line(200, 200, 150, 200));
+ assertEquals("split into two", 2, r.length);
+
+ r = triangulate(p, new Line(125, 250, 150, 200));
+ assertEquals("split into two", 2, r.length);
+ }
+
+ @Test
+ public void triangulate_precision_error() {
+ Polygon t = new Polygon(100.0, 100.0, 371.1146624051138,
+ 197.80263683579705, 370.0, 189.99999999999997);
+ Line l = new Line(370.0, 190.0, 400.0, 400.0);
+
+ // throws an exception if it fails
+ triangulate(t, l);
+ }
+
+ }
+
+ public static class ContainmentTests {
+
+ @Test
+ public void cover_single_polygon() {
+ Polygon p1 = new Polygon(1, 2, 1, 3, 2, 4, 3, 4, 4, 3, 4, 2, 3, 1,
+ 2, 1);
+ Ring ring = new Ring(p1);
+
+ assertFalse(ring.contains(new Polygon(0, 0, 1, 0, 1, 1, 0, 1)));
+ assertFalse(ring.contains(new Polygon(1, 1, 3, 1, 2, 2)));
+ assertTrue(ring.contains(new Polygon(2, 2, 2, 3, 3, 3, 3, 2)));
+ assertTrue(ring.contains(p1));
+ }
+
+ @Test
+ public void cover_two_distinct_polygons() {
+ Polygon p1 = new Polygon(1, 2, 1, 3, 2, 4, 3, 4, 4, 3, 4, 2, 3, 1,
+ 2, 1);
+ Polygon p2 = new Polygon(4, 4, 4, 5, 5, 5, 5, 4);
+ Ring ring = new Ring(p1, p2);
+
+ assertFalse(ring.contains(new Polygon(0, 0, 1, 0, 1, 1, 0, 1)));
+ assertFalse(ring.contains(new Polygon(1, 1, 3, 1, 2, 2)));
+ assertFalse(ring.contains(new Polygon(4.5, 4.5, 4.5, 5.5, 5.5, 5.5,
+ 5.5, 4.5)));
+ assertFalse(ring.contains(new Polygon(3, 3, 5, 3, 5, 5)));
+ assertTrue(ring.contains(new Polygon(2, 2, 2, 3, 3, 3, 3, 2)));
+ assertTrue(ring.contains(new Polygon(4.1, 4.1, 4.9, 4.1, 4.9, 4.9,
+ 4.1, 4.9)));
+ assertTrue(ring.contains(p1));
+ assertTrue(ring.contains(p2));
+ }
+
+ @Test
+ public void cover_two_intersecting_polygons() {
+ Polygon p1 = new Polygon(1, 2, 1, 3, 2, 4, 3, 4, 4, 3, 4, 2, 3, 1,
+ 2, 1);
+ Polygon p2 = new Polygon(2.5, 2.5, 2.5, 5, 5, 5, 5, 2.5);
+ Ring ring = new Ring(p1, p2);
+
+ assertFalse(ring.contains(new Polygon(0, 0, 1, 0, 1, 1, 0, 1)));
+ assertFalse(ring.contains(new Polygon(1, 1, 3, 1, 2, 2)));
+ assertFalse(ring.contains(new Polygon(4.5, 4.5, 4.5, 5.5, 5.5, 5.5,
+ 5.5, 4.5)));
+ assertTrue(ring.contains(new Polygon(2, 2, 2, 3, 3, 3, 3, 2)));
+ assertTrue(ring.contains(new Polygon(3, 3, 5, 3, 5, 5)));
+ assertTrue(ring.contains(p1));
+ assertTrue(ring.contains(p2));
+ assertTrue(ring.contains(new Polygon(2, 2, 2, 3, 3, 3, 3, 2)));
+ }
+
+ }
+
+}
diff --git a/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/RoundedRectangleTests.java b/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/RoundedRectangleTests.java
new file mode 100644
index 0000000..5586fc5
--- /dev/null
+++ b/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/RoundedRectangleTests.java
@@ -0,0 +1,214 @@
+/*******************************************************************************
+ * Copyright (c) 2012 itemis AG and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Matthias Wienand (itemis AG) - initial API and implementation
+ *
+ *******************************************************************************/
+package org.eclipse.gef4.geometry.tests;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.eclipse.gef4.geometry.Angle;
+import org.eclipse.gef4.geometry.Point;
+import org.eclipse.gef4.geometry.planar.Arc;
+import org.eclipse.gef4.geometry.planar.ICurve;
+import org.eclipse.gef4.geometry.planar.IGeometry;
+import org.eclipse.gef4.geometry.planar.Line;
+import org.eclipse.gef4.geometry.planar.Rectangle;
+import org.eclipse.gef4.geometry.planar.RoundedRectangle;
+import org.eclipse.gef4.geometry.utils.PrecisionUtils;
+import org.junit.Before;
+import org.junit.Test;
+
+public class RoundedRectangleTests {
+
+ double x = 1, y = 2, w = 5, h = 6, aw = 1, ah = 2;
+ RoundedRectangle rr;
+
+ @Before
+ public void setUp() {
+ rr = new RoundedRectangle(x, y, w, h, aw, ah);
+ }
+
+ @Test
+ public void test_equals() {
+ assertEquals(rr,
+ new RoundedRectangle(new Rectangle(x, y, w, h), aw, ah));
+ assertEquals(rr, rr.getCopy());
+ assertFalse(rr.equals(null));
+ assertFalse(rr.equals(new Point()));
+
+ assertFalse(rr.equals(new RoundedRectangle(x + 10, y, w, h, aw, ah)));
+ assertFalse(rr.equals(new RoundedRectangle(x, y + 10, w, h, aw, ah)));
+ assertFalse(rr.equals(new RoundedRectangle(x, y, w + 10, h, aw, ah)));
+ assertFalse(rr.equals(new RoundedRectangle(x, y, w, h + 10, aw, ah)));
+ assertFalse(rr.equals(new RoundedRectangle(x, y, w, h, aw + 10, ah)));
+ assertFalse(rr.equals(new RoundedRectangle(x, y, w, h, aw, ah + 10)));
+ }
+
+ @Test
+ public void test_getters() {
+ check_values_with_getters(rr, x, y, w, h, aw, ah);
+ }
+
+ @Test
+ public void test_setters() {
+ // TODO: change values and test if the changes are applied correctly
+ RoundedRectangle rrCopy = rr.getCopy();
+
+ double nx = 9, ny = 8, nw = 7, nh = 6, naw = 5, nah = 4;
+
+ rrCopy.setX(nx);
+ rrCopy.setY(ny);
+ rrCopy.setWidth(nw);
+ rrCopy.setHeight(nh);
+ rrCopy.setArcWidth(naw);
+ rrCopy.setArcHeight(nah);
+
+ check_values_with_getters(rrCopy, nx, ny, nw, nh, naw, nah);
+ check_values_with_getters(rr, x, y, w, h, aw, ah);
+ }
+
+ @Test
+ public void test_contains_Point() {
+ check_Point_containment(rr);
+ }
+
+ @Test
+ public void test_toPath() {
+ check_Point_containment(rr.toPath());
+ }
+
+ @Test
+ public void test_getOutlineSegments() {
+ ICurve[] outlineSegments = rr.getOutlineSegments();
+ assertEquals(8, outlineSegments.length);
+
+ // consecutive
+ for (int i = 0; i < 7; i++)
+ assertEquals(outlineSegments[i].getP2(),
+ outlineSegments[i + 1].getP1());
+ assertEquals(outlineSegments[7].getP2(), outlineSegments[0].getP1());
+
+ // position
+ assertEquals(new Point(x + w, y + ah), outlineSegments[0].getP1());
+ assertEquals(new Point(x + w - aw, y), outlineSegments[1].getP1());
+ assertEquals(new Point(x + aw, y), outlineSegments[2].getP1());
+ assertEquals(new Point(x, y + ah), outlineSegments[3].getP1());
+ assertEquals(new Point(x, y + h - ah), outlineSegments[4].getP1());
+ assertEquals(new Point(x + aw, y + h), outlineSegments[5].getP1());
+ assertEquals(new Point(x + w - aw, y + h), outlineSegments[6].getP1());
+ assertEquals(new Point(x + w, y + h - ah), outlineSegments[7].getP1());
+ }
+
+ @Test
+ public void test_toString() {
+ assertEquals("RoundedRectangle(" + x + ", " + y + ", " + w + ", " + h
+ + ", " + aw + ", " + ah + ")", rr.toString());
+ }
+
+ @Test
+ public void test_getOutline() {
+ // coherence with getOutlineSegments
+ ICurve[] outlineSegments = rr.getOutlineSegments();
+ ICurve[] outlineCurves = rr.getOutline().getCurves();
+ assertEquals(outlineSegments.length, outlineCurves.length);
+ for (int i = 0; i < 8; i++)
+ assertEquals(outlineSegments[i], outlineCurves[i]);
+ }
+
+ @Test
+ public void test_contains_shape() {
+ // translate it by some values and test that the translated versions are
+ // not contained
+ for (double tx : new double[] { -1, 1 })
+ for (double ty : new double[] { -1, 1 })
+ assertFalse(rr.contains(rr.getTranslated(tx, ty)));
+
+ // scale it down by some values and test that the smaller versions are
+ // contained
+ for (double s = 1; s > 0; s -= 0.1)
+ assertTrue(rr.contains(rr.getScaled(s)));
+
+ // scale it up by some values and test that the greater versions are not
+ // contained
+ for (double s = 1.1; s < 2; s += 0.1)
+ assertFalse(rr.contains(rr.getScaled(s)));
+ }
+
+ private void check_Point_containment(IGeometry g) {
+ assertTrue(g.contains(new Point(3.5, 5)));
+ assertTrue(g.contains(new Point(1.5, 5)));
+ assertTrue(g.contains(new Point(1, 5)));
+ assertTrue(g.contains(new Point(5.5, 5)));
+ // TODO: next test is commented out because the AWT Path does not
+ // recognize points on the right and bottom sides
+ // assertTrue(g.contains(new Point(6, 5)));
+ assertTrue(g.contains(new Point(3.5, 2.5)));
+ assertTrue(g.contains(new Point(3.5, 2)));
+ assertTrue(g.contains(new Point(3.5, 7.5)));
+ // TODO: next test is commented out because the AWT Path does not
+ // recognize points on the right and bottom sides
+ // assertTrue(g.contains(new Point(3.5, 8)));
+ assertTrue(g.contains(new Point(1.5, 3.5)));
+ assertTrue(g.contains(new Point(5.5, 3.5)));
+ assertTrue(g.contains(new Point(1.5, 6.5)));
+ assertTrue(g.contains(new Point(5.5, 6.5)));
+ assertFalse(g.contains(new Point(0, 0)));
+ assertFalse(g.contains(new Point(4, 0)));
+ assertFalse(g.contains(new Point(7, 0)));
+ assertFalse(g.contains(new Point(0, 5)));
+ assertFalse(g.contains(new Point(7, 5)));
+ assertFalse(g.contains(new Point(0, 9)));
+ assertFalse(g.contains(new Point(4, 9)));
+ assertFalse(g.contains(new Point(7, 9)));
+ assertFalse(g.contains(new Point(1, 2)));
+ assertFalse(g.contains(new Point(6, 2)));
+ assertFalse(g.contains(new Point(1, 8)));
+ assertFalse(g.contains(new Point(6, 8)));
+ }
+
+ private void check_values_with_getters(RoundedRectangle r, double px,
+ double py, double pw, double ph, double paw, double pah) {
+ assertTrue(PrecisionUtils.equal(px, r.getX()));
+ assertTrue(PrecisionUtils.equal(py, r.getY()));
+ assertTrue(PrecisionUtils.equal(pw, r.getWidth()));
+ assertTrue(PrecisionUtils.equal(ph, r.getHeight()));
+ assertTrue(PrecisionUtils.equal(paw, r.getArcWidth()));
+ assertTrue(PrecisionUtils.equal(pah, r.getArcHeight()));
+ assertEquals(new Point(px, py), r.getLocation());
+ assertEquals(new Rectangle(px, py, pw, ph), r.getBounds());
+
+ // generated arcs have double width and height as specified so that the
+ // underlying ellipse fits into the respective rectangle
+ assertEquals(
+ new Arc(px + pw - 2 * paw, py, 2 * paw, 2 * pah,
+ Angle.fromDeg(0), Angle.fromDeg(90)),
+ r.getTopRightArc());
+ assertEquals(
+ new Arc(px, py, 2 * paw, 2 * pah, Angle.fromDeg(90),
+ Angle.fromDeg(90)), r.getTopLeftArc());
+ assertEquals(
+ new Arc(px, py + ph - 2 * pah, 2 * paw, 2 * pah,
+ Angle.fromDeg(180), Angle.fromDeg(90)),
+ r.getBottomLeftArc());
+ assertEquals(new Arc(px + pw - 2 * paw, py + ph - 2 * pah, 2 * paw,
+ 2 * pah, Angle.fromDeg(270), Angle.fromDeg(90)),
+ r.getBottomRightArc());
+
+ assertEquals(new Line(px + paw, py, px + pw - paw, py), r.getTop());
+ assertEquals(new Line(px + paw, py + ph, px + pw - paw, py + ph),
+ r.getBottom());
+ assertEquals(new Line(px, py + pah, px, py + ph - pah), r.getLeft());
+ assertEquals(new Line(px + pw, py + pah, px + pw, py + ph - pah),
+ r.getRight());
+ }
+
+}
diff --git a/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/StraightTests.java b/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/StraightTests.java
index c861033..4420f5a 100644
--- a/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/StraightTests.java
+++ b/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/StraightTests.java
@@ -328,6 +328,10 @@ public class StraightTests {
assertTrue(s1.getPointAt(0).equals(new Point()));
assertTrue(s1.getPointAt(1).equals(new Point(1, 0)));
assertTrue(s1.getPointAt(-1).equals(new Point(-1, 0)));
+
+ // test 0/0 straight (not a straight anymore)
+ s1 = new Straight(new Point(), new Point());
+ assertTrue(PrecisionUtils.equal(0, s1.getParameterAt(new Point())));
}
@Test
diff --git a/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/Vector3DTests.java b/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/Vector3DTests.java
new file mode 100644
index 0000000..10031da
--- /dev/null
+++ b/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/Vector3DTests.java
@@ -0,0 +1,115 @@
+/*******************************************************************************
+ * Copyright (c) 2012 itemis AG and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Matthias Wienand (itemis AG) - initial API and implementation
+ *
+ *******************************************************************************/
+package org.eclipse.gef4.geometry.tests;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertTrue;
+
+import org.eclipse.gef4.geometry.Point;
+import org.eclipse.gef4.geometry.projective.Vector3D;
+import org.eclipse.gef4.geometry.utils.PrecisionUtils;
+import org.junit.Test;
+
+public class Vector3DTests {
+
+ @Test
+ public void test_equals() {
+ Vector3D v0 = new Vector3D(1, 1, 1);
+ assertFalse(v0.equals(null));
+ assertFalse(v0.equals(new Point()));
+ assertEquals(v0, v0);
+ assertEquals(v0, new Vector3D(1, 1, 1));
+ assertEquals(v0, new Vector3D(new Point(1, 1)));
+ assertEquals(v0, new Vector3D(2, 2, 2));
+ }
+
+ @Test
+ public void test_getCopy() {
+ Vector3D v0 = new Vector3D(1, 2, 3);
+ Vector3D v1 = v0.getCopy();
+ assertEquals(v0, v1);
+ assertNotSame(v0, v1);
+ v0.x++;
+ v0.y--;
+ assertFalse(v0.equals(v1));
+ }
+
+ @Test
+ public void test_toString() {
+ Vector3D v0 = new Vector3D(1, 2, 3);
+ assertEquals("Vector3D(1.0, 2.0, 3.0)", v0.toString());
+ }
+
+ @Test
+ public void test_getAdded() {
+ Vector3D v0 = new Vector3D(1, 0, 5);
+ Vector3D v1 = new Vector3D(0, 1, 5);
+ assertEquals(new Vector3D(1, 0, 5), v0.getAdded(v0));
+ assertEquals(new Vector3D(0, 1, 5), v1.getAdded(v1));
+ assertEquals(new Vector3D(1, 1, 10), v0.getAdded(v1));
+ assertEquals(new Vector3D(1, 1, 10), v1.getAdded(v0));
+ assertEquals(new Vector3D(2, 2, 20), v0.getAdded(v1));
+ assertEquals(new Vector3D(2, 2, 20), v1.getAdded(v0));
+ }
+
+ @Test
+ public void test_getSubtracted() {
+ Vector3D v0 = new Vector3D(10, 5, 1);
+ Vector3D v1 = new Vector3D(5, 10, 1);
+ assertEquals(new Vector3D(0, 0, 0), v0.getSubtracted(v0));
+ assertEquals(new Vector3D(0, 0, 0), v1.getSubtracted(v1));
+ assertFalse(v0.getSubtracted(v1).equals(new Vector3D(0, 0, 1)));
+ assertEquals(new Vector3D(5, -5, 0), v0.getSubtracted(v1));
+ assertEquals(new Vector3D(5, -5, 0), v1.getSubtracted(v0));
+ assertEquals(new Vector3D(1, -1, 1 / 5), v0.getSubtracted(v1));
+ assertEquals(new Vector3D(1, -1, 1 / 5), v1.getSubtracted(v0));
+ }
+
+ @Test
+ public void test_getScaled() {
+ Vector3D v0 = new Vector3D(1, 2, 3);
+ for (double s = -1.1; s <= 1.1; s += 0.2)
+ assertEquals(new Vector3D(1, 2, 3), v0.getScaled(s));
+ }
+
+ @Test
+ public void test_getDot() {
+ Vector3D v0 = new Vector3D(1, 0, 1);
+ Vector3D v1 = new Vector3D(0, 1, 1);
+ assertTrue(PrecisionUtils.equal(1, v0.getDot(v1)));
+ assertTrue(PrecisionUtils.equal(1, v1.getDot(v0)));
+
+ v0 = new Vector3D(1, 2, 3);
+ v1 = new Vector3D(3, 2, 1);
+ assertTrue(PrecisionUtils.equal(10, v0.getDot(v1)));
+ assertTrue(PrecisionUtils.equal(10, v1.getDot(v0)));
+ }
+
+ @Test
+ public void test_getCrossed() {
+ Vector3D v0 = new Vector3D(1, 0, 1);
+ Vector3D v1 = new Vector3D(0, 1, 1);
+ assertEquals(new Vector3D(-1, -1, 1), v0.getCrossed(v1));
+ assertEquals(new Vector3D(1, 1, -1), v1.getCrossed(v0));
+ }
+
+ @Test
+ public void test_getRatio() {
+ Vector3D v0 = new Vector3D(0, 0, 1);
+ Vector3D v1 = new Vector3D(10, 10, 1);
+ assertEquals(new Vector3D(5, 5, 1), v0.getRatio(v1, 0.5));
+ assertEquals(new Vector3D(5, 5, 1), v1.getRatio(v0, 0.5));
+ }
+
+}
diff --git a/org.eclipse.gef4.geometry/META-INF/MANIFEST.MF b/org.eclipse.gef4.geometry/META-INF/MANIFEST.MF
index 7f37060..cd045d8 100644
--- a/org.eclipse.gef4.geometry/META-INF/MANIFEST.MF
+++ b/org.eclipse.gef4.geometry/META-INF/MANIFEST.MF
@@ -7,7 +7,9 @@ Bundle-Vendor: Eclipse.org
Bundle-RequiredExecutionEnvironment: J2SE-1.5
Require-Bundle: org.eclipse.swt;bundle-version="[3.2.0,4.0.0)"
Export-Package: org.eclipse.gef4.geometry,
+ org.eclipse.gef4.geometry.convert,
org.eclipse.gef4.geometry.euclidean,
org.eclipse.gef4.geometry.planar,
+ org.eclipse.gef4.geometry.projective,
org.eclipse.gef4.geometry.transform,
org.eclipse.gef4.geometry.utils;x-friends:="org.eclipse.gef4.geometry.tests"
diff --git a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/Point.java b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/Point.java
index 362daaa..9f3e3ab 100644
--- a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/Point.java
+++ b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/Point.java
@@ -216,8 +216,7 @@ public class Point implements Cloneable, Serializable {
* @return The new, scaled {@link Point}
*/
public Point getScaled(double factorX, double factorY, Point center) {
- return getTranslated(center.getNegated()).scale(factorX, factorY)
- .translate(center);
+ return getCopy().scale(factorX, factorY, center);
}
/**
diff --git a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/euclidean/Straight.java b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/euclidean/Straight.java
index bdacc4e..d55ee0b 100644
--- a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/euclidean/Straight.java
+++ b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/euclidean/Straight.java
@@ -297,7 +297,7 @@ public class Straight implements Cloneable, Serializable {
* {@link Point} p
*/
public double getParameterAt(Point p) {
- if (direction.x != 0) {
+ if (Math.abs(direction.x) > Math.abs(direction.y)) {
return (p.x - position.x) / direction.x;
}
if (direction.y != 0) {
@@ -473,4 +473,4 @@ public class Straight implements Cloneable, Serializable {
return -line.getSignedDistanceCW(new Vector3D(r));
}
-} \ No newline at end of file
+}
diff --git a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/AbstractArcBasedGeometry.java b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/AbstractArcBasedGeometry.java
new file mode 100644
index 0000000..10e7e1a
--- /dev/null
+++ b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/AbstractArcBasedGeometry.java
@@ -0,0 +1,300 @@
+/*******************************************************************************
+ * Copyright (c) 2012 itemis AG and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Matthias Wienand (itemis AG) - contribution for Bugzilla #355997
+ *
+ *******************************************************************************/
+package org.eclipse.gef4.geometry.planar;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.gef4.geometry.Angle;
+import org.eclipse.gef4.geometry.Point;
+import org.eclipse.gef4.geometry.utils.CurveUtils;
+
+/**
+ * An {@link AbstractArcBasedGeometry} describes the arc of an {@link Ellipse}.
+ * It provides functionality to modify and query attributes of the arc and to
+ * compute a Bezier approximation of the arc (the outline).
+ *
+ * @param <T>
+ * type of the inheriting class
+ */
+public abstract class AbstractArcBasedGeometry<T extends AbstractArcBasedGeometry<?>>
+ extends AbstractRectangleBasedGeometry<T, IGeometry> {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * The CCW (counter-clock-wise) {@link Angle} to the x-axis at which this
+ * {@link AbstractArcBasedGeometry} begins.
+ */
+ protected Angle startAngle;
+
+ /**
+ * The CCW (counter-clock-wise) {@link Angle} that spans this
+ * {@link AbstractArcBasedGeometry}.
+ */
+ protected Angle angularExtent;
+
+ /**
+ * Constructs a new {@link AbstractArcBasedGeometry} so that it is fully
+ * contained within the framing rectangle defined by (x, y, width, height),
+ * spanning the given extend (in CCW direction) from the given start angle
+ * (relative to the x-axis).
+ *
+ * @param x
+ * the x-coordinate of the framing rectangle
+ * @param y
+ * the y-coordinate of the framing rectangle
+ * @param width
+ * @param height
+ * @param startAngle
+ * @param angularExtent
+ */
+ public AbstractArcBasedGeometry(double x, double y, double width,
+ double height, Angle startAngle, Angle angularExtent) {
+ this.x = x;
+ this.y = y;
+ this.width = width;
+ this.height = height;
+ this.startAngle = startAngle;
+ this.angularExtent = angularExtent;
+ }
+
+ /**
+ * Returns the extension {@link Angle} of this
+ * {@link AbstractArcBasedGeometry}, i.e. the {@link Angle} defining the
+ * span of this {@link AbstractArcBasedGeometry}.
+ *
+ * @return the extension {@link Angle} of this
+ * {@link AbstractArcBasedGeometry}
+ */
+ public Angle getAngularExtent() {
+ return angularExtent;
+ }
+
+ /**
+ * Returns a {@link Point} representing the start {@link Point} of this
+ * {@link AbstractArcBasedGeometry}.
+ *
+ * @return the start {@link Point} of this {@link AbstractArcBasedGeometry}
+ */
+ public Point getP1() {
+ return getPoint(Angle.fromRad(0));
+ }
+
+ /**
+ * Returns a {@link Point} representing the end {@link Point} of this
+ * {@link AbstractArcBasedGeometry}.
+ *
+ * @return the end {@link Point} of this {@link AbstractArcBasedGeometry}
+ */
+ public Point getP2() {
+ return getPoint(angularExtent);
+ }
+
+ /**
+ * Computes a {@link Point} on this {@link AbstractArcBasedGeometry}. The
+ * {@link Point}'s coordinates are calculated by moving the given
+ * {@link Angle} on this {@link AbstractArcBasedGeometry} starting at the
+ * {@link AbstractArcBasedGeometry}'s start {@link Point}.
+ *
+ * @param angularExtent
+ * @return the {@link Point} at the given {@link Angle}
+ */
+ public Point getPoint(Angle angularExtent) {
+ double a = width / 2;
+ double b = height / 2;
+
+ // // calculate start and end points of the arc from start to end
+ return new Point(x + a + a
+ * Math.cos(startAngle.rad() + angularExtent.rad()), y + b - b
+ * Math.sin(startAngle.rad() + angularExtent.rad()));
+ }
+
+ /**
+ * Returns this {@link AbstractArcBasedGeometry}'s start {@link Angle}.
+ *
+ * @return this {@link AbstractArcBasedGeometry}'s start {@link Angle}
+ */
+ public Angle getStartAngle() {
+ return startAngle;
+ }
+
+ /**
+ * Returns the x-coordinate of the start {@link Point} of this
+ * {@link AbstractArcBasedGeometry}.
+ *
+ * @return the x-coordinate of the start {@link Point} of this
+ * {@link AbstractArcBasedGeometry}
+ */
+ public double getX1() {
+ return getP1().x;
+ }
+
+ /**
+ * Returns the x-coordinate of the end {@link Point} of this
+ * {@link AbstractArcBasedGeometry}.
+ *
+ * @return the x-coordinate of the end {@link Point} of this
+ * {@link AbstractArcBasedGeometry}
+ */
+ public double getX2() {
+ return getP2().x;
+ }
+
+ /**
+ * Returns the y-coordinate of the start {@link Point} of this
+ * {@link AbstractArcBasedGeometry}.
+ *
+ * @return the y-coordinate of the start {@link Point} of this
+ * {@link AbstractArcBasedGeometry}
+ */
+ public double getY1() {
+ return getP1().y;
+ }
+
+ /**
+ * Returns the y-coordinate of the end {@link Point} of this
+ * {@link AbstractArcBasedGeometry}.
+ *
+ * @return the y-coordinate of the end {@link Point} of this
+ * {@link AbstractArcBasedGeometry}
+ */
+ public double getY2() {
+ return getP2().y;
+ }
+
+ /**
+ * Sets the extension {@link Angle} of this {@link AbstractArcBasedGeometry}
+ * .
+ *
+ * @param angularExtent
+ * the new extension {@link Angle} for this
+ * {@link AbstractArcBasedGeometry}
+ */
+ public void setAngularExtent(Angle angularExtent) {
+ this.angularExtent = angularExtent;
+ }
+
+ /**
+ * Sets the start {@link Angle} of this {@link AbstractArcBasedGeometry}.
+ *
+ * @param startAngle
+ * the new start {@link Angle} for this
+ * {@link AbstractArcBasedGeometry}
+ */
+ public void setStartAngle(Angle startAngle) {
+ this.startAngle = startAngle;
+ }
+
+ /**
+ * Computes a Bezier approximation for this {@link AbstractArcBasedGeometry}
+ * . It is approximated by at most four {@link CubicCurve}s which span at
+ * most 90 degrees.
+ *
+ * @return a Bezier approximation for this {@link AbstractArcBasedGeometry}
+ */
+ protected CubicCurve[] computeBezierApproximation() {
+ double start = getStartAngle().rad();
+ double end = getStartAngle().rad() + getAngularExtent().rad();
+
+ // approximation is for arcs with angle < 90 degrees, so we may have to
+ // split the arc into up to 4 cubic curves
+ List<CubicCurve> segments = new ArrayList<CubicCurve>();
+ if (angularExtent.deg() <= 90.0) {
+ segments.add(CurveUtils.computeEllipticalArcApproximation(x, y,
+ width, height, Angle.fromRad(start), Angle.fromRad(end)));
+ } else {
+ // two or more segments, the first will be an ellipse segment
+ // approximation
+ segments.add(CurveUtils.computeEllipticalArcApproximation(x, y,
+ width, height, Angle.fromRad(start),
+ Angle.fromRad(start + Math.PI / 2)));
+ if (angularExtent.deg() <= 180.0) {
+ // two segments, calculate the second (which is below 90
+ // degrees)
+ segments.add(CurveUtils.computeEllipticalArcApproximation(x, y,
+ width, height, Angle.fromRad(start + Math.PI / 2),
+ Angle.fromRad(end)));
+ } else {
+ // three or more segments, so calculate the second one
+ segments.add(CurveUtils.computeEllipticalArcApproximation(x, y,
+ width, height, Angle.fromRad(start + Math.PI / 2),
+ Angle.fromRad(start + Math.PI)));
+ if (angularExtent.deg() <= 270.0) {
+ // three segments, calculate the third (which is below 90
+ // degrees)
+ segments.add(CurveUtils.computeEllipticalArcApproximation(
+ x, y, width, height,
+ Angle.fromRad(start + Math.PI), Angle.fromRad(end)));
+ } else {
+ // four segments (fourth below 90 degrees), so calculate the
+ // third and fourth
+ segments.add(CurveUtils.computeEllipticalArcApproximation(
+ x, y, width, height,
+ Angle.fromRad(start + Math.PI),
+ Angle.fromRad(start + 3 * Math.PI / 2)));
+ segments.add(CurveUtils.computeEllipticalArcApproximation(
+ x, y, width, height,
+ Angle.fromRad(start + 3 * Math.PI / 2),
+ Angle.fromRad(end)));
+ }
+ }
+ }
+ return segments.toArray(new CubicCurve[] {});
+ }
+
+ /**
+ * @see IGeometry#toPath()
+ */
+ public Path toPath() {
+ return CurveUtils.toPath(computeBezierApproximation());
+ }
+
+ @SuppressWarnings("unchecked")
+ public T getRotatedCCW(Angle angle) {
+ return (T) ((T) getCopy()).rotateCCW(angle);
+ }
+
+ /**
+ * Rotates this {@link AbstractArcBasedGeometry} counter-clock-wise (CCW) by
+ * the given {@link Angle} around its center {@link Point}.
+ *
+ * @param angle
+ * the rotation {@link Angle}
+ * @return <code>this</code> for convenience
+ */
+ @SuppressWarnings("unchecked")
+ public T rotateCCW(Angle angle) {
+ startAngle.setRad(startAngle.getAdded(angle).rad());
+ return (T) this;
+ }
+
+ @SuppressWarnings("unchecked")
+ public T getRotatedCW(Angle angle) {
+ return (T) ((T) getCopy()).rotateCW(angle);
+ }
+
+ /**
+ * Rotates this {@link AbstractArcBasedGeometry} clock-wise (CW) by the
+ * given {@link Angle} around its center {@link Point}.
+ *
+ * @param angle
+ * the rotation {@link Angle}
+ * @return <code>this</code> for convenience
+ */
+ @SuppressWarnings("unchecked")
+ public T rotateCW(Angle angle) {
+ startAngle.setRad(startAngle.getAdded(angle.getOppositeFull()).rad());
+ return (T) this;
+ }
+
+}
diff --git a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/AbstractPointListBasedGeometry.java b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/AbstractPointListBasedGeometry.java
index 5ecb1fc..b8f8309 100644
--- a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/AbstractPointListBasedGeometry.java
+++ b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/AbstractPointListBasedGeometry.java
@@ -15,10 +15,14 @@ package org.eclipse.gef4.geometry.planar;
import org.eclipse.gef4.geometry.Angle;
import org.eclipse.gef4.geometry.Point;
import org.eclipse.gef4.geometry.euclidean.Vector;
+import org.eclipse.gef4.geometry.transform.IRotatable;
+import org.eclipse.gef4.geometry.transform.IScalable;
+import org.eclipse.gef4.geometry.transform.ITranslatable;
import org.eclipse.gef4.geometry.utils.PointListUtils;
abstract class AbstractPointListBasedGeometry<T extends AbstractPointListBasedGeometry<?>>
- extends AbstractGeometry {
+ extends AbstractGeometry implements ITranslatable<T>, IScalable<T>,
+ IRotatable<T> {
private static final long serialVersionUID = 1L;
@@ -54,28 +58,7 @@ abstract class AbstractPointListBasedGeometry<T extends AbstractPointListBasedGe
* {@link AbstractPointListBasedGeometry}
*/
public Point getCentroid() {
- if (points.length == 0) {
- return null;
- } else if (points.length == 1) {
- return points[0].getCopy();
- }
-
- double cx = 0, cy = 0, a, sa = 0;
- for (int i = 0; i < points.length - 1; i++) {
- a = points[i].x * points[i + 1].y - points[i].y * points[i + 1].x;
- sa += a;
- cx += (points[i].x + points[i + 1].x) * a;
- cy += (points[i].y + points[i + 1].y) * a;
- }
-
- // closing segment
- a = points[points.length - 2].x * points[points.length - 1].y
- - points[points.length - 2].y * points[points.length - 1].x;
- sa += a;
- cx += (points[points.length - 2].x + points[points.length - 1].x) * a;
- cy += (points[points.length - 2].x + points[points.length - 1].x) * a;
-
- return new Point(cx / (3 * sa), cy / (3 * sa));
+ return PointListUtils.computeCentroid(points);
}
/**
@@ -116,6 +99,10 @@ abstract class AbstractPointListBasedGeometry<T extends AbstractPointListBasedGe
return getRotatedCCW(alpha, getCentroid());
}
+ public T getRotatedCCW(Angle angle, double cx, double cy) {
+ return getRotatedCCW(angle, new Point(cx, cy));
+ }
+
/**
* Returns a new {@link AbstractPointListBasedGeometry} which is rotated
* counter-clock-wise by the given {@link Angle} around the given
@@ -149,6 +136,10 @@ abstract class AbstractPointListBasedGeometry<T extends AbstractPointListBasedGe
return getRotatedCW(alpha, getCentroid());
}
+ public T getRotatedCW(Angle angle, double cx, double cy) {
+ return getRotatedCW(angle, new Point(cx, cy));
+ }
+
/**
* Returns a new {@link AbstractPointListBasedGeometry} which is rotated
* clock-wise by the given {@link Angle} around the given {@link Point}.
@@ -187,6 +178,14 @@ abstract class AbstractPointListBasedGeometry<T extends AbstractPointListBasedGe
return (T) ((T) getCopy()).scale(factorX, factorY);
}
+ public T getScaled(double factor, double cx, double cy) {
+ return getScaled(factor, factor, new Point(cx, cy));
+ }
+
+ public T getScaled(double fx, double fy, double cx, double cy) {
+ return getScaled(fx, fy, new Point(cx, cy));
+ }
+
@SuppressWarnings("unchecked")
public T getScaled(double factorX, double factorY, Point center) {
return (T) ((T) getCopy()).scale(factorX, factorY, center);
@@ -238,6 +237,10 @@ abstract class AbstractPointListBasedGeometry<T extends AbstractPointListBasedGe
return rotateCCW(alpha, getCentroid());
}
+ public T rotateCCW(Angle angle, double cx, double cy) {
+ return rotateCCW(angle, new Point(cx, cy));
+ }
+
/**
* Rotates this {@link AbstractPointListBasedGeometry} counter-clock-wise by
* the given {@link Angle} around the given {@link Point}.
@@ -281,7 +284,11 @@ abstract class AbstractPointListBasedGeometry<T extends AbstractPointListBasedGe
* @see #rotateCW(Angle, Point)
*/
public T rotateCW(Angle alpha) {
- return rotateCW(alpha, getCentroid());
+ return (T) rotateCW(alpha, getCentroid());
+ }
+
+ public T rotateCW(Angle angle, double cx, double cy) {
+ return rotateCW(angle, new Point(cx, cy));
}
/**
@@ -331,14 +338,22 @@ abstract class AbstractPointListBasedGeometry<T extends AbstractPointListBasedGe
return scale(factor, factor);
}
- public T scale(double factorX, double factorY) {
- return scale(factorX, factorY, getCentroid());
+ public T scale(double fx, double fy) {
+ return scale(fx, fy, getCentroid());
+ }
+
+ public T scale(double factor, double cx, double cy) {
+ return scale(factor, factor, new Point(cx, cy));
+ }
+
+ public T scale(double fx, double fy, double cx, double cy) {
+ return scale(fx, fy, new Point(cx, cy));
}
@SuppressWarnings("unchecked")
- public T scale(double factorX, double factorY, Point center) {
+ public T scale(double fx, double fy, Point center) {
for (Point p : points) {
- Point np = p.getScaled(factorX, factorY, center);
+ Point np = p.getScaled(fx, fy, center);
p.x = np.x;
p.y = np.y;
}
@@ -392,4 +407,4 @@ abstract class AbstractPointListBasedGeometry<T extends AbstractPointListBasedGe
return translate(p.x, p.y);
}
-} \ No newline at end of file
+}
diff --git a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/AbstractPolyShape.java b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/AbstractPolyShape.java
new file mode 100644
index 0000000..d0414d8
--- /dev/null
+++ b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/AbstractPolyShape.java
@@ -0,0 +1,181 @@
+/*******************************************************************************
+ * Copyright (c) 2012 itemis AG and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Matthias Wienand (itemis AG) - initial API and implementation
+ *
+ *******************************************************************************/
+package org.eclipse.gef4.geometry.planar;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Stack;
+
+import org.eclipse.gef4.geometry.Point;
+import org.eclipse.gef4.geometry.utils.PrecisionUtils;
+
+/**
+ * The {@link AbstractPolyShape} class contains an algorithm to find the outline
+ * segments of an object of an inheriting class.
+ *
+ */
+public abstract class AbstractPolyShape extends AbstractGeometry implements
+ IPolyShape {
+
+ private static final long serialVersionUID = 1L;
+
+ private void assignRemainingSegment(HashMap<Line, Integer> seen,
+ Stack<Line> addends, Line toAdd, Point start, Point end) {
+ if (!start.equals(end)) {
+ Line rest = new Line(start, end);
+ if (start.equals(toAdd.getP1()) || start.equals(toAdd.getP2())) {
+ // System.out
+ // .println(" pushing rest (" + rest + ") to addends");
+ addends.push(rest);
+ } else {
+ // System.out.println(" marking rest (" + rest +
+ // ") as seen");
+ seen.put(rest,
+ seen.containsKey(rest) && seen.get(rest) == 2 ? 2 : 1);
+ }
+ }
+ }
+
+ public boolean contains(Point p) {
+ for (IShape s : getShapes()) {
+ if (s.contains(p)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Inner segments are identified by a segment count of exactly 2.
+ *
+ * @param seen
+ */
+ private void filterOutInnerSegments(HashMap<Line, Integer> seen) {
+ for (Line seg : new HashSet<Line>(seen.keySet())) {
+ if (seen.get(seg) == 2) {
+ seen.remove(seg);
+ }
+ }
+ }
+
+ /**
+ * Collects all edges of the internal {@link IShape}s. For a {@link Region}
+ * the internal {@link IShape}s are {@link Rectangle}s. For a {@link Ring}
+ * the internal {@link IShape}s are {@link Polygon}s (triangles).
+ *
+ * The internal edges are needed to determine inner and outer segments of
+ * the {@link IPolyShape}. Based on the outline of the {@link IPolyShape},
+ * the outline intersections can be computed. These outline intersections
+ * are required to test if an {@link ICurve} is fully-contained by the
+ * {@link IPolyShape}.
+ *
+ * @return the edges of all internal {@link IShape}s
+ */
+ abstract protected Line[] getAllEdges();
+
+ /**
+ * Computes the outline of this {@link AbstractPolyShape}.
+ *
+ * @return the outline of this {@link AbstractPolyShape}
+ * @see #getOutlineSegments()
+ */
+ public Polyline getOutline() {
+ return new Polyline(getOutlineSegments());
+ }
+
+ /**
+ * Computes the outline segments of this {@link AbstractPolyShape}.
+ *
+ * The outline segments are those outline segments of the internal
+ * {@link Rectangle}s that only exist once.
+ *
+ * @return the outline segments of this {@link AbstractPolyShape}
+ */
+ public Line[] getOutlineSegments() {
+ // System.out.println("collecting all edges...");
+ HashMap<Line, Integer> seen = new HashMap<Line, Integer>();
+ Stack<Line> elementsToAdd = new Stack<Line>();
+ for (Line e : getAllEdges())
+ elementsToAdd.push(e);
+
+ int c = 0;
+ addingElements: while (c++ < 1000 && !elementsToAdd.empty()) {
+ Line toAdd = elementsToAdd.pop();
+ // System.out.println("adding " + toAdd + "...");
+ for (Line seg : new HashSet<Line>(seen.keySet())) {
+ if (seg.overlaps(toAdd)) {
+ // System.out.println(" overlaps with " + seg);
+ Point[] p = getSortedEndpoints(toAdd, seg);
+ seen.remove(seg);
+ assignRemainingSegment(seen, elementsToAdd, toAdd, p[0],
+ p[1]);
+ assignRemainingSegment(seen, elementsToAdd, toAdd, p[3],
+ p[2]);
+ markOverlap(seen, p[1], p[2]);
+ continue addingElements;
+ }
+ }
+ // System.out.println(" did not overlap");
+ seen.put(toAdd, 1);
+ }
+
+ // System.out.println("filter out inner segments...");
+ filterOutInnerSegments(seen);
+
+ return seen.keySet().toArray(new Line[] {});
+ }
+
+ /**
+ * Sorts the end {@link Point}s of two {@link Line}s that do overlap by
+ * their coordinate values.
+ *
+ * @param toAdd
+ * @param seg
+ * @return the sorted {@link Point}s
+ */
+ private Point[] getSortedEndpoints(Line toAdd, Line seg) {
+ final Point[] p = new Point[] { seg.getP1(), seg.getP2(),
+ toAdd.getP1(), toAdd.getP2() };
+ Arrays.sort(p, new Comparator<Point>() {
+ public int compare(Point p1, Point p2) {
+ if (PrecisionUtils.equal(p1.x, p2.x)) {
+ return p1.y < p2.y ? 1 : -1;
+ }
+ return p1.x < p2.x ? 1 : -1;
+ }
+ });
+ return p;
+ }
+
+ /**
+ * Marks a given segment from start to end {@link Point} as an overlap in
+ * the seen {@link HashMap} if the segment is not degenerated, i.e. it is
+ * not just a single {@link Point}.
+ *
+ * @param seen
+ * @param start
+ * @param end
+ */
+ private void markOverlap(HashMap<Line, Integer> seen, Point start, Point end) {
+ if (!start.equals(end)) {
+ // Count an overlapping segment twice to assure that it is going to
+ // get deleted afterwards.
+ Line overlap = new Line(start, end);
+ seen.put(overlap, 2);
+ // System.out.println(" mark segment " + overlap +
+ // " as overlap");
+ }
+ }
+
+}
diff --git a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/AbstractRectangleBasedGeometry.java b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/AbstractRectangleBasedGeometry.java
index b1760d8..94d8318 100644
--- a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/AbstractRectangleBasedGeometry.java
+++ b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/AbstractRectangleBasedGeometry.java
@@ -14,16 +14,32 @@ package org.eclipse.gef4.geometry.planar;
import org.eclipse.gef4.geometry.Dimension;
import org.eclipse.gef4.geometry.Point;
+import org.eclipse.gef4.geometry.transform.IRotatable;
+import org.eclipse.gef4.geometry.transform.IScalable;
+import org.eclipse.gef4.geometry.transform.ITranslatable;
/**
+ * <p>
* Abstract superclass of geometries that are defined by means of their upper
* left coordinate (x,y) and a given width and height.
+ * </p>
*
- * @author anyssen
+ * <p>
+ * The type parameter <code>T</code> specifies the type of the inheriting class.
+ * This is to be able to return the correct type, so that a type cast is
+ * unnecessary.
+ * </p>
+ *
+ * <p>
+ * The type parameter <code>S</code> specifies the result type of all rotation
+ * short-cut methods. See {@link IRotatable} for more information.
+ * </p>
*
+ * @author anyssen
*/
-abstract class AbstractRectangleBasedGeometry<T extends AbstractRectangleBasedGeometry<?>>
- extends AbstractGeometry {
+abstract class AbstractRectangleBasedGeometry<T extends AbstractRectangleBasedGeometry<?, ?>, S extends IGeometry>
+ extends AbstractGeometry implements ITranslatable<T>, IScalable<T>,
+ IRotatable<S> {
private static final long serialVersionUID = 1L;
@@ -53,29 +69,28 @@ abstract class AbstractRectangleBasedGeometry<T extends AbstractRectangleBasedGe
* which is the location of its bounds.
*
* @return a {@link Point} representing the location of this
- * {@link AbstractRectangleBasedGeometry} 's bounds
+ * {@link AbstractRectangleBasedGeometry}'s bounds
*/
public Point getLocation() {
return new Point(x, y);
}
- /**
- * Returns a new {@link AbstractPointListBasedGeometry} which is scaled by
- * the given factor. The {@link AbstractPointListBasedGeometry} is
- * translated by the negated centroid (see {@link #getCentroid()}) first.
- * The translation is reversed afterwards.
- *
- * @param factor
- * The scale-factor
- * @return The new scaled {@link AbstractPointListBasedGeometry}
- * @see #getScaled(double, Point)
- */
@SuppressWarnings("unchecked")
public T getScaled(double factor) {
return (T) ((T) getCopy()).scale(factor);
}
@SuppressWarnings("unchecked")
+ public T getScaled(double factor, Point center) {
+ return (T) ((T) getCopy()).scale(factor, center);
+ }
+
+ @SuppressWarnings("unchecked")
+ public T getScaled(double factor, double centerX, double centerY) {
+ return (T) ((T) getCopy()).scale(factor, centerX, centerY);
+ }
+
+ @SuppressWarnings("unchecked")
public T getScaled(double factorX, double factorY) {
return (T) ((T) getCopy()).scale(factorX, factorY);
}
@@ -86,8 +101,9 @@ abstract class AbstractRectangleBasedGeometry<T extends AbstractRectangleBasedGe
}
@SuppressWarnings("unchecked")
- public T getScaled(double factor, Point center) {
- return (T) ((T) getCopy()).scale(factor, center);
+ public T getScaled(double factorX, double factorY, double centerX,
+ double centerY) {
+ return (T) ((T) getCopy()).scale(factorX, factorY, centerX, centerY);
}
/**
@@ -139,37 +155,33 @@ abstract class AbstractRectangleBasedGeometry<T extends AbstractRectangleBasedGe
return y;
}
- /**
- * Scales this {@link AbstractPointListBasedGeometry} by the given factor.
- * The {@link AbstractPointListBasedGeometry} is translated by its negated
- * centroid (see {@link #getCentroid()}) first. The translation is reversed
- * afterwards.
- *
- * @see #scale(double, Point)
- * @param factor
- * @return <code>this</code> for convenience
- */
- public T scale(double factor) {
- return scale(factor, factor);
+ @SuppressWarnings("unchecked")
+ public T scale(double fx, double fy, double cx, double cy) {
+ x = (x - cx) * fx + cx;
+ y = (y - cy) * fy + cy;
+ width *= fx;
+ height *= fy;
+ return (T) this;
}
- public T scale(double factorX, double factorY) {
- return scale(factorX, factorY, getCentroid());
+ public T scale(double fx, double fy, Point center) {
+ return scale(fx, fy, center.x, center.y);
}
- @SuppressWarnings("unchecked")
- public T scale(double factorX, double factorY, Point center) {
- double nx = (x - center.x) * factorX + center.x;
- double ny = (y - center.y) * factorY + center.y;
- width = (x + width - center.x) * factorX + center.x - nx;
- height = (y + height - center.y) * factorY + center.y - ny;
- x = nx;
- y = ny;
- return (T) this;
+ public T scale(double fx, double fy) {
+ return scale(fx, fy, getCentroid());
+ }
+
+ public T scale(double factor) {
+ return scale(factor, factor);
}
public T scale(double factor, Point center) {
- return scale(factor, factor, center);
+ return scale(factor, center.x, center.y);
+ }
+
+ public T scale(double factor, double cx, double cy) {
+ return scale(factor, factor, cx, cy);
}
/**
@@ -355,4 +367,4 @@ abstract class AbstractRectangleBasedGeometry<T extends AbstractRectangleBasedGe
return translate(p.x, p.y);
}
-} \ No newline at end of file
+}
diff --git a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Arc.java b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Arc.java
index de25471..f45d1f0 100644
--- a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Arc.java
+++ b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Arc.java
@@ -7,16 +7,15 @@
*
* Contributors:
* Alexander Nyßen (itemis AG) - initial API and implementation
+ * Matthias Wienand (itemis AG) - contribution for Bugzilla #355997
*
*******************************************************************************/
package org.eclipse.gef4.geometry.planar;
-import java.util.ArrayList;
-import java.util.List;
-
import org.eclipse.gef4.geometry.Angle;
import org.eclipse.gef4.geometry.Point;
import org.eclipse.gef4.geometry.utils.CurveUtils;
+import org.eclipse.gef4.geometry.utils.PrecisionUtils;
/**
* Represents the geometric shape of an arc, which is defined by its enclosing
@@ -26,38 +25,21 @@ import org.eclipse.gef4.geometry.utils.CurveUtils;
* @author anyssen
*
*/
-public final class Arc extends AbstractRectangleBasedGeometry<Arc> implements
- ICurve {
+public final class Arc extends AbstractArcBasedGeometry<Arc> implements ICurve {
private static final long serialVersionUID = 1L;
- // TODO: move to utilities
- private static final Path toPath(CubicCurve... curves) {
- Path p = new Path();
- for (int i = 0; i < curves.length; i++) {
- if (i == 0) {
- p.moveTo(curves[i].getX1(), curves[i].getY1());
- }
- p.curveTo(curves[i].getCtrlX1(), curves[i].getCtrlY1(),
- curves[i].getCtrlX2(), curves[i].getCtrlY2(),
- curves[i].getX2(), curves[i].getY2());
- }
- return p;
- }
-
- private Angle startAngle;
- private Angle angularExtent;
-
/**
- * Constructs a new {@link Arc} so that it is fully contained within the
- * framing rectangle defined by (x, y, width, height), spanning the given
- * extend (in CCW direction) from the given start angle (relative to the
- * x-axis).
+ * Constructs a new {@link Arc} of the given values. A {@link Rectangle} is
+ * used to define the {@link Ellipse} from which the {@link Arc} is cut out.
+ * The start {@link Angle} is the CCW (counter-clock-wise) {@link Angle} to
+ * the x-axis at which the {@link Arc} begins. The angular extent is the CCW
+ * {@link Angle} that spans the {@link Arc}, i.e. the resulting end
+ * {@link Angle} of the {@link Arc} is the sum of the start {@link Angle}
+ * and the angular extent.
*
* @param x
- * the x-coordinate of the framing rectangle
* @param y
- * the y-coordinate of the framing rectangle
* @param width
* @param height
* @param startAngle
@@ -65,56 +47,22 @@ public final class Arc extends AbstractRectangleBasedGeometry<Arc> implements
*/
public Arc(double x, double y, double width, double height,
Angle startAngle, Angle angularExtent) {
- this.x = x;
- this.y = y;
- this.width = width;
- this.height = height;
- this.startAngle = startAngle;
- this.angularExtent = angularExtent;
+ super(x, y, width, height, startAngle, angularExtent);
}
- private CubicCurve computeApproximation(double start, double end) {
- // compute major and minor axis length
- double a = width / 2;
- double b = height / 2;
-
- // // calculate start and end points of the arc from start to end
- Point startPoint = new Point(x + a + a * Math.cos(start), y + b - b
- * Math.sin(start));
- Point endPoint = new Point(x + a + a * Math.cos(end), y + b - b
- * Math.sin(end));
-
- // approximation by cubic Bezier according to approximation provided in:
- // http://www.spaceroots.org/documents/ellipse/elliptical-arc.pdf
- double t = Math.tan((end - start) / 2);
- double alpha = Math.sin(end - start)
- * (Math.sqrt(4.0d + 3.0d * t * t) - 1) / 3;
- Point controlPoint1 = new Point(startPoint.x + alpha * -a
- * Math.sin(start), startPoint.y - alpha * b * Math.cos(start));
- Point controlPoint2 = new Point(
- endPoint.x - alpha * -a * Math.sin(end), endPoint.y + alpha * b
- * Math.cos(end));
-
- Point[] points = new Point[] { startPoint, controlPoint1,
- controlPoint2, endPoint };
- return new CubicCurve(points);
- }
-
- /**
- * @see IGeometry#contains(Point)
- */
- public boolean contains(Point p) {
- return false;
- }
-
- /**
- * Returns the extension {@link Angle} of this {@link Arc}, i.e. the
- * {@link Angle} defining the span of the {@link Arc}.
- *
- * @return the extension {@link Angle} of this {@link Arc}
- */
- public Angle getAngularExtent() {
- return angularExtent;
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof Arc)) {
+ return false;
+ }
+ Arc o = (Arc) obj;
+ return PrecisionUtils.equal(x, o.x)
+ && PrecisionUtils.equal(y, o.y)
+ && PrecisionUtils.equal(width, o.width)
+ && PrecisionUtils.equal(height, o.height)
+ && PrecisionUtils.equal(angularExtent.rad(),
+ o.angularExtent.rad())
+ && PrecisionUtils.equal(startAngle.rad(), o.startAngle.rad());
}
/**
@@ -124,64 +72,27 @@ public final class Arc extends AbstractRectangleBasedGeometry<Arc> implements
return new Arc(x, y, width, height, startAngle, angularExtent);
}
- public Point[] getIntersections(ICurve g) {
- return CurveUtils.getIntersections(this, g);
- }
-
- /**
- * Returns a {@link Point} representing the start point of this {@link Arc}.
- *
- * @return the start {@link Point} of this {@link Arc}
- */
- public Point getP1() {
- return getPoint(Angle.fromRad(0));
- }
-
- public Point getP2() {
- return getPoint(angularExtent);
- }
-
/**
- * Computes a {@link Point} on this {@link Arc}. The {@link Point}'s
- * coordinates are calculated by moving the given {@link Angle} on the
- * {@link Arc} starting at the {@link Arc} start {@link Point}.
- *
- * @param angularExtent
- * @return the {@link Point} at the given {@link Angle}
+ * @see IGeometry#contains(Point)
*/
- public Point getPoint(Angle angularExtent) {
- double a = width / 2;
- double b = height / 2;
-
- // // calculate start and end points of the arc from start to end
- return new Point(x + a + a
- * Math.cos(startAngle.rad() + angularExtent.rad()), y + b - b
- * Math.sin(startAngle.rad() + angularExtent.rad()));
+ public boolean contains(Point p) {
+ for (CubicCurve c : computeBezierApproximation()) {
+ if (c.contains(p)) {
+ return true;
+ }
+ }
+ return false;
}
/**
- * Returns this {@link Arc}'s start {@link Angle}.
+ * Computes the {@link Point}s of intersection of this {@link Arc} and the
+ * given {@link ICurve}.
*
- * @return this {@link Arc}'s start {@link Angle}
+ * @param c
+ * @return the intersection {@link Point}s
*/
- public Angle getStartAngle() {
- return startAngle;
- }
-
- public double getX1() {
- return getP1().x;
- }
-
- public double getX2() {
- return getP2().x;
- }
-
- public double getY1() {
- return getP1().y;
- }
-
- public double getY2() {
- return getP2().y;
+ public Point[] getIntersections(ICurve c) {
+ return CurveUtils.getIntersections(this, c);
}
public boolean intersects(ICurve c) {
@@ -189,7 +100,7 @@ public final class Arc extends AbstractRectangleBasedGeometry<Arc> implements
}
public boolean overlaps(ICurve c) {
- for (BezierCurve seg1 : toBezier()) {
+ for (BezierCurve seg1 : computeBezierApproximation()) {
if (seg1.overlaps(c)) {
return true;
}
@@ -197,68 +108,35 @@ public final class Arc extends AbstractRectangleBasedGeometry<Arc> implements
return false;
}
- /**
- * Sets the extension {@link Angle} of this {@link Arc}.
- *
- * @param angularExtent
- * the new extension {@link Angle} for this {@link Arc}
- */
- public void setAngularExtent(Angle angularExtent) {
- this.angularExtent = angularExtent;
+ public CubicCurve[] toBezier() {
+ return computeBezierApproximation();
}
- /**
- * Sets the start {@link Angle} of this {@link Arc}.
- *
- * @param startAngle
- * the new start {@link Angle} for this {@link Arc}
- */
- public void setStartAngle(Angle startAngle) {
- this.startAngle = startAngle;
+ @Override
+ public String toString() {
+ return "Arc(" + "x = " + x + ", y = " + y + ", width = " + width
+ + ", height = " + height + ", startAngle = " + startAngle.deg()
+ + ", angularExtend = " + angularExtent.deg() + ")";
}
- public CubicCurve[] toBezier() {
- double start = getStartAngle().rad();
- double end = getStartAngle().rad() + getAngularExtent().rad();
+ public PolyBezier getRotatedCCW(Angle angle, double cx, double cy) {
+ return new PolyBezier(computeBezierApproximation()).rotateCCW(angle,
+ cx, cy);
+ }
- // approximation is for arcs with angle < 90 degrees, so we may have to
- // split the arc into up to 4 cubic curves
- List<CubicCurve> segments = new ArrayList<CubicCurve>();
- if (angularExtent.deg() <= 90.0) {
- segments.add(computeApproximation(start, end));
- } else {
- // two or more segments, the first will be an ellipse segment
- // approximation
- segments.add(computeApproximation(start, start + Math.PI / 2));
- if (angularExtent.deg() <= 180.0) {
- // two segments, calculate the second (which is below 90
- // degrees)
- segments.add(computeApproximation(start + Math.PI / 2, end));
- } else {
- // three or more segments, so calculate the second one
- segments.add(computeApproximation(start + Math.PI / 2, start
- + Math.PI));
- if (angularExtent.deg() <= 270.0) {
- // three segments, calculate the third (which is below 90
- // degrees)
- segments.add(computeApproximation(start + Math.PI, end));
- } else {
- // four segments (fourth below 90 degrees), so calculate the
- // third and fourth
- segments.add(computeApproximation(start + Math.PI, start
- + 3 * Math.PI / 2));
- segments.add(computeApproximation(start + 3 * Math.PI / 2,
- end));
- }
- }
- }
- return segments.toArray(new CubicCurve[] {});
+ public PolyBezier getRotatedCCW(Angle angle, Point center) {
+ return new PolyBezier(computeBezierApproximation()).rotateCCW(angle,
+ center);
}
- /**
- * @see IGeometry#toPath()
- */
- public Path toPath() {
- return toPath(toBezier());
+ public PolyBezier getRotatedCW(Angle angle, double cx, double cy) {
+ return new PolyBezier(computeBezierApproximation()).rotateCW(angle, cx,
+ cy);
+ }
+
+ public PolyBezier getRotatedCW(Angle angle, Point center) {
+ return new PolyBezier(computeBezierApproximation()).rotateCW(angle,
+ center);
}
+
}
diff --git a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/BezierCurve.java b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/BezierCurve.java
index 8b2bae2..1f1bdfd 100644
--- a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/BezierCurve.java
+++ b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/BezierCurve.java
@@ -14,8 +14,10 @@ package org.eclipse.gef4.geometry.planar;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.HashSet;
-import java.util.List;
+import java.util.Iterator;
import java.util.Set;
import java.util.Stack;
@@ -24,21 +26,20 @@ import org.eclipse.gef4.geometry.Point;
import org.eclipse.gef4.geometry.euclidean.Vector;
import org.eclipse.gef4.geometry.projective.Straight3D;
import org.eclipse.gef4.geometry.projective.Vector3D;
+import org.eclipse.gef4.geometry.transform.IRotatable;
+import org.eclipse.gef4.geometry.transform.IScalable;
+import org.eclipse.gef4.geometry.transform.ITranslatable;
import org.eclipse.gef4.geometry.utils.PointListUtils;
import org.eclipse.gef4.geometry.utils.PrecisionUtils;
/**
* Abstract base class of Bezier Curves.
*
- * TODO: make concrete -> leaf specializations in place but delegate
- * functionality to here.
- *
* @author anyssen
- *
*/
-public class BezierCurve extends AbstractGeometry implements ICurve {
-
- private static final long serialVersionUID = 1L;
+public class BezierCurve extends AbstractGeometry implements ICurve,
+ ITranslatable<BezierCurve>, IScalable<BezierCurve>,
+ IRotatable<BezierCurve> {
private static class FatLine {
public static FatLine from(BezierCurve c, boolean ortho) {
@@ -186,6 +187,19 @@ public class BezierCurve extends AbstractGeometry implements ICurve {
}
/**
+ * Expands this {@link Interval} to include the given other
+ * {@link Interval}.
+ *
+ * @param i
+ */
+ public void expand(Interval i) {
+ if (i.a < a)
+ a = i.a;
+ if (i.b > b)
+ b = i.b;
+ }
+
+ /**
* Returns a copy of this {@link Interval}.
*
* @return a copy of this {@link Interval}
@@ -305,6 +319,22 @@ public class BezierCurve extends AbstractGeometry implements ICurve {
}
/**
+ * Expands this {@link IntervalPair} to include the given other
+ * {@link IntervalPair}.
+ *
+ * @param ip
+ */
+ public void expand(IntervalPair ip) {
+ if (p == ip.p) {
+ pi.expand(ip.pi);
+ qi.expand(ip.qi);
+ } else {
+ pi.expand(ip.qi);
+ qi.expand(ip.pi);
+ }
+ }
+
+ /**
* Returns a copy of this {@link IntervalPair}. The underlying
* {@link BezierCurve}s are only shallow copied. The corresponding
* parameter {@link Interval}s are truly copied.
@@ -323,7 +353,7 @@ public class BezierCurve extends AbstractGeometry implements ICurve {
* @return the first sub-curve of this {@link IntervalPair}
*/
public BezierCurve getPClipped() {
- return p.getClipped(pi.a, pi.b);
+ return p.getClipped(Math.max(pi.a, 0), Math.min(pi.b, 1));
}
/**
@@ -350,7 +380,7 @@ public class BezierCurve extends AbstractGeometry implements ICurve {
* @return the second sub-curve of this {@link IntervalPair}
*/
public BezierCurve getQClipped() {
- return q.getClipped(qi.a, qi.b);
+ return q.getClipped(Math.max(qi.a, 0), Math.min(qi.b, 1));
}
/**
@@ -403,6 +433,8 @@ public class BezierCurve extends AbstractGeometry implements ICurve {
public boolean pIsBetterThanQ(Point p, Point q);
}
+ private static final long serialVersionUID = 1L;
+
// TODO: use constants that limit the number of iterations for the
// different iterative/recursive algorithms:
// INTERSECTIONS_MAX_ITERATIONS, APPROXIMATION_MAX_ITERATIONS
@@ -418,32 +450,49 @@ public class BezierCurve extends AbstractGeometry implements ICurve {
private static IntervalPair[] clusterChunks(IntervalPair[] intervalPairs,
int shift) {
- List<IntervalPair> clusters = new ArrayList<IntervalPair>();
+ ArrayList<IntervalPair> ips = new ArrayList<IntervalPair>();
- // TODO: do something intelligent instead!
- boolean isCompletelyClustered = true;
+ ips.addAll(Arrays.asList(intervalPairs));
- for (IntervalPair ip : intervalPairs) {
- boolean isExpansion = false;
-
- for (IntervalPair cluster : clusters) {
- if (isNextTo(cluster, ip, shift)) {
- expand(cluster, ip);
- isExpansion = true;
- break;
+ Collections.sort(ips, new Comparator<IntervalPair>() {
+ public int compare(IntervalPair i, IntervalPair j) {
+ return i.pi.a <= j.pi.a ? -1 : 1;
+ }
+ });
+
+ // for (IntervalPair ip : ips) {
+ // System.out.println("P [" + ip.pi.a + ";" + ip.pi.b + "] Q ["
+ // + ip.qi.a + ";" + ip.qi.b + "]");
+ // }
+
+ ArrayList<IntervalPair> clusters = new ArrayList<IntervalPair>();
+ IntervalPair current = null;
+ boolean couldMerge;
+
+ do {
+ clusters.clear();
+ couldMerge = false;
+ for (IntervalPair i : ips) {
+ if (current == null) {
+ current = i.getCopy();
+ } else if (isNextTo(current, i, shift)) {
+ couldMerge = true;
+ current.expand(i);
+ } else {
+ isNextTo(current, i, shift);
+ clusters.add(current);
+ current = i.getCopy();
}
}
-
- if (!isExpansion) {
- clusters.add(ip);
- } else {
- isCompletelyClustered = false;
+ if (current != null) {
+ clusters.add(current);
+ current = null;
}
- }
+ ips.clear();
+ ips.addAll(clusters);
+ } while (couldMerge);
- IntervalPair[] clustersArray = clusters.toArray(new IntervalPair[] {});
- return isCompletelyClustered ? clustersArray : clusterChunks(
- clustersArray, shift);
+ return clusters.toArray(new IntervalPair[] {});
}
private static void copyIntervalPair(IntervalPair a, IntervalPair b) {
@@ -453,19 +502,65 @@ public class BezierCurve extends AbstractGeometry implements ICurve {
a.qi = b.qi;
}
- private static void expand(IntervalPair group, IntervalPair newcomer) {
- if (group.pi.a > newcomer.pi.a) {
- group.pi.a = newcomer.pi.a;
+ private static IntervalPair extractOverlap(
+ IntervalPair[] intersectionCandidates, IntervalPair[] endPoints) {
+ // merge intersection candidates and end points
+ IntervalPair[] fineChunks = new IntervalPair[intersectionCandidates.length
+ + endPoints.length];
+ for (int i = 0; i < intersectionCandidates.length; i++) {
+ fineChunks[i] = intersectionCandidates[i];
}
- if (group.pi.b < newcomer.pi.b) {
- group.pi.b = newcomer.pi.b;
+ for (int i = 0; i < endPoints.length; i++) {
+ fineChunks[intersectionCandidates.length + i] = endPoints[i];
}
- if (group.qi.a > newcomer.qi.a) {
- group.qi.a = newcomer.qi.a;
+
+ if (fineChunks.length == 0) {
+ return null;
}
- if (group.qi.b < newcomer.qi.b) {
- group.qi.b = newcomer.qi.b;
+
+ // recluster chunks
+ normalizeIntervalPairs(fineChunks);
+ IntervalPair[] chunks = clusterChunks(fineChunks, CHUNK_SHIFT - 1);
+
+ /*
+ * if they overlap, the chunk has to start/end in a start-/endpoint of
+ * the curves.
+ */
+
+ for (IntervalPair overlap : chunks) {
+ if (PrecisionUtils.smallerEqual(overlap.pi.a, 0)
+ && PrecisionUtils.greaterEqual(overlap.pi.b, 1)
+ || PrecisionUtils.smallerEqual(overlap.qi.a, 0)
+ && PrecisionUtils.greaterEqual(overlap.qi.b, 1)
+ || (PrecisionUtils.smallerEqual(overlap.pi.a, 0) || PrecisionUtils
+ .greaterEqual(overlap.pi.b, 1))
+ && (PrecisionUtils.smallerEqual(overlap.qi.a, 0) || PrecisionUtils
+ .greaterEqual(overlap.qi.b, 1))) {
+ // it overlaps
+ if (PrecisionUtils.smallerEqual(overlap.pi.a, 0,
+ CHUNK_SHIFT - 1)
+ && PrecisionUtils.smallerEqual(overlap.pi.b, 0,
+ CHUNK_SHIFT - 1)
+ || PrecisionUtils.greaterEqual(overlap.pi.a, 1,
+ CHUNK_SHIFT - 1)
+ && PrecisionUtils.greaterEqual(overlap.pi.b, 1,
+ CHUNK_SHIFT - 1)
+ || PrecisionUtils.smallerEqual(overlap.qi.a, 0,
+ CHUNK_SHIFT - 1)
+ && PrecisionUtils.smallerEqual(overlap.qi.b, 0,
+ CHUNK_SHIFT - 1)
+ || PrecisionUtils.greaterEqual(overlap.qi.a, 1,
+ CHUNK_SHIFT - 1)
+ && PrecisionUtils.greaterEqual(overlap.qi.b, 1,
+ CHUNK_SHIFT - 1)) {
+ // only end-point-intersection
+ return null;
+ }
+ return refineOverlap(overlap);
+ }
}
+
+ return null;
}
/**
@@ -515,84 +610,13 @@ public class BezierCurve extends AbstractGeometry implements ICurve {
return (y - p.y + m * p.x) / m;
}
- private static boolean isNextTo(IntervalPair a, IntervalPair b, int shift) {
- boolean isPNeighbour = PrecisionUtils.greaterEqual(a.pi.a, b.pi.a,
- shift)
- && PrecisionUtils.smallerEqual(a.pi.a, b.pi.b, shift)
- || PrecisionUtils.smallerEqual(a.pi.a, b.pi.a, shift)
- && PrecisionUtils.greaterEqual(a.pi.b, b.pi.a, shift);
- boolean isQNeighbour = PrecisionUtils.greaterEqual(a.qi.a, b.qi.a,
- shift)
- && PrecisionUtils.smallerEqual(a.qi.a, b.qi.b, shift)
- || PrecisionUtils.smallerEqual(a.qi.a, b.qi.a, shift)
- && PrecisionUtils.greaterEqual(a.qi.b, b.qi.a, shift);
-
- return isPNeighbour && isQNeighbour;
+ private static boolean isNextTo(Interval i, Interval j, int shift) {
+ return PrecisionUtils.smallerEqual(j.a, i.b, shift)
+ && PrecisionUtils.greaterEqual(j.b, i.a, shift);
}
- private static IntervalPair isOverlap(
- IntervalPair[] intersectionCandidates, IntervalPair[] endPoints) {
- // merge intersection candidates and end points
- IntervalPair[] fineChunks = new IntervalPair[intersectionCandidates.length
- + endPoints.length];
- for (int i = 0; i < intersectionCandidates.length; i++) {
- fineChunks[i] = intersectionCandidates[i];
- }
- for (int i = 0; i < endPoints.length; i++) {
- fineChunks[intersectionCandidates.length + i] = endPoints[i];
- }
-
- if (fineChunks.length == 0) {
- return new IntervalPair(null, null, null, null);
- }
-
- // recluster chunks
- normalizeIntervalPairs(fineChunks);
- IntervalPair[] chunks = clusterChunks(fineChunks, CHUNK_SHIFT - 1);
-
- // we should have a single chunk now
- if (chunks.length != 1) {
- return new IntervalPair(null, null, null, null);
- }
-
- IntervalPair overlap = chunks[0];
-
- /*
- * if they do overlap in a single point, the point of intersection has
- * to be an end-point of both curves. therefore, we do not have to
- * consider this case here, because it is already checked in the main
- * intersection method.
- *
- * if they overlap, the chunk has to start/end in a start-/endpoint of
- * the curves.
- */
-
- if (PrecisionUtils.equal(overlap.pi.a, 0)
- && PrecisionUtils.equal(overlap.pi.b, 1)
- || PrecisionUtils.equal(overlap.qi.a, 0)
- && PrecisionUtils.equal(overlap.qi.b, 1)
- || (PrecisionUtils.equal(overlap.pi.a, 0) || PrecisionUtils
- .equal(overlap.pi.b, 1))
- && (PrecisionUtils.equal(overlap.qi.a, 0) || PrecisionUtils
- .equal(overlap.qi.b, 1))) {
- // it overlaps
-
- if (PrecisionUtils.equal(overlap.pi.a, 0, CHUNK_SHIFT - 1)
- && PrecisionUtils.equal(overlap.pi.b, 0, CHUNK_SHIFT - 1)
- || PrecisionUtils.equal(overlap.pi.a, 1, CHUNK_SHIFT - 1)
- && PrecisionUtils.equal(overlap.pi.b, 1, CHUNK_SHIFT - 1)
- || PrecisionUtils.equal(overlap.qi.a, 0, CHUNK_SHIFT - 1)
- && PrecisionUtils.equal(overlap.qi.b, 0, CHUNK_SHIFT - 1)
- || PrecisionUtils.equal(overlap.qi.a, 1, CHUNK_SHIFT - 1)
- && PrecisionUtils.equal(overlap.qi.b, 1, CHUNK_SHIFT - 1)) {
- // end-point-intersection
- return new IntervalPair(null, null, null, null);
- }
-
- return overlap;
- }
-
- return new IntervalPair(null, null, null, null);
+ private static boolean isNextTo(IntervalPair a, IntervalPair b, int shift) {
+ return isNextTo(a.pi, b.pi, shift) && isNextTo(a.qi, b.qi, shift);
}
private static void normalizeIntervalPairs(IntervalPair[] intervalPairs) {
@@ -621,6 +645,79 @@ public class BezierCurve extends AbstractGeometry implements ICurve {
&& PrecisionUtils.equal(p1.y, p2.y, shift);
}
+ /**
+ * Binary search from the intervals' limits to the intervals' inner values.
+ *
+ * @param overlap
+ * {@link IntervalPair} representing the overlap of two
+ * {@link BezierCurve}s
+ * @return refined overlap
+ */
+ private static IntervalPair refineOverlap(IntervalPair overlap) {
+ Interval piLo = refineOverlapLo(overlap.p, overlap.pi.a,
+ overlap.pi.getMid(), overlap.q);
+ Interval piHi = refineOverlapHi(overlap.p, overlap.pi.getMid(),
+ overlap.pi.b, overlap.q);
+ Interval qiLo = refineOverlapLo(overlap.q, overlap.qi.a,
+ overlap.qi.getMid(), overlap.p);
+ Interval qiHi = refineOverlapHi(overlap.q, overlap.qi.getMid(),
+ overlap.qi.b, overlap.p);
+ overlap.pi.a = piLo.b;
+ overlap.pi.b = piHi.a;
+ overlap.qi.a = qiLo.b;
+ overlap.qi.b = qiHi.a;
+ return overlap;
+ }
+
+ private static Interval refineOverlapHi(BezierCurve p, double mid,
+ double b, BezierCurve q) {
+ Interval i = new Interval(Math.max(mid, 0), Math.min(b, 1));
+ double prevLo;
+ Point pLo;
+ int c = 0;
+
+ while (c++ < 30 && !i.converges()) {
+ prevLo = i.a;
+ i.a = i.getMid();
+ pLo = p.get(i.a);
+
+ if (!q.contains(pLo)) {
+ i.b = i.a;
+ i.a = prevLo;
+ }
+ }
+
+ return i;
+ }
+
+ /**
+ * @param p
+ * @param a
+ * @param mid
+ * @param q
+ * @return
+ */
+ private static Interval refineOverlapLo(BezierCurve p, double a,
+ double mid, BezierCurve q) {
+ Interval i = new Interval(Math.max(a, 0), Math.min(mid, 1));
+ double prevHi;
+ Point pHi;
+ int c = 0;
+
+ while (c++ < 30 && !i.converges()) {
+ prevHi = i.b;
+ i.b = i.getMid();
+ pHi = p.get(i.b);
+
+ if (!q.contains(pHi)) {
+ i.a = i.b;
+ i.b = prevHi;
+ }
+ }
+
+ return i;
+ }
+
private Vector3D[] points;
private static final IPointCmp xminCmp = new IPointCmp() {
@@ -816,6 +913,27 @@ public class BezierCurve extends AbstractGeometry implements ICurve {
}
/**
+ * <p>
+ * Tests if this {@link BezierCurve} contains the given other
+ * {@link BezierCurve}.
+ * </p>
+ *
+ * <p>
+ * The other {@link BezierCurve} is regarded to be contained if its start
+ * and end {@link Point} lie on this {@link BezierCurve} and an overlapping
+ * segment of the two curves can be detected.
+ * </p>
+ *
+ * @param o
+ * @return <code>true</code> if the given {@link BezierCurve} is contained
+ * by this {@link BezierCurve}, otherwise <code>false</code>
+ */
+ public boolean contains(BezierCurve o) {
+ return contains(o.getP1()) && contains(o.getP2())
+ && getOverlap(o) != null;
+ }
+
+ /**
* Returns true if the given {@link Point} lies on this {@link BezierCurve}.
* Returns false, otherwise.
*
@@ -831,6 +949,23 @@ public class BezierCurve extends AbstractGeometry implements ICurve {
return containmentParameter(this, new double[] { 0, 1 }, p);
}
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof BezierCurve) {
+ BezierCurve o = (BezierCurve) obj;
+ BezierCurve t = this;
+ while (o.points.length < t.points.length)
+ o = o.getElevated();
+ while (t.points.length < o.points.length)
+ t = t.getElevated();
+ Point[] oPoints = o.getPoints();
+ Point[] tPoints = t.getPoints();
+ return PointListUtils.equals(oPoints, tPoints)
+ || PointListUtils.equalsReverse(oPoints, tPoints);
+ }
+ return false;
+ }
+
private void findEndPointIntersections(IntervalPair ip,
Set<IntervalPair> endPointIntervalPairs, Set<Point> intersections) {
final double CHUNK_SHIFT_EPSILON = PrecisionUtils
@@ -954,8 +1089,12 @@ public class BezierCurve extends AbstractGeometry implements ICurve {
if (L1 == null || L2 == null) {
// q is degenerated
Point poi = ip.q.getHC(ip.qi.getMid()).toPoint();
- if (ip.p.contains(poi)) {
+ double[] interval = new double[] { 0, 1 };
+ if (poi != null && containmentParameter(ip.p, interval, poi)) {
intersections.add(poi);
+ // intervalPairs.add(new IntervalPair(ip.p,
+ // new Interval(interval), ip.q, new Interval(ip.qi
+ // .getMid(), ip.qi.getMid())));
}
return;
}
@@ -1195,6 +1334,25 @@ public class BezierCurve extends AbstractGeometry implements ICurve {
}
/**
+ * Computes a {@link BezierCurve} with a degree of one higher than this
+ * {@link BezierCurve}'s degree but of the same shape.
+ *
+ * @return a {@link BezierCurve} of the same shape as this
+ * {@link BezierCurve} but with one more control {@link Point}
+ */
+ public BezierCurve getElevated() {
+ Point[] p = getPoints();
+ Point[] q = new Point[p.length + 1];
+ q[0] = p[0];
+ q[p.length] = p[p.length - 1];
+ for (int i = 1; i < p.length; i++) {
+ double c = (double) i / (double) (p.length);
+ q[i] = p[i - 1].getScaled(c).getTranslated(p[i].getScaled(1 - c));
+ }
+ return new BezierCurve(q);
+ }
+
+ /**
* Returns the {@link Point} at the given parameter value t.
*
* @param t
@@ -1259,29 +1417,43 @@ public class BezierCurve extends AbstractGeometry implements ICurve {
IntervalPair[] clusters = clusterChunks(
intervalPairs.toArray(new IntervalPair[] {}), 0);
- if (isOverlap(clusters,
- endPointIntervalPairs.toArray(new IntervalPair[] {})).p != null) {
- return new HashSet<IntervalPair>(0);
- }
+ IntervalPair overlapIntervalPair = extractOverlap(clusters,
+ endPointIntervalPairs.toArray(new IntervalPair[] {}));
+ BezierCurve overlap = overlapIntervalPair == null ? null
+ : overlapIntervalPair.getPClipped();
Set<IntervalPair> results = new HashSet<IntervalPair>();
- results.addAll(endPointIntervalPairs);
- outer: for (IntervalPair cluster : clusters) {
- for (IntervalPair epip : endPointIntervalPairs) {
- if (isNextTo(cluster, epip, CHUNK_SHIFT)) {
- continue outer;
+ for (IntervalPair epip : endPointIntervalPairs) {
+ if (overlapIntervalPair == null
+ || !isNextTo(overlapIntervalPair, epip, CHUNK_SHIFT)) {
+ results.add(epip);
+ } else {
+ for (Iterator<Point> iterator = intersections.iterator(); iterator
+ .hasNext();) {
+ if (overlap.contains(iterator.next())) {
+ iterator.remove();
+ }
}
}
+ }
+
+ outer: for (IntervalPair cluster : clusters) {
+ if (overlapIntervalPair != null)
+ if (isNextTo(overlapIntervalPair, cluster, CHUNK_SHIFT))
+ continue outer;
+
+ for (IntervalPair epip : endPointIntervalPairs)
+ if (isNextTo(cluster, epip, CHUNK_SHIFT))
+ continue outer;
// a.t.m. assume for every cluster just a single point of
// intersection:
Point poi = findSinglePreciseIntersection(cluster);
if (poi != null) {
+ intersections.add(poi);
if (cluster.converges()) {
results.add(cluster.getCopy());
- } else {
- intersections.add(poi);
}
}
}
@@ -1299,38 +1471,7 @@ public class BezierCurve extends AbstractGeometry implements ICurve {
*/
public Point[] getIntersections(BezierCurve other) {
Set<Point> intersections = new HashSet<Point>();
- Set<IntervalPair> intervalPairs = new HashSet<IntervalPair>();
- Set<IntervalPair> endPointIntervalPairs = new HashSet<IntervalPair>();
-
- IntervalPair ip = new IntervalPair(this, Interval.getFull(), other,
- Interval.getFull());
-
- findEndPointIntersections(ip, endPointIntervalPairs, intersections);
- findIntersectionChunks(ip, intervalPairs, intersections);
- normalizeIntervalPairs(intervalPairs.toArray(new IntervalPair[] {}));
- IntervalPair[] clusters = clusterChunks(
- intervalPairs.toArray(new IntervalPair[] {}), 0);
-
- if (isOverlap(clusters,
- endPointIntervalPairs.toArray(new IntervalPair[] {})).p != null) {
- return new Point[] {};
- }
-
- outer: for (IntervalPair cluster : clusters) {
- for (IntervalPair epip : endPointIntervalPairs) {
- if (isNextTo(cluster, epip, CHUNK_SHIFT)) {
- continue outer;
- }
- }
-
- // a.t.m. assume for every cluster just a single point of
- // intersection:
- Point poi = findSinglePreciseIntersection(cluster);
- if (poi != null) {
- intersections.add(poi);
- }
- }
-
+ getIntersectionIntervalPairs(other, intersections);
return intersections.toArray(new Point[] {});
}
@@ -1366,16 +1507,14 @@ public class BezierCurve extends AbstractGeometry implements ICurve {
findEndPointIntersections(ip, endPointIntervalPairs, intersections);
findIntersectionChunks(ip, intervalPairs, intersections);
- IntervalPair[] clusters = clusterChunks(
- intervalPairs.toArray(new IntervalPair[] {}), 0);
+ IntervalPair[] intervalPairs2 = intervalPairs
+ .toArray(new IntervalPair[] {});
+ normalizeIntervalPairs(intervalPairs2);
+ IntervalPair[] clusters = clusterChunks(intervalPairs2, 0);
- IntervalPair overlap = isOverlap(clusters,
+ IntervalPair overlap = extractOverlap(clusters,
endPointIntervalPairs.toArray(new IntervalPair[] {}));
-
- if (overlap.p != null) {
- return overlap.getPClipped();
- }
- return null;
+ return overlap == null ? null : overlap.getPClipped();
}
public Point getP1() {
@@ -1455,22 +1594,60 @@ public class BezierCurve extends AbstractGeometry implements ICurve {
return copy;
}
- /**
- * Creates a new {@link BezierCurve} with all points translated by the given
- * {@link Point}.
- *
- * @param p
- * @return a new {@link BezierCurve} with all points translated by the given
- * {@link Point}
- */
- public BezierCurve getTranslated(Point p) {
- Point[] translated = new Point[points.length];
+ public BezierCurve getRotatedCCW(Angle angle) {
+ return getCopy().rotateCCW(angle);
+ }
- for (int i = 0; i < translated.length; i++) {
- translated[i] = points[i].toPoint().getTranslated(p);
- }
+ public BezierCurve getRotatedCCW(Angle angle, double cx, double cy) {
+ return getCopy().rotateCCW(angle, cx, cy);
+ }
+
+ public BezierCurve getRotatedCCW(Angle angle, Point center) {
+ return getCopy().rotateCCW(angle, center);
+ }
+
+ public BezierCurve getRotatedCW(Angle angle) {
+ return getCopy().rotateCW(angle);
+ }
+
+ public BezierCurve getRotatedCW(Angle angle, double cx, double cy) {
+ return getCopy().rotateCW(angle, cx, cy);
+ }
+
+ public BezierCurve getRotatedCW(Angle angle, Point center) {
+ return getCopy().rotateCW(angle, center);
+ }
- return new BezierCurve(translated);
+ public BezierCurve getScaled(double factor) {
+ return getCopy().getScaled(factor);
+ }
+
+ public BezierCurve getScaled(double fx, double fy) {
+ return getCopy().getScaled(fx, fy);
+ }
+
+ public BezierCurve getScaled(double factor, double cx, double cy) {
+ return getCopy().getScaled(factor, cx, cy);
+ }
+
+ public BezierCurve getScaled(double fx, double fy, double cx, double cy) {
+ return getCopy().getScaled(fx, fy, cx, cy);
+ }
+
+ public BezierCurve getScaled(double fx, double fy, Point center) {
+ return getCopy().getScaled(fx, fy, center);
+ }
+
+ public BezierCurve getScaled(double factor, Point center) {
+ return getCopy().getScaled(factor, center);
+ }
+
+ public BezierCurve getTranslated(double dx, double dy) {
+ return getCopy().translate(dx, dy);
+ }
+
+ public BezierCurve getTranslated(Point d) {
+ return getCopy().translate(d.x, d.y);
}
public double getX1() {
@@ -1520,20 +1697,7 @@ public class BezierCurve extends AbstractGeometry implements ICurve {
* overlap, otherwise <code>false</code>
*/
public boolean overlaps(BezierCurve other) {
- Set<Point> intersections = new HashSet<Point>();
- Set<IntervalPair> intervalPairs = new HashSet<IntervalPair>();
- Set<IntervalPair> endPointIntervalPairs = new HashSet<IntervalPair>();
-
- IntervalPair ip = new IntervalPair(this, Interval.getFull(), other,
- Interval.getFull());
-
- findEndPointIntersections(ip, endPointIntervalPairs, intersections);
- findIntersectionChunks(ip, intervalPairs, intersections);
- IntervalPair[] clusters = clusterChunks(
- intervalPairs.toArray(new IntervalPair[] {}), 0);
-
- return isOverlap(clusters,
- endPointIntervalPairs.toArray(new IntervalPair[] {})).p != null;
+ return getOverlap(other) != null;
}
public final boolean overlaps(ICurve c) {
@@ -1545,16 +1709,75 @@ public class BezierCurve extends AbstractGeometry implements ICurve {
return false;
}
- /**
- * @param alpha
- * @param center
- */
- public void rotateCCW(Angle alpha, Point center) {
+ public BezierCurve rotateCCW(Angle angle) {
+ Point centroid = PointListUtils.computeCentroid(getPoints());
+ return rotateCCW(angle, centroid.x, centroid.y);
+ }
+
+ public BezierCurve rotateCCW(Angle angle, double cx, double cy) {
+ Point[] realPoints = getPoints();
+ PointListUtils.rotateCCW(realPoints, angle, cx, cy);
+ for (int i = 0; i < realPoints.length; i++) {
+ setPoint(i, realPoints[i]);
+ }
+ return this;
+ }
+
+ public BezierCurve rotateCCW(Angle alpha, Point center) {
for (int i = 0; i < points.length; i++) {
points[i] = new Vector3D(new Vector(points[i].toPoint()
.getTranslated(center.getNegated())).getRotatedCCW(alpha)
.toPoint().getTranslated(center));
}
+ return this;
+ }
+
+ public BezierCurve rotateCW(Angle angle) {
+ Point centroid = PointListUtils.computeCentroid(getPoints());
+ return rotateCW(angle, centroid.x, centroid.y);
+ }
+
+ public BezierCurve rotateCW(Angle angle, double cx, double cy) {
+ Point[] realPoints = getPoints();
+ PointListUtils.rotateCW(realPoints, angle, cx, cy);
+ for (int i = 0; i < realPoints.length; i++) {
+ setPoint(i, realPoints[i]);
+ }
+ return this;
+ }
+
+ public BezierCurve rotateCW(Angle angle, Point center) {
+ return rotateCW(angle, center.x, center.y);
+ }
+
+ public BezierCurve scale(double factor) {
+ return scale(factor, factor);
+ }
+
+ public BezierCurve scale(double fx, double fy) {
+ Point centroid = PointListUtils.computeCentroid(getPoints());
+ return scale(fx, fy, centroid.x, centroid.y);
+ }
+
+ public BezierCurve scale(double factor, double cx, double cy) {
+ return scale(factor, factor, cx, cy);
+ }
+
+ public BezierCurve scale(double fx, double fy, double cx, double cy) {
+ Point[] realPoints = getPoints();
+ PointListUtils.scale(realPoints, fx, fy, cx, cy);
+ for (int i = 0; i < realPoints.length; i++) {
+ setPoint(i, realPoints[i]);
+ }
+ return this;
+ }
+
+ public BezierCurve scale(double fx, double fy, Point center) {
+ return scale(fx, fy, center.x, center.y);
+ }
+
+ public BezierCurve scale(double factor, Point center) {
+ return scale(factor, factor, center.x, center.y);
}
/**
@@ -1641,15 +1864,17 @@ public class BezierCurve extends AbstractGeometry implements ICurve {
/**
* Returns a hard approximation of this {@link BezierCurve} as a
* {@link CubicCurve}. The new {@link CubicCurve} is constructed from the
- * first four {@link Point}s in this {@link BezierCurve}'s {@link Point}s
- * array. If this {@link BezierCurve} is not of degree four or higher, i.e.
- * it does not have four or more control {@link Point}s (including start and
- * end {@link Point}), <code>null</code> is returned.
+ * first three {@link Point}s in this {@link BezierCurve}'s {@link Point}s
+ * array and the end {@link Point} of this {@link BezierCurve}. If this
+ * {@link BezierCurve} is not of degree four or higher, i.e. it does not
+ * have four or more control {@link Point}s (including start and end
+ * {@link Point}), <code>null</code> is returned.
*
- * @return a new {@link CubicCurve} that is constructed by the first four
- * control {@link Point}s of this {@link BezierCurve} or
- * <code>null</code> if this {@link BezierCurve} does not have at
- * least four control {@link Point}s
+ * @return a new {@link CubicCurve} that is constructed by the first three
+ * {@link Point}s and the end {@link Point} of this
+ * {@link BezierCurve} or <code>null</code> if this
+ * {@link BezierCurve} does not have at least four control
+ * {@link Point}s
*/
public CubicCurve toCubic() {
if (points.length > 3) {
@@ -1819,15 +2044,17 @@ public class BezierCurve extends AbstractGeometry implements ICurve {
/**
* Returns a hard approximation of this {@link BezierCurve} as a
* {@link QuadraticCurve}. The new {@link QuadraticCurve} is constructed
- * from the first three {@link Point}s in this {@link BezierCurve}'s
- * {@link Point}s array. If this {@link BezierCurve} is not of degree three
+ * from the first two {@link Point}s in this {@link BezierCurve}'s
+ * {@link Point}s array and the end {@link Point} of this
+ * {@link BezierCurve}. If this {@link BezierCurve} is not of degree three
* or higher, i.e. it does not have three or more control {@link Point}s
* (including start and end {@link Point}), <code>null</code> is returned.
*
- * @return a new {@link QuadraticCurve} that is constructed by the first
- * three control {@link Point}s of this {@link BezierCurve} or
- * <code>null</code> if this {@link BezierCurve} does not have at
- * least three control {@link Point}s
+ * @return a new {@link QuadraticCurve} that is constructed by the first two
+ * {@link Point}s and the end {@link Point} of this
+ * {@link BezierCurve} or <code>null</code> if this
+ * {@link BezierCurve} does not have at least three control
+ * {@link Point}s
*/
public QuadraticCurve toQuadratic() {
if (points.length > 2) {
@@ -1837,261 +2064,17 @@ public class BezierCurve extends AbstractGeometry implements ICurve {
return null;
}
- // double x1;
- // double y1;
- // double x2;
- // double y2;
- //
- // // TODO: use point array instead
- // double[] ctrlCoordinates = null;
- //
- // public BezierCurve(double... coordinates) {
- // if (coordinates.length < 4) {
- // throw new IllegalArgumentException(
- // "A bezier curve needs at least a start and an end point");
- // }
- // this.x1 = coordinates[0];
- // this.y1 = coordinates[1];
- // this.x2 = coordinates[coordinates.length - 2];
- // this.y2 = coordinates[coordinates.length - 1];
- // if (coordinates.length > 4) {
- // this.ctrlCoordinates = new double[coordinates.length - 4];
- // System.arraycopy(coordinates, 2, ctrlCoordinates, 0,
- // coordinates.length - 4);
- // }
- // }
- //
- // public BezierCurve(Point... points) {
- // this(PointListUtils.toCoordinatesArray(points));
- // }
- //
- // public final boolean contains(Rectangle r) {
- // // TODO: may contain the rectangle only in case the rectangle is
- // // degenerated...
- // return false;
- // }
- //
- // public Point getCtrl(int i) {
- // return new Point(getCtrlX(i), getCtrlY(i));
- // }
- //
- // /**
- // * Returns the point-wise coordinates (i.e. x1, y1, x2, y2, etc.) of the
- // * inner control points of this {@link BezierCurve}, i.e. exclusive of the
- // * start and end points.
- // *
- // * @see BezierCurve#getCtrls()
- // *
- // * @return an array containing the inner control points' coordinates
- // */
- // public double[] getCtrlCoordinates() {
- // return PointListUtils.getCopy(ctrlCoordinates);
- //
- // }
- //
- // /**
- // * Returns an array of points representing the inner control points of
- // this
- // * curve, i.e. excluding the start and end points. In case of s linear
- // * curve, no control points will be returned, in case of a quadratic
- // curve,
- // * one control point, and so on.
- // *
- // * @return an array of points with the coordinates of the inner control
- // * points of this {@link BezierCurve}, i.e. exclusive of the start
- // * and end point. The number of control points will depend on the
- // * degree ({@link #getDegree()}) of the curve, so in case of a line
- // * (linear curve) the array will be empty, in case of a quadratic
- // * curve, it will be of size <code>1</code>, in case of a cubic
- // * curve of size <code>2</code>, etc..
- // */
- // public Point[] getCtrls() {
- // return PointListUtils.toPointsArray(ctrlCoordinates);
- // }
- //
- // public double getCtrlX(int i) {
- // return ctrlCoordinates[2 * i];
- // }
- //
- // public double getCtrlY(int i) {
- // return ctrlCoordinates[2 * i + 1];
- // }
- //
- // /**
- // * Returns the degree of this curve which corresponds to the number of
- // * overall control points (including start and end point) used to define
- // the
- // * curve. The degree is zero-based, so a line (linear curve) will have
- // * degree <code>1</code>, a quadratic curve will have degree
- // <code>2</code>,
- // * and so on. <code>1</code> in case of a
- // *
- // * @return The degree of this {@link ICurve}, which corresponds to the
- // * zero-based overall number of control points (including start and
- // * end point) used to define this {@link ICurve}.
- // */
- // public int getDegree() {
- // return getCtrls().length + 1;
- // }
- //
- // /**
- // * Returns an array of points that represent this {@link BezierCurve},
- // i.e.
- // * the start point, the inner control points, and the end points.
- // *
- // * @return an array of points representing the control points (including
- // * start and end point) of this {@link BezierCurve}
- // */
- // public Point[] getPoints() {
- // Point[] points = new Point[ctrlCoordinates.length / 2 + 2];
- // points[0] = new Point(x1, y1);
- // points[points.length - 1] = new Point(x2, y2);
- // for (int i = 1; i < points.length - 1; i++) {
- // points[i] = new Point(ctrlCoordinates[2 * i - 2],
- // ctrlCoordinates[2 * i - 1]);
- // }
- // return points;
- // }
- //
- // /**
- // * {@inheritDoc}
- // *
- // * @see org.eclipse.gef4.geometry.planar.ICurve#getP1()
- // */
- // public Point getP1() {
- // return new Point(x1, y1);
- // }
- //
- // /**
- // * {@inheritDoc}
- // *
- // * @see org.eclipse.gef4.geometry.planar.ICurve#getP2()
- // */
- // public Point getP2() {
- // return new Point(x2, y2);
- // }
- //
- // /**
- // * {@inheritDoc}
- // *
- // * @see org.eclipse.gef4.geometry.planar.ICurve#getX1()
- // */
- // public double getX1() {
- // return x1;
- // }
- //
- // /**
- // * {@inheritDoc}
- // *
- // * @see org.eclipse.gef4.geometry.planar.ICurve#getX2()
- // */
- // public double getX2() {
- // return x2;
- // }
- //
- // /**
- // * {@inheritDoc}
- // *
- // * @see org.eclipse.gef4.geometry.planar.ICurve#getY1()
- // */
- // public double getY1() {
- // return y1;
- // }
- //
- // /**
- // * {@inheritDoc}
- // *
- // * @see org.eclipse.gef4.geometry.planar.ICurve#getY2()
- // */
- // public double getY2() {
- // return y2;
- // }
- //
- // protected void setCtrl(int i, Point p) {
- // setCtrlX(i, p.x);
- // setCtrlY(i, p.y);
- // }
- //
- // public void setCtrls(Point... ctrls) {
- // ctrlCoordinates = PointListUtils.toCoordinatesArray(ctrls);
- // }
- //
- // protected void setCtrlX(int i, double x) {
- // // TODO: enlarge array if its too small
- // ctrlCoordinates[2 * i] = x;
- // }
- //
- // protected void setCtrlY(int i, double y) {
- // // TODO: enlarge array if its too small
- // ctrlCoordinates[2 * i + 1] = y;
- // }
- //
- // /**
- // * Sets the start {@link Point} of this {@link BezierCurve} to the given
- // * {@link Point} p1.
- // *
- // * @param p1
- // * the new start {@link Point}
- // */
- // public void setP1(Point p1) {
- // this.x1 = p1.x;
- // this.y1 = p1.y;
- // }
- //
- // /**
- // * Sets the end {@link Point} of this {@link BezierCurve} to the given
- // * {@link Point} p2.
- // *
- // * @param p2
- // * the new end {@link Point}
- // */
- // public void setP2(Point p2) {
- // this.x2 = p2.x;
- // this.y2 = p2.y;
- // }
- //
- // /**
- // * Sets the x-coordinate of the start {@link Point} of this
- // * {@link BezierCurve} to x1.
- // *
- // * @param x1
- // * the new start {@link Point}'s x-coordinate
- // */
- // public void setX1(double x1) {
- // this.x1 = x1;
- // }
- //
- // /**
- // * Sets the x-coordinate of the end {@link Point} of this
- // * {@link BezierCurve} to x2.
- // *
- // * @param x2
- // * the new end {@link Point}'s x-coordinate
- // */
- // public void setX2(double x2) {
- // this.x2 = x2;
- // }
- //
- // /**
- // * Sets the y-coordinate of the start {@link Point} of this
- // * {@link BezierCurve} to y1.
- // *
- // * @param y1
- // * the new start {@link Point}'s y-coordinate
- // */
- // public void setY1(double y1) {
- // this.y1 = y1;
- // }
- //
- // /**
- // * Sets the y-coordinate of the end {@link Point} of this
- // * {@link BezierCurve} to y2.
- // *
- // * @param y2
- // * the new end {@link Point}'s y-coordinate
- // */
- // public void setY2(double y2) {
- // this.y2 = y2;
- // }
+ public BezierCurve translate(double dx, double dy) {
+ Point[] realPoints = getPoints();
+ PointListUtils.translate(realPoints, dx, dy);
+ for (int i = 0; i < realPoints.length; i++) {
+ setPoint(i, realPoints[i]);
+ }
+ return this;
+ }
+
+ public BezierCurve translate(Point d) {
+ return translate(d.x, d.y);
+ }
}
diff --git a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/BezierSpline.java b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/BezierSpline.java
index 0373f1e..ce94af9 100644
--- a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/BezierSpline.java
+++ b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/BezierSpline.java
@@ -14,7 +14,7 @@ package org.eclipse.gef4.geometry.planar;
import org.eclipse.gef4.geometry.Point;
import org.eclipse.gef4.geometry.transform.AffineTransform;
-public class BezierSpline implements ICurve {
+public class BezierSpline extends AbstractGeometry implements ICurve {
public boolean contains(Point p) {
// TODO Auto-generated method stub
diff --git a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/CubicCurve.java b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/CubicCurve.java
index e77beb5..9fe1bd9 100644
--- a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/CubicCurve.java
+++ b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/CubicCurve.java
@@ -14,13 +14,14 @@ package org.eclipse.gef4.geometry.planar;
import org.eclipse.gef4.geometry.Point;
import org.eclipse.gef4.geometry.transform.AffineTransform;
-import org.eclipse.gef4.geometry.utils.PolynomCalculationUtils;
/**
* Represents the geometric shape of a cubic Bézier curve.
*
- * @author anyssen
+ * TODO: Overwrite all BezierCurve methods that return a BezierCurve and add a
+ * cast to a CubicCurve. OR: Make BezierCurve parameterized
*
+ * @author anyssen
*/
public class CubicCurve extends BezierCurve {
@@ -124,70 +125,40 @@ public class CubicCurve extends BezierCurve {
}
/**
+ * Erroneous getBounds() implementation... use the generic one instead.
+ *
+ * TODO: find out why the mathematical solution is erroneous in some cases.
+ *
* @see IGeometry#getBounds()
*/
- @Override
- public Rectangle getBounds() {
- // extremes of the x(t) and y(t) functions:
- double[] xts;
- try {
- xts = PolynomCalculationUtils.getQuadraticRoots(-3 * getX1() + 9
- * getCtrlX1() - 9 * getCtrlX2() + 3 * getX2(), 6 * getX1()
- - 12 * getCtrlX1() + 6 * getCtrlX2(), 3 * getCtrlX1() - 3
- * getX1());
- } catch (ArithmeticException x) {
- return new Rectangle(getP1(), getP2());
- }
-
- double xmin = getX1(), xmax = getX1();
- if (getX2() < xmin) {
- xmin = getX2();
- } else {
- xmax = getX2();
- }
-
- for (double t : xts) {
- if (t >= 0 && t <= 1) {
- double x = get(t).x;
- if (x < xmin) {
- xmin = x;
- } else if (x > xmax) {
- xmax = x;
- }
- }
- }
-
- double[] yts;
- try {
- yts = PolynomCalculationUtils.getQuadraticRoots(-3 * getY1() + 9
- * getCtrlY1() - 9 * getCtrlY2() + 3 * getY2(), 6 * getY1()
- - 12 * getCtrlY1() + 6 * getCtrlY2(), 3 * getCtrlY1() - 3
- * getY1());
- } catch (ArithmeticException x) {
- return new Rectangle(new Point(xmin, getP1().y), new Point(xmax,
- getP2().y));
- }
-
- double ymin = getY1(), ymax = getY1();
- if (getY2() < ymin) {
- ymin = getY2();
- } else {
- ymax = getY2();
- }
-
- for (double t : yts) {
- if (t >= 0 && t <= 1) {
- double y = get(t).y;
- if (y < ymin) {
- ymin = y;
- } else if (y > ymax) {
- ymax = y;
- }
- }
- }
-
- return new Rectangle(new Point(xmin, ymin), new Point(xmax, ymax));
- }
+ /*
+ * public Rectangle getBounds() { // extremes of the x(t) and y(t)
+ * functions: double[] xts; try { xts =
+ * PolynomCalculationUtils.getQuadraticRoots(-3 * getX1() + 9 getCtrlX1() -
+ * 9 * getCtrlX2() + 3 * getX2(), 6 * getX1() - 12 * getCtrlX1() + 6 *
+ * getCtrlX2(), 3 * getCtrlX1() - 3 getX1()); } catch (ArithmeticException
+ * x) { return new Rectangle(getP1(), getP2()); }
+ *
+ * double xmin = getX1(), xmax = getX1(); if (getX2() < xmin) { xmin =
+ * getX2(); } else { xmax = getX2(); }
+ *
+ * for (double t : xts) { if (t >= 0 && t <= 1) { double x = get(t).x; if (x
+ * < xmin) { xmin = x; } else if (x > xmax) { xmax = x; } } }
+ *
+ * double[] yts; try { yts = PolynomCalculationUtils.getQuadraticRoots(-3 *
+ * getY1() + 9 getCtrlY1() - 9 * getCtrlY2() + 3 * getY2(), 6 * getY1() - 12
+ * * getCtrlY1() + 6 * getCtrlY2(), 3 * getCtrlY1() - 3 getY1()); } catch
+ * (ArithmeticException x) { return new Rectangle(new Point(xmin,
+ * getP1().y), new Point(xmax, getP2().y)); }
+ *
+ * double ymin = getY1(), ymax = getY1(); if (getY2() < ymin) { ymin =
+ * getY2(); } else { ymax = getY2(); }
+ *
+ * for (double t : yts) { if (t >= 0 && t <= 1) { double y = get(t).y; if (y
+ * < ymin) { ymin = y; } else if (y > ymax) { ymax = y; } } }
+ *
+ * return new Rectangle(new Point(xmin, ymin), new Point(xmax, ymax)); }
+ */
private Polygon getControlPolygon() {
return new Polygon(getP1(), getCtrl1(), getCtrl2(), getP2());
diff --git a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Ellipse.java b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Ellipse.java
index e9d41ce..868f8be 100644
--- a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Ellipse.java
+++ b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Ellipse.java
@@ -18,6 +18,7 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
+import org.eclipse.gef4.geometry.Angle;
import org.eclipse.gef4.geometry.Point;
import org.eclipse.gef4.geometry.transform.AffineTransform;
import org.eclipse.gef4.geometry.utils.CurveUtils;
@@ -34,8 +35,8 @@ import org.eclipse.gef4.geometry.utils.PrecisionUtils;
* @author anyssen
* @author Matthias Wienand
*/
-public class Ellipse extends AbstractRectangleBasedGeometry<Ellipse> implements
- IShape {
+public class Ellipse extends
+ AbstractRectangleBasedGeometry<Ellipse, PolyBezier> implements IShape {
private static final long serialVersionUID = 1L;
@@ -344,40 +345,15 @@ public class Ellipse extends AbstractRectangleBasedGeometry<Ellipse> implements
* @return border-segments
*/
public CubicCurve[] getOutlineSegments() {
- CubicCurve[] segs = new CubicCurve[4];
- // see http://whizkidtech.redprince.net/bezier/circle/kappa/ for details
- // on the approximation used here
- final double kappa = 4.0d * (Math.sqrt(2.0d) - 1.0d) / 3.0d;
- double a = width / 2;
- double b = height / 2;
-
- double ox = x + a;
- double oy = y;
-
- segs[0] = new CubicCurve(ox, oy, x + a + kappa * a, y, x + width, y + b
- - kappa * b, x + width, y + b);
-
- ox = x + width;
- oy = y + b;
-
- segs[1] = new CubicCurve(ox, oy, x + width, y + b + kappa * b, x + a
- + kappa * a, y + height, x + a, y + height);
-
- ox = x + a;
- oy = y + height;
-
- segs[2] = new CubicCurve(ox, oy, x + width / 2 - kappa * width / 2, y
- + height, x, y + height / 2 + kappa * height / 2, x, y + height
- / 2);
-
- ox = x;
- oy = y + height / 2;
-
- segs[3] = new CubicCurve(ox, oy, x,
- y + height / 2 - kappa * height / 2, x + width / 2 - kappa
- * width / 2, y, x + width / 2, y);
-
- return segs;
+ return new CubicCurve[] {
+ CurveUtils.computeEllipticalArcApproximation(x, y, width,
+ height, Angle.fromDeg(0), Angle.fromDeg(90)),
+ CurveUtils.computeEllipticalArcApproximation(x, y, width,
+ height, Angle.fromDeg(90), Angle.fromDeg(180)),
+ CurveUtils.computeEllipticalArcApproximation(x, y, width,
+ height, Angle.fromDeg(180), Angle.fromDeg(270)),
+ CurveUtils.computeEllipticalArcApproximation(x, y, width,
+ height, Angle.fromDeg(270), Angle.fromDeg(360)), };
}
/**
@@ -398,26 +374,44 @@ public class Ellipse extends AbstractRectangleBasedGeometry<Ellipse> implements
public Path toPath() {
// see http://whizkidtech.redprince.net/bezier/circle/kappa/ for details
// on the approximation used here
- final double kappa = 4.0d * (Math.sqrt(2.0d) - 1.0d) / 3.0d;
- final Path p = new Path();
- double a = width / 2;
- double b = height / 2;
- p.moveTo(x + a, y);
- p.curveTo(x + a + kappa * a, y, x + width, y + b - kappa * b,
- x + width, y + b);
- p.curveTo(x + width, y + b + kappa * b, x + a + kappa * a, y + height,
- x + a, y + height);
- p.curveTo(x + width / 2 - kappa * width / 2, y + height, x, y + height
- / 2 + kappa * height / 2, x, y + height / 2);
- p.curveTo(x, y + height / 2 - kappa * height / 2, x + width / 2 - kappa
- * width / 2, y, x + width / 2, y);
- return p;
+ return CurveUtils.toPath(CurveUtils.computeEllipticalArcApproximation(
+ x, y, width, height, Angle.fromDeg(0), Angle.fromDeg(90)),
+ CurveUtils.computeEllipticalArcApproximation(x, y, width,
+ height, Angle.fromDeg(90), Angle.fromDeg(180)),
+ CurveUtils.computeEllipticalArcApproximation(x, y, width,
+ height, Angle.fromDeg(180), Angle.fromDeg(270)),
+ CurveUtils.computeEllipticalArcApproximation(x, y, width,
+ height, Angle.fromDeg(270), Angle.fromDeg(360)));
}
@Override
public String toString() {
- return "Ellipse: (" + x + ", " + y + ", " + //$NON-NLS-3$//$NON-NLS-2$//$NON-NLS-1$
+ return "Ellipse (" + x + ", " + y + ", " + //$NON-NLS-3$//$NON-NLS-2$//$NON-NLS-1$
width + ", " + height + ")";//$NON-NLS-2$//$NON-NLS-1$
}
+ public PolyBezier getRotatedCCW(Angle angle) {
+ return new PolyBezier(getOutlineSegments()).rotateCCW(angle);
+ }
+
+ public PolyBezier getRotatedCCW(Angle angle, double cx, double cy) {
+ return new PolyBezier(getOutlineSegments()).rotateCCW(angle, cx, cy);
+ }
+
+ public PolyBezier getRotatedCCW(Angle angle, Point center) {
+ return new PolyBezier(getOutlineSegments()).rotateCCW(angle, center);
+ }
+
+ public PolyBezier getRotatedCW(Angle angle) {
+ return new PolyBezier(getOutlineSegments()).rotateCW(angle);
+ }
+
+ public PolyBezier getRotatedCW(Angle angle, double cx, double cy) {
+ return new PolyBezier(getOutlineSegments()).rotateCW(angle, cx, cy);
+ }
+
+ public PolyBezier getRotatedCW(Angle angle, Point center) {
+ return new PolyBezier(getOutlineSegments()).rotateCW(angle, center);
+ }
+
}
diff --git a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/IPolyShape.java b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/IPolyShape.java
index 3898f50..bd3e9af 100644
--- a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/IPolyShape.java
+++ b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/IPolyShape.java
@@ -25,6 +25,30 @@ public interface IPolyShape extends IGeometry {
*/
IShape[] getShapes();
- // contains()
+ /**
+ * <p>
+ * Computes the outline segments of this {@link IPolyShape}.
+ * </p>
+ *
+ * <p>
+ * Each {@link ICurve} segment of the outline of the internal {@link IShape}
+ * s can be either an inner segment or an outer segment. This method
+ * extracts only the outer segments. The segments bordering voids are
+ * considered to be outer segments, too.
+ * </p>
+ *
+ * @return the outline segments of this {@link IPolyShape}
+ */
+ public ICurve[] getOutlineSegments();
+
+ /**
+ * Checks if the given {@link IGeometry} is fully contained by this
+ * {@link IPolyShape}.
+ *
+ * @param g
+ * @return <code>true</code> if the {@link IGeometry} is contained by this
+ * {@link IPolyShape}, otherwise <code>false</code>
+ */
+ public boolean contains(final IGeometry g);
}
diff --git a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Line.java b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Line.java
index 7ab0e71..49948ff 100644
--- a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Line.java
+++ b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Line.java
@@ -12,6 +12,9 @@
*******************************************************************************/
package org.eclipse.gef4.geometry.planar;
+import java.util.HashSet;
+import java.util.Set;
+
import org.eclipse.gef4.geometry.Point;
import org.eclipse.gef4.geometry.euclidean.Straight;
import org.eclipse.gef4.geometry.euclidean.Vector;
@@ -198,6 +201,43 @@ public class Line extends BezierCurve {
: null;
}
+ /**
+ * Provides an optimized version of the
+ * {@link BezierCurve#getIntersectionIntervalPairs(BezierCurve, Set)}
+ * method.
+ *
+ * @param other
+ * @param intersections
+ * @return see
+ * {@link BezierCurve#getIntersectionIntervalPairs(BezierCurve, Set)}
+ */
+ public Set<IntervalPair> getIntersectionIntervalPairs(Line other,
+ Set<Point> intersections) {
+ Straight s1 = new Straight(this);
+ Straight s2 = new Straight(other);
+ Vector vi = s1.getIntersection(s2);
+ if (vi != null) {
+ Point pi = vi.toPoint();
+ if (contains(pi)) {
+ double param1 = s1.getParameterAt(pi);
+ double param2 = s2.getParameterAt(pi);
+ HashSet<IntervalPair> intervalPairs = new HashSet<IntervalPair>();
+ intervalPairs.add(new IntervalPair(this, new Interval(param1,
+ param1), other, new Interval(param2, param2)));
+ return intervalPairs;
+ }
+ }
+ return new HashSet<IntervalPair>();
+ }
+
+ public Set<IntervalPair> getIntersectionIntervalPairs(BezierCurve other,
+ Set<Point> intersections) {
+ if (other instanceof Line) {
+ return getIntersectionIntervalPairs((Line) other, intersections);
+ }
+ return super.getIntersectionIntervalPairs(other, intersections);
+ }
+
@Override
public Point[] getIntersections(BezierCurve curve) {
if (curve instanceof Line) {
@@ -233,6 +273,13 @@ public class Line extends BezierCurve {
return new Line(transformed[0], transformed[1]);
}
+ /**
+ * Provides an optimized version of the
+ * {@link BezierCurve#intersects(ICurve)} method.
+ *
+ * @param l
+ * @return see {@link BezierCurve#intersects(ICurve)}
+ */
public boolean intersects(Line l) {
return getIntersection(l) != null;
}
diff --git a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Pie.java b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Pie.java
index effb8f7..90c69d2 100644
--- a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Pie.java
+++ b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Pie.java
@@ -7,28 +7,110 @@
*
* Contributors:
* Alexander Nyßen (itemis AG) - initial API and implementation
+ * Matthias Wienand (itemis AG) - contribution for Bugzilla #355997
*
*******************************************************************************/
package org.eclipse.gef4.geometry.planar;
+import org.eclipse.gef4.geometry.Angle;
import org.eclipse.gef4.geometry.Point;
+import org.eclipse.gef4.geometry.euclidean.Vector;
+import org.eclipse.gef4.geometry.utils.CurveUtils;
+import org.eclipse.gef4.geometry.utils.PrecisionUtils;
-public class Pie extends AbstractGeometry implements IGeometry {
+/**
+ * The {@link Pie} is a closed {@link AbstractArcBasedGeometry}. It is the
+ * complement of the {@link Arc}, which is an open
+ * {@link AbstractArcBasedGeometry}.
+ *
+ * The {@link Pie} covers an area, therefore it implements the {@link IShape}
+ * interface.
+ */
+public class Pie extends AbstractArcBasedGeometry<Pie> implements IShape {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructs a new {@link Pie} from the given values.
+ *
+ * @see AbstractArcBasedGeometry#AbstractArcBasedGeometry(double, double,
+ * double, double, Angle, Angle)
+ *
+ * @param x
+ * @param y
+ * @param width
+ * @param height
+ * @param startAngle
+ * @param angularExtent
+ */
+ public Pie(double x, double y, double width, double height,
+ Angle startAngle, Angle angularExtent) {
+ super(x, y, width, height, startAngle, angularExtent);
+ }
+
+ /**
+ * @see org.eclipse.gef4.geometry.planar.IGeometry#getCopy()
+ */
+ public Pie getCopy() {
+ return new Pie(x, y, width, height, startAngle, angularExtent);
+ }
+
+ public PolyBezier getOutline() {
+ return new PolyBezier(computeBezierApproximation());
+ }
+
+ public CubicCurve[] getOutlineSegments() {
+ return computeBezierApproximation();
+ }
public boolean contains(Point p) {
- throw new UnsupportedOperationException("Not yet implemented.");
+ // check if the point is in the arc's angle
+ Angle pAngle = new Vector(1, 0)
+ .getAngleCCW(new Vector(getCentroid(), p));
+ if (!(PrecisionUtils.greater(pAngle.rad(), startAngle.rad()) && PrecisionUtils
+ .smaller(pAngle.rad(), startAngle.getAdded(angularExtent).rad()))) {
+ return false;
+ }
+
+ // angle is correct, check if the point is inside the bounding ellipse
+ return new Ellipse(x, y, width, height).contains(p);
}
- public Rectangle getBounds() {
- throw new UnsupportedOperationException("Not yet implemented.");
+ public boolean contains(IGeometry g) {
+ return CurveUtils.contains(this, g);
}
public Path toPath() {
- throw new UnsupportedOperationException("Not yet implemented.");
+ CubicCurve[] arc = computeBezierApproximation();
+ Line endToMid = new Line(arc[arc.length - 1].getP2(), getCentroid());
+ Line midToStart = new Line(getCentroid(), arc[0].getP1());
+ ICurve[] curves = new ICurve[arc.length + 2];
+ for (int i = 0; i < arc.length; i++) {
+ curves[i] = arc[i];
+ }
+ curves[arc.length] = endToMid;
+ curves[arc.length + 1] = midToStart;
+ return CurveUtils.toPath(curves);
+ }
+
+ public Path getRotatedCCW(Angle angle, double cx, double cy) {
+ return new PolyBezier(computeBezierApproximation()).rotateCCW(angle,
+ cx, cy).toPath();
+ }
+
+ public Path getRotatedCCW(Angle angle, Point center) {
+ return new PolyBezier(computeBezierApproximation()).rotateCCW(angle,
+ center).toPath();
+ }
+
+ public Path getRotatedCW(Angle angle, double cx, double cy) {
+ return new PolyBezier(computeBezierApproximation()).rotateCW(angle, cx,
+ cy).toPath();
}
- public IGeometry getCopy() {
- throw new UnsupportedOperationException("Not yet implemented.");
+ public Path getRotatedCW(Angle angle, Point center) {
+ return new PolyBezier(computeBezierApproximation()).rotateCW(angle,
+ center).toPath();
}
}
diff --git a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/PolyBezier.java b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/PolyBezier.java
index 5124233..4d487ad 100644
--- a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/PolyBezier.java
+++ b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/PolyBezier.java
@@ -11,16 +11,37 @@
*******************************************************************************/
package org.eclipse.gef4.geometry.planar;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import org.eclipse.gef4.geometry.Angle;
import org.eclipse.gef4.geometry.Point;
+import org.eclipse.gef4.geometry.transform.IRotatable;
+import org.eclipse.gef4.geometry.transform.IScalable;
+import org.eclipse.gef4.geometry.transform.ITranslatable;
import org.eclipse.gef4.geometry.utils.CurveUtils;
+import org.eclipse.gef4.geometry.utils.PointListUtils;
/**
* A {@link PolyBezier} is an {@link IPolyCurve} which consists of one or more
* connected {@link BezierCurve}s.
*/
-public class PolyBezier extends AbstractGeometry implements IPolyCurve {
+public class PolyBezier extends AbstractGeometry implements IPolyCurve,
+ ITranslatable<PolyBezier>, IScalable<PolyBezier>,
+ IRotatable<PolyBezier> {
private static final long serialVersionUID = 1L;
+
+ private static BezierCurve[] copy(BezierCurve... beziers) {
+ BezierCurve[] copy = new BezierCurve[beziers.length];
+
+ for (int i = 0; i < beziers.length; i++) {
+ copy[i] = beziers[i].getCopy();
+ }
+
+ return copy;
+ }
+
private BezierCurve[] beziers;
/**
@@ -54,45 +75,96 @@ public class PolyBezier extends AbstractGeometry implements IPolyCurve {
return bounds;
}
- public Path toPath() {
- // TODO: need a Path.append(Path)
- throw new UnsupportedOperationException("Not yet implemented.");
+ public PolyBezier getCopy() {
+ return new PolyBezier(beziers);
}
- public IGeometry getCopy() {
- return new PolyBezier(beziers);
+ public BezierCurve[] getCurves() {
+ return copy(beziers);
}
- public double getY2() {
- return getP2().y;
+ public Point[] getIntersections(ICurve g) {
+ return CurveUtils.getIntersections(g, this);
}
- public double getY1() {
- return getP1().y;
+ public Point getP1() {
+ return beziers[0].getP1();
}
- public double getX2() {
- return getP2().x;
+ public Point getP2() {
+ return beziers[beziers.length - 1].getP2();
}
- public double getX1() {
- return getP1().x;
+ public PolyBezier getRotatedCCW(Angle angle) {
+ return getCopy().rotateCCW(angle);
}
- public Point getP2() {
- return beziers[beziers.length - 1].getP2();
+ public PolyBezier getRotatedCCW(Angle angle, double cx, double cy) {
+ return getCopy().getRotatedCCW(angle, cx, cy);
}
- public Point getP1() {
- return beziers[0].getP1();
+ public PolyBezier getRotatedCCW(Angle angle, Point center) {
+ return getCopy().getRotatedCCW(angle, center);
}
- public BezierCurve[] toBezier() {
- return copy(beziers);
+ public PolyBezier getRotatedCW(Angle angle) {
+ return getCopy().getRotatedCW(angle);
}
- public Point[] getIntersections(ICurve g) {
- return CurveUtils.getIntersections(g, this);
+ public PolyBezier getRotatedCW(Angle angle, double cx, double cy) {
+ return getCopy().getRotatedCW(angle, cx, cy);
+ }
+
+ public PolyBezier getRotatedCW(Angle angle, Point center) {
+ return getCopy().getRotatedCW(angle, center);
+ }
+
+ public PolyBezier getScaled(double factor) {
+ return getCopy().scale(factor);
+ }
+
+ public PolyBezier getScaled(double fx, double fy) {
+ return getCopy().scale(fx, fy);
+ }
+
+ public PolyBezier getScaled(double factor, double cx, double cy) {
+ return getCopy().scale(factor, cx, cy);
+ }
+
+ public PolyBezier getScaled(double fx, double fy, double cx, double cy) {
+ return getCopy().scale(fx, fy, cx, cy);
+ }
+
+ public PolyBezier getScaled(double fx, double fy, Point center) {
+ return getCopy().scale(fx, fy, center);
+ }
+
+ public PolyBezier getScaled(double factor, Point center) {
+ return getCopy().scale(factor, center);
+ }
+
+ public PolyBezier getTranslated(double dx, double dy) {
+ return getCopy().translate(dx, dy);
+ }
+
+ public PolyBezier getTranslated(Point d) {
+ return getCopy().translate(d.x, d.y);
+ }
+
+ public double getX1() {
+ return getP1().x;
+ }
+
+ public double getX2() {
+ return getP2().x;
+ }
+
+ public double getY1() {
+ return getP1().y;
+ }
+
+ public double getY2() {
+ return getP2().y;
}
public boolean intersects(ICurve c) {
@@ -103,18 +175,98 @@ public class PolyBezier extends AbstractGeometry implements IPolyCurve {
return CurveUtils.overlaps(c, this);
}
- public BezierCurve[] getCurves() {
+ public PolyBezier rotateCCW(Angle angle) {
+ ArrayList<Point> points = new ArrayList<Point>();
+ for (BezierCurve c : beziers) {
+ points.addAll(Arrays.asList(c.getPoints()));
+ }
+ Point centroid = PointListUtils.computeCentroid(points
+ .toArray(new Point[] {}));
+ return rotateCCW(angle, centroid.x, centroid.y);
+ }
+
+ public PolyBezier rotateCCW(Angle angle, double cx, double cy) {
+ for (BezierCurve c : beziers) {
+ c.rotateCCW(angle, cx, cy);
+ }
+ return this;
+ }
+
+ public PolyBezier rotateCCW(Angle angle, Point center) {
+ return rotateCCW(angle, center.x, center.y);
+ }
+
+ public PolyBezier rotateCW(Angle angle) {
+ ArrayList<Point> points = new ArrayList<Point>();
+ for (BezierCurve c : beziers) {
+ points.addAll(Arrays.asList(c.getPoints()));
+ }
+ Point centroid = PointListUtils.computeCentroid(points
+ .toArray(new Point[] {}));
+ return rotateCW(angle, centroid.x, centroid.y);
+ }
+
+ public PolyBezier rotateCW(Angle angle, double cx, double cy) {
+ for (BezierCurve c : beziers) {
+ c.rotateCW(angle, cx, cy);
+ }
+ return this;
+ }
+
+ public PolyBezier rotateCW(Angle angle, Point center) {
+ return rotateCW(angle, center.x, center.y);
+ }
+
+ public PolyBezier scale(double factor) {
+ return scale(factor, factor);
+ }
+
+ public PolyBezier scale(double fx, double fy) {
+ ArrayList<Point> points = new ArrayList<Point>();
+ for (BezierCurve c : beziers) {
+ points.addAll(Arrays.asList(c.getPoints()));
+ }
+ Point centroid = PointListUtils.computeCentroid(points
+ .toArray(new Point[] {}));
+ return scale(fx, fy, centroid.x, centroid.y);
+ }
+
+ public PolyBezier scale(double factor, double cx, double cy) {
+ return scale(factor, factor, cx, cy);
+ }
+
+ public PolyBezier scale(double fx, double fy, double cx, double cy) {
+ for (BezierCurve c : beziers) {
+ c.scale(fx, fy, cx, cy);
+ }
+ return this;
+ }
+
+ public PolyBezier scale(double fx, double fy, Point center) {
+ return scale(fx, fx, center.x, center.y);
+ }
+
+ public PolyBezier scale(double factor, Point center) {
+ return scale(factor, factor, center.x, center.y);
+ }
+
+ public BezierCurve[] toBezier() {
return copy(beziers);
}
- private static BezierCurve[] copy(BezierCurve... beziers) {
- BezierCurve[] copy = new BezierCurve[beziers.length];
+ public Path toPath() {
+ return CurveUtils.toPath(beziers);
+ }
- for (int i = 0; i < beziers.length; i++) {
- copy[i] = beziers[i].getCopy();
+ public PolyBezier translate(double dx, double dy) {
+ for (BezierCurve c : beziers) {
+ c.translate(dx, dy);
}
+ return this;
+ }
- return copy;
+ public PolyBezier translate(Point d) {
+ return translate(d.x, d.y);
}
}
diff --git a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Polygon.java b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Polygon.java
index d366acd..5e9e3cf 100644
--- a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Polygon.java
+++ b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Polygon.java
@@ -13,6 +13,10 @@
package org.eclipse.gef4.geometry.planar;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Set;
import org.eclipse.gef4.geometry.Point;
import org.eclipse.gef4.geometry.transform.AffineTransform;
@@ -35,6 +39,24 @@ public class Polygon extends AbstractPointListBasedGeometry<Polygon> implements
IShape {
/**
+ * The {@link NonSimplePolygonException} is thrown if a non-simple
+ * {@link Polygon}, i.e. a {@link Polygon} with self-intersections, is asked
+ * for its triangulation ({@link Polygon#getTriangulation()}).
+ */
+ @SuppressWarnings("serial")
+ public class NonSimplePolygonException extends RuntimeException {
+ @SuppressWarnings("javadoc")
+ public NonSimplePolygonException() {
+ super();
+ }
+
+ @SuppressWarnings("javadoc")
+ public NonSimplePolygonException(String s) {
+ super(s);
+ }
+ }
+
+ /**
* Pair of {@link Line} segment and integer counter to count segments of
* {@link Polygon}s.
*/
@@ -73,6 +95,87 @@ public class Polygon extends AbstractPointListBasedGeometry<Polygon> implements
private static final long serialVersionUID = 1L;
+ private static Polygon clipEar(Polygon p, int[] ear, ArrayList<Polygon> ears) {
+ Point[] points = p.getPoints();
+ ears.add(new Polygon(points[ear[0]], points[ear[1]], points[ear[2]]));
+ return new Polygon(getPointsWithout(points, ear[1]));
+ }
+
+ /**
+ * Searches the given list of {@link Point}s for a vertex that starts an
+ * ear. An ear is a list of 3 vertices which build up a triangle that lies
+ * inside the {@link Polygon} respective to the list of {@link Point}s and
+ * can be clipped out of it so that the remaining {@link Polygon} remains
+ * simple.
+ *
+ * @param points
+ * @return
+ */
+ private static int[] findEarVertex(Polygon p) {
+ Point[] points = p.getPoints();
+
+ for (int start = 0; start < points.length; start++) {
+ int mid = start == points.length - 1 ? 0 : start + 1;
+ int end = start == points.length - 2 ? 0
+ : start == points.length - 1 ? 1 : start + 2;
+
+ if (p.contains(new Line(points[start], points[end]))) {
+ return new int[] { start, mid, end };
+ }
+ }
+
+ // this should never happen (for simple polygons)
+ return null;
+ }
+
+ private static Point[] getPointsWithout(Point[] points,
+ int... indicesToRemove) {
+ Point[] rest = new Point[points.length - indicesToRemove.length];
+ Arrays.sort(indicesToRemove);
+ for (int i = 0, j = 0; i < indicesToRemove.length; i++) {
+ for (int r = j; r < indicesToRemove[i]; r++)
+ rest[r - i] = points[r];
+ j = indicesToRemove[i] + 1;
+ }
+ for (int i = indicesToRemove[indicesToRemove.length - 1] + 1; i < points.length; i++)
+ rest[i - indicesToRemove.length] = points[i];
+ return rest;
+ }
+
+ /**
+ * Clips exactly one ear off of the given {@link Polygon} and adds it to the
+ * list of ears. If the resulting {@link Polygon} is a triangle, this is
+ * added to the list of ears, too. Otherwise, the method recurses.
+ *
+ * @param p
+ * @param ears
+ */
+ private static void triangulate(Polygon p, ArrayList<Polygon> ears) {
+ if (p == null) {
+ throw new IllegalArgumentException(
+ "The given Polygon may not be null.");
+ }
+ if (ears == null) {
+ throw new IllegalArgumentException(
+ "The given ear-list may not be null.");
+ }
+ if (p.points.length < 3) {
+ throw new IllegalArgumentException(
+ "The given Polygon may not have less than three vertices.");
+ }
+
+ if (p.points.length == 3) {
+ ears.add(p.getCopy());
+ return;
+ }
+
+ int[] ear = findEarVertex(p);
+ Polygon rest = clipEar(p, ear, ears);
+
+ // recurse
+ triangulate(rest, ears);
+ }
+
/**
* Constructs a new {@link Polygon} from a even-numbered sequence of
* coordinates. Similar to {@link Polygon#Polygon(Point...)}, only that
@@ -102,6 +205,34 @@ public class Polygon extends AbstractPointListBasedGeometry<Polygon> implements
}
/**
+ * Assures that this {@link Polygon} is simple, i.e. it does not have any
+ * self-intersections. We do not need to test for voids as they are not
+ * considered in the interpretation of the {@link Polygon}'s {@link Point}s.
+ *
+ * If the {@link Polygon} does not have at least three vertices, a
+ * {@link NonSimplePolygonException} is thrown.
+ *
+ * The edges are added to the {@link Polygon} one after the other. If a
+ * self-intersection is found an {@link NonSimplePolygonException} is
+ * thrown.
+ */
+ private void assureSimplicity() throws NonSimplePolygonException {
+ if (points.length < 3)
+ throw new NonSimplePolygonException(
+ "A polygon can only be constructed of at least 3 vertices.");
+
+ for (Line e1 : getOutlineSegments())
+ for (Line e2 : getOutlineSegments())
+ if (!e1.getP1().equals(e2.getP1())
+ && !e1.getP2().equals(e2.getP1())
+ && !e1.getP1().equals(e2.getP2())
+ && !e1.getP2().equals(e2.getP2()))
+ if (e1.touches(e2))
+ throw new NonSimplePolygonException(
+ "Only simple polygons allowed. A polygon without any self-intersections is considered to be simple. This polygon is not simple.");
+ }
+
+ /**
* Checks whether the point that is represented by its x- and y-coordinates
* is contained within this {@link Polygon}.
*
@@ -117,6 +248,67 @@ public class Polygon extends AbstractPointListBasedGeometry<Polygon> implements
return contains(new Point(x, y));
}
+ public boolean contains(IGeometry g) {
+ if (g instanceof Line) {
+ return contains((Line) g);
+ } else if (g instanceof Polygon) {
+ return contains((Polygon) g);
+ } else if (g instanceof Polyline) {
+ return contains((Polyline) g);
+ } else if (g instanceof Rectangle) {
+ return contains((Rectangle) g);
+ }
+ return CurveUtils.contains(this, g);
+ }
+
+ /**
+ * Checks whether the given {@link Line} is fully contained within this
+ * {@link Polygon}.
+ *
+ * @param line
+ * The {@link Line} to test for containment
+ * @return <code>true</code> if the given {@link Line} is fully contained,
+ * <code>false</code> otherwise
+ */
+ public boolean contains(Line line) {
+ // quick rejection test: if the end points are not contained, the line
+ // may not be contained
+ if (!contains(line.getP1()) || !contains(line.getP2())) {
+ return false;
+ }
+
+ Set<Double> intersectionParams = new HashSet<Double>();
+
+ for (Line seg : getOutlineSegments()) {
+ Point poi = seg.getIntersection(line);
+ if (poi != null)
+ intersectionParams.add(line.getParameterAt(poi));
+ }
+
+ if (intersectionParams.size() <= 1) {
+ return true;
+ }
+
+ Double[] poiParams = intersectionParams.toArray(new Double[] {});
+ Arrays.sort(poiParams, new Comparator<Double>() {
+ public int compare(Double t, Double u) {
+ double d = t - u;
+ return d < 0 ? -1 : d > 0 ? 1 : 0;
+ }
+ });
+
+ // check the points between the intersections for containment
+ if (!contains(line.get(poiParams[0] / 2))) {
+ return false;
+ }
+ for (int i = 0; i < poiParams.length - 1; i++) {
+ if (!contains(line.get((poiParams[i] + poiParams[i + 1]) / 2))) {
+ return false;
+ }
+ }
+ return contains(line.get((poiParams[poiParams.length - 1] + 1) / 2));
+ }
+
/**
* @see IGeometry#contains(Point)
*/
@@ -211,6 +403,57 @@ public class Polygon extends AbstractPointListBasedGeometry<Polygon> implements
}
}
+ /**
+ * Checks whether the given {@link Polygon} is fully contained within this
+ * {@link Polygon}.
+ *
+ * @param p
+ * The {@link Polygon} to test for containment
+ * @return <code>true</code> if the given {@link Polygon} is fully
+ * contained, <code>false</code> otherwise.
+ */
+ public boolean contains(Polygon p) {
+ // all segments of the given polygon have to be contained
+ Line[] otherSegments = p.getOutlineSegments();
+ for (int i = 0; i < otherSegments.length; i++) {
+ if (!contains(otherSegments[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Tests if the given {@link Polyline} p is contained in this
+ * {@link Polygon}.
+ *
+ * @param p
+ * @return true if it is contained, false otherwise
+ */
+ public boolean contains(Polyline p) {
+ // all segments of the given polygon have to be contained
+ Line[] otherSegments = p.getCurves();
+ for (int i = 0; i < otherSegments.length; i++) {
+ if (!contains(otherSegments[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Checks whether the given {@link Rectangle} is fully contained within this
+ * {@link Polygon}.
+ *
+ * @param r
+ * the {@link Rectangle} to test for containment
+ * @return <code>true</code> if the given {@link Rectangle} is fully
+ * contained, <code>false</code> otherwise.
+ */
+ public boolean contains(Rectangle r) {
+ return contains(r.toPolygon());
+ }
+
@Override
public boolean equals(Object o) {
if (this == o)
@@ -272,21 +515,7 @@ public class Polygon extends AbstractPointListBasedGeometry<Polygon> implements
* @return the area of this {@link Polygon}
*/
public double getArea() {
- if (points.length < 3) {
- return 0;
- }
-
- double area = 0;
- for (int i = 0; i < points.length - 1; i++) {
- area += points[i].x * points[i + 1].y - points[i].y
- * points[i + 1].x;
- }
-
- // closing segment
- area += points[points.length - 2].x * points[points.length - 1].y
- - points[points.length - 2].y * points[points.length - 1].x;
-
- return Math.abs(area) * 0.5;
+ return Math.abs(getSignedArea());
}
/**
@@ -299,6 +528,10 @@ public class Polygon extends AbstractPointListBasedGeometry<Polygon> implements
return new Polygon(getPoints());
}
+ public Polyline getOutline() {
+ return new Polyline(PointListUtils.toSegmentsArray(points, true));
+ }
+
/**
* Returns a sequence of {@link Line}s, representing the segments that are
* obtained by linking each two successive point of this {@link Polygon}
@@ -312,6 +545,31 @@ public class Polygon extends AbstractPointListBasedGeometry<Polygon> implements
}
/**
+ * Computes the signed area of this {@link Polygon}. The sign of the area is
+ * negative for counter clockwise ordered vertices. It is positive for
+ * clockwise ordered vertices.
+ *
+ * @return the signed area of this {@link Polygon}
+ */
+ public double getSignedArea() {
+ if (points.length < 3) {
+ return 0;
+ }
+
+ double area = 0;
+ for (int i = 0; i < points.length - 1; i++) {
+ area += points[i].x * points[i + 1].y - points[i].y
+ * points[i + 1].x;
+ }
+
+ // closing segment
+ area += points[points.length - 2].x * points[points.length - 1].y
+ - points[points.length - 2].y * points[points.length - 1].x;
+
+ return area * 0.5;
+ }
+
+ /**
* @see IGeometry#getTransformed(AffineTransform)
*/
@Override
@@ -321,6 +579,21 @@ public class Polygon extends AbstractPointListBasedGeometry<Polygon> implements
}
/**
+ * Naive, recursive ear-clipping algorithm to triangulate this simple,
+ * planar {@link Polygon}.
+ *
+ * @return triangulation {@link Polygon}s (triangles)
+ * @throws NonSimplePolygonException
+ * if <code>this</code> is a non-simple {@link Polygon}
+ */
+ public Polygon[] getTriangulation() throws NonSimplePolygonException {
+ assureSimplicity();
+ ArrayList<Polygon> ears = new ArrayList<Polygon>(points.length - 2);
+ triangulate(this, ears);
+ return ears.toArray(new Polygon[] {});
+ }
+
+ /**
* @see IGeometry#toPath()
*/
public Path toPath() {
@@ -351,110 +624,4 @@ public class Polygon extends AbstractPointListBasedGeometry<Polygon> implements
return stringBuffer.toString();
}
- public Polyline getOutline() {
- return new Polyline(PointListUtils.toSegmentsArray(points, true));
- }
-
- /**
- * Checks whether the given {@link Line} is fully contained within this
- * {@link Polygon}.
- *
- * @param line
- * The {@link Line} to test for containment
- * @return <code>true</code> if the given {@link Line} is fully contained,
- * <code>false</code> otherwise
- */
- public boolean contains(Line line) {
- // quick rejection test: if the end points are not contained, the line
- // may not be contained
- if (!contains(line.getP1()) || !contains(line.getP2())) {
- return false;
- }
-
- // check for intersections with the segments of this polygon
- for (int i = 0; i < points.length; i++) {
- Point p1 = points[i];
- Point p2 = i + 1 < points.length ? points[i + 1] : points[0];
- Line segment = new Line(p1, p2);
- if (line.intersects(segment)) {
- Point intersection = line.getIntersection(segment);
- if (intersection != null && !line.getP1().equals(intersection)
- && !line.getP2().equals(intersection)
- && !segment.getP1().equals(intersection)
- && !segment.getP2().equals(intersection)) {
- // if we have a single intersection point and this does not
- // match one of the end points of the line, the line is not
- // contained
- return false;
- }
- }
- }
- return true;
- }
-
- /**
- * Checks whether the given {@link Polygon} is fully contained within this
- * {@link Polygon}.
- *
- * @param p
- * The {@link Polygon} to test for containment
- * @return <code>true</code> if the given {@link Polygon} is fully
- * contained, <code>false</code> otherwise.
- */
- public boolean contains(Polygon p) {
- // all segments of the given polygon have to be contained
- Line[] otherSegments = p.getOutlineSegments();
- for (int i = 0; i < otherSegments.length; i++) {
- if (!contains(otherSegments[i])) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Checks whether the given {@link Rectangle} is fully contained within this
- * {@link Polygon}.
- *
- * @param r
- * the {@link Rectangle} to test for containment
- * @return <code>true</code> if the given {@link Rectangle} is fully
- * contained, <code>false</code> otherwise.
- */
- public boolean contains(Rectangle r) {
- return contains(r.toPolygon());
- }
-
- /**
- * Tests if the given {@link Polyline} p is contained in this
- * {@link Polygon}.
- *
- * @param p
- * @return true if it is contained, false otherwise
- */
- public boolean contains(Polyline p) {
- // all segments of the given polygon have to be contained
- Line[] otherSegments = p.getCurves();
- for (int i = 0; i < otherSegments.length; i++) {
- if (!contains(otherSegments[i])) {
- return false;
- }
- }
- return true;
- }
-
- public boolean contains(IGeometry g) {
- if (g instanceof Line) {
- return contains((Line) g);
- } else if (g instanceof Polygon) {
- return contains((Polygon) g);
- } else if (g instanceof Polyline) {
- return contains((Polyline) g);
- } else if (g instanceof Rectangle) {
- return contains((Rectangle) g);
- }
- return CurveUtils.contains(this, g);
- }
-
- // TODO: union point, rectangle, polygon, etc.
}
diff --git a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Polyline.java b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Polyline.java
index 3347b5b..6701353 100644
--- a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Polyline.java
+++ b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Polyline.java
@@ -48,6 +48,16 @@ public class Polyline extends AbstractPointListBasedGeometry<Polyline>
}
/**
+ * Constructs a new {@link Polyline} from the given array of {@link Line}
+ * segments.
+ *
+ * @param segmentsArray
+ */
+ public Polyline(Line[] segmentsArray) {
+ super(PointListUtils.toPointsArray(segmentsArray, false));
+ }
+
+ /**
* Constructs a new {@link Polyline} from the given sequence of
* {@link Point} s. The {@link Polyline} that is created will be
* automatically closed, i.e. it will not only contain a segment between
@@ -63,16 +73,6 @@ public class Polyline extends AbstractPointListBasedGeometry<Polyline>
}
/**
- * Constructs a new {@link Polyline} from the given array of {@link Line}
- * segments.
- *
- * @param segmentsArray
- */
- public Polyline(Line[] segmentsArray) {
- super(PointListUtils.toPointsArray(segmentsArray, false));
- }
-
- /**
* Checks whether the point that is represented by its x- and y-coordinates
* is contained within this {@link Polyline}.
*
@@ -106,7 +106,7 @@ public class Polyline extends AbstractPointListBasedGeometry<Polyline>
public boolean equals(Object o) {
if (this == o)
return true;
- if (o instanceof Polygon) {
+ if (o instanceof Polyline) {
Polyline p = (Polyline) o;
return equals(p.getPoints());
}
@@ -129,7 +129,15 @@ public class Polyline extends AbstractPointListBasedGeometry<Polyline>
if (points.length != this.points.length) {
return false;
}
- return PointListUtils.equals(this.points, points);
+ return PointListUtils.equals(this.points, points)
+ || PointListUtils.equalsReverse(this.points, points);
+ }
+
+ /**
+ * @see org.eclipse.gef4.geometry.planar.IGeometry#getCopy()
+ */
+ public Polyline getCopy() {
+ return new Polyline(getPoints());
}
/**
@@ -144,6 +152,18 @@ public class Polyline extends AbstractPointListBasedGeometry<Polyline>
return PointListUtils.toSegmentsArray(points, false);
}
+ public Point[] getIntersections(ICurve c) {
+ return CurveUtils.getIntersections(c, this);
+ }
+
+ public Point getP1() {
+ return points[0].getCopy();
+ }
+
+ public Point getP2() {
+ return points[points.length - 1].getCopy();
+ }
+
/**
* @see IGeometry#getTransformed(AffineTransform)
*/
@@ -152,18 +172,32 @@ public class Polyline extends AbstractPointListBasedGeometry<Polyline>
return new Polyline(t.getTransformed(points));
}
- /**
- * Tests whether this {@link Polyline} and the given {@link Rectangle}
- * touch, i.e. they have at least one {@link Point} in common.
- *
- * @param rect
- * the {@link Rectangle} to test
- * @return <code>true</code> if this {@link Polyline} and the
- * {@link Rectangle} touch, otherwise <code>false</code>
- * @see IGeometry#touches(IGeometry)
- */
- public boolean touches(Rectangle rect) {
- throw new UnsupportedOperationException("Not yet implemented.");
+ public double getX1() {
+ return getP1().x;
+ }
+
+ public double getX2() {
+ return getP2().x;
+ }
+
+ public double getY1() {
+ return getP1().y;
+ }
+
+ public double getY2() {
+ return getP2().y;
+ }
+
+ public boolean intersects(ICurve c) {
+ return CurveUtils.intersects(c, this);
+ }
+
+ public boolean overlaps(ICurve c) {
+ return CurveUtils.overlaps(c, this);
+ }
+
+ public Line[] toBezier() {
+ return PointListUtils.toSegmentsArray(points, false);
}
/**
@@ -208,50 +242,17 @@ public class Polyline extends AbstractPointListBasedGeometry<Polyline>
}
/**
- * @see org.eclipse.gef4.geometry.planar.IGeometry#getCopy()
+ * Tests whether this {@link Polyline} and the given {@link Rectangle}
+ * touch, i.e. they have at least one {@link Point} in common.
+ *
+ * @param rect
+ * the {@link Rectangle} to test
+ * @return <code>true</code> if this {@link Polyline} and the
+ * {@link Rectangle} touch, otherwise <code>false</code>
+ * @see IGeometry#touches(IGeometry)
*/
- public Polyline getCopy() {
- return new Polyline(getPoints());
- }
-
- public double getY2() {
- return getP2().y;
- }
-
- public double getY1() {
- return getP1().y;
- }
-
- public double getX2() {
- return getP2().x;
- }
-
- public double getX1() {
- return getP1().x;
- }
-
- public Point getP2() {
- return points[points.length - 1].getCopy();
- }
-
- public Point getP1() {
- return points[0].getCopy();
- }
-
- public Line[] toBezier() {
- return PointListUtils.toSegmentsArray(points, false);
- }
-
- public Point[] getIntersections(ICurve c) {
- return CurveUtils.getIntersections(c, this);
- }
-
- public boolean intersects(ICurve c) {
- return CurveUtils.intersects(c, this);
- }
-
- public boolean overlaps(ICurve c) {
- return CurveUtils.overlaps(c, this);
+ public boolean touches(Rectangle rect) {
+ throw new UnsupportedOperationException("Not yet implemented.");
}
}
diff --git a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/QuadraticCurve.java b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/QuadraticCurve.java
index dbc1d1d..fb52aea 100644
--- a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/QuadraticCurve.java
+++ b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/QuadraticCurve.java
@@ -13,7 +13,6 @@
package org.eclipse.gef4.geometry.planar;
import org.eclipse.gef4.geometry.Point;
-import org.eclipse.gef4.geometry.utils.PolynomCalculationUtils;
/**
* Represents the geometric shape of a quadratic Bézier curve.
@@ -88,19 +87,6 @@ public class QuadraticCurve extends BezierCurve {
this(p1.x, p1.y, pCtrl.x, pCtrl.y, p2.x, p2.y);
}
- /**
- * Clips this {@link QuadraticCurve} at parameter values t1 and t2 so that
- * the resulting {@link QuadraticCurve} is the section of the original
- * {@link QuadraticCurve} for the parameter interval [t1, t2].
- *
- * @param t1
- * @param t2
- * @return the {@link QuadraticCurve} on the interval [t1, t2]
- */
- public QuadraticCurve clip(double t1, double t2) {
- return super.getClipped(t1, t2).toQuadratic();
- }
-
@Override
public boolean equals(Object other) {
QuadraticCurve o = (QuadraticCurve) other;
@@ -112,68 +98,45 @@ public class QuadraticCurve extends BezierCurve {
}
/**
+ * Erroneous getBounds() implementation... use the generic one instead.
+ *
+ * TODO: find out why the mathematical solution is erroneous in some cases.
+ *
* Returns the bounds of this QuadraticCurve. The bounds are calculated by
* examining the extreme points of the x(t) and y(t) function
* representations of this QuadraticCurve.
*
* @return the bounds {@link Rectangle}
*/
- @Override
- public Rectangle getBounds() {
- // extremes of the x(t) and y(t) functions:
- double[] xts;
- try {
- xts = PolynomCalculationUtils.getLinearRoots(2 * (getX1() - 2
- * getCtrlX() + getX2()), 2 * (getCtrlX() - getX1()));
- } catch (ArithmeticException x) {
- return new Rectangle(getP1(), getP2());
- }
-
- double xmin = getX1(), xmax = getX1();
- if (getX2() < xmin) {
- xmin = getX2();
- } else {
- xmax = getX2();
- }
-
- for (double t : xts) {
- if (t >= 0 && t <= 1) {
- double x = get(t).x;
- if (x < xmin) {
- xmin = x;
- } else if (x > xmax) {
- xmax = x;
- }
- }
- }
-
- double[] yts;
- try {
- yts = PolynomCalculationUtils.getLinearRoots(2 * (getY1() - 2
- * getCtrlY() + getY2()), 2 * (getCtrlY() - getY1()));
- } catch (ArithmeticException x) {
- return new Rectangle(getP1(), getP2());
- }
-
- double ymin = getY1(), ymax = getY1();
- if (getY2() < ymin) {
- ymin = getY2();
- } else {
- ymax = getY2();
- }
-
- for (double t : yts) {
- if (t >= 0 && t <= 1) {
- double y = get(t).y;
- if (y < ymin) {
- ymin = y;
- } else if (y > ymax) {
- ymax = y;
- }
- }
- }
+ /*
+ * public Rectangle getBounds() { // extremes of the x(t) and y(t)
+ * functions: double[] xts; try { xts =
+ * PolynomCalculationUtils.getLinearRoots(2 * (getX1() - 2 getCtrlX() +
+ * getX2()), 2 * (getCtrlX() - getX1())); } catch (ArithmeticException x) {
+ * return new Rectangle(getP1(), getP2()); }
+ *
+ * double xmin = getX1(), xmax = getX1(); if (getX2() < xmin) { xmin =
+ * getX2(); } else { xmax = getX2(); }
+ *
+ * for (double t : xts) { if (t >= 0 && t <= 1) { double x = get(t).x; if (x
+ * < xmin) { xmin = x; } else if (x > xmax) { xmax = x; } } }
+ *
+ * double[] yts; try { yts = PolynomCalculationUtils.getLinearRoots(2 *
+ * (getY1() - 2 getCtrlY() + getY2()), 2 * (getCtrlY() - getY1())); } catch
+ * (ArithmeticException x) { return new Rectangle(getP1(), getP2()); }
+ *
+ * double ymin = getY1(), ymax = getY1(); if (getY2() < ymin) { ymin =
+ * getY2(); } else { ymax = getY2(); }
+ *
+ * for (double t : yts) { if (t >= 0 && t <= 1) { double y = get(t).y; if (y
+ * < ymin) { ymin = y; } else if (y > ymax) { ymax = y; } } }
+ *
+ * return new Rectangle(new Point(xmin, ymin), new Point(xmax, ymax)); }
+ */
- return new Rectangle(new Point(xmin, ymin), new Point(xmax, ymax));
+ @Override
+ public QuadraticCurve getClipped(double t1, double t2) {
+ return super.getClipped(t1, t2).toQuadratic();
}
private Polygon getControlPolygon() {
diff --git a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Rectangle.java b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Rectangle.java
index efa9a75..447eaf8 100644
--- a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Rectangle.java
+++ b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Rectangle.java
@@ -39,8 +39,8 @@ import org.eclipse.gef4.geometry.utils.PrecisionUtils;
* @author ahunter
* @author anyssen
*/
-public final class Rectangle extends AbstractRectangleBasedGeometry<Rectangle>
- implements IShape {
+public final class Rectangle extends
+ AbstractRectangleBasedGeometry<Rectangle, Polygon> implements IShape {
private static final long serialVersionUID = 1L;
@@ -149,6 +149,37 @@ public final class Rectangle extends AbstractRectangleBasedGeometry<Rectangle>
}
/**
+ * Returns true in case the rectangle specified by (x, y, width, height) is
+ * contained within this {@link Rectangle}.
+ *
+ * @param x
+ * The x coordinate of the rectangle to be tested for containment
+ * @param y
+ * The y coordinate of the rectangle to be tested for containment
+ * @param width
+ * The width of the rectangle to be tested for containment
+ * @param height
+ * The height of the rectangle to be tested for containment
+ * @return <code>true</code> if the rectangle characterized by (x,y, width,
+ * height) is (imprecisely) fully contained within this
+ * {@link Rectangle}, <code>false</code> otherwise
+ */
+ public boolean contains(double x, double y, double width, double height) {
+ return PrecisionUtils.smallerEqual(this.x, x)
+ && PrecisionUtils.smallerEqual(this.y, y)
+ && PrecisionUtils.greaterEqual(this.x + this.width, x + width)
+ && PrecisionUtils
+ .greaterEqual(this.y + this.height, y + height);
+ }
+
+ public boolean contains(IGeometry g) {
+ if (g instanceof Rectangle) {
+ return contains((Rectangle) g);
+ }
+ return CurveUtils.contains(this, g);
+ }
+
+ /**
* Returns whether the given point is within the boundaries of this
* Rectangle. The boundaries are inclusive of the top and left edges, but
* exclusive of the bottom and right edges.
@@ -163,6 +194,21 @@ public final class Rectangle extends AbstractRectangleBasedGeometry<Rectangle>
}
/**
+ * Tests whether this {@link Rectangle} fully contains the given other
+ * {@link Rectangle}.
+ *
+ * @param r
+ * the other {@link Rectangle} to test for being contained by
+ * this {@link Rectangle}
+ * @return <code>true</code> if this {@link Rectangle} contains the other
+ * {@link Rectangle}, otherwise <code>false</code>
+ * @see IShape#contains(IGeometry)
+ */
+ public boolean contains(Rectangle r) {
+ return contains(r.x, r.y, r.width, r.height);
+ }
+
+ /**
* Returns <code>true</code> if this Rectangle's x, y, width, and height
* values are identical to the provided ones.
*
@@ -259,33 +305,6 @@ public final class Rectangle extends AbstractRectangleBasedGeometry<Rectangle>
}
/**
- * Returns an array of {@link Point}s representing the top-left, top-right,
- * bottom-right, and bottom-left border points of this {@link Rectangle}.
- *
- * @return An array containing the border points of this {@link Rectangle}
- */
- public Point[] getPoints() {
- return new Point[] { getTopLeft(), getTopRight(), getBottomRight(),
- getBottomLeft() };
- }
-
- /**
- * Returns an array of {@link Line}s representing the top, right, bottom,
- * and left borders of this {@link Rectangle}.
- *
- * @return An array containing {@link Line} representations of this
- * {@link Rectangle}'s borders.
- */
- public Line[] getOutlineSegments() {
- Line[] segments = new Line[4];
- segments[0] = new Line(x, y, x + width, y);
- segments[1] = new Line(x + width, y, x + width, y + height);
- segments[2] = new Line(x + width, y + height, x, y + height);
- segments[3] = new Line(x, y + height, x, y);
- return segments;
- }
-
- /**
* Returns a new Point representing the middle point of the bottom side of
* this Rectangle.
*
@@ -383,6 +402,38 @@ public final class Rectangle extends AbstractRectangleBasedGeometry<Rectangle>
return new Point(x, y + height / 2);
}
+ public Polyline getOutline() {
+ return new Polyline(x, y, x + width, y, x + width, y + height, x, y
+ + height, x, y);
+ }
+
+ /**
+ * Returns an array of {@link Line}s representing the top, right, bottom,
+ * and left borders of this {@link Rectangle}.
+ *
+ * @return An array containing {@link Line} representations of this
+ * {@link Rectangle}'s borders.
+ */
+ public Line[] getOutlineSegments() {
+ Line[] segments = new Line[4];
+ segments[0] = new Line(x, y, x + width, y);
+ segments[1] = new Line(x + width, y, x + width, y + height);
+ segments[2] = new Line(x + width, y + height, x, y + height);
+ segments[3] = new Line(x, y + height, x, y);
+ return segments;
+ }
+
+ /**
+ * Returns an array of {@link Point}s representing the top-left, top-right,
+ * bottom-right, and bottom-left border points of this {@link Rectangle}.
+ *
+ * @return An array containing the border points of this {@link Rectangle}
+ */
+ public Point[] getPoints() {
+ return new Point[] { getTopLeft(), getTopRight(), getBottomRight(),
+ getBottomLeft() };
+ }
+
/**
* Returns a new Point which represents the middle point of the right hand
* side of this Rectangle.
@@ -394,21 +445,42 @@ public final class Rectangle extends AbstractRectangleBasedGeometry<Rectangle>
}
/**
- * Rotates this {@link Rectangle} clock-wise by the given {@link Angle}
- * around the center ({@link #getCentroid()}) of this {@link Rectangle}.
+ * Rotates this {@link Rectangle} counter-clock-wise by the given
+ * {@link Angle} around the center {@link Point} of this {@link Rectangle}
+ * (see {@link #getCentroid()}).
*
- * @see #getRotatedCW(Angle, Point)
+ * @see #getRotatedCCW(Angle, Point)
* @param alpha
- * the rotation {@link Angle}
* @return the resulting {@link Polygon}
*/
- public Polygon getRotatedCW(Angle alpha) {
- return getRotatedCW(alpha, getCentroid());
+ public Polygon getRotatedCCW(Angle alpha) {
+ Point centroid = getCentroid();
+ return toPolygon().rotateCCW(alpha, centroid.x, centroid.y);
}
/**
- * Rotates this {@link Rectangle} clock-wise by the given {@link Angle}
- * alpha around the given {@link Point}.
+ * Rotates this {@link Rectangle} counter-clock-wise by the given
+ * {@link Angle} around the given {@link Point}.
+ *
+ * If the rotation {@link Angle} is not an integer multiple of 90 degrees,
+ * the resulting figure cannot be expressed as a {@link Rectangle} object.
+ * That's why this method returns a {@link Polygon} instead.
+ *
+ * @param alpha
+ * the rotation angle
+ * @param cx
+ * x-component of the center point for the rotation
+ * @param cy
+ * y-component of the center point for the rotation
+ * @return the resulting {@link Polygon}
+ */
+ public Polygon getRotatedCCW(Angle alpha, double cx, double cy) {
+ return toPolygon().rotateCCW(alpha, cx, cy);
+ }
+
+ /**
+ * Rotates this {@link Rectangle} counter-clock-wise by the given
+ * {@link Angle} around the given {@link Point}.
*
* If the rotation {@link Angle} is not an integer multiple of 90 degrees,
* the resulting figure cannot be expressed as a {@link Rectangle} object.
@@ -420,26 +492,47 @@ public final class Rectangle extends AbstractRectangleBasedGeometry<Rectangle>
* the center point for the rotation
* @return the resulting {@link Polygon}
*/
- public Polygon getRotatedCW(Angle alpha, Point center) {
- return toPolygon().rotateCW(alpha, center);
+ public Polygon getRotatedCCW(Angle alpha, Point center) {
+ return toPolygon().rotateCCW(alpha, center.x, center.y);
}
/**
- * Rotates this {@link Rectangle} counter-clock-wise by the given
- * {@link Angle} around the center {@link Point} of this {@link Rectangle}
- * (see {@link #getCentroid()}).
+ * Rotates this {@link Rectangle} clock-wise by the given {@link Angle}
+ * around the center ({@link #getCentroid()}) of this {@link Rectangle}.
*
- * @see #getRotatedCCW(Angle, Point)
+ * @see #getRotatedCW(Angle, Point)
* @param alpha
+ * the rotation {@link Angle}
* @return the resulting {@link Polygon}
*/
- public Polygon getRotatedCCW(Angle alpha) {
- return getRotatedCCW(alpha, getCentroid());
+ public Polygon getRotatedCW(Angle alpha) {
+ Point centroid = getCentroid();
+ return toPolygon().rotateCW(alpha, centroid.x, centroid.y);
}
/**
- * Rotates this {@link Rectangle} counter-clock-wise by the given
- * {@link Angle} around the given {@link Point}.
+ * Rotates this {@link Rectangle} clock-wise by the given {@link Angle}
+ * alpha around the given {@link Point} (cx, cy).
+ *
+ * If the rotation {@link Angle} is not an integer multiple of 90 degrees,
+ * the resulting figure cannot be expressed as a {@link Rectangle} object.
+ * That's why this method returns a {@link Polygon} instead.
+ *
+ * @param alpha
+ * the rotation angle
+ * @param cx
+ * x-component of the center point for the rotation
+ * @param cy
+ * y-component of the center point for the rotation
+ * @return the resulting {@link Polygon}
+ */
+ public Polygon getRotatedCW(Angle alpha, double cx, double cy) {
+ return toPolygon().rotateCW(alpha, cx, cy);
+ }
+
+ /**
+ * Rotates this {@link Rectangle} clock-wise by the given {@link Angle}
+ * alpha around the given {@link Point}.
*
* If the rotation {@link Angle} is not an integer multiple of 90 degrees,
* the resulting figure cannot be expressed as a {@link Rectangle} object.
@@ -451,8 +544,8 @@ public final class Rectangle extends AbstractRectangleBasedGeometry<Rectangle>
* the center point for the rotation
* @return the resulting {@link Polygon}
*/
- public Polygon getRotatedCCW(Angle alpha, Point center) {
- return toPolygon().rotateCCW(alpha, center);
+ public Polygon getRotatedCW(Angle alpha, Point center) {
+ return toPolygon().rotateCW(alpha, center.x, center.y);
}
/**
@@ -592,58 +685,6 @@ public final class Rectangle extends AbstractRectangleBasedGeometry<Rectangle>
}
/**
- * Tests whether this {@link Rectangle} and the given {@link Line} touch,
- * i.e. whether they have at least one point in common.
- *
- * @param l
- * The {@link Line} to test.
- * @return <code>true</code> if this {@link Rectangle} and the given
- * {@link Line} share at least one common point, <code>false</code>
- * otherwise.
- */
- public boolean touches(Line l) {
- if (contains(l.getP1()) || contains(l.getP2())) {
- return true;
- }
-
- for (Line segment : getOutlineSegments()) {
- if (segment.intersects(l)) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Tests whether this {@link Rectangle} and the given other
- * {@link Rectangle} touch, i.e. whether they have at least one point in
- * common.
- *
- * @param r
- * The {@link Rectangle} to test
- * @return <code>true</code> if this {@link Rectangle} and the given
- * {@link Rectangle} share at least one common point,
- * <code>false</code> otherwise.
- * @see IGeometry#touches(IGeometry)
- */
- public boolean touches(Rectangle r) {
- return PrecisionUtils.smallerEqual(r.x, x + width)
- && PrecisionUtils.smallerEqual(r.y, y + height)
- && PrecisionUtils.greaterEqual(r.x + r.width, x)
- && PrecisionUtils.greaterEqual(r.y + r.height, y);
- }
-
- @Override
- public boolean touches(IGeometry g) {
- if (g instanceof Line) {
- return touches((Line) g);
- } else if (g instanceof Rectangle) {
- return touches((Rectangle) g);
- }
- return super.touches(g);
- }
-
- /**
* Returns <code>true</code> if this Rectangle's width or height is less
* than or equal to 0.
*
@@ -744,6 +785,57 @@ public final class Rectangle extends AbstractRectangleBasedGeometry<Rectangle>
(int) Math.ceil(height + y - Math.floor(y)));
}
+ public boolean touches(IGeometry g) {
+ if (g instanceof Line) {
+ return touches((Line) g);
+ } else if (g instanceof Rectangle) {
+ return touches((Rectangle) g);
+ }
+ return super.touches(g);
+ }
+
+ /**
+ * Tests whether this {@link Rectangle} and the given {@link Line} touch,
+ * i.e. whether they have at least one point in common.
+ *
+ * @param l
+ * The {@link Line} to test.
+ * @return <code>true</code> if this {@link Rectangle} and the given
+ * {@link Line} share at least one common point, <code>false</code>
+ * otherwise.
+ */
+ public boolean touches(Line l) {
+ if (contains(l.getP1()) || contains(l.getP2())) {
+ return true;
+ }
+
+ for (Line segment : getOutlineSegments()) {
+ if (segment.intersects(l)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Tests whether this {@link Rectangle} and the given other
+ * {@link Rectangle} touch, i.e. whether they have at least one point in
+ * common.
+ *
+ * @param r
+ * The {@link Rectangle} to test
+ * @return <code>true</code> if this {@link Rectangle} and the given
+ * {@link Rectangle} share at least one common point,
+ * <code>false</code> otherwise.
+ * @see IGeometry#touches(IGeometry)
+ */
+ public boolean touches(Rectangle r) {
+ return PrecisionUtils.smallerEqual(r.x, x + width)
+ && PrecisionUtils.smallerEqual(r.y, y + height)
+ && PrecisionUtils.greaterEqual(r.x + r.width, x)
+ && PrecisionUtils.greaterEqual(r.y + r.height, y);
+ }
+
/**
* Switches the x and y values, as well as the width and height of this
* Rectangle. Useful for orientation changes.
@@ -836,55 +928,4 @@ public final class Rectangle extends AbstractRectangleBasedGeometry<Rectangle>
return union(r.x, r.y, r.width, r.height);
}
- public Polyline getOutline() {
- return new Polyline(x, y, x + width, y, x + width, y + height, x, y
- + height, x, y);
- }
-
- public boolean contains(IGeometry g) {
- if (g instanceof Rectangle) {
- return contains((Rectangle) g);
- }
- return CurveUtils.contains(this, g);
- }
-
- /**
- * Returns true in case the rectangle specified by (x, y, width, height) is
- * contained within this {@link Rectangle}.
- *
- * @param x
- * The x coordinate of the rectangle to be tested for containment
- * @param y
- * The y coordinate of the rectangle to be tested for containment
- * @param width
- * The width of the rectangle to be tested for containment
- * @param height
- * The height of the rectangle to be tested for containment
- * @return <code>true</code> if the rectangle characterized by (x,y, width,
- * height) is (imprecisely) fully contained within this
- * {@link Rectangle}, <code>false</code> otherwise
- */
- public boolean contains(double x, double y, double width, double height) {
- return PrecisionUtils.smallerEqual(this.x, x)
- && PrecisionUtils.smallerEqual(this.y, y)
- && PrecisionUtils.greaterEqual(this.x + this.width, x + width)
- && PrecisionUtils
- .greaterEqual(this.y + this.height, y + height);
- }
-
- /**
- * Tests whether this {@link Rectangle} fully contains the given other
- * {@link Rectangle}.
- *
- * @param r
- * the other {@link Rectangle} to test for being contained by
- * this {@link Rectangle}
- * @return <code>true</code> if this {@link Rectangle} contains the other
- * {@link Rectangle}, otherwise <code>false</code>
- * @see IShape#contains(IGeometry)
- */
- public boolean contains(Rectangle r) {
- return contains(r.x, r.y, r.width, r.height);
- }
-
}
diff --git a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Region.java b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Region.java
index b821610..ea0806b 100644
--- a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Region.java
+++ b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Region.java
@@ -7,46 +7,377 @@
*
* Contributors:
* Alexander Nyßen (itemis AG) - initial API and implementation
+ * Matthias Wienand (itemis AG) - contribution for Bugzilla #355997
*
*******************************************************************************/
package org.eclipse.gef4.geometry.planar;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.Stack;
+
+import org.eclipse.gef4.geometry.Angle;
import org.eclipse.gef4.geometry.Point;
+import org.eclipse.gef4.geometry.transform.IRotatable;
+import org.eclipse.gef4.geometry.transform.IScalable;
+import org.eclipse.gef4.geometry.transform.ITranslatable;
+import org.eclipse.gef4.geometry.utils.CurveUtils;
/**
- * a combination of rectangles...
+ * A combination of {@link Rectangle}s. The {@link Rectangle}s that build up a
+ * {@link Region} do not have to be touching. The area covered by the
+ * {@link Region} is exactly the area that all of its corresponding
+ * {@link Rectangle}s are covering.
+ *
+ * A {@link Region} differentiates between the internal {@link Rectangle}s and
+ * the external {@link Rectangle}s. The external {@link Rectangle}s are those
+ * that you feed it, in order to construct the {@link Region}. The internal
+ * {@link Rectangle}s are those used for computations of the {@link Region}.
+ * They are defined to not share any area, so that only their borders can be
+ * overlapping.
*
* @author nyssen
*
*/
-public class Region extends AbstractGeometry implements IPolyShape {
+public class Region extends AbstractPolyShape implements ITranslatable<Region>,
+ IScalable<Region>, IRotatable<Ring> {
- public Rectangle[] getShapes() {
- throw new UnsupportedOperationException("Not yet implemented.");
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Cuts the given {@link Rectangle}s along the given parallel to the x-axis.
+ *
+ * @param y
+ * the distance of the cut-line to the x-axis
+ * @param parts
+ * the {@link Rectangle}s to cut along that line
+ */
+ private static void cutH(double y, ArrayList<Rectangle> parts) {
+ for (Rectangle r : new ArrayList<Rectangle>(parts)) {
+ if (r.y < y && y < r.y + r.height) {
+ parts.remove(r);
+ parts.add(new Rectangle(r.x, r.y, r.width, y - r.y));
+ parts.add(new Rectangle(r.x, y, r.width, r.y + r.height - y));
+ }
+ }
+ }
+
+ /**
+ * Cuts the given {@link Rectangle}s along the given parallel to the y-axis.
+ *
+ * @param x
+ * the distance of the cut-line to the y-axis
+ * @param parts
+ * the {@link Rectangle}s to cut along that line
+ */
+ private static void cutV(double x, ArrayList<Rectangle> parts) {
+ for (Rectangle r : new ArrayList<Rectangle>(parts)) {
+ if (r.x < x && x < r.x + r.width) {
+ parts.remove(r);
+ parts.add(new Rectangle(r.x, r.y, x - r.x, r.height));
+ parts.add(new Rectangle(x, r.y, r.x + r.width - x, r.height));
+ }
+ }
+ }
+
+ private ArrayList<Rectangle> rects;
+
+ /**
+ * Constructs a new {@link Region} not covering any area.
+ */
+ public Region() {
+ rects = new ArrayList<Rectangle>();
+ }
+
+ /**
+ * Constructs a new {@link Region} from the given list of {@link Rectangle}
+ * s.
+ *
+ * The given {@link Rectangle}s are {@link #add(Rectangle)}ed to the
+ * {@link Region} one after the other.
+ *
+ * @param rectangles
+ */
+ public Region(Rectangle... rectangles) {
+ this();
+ rects.add(rectangles[0].getCopy());
+
+ for (int i = 1; i < rectangles.length; i++) {
+ add(rectangles[i].getCopy());
+ }
+ }
+
+ /**
+ * Constructs a new {@link Region} from the given other {@link Region}. In
+ * other words, it copies the given other {@link Region}.
+ *
+ * @param other
+ */
+ public Region(Region other) {
+ rects = new ArrayList<Rectangle>(other.rects.size());
+
+ for (Rectangle or : other.rects) {
+ rects.add(or.getCopy());
+ }
+ }
+
+ /**
+ * Adds the given {@link Rectangle} to this {@link Region}.
+ *
+ * To assure the required conditions for internal {@link Rectangle}s, the
+ * given {@link Rectangle} is cut into several sub-{@link Rectangle}s so
+ * that no internal {@link Rectangle}s share any area.
+ *
+ * @param rectangle
+ * the {@link Rectangle} to add to this {@link Region}
+ * @return <code>this</code> for convenience
+ */
+ public Region add(Rectangle rectangle) {
+ ArrayList<Rectangle> toAdd = new ArrayList<Rectangle>(1);
+
+ toAdd.add(rectangle.getCopy());
+
+ for (Rectangle retain : rects) {
+ for (Rectangle addend : new ArrayList<Rectangle>(toAdd)) {
+ ArrayList<Rectangle> parts = new ArrayList<Rectangle>(8);
+ parts.add(addend);
+
+ if (addend.x <= retain.x && retain.x <= addend.x + addend.width
+ || retain.x <= addend.x
+ && addend.x <= retain.x + retain.width) {
+ cutH(retain.y, parts);
+ cutH(retain.y + retain.height, parts);
+ }
+
+ if (addend.y <= retain.y
+ && retain.y <= addend.y + addend.height
+ || retain.y <= addend.y
+ && addend.y <= retain.y + retain.height) {
+ cutV(retain.x, parts);
+ cutV(retain.x + retain.width, parts);
+ }
+
+ // filter inner parts:
+ for (Iterator<Rectangle> p = parts.iterator(); p.hasNext();) {
+ if (retain.contains(p.next())) {
+ p.remove();
+ }
+ }
+
+ toAdd.remove(addend);
+ toAdd.addAll(parts);
+ }
+ }
+
+ rects.addAll(toAdd);
+
+ return this;
}
- public boolean contains(Point p) {
- throw new UnsupportedOperationException("Not yet implemented.");
+ public boolean contains(IGeometry g) {
+ return CurveUtils.contains(this, g);
}
- public boolean contains(Rectangle r) {
- throw new UnsupportedOperationException("Not yet implemented.");
+ /**
+ * Collects all outline segments of the internal {@link Rectangle}s.
+ *
+ * @return all the outline segments of the internal {@link Rectangle}s
+ */
+ protected Line[] getAllEdges() {
+ Stack<Line> edges = new Stack<Line>();
+
+ for (Rectangle r : rects) {
+ for (Line e : r.getOutlineSegments()) {
+ edges.push(e);
+ }
+ }
+ return edges.toArray(new Line[] {});
}
public Rectangle getBounds() {
- throw new UnsupportedOperationException("Not yet implemented.");
+ if (rects.size() == 0)
+ return null;
+
+ Rectangle bounds = rects.get(0).getBounds();
+ for (int i = 1; i < rects.size(); i++)
+ bounds.union(rects.get(i).getBounds());
+
+ return bounds;
+ }
+
+ public Region getCopy() {
+ return new Region(this);
}
- public boolean touches(Rectangle r) {
- throw new UnsupportedOperationException("Not yet implemented.");
+ /**
+ * Computes the {@link Point}s of intersection of this {@link Region} with
+ * the given {@link ICurve}.
+ *
+ * @param c
+ * @return the intersection {@link Point}s
+ */
+ public Point[] getOutlineIntersections(ICurve c) {
+ Set<Point> intersections = new HashSet<Point>(0);
+
+ for (Line seg : getOutlineSegments()) {
+ intersections.addAll(Arrays.asList(seg.getIntersections(c)));
+ }
+
+ return intersections.toArray(new Point[] {});
+ }
+
+ public Rectangle[] getShapes() {
+ return rects.toArray(new Rectangle[] {});
}
public Path toPath() {
- throw new UnsupportedOperationException("Not yet implemented.");
+ return getOutline().toPath();
+ }
+
+ /**
+ * Constructs a new {@link Ring} that covers the same area as this
+ * {@link Region}.
+ *
+ * @return a new {@link Ring} that covers the same area as this
+ * {@link Region}
+ */
+ public Ring toRing() {
+ Polygon[] polys = new Polygon[rects.size()];
+ Iterator<Rectangle> i = rects.iterator();
+ for (int j = 0; j < rects.size(); j++) {
+ polys[j] = i.next().toPolygon();
+ }
+ return new Ring(polys);
+ }
+
+ /**
+ * <p>
+ * Constructs a new {@link org.eclipse.swt.graphics.Region} that covers the
+ * same area as this {@link Region}. This is to ease the use of a
+ * {@link Region} for clipping:
+ * </p>
+ *
+ * <p>
+ * <code>gc.setClipping(region.toSWTRegion());</code>
+ * </p>
+ *
+ * @return a new {@link org.eclipse.swt.graphics.Region} that covers the
+ * same area as this {@link Region}
+ */
+ public org.eclipse.swt.graphics.Region toSWTRegion() {
+ org.eclipse.swt.graphics.Region swtRegion = new org.eclipse.swt.graphics.Region();
+
+ for (Rectangle r : rects) {
+ swtRegion.add(r.toSWTRectangle());
+ }
+
+ return swtRegion;
+ }
+
+ public Ring getRotatedCCW(Angle angle) {
+ Point centroid = getBounds().getCentroid();
+ return getRotatedCCW(angle, centroid.x, centroid.y);
+ }
+
+ public Ring getRotatedCCW(Angle angle, double cx, double cy) {
+ Polygon[] polys = new Polygon[rects.size()];
+ for (int i = 0; i < polys.length; i++)
+ polys[i] = rects.get(i).getRotatedCCW(angle, cx, cy);
+ return new Ring(polys);
+ }
+
+ public Ring getRotatedCCW(Angle angle, Point center) {
+ return getRotatedCCW(angle, center.x, center.y);
+ }
+
+ public Ring getRotatedCW(Angle angle) {
+ Point centroid = getBounds().getCentroid();
+ return getRotatedCW(angle, centroid.x, centroid.y);
+ }
+
+ public Ring getRotatedCW(Angle angle, double cx, double cy) {
+ Polygon[] polys = new Polygon[rects.size()];
+ for (int i = 0; i < polys.length; i++)
+ polys[i] = rects.get(i).getRotatedCW(angle, cx, cy);
+ return new Ring(polys);
+ }
+
+ public Ring getRotatedCW(Angle angle, Point center) {
+ return getRotatedCW(angle, center.x, center.y);
+ }
+
+ public Region scale(double factor) {
+ return scale(factor, factor);
+ }
+
+ public Region scale(double factor, double cx, double cy) {
+ return scale(factor, factor, cx, cy);
+ }
+
+ public Region scale(double factor, Point center) {
+ return scale(factor, factor, center.x, center.y);
+ }
+
+ public Region scale(double fx, double fy) {
+ Point centroid = getBounds().getCentroid();
+ return scale(fx, fy, centroid.x, centroid.y);
+ }
+
+ public Region scale(double fx, double fy, double cx, double cy) {
+ for (Rectangle r : rects) {
+ r.scale(fx, fy, cx, cy);
+ }
+ return this;
+ }
+
+ public Region scale(double fx, double fy, Point center) {
+ return scale(fx, fy, center.x, center.y);
+ }
+
+ public Region getScaled(double factor) {
+ return getCopy().scale(factor);
+ }
+
+ public Region getScaled(double factor, double cx, double cy) {
+ return getCopy().scale(factor, cx, cy);
+ }
+
+ public Region getScaled(double factor, Point center) {
+ return getCopy().scale(factor, center);
+ }
+
+ public Region getScaled(double fx, double fy) {
+ return getCopy().scale(fx, fy);
+ }
+
+ public Region getScaled(double fx, double fy, double cx, double cy) {
+ return getCopy().scale(fx, fy, cx, cy);
+ }
+
+ public Region getScaled(double fx, double fy, Point center) {
+ return getCopy().scale(fx, fy, center);
+ }
+
+ public Region translate(double dx, double dy) {
+ for (Rectangle r : rects) {
+ r.translate(dx, dy);
+ }
+ return this;
+ }
+
+ public Region translate(Point d) {
+ return translate(d.x, d.y);
+ }
+
+ public Region getTranslated(double dx, double dy) {
+ return getCopy().translate(dx, dy);
}
- public IGeometry getCopy() {
- throw new UnsupportedOperationException("Not yet implemented.");
+ public Region getTranslated(Point d) {
+ return getCopy().translate(d.x, d.y);
}
}
diff --git a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Ring.java b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Ring.java
index 403c453..806122b 100644
--- a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Ring.java
+++ b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Ring.java
@@ -7,11 +7,23 @@
*
* Contributors:
* Alexander Nyßen (itemis AG) - initial API and implementation
+ * Matthias Wienand (itemis AG) - contribution for Bugzilla #355997
*
*******************************************************************************/
package org.eclipse.gef4.geometry.planar;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Stack;
+
+import org.eclipse.gef4.geometry.Angle;
import org.eclipse.gef4.geometry.Point;
+import org.eclipse.gef4.geometry.euclidean.Straight;
+import org.eclipse.gef4.geometry.euclidean.Vector;
+import org.eclipse.gef4.geometry.transform.IRotatable;
+import org.eclipse.gef4.geometry.transform.IScalable;
+import org.eclipse.gef4.geometry.transform.ITranslatable;
+import org.eclipse.gef4.geometry.utils.CurveUtils;
/**
*
@@ -20,34 +32,502 @@ import org.eclipse.gef4.geometry.Point;
* @author anyssen
*
*/
-public class Ring extends AbstractGeometry implements IPolyShape {
+public class Ring extends AbstractPolyShape implements ITranslatable<Ring>,
+ IScalable<Ring>, IRotatable<Ring> {
- public Polygon[] getShapes() {
- throw new UnsupportedOperationException("Not yet implemented.");
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Triangulates the given triangle ({@link Polygon}) at the given
+ * {@link Line}. The triangulation is done using the simpler
+ * {@link #triangulate(Polygon, Point, Point)} method. The real and
+ * imaginary {@link Point}s of intersection of the {@link Line} and the
+ * {@link Polygon} are used as the split {@link Point}s.
+ *
+ * The triangulation is only done, if the {@link Line} intersects the
+ * {@link Polygon}, i.e. at least one {@link Point} of the {@link Line} lies
+ * inside the {@link Polygon} but not on its outline.
+ *
+ * @param p
+ * the triangle ({@link Polygon}) to triangulate
+ * @param l
+ * the line that determines the split {@link Point}s
+ * @return at least one and up to three {@link Polygon}s which are the
+ * resulting triangles
+ */
+ private static Polygon[] triangulate(Polygon p, Line l) {
+ if (p == null) {
+ throw new IllegalArgumentException(
+ "The given Polygon parameter may not be null.");
+ }
+ if (l == null) {
+ throw new IllegalArgumentException(
+ "The given Line parameter may not be null.");
+ }
+
+ boolean intersecting = l.getIntersections(p.getOutline()).length == 2;
+
+ if (!intersecting) {
+ // test if at least one point of the line is really inside the
+ // polygon
+ for (Point lp : l.getPoints()) {
+ if (p.contains(lp) && !p.getOutline().contains(lp)) {
+ intersecting = true;
+ break;
+ }
+ }
+
+ if (!intersecting) {
+ return new Polygon[] { p.getCopy() };
+ }
+ }
+
+ // calculate real and imaginary intersection points
+ Straight s = new Straight(l);
+ Point inters[] = new Point[2];
+ int i = 0;
+
+ for (Line e : p.getOutlineSegments()) {
+ Vector vi = s.getIntersection(new Straight(e));
+ if (vi != null) {
+ Point poi = vi.toPoint();
+ if (e.contains(poi))
+ if (i > 0 && inters[0].equals(poi))
+ continue;
+ else
+ inters[i++] = poi;
+ }
+ if (i > 1)
+ break;
+ }
+
+ if (inters[0] == null || inters[1] == null) {
+ throw new IllegalStateException(
+ "The determined points of intersection do not lie on the polygon.");
+ }
+
+ return triangulate(p, inters[0], inters[1]);
+ }
+
+ /**
+ * <p>
+ * Splits a triangle at the line through points p1 and p2, which are
+ * required to lie on the outline of the triangle.
+ * </p>
+ *
+ * <p>
+ * If the points p1 and p2 lie on the same edge on the triangle, a copy of
+ * the given {@link Polygon} is returned.
+ * </p>
+ *
+ * <p>
+ * If one of the points lies on an edge, and the other point lies on a
+ * vertex of the triangle, two {@link Polygon}s are returned. They represent
+ * the areas left and right to the line through p1 and p2.
+ * </p>
+ *
+ * <p>
+ * If both points lie on different edges, three {@link Polygon}s are
+ * returned. One of them represents the triangle which lies on one side of
+ * the line through p1 and p2. The other two triangles are the triangulation
+ * of the tetragon on the other side of the line through p1 and p2.
+ * </p>
+ *
+ * @param p
+ * @param p1
+ * @param p2
+ * @return
+ */
+ private static Polygon[] triangulate(Polygon p, Point p1, Point p2) {
+ Point[] v = p == null ? new Point[] {} : p.getPoints();
+ if (v.length != 3) {
+ throw new IllegalArgumentException(
+ "Only triangles are allowed as the Polygon parameter.");
+ }
+ if (p1 == null) {
+ throw new IllegalArgumentException(
+ "The given p1 Point parameter may not be null.");
+ }
+ if (p2 == null) {
+ throw new IllegalArgumentException(
+ "The given p2 Point parameter may not be null.");
+ }
+
+ Line[] e = new Line[] { new Line(v[0], v[1]), new Line(v[1], v[2]),
+ new Line(v[2], v[0]) };
+
+ // determine the edges on which the points lie
+ boolean p1_on_e0 = e[0].contains(p1);
+ boolean p1_on_e1 = e[1].contains(p1);
+ boolean p1_on_e2 = e[2].contains(p1);
+ boolean p2_on_e0 = e[0].contains(p2);
+ boolean p2_on_e1 = e[1].contains(p2);
+ boolean p2_on_e2 = e[2].contains(p2);
+
+ // if both points lie on the same edge, we have nothing to do
+ if (p1_on_e0 && p2_on_e0 || p1_on_e1 && p2_on_e1 || p1_on_e2
+ && p2_on_e2) {
+ return new Polygon[] { p.getCopy() };
+ }
+
+ // check if both points are on the triangle
+ else if (!(p1_on_e0 || p1_on_e1 || p1_on_e2)
+ || !(p2_on_e0 || p2_on_e1 || p2_on_e2)) {
+ throw new IllegalArgumentException(
+ "The Point objects have to lie on the outline of the Polygon object.");
+ }
+
+ // determine if one of the points lies on a vertex
+ else if (p1.equals(v[0])) {
+ return new Polygon[] { new Polygon(v[0], v[1], p2),
+ new Polygon(v[0], v[2], p2) };
+ } else if (p1.equals(v[1])) {
+ return new Polygon[] { new Polygon(v[0], v[1], p2),
+ new Polygon(v[1], v[2], p2) };
+ } else if (p1.equals(v[2])) {
+ return new Polygon[] { new Polygon(v[0], v[2], p2),
+ new Polygon(v[1], v[2], p2) };
+ } else if (p2.equals(v[0])) {
+ return new Polygon[] { new Polygon(v[0], v[1], p1),
+ new Polygon(v[0], v[2], p1) };
+ } else if (p2.equals(v[1])) {
+ return new Polygon[] { new Polygon(v[0], v[1], p1),
+ new Polygon(v[1], v[2], p1) };
+ } else if (p2.equals(v[2])) {
+ return new Polygon[] { new Polygon(v[0], v[2], p1),
+ new Polygon(v[1], v[2], p1) };
+ }
+
+ // both points on different edges, determine isolated vertex
+ else if (p1_on_e0 && p2_on_e2 || p1_on_e2 && p2_on_e0) {
+ // v0 isolated
+ return new Polygon[] { new Polygon(v[0], p1, p2),
+ new Polygon(p1, p2, v[1]),
+ new Polygon(p1_on_e0 ? p2 : p1, v[1], v[2]) };
+ } else if (p1_on_e0 && p2_on_e1 || p1_on_e1 && p2_on_e0) {
+ // v1 isolated
+ return new Polygon[] { new Polygon(v[1], p1, p2),
+ new Polygon(p1, p2, v[0]),
+ new Polygon(p1_on_e0 ? p2 : p1, v[0], v[2]) };
+ } else if (p1_on_e1 && p2_on_e2 || p1_on_e2 && p2_on_e1) {
+ // v2 isolated
+ return new Polygon[] { new Polygon(v[2], p1, p2),
+ new Polygon(p1, p2, v[1]),
+ new Polygon(p1_on_e1 ? p2 : p1, v[1], v[0]) };
+ } else {
+ throw new IllegalStateException(
+ "Unreachable, because for two points on a triangle, they have to be located either (edge, edge), (vertex, edge), or (edge, vertex).");
+ }
}
- public boolean contains(Point p) {
- throw new UnsupportedOperationException("Not yet implemented.");
+ private ArrayList<Polygon> triangles;
+
+ /**
+ * Constructs a new empty {@link Ring}.
+ */
+ public Ring() {
+ triangles = new ArrayList<Polygon>();
}
- public boolean contains(Rectangle r) {
- throw new UnsupportedOperationException("Not yet implemented.");
+ /**
+ * Constructs a new {@link Ring} from the given {@link Polygon}s.
+ *
+ * @param polygons
+ */
+ public Ring(Polygon... polygons) {
+ this();
+ for (Polygon p : polygons) {
+ add(p);
+ }
+ }
+
+ /**
+ * Constructs a new {@link Ring} of the given other {@link Ring}. The
+ * internal {@link IShape}s of the other {@link Ring} are copied to prevent
+ * actions at a distance.
+ *
+ * @param other
+ */
+ public Ring(Ring other) {
+ this();
+ for (Polygon p : other.triangles) {
+ add(p);
+ }
+ }
+
+ /**
+ * Adds the given {@link Polygon} to this {@link Ring}.
+ *
+ * @param p
+ * @return <code>this</code> for convenience
+ */
+ public Ring add(Polygon p) {
+ Stack<Polygon> toAdd = new Stack<Polygon>();
+ for (Polygon triangleToAdd : p.getTriangulation())
+ toAdd.push(triangleToAdd);
+
+ while (!toAdd.empty()) {
+ Polygon triangleToAdd = toAdd.pop();
+ Stack<Polygon> localAddends = new Stack<Polygon>();
+ localAddends.push(triangleToAdd);
+ for (Polygon triangleAlreadyThere : triangles) {
+ for (Line e : triangleAlreadyThere.getOutlineSegments()) {
+ Stack<Polygon> nextAddends = new Stack<Polygon>();
+ for (Iterator<Polygon> i = localAddends.iterator(); i
+ .hasNext();) {
+ Polygon addend = i.next();
+ i.remove();
+ for (Polygon subTriangleToAdd : triangulate(addend, e))
+ if (!triangleAlreadyThere
+ .contains(subTriangleToAdd))
+ nextAddends.push(subTriangleToAdd);
+ }
+ localAddends = nextAddends;
+ }
+ }
+ for (Polygon addend : localAddends) {
+ triangles.add(addend);
+ }
+ }
+
+ optimizeTriangles();
+
+ return this;
+ }
+
+ public boolean contains(IGeometry g) {
+ return CurveUtils.contains(this, g);
+ }
+
+ private boolean findSharedAndOuterVertices(Polygon t1, Polygon t2,
+ Point[] shared, Point[] outer) {
+ Point[] t1Points = t1.getPoints();
+ Point[] t2Points = t2.getPoints();
+ boolean[] t1IsShared = new boolean[] { false, false, false };
+ boolean[] t2IsShared = new boolean[] { false, false, false };
+
+ int sc = 0;
+ for (int i = 0; i < t1Points.length; i++) {
+ for (int j = 0; j < t2Points.length; j++) {
+ if (t1Points[i].equals(t2Points[j])) {
+ if (sc++ == 2) {
+ return false;
+ }
+ t1IsShared[i] = true;
+ t2IsShared[j] = true;
+ }
+ }
+ }
+ if (sc != 2) {
+ return false;
+ }
+
+ for (int i = 0, c = 0; i < t1Points.length; i++) {
+ if (t1IsShared[i]) {
+ shared[c++] = t1Points[i];
+ } else {
+ outer[0] = t1Points[i];
+ }
+
+ if (!t2IsShared[i]) {
+ outer[1] = t2Points[i];
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ protected Line[] getAllEdges() {
+ Stack<Line> edges = new Stack<Line>();
+
+ for (Polygon t : triangles) {
+ for (Line e : t.getOutlineSegments()) {
+ edges.push(e);
+ }
+ }
+ return edges.toArray(new Line[] {});
}
public Rectangle getBounds() {
- throw new UnsupportedOperationException("Not yet implemented.");
+ if (triangles.size() == 0)
+ return null;
+
+ Rectangle bounds = triangles.get(0).getBounds();
+ for (int i = 1; i < triangles.size(); i++)
+ bounds.union(triangles.get(i).getBounds());
+
+ return bounds;
}
- public boolean touches(Rectangle r) {
- throw new UnsupportedOperationException("Not yet implemented.");
+ public Ring getCopy() {
+ return new Ring(this);
+ }
+
+ public Polygon[] getShapes() {
+ return triangles.toArray(new Polygon[] {});
+ }
+
+ private Polygon mergeTriangles(Polygon t1, Polygon t2) {
+ Point[] shared = new Point[2], outer = new Point[2];
+ boolean found = findSharedAndOuterVertices(t1, t2, shared, outer);
+ if (found) {
+ Line outerLink = new Line(outer[0], outer[1]);
+ if (outerLink.contains(shared[0])) {
+ return new Polygon(outer[0], outer[1], shared[1]);
+ } else if (outerLink.contains(shared[1])) {
+ return new Polygon(outer[0], outer[1], shared[0]);
+ }
+ }
+
+ return null;
+ }
+
+ private void optimizeTriangles() {
+ for (int i = 0; i < triangles.size(); i++) {
+ Polygon t1 = triangles.get(i);
+ for (int j = i + 1; j < triangles.size(); j++) {
+ Polygon t2 = triangles.get(j);
+ Polygon merge = mergeTriangles(t1, t2);
+ if (merge != null) {
+ triangles.set(i, merge);
+ t1 = merge;
+ triangles.remove(j);
+ j = i;
+ }
+ }
+ }
}
public Path toPath() {
- throw new UnsupportedOperationException("Not yet implemented.");
+ return getOutline().toPath();
+ }
+
+ public Ring getRotatedCCW(Angle angle) {
+ return getCopy().rotateCCW(angle);
+ }
+
+ public Ring getRotatedCCW(Angle angle, double cx, double cy) {
+ return getCopy().rotateCCW(angle, cx, cy);
+ }
+
+ public Ring getRotatedCCW(Angle angle, Point center) {
+ return getCopy().rotateCCW(angle, center);
+ }
+
+ public Ring getRotatedCW(Angle angle) {
+ return getCopy().rotateCW(angle);
+ }
+
+ public Ring getRotatedCW(Angle angle, double cx, double cy) {
+ return getCopy().rotateCW(angle, cx, cy);
+ }
+
+ public Ring getRotatedCW(Angle angle, Point center) {
+ return getCopy().rotateCW(angle, center);
+ }
+
+ public Ring rotateCCW(Angle angle) {
+ Point centroid = getBounds().getCentroid();
+ return rotateCCW(angle, centroid.x, centroid.y);
+ }
+
+ public Ring rotateCCW(Angle angle, double cx, double cy) {
+ for (Polygon p : triangles) {
+ p.rotateCCW(angle, cx, cy);
+ }
+ return this;
+ }
+
+ public Ring rotateCCW(Angle angle, Point center) {
+ return rotateCCW(angle, center.x, center.y);
+ }
+
+ public Ring rotateCW(Angle angle) {
+ Point centroid = getBounds().getCentroid();
+ return rotateCW(angle, centroid.x, centroid.y);
+ }
+
+ public Ring rotateCW(Angle angle, double cx, double cy) {
+ for (Polygon p : triangles) {
+ p.rotateCW(angle, cx, cy);
+ }
+ return this;
+ }
+
+ public Ring rotateCW(Angle angle, Point center) {
+ return rotateCW(angle, center.x, center.y);
+ }
+
+ public Ring scale(double factor) {
+ return scale(factor, factor);
+ }
+
+ public Ring scale(double factor, double cx, double cy) {
+ return scale(factor, factor, cx, cy);
+ }
+
+ public Ring scale(double factor, Point center) {
+ return scale(factor, factor, center.x, center.y);
+ }
+
+ public Ring scale(double fx, double fy) {
+ Point centroid = getBounds().getCentroid();
+ return scale(fx, fy, centroid.x, centroid.y);
+ }
+
+ public Ring scale(double fx, double fy, double cx, double cy) {
+ for (Polygon p : triangles) {
+ p.scale(fx, fy, cx, cy);
+ }
+ return this;
+ }
+
+ public Ring scale(double fx, double fy, Point center) {
+ return scale(fx, fy, center.x, center.y);
+ }
+
+ public Ring getScaled(double factor) {
+ return getCopy().scale(factor);
+ }
+
+ public Ring getScaled(double factor, double cx, double cy) {
+ return getCopy().scale(factor, cx, cy);
+ }
+
+ public Ring getScaled(double factor, Point center) {
+ return getCopy().scale(factor, center);
+ }
+
+ public Ring getScaled(double fx, double fy) {
+ return getCopy().scale(fx, fy);
+ }
+
+ public Ring getScaled(double fx, double fy, double cx, double cy) {
+ return getCopy().scale(fx, fy, cx, cy);
+ }
+
+ public Ring getScaled(double fx, double fy, Point center) {
+ return getCopy().scale(fx, fy, center);
+ }
+
+ public Ring translate(double dx, double dy) {
+ for (Polygon p : triangles) {
+ p.translate(dx, dy);
+ }
+ return this;
+ }
+
+ public Ring translate(Point d) {
+ return translate(d.x, d.y);
+ }
+
+ public Ring getTranslated(double dx, double dy) {
+ return getCopy().translate(dx, dy);
}
- public IGeometry getCopy() {
- throw new UnsupportedOperationException("Not yet implemented.");
+ public Ring getTranslated(Point d) {
+ return getCopy().translate(d.x, d.y);
}
}
diff --git a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/RoundedRectangle.java b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/RoundedRectangle.java
index 028d581..2e975ca 100644
--- a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/RoundedRectangle.java
+++ b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/RoundedRectangle.java
@@ -29,7 +29,8 @@ import org.eclipse.gef4.geometry.utils.PrecisionUtils;
* @author anyssen
*/
public final class RoundedRectangle extends
- AbstractRectangleBasedGeometry<Rectangle> implements IShape {
+ AbstractRectangleBasedGeometry<RoundedRectangle, PolyBezier> implements
+ IShape {
private static final long serialVersionUID = 1L;
@@ -85,6 +86,10 @@ public final class RoundedRectangle extends
arcHeight);
}
+ public boolean contains(IGeometry g) {
+ return CurveUtils.contains(this, g);
+ }
+
/**
* @see IGeometry#contains(Point)
*/
@@ -126,6 +131,19 @@ public final class RoundedRectangle extends
return false;
}
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof RoundedRectangle)) {
+ return false;
+ }
+ RoundedRectangle o = (RoundedRectangle) obj;
+ return PrecisionUtils.equal(x, o.x) && PrecisionUtils.equal(y, o.y)
+ && PrecisionUtils.equal(width, o.width)
+ && PrecisionUtils.equal(height, o.height)
+ && PrecisionUtils.equal(arcWidth, o.arcWidth)
+ && PrecisionUtils.equal(arcHeight, o.arcHeight);
+ }
+
/**
* Returns the arc height of this {@link RoundedRectangle}, which is the
* height of the arc used to define its rounded corners.
@@ -147,63 +165,81 @@ public final class RoundedRectangle extends
}
/**
- * Sets the arc height of this {@link RoundedRectangle}, which is the height
- * of the arc used to define its rounded corners.
+ * Returns the bottom edge of this {@link RoundedRectangle}.
*
- * @param arcHeight
- * the new arc height
+ * @return the bottom edge of this {@link RoundedRectangle}.
*/
- public void setArcHeight(double arcHeight) {
- this.arcHeight = arcHeight;
+ public Line getBottom() {
+ return new Line(x + arcWidth, y + height, x + width - arcWidth, y
+ + height);
}
/**
- * Sets the arc width of this {@link RoundedRectangle}, which is the width
- * of the arc used to define its rounded corners.
+ * Returns the bottom left {@link Arc} of this {@link RoundedRectangle}.
*
- * @param arcWidth
- * the new arc width
+ * @return the bottom left {@link Arc} of this {@link RoundedRectangle}.
*/
- public void setArcWidth(double arcWidth) {
- this.arcWidth = arcWidth;
+ public Arc getBottomLeftArc() {
+ return new Arc(x, y + height - 2 * arcHeight, 2 * arcWidth,
+ 2 * arcHeight, Angle.fromDeg(180), Angle.fromDeg(90));
}
/**
- * @see IGeometry#toPath()
+ * Returns the bottom right {@link Arc} of this {@link RoundedRectangle}.
+ *
+ * @return the bottom right {@link Arc} of this {@link RoundedRectangle}.
*/
- public Path toPath() {
- // overwritten to optimize w.r.t. object creation (could otherwise use
- // the segments)
- Path path = new Path();
- path.moveTo(x, y - arcHeight);
- path.quadTo(x + arcWidth, y, x, y);
- path.lineTo(x + width - arcWidth * 2, y);
- path.quadTo(x + width, y + arcHeight, x + width, y);
- path.lineTo(x + width, y + height - arcHeight * 2);
- path.quadTo(x + width - arcWidth * 2, y + height, x + width, y + width);
- path.lineTo(x + arcWidth, y + height);
- path.quadTo(x, y + height - arcHeight * 2, x, y + height);
- path.close();
- return path;
+ public Arc getBottomRightArc() {
+ return new Arc(x + width - 2 * arcWidth, y + height - 2 * arcHeight,
+ 2 * arcWidth, 2 * arcHeight, Angle.fromDeg(270),
+ Angle.fromDeg(90));
}
/**
- * Returns the top edge of this {@link RoundedRectangle}.
- *
- * @return the top edge of this {@link RoundedRectangle}.
+ * @see IGeometry#getCopy()
*/
- public Line getTop() {
- return new Line(x + arcWidth, y, x + width - arcWidth, y);
+ public RoundedRectangle getCopy() {
+ return new RoundedRectangle(x, y, width, height, arcWidth, arcHeight);
}
/**
- * Returns the bottom edge of this {@link RoundedRectangle}.
+ * Returns the left edge of this {@link RoundedRectangle}.
*
- * @return the bottom edge of this {@link RoundedRectangle}.
+ * @return the left edge of this {@link RoundedRectangle}.
*/
- public Line getBottom() {
- return new Line(x + arcWidth, y + height - arcHeight, x + width
- - arcWidth, y + height - arcHeight);
+ public Line getLeft() {
+ return new Line(x, y + arcHeight, x, y + height - arcHeight);
+ }
+
+ public PolyBezier getOutline() {
+ return CurveUtils.getOutline(this);
+ }
+
+ /**
+ * @see org.eclipse.gef4.geometry.planar.IShape#getOutlineSegments()
+ */
+ public ICurve[] getOutlineSegments() {
+ // see http://whizkidtech.redprince.net/bezier/circle/kappa/ for details
+ // on the approximation used here
+ return new ICurve[] {
+ CurveUtils.computeEllipticalArcApproximation(x + width - 2
+ * arcWidth, y, 2 * arcWidth, 2 * arcHeight,
+ Angle.fromDeg(0), Angle.fromDeg(90)),
+ new Line(x + width - arcWidth, y, x + arcWidth, y),
+ CurveUtils.computeEllipticalArcApproximation(x, y,
+ 2 * arcWidth, 2 * arcHeight, Angle.fromDeg(90),
+ Angle.fromDeg(180)),
+ new Line(x, y + arcHeight, x, y + height - arcHeight),
+ CurveUtils.computeEllipticalArcApproximation(x, y + height - 2
+ * arcHeight, 2 * arcWidth, 2 * arcHeight,
+ Angle.fromDeg(180), Angle.fromDeg(270)),
+ new Line(x + arcWidth, y + height, x + width - arcWidth, y
+ + height),
+ CurveUtils.computeEllipticalArcApproximation(x + width - 2
+ * arcWidth, y + height - 2 * arcHeight, 2 * arcWidth,
+ 2 * arcHeight, Angle.fromDeg(270), Angle.fromDeg(360)),
+ new Line(x + width, y + height - arcHeight, x + width, y
+ + arcHeight) };
}
/**
@@ -216,32 +252,37 @@ public final class RoundedRectangle extends
- arcHeight);
}
- /**
- * @see org.eclipse.gef4.geometry.planar.IShape#getOutlineSegments()
- */
- public ICurve[] getOutlineSegments() {
- return new ICurve[] {
- new QuadraticCurve(x, y - arcHeight, x + arcWidth, y, x, y),
- new Line(x, y, x + width - arcWidth * 2, y),
- new QuadraticCurve(x + width - arcWidth * 2, y, x + width, y
- + arcHeight, x + width, y),
- new Line(x + width, y, x + width, y + height - arcHeight * 2),
- new QuadraticCurve(x + width, y + height - arcHeight * 2, x
- + width - arcWidth * 2, y + height, x + width, y
- + width),
- new Line(x + width, y + width, x + arcWidth, y + height),
- new QuadraticCurve(x + arcWidth, y + height, x, y + height
- - arcHeight * 2, x, y + height),
- new Line(x, y + height, x, y - arcHeight) };
+ public PolyBezier getRotatedCCW(Angle angle) {
+ return getOutline().rotateCCW(angle);
+ }
+
+ public PolyBezier getRotatedCCW(Angle angle, double cx, double cy) {
+ return getOutline().rotateCCW(angle, cx, cy);
+ }
+
+ public PolyBezier getRotatedCCW(Angle angle, Point center) {
+ return getOutline().rotateCCW(angle, center);
+ }
+
+ public PolyBezier getRotatedCW(Angle angle) {
+ return getOutline().rotateCW(angle);
+ }
+
+ public PolyBezier getRotatedCW(Angle angle, double cx, double cy) {
+ return getOutline().rotateCW(angle, cx, cy);
+ }
+
+ public PolyBezier getRotatedCW(Angle angle, Point center) {
+ return getOutline().rotateCW(angle, center);
}
/**
- * Returns the left edge of this {@link RoundedRectangle}.
+ * Returns the top edge of this {@link RoundedRectangle}.
*
- * @return the left edge of this {@link RoundedRectangle}.
+ * @return the top edge of this {@link RoundedRectangle}.
*/
- public Line getLeft() {
- return new Line(x, y + arcHeight, x, y + height - arcHeight);
+ public Line getTop() {
+ return new Line(x + arcWidth, y, x + width - arcWidth, y);
}
/**
@@ -250,7 +291,7 @@ public final class RoundedRectangle extends
* @return the top left {@link Arc} of this {@link RoundedRectangle}.
*/
public Arc getTopLeftArc() {
- return new Arc(x, y, arcWidth, arcHeight, Angle.fromDeg(90),
+ return new Arc(x, y, 2 * arcWidth, 2 * arcHeight, Angle.fromDeg(90),
Angle.fromDeg(90));
}
@@ -260,48 +301,56 @@ public final class RoundedRectangle extends
* @return the top right {@link Arc} of this {@link RoundedRectangle}.
*/
public Arc getTopRightArc() {
- return new Arc(x + width - arcWidth, y, arcWidth, arcHeight,
- Angle.fromDeg(0), Angle.fromDeg(90));
+ return new Arc(x + width - 2 * arcWidth, y, 2 * arcWidth,
+ 2 * arcHeight, Angle.fromDeg(0), Angle.fromDeg(90));
}
/**
- * Returns the bottom left {@link Arc} of this {@link RoundedRectangle}.
+ * Sets the arc height of this {@link RoundedRectangle}, which is the height
+ * of the arc used to define its rounded corners.
*
- * @return the bottom left {@link Arc} of this {@link RoundedRectangle}.
+ * @param arcHeight
+ * the new arc height
*/
- public Arc getBottomLeftArc() {
- return new Arc(x, y + height - arcHeight, arcWidth, arcHeight,
- Angle.fromDeg(180), Angle.fromDeg(90));
+ public void setArcHeight(double arcHeight) {
+ this.arcHeight = arcHeight;
}
/**
- * Returns the bottom right {@link Arc} of this {@link RoundedRectangle}.
+ * Sets the arc width of this {@link RoundedRectangle}, which is the width
+ * of the arc used to define its rounded corners.
*
- * @return the bottom right {@link Arc} of this {@link RoundedRectangle}.
+ * @param arcWidth
+ * the new arc width
*/
- public Arc getBottomRightArc() {
- return new Arc(x + width - arcWidth, y + height - arcHeight, arcWidth,
- arcHeight, Angle.fromDeg(270), Angle.fromDeg(90));
- }
-
- @Override
- public String toString() {
- return "RoundedRectangle: (" + x + ", " + y + ", " + width + ", " + height + ", " + arcWidth + ", " + arcHeight; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$
+ public void setArcWidth(double arcWidth) {
+ this.arcWidth = arcWidth;
}
/**
- * @see IGeometry#getCopy()
+ * @see IGeometry#toPath()
*/
- public RoundedRectangle getCopy() {
- return new RoundedRectangle(x, y, width, height, arcWidth, arcHeight);
- }
-
- public IPolyCurve getOutline() {
- return CurveUtils.getOutline(this);
+ public Path toPath() {
+ // return CurveUtils.toPath(getOutlineSegments());
+ // TODO: use cubic curves instead of quadratic curves here!
+ // overwritten to optimize w.r.t. object creation (could otherwise use
+ // the segments)
+ Path path = new Path();
+ path.moveTo(x, y + arcHeight);
+ path.quadTo(x, y, x + arcWidth, y);
+ path.lineTo(x + width - arcWidth, y);
+ path.quadTo(x + width, y, x + width, y + arcHeight);
+ path.lineTo(x + width, y + height - arcHeight);
+ path.quadTo(x + width, y + height, x + width - arcWidth, y + height);
+ path.lineTo(x + arcWidth, y + height);
+ path.quadTo(x, y + height, x, y + height - arcHeight);
+ path.close();
+ return path;
}
- public boolean contains(IGeometry g) {
- return CurveUtils.contains(this, g);
+ @Override
+ public String toString() {
+ return "RoundedRectangle(" + x + ", " + y + ", " + width + ", " + height + ", " + arcWidth + ", " + arcHeight + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$
}
}
diff --git a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/projective/Vector3D.java b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/projective/Vector3D.java
index 4b238ed..5cc50cd 100644
--- a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/projective/Vector3D.java
+++ b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/projective/Vector3D.java
@@ -184,7 +184,7 @@ public final class Vector3D {
@Override
public String toString() {
- return "Vector3D (" + x + ", " + y + ", " + z + ")";
+ return "Vector3D(" + x + ", " + y + ", " + z + ")";
}
@Override
@@ -193,4 +193,4 @@ public final class Vector3D {
// comparisons
return 0;
}
-} \ No newline at end of file
+}
diff --git a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/transform/IRotatable.java b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/transform/IRotatable.java
new file mode 100644
index 0000000..96413b3
--- /dev/null
+++ b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/transform/IRotatable.java
@@ -0,0 +1,134 @@
+/*******************************************************************************
+ * Copyright (c) 2012 itemis AG and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Matthias Wienand (itemis AG) - initial API and implementation
+ *
+ *******************************************************************************/
+package org.eclipse.gef4.geometry.transform;
+
+import org.eclipse.gef4.geometry.Angle;
+import org.eclipse.gef4.geometry.Point;
+import org.eclipse.gef4.geometry.planar.Ellipse;
+import org.eclipse.gef4.geometry.planar.IGeometry;
+import org.eclipse.gef4.geometry.planar.Rectangle;
+import org.eclipse.gef4.geometry.planar.Region;
+import org.eclipse.gef4.geometry.planar.RoundedRectangle;
+
+/**
+ * <p>
+ * The {@link IRotatable} interface collects the out-of-place rotation short-cut
+ * methods.
+ * </p>
+ *
+ * <p>
+ * Rotation cannot be applied directly to all {@link IGeometry}s. For example,
+ * {@link Rectangle}, {@link Ellipse}, {@link Region} and
+ * {@link RoundedRectangle} cannot be slanted. Therefore, you have to specify
+ * the result type for the rotation methods via a type parameter.
+ * </p>
+ *
+ * <p>
+ * There are two directions of rotation: clock-wise (CW) and counter-clock-wise
+ * (CCW). The individual method names reflect the direction of rotation that is
+ * used. These are the rotation methods: {@link #getRotatedCCW(Angle)},
+ * {@link #getRotatedCCW(Angle, Point)},
+ * {@link #getRotatedCCW(Angle, double, double)}, {@link #getRotatedCW(Angle)},
+ * {@link #getRotatedCW(Angle, Point)},
+ * {@link #getRotatedCW(Angle, double, double)}.
+ * </p>
+ *
+ * <p>
+ * If you do not specify a {@link Point} to rotate around, the implementation
+ * can appropriately choose one. In most cases, this will be the center
+ * {@link Point} of the rotated object.
+ * </p>
+ *
+ * @param <T>
+ * type of the rotation results
+ */
+public interface IRotatable<T extends IGeometry> {
+
+ /**
+ * Rotates the calling object by specified {@link Angle} counter-clock-wise
+ * (CCW) around its center {@link Point}. Does not necessarily return an
+ * object of the same type.
+ *
+ * @param angle
+ * rotation {@link Angle}
+ * @return an {@link IGeometry} representing the result of the rotation
+ */
+ public T getRotatedCCW(Angle angle);
+
+ /**
+ * Rotates the calling object by the specified {@link Angle}
+ * counter-clock-wise (CCW) around the specified center {@link Point} (cx,
+ * cy). Does not necessarily return an object of the same type.
+ *
+ * @param angle
+ * rotation {@link Angle}
+ * @param cx
+ * x-coordinate of the relative {@link Point} for the rotation
+ * @param cy
+ * y-coordinate of the relative {@link Point} for the rotation
+ * @return an {@link IGeometry} representing the result of the rotation
+ */
+ public T getRotatedCCW(Angle angle, double cx, double cy);
+
+ /**
+ * Rotates the calling object by the specified {@link Angle}
+ * counter-clock-wise (CCW) around the specified center {@link Point}. Does
+ * not necessarily return an object of the same type.
+ *
+ * @param angle
+ * rotation {@link Angle}
+ * @param center
+ * relative {@link Point} for the rotation
+ * @return an {@link IGeometry} representing the result of the rotation
+ */
+ public T getRotatedCCW(Angle angle, Point center);
+
+ /**
+ * Rotates the calling object by specified {@link Angle} clock-wise (CW)
+ * around its center {@link Point}. Does not necessarily return an object of
+ * the same type.
+ *
+ * @param angle
+ * rotation {@link Angle}
+ * @return an {@link IGeometry} representing the result of the rotation
+ */
+ public T getRotatedCW(Angle angle);
+
+ /**
+ * Rotates the calling object by the specified {@link Angle} clock-wise (CW)
+ * around the specified center {@link Point} (cx, cy). Does not necessarily
+ * return an object of the same type.
+ *
+ * @param angle
+ * rotation {@link Angle}
+ * @param cx
+ * x-coordinate of the relative {@link Point} for the rotation
+ * @param cy
+ * y-coordinate of the relative {@link Point} for the rotation
+ * @return an {@link IGeometry} representing the result of the rotation
+ */
+ public T getRotatedCW(Angle angle, double cx, double cy);
+
+ /**
+ * Rotates the calling object by the specified {@link Angle} clock-wise (CW)
+ * around the specified center {@link Point}. Does not necessarily return an
+ * object of the same type.
+ *
+ * @param angle
+ * rotation {@link Angle}
+ * @param center
+ * relative {@link Point} for the rotation
+ * @return an {@link IGeometry} representing the result of the rotation
+ */
+ public T getRotatedCW(Angle angle, Point center);
+
+}
diff --git a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/transform/IScalable.java b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/transform/IScalable.java
new file mode 100644
index 0000000..9200f0a
--- /dev/null
+++ b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/transform/IScalable.java
@@ -0,0 +1,207 @@
+/*******************************************************************************
+ * Copyright (c) 2012 itemis AG and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Matthias Wienand (itemis AG) - initial API and implementation
+ *
+ *******************************************************************************/
+package org.eclipse.gef4.geometry.transform;
+
+import org.eclipse.gef4.geometry.Point;
+import org.eclipse.gef4.geometry.planar.IGeometry;
+
+/**
+ * <p>
+ * The {@link IScalable} interface collects all scaling short-cut methods.
+ * </p>
+ *
+ * <p>
+ * The {@link #scale(double)}, {@link #scale(double, double)},
+ * {@link #scale(double, Point)}, {@link #scale(double, double, double)},
+ * {@link #scale(double, double, Point)} and
+ * {@link #scale(double, double, double, double)} methods are directly applied
+ * to the calling object. They scale it by the given factor(s) around the given
+ * {@link Point} or an appropriate default.
+ * </p>
+ *
+ * <p>
+ * On the other hand, the {@link #getScaled(double)},
+ * {@link #getScaled(double, double)}, {@link #getScaled(double, Point)},
+ * {@link #getScaled(double, double, double)},
+ * {@link #getScaled(double, double, Point)} and
+ * {@link #getScaled(double, double, double, double)} methods are applied to a
+ * copy of the calling object.
+ * </p>
+ *
+ * <p>
+ * If you do not specify the relative {@link Point} for the scaling, the
+ * implementation will appropriately choose one. In most cases, this will be the
+ * center of the scaled object.
+ * </p>
+ *
+ * @param <T>
+ * the implementing type
+ */
+public interface IScalable<T extends IGeometry> {
+
+ /**
+ * Scales the calling object by the given factor relative to its center
+ * {@link Point}.
+ *
+ * @param factor
+ * scale-factor
+ * @return <code>this</code> for convenience
+ */
+ public T scale(double factor);
+
+ /**
+ * Scales the calling object by the given factor relative to the given
+ * center {@link Point} (cx, cy).
+ *
+ * @param factor
+ * scale-factor
+ * @param cx
+ * x-coordinate of the relative {@link Point} for the scaling
+ * @param cy
+ * y-coordinate of the relative {@link Point} for the scaling
+ * @return <code>this</code> for convenience
+ */
+ public T scale(double factor, double cx, double cy);
+
+ /**
+ * Scales the calling object by the given factor relative to the given
+ * center {@link Point}.
+ *
+ * @param factor
+ * scale-factor
+ * @param center
+ * relative {@link Point} for the scaling
+ * @return <code>this</code> for convenience
+ */
+ public T scale(double factor, Point center);
+
+ /**
+ * Scales the calling object by the given factors relative to the given
+ * center {@link Point}.
+ *
+ * @param fx
+ * x-scale-factor
+ * @param fy
+ * y-scale-factor
+ * @return <code>this</code> for convenience
+ */
+ public T scale(double fx, double fy);
+
+ /**
+ * Scales the calling object by the given factors relative to the given
+ * center {@link Point} (cx, cy).
+ *
+ * @param fx
+ * x-scale-factor
+ * @param fy
+ * y-scale-factor
+ * @param cx
+ * x-coordinate of the relative {@link Point} for the scaling
+ * @param cy
+ * y-coordinate of the relative {@link Point} for the scaling
+ * @return <code>this</code> for convenience
+ */
+ public T scale(double fx, double fy, double cx, double cy);
+
+ /**
+ * Scales the calling object by the given factors relative to the given
+ * center {@link Point}.
+ *
+ * @param fx
+ * x-scale-factor
+ * @param fy
+ * y-scale-factor
+ * @param center
+ * relative {@link Point} for the scaling
+ * @return <code>this</code> for convenience
+ */
+ public T scale(double fx, double fy, Point center);
+
+ /**
+ * Scales a copy of the calling object by the given factor relative to its
+ * center {@link Point}.
+ *
+ * @param factor
+ * scale-factor
+ * @return the new, scaled object
+ */
+ public T getScaled(double factor);
+
+ /**
+ * Scales a copy of the calling object by the given factor relative to the
+ * given center {@link Point} (cx, cy).
+ *
+ * @param factor
+ * scale-factor
+ * @param cx
+ * x-coordinate of the relative {@link Point} for the scaling
+ * @param cy
+ * y-coordinate of the relative {@link Point} for the scaling
+ * @return the new, scaled object
+ */
+ public T getScaled(double factor, double cx, double cy);
+
+ /**
+ * Scales a copy of the calling object by the given factor relative to the
+ * given center {@link Point}.
+ *
+ * @param factor
+ * scale-factor
+ * @param center
+ * relative {@link Point} for the scaling
+ * @return the new, scaled object
+ */
+ public T getScaled(double factor, Point center);
+
+ /**
+ * Scales a copy of the calling object by the given factors relative to its
+ * center {@link Point}.
+ *
+ * @param fx
+ * x-scale-factor
+ * @param fy
+ * y-scale-factor
+ * @return the new, scaled object
+ */
+ public T getScaled(double fx, double fy);
+
+ /**
+ * Scales a copy of the calling object by the given factors relative to the
+ * given center {@link Point} (cx, cy).
+ *
+ * @param fx
+ * x-scale-factor
+ * @param fy
+ * y-scale-factor
+ * @param cx
+ * x-coordinate of the relative {@link Point} for the scaling
+ * @param cy
+ * y-coordinate of the relative {@link Point} for the scaling
+ * @return the new, scaled object
+ */
+ public T getScaled(double fx, double fy, double cx, double cy);
+
+ /**
+ * Scales a copy of the calling object by the given factors relative to the
+ * given center {@link Point}.
+ *
+ * @param fx
+ * x-scale-factor
+ * @param fy
+ * y-scale-factor
+ * @param center
+ * relative {@link Point} for the scaling
+ * @return the new, scaled object
+ */
+ public T getScaled(double fx, double fy, Point center);
+
+}
diff --git a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/transform/ITranslatable.java b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/transform/ITranslatable.java
new file mode 100644
index 0000000..063301b
--- /dev/null
+++ b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/transform/ITranslatable.java
@@ -0,0 +1,81 @@
+/*******************************************************************************
+ * Copyright (c) 2012 itemis AG and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Matthias Wienand (itemis AG) - initial API and implementation
+ *
+ *******************************************************************************/
+package org.eclipse.gef4.geometry.transform;
+
+import org.eclipse.gef4.geometry.Point;
+import org.eclipse.gef4.geometry.planar.IGeometry;
+
+/**
+ * <p>
+ * The {@link ITranslatable} interface collects all translation short-cut
+ * methods.
+ * </p>
+ *
+ * <p>
+ * Translation can be applied directly on an object via the
+ * {@link #translate(Point)} and {@link #translate(double, double)} methods.
+ * They return the scaled, calling object for convenience.
+ * </p>
+ *
+ * <p>
+ * On the other hand, the {@link #getTranslated(Point)} and
+ * {@link #getTranslated(double, double)} methods create a translated copy of
+ * the original object.
+ * </p>
+ *
+ * @param <T>
+ * the implementing type
+ */
+public interface ITranslatable<T extends IGeometry> {
+
+ /**
+ * Translates the object by the given values in x and y direction.
+ *
+ * @param dx
+ * x-translation
+ * @param dy
+ * y-translation
+ * @return <code>this</code> for convenience
+ */
+ public T translate(double dx, double dy);
+
+ /**
+ * Translates the object by the given {@link Point}.
+ *
+ * @param d
+ * translation {@link Point}
+ * @return <code>this</code> for convenience
+ */
+ public T translate(Point d);
+
+ /**
+ * Translates a copy of this object by the given values in x and y
+ * direction.
+ *
+ * @param dx
+ * x-translation
+ * @param dy
+ * y-translation
+ * @return a new, translated object
+ */
+ public T getTranslated(double dx, double dy);
+
+ /**
+ * Translates a copy of this object by the given {@link Point}.
+ *
+ * @param d
+ * translation {@link Point}
+ * @return a new, translated object
+ */
+ public T getTranslated(Point d);
+
+}
diff --git a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/utils/CurveUtils.java b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/utils/CurveUtils.java
index 4605a63..8115b18 100644
--- a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/utils/CurveUtils.java
+++ b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/utils/CurveUtils.java
@@ -17,17 +17,22 @@ import java.util.Comparator;
import java.util.HashSet;
import java.util.Set;
+import org.eclipse.gef4.geometry.Angle;
import org.eclipse.gef4.geometry.Point;
import org.eclipse.gef4.geometry.euclidean.Straight;
+import org.eclipse.gef4.geometry.planar.Arc;
import org.eclipse.gef4.geometry.planar.BezierCurve;
import org.eclipse.gef4.geometry.planar.BezierCurve.IntervalPair;
+import org.eclipse.gef4.geometry.planar.CubicCurve;
import org.eclipse.gef4.geometry.planar.ICurve;
import org.eclipse.gef4.geometry.planar.IGeometry;
import org.eclipse.gef4.geometry.planar.IPolyCurve;
import org.eclipse.gef4.geometry.planar.IPolyShape;
import org.eclipse.gef4.geometry.planar.IShape;
import org.eclipse.gef4.geometry.planar.Line;
+import org.eclipse.gef4.geometry.planar.Path;
import org.eclipse.gef4.geometry.planar.PolyBezier;
+import org.eclipse.gef4.geometry.planar.QuadraticCurve;
/**
* The {@link CurveUtils} class provides functionality that can be used for
@@ -259,12 +264,7 @@ public class CurveUtils {
for (ICurve segC : shape.getOutlineSegments()) {
for (BezierCurve seg : segC.toBezier()) {
Set<Point> inters = new HashSet<Point>();
- Set<IntervalPair> ips = c.getIntersectionIntervalPairs(
- new BezierCurve(seg.getP1(), seg.getP2()), inters);
- for (IntervalPair ip : ips) {
- intersectionParams.add(ip.p == c ? ip.pi.getMid() : ip.qi
- .getMid());
- }
+ c.getIntersectionIntervalPairs(seg, inters);
for (Point poi : inters) {
intersectionParams.add(c.getParameterAt(poi));
}
@@ -278,7 +278,7 @@ public class CurveUtils {
*
* TODO: Special case! There is a special case where the Bezier curve
* leaves and enters the shape in the same point. This is only possible
- * if the Bezier curve has a self intersections at that point.
+ * if the Bezier curve has a self intersection at that point.
*/
if (intersectionParams.size() <= 1) {
return true;
@@ -305,6 +305,155 @@ public class CurveUtils {
}
/**
+ * TODO: generalize the contains() method for IShape and IPolyShape.
+ *
+ * @param polyShape
+ * @param c
+ * @return <code>true</code> if the {@link BezierCurve} is contained by the
+ * {@link IPolyShape}, otherwise <code>false</code>
+ */
+ public static boolean contains(IPolyShape polyShape, BezierCurve c) {
+ if (!(polyShape.contains(c.getP1()) && polyShape.contains(c.getP2()))) {
+ return false;
+ }
+
+ Set<Double> intersectionParams = new HashSet<Double>();
+
+ for (ICurve segC : polyShape.getOutlineSegments()) {
+ for (BezierCurve seg : segC.toBezier()) {
+ Set<Point> inters = new HashSet<Point>();
+ Set<IntervalPair> ips = c.getIntersectionIntervalPairs(seg,
+ inters);
+ for (IntervalPair ip : ips) {
+ intersectionParams.add(ip.p == c ? ip.pi.getMid() : ip.qi
+ .getMid());
+ }
+ for (Point poi : inters) {
+ intersectionParams.add(c.getParameterAt(poi));
+ }
+ }
+ }
+
+ /*
+ * Start and end point of the curve are guaranteed to lie inside the
+ * IPolyShape. If the curve would not be contained by the shape, at
+ * least two intersections could be found.
+ *
+ * TODO: Special case! There is a special case where the Bezier curve
+ * leaves and enters the shape in the same point. This is only possible
+ * if the Bezier curve has a self intersection at that point.
+ */
+ if (intersectionParams.size() <= 1) {
+ return true;
+ }
+
+ Double[] poiParams = intersectionParams.toArray(new Double[] {});
+ Arrays.sort(poiParams, new Comparator<Double>() {
+ public int compare(Double t, Double u) {
+ double d = t - u;
+ return d < 0 ? -1 : d > 0 ? 1 : 0;
+ }
+ });
+
+ // check the points between the intersections for containment
+ if (!polyShape.contains(c.get(poiParams[0] / 2))) {
+ return false;
+ }
+ for (int i = 0; i < poiParams.length - 1; i++) {
+ if (!polyShape.contains(c
+ .get((poiParams[i] + poiParams[i + 1]) / 2))) {
+ return false;
+ }
+ }
+ return polyShape.contains(c
+ .get((poiParams[poiParams.length - 1] + 1) / 2));
+ }
+
+ /**
+ * Checks if the given {@link ICurve} is contained by the given
+ * {@link IPolyShape}.
+ *
+ * @param ps
+ * @param c
+ * @return <code>true</code> if the {@link ICurve} is contained by the
+ * {@link IPolyShape}, otherwise <code>false</code>
+ */
+ public static boolean contains(IPolyShape ps, ICurve c) {
+ for (BezierCurve bc : c.toBezier())
+ if (!contains(ps, bc))
+ return false;
+ return true;
+ }
+
+ /**
+ * Checks if the {@link IShape} is contained by the {@link IPolyShape}.
+ *
+ * @param ps
+ * @param s
+ * @return <code>true</code> if the {@link IShape} is contained by the
+ * {@link IPolyShape}, otherwise <code>false</code>
+ */
+ public static boolean contains(IPolyShape ps, IShape s) {
+ for (ICurve c : s.getOutlineSegments())
+ if (!contains(ps, c))
+ return false;
+ return true;
+ }
+
+ /**
+ * Checks if the {@link IPolyCurve} is contained by the {@link IPolyShape}.
+ *
+ * @param ps
+ * @param pc
+ * @return <code>true</code> if the {@link IPolyCurve} is contained by the
+ * {@link IPolyShape}, otherwise <code>false</code>
+ */
+ public static boolean contains(IPolyShape ps, IPolyCurve pc) {
+ for (ICurve c : pc.getCurves())
+ if (!contains(ps, c))
+ return false;
+ return true;
+ }
+
+ /**
+ * Checks if the second {@link IPolyShape} is contained by the first
+ * {@link IPolyShape}.
+ *
+ * @param ps
+ * @param ps2
+ * @return <code>true</code> if the second {@link IPolyShape} is contained
+ * by the first {@link IPolyShape}, otherwise <code>false</code>
+ */
+ public static boolean contains(IPolyShape ps, IPolyShape ps2) {
+ for (IShape s : ps2.getShapes())
+ if (!contains(ps, s))
+ return false;
+ return true;
+ }
+
+ /**
+ * Checks if the {@link IGeometry} is contained by the {@link IPolyShape}.
+ *
+ * @param ps
+ * @param g
+ * @return <code>true</code> if the {@link IGeometry} is contained by the
+ * {@link IPolyShape}, otherwise <code>false</code>
+ */
+ public static boolean contains(IPolyShape ps, IGeometry g) {
+ if (g instanceof ICurve) {
+ return contains(ps, (ICurve) g);
+ } else if (g instanceof IShape) {
+ return contains(ps, (IShape) g);
+ } else if (g instanceof IPolyCurve) {
+ return contains(ps, (IPolyCurve) g);
+ } else if (g instanceof IPolyShape) {
+ return contains(ps, (IPolyShape) g);
+ } else {
+ throw new UnsupportedOperationException("Not yet implemented.");
+ }
+ }
+
+ /**
* Returns <code>true</code> if the given {@link IShape} fully contains the
* given {@link ICurve}. Otherwise, <code>false</code> is returned. A
* {@link ICurve} is contained by a {@link IShape} if the {@link ICurve}'s
@@ -457,7 +606,7 @@ public class CurveUtils {
if (geom1 instanceof IShape) {
return contains((IShape) geom1, geom2);
} else if (geom1 instanceof IPolyShape) {
- throw new UnsupportedOperationException("Not yet implemented.");
+ return contains((IPolyShape) geom1, geom2);
} else {
return false;
}
@@ -487,4 +636,102 @@ public class CurveUtils {
return new PolyBezier(beziers.toArray(new BezierCurve[] {}));
}
+
+ /**
+ * Builds up a {@link Path} from the given {@link ICurve}s. Only
+ * {@link Line}, {@link QuadraticCurve} and {@link CubicCurve} objects can
+ * be integrated into the constructed {@link Path}.
+ *
+ * @param curves
+ * @return a {@link Path} representing the given {@link ICurve}s
+ */
+ public static final Path toPath(ICurve... curves) {
+ Path p = new Path();
+ for (int i = 0; i < curves.length; i++) {
+ if (i == 0) {
+ p.moveTo(curves[i].getX1(), curves[i].getY1());
+ }
+ ICurve c = curves[i];
+ if (c instanceof Line) {
+ p.lineTo(c.getX2(), c.getY2());
+ } else if (c instanceof QuadraticCurve) {
+ p.quadTo(((QuadraticCurve) c).getCtrlX(),
+ ((QuadraticCurve) c).getCtrlY(), c.getX2(), c.getY2());
+ } else if (c instanceof CubicCurve) {
+ p.curveTo(((CubicCurve) c).getCtrlX1(),
+ ((CubicCurve) c).getCtrlY1(),
+ ((CubicCurve) c).getCtrlX2(),
+ ((CubicCurve) c).getCtrlY2(), ((CubicCurve) c).getX2(),
+ ((CubicCurve) c).getY2());
+ } else {
+ throw new UnsupportedOperationException(
+ "This type of ICurve is not yet implemented.");
+ }
+ }
+ return p;
+ }
+
+ /**
+ * <p>
+ * Computes a {@link CubicCurve} that approximates the elliptical
+ * {@link Arc} given by the location, the width, and the height of the
+ * implied ellipse and the start and end {@link Angle}s of the arc.
+ * </p>
+ *
+ * <p>
+ * The given start and end {@link Angle}s may not span an {@link Angle} of
+ * more than 90 degrees.
+ * </p>
+ *
+ * @param x
+ * left coordinate value of the aforementioned ellipse
+ * @param y
+ * top coordinate value of the aforementioned ellipse
+ * @param width
+ * width of the aforementioned ellipse
+ * @param height
+ * height of the aforementioned ellipse
+ * @param start
+ * start angle (in radiant) of the elliptical arc
+ * @param end
+ * end angle (in radiant) of the elliptical arc
+ * @return {@link CubicCurve} approximating the determinated elliptical arc
+ */
+ public static CubicCurve computeEllipticalArcApproximation(double x,
+ double y, double width, double height, Angle start, Angle end) {
+ // TODO: verify that the following test is valid
+ if (!PrecisionUtils.smallerEqual(end.getAdded(start.getOppositeFull())
+ .deg(), 90)) {
+ throw new IllegalArgumentException(
+ "Only angular extents of up to 90 degrees are allowed.");
+ }
+
+ // compute major and minor axis length
+ double a = width / 2;
+ double b = height / 2;
+
+ double srad = start.rad();
+ double erad = end.rad();
+
+ // calculate start and end points of the arc from start to end
+ Point startPoint = new Point(x + a + a * Math.cos(srad), y + b - b
+ * Math.sin(srad));
+ Point endPoint = new Point(x + a + a * Math.cos(erad), y + b - b
+ * Math.sin(erad));
+
+ // approximation by cubic Bezier according to approximation provided in:
+ // http://www.spaceroots.org/documents/ellipse/elliptical-arc.pdf
+ double t = Math.tan((erad - srad) / 2);
+ double alpha = Math.sin(erad - srad)
+ * (Math.sqrt(4.0d + 3.0d * t * t) - 1) / 3;
+ Point controlPoint1 = new Point(startPoint.x + alpha * -a
+ * Math.sin(srad), startPoint.y - alpha * b * Math.cos(srad));
+ Point controlPoint2 = new Point(endPoint.x - alpha * -a
+ * Math.sin(erad), endPoint.y + alpha * b * Math.cos(erad));
+
+ Point[] points = new Point[] { startPoint, controlPoint1,
+ controlPoint2, endPoint };
+ return new CubicCurve(points);
+ }
+
}
diff --git a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/utils/PointListUtils.java b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/utils/PointListUtils.java
index 5911ef9..e373eef 100644
--- a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/utils/PointListUtils.java
+++ b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/utils/PointListUtils.java
@@ -16,8 +16,10 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
+import org.eclipse.gef4.geometry.Angle;
import org.eclipse.gef4.geometry.Point;
import org.eclipse.gef4.geometry.euclidean.Straight;
+import org.eclipse.gef4.geometry.euclidean.Vector;
import org.eclipse.gef4.geometry.planar.Line;
import org.eclipse.gef4.geometry.planar.Polygon;
import org.eclipse.gef4.geometry.planar.Polyline;
@@ -32,13 +34,15 @@ import org.eclipse.gef4.geometry.planar.Rectangle;
public class PointListUtils {
/**
- * Compares two array of {@link Point} for equality.
+ * Compares two arrays of {@link Point} for equality.
+ *
+ * TODO: What is the benefit over using Arrays.equals()?
*
* @param p1
* the first array of points to compare
* @param p2
* the second array of points to compare
- * @return <code>true</code> in case both arrays are of the same lenght and
+ * @return <code>true</code> in case both arrays are of the same length and
* for each index <code>i</code> it holds that <code>p1[i]</code>
* equals <code>p2[i]</code>, <code>false</code> otherwise
*/
@@ -55,6 +59,29 @@ public class PointListUtils {
}
/**
+ * Compares two arrays of {@link Point} for reverse equality, i.e. if one
+ * array is the reverse of the other array.
+ *
+ * @param p1
+ * the first array of {@link Point} to compare
+ * @param p2
+ * the second array of {@link Point} to compare
+ * @return <code>true</code> in case one array is the reverse of the other
+ * array, <code>false</code> otherwise
+ */
+ public static boolean equalsReverse(Point[] p1, Point[] p2) {
+ if (p1.length != p2.length) {
+ return false;
+ }
+ for (int i = 0; i < p1.length; i++) {
+ if (!p1[i].equals(p2[p1.length - i - 1])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
* Returns the smallest {@link Rectangle} that encloses all {@link Point}s
* in the given sequence. Note that the right and bottom borders of a
* {@link Rectangle} are regarded as being part of the {@link Rectangle}.
@@ -323,4 +350,99 @@ public class PointListUtils {
return points;
}
+ /**
+ * Computes the centroid of the given {@link Point}s. The centroid is the
+ * "center of gravity", i.e. assuming the {@link Polygon} spanned by the
+ * {@link Point}s is made of a material of constant density, it will be in a
+ * balanced state, if you put it on a pin that is placed exactly on its
+ * centroid.
+ *
+ * @param points
+ * @return the center {@link Point} (or centroid) of the given {@link Point}
+ * s
+ */
+ public static Point computeCentroid(Point... points) {
+ if (points.length == 0) {
+ return null;
+ } else if (points.length == 1) {
+ return points[0].getCopy();
+ }
+
+ double cx = 0, cy = 0, a, sa = 0;
+ for (int i = 0; i < points.length - 1; i++) {
+ a = points[i].x * points[i + 1].y - points[i].y * points[i + 1].x;
+ sa += a;
+ cx += (points[i].x + points[i + 1].x) * a;
+ cy += (points[i].y + points[i + 1].y) * a;
+ }
+
+ // closing segment
+ a = points[points.length - 2].x * points[points.length - 1].y
+ - points[points.length - 2].y * points[points.length - 1].x;
+ sa += a;
+ cx += (points[points.length - 2].x + points[points.length - 1].x) * a;
+ cy += (points[points.length - 2].x + points[points.length - 1].x) * a;
+
+ return new Point(cx / (3 * sa), cy / (3 * sa));
+ }
+
+ /**
+ * Rotates (in-place) the given {@link Point}s counter-clock-wise (CCW) by
+ * the specified {@link Angle} around the given center {@link Point}.
+ *
+ * @param points
+ * @param angle
+ * @param cx
+ * @param cy
+ */
+ public static void rotateCCW(Point[] points, Angle angle, double cx,
+ double cy) {
+ translate(points, -cx, -cy);
+ for (Point p : points) {
+ Point np = new Vector(p).rotateCCW(angle).toPoint();
+ p.x = np.x;
+ p.y = np.y;
+ }
+ translate(points, cx, cy);
+ }
+
+ /**
+ * Rotates (in-place) the given {@link Point}s clock-wise (CW) by the
+ * specified {@link Angle} around the given center {@link Point}.
+ *
+ * @param points
+ * @param angle
+ * @param cx
+ * @param cy
+ */
+ public static void rotateCW(Point[] points, Angle angle, double cx,
+ double cy) {
+ translate(points, -cx, -cy);
+ for (Point p : points) {
+ Point np = new Vector(p).rotateCW(angle).toPoint();
+ p.x = np.x;
+ p.y = np.y;
+ }
+ translate(points, cx, cy);
+ }
+
+ /**
+ * Scales the given array of {@link Point}s by the given x and y scale
+ * factors around the given center {@link Point} (cx, cy).
+ *
+ * @param points
+ * @param fx
+ * @param fy
+ * @param cx
+ * @param cy
+ */
+ public static void scale(Point[] points, double fx, double fy, double cx,
+ double cy) {
+ translate(points, -cx, -cy);
+ for (Point p : points) {
+ p.scale(fx, fy);
+ }
+ translate(points, cx, cy);
+ }
+
}
diff --git a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/utils/PolynomCalculationUtils.java b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/utils/PolynomCalculationUtils.java
index 452fded..a936b33 100644
--- a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/utils/PolynomCalculationUtils.java
+++ b/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/utils/PolynomCalculationUtils.java
@@ -80,8 +80,8 @@ public final class PolynomCalculationUtils {
public static final double[] getCubicRoots(double A, double B, double C,
double D) {
// TODO: use an algorithm that abstracts the polynom's order. A
- // possibility would be to use the CurveUtils$BezierCurve#contains(Point
- // p) method.
+ // possibility would be to use the BezierCurve#contains(Point p)
+ // method.
if (A == 0) {
return getQuadraticRoots(B, C, D);