View | Details | Raw Unified | Return to bug 355997 | Differences between
and this patch

Collapse All | Expand All

(-)a/org.eclipse.gef4.geometry.doc/reference/tutorial/tutorial.html (+19 lines)
Added Link Here
1
<?xml version='1.0' encoding='utf-8' ?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/></head><body><h2 id="Description">Description</h2><p>This is a small tutorial for the GEF4 Geometry API.</p><h2 id="REPL">REPL</h2><p>The GEF 4 Geometry API ships with an examples packages (org.eclipse.gef4.geometry.examples) which includes a graphical REPL (Read-Evaluate-Print-Loop). This tutorial explains the API with small code snippets which can be executed directly in the REPL.</p><p><a href="/wiki/File:where-is-the-repl.png" title="File:where-is-the-repl.png">Where is the REPL?</a>
2
<a href="/wiki/File:repl-start" title="File:repl-start">REPL on start</a>
3
<a href="/wiki/File:repl-example" title="File:repl-example">Ellipse visualization</a></p><p>You can drag the blue control points to change the underlying figures.</p><h2 id="1._Class_hierarchy">1. Class hierarchy</h2><p>The GEF 4 Geometry API is based on a skeleton of interfaces and implementation categories which classify the individual figures into groups according to their dimensionality and multiplicity (interface hierarchy) and their construction method (inheritance hierarchy) as follows:</p><h2 id="2._General_behaviour">2. General behaviour</h2><p>You can find all the figures in the <a href="/wiki/GEF/GEF4/Geometry#Planar" title="GEF/GEF4/Geometry#Planar">org.eclipse.gef4.geometry.planar</a> package. In the majority of cases, you will get along with those predeclared figures. If you feel the need of another one that is not provided there, you can draw the joker (<a href="/wiki/GEF/GEF4/Geometry#Path" title="GEF/GEF4/Geometry#Path">Path</a>) which can be used to work with complicated composed shapes and curves. But we will begin by demonstrating the other classes first.</p><h3 id="Object_construction">Object construction</h3><p>The constructor methods of the curves and shapes are overloaded to provide either raw data to create the object from:</p><pre>   Point p1 = new Point(100, 100);
4
   Point p2 = new Point(200, 200);
5
   Line line = new Line(100, 100, 200, 200);
6
</pre><p>Or to create it from existing curves/shapes:</p><pre>   Line line = new Line(p1, p2);
7
</pre><p>Each shape can bake a copy of its own via its <code>getCopy()</code> method.</p><pre>   Line line2 = line.getCopy();
8
</pre><h3 id="Transformations">Transformations</h3><p>If you want to transform a shape, there are several ways to do this. You may use...</p><ul><li>...the general <a href="/wiki/GEF/GEF4/Geometry#AffineTransform" title="GEF/GEF4/Geometry#AffineTransform">AffineTransform</a> class using the <code>shape.getTransformed(transformation)</code> method</li><li>...short-cut methods for the individual transformations</li></ul><p>You can easily rotate a <a href="/wiki/GEF/GEF4/Geometry#Rectangle" title="GEF/GEF4/Geometry#Rectangle">Rectangle</a> by calling its <code>getRotatedCCW(angle)</code> method:</p><pre>   Rectangle quad = new Rectangle(100, 100, 100, 100);
9
   Polygon rhomb = quad.getRotatedCCW(Angle.fromDeg(45));
10
</pre><p>As you can see, the resulting shape is not a <a href="/wiki/GEF/GEF4/Geometry#Rectangle" title="GEF/GEF4/Geometry#Rectangle">Rectangle</a> anymore. This is due to the fact, that <a href="/wiki/GEF/GEF4/Geometry#Rectangle" title="GEF/GEF4/Geometry#Rectangle">Rectangles</a> are always parallel to the x- and y-axis.</p><p>If you want to rotate a Rectangle by an integer multiple of 90° and get a <a href="/wiki/GEF/GEF4/Geometry#Rectangle" title="GEF/GEF4/Geometry#Rectangle">Rectangle</a> back, you have to use the <code>polygon.getBounds()</code> method on the <a href="/wiki/GEF/GEF4/Geometry#Polygon" title="GEF/GEF4/Geometry#Polygon">Polygon</a> that the <code>getRotatedCCW(angle)</code> method returns:</p><pre>   Rectangle rect = new Rectangle(100, 100, 200, 50);
11
   Rectangle rotated = rect.getRotatedCCW(Angle.fromDeg(90)).getBounds();
12
</pre><p>You may wonder why the rotation method is called <code>getRotatedCCW()</code> and not just <code>getRotated()</code>. This is to assure, that the direction of rotation is correct. The postfix indicates whether you want to rotate counter-clock-wise (CCW) or clock-wise (CW). Another small assistance is the <a href="/wiki/GEF/GEF4/Geometry#Angle" title="GEF/GEF4/Geometry#Angle">Angle</a> class. It prevents you from mixing up degrees and radients in rotation calculations.</p><p>Translation and scaling are directly available on each form, too:</p><pre>   Rectangle rect = new Rectangle(100, 100, 200, 50);
13
   Rectangle big = rect.getScaled(2);
14
   Rectangle small = rect.getScaled(0.5);
15
   Rectangle translated = rect.getTranslated(-50, 50);
16
   //...
17
</pre><p>Rotation and scaling do always use a relative point to rotate around and scale away from, respectively. If you omit it, the transformation will use the mid-point of the <a href="/wiki/GEF/GEF4/Geometry#IGeometry" title="GEF/GEF4/Geometry#IGeometry">IGeometry</a> as the relative point for the transformation. This behaviour is considered to be least surprising, although some people might expect the scaling and the rotation to be relative to (0, 0) which is true for Points and Vectors.</p><h3 id="Interaction_of_geometric_objects">Interaction of geometric objects</h3><p>Curves and shapes can interact with each other. You can test them for intersection or overlap, and you can compute the points of intersection of two curves/shapes and the overlapping section of two curves/shapes.</p><p>To do so, the <a href="/wiki/GEF/GEF4/Geometry#ICurve" title="GEF/GEF4/Geometry#ICurve">ICurve</a> and <a href="/wiki/GEF/GEF4/Geometry#IShape" title="GEF/GEF4/Geometry#IShape">IShape</a> interfaces provide various methods, namely touches(), contains(), overlaps(), getIntersections() and getOverlap() which return information on the relationship of two shapes.</p><p>For example, you may wish to compute the points of intersection between an ellipse an a line:</p><pre>   Point[] intersections = ellipse.getIntersections(line);
18
</pre><p>Or you are dealing with two overlapping curves and you want to get the overlap:</p><pre>   CubicCurve overlap = c1.getOverlap(c2);
19
</pre><h3 id="Regions">Regions</h3><p>TODO... delegates to the AWT functionality.</p></body></html>
(-)a/org.eclipse.gef4.geometry.doc/reference/tutorial/tutorial.mediawiki (+89 lines)
Added Link Here
1
== Description ==
2
3
This is a small tutorial for the GEF4 Geometry API.
4
5
== REPL ==
6
7
The GEF 4 Geometry API ships with an examples packages (org.eclipse.gef4.geometry.examples) which includes a graphical REPL (Read-Evaluate-Print-Loop). This tutorial explains the API with small code snippets which can be executed directly in the REPL.
8
9
[[File:where-is-the-repl.png|Where is the REPL?]]
10
[[File:repl-start|REPL on start]]
11
[[File:repl-example|Ellipse visualization]]
12
13
You can drag the blue control points to change the underlying figures.
14
15
== 1. Class hierarchy ==
16
17
The GEF 4 Geometry API is based on a skeleton of interfaces and implementation categories which classify the individual figures into groups according to their dimensionality and multiplicity (interface hierarchy) and their construction method (inheritance hierarchy) as follows:
18
19
20
21
22
== 2. General behaviour ==
23
24
You can find all the figures in the [[GEF/GEF4/Geometry#Planar | org.eclipse.gef4.geometry.planar]] package. In the majority of cases, you will get along with those predeclared figures. If you feel the need of another one that is not provided there, you can draw the joker ([[GEF/GEF4/Geometry#Path |Path]]) which can be used to work with complicated composed shapes and curves. But we will begin by demonstrating the other classes first.
25
26
=== Object construction ===
27
28
The constructor methods of the curves and shapes are overloaded to provide either raw data to create the object from:
29
30
    Point p1 = new Point(100, 100);
31
    Point p2 = new Point(200, 200);
32
    Line line = new Line(100, 100, 200, 200);
33
34
Or to create it from existing curves/shapes:
35
36
    Line line = new Line(p1, p2);
37
38
Each shape can bake a copy of its own via its <code>getCopy()</code> method.
39
40
    Line line2 = line.getCopy();
41
42
=== Transformations ===
43
44
If you want to transform a shape, there are several ways to do this. You may use...
45
46
* ...the general [[GEF/GEF4/Geometry#AffineTransform |AffineTransform]] class using the <code>shape.getTransformed(transformation)</code> method
47
* ...short-cut methods for the individual transformations
48
49
You can easily rotate a [[GEF/GEF4/Geometry#Rectangle |Rectangle]] by calling its <code>getRotatedCCW(angle)</code> method:
50
51
    Rectangle quad = new Rectangle(100, 100, 100, 100);
52
    Polygon rhomb = quad.getRotatedCCW(Angle.fromDeg(45));
53
54
As you can see, the resulting shape is not a [[GEF/GEF4/Geometry#Rectangle |Rectangle]] anymore. This is due to the fact, that [[GEF/GEF4/Geometry#Rectangle |Rectangles]] are always parallel to the x- and y-axis.
55
56
If you want to rotate a Rectangle by an integer multiple of 90° and get a [[GEF/GEF4/Geometry#Rectangle |Rectangle]] back, you have to use the <code>polygon.getBounds()</code> method on the [[GEF/GEF4/Geometry#Polygon |Polygon]] that the <code>getRotatedCCW(angle)</code> method returns:
57
58
    Rectangle rect = new Rectangle(100, 100, 200, 50);
59
    Rectangle rotated = rect.getRotatedCCW(Angle.fromDeg(90)).getBounds();
60
61
You may wonder why the rotation method is called <code>getRotatedCCW()</code> and not just <code>getRotated()</code>. This is to assure, that the direction of rotation is correct. The postfix indicates whether you want to rotate counter-clock-wise (CCW) or clock-wise (CW). Another small assistance is the [[GEF/GEF4/Geometry#Angle |Angle]] class. It prevents you from mixing up degrees and radians in rotation calculations.
62
63
Translation and scaling are directly available on each form, too:
64
65
    Rectangle rect = new Rectangle(100, 100, 200, 50);
66
    Rectangle big = rect.getScaled(2);
67
    Rectangle small = rect.getScaled(0.5);
68
    Rectangle translated = rect.getTranslated(-50, 50);
69
    //...
70
71
Rotation and scaling do always use a relative point to rotate around and scale away from, respectively. If you omit it, the transformation will use the mid-point of the [[GEF/GEF4/Geometry#IGeometry| IGeometry]] as the relative point for the transformation. This behavior is considered to be least surprising, although some people might expect the scaling and the rotation to be relative to (0, 0) which is the default behavior for Points and Vectors.
72
73
=== Interaction of geometric objects ===
74
75
Curves and shapes can interact with each other. You can test them for intersection or overlap, and you can compute the points of intersection of two curves/shapes and the overlapping section of two curves/shapes.
76
77
To do so, the [[GEF/GEF4/Geometry#ICurve| ICurve]] and [[GEF/GEF4/Geometry#IShape| IShape]] interfaces provide various methods, namely touches(), contains(), overlaps(), getIntersections() and getOverlap() which return information on the relationship of two shapes.
78
79
For example, you may wish to compute the points of intersection between and ellipse an a line:
80
81
    Point[] intersections = ellipse.getIntersections(line);
82
83
Or you are dealing with two overlapping curves and you want to get the overlap:
84
85
    CubicCurve overlap = c1.getOverlap(c2);
86
87
=== Regions ===
88
89
TODO... delegates to the AWT functionality.
(-)a/org.eclipse.gef4.geometry.examples/.classpath (+7 lines)
Added Link Here
1
<?xml version="1.0" encoding="UTF-8"?>
2
<classpath>
3
	<classpathentry kind="src" path="src"/>
4
	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
5
	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
6
	<classpathentry kind="output" path="bin"/>
7
</classpath>
(-)a/org.eclipse.gef4.geometry.examples/.gitignore (+1 lines)
Added Link Here
1
/bin
(-)a/org.eclipse.gef4.geometry.examples/.project (+29 lines)
Added Link Here
1
<?xml version="1.0" encoding="UTF-8"?>
2
<projectDescription>
3
	<name>org.eclipse.gef4.geometry.examples</name>
4
	<comment></comment>
5
	<projects>
6
		<project>org.eclipse.gef4.geometry</project>
7
	</projects>
8
	<buildSpec>
9
		<buildCommand>
10
			<name>org.eclipse.jdt.core.javabuilder</name>
11
			<arguments>
12
			</arguments>
13
		</buildCommand>
14
		<buildCommand>
15
			<name>org.eclipse.pde.ManifestBuilder</name>
16
			<arguments>
17
			</arguments>
18
		</buildCommand>
19
		<buildCommand>
20
			<name>org.eclipse.pde.SchemaBuilder</name>
21
			<arguments>
22
			</arguments>
23
		</buildCommand>
24
	</buildSpec>
25
	<natures>
26
		<nature>org.eclipse.jdt.core.javanature</nature>
27
		<nature>org.eclipse.pde.PluginNature</nature>
28
	</natures>
29
</projectDescription>
(-)a/org.eclipse.gef4.geometry.examples/.settings/org.eclipse.jdt.core.prefs (+12 lines)
Added Link Here
1
#Tue Aug 30 13:06:30 CEST 2011
2
eclipse.preferences.version=1
3
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
4
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
5
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
6
org.eclipse.jdt.core.compiler.compliance=1.6
7
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
8
org.eclipse.jdt.core.compiler.debug.localVariable=generate
9
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
10
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
11
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
12
org.eclipse.jdt.core.compiler.source=1.6
(-)a/org.eclipse.gef4.geometry.examples/.settings/org.eclipse.jdt.ui.prefs (+55 lines)
Added Link Here
1
#Tue Aug 30 15:29:00 CEST 2011
2
eclipse.preferences.version=1
3
editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
4
sp_cleanup.add_default_serial_version_id=true
5
sp_cleanup.add_generated_serial_version_id=false
6
sp_cleanup.add_missing_annotations=true
7
sp_cleanup.add_missing_deprecated_annotations=true
8
sp_cleanup.add_missing_methods=false
9
sp_cleanup.add_missing_nls_tags=false
10
sp_cleanup.add_missing_override_annotations=true
11
sp_cleanup.add_missing_override_annotations_interface_methods=true
12
sp_cleanup.add_serial_version_id=false
13
sp_cleanup.always_use_blocks=true
14
sp_cleanup.always_use_parentheses_in_expressions=false
15
sp_cleanup.always_use_this_for_non_static_field_access=false
16
sp_cleanup.always_use_this_for_non_static_method_access=false
17
sp_cleanup.convert_to_enhanced_for_loop=false
18
sp_cleanup.correct_indentation=false
19
sp_cleanup.format_source_code=true
20
sp_cleanup.format_source_code_changes_only=false
21
sp_cleanup.make_local_variable_final=false
22
sp_cleanup.make_parameters_final=false
23
sp_cleanup.make_private_fields_final=true
24
sp_cleanup.make_type_abstract_if_missing_method=false
25
sp_cleanup.make_variable_declarations_final=true
26
sp_cleanup.never_use_blocks=false
27
sp_cleanup.never_use_parentheses_in_expressions=true
28
sp_cleanup.on_save_use_additional_actions=false
29
sp_cleanup.organize_imports=true
30
sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
31
sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
32
sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
33
sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
34
sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
35
sp_cleanup.remove_private_constructors=true
36
sp_cleanup.remove_trailing_whitespaces=false
37
sp_cleanup.remove_trailing_whitespaces_all=true
38
sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
39
sp_cleanup.remove_unnecessary_casts=true
40
sp_cleanup.remove_unnecessary_nls_tags=false
41
sp_cleanup.remove_unused_imports=false
42
sp_cleanup.remove_unused_local_variables=false
43
sp_cleanup.remove_unused_private_fields=true
44
sp_cleanup.remove_unused_private_members=false
45
sp_cleanup.remove_unused_private_methods=true
46
sp_cleanup.remove_unused_private_types=true
47
sp_cleanup.sort_members=false
48
sp_cleanup.sort_members_all=false
49
sp_cleanup.use_blocks=false
50
sp_cleanup.use_blocks_only_for_return_and_throw=false
51
sp_cleanup.use_parentheses_in_expressions=false
52
sp_cleanup.use_this_for_non_static_field_access=false
53
sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
54
sp_cleanup.use_this_for_non_static_method_access=false
55
sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
(-)a/org.eclipse.gef4.geometry.examples/.settings/org.eclipse.ltk.core.refactoring.prefs (+3 lines)
Added Link Here
1
#Thu Nov 03 10:57:56 CET 2011
2
eclipse.preferences.version=1
3
org.eclipse.ltk.core.refactoring.enable.project.refactoring.history=false
(-)a/org.eclipse.gef4.geometry.examples/META-INF/MANIFEST.MF (+8 lines)
Added Link Here
1
Manifest-Version: 1.0
2
Bundle-ManifestVersion: 2
3
Bundle-Name: Examples
4
Bundle-SymbolicName: org.eclipse.gef4.geometry.examples
5
Bundle-Version: 1.0.0.qualifier
6
Require-Bundle: org.eclipse.swt;bundle-version="3.7.0",
7
 org.eclipse.gef4.geometry;bundle-version="0.1.0",
8
 org.eclipse.gef4.geometry
(-)a/org.eclipse.gef4.geometry.examples/build.properties (+4 lines)
Added Link Here
1
source.. = src/
2
bin.includes = META-INF/,\
3
               .,\
4
               src/
(-)a/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/containment/AbstractContainmentExample.java (+367 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2011 itemis AG and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthias Wienand (itemis AG) - initial API and implementation
10
 *     
11
 *******************************************************************************/
12
package org.eclipse.gef4.geometry.examples.containment;
13
14
import java.util.ArrayList;
15
16
import org.eclipse.gef4.geometry.Point;
17
import org.eclipse.gef4.geometry.planar.Ellipse;
18
import org.eclipse.gef4.geometry.planar.IGeometry;
19
import org.eclipse.gef4.geometry.planar.Rectangle;
20
import org.eclipse.swt.SWT;
21
import org.eclipse.swt.events.MouseEvent;
22
import org.eclipse.swt.events.MouseListener;
23
import org.eclipse.swt.events.MouseMoveListener;
24
import org.eclipse.swt.events.PaintEvent;
25
import org.eclipse.swt.events.PaintListener;
26
import org.eclipse.swt.graphics.GC;
27
import org.eclipse.swt.widgets.Canvas;
28
import org.eclipse.swt.widgets.Display;
29
import org.eclipse.swt.widgets.Event;
30
import org.eclipse.swt.widgets.Listener;
31
import org.eclipse.swt.widgets.Shell;
32
33
public abstract class AbstractContainmentExample implements PaintListener {
34
35
	abstract public class AbstractControllableShape {
36
37
		private static final int CONTROL_POINT_COLOR = SWT.COLOR_BLUE;
38
		private static final int CONTROL_POINT_RADIUS = 5;
39
40
		private Canvas canvas;
41
		private ArrayList<ControlPoint> points;
42
43
		public AbstractControllableShape(Canvas canvas) {
44
			this.canvas = canvas;
45
			points = new ArrayList<ControlPoint>();
46
			createControlPoints();
47
		}
48
49
		public ControlPoint addControlPoint(Point p) {
50
			return addControlPoint(p, CONTROL_POINT_COLOR);
51
		}
52
53
		public ControlPoint addControlPoint(Point p, int color) {
54
			return addControlPoint(p, color, CONTROL_POINT_RADIUS);
55
		}
56
57
		public ControlPoint addControlPoint(Point p, int color, double radius) {
58
			ControlPoint cp = new ControlPoint(canvas, p, radius, color);
59
			for (ControlPoint ocp : points) {
60
				ocp.addForbiddenArea(cp);
61
				cp.addForbiddenArea(ocp);
62
			}
63
			points.add(cp);
64
			return cp;
65
		}
66
67
		abstract public void createControlPoints();
68
69
		abstract public IGeometry createGeometry();
70
71
		public void drawControlPoints(GC gc) {
72
			for (ControlPoint cp : points) {
73
				cp.draw(gc);
74
			}
75
		}
76
77
		abstract public void drawShape(GC gc);
78
79
		public void fillShape(GC gc) {
80
			drawShape(gc);
81
		}
82
83
		public Canvas getCanvas() {
84
			return canvas;
85
		}
86
87
		public Point[] getControlPoints() {
88
			Point[] points = new Point[this.points.size()];
89
90
			int i = 0;
91
			for (ControlPoint cp : this.points) {
92
				points[i++] = cp.getPoint();
93
			}
94
95
			return points;
96
		}
97
	}
98
99
	/**
100
	 * A draggable point. On the screen it is represented as an ellipse.
101
	 * 
102
	 * @author wienand
103
	 * 
104
	 */
105
	class ControlPoint implements MouseListener, MouseMoveListener, Listener {
106
		private Canvas canvas;
107
		private int color = SWT.COLOR_BLUE;
108
109
		private Ellipse ellipse;
110
		private boolean isDragged = false;
111
112
		// to rescale points on resize
113
		private double oldShellHeight;
114
		private double oldShellWidth;
115
116
		private ArrayList<ControlPoint> forbidden;
117
		private ArrayList<ControlPoint> updateLinks;
118
		private ControlPoint xLink, yLink;
119
		private double relX, relY;
120
121
		private Point p;
122
		private double radius = 5;
123
124
		/**
125
		 * Creates a new ControlPoint object. Adds event listeners to the given
126
		 * Canvas object, so that the user can drag the control point with the
127
		 * mouse.
128
		 * 
129
		 * @param canvas
130
		 *            Drawing area
131
		 */
132
		public ControlPoint(Canvas canvas) {
133
			this.canvas = canvas;
134
			canvas.addMouseListener(this);
135
			canvas.addMouseMoveListener(this);
136
			canvas.addListener(SWT.Resize, this);
137
			oldShellWidth = canvas.getClientArea().width;
138
			oldShellHeight = canvas.getClientArea().height;
139
			p = new Point(0, 0);
140
			updateLinks = new ArrayList<ControlPoint>();
141
			forbidden = new ArrayList<ControlPoint>();
142
			update();
143
		}
144
145
		/**
146
		 * Creates a new ControlPoint object. Adds event listeners to the given
147
		 * Canvas object, so that the user can drag the control point with the
148
		 * mouse.
149
		 * 
150
		 * @param canvas
151
		 *            Drawing area
152
		 * @param p
153
		 *            Exact point
154
		 * @param radius
155
		 *            Of the ellipse that represents the point
156
		 * @param color
157
		 *            Of the ellipse that represents the point
158
		 */
159
		public ControlPoint(Canvas canvas, Point p, double radius, int color) {
160
			this(canvas);
161
			this.p = p;
162
			this.radius = radius;
163
			this.color = color;
164
			update();
165
		}
166
167
		public void addForbiddenArea(ControlPoint cp) {
168
			forbidden.add(cp);
169
		}
170
171
		/**
172
		 * Draws an ellipse with the given GC at the control points location.
173
		 * 
174
		 * @param gc
175
		 */
176
		public void draw(GC gc) {
177
			// System.out.println(ellipse.toString());
178
			gc.setBackground(Display.getCurrent().getSystemColor(color));
179
			gc.fillOval((int) ellipse.getX(), (int) ellipse.getY(),
180
					(int) ellipse.getWidth(), (int) ellipse.getHeight());
181
		}
182
183
		/**
184
		 * Returns the exact Point of this ControlPoint object.
185
		 * 
186
		 * @return The exact Point of this ControlPoint object.
187
		 */
188
		public Point getPoint() {
189
			return p;
190
		}
191
192
		@Override
193
		public void handleEvent(Event e) {
194
			switch (e.type) {
195
			case SWT.Resize:
196
				Rectangle bounds = new Rectangle(canvas.getBounds());
197
				p.scale(bounds.getWidth() / oldShellWidth, bounds.getHeight()
198
						/ oldShellHeight);
199
				oldShellWidth = bounds.getWidth();
200
				oldShellHeight = bounds.getHeight();
201
				update();
202
				break;
203
			}
204
		}
205
206
		private double inRange(double low, double value, double high) {
207
			if (value < low) {
208
				return low;
209
			} else if (value > high) {
210
				return high;
211
			}
212
			return value;
213
		}
214
215
		@Override
216
		public void mouseDoubleClick(MouseEvent e) {
217
		}
218
219
		@Override
220
		public void mouseDown(MouseEvent e) {
221
			if (ellipse.contains(new Point(e.x, e.y))) {
222
				isDragged = true;
223
			}
224
		}
225
226
		@Override
227
		public void mouseMove(MouseEvent e) {
228
			if (isDragged) {
229
				relX = e.x - p.x;
230
				relY = e.y - p.y;
231
				p.x = e.x;
232
				p.y = e.y;
233
				update();
234
				canvas.redraw();
235
			}
236
		}
237
238
		@Override
239
		public void mouseUp(MouseEvent e) {
240
			isDragged = false;
241
		}
242
243
		public void setXLink(ControlPoint cp) {
244
			xLink = cp;
245
			cp.updateLinks.add(this);
246
		}
247
248
		public void setYLink(ControlPoint cp) {
249
			yLink = cp;
250
			cp.updateLinks.add(this);
251
		}
252
253
		private void update() {
254
			double oldX = p.x, oldY = p.y;
255
256
			// check canvas pane:
257
			p.x = inRange(canvas.getClientArea().x + radius, p.x,
258
					canvas.getClientArea().x + canvas.getClientArea().width
259
							- radius);
260
			p.y = inRange(canvas.getClientArea().y + radius, p.y,
261
					canvas.getClientArea().y + canvas.getClientArea().height
262
							- radius);
263
264
			// check links:
265
			if (xLink != null) {
266
				p.x = xLink.p.x;
267
				p.y += xLink.relY;
268
			} else if (yLink != null) { // no need to link both x and y
269
				p.x += yLink.relX;
270
				p.y = yLink.p.y;
271
			}
272
273
			// check forbidden areas:
274
			for (ControlPoint cp : forbidden) {
275
				double minDistance = radius + cp.radius;
276
				if (p.getDistance(cp.p) < minDistance) {
277
					if (relX > 0) {
278
						p.x = cp.p.x - minDistance;
279
					} else if (relX < 0) {
280
						p.x = cp.p.x + minDistance;
281
					} else if (relY > 0) {
282
						p.y = cp.p.y - minDistance;
283
					} else {
284
						p.y = cp.p.y + minDistance;
285
					}
286
				}
287
			}
288
289
			relX += p.x - oldX;
290
			relY += p.y - oldY;
291
292
			for (ControlPoint cp : updateLinks) {
293
				cp.update();
294
			}
295
296
			ellipse = new Ellipse(p.x - radius, p.y - radius, radius * 2,
297
					radius * 2);
298
		}
299
	}
300
301
	private static final int INTERSECTS_COLOR = SWT.COLOR_YELLOW;
302
303
	private static final int CONTAINS_COLOR = SWT.COLOR_GREEN;
304
305
	private AbstractControllableShape controllableShape1, controllableShape2;
306
307
	private Shell shell;
308
309
	/**
310
	 * 
311
	 */
312
	public AbstractContainmentExample(String title) {
313
		Display display = new Display();
314
		shell = new Shell(display, SWT.SHELL_TRIM | SWT.DOUBLE_BUFFERED);
315
		shell.setText(title);
316
317
		shell.setBounds(0, 0, 640, 480);
318
319
		controllableShape1 = createControllableShape1(shell);
320
		controllableShape2 = createControllableShape2(shell);
321
322
		shell.addPaintListener(this);
323
		shell.open();
324
325
		while (!shell.isDisposed()) {
326
			if (!display.readAndDispatch()) {
327
				display.sleep();
328
			}
329
		}
330
	}
331
332
	protected abstract boolean computeIntersects(IGeometry g1, IGeometry g2);
333
334
	protected abstract boolean computeContains(IGeometry g1, IGeometry g2);
335
336
	protected abstract AbstractControllableShape createControllableShape1(
337
			Canvas canvas);
338
339
	protected abstract AbstractControllableShape createControllableShape2(
340
			Canvas canvas);
341
342
	@Override
343
	public void paintControl(PaintEvent e) {
344
		e.gc.setAntialias(SWT.ON);
345
346
		IGeometry cs1geometry = controllableShape1.createGeometry();
347
		IGeometry cs2geometry = controllableShape2.createGeometry();
348
349
		if (computeIntersects(cs1geometry, cs2geometry)) {
350
			e.gc.setBackground(Display.getCurrent().getSystemColor(
351
					INTERSECTS_COLOR));
352
			controllableShape2.fillShape(e.gc);
353
		}
354
355
		if (computeContains(cs1geometry, cs2geometry)) {
356
			e.gc.setBackground(Display.getCurrent().getSystemColor(
357
					CONTAINS_COLOR));
358
			controllableShape2.fillShape(e.gc);
359
		}
360
361
		controllableShape1.drawShape(e.gc);
362
		controllableShape2.drawShape(e.gc);
363
364
		controllableShape1.drawControlPoints(e.gc);
365
		controllableShape2.drawControlPoints(e.gc);
366
	}
367
}
(-)a/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/containment/AbstractEllipseContainmentExample.java (+48 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2011 itemis AG and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthias Wienand (itemis AG) - initial API and implementation
10
 *     
11
 *******************************************************************************/
12
package org.eclipse.gef4.geometry.examples.containment;
13
14
import org.eclipse.gef4.geometry.planar.Ellipse;
15
import org.eclipse.swt.graphics.GC;
16
import org.eclipse.swt.widgets.Canvas;
17
18
public abstract class AbstractEllipseContainmentExample extends
19
		AbstractContainmentExample {
20
21
	public AbstractEllipseContainmentExample(String title) {
22
		super(title);
23
	}
24
25
	protected AbstractControllableShape createControllableShape1(Canvas canvas) {
26
		return new AbstractControllableShape(canvas) {
27
			@Override
28
			public void createControlPoints() {
29
				// the ellipse does not have any control points
30
			}
31
32
			@Override
33
			public Ellipse createGeometry() {
34
				double w5 = getCanvas().getClientArea().width / 5;
35
				double h5 = getCanvas().getClientArea().height / 5;
36
				return new Ellipse(w5, h5, 3 * w5, 3 * h5);
37
			}
38
39
			@Override
40
			public void drawShape(GC gc) {
41
				Ellipse ellipse = (Ellipse) createGeometry();
42
				gc.drawOval((int) ellipse.getX(), (int) ellipse.getY(),
43
						(int) ellipse.getWidth(), (int) ellipse.getHeight());
44
			}
45
		};
46
	}
47
48
}
(-)a/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/containment/AbstractPolygonContainmentExample.java (+60 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2011 itemis AG and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthias Wienand (itemis AG) - initial API and implementation
10
 *     
11
 *******************************************************************************/
12
package org.eclipse.gef4.geometry.examples.containment;
13
14
import org.eclipse.gef4.geometry.Point;
15
import org.eclipse.gef4.geometry.planar.Line;
16
import org.eclipse.gef4.geometry.planar.Polygon;
17
import org.eclipse.swt.graphics.GC;
18
import org.eclipse.swt.graphics.Rectangle;
19
import org.eclipse.swt.widgets.Canvas;
20
21
public abstract class AbstractPolygonContainmentExample extends
22
		AbstractContainmentExample {
23
24
	public AbstractPolygonContainmentExample(String title) {
25
		super(title);
26
	}
27
28
	@Override
29
	protected AbstractControllableShape createControllableShape1(Canvas canvas) {
30
		return new AbstractControllableShape(canvas) {
31
			@Override
32
			public void createControlPoints() {
33
				// no control points => user cannot change it
34
			}
35
36
			@Override
37
			public Polygon createGeometry() {
38
				Rectangle ca = getCanvas().getClientArea();
39
				double w = ca.width;
40
				double wg = w / 6;
41
				double h = ca.height;
42
				double hg = h / 6;
43
44
				return new Polygon(new Point[] { new Point(wg, hg),
45
						new Point(w - wg, h - hg), new Point(wg, h - hg),
46
						new Point(w - wg, hg) });
47
			}
48
49
			@Override
50
			public void drawShape(GC gc) {
51
				Polygon polygon = createGeometry();
52
				for (Line segment : polygon.getOutlineSegments()) {
53
					gc.drawLine((int) segment.getX1(), (int) segment.getY1(),
54
							(int) segment.getX2(), (int) segment.getY2());
55
				}
56
			}
57
		};
58
	}
59
60
}
(-)a/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/containment/EllipseLineContainment.java (+76 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2011 itemis AG and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthias Wienand (itemis AG) - initial API and implementation
10
 *     
11
 *******************************************************************************/
12
package org.eclipse.gef4.geometry.examples.containment;
13
14
import org.eclipse.gef4.geometry.Point;
15
import org.eclipse.gef4.geometry.planar.Ellipse;
16
import org.eclipse.gef4.geometry.planar.IGeometry;
17
import org.eclipse.gef4.geometry.planar.Line;
18
import org.eclipse.swt.graphics.Color;
19
import org.eclipse.swt.graphics.GC;
20
import org.eclipse.swt.widgets.Canvas;
21
22
public class EllipseLineContainment extends AbstractEllipseContainmentExample {
23
	public static void main(String[] args) {
24
		new EllipseLineContainment();
25
	}
26
27
	public EllipseLineContainment() {
28
		super("Ellipse/Line Containment");
29
	}
30
31
	@Override
32
	protected boolean computeIntersects(IGeometry g1, IGeometry g2) {
33
		return ((Ellipse) g1).touches((Line) g2);
34
	}
35
36
	@Override
37
	protected boolean computeContains(IGeometry g1, IGeometry g2) {
38
		return ((Ellipse) g1).contains((Line) g2);
39
	}
40
41
	@Override
42
	protected AbstractControllableShape createControllableShape2(Canvas canvas) {
43
		return new AbstractControllableShape(canvas) {
44
			@Override
45
			public void createControlPoints() {
46
				addControlPoint(new Point(100, 100));
47
				addControlPoint(new Point(300, 300));
48
			}
49
50
			@Override
51
			public Line createGeometry() {
52
				Point[] points = getControlPoints();
53
				return new Line(points[0], points[1]);
54
			}
55
56
			@Override
57
			public void drawShape(GC gc) {
58
				Line line = createGeometry();
59
				gc.drawPolyline(line.toSWTPointArray());
60
			}
61
62
			@Override
63
			public void fillShape(GC gc) {
64
				int lineWidth = gc.getLineWidth();
65
				Color fg = gc.getForeground();
66
67
				gc.setLineWidth(3);
68
				gc.setForeground(gc.getBackground());
69
				drawShape(gc);
70
71
				gc.setForeground(fg);
72
				gc.setLineWidth(lineWidth);
73
			}
74
		};
75
	}
76
}
(-)a/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/containment/EllipsePolygonContainment.java (+72 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2011 itemis AG and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthias Wienand (itemis AG) - initial API and implementation
10
 *     
11
 *******************************************************************************/
12
package org.eclipse.gef4.geometry.examples.containment;
13
14
import org.eclipse.gef4.geometry.Point;
15
import org.eclipse.gef4.geometry.planar.Ellipse;
16
import org.eclipse.gef4.geometry.planar.IGeometry;
17
import org.eclipse.gef4.geometry.planar.Polygon;
18
import org.eclipse.swt.graphics.GC;
19
import org.eclipse.swt.widgets.Canvas;
20
21
public class EllipsePolygonContainment extends
22
		AbstractEllipseContainmentExample {
23
24
	public static void main(String[] args) {
25
		new EllipsePolygonContainment();
26
	}
27
28
	public EllipsePolygonContainment() {
29
		super("Ellipse/Polygon Containment");
30
	}
31
32
	@Override
33
	protected boolean computeIntersects(IGeometry g1, IGeometry g2) {
34
		return ((Ellipse) g1).touches((Polygon) g2);
35
	}
36
37
	@Override
38
	protected boolean computeContains(IGeometry g1, IGeometry g2) {
39
		return ((Ellipse) g1).contains((Polygon) g2);
40
	}
41
42
	@Override
43
	protected AbstractControllableShape createControllableShape2(Canvas canvas) {
44
		return new AbstractControllableShape(canvas) {
45
			@Override
46
			public void createControlPoints() {
47
				addControlPoint(new Point(200, 200));
48
			}
49
50
			@Override
51
			public Polygon createGeometry() {
52
				Point[] points = getControlPoints();
53
				Polygon polygon = new Polygon(points[0].x - 50,
54
						points[0].y - 50, points[0].x + 100, points[0].y - 30,
55
						points[0].x - 40, points[0].y + 60);
56
				return polygon;
57
			}
58
59
			@Override
60
			public void drawShape(GC gc) {
61
				Polygon polygon = createGeometry();
62
				gc.drawPolygon(polygon.toSWTPointArray());
63
			}
64
65
			@Override
66
			public void fillShape(GC gc) {
67
				Polygon polygon = createGeometry();
68
				gc.fillPolygon(polygon.toSWTPointArray());
69
			}
70
		};
71
	}
72
}
(-)a/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/containment/EllipsePolylineContainment.java (+81 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2011 itemis AG and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthias Wienand (itemis AG) - initial API and implementation
10
 *     
11
 *******************************************************************************/
12
package org.eclipse.gef4.geometry.examples.containment;
13
14
import org.eclipse.gef4.geometry.Point;
15
import org.eclipse.gef4.geometry.planar.Ellipse;
16
import org.eclipse.gef4.geometry.planar.IGeometry;
17
import org.eclipse.gef4.geometry.planar.Polyline;
18
import org.eclipse.swt.graphics.Color;
19
import org.eclipse.swt.graphics.GC;
20
import org.eclipse.swt.widgets.Canvas;
21
22
public class EllipsePolylineContainment extends
23
		AbstractEllipseContainmentExample {
24
25
	public static void main(String[] args) {
26
		new EllipsePolylineContainment();
27
	}
28
29
	public EllipsePolylineContainment() {
30
		super("Ellipse/Polyline Containment");
31
	}
32
33
	@Override
34
	protected boolean computeIntersects(IGeometry g1, IGeometry g2) {
35
		return ((Ellipse) g1).touches((Polyline) g2);
36
	}
37
38
	@Override
39
	protected boolean computeContains(IGeometry g1, IGeometry g2) {
40
		return ((Ellipse) g1).contains((Polyline) g2);
41
	}
42
43
	@Override
44
	protected AbstractControllableShape createControllableShape2(Canvas canvas) {
45
		return new AbstractControllableShape(canvas) {
46
			@Override
47
			public void createControlPoints() {
48
				addControlPoint(new Point(100, 100));
49
				addControlPoint(new Point(500, 400));
50
				addControlPoint(new Point(100, 400));
51
				addControlPoint(new Point(500, 100));
52
			}
53
54
			@Override
55
			public Polyline createGeometry() {
56
				Point[] points = getControlPoints();
57
				Polyline polyline = new Polyline(points);
58
				return polyline;
59
			}
60
61
			@Override
62
			public void drawShape(GC gc) {
63
				Polyline polyline = createGeometry();
64
				gc.drawPolyline(polyline.toSWTPointArray());
65
			}
66
67
			@Override
68
			public void fillShape(GC gc) {
69
				int lineWidth = gc.getLineWidth();
70
				Color fg = gc.getForeground();
71
72
				gc.setLineWidth(3);
73
				gc.setForeground(gc.getBackground());
74
				drawShape(gc);
75
76
				gc.setLineWidth(lineWidth);
77
				gc.setForeground(fg);
78
			}
79
		};
80
	}
81
}
(-)a/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/containment/EllipseRectangleContainment.java (+72 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2011 itemis AG and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthias Wienand (itemis AG) - initial API and implementation
10
 *     
11
 *******************************************************************************/
12
package org.eclipse.gef4.geometry.examples.containment;
13
14
import org.eclipse.gef4.geometry.Point;
15
import org.eclipse.gef4.geometry.planar.Ellipse;
16
import org.eclipse.gef4.geometry.planar.IGeometry;
17
import org.eclipse.gef4.geometry.planar.Rectangle;
18
import org.eclipse.swt.graphics.GC;
19
import org.eclipse.swt.widgets.Canvas;
20
21
public class EllipseRectangleContainment extends
22
		AbstractEllipseContainmentExample {
23
	public static void main(String[] args) {
24
		new EllipseRectangleContainment();
25
	}
26
27
	public EllipseRectangleContainment() {
28
		super("Ellipse/Rectangle containment");
29
	}
30
31
	@Override
32
	protected boolean computeIntersects(IGeometry g1, IGeometry g2) {
33
		return ((Ellipse) g1).touches((Rectangle) g2);
34
	}
35
36
	@Override
37
	protected boolean computeContains(IGeometry g1, IGeometry g2) {
38
		return ((Ellipse) g1).contains((Rectangle) g2);
39
	}
40
41
	@Override
42
	protected AbstractControllableShape createControllableShape2(Canvas canvas) {
43
		return new AbstractControllableShape(canvas) {
44
			private final double WIDTH = 50;
45
			private final double HEIGHT = 75;
46
47
			@Override
48
			public void createControlPoints() {
49
				addControlPoint(new Point(110, 70));
50
			}
51
52
			@Override
53
			public Rectangle createGeometry() {
54
				Point[] points = getControlPoints();
55
				return new Rectangle(points[0].x - WIDTH / 2, points[0].y
56
						- HEIGHT / 2, WIDTH, HEIGHT);
57
			}
58
59
			@Override
60
			public void drawShape(GC gc) {
61
				Rectangle rect = createGeometry();
62
				gc.drawRectangle(rect.toSWTRectangle());
63
			}
64
65
			@Override
66
			public void fillShape(GC gc) {
67
				Rectangle rect = createGeometry();
68
				gc.fillRectangle(rect.toSWTRectangle());
69
			}
70
		};
71
	}
72
}
(-)a/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/containment/PolygonCubicCurveContainment.java (+82 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2011 itemis AG and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthias Wienand (itemis AG) - initial API and implementation
10
 *     
11
 *******************************************************************************/
12
package org.eclipse.gef4.geometry.examples.containment;
13
14
import org.eclipse.gef4.geometry.Point;
15
import org.eclipse.gef4.geometry.planar.CubicCurve;
16
import org.eclipse.gef4.geometry.planar.IGeometry;
17
import org.eclipse.gef4.geometry.planar.Polygon;
18
import org.eclipse.swt.graphics.Color;
19
import org.eclipse.swt.graphics.GC;
20
import org.eclipse.swt.widgets.Canvas;
21
import org.eclipse.swt.widgets.Display;
22
23
public class PolygonCubicCurveContainment extends
24
		AbstractPolygonContainmentExample {
25
26
	public static void main(String[] args) {
27
		new PolygonCubicCurveContainment("Polygon / Cubic Curve - Containment");
28
	}
29
30
	public PolygonCubicCurveContainment(String title) {
31
		super(title);
32
	}
33
34
	@Override
35
	protected boolean computeIntersects(IGeometry g1, IGeometry g2) {
36
		return ((Polygon) g1).touches((CubicCurve) g2);
37
	}
38
39
	@Override
40
	protected boolean computeContains(IGeometry g1, IGeometry g2) {
41
		return ((Polygon) g1).contains((CubicCurve) g2);
42
	}
43
44
	@Override
45
	protected AbstractControllableShape createControllableShape2(Canvas canvas) {
46
		return new AbstractControllableShape(canvas) {
47
			@Override
48
			public void createControlPoints() {
49
				addControlPoint(new Point(200, 100));
50
				addControlPoint(new Point(190, 310));
51
				addControlPoint(new Point(410, 90));
52
				addControlPoint(new Point(400, 300));
53
			}
54
55
			@Override
56
			public CubicCurve createGeometry() {
57
				return new CubicCurve(getControlPoints());
58
			}
59
60
			@Override
61
			public void drawShape(GC gc) {
62
				CubicCurve c = createGeometry();
63
				gc.drawPath(new org.eclipse.swt.graphics.Path(Display
64
						.getCurrent(), c.toPath().toSWTPathData()));
65
			}
66
67
			@Override
68
			public void fillShape(GC gc) {
69
				int lineWidth = gc.getLineWidth();
70
				Color fg = gc.getForeground();
71
72
				gc.setLineWidth(3);
73
				gc.setForeground(gc.getBackground());
74
				drawShape(gc);
75
76
				gc.setForeground(fg);
77
				gc.setLineWidth(lineWidth);
78
			}
79
		};
80
	}
81
82
}
(-)a/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/containment/PolygonEllipseContainment.java (+78 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2011 itemis AG and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthias Wienand (itemis AG) - initial API and implementation
10
 *     
11
 *******************************************************************************/
12
package org.eclipse.gef4.geometry.examples.containment;
13
14
import org.eclipse.gef4.geometry.Point;
15
import org.eclipse.gef4.geometry.planar.Ellipse;
16
import org.eclipse.gef4.geometry.planar.IGeometry;
17
import org.eclipse.gef4.geometry.planar.Polygon;
18
import org.eclipse.swt.graphics.GC;
19
import org.eclipse.swt.widgets.Canvas;
20
21
public class PolygonEllipseContainment extends
22
		AbstractPolygonContainmentExample {
23
24
	public static void main(String[] args) {
25
		new PolygonEllipseContainment();
26
	}
27
28
	public PolygonEllipseContainment() {
29
		super("Polygon/Ellipse Containment");
30
	}
31
32
	@Override
33
	protected boolean computeIntersects(IGeometry g1, IGeometry g2) {
34
		return ((Polygon) g1).touches((Ellipse) g2);
35
	}
36
37
	@Override
38
	protected boolean computeContains(IGeometry g1, IGeometry g2) {
39
		return ((Polygon) g1).contains((Ellipse) g2);
40
	}
41
42
	@Override
43
	protected AbstractControllableShape createControllableShape2(Canvas canvas) {
44
		return new AbstractControllableShape(canvas) {
45
			@Override
46
			public void createControlPoints() {
47
				ControlPoint center = addControlPoint(new Point(300, 300));
48
				ControlPoint a = addControlPoint(new Point(400, 300));
49
				ControlPoint b = addControlPoint(new Point(300, 200));
50
				a.setYLink(center);
51
				b.setXLink(center);
52
			}
53
54
			@Override
55
			public Ellipse createGeometry() {
56
				Point[] points = getControlPoints();
57
				double a = Math.abs(points[0].x - points[1].x);
58
				double b = Math.abs(points[0].y - points[2].y);
59
				return new Ellipse(points[0].x - a, points[0].y - b, 2 * a,
60
						2 * b);
61
			}
62
63
			@Override
64
			public void drawShape(GC gc) {
65
				Ellipse ellipse = createGeometry();
66
				gc.drawOval((int) ellipse.getX(), (int) ellipse.getY(),
67
						(int) ellipse.getWidth(), (int) ellipse.getHeight());
68
			}
69
70
			@Override
71
			public void fillShape(GC gc) {
72
				Ellipse ellipse = createGeometry();
73
				gc.fillOval((int) ellipse.getX(), (int) ellipse.getY(),
74
						(int) ellipse.getWidth(), (int) ellipse.getHeight());
75
			}
76
		};
77
	}
78
}
(-)a/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/containment/PolygonLineContainment.java (+76 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2011 itemis AG and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthias Wienand (itemis AG) - initial API and implementation
10
 *     
11
 *******************************************************************************/
12
package org.eclipse.gef4.geometry.examples.containment;
13
14
import org.eclipse.gef4.geometry.Point;
15
import org.eclipse.gef4.geometry.planar.IGeometry;
16
import org.eclipse.gef4.geometry.planar.Line;
17
import org.eclipse.gef4.geometry.planar.Polygon;
18
import org.eclipse.swt.graphics.Color;
19
import org.eclipse.swt.graphics.GC;
20
import org.eclipse.swt.widgets.Canvas;
21
22
public class PolygonLineContainment extends AbstractPolygonContainmentExample {
23
	public static void main(String[] args) {
24
		new PolygonLineContainment();
25
	}
26
27
	public PolygonLineContainment() {
28
		super("Polygon/Line Containment");
29
	}
30
31
	@Override
32
	protected boolean computeIntersects(IGeometry g1, IGeometry g2) {
33
		return ((Polygon) g1).touches((Line) g2);
34
	}
35
36
	@Override
37
	protected boolean computeContains(IGeometry g1, IGeometry g2) {
38
		return ((Polygon) g1).contains((Line) g2);
39
	}
40
41
	@Override
42
	protected AbstractControllableShape createControllableShape2(Canvas canvas) {
43
		return new AbstractControllableShape(canvas) {
44
			@Override
45
			public void createControlPoints() {
46
				addControlPoint(new Point(100, 100));
47
				addControlPoint(new Point(300, 300));
48
			}
49
50
			@Override
51
			public Line createGeometry() {
52
				Point[] points = getControlPoints();
53
				return new Line(points[0], points[1]);
54
			}
55
56
			@Override
57
			public void drawShape(GC gc) {
58
				Line line = createGeometry();
59
				gc.drawPolyline(line.toSWTPointArray());
60
			}
61
62
			@Override
63
			public void fillShape(GC gc) {
64
				int lineWidth = gc.getLineWidth();
65
				Color fg = gc.getForeground();
66
67
				gc.setLineWidth(3);
68
				gc.setForeground(gc.getBackground());
69
				drawShape(gc);
70
71
				gc.setForeground(fg);
72
				gc.setLineWidth(lineWidth);
73
			}
74
		};
75
	}
76
}
(-)a/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/containment/PolygonPolygonContainment.java (+71 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2011 itemis AG and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthias Wienand (itemis AG) - initial API and implementation
10
 *     
11
 *******************************************************************************/
12
package org.eclipse.gef4.geometry.examples.containment;
13
14
import org.eclipse.gef4.geometry.Point;
15
import org.eclipse.gef4.geometry.planar.IGeometry;
16
import org.eclipse.gef4.geometry.planar.Polygon;
17
import org.eclipse.swt.graphics.GC;
18
import org.eclipse.swt.widgets.Canvas;
19
20
public class PolygonPolygonContainment extends
21
		AbstractPolygonContainmentExample {
22
23
	public static void main(String[] args) {
24
		new PolygonPolygonContainment();
25
	}
26
27
	public PolygonPolygonContainment() {
28
		super("Polygon/Polygon Containment");
29
	}
30
31
	@Override
32
	protected boolean computeIntersects(IGeometry g1, IGeometry g2) {
33
		return ((Polygon) g1).touches((Polygon) g2);
34
	}
35
36
	@Override
37
	protected boolean computeContains(IGeometry g1, IGeometry g2) {
38
		return ((Polygon) g1).contains((Polygon) g2);
39
	}
40
41
	@Override
42
	protected AbstractControllableShape createControllableShape2(Canvas canvas) {
43
		return new AbstractControllableShape(canvas) {
44
			@Override
45
			public void createControlPoints() {
46
				addControlPoint(new Point(200, 200));
47
			}
48
49
			@Override
50
			public Polygon createGeometry() {
51
				Point[] points = getControlPoints();
52
				Polygon polygon = new Polygon(points[0].x - 30,
53
						points[0].y - 30, points[0].x + 80, points[0].y - 20,
54
						points[0].x - 20, points[0].y + 40);
55
				return polygon;
56
			}
57
58
			@Override
59
			public void drawShape(GC gc) {
60
				Polygon polygon = createGeometry();
61
				gc.drawPolygon(polygon.toSWTPointArray());
62
			}
63
64
			@Override
65
			public void fillShape(GC gc) {
66
				Polygon polygon = createGeometry();
67
				gc.fillPolygon(polygon.toSWTPointArray());
68
			}
69
		};
70
	}
71
}
(-)a/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/containment/PolygonPolylineContainment.java (+81 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2011 itemis AG and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthias Wienand (itemis AG) - initial API and implementation
10
 *     
11
 *******************************************************************************/
12
package org.eclipse.gef4.geometry.examples.containment;
13
14
import org.eclipse.gef4.geometry.Point;
15
import org.eclipse.gef4.geometry.planar.IGeometry;
16
import org.eclipse.gef4.geometry.planar.Polygon;
17
import org.eclipse.gef4.geometry.planar.Polyline;
18
import org.eclipse.swt.graphics.Color;
19
import org.eclipse.swt.graphics.GC;
20
import org.eclipse.swt.widgets.Canvas;
21
22
public class PolygonPolylineContainment extends
23
		AbstractPolygonContainmentExample {
24
25
	public static void main(String[] args) {
26
		new PolygonPolylineContainment();
27
	}
28
29
	public PolygonPolylineContainment() {
30
		super("Polygon/Polyline Containment");
31
	}
32
33
	@Override
34
	protected boolean computeIntersects(IGeometry g1, IGeometry g2) {
35
		return ((Polygon) g1).touches((Polyline) g2);
36
	}
37
38
	@Override
39
	protected boolean computeContains(IGeometry g1, IGeometry g2) {
40
		return ((Polygon) g1).contains((Polyline) g2);
41
	}
42
43
	@Override
44
	protected AbstractControllableShape createControllableShape2(Canvas canvas) {
45
		return new AbstractControllableShape(canvas) {
46
			@Override
47
			public void createControlPoints() {
48
				addControlPoint(new Point(100, 100));
49
				addControlPoint(new Point(100, 300));
50
				addControlPoint(new Point(300, 300));
51
				addControlPoint(new Point(300, 100));
52
			}
53
54
			@Override
55
			public Polyline createGeometry() {
56
				Point[] points = getControlPoints();
57
				Polyline polyline = new Polyline(points);
58
				return polyline;
59
			}
60
61
			@Override
62
			public void drawShape(GC gc) {
63
				Polyline polyline = createGeometry();
64
				gc.drawPolyline(polyline.toSWTPointArray());
65
			}
66
67
			@Override
68
			public void fillShape(GC gc) {
69
				int lineWidth = gc.getLineWidth();
70
				Color fg = gc.getForeground();
71
72
				gc.setLineWidth(3);
73
				gc.setForeground(gc.getBackground());
74
				drawShape(gc);
75
76
				gc.setLineWidth(lineWidth);
77
				gc.setForeground(fg);
78
			}
79
		};
80
	}
81
}
(-)a/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/containment/PolygonRectangleContainment.java (+73 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2011 itemis AG and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthias Wienand (itemis AG) - initial API and implementation
10
 *     
11
 *******************************************************************************/
12
package org.eclipse.gef4.geometry.examples.containment;
13
14
import org.eclipse.gef4.geometry.Point;
15
import org.eclipse.gef4.geometry.planar.IGeometry;
16
import org.eclipse.gef4.geometry.planar.Polygon;
17
import org.eclipse.gef4.geometry.planar.Rectangle;
18
import org.eclipse.swt.graphics.GC;
19
import org.eclipse.swt.widgets.Canvas;
20
21
public class PolygonRectangleContainment extends
22
		AbstractPolygonContainmentExample {
23
24
	public static void main(String[] args) {
25
		new PolygonRectangleContainment();
26
	}
27
28
	public PolygonRectangleContainment() {
29
		super("Polygon/Rectangle containment");
30
	}
31
32
	@Override
33
	protected boolean computeIntersects(IGeometry g1, IGeometry g2) {
34
		return ((Polygon) g1).touches((Rectangle) g2);
35
	}
36
37
	@Override
38
	protected boolean computeContains(IGeometry g1, IGeometry g2) {
39
		return ((Polygon) g1).contains((Rectangle) g2);
40
	}
41
42
	@Override
43
	protected AbstractControllableShape createControllableShape2(Canvas canvas) {
44
		return new AbstractControllableShape(canvas) {
45
			private final double WIDTH = 50;
46
			private final double HEIGHT = 75;
47
48
			@Override
49
			public void createControlPoints() {
50
				addControlPoint(new Point(110, 70));
51
			}
52
53
			@Override
54
			public Rectangle createGeometry() {
55
				Point[] points = getControlPoints();
56
				return new Rectangle(points[0].x - WIDTH / 2, points[0].y
57
						- HEIGHT / 2, WIDTH, HEIGHT);
58
			}
59
60
			@Override
61
			public void drawShape(GC gc) {
62
				Rectangle rect = createGeometry();
63
				gc.drawRectangle(rect.toSWTRectangle());
64
			}
65
66
			@Override
67
			public void fillShape(GC gc) {
68
				Rectangle rect = createGeometry();
69
				gc.fillRectangle(rect.toSWTRectangle());
70
			}
71
		};
72
	}
73
}
(-)a/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/intersection/AbstractEllipseIntersectionExample.java (+48 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2011 itemis AG and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthias Wienand (itemis AG) - initial API and implementation
10
 *     
11
 *******************************************************************************/
12
package org.eclipse.gef4.geometry.examples.intersection;
13
14
import org.eclipse.gef4.geometry.planar.Ellipse;
15
import org.eclipse.swt.graphics.GC;
16
import org.eclipse.swt.widgets.Canvas;
17
18
public abstract class AbstractEllipseIntersectionExample extends
19
		AbstractIntersectionExample {
20
21
	public AbstractEllipseIntersectionExample(String title) {
22
		super(title);
23
	}
24
25
	protected AbstractControllableShape createControllableShape1(Canvas canvas) {
26
		return new AbstractControllableShape(canvas) {
27
			@Override
28
			public void createControlPoints() {
29
				// the ellipse does not have any control points
30
			}
31
32
			@Override
33
			public Ellipse createGeometry() {
34
				double w5 = getCanvas().getClientArea().width / 5;
35
				double h5 = getCanvas().getClientArea().height / 5;
36
				return new Ellipse(w5, h5, 3 * w5, 3 * h5);
37
			}
38
39
			@Override
40
			public void drawShape(GC gc) {
41
				Ellipse ellipse = (Ellipse) createGeometry();
42
				gc.drawOval((int) ellipse.getX(), (int) ellipse.getY(),
43
						(int) ellipse.getWidth(), (int) ellipse.getHeight());
44
			}
45
		};
46
	}
47
48
}
(-)a/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/intersection/AbstractIntersectionExample.java (+378 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2011 itemis AG and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthias Wienand (itemis AG) - initial API and implementation
10
 *     
11
 *******************************************************************************/
12
package org.eclipse.gef4.geometry.examples.intersection;
13
14
import java.util.ArrayList;
15
16
import org.eclipse.gef4.geometry.Point;
17
import org.eclipse.gef4.geometry.planar.Ellipse;
18
import org.eclipse.gef4.geometry.planar.IGeometry;
19
import org.eclipse.gef4.geometry.planar.Rectangle;
20
import org.eclipse.swt.SWT;
21
import org.eclipse.swt.events.MouseEvent;
22
import org.eclipse.swt.events.MouseListener;
23
import org.eclipse.swt.events.MouseMoveListener;
24
import org.eclipse.swt.events.PaintEvent;
25
import org.eclipse.swt.events.PaintListener;
26
import org.eclipse.swt.graphics.GC;
27
import org.eclipse.swt.layout.FormAttachment;
28
import org.eclipse.swt.layout.FormData;
29
import org.eclipse.swt.layout.FormLayout;
30
import org.eclipse.swt.widgets.Canvas;
31
import org.eclipse.swt.widgets.Display;
32
import org.eclipse.swt.widgets.Event;
33
import org.eclipse.swt.widgets.Label;
34
import org.eclipse.swt.widgets.Listener;
35
import org.eclipse.swt.widgets.Shell;
36
37
public abstract class AbstractIntersectionExample implements PaintListener {
38
39
	abstract public class AbstractControllableShape {
40
41
		private static final int CONTROL_POINT_COLOR = SWT.COLOR_BLUE;
42
		private static final int CONTROL_POINT_RADIUS = 5;
43
44
		private Canvas canvas;
45
		private ArrayList<ControlPoint> points;
46
47
		public AbstractControllableShape(Canvas canvas) {
48
			this.canvas = canvas;
49
			points = new ArrayList<ControlPoint>();
50
			createControlPoints();
51
		}
52
53
		public ControlPoint addControlPoint(Point p) {
54
			return addControlPoint(p, CONTROL_POINT_COLOR);
55
		}
56
57
		public ControlPoint addControlPoint(Point p, int color) {
58
			return addControlPoint(p, color, CONTROL_POINT_RADIUS);
59
		}
60
61
		public ControlPoint addControlPoint(Point p, int color, double radius) {
62
			ControlPoint cp = new ControlPoint(canvas, p, radius, color);
63
			for (ControlPoint ocp : points) {
64
				ocp.addForbiddenArea(cp);
65
				cp.addForbiddenArea(ocp);
66
			}
67
			points.add(cp);
68
			return cp;
69
		}
70
71
		abstract public void createControlPoints();
72
73
		abstract public IGeometry createGeometry();
74
75
		public void drawControlPoints(GC gc) {
76
			for (ControlPoint cp : points) {
77
				cp.draw(gc);
78
			}
79
		}
80
81
		abstract public void drawShape(GC gc);
82
83
		public Canvas getCanvas() {
84
			return canvas;
85
		}
86
87
		public Point[] getControlPoints() {
88
			Point[] points = new Point[this.points.size()];
89
90
			int i = 0;
91
			for (ControlPoint cp : this.points) {
92
				points[i++] = cp.getPoint();
93
			}
94
95
			return points;
96
		}
97
	}
98
99
	/**
100
	 * A draggable point. On the screen it is represented as an ellipse.
101
	 * 
102
	 * @author wienand
103
	 * 
104
	 */
105
	class ControlPoint implements MouseListener, MouseMoveListener, Listener {
106
		private Canvas canvas;
107
		private int color = SWT.COLOR_BLUE;
108
109
		private Ellipse ellipse;
110
		private boolean isDragged = false;
111
112
		// to rescale points on resize
113
		private double oldShellHeight;
114
		private double oldShellWidth;
115
116
		private ArrayList<ControlPoint> forbidden;
117
		private ArrayList<ControlPoint> updateLinks;
118
		private ControlPoint xLink, yLink;
119
		private double relX, relY;
120
121
		private Point p;
122
		private double radius = 5;
123
124
		/**
125
		 * Creates a new ControlPoint object. Adds event listeners to the given
126
		 * Canvas object, so that the user can drag the control point with the
127
		 * mouse.
128
		 * 
129
		 * @param canvas
130
		 *            Drawing area
131
		 */
132
		public ControlPoint(Canvas canvas) {
133
			this.canvas = canvas;
134
			canvas.addMouseListener(this);
135
			canvas.addMouseMoveListener(this);
136
			canvas.addListener(SWT.Resize, this);
137
			oldShellWidth = canvas.getClientArea().width;
138
			oldShellHeight = canvas.getClientArea().height;
139
			p = new Point(0, 0);
140
			updateLinks = new ArrayList<ControlPoint>();
141
			forbidden = new ArrayList<ControlPoint>();
142
			update();
143
		}
144
145
		/**
146
		 * Creates a new ControlPoint object. Adds event listeners to the given
147
		 * Canvas object, so that the user can drag the control point with the
148
		 * mouse.
149
		 * 
150
		 * @param canvas
151
		 *            Drawing area
152
		 * @param p
153
		 *            Exact point
154
		 * @param radius
155
		 *            Of the ellipse that represents the point
156
		 * @param color
157
		 *            Of the ellipse that represents the point
158
		 */
159
		public ControlPoint(Canvas canvas, Point p, double radius, int color) {
160
			this(canvas);
161
			this.p = p;
162
			this.radius = radius;
163
			this.color = color;
164
			update();
165
		}
166
167
		public void addForbiddenArea(ControlPoint cp) {
168
			forbidden.add(cp);
169
		}
170
171
		/**
172
		 * Draws an ellipse with the given GC at the control points location.
173
		 * 
174
		 * @param gc
175
		 */
176
		public void draw(GC gc) {
177
			// System.out.println(ellipse.toString());
178
			gc.setBackground(Display.getCurrent().getSystemColor(color));
179
			gc.fillOval((int) ellipse.getX(), (int) ellipse.getY(),
180
					(int) ellipse.getWidth(), (int) ellipse.getHeight());
181
		}
182
183
		/**
184
		 * Returns the exact Point of this ControlPoint object.
185
		 * 
186
		 * @return The exact Point of this ControlPoint object.
187
		 */
188
		public Point getPoint() {
189
			return p;
190
		}
191
192
		@Override
193
		public void handleEvent(Event e) {
194
			switch (e.type) {
195
			case SWT.Resize:
196
				Rectangle bounds = new Rectangle(canvas.getBounds());
197
				p.scale(bounds.getWidth() / oldShellWidth, bounds.getHeight()
198
						/ oldShellHeight);
199
				oldShellWidth = bounds.getWidth();
200
				oldShellHeight = bounds.getHeight();
201
				update();
202
				break;
203
			}
204
		}
205
206
		private double inRange(double low, double value, double high) {
207
			if (value < low) {
208
				return low;
209
			} else if (value > high) {
210
				return high;
211
			}
212
			return value;
213
		}
214
215
		@Override
216
		public void mouseDoubleClick(MouseEvent e) {
217
		}
218
219
		@Override
220
		public void mouseDown(MouseEvent e) {
221
			if (ellipse.contains(new Point(e.x, e.y))) {
222
				isDragged = true;
223
			}
224
		}
225
226
		@Override
227
		public void mouseMove(MouseEvent e) {
228
			if (isDragged) {
229
				relX = e.x - p.x;
230
				relY = e.y - p.y;
231
				p.x = e.x;
232
				p.y = e.y;
233
				update();
234
				canvas.redraw();
235
			}
236
		}
237
238
		@Override
239
		public void mouseUp(MouseEvent e) {
240
			isDragged = false;
241
		}
242
243
		public void setXLink(ControlPoint cp) {
244
			xLink = cp;
245
			cp.updateLinks.add(this);
246
		}
247
248
		public void setYLink(ControlPoint cp) {
249
			yLink = cp;
250
			cp.updateLinks.add(this);
251
		}
252
253
		private void update() {
254
			double oldX = p.x, oldY = p.y;
255
256
			// check canvas pane:
257
			p.x = inRange(canvas.getClientArea().x + radius, p.x,
258
					canvas.getClientArea().x + canvas.getClientArea().width
259
							- radius);
260
			p.y = inRange(canvas.getClientArea().y + radius, p.y,
261
					canvas.getClientArea().y + canvas.getClientArea().height
262
							- radius);
263
264
			// check links:
265
			if (xLink != null) {
266
				p.x = xLink.p.x;
267
				p.y += xLink.relY;
268
			} else if (yLink != null) { // no need to link both x and y
269
				p.x += yLink.relX;
270
				p.y = yLink.p.y;
271
			}
272
273
			// check forbidden areas:
274
			for (ControlPoint cp : forbidden) {
275
				double minDistance = radius + cp.radius;
276
				if (p.getDistance(cp.p) < minDistance) {
277
					if (relX > 0) {
278
						p.x = cp.p.x - minDistance;
279
					} else if (relX < 0) {
280
						p.x = cp.p.x + minDistance;
281
					} else if (relY > 0) {
282
						p.y = cp.p.y - minDistance;
283
					} else {
284
						p.y = cp.p.y + minDistance;
285
					}
286
				}
287
			}
288
289
			relX += p.x - oldX;
290
			relY += p.y - oldY;
291
292
			for (ControlPoint cp : updateLinks) {
293
				cp.update();
294
			}
295
296
			ellipse = new Ellipse(p.x - radius, p.y - radius, radius * 2,
297
					radius * 2);
298
		}
299
	}
300
301
	private static final int INTERSECTION_POINT_COLOR = SWT.COLOR_RED;
302
303
	private static final int INTERSECTION_POINT_RADIUS = 5;
304
305
	protected AbstractControllableShape controllableShape1, controllableShape2;
306
307
	private Shell shell;
308
309
	public AbstractIntersectionExample(String title) {
310
		this(title, "drag the blue control points", "resize the window");
311
	}
312
313
	/**
314
	 * 
315
	 */
316
	public AbstractIntersectionExample(String title, String... infos) {
317
		Display display = new Display();
318
319
		shell = new Shell(display, SWT.SHELL_TRIM | SWT.DOUBLE_BUFFERED);
320
		shell.setText(title);
321
		shell.setBounds(0, 0, 640, 480);
322
		shell.setLayout(new FormLayout());
323
324
		Label infoLabel = new Label(shell, SWT.NONE);
325
		FormData infoLabelFormData = new FormData();
326
		infoLabelFormData.right = new FormAttachment(100, -10);
327
		infoLabelFormData.bottom = new FormAttachment(100, -10);
328
		infoLabel.setLayoutData(infoLabelFormData);
329
330
		String infoText = "You can...";
331
		for (int i = 0; i < infos.length; i++) {
332
			infoText += "\n..." + infos[i];
333
		}
334
		infoLabel.setText(infoText);
335
336
		controllableShape1 = createControllableShape1(shell);
337
		controllableShape2 = createControllableShape2(shell);
338
339
		shell.addPaintListener(this);
340
		shell.open();
341
342
		while (!shell.isDisposed()) {
343
			if (!display.readAndDispatch()) {
344
				display.sleep();
345
			}
346
		}
347
	}
348
349
	protected abstract Point[] computeIntersections(IGeometry g1, IGeometry g2);
350
351
	protected abstract AbstractControllableShape createControllableShape1(
352
			Canvas canvas);
353
354
	protected abstract AbstractControllableShape createControllableShape2(
355
			Canvas canvas);
356
357
	@Override
358
	public void paintControl(PaintEvent e) {
359
		e.gc.setAntialias(SWT.ON);
360
361
		controllableShape1.drawShape(e.gc);
362
		controllableShape2.drawShape(e.gc);
363
364
		controllableShape1.drawControlPoints(e.gc);
365
		controllableShape2.drawControlPoints(e.gc);
366
367
		e.gc.setBackground(Display.getCurrent().getSystemColor(
368
				INTERSECTION_POINT_COLOR));
369
370
		for (Point p : computeIntersections(
371
				controllableShape1.createGeometry(),
372
				controllableShape2.createGeometry())) {
373
			e.gc.fillOval((int) p.x - INTERSECTION_POINT_RADIUS, (int) p.y
374
					- INTERSECTION_POINT_RADIUS, INTERSECTION_POINT_RADIUS * 2,
375
					INTERSECTION_POINT_RADIUS * 2);
376
		}
377
	}
378
}
(-)a/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/intersection/AbstractPolygonIntersectionExample.java (+58 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2011 itemis AG and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthias Wienand (itemis AG) - initial API and implementation
10
 *     
11
 *******************************************************************************/
12
package org.eclipse.gef4.geometry.examples.intersection;
13
14
import org.eclipse.gef4.geometry.Point;
15
import org.eclipse.gef4.geometry.planar.Line;
16
import org.eclipse.gef4.geometry.planar.Polygon;
17
import org.eclipse.swt.graphics.GC;
18
import org.eclipse.swt.graphics.Rectangle;
19
import org.eclipse.swt.widgets.Canvas;
20
21
public abstract class AbstractPolygonIntersectionExample extends
22
		AbstractIntersectionExample {
23
24
	public AbstractPolygonIntersectionExample(String title) {
25
		super(title);
26
	}
27
28
	protected AbstractControllableShape createControllableShape1(Canvas canvas) {
29
		return new AbstractControllableShape(canvas) {
30
			@Override
31
			public void createControlPoints() {
32
				// no control points => user cannot change it
33
			}
34
35
			@Override
36
			public Polygon createGeometry() {
37
				Rectangle ca = getCanvas().getClientArea();
38
				double w = ca.width;
39
				double wg = w / 6;
40
				double h = ca.height;
41
				double hg = h / 6;
42
43
				return new Polygon(new Point[] { new Point(wg, hg),
44
						new Point(w - wg, h - hg), new Point(wg, h - hg),
45
						new Point(w - wg, hg) });
46
			}
47
48
			@Override
49
			public void drawShape(GC gc) {
50
				Polygon polygon = createGeometry();
51
				for (Line segment : polygon.getOutlineSegments()) {
52
					gc.drawLine((int) segment.getX1(), (int) segment.getY1(),
53
							(int) segment.getX2(), (int) segment.getY2());
54
				}
55
			}
56
		};
57
	}
58
}
(-)a/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/intersection/BezierApproximationExample.java (+80 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2011 itemis AG and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthias Wienand (itemis AG) - initial API and implementation
10
 *     
11
 *******************************************************************************/
12
package org.eclipse.gef4.geometry.examples.intersection;
13
14
import org.eclipse.gef4.geometry.Point;
15
import org.eclipse.gef4.geometry.planar.BezierCurve;
16
import org.eclipse.gef4.geometry.planar.IGeometry;
17
import org.eclipse.gef4.geometry.planar.Line;
18
import org.eclipse.gef4.geometry.planar.Path;
19
import org.eclipse.swt.graphics.GC;
20
import org.eclipse.swt.widgets.Canvas;
21
import org.eclipse.swt.widgets.Display;
22
23
public class BezierApproximationExample extends AbstractIntersectionExample {
24
	public static void main(String[] args) {
25
		new BezierApproximationExample("Bezier Approximation Example");
26
	}
27
28
	public BezierApproximationExample(String title) {
29
		super(title);
30
	}
31
32
	protected AbstractControllableShape createControllableShape1(Canvas canvas) {
33
		return new AbstractControllableShape(canvas) {
34
			@Override
35
			public void createControlPoints() {
36
				addControlPoint(new Point(100, 100));
37
				addControlPoint(new Point(150, 400));
38
				addControlPoint(new Point(200, 300));
39
				addControlPoint(new Point(250, 150));
40
				addControlPoint(new Point(300, 250));
41
				addControlPoint(new Point(350, 200));
42
				addControlPoint(new Point(400, 350));
43
			}
44
45
			@Override
46
			public IGeometry createGeometry() {
47
				return new BezierCurve(getControlPoints()).toPath();
48
			}
49
50
			@Override
51
			public void drawShape(GC gc) {
52
				Path curve = (Path) createGeometry();
53
				gc.drawPath(new org.eclipse.swt.graphics.Path(Display
54
						.getCurrent(), curve.toSWTPathData()));
55
			}
56
		};
57
	}
58
59
	protected AbstractControllableShape createControllableShape2(Canvas canvas) {
60
		return new AbstractControllableShape(canvas) {
61
			@Override
62
			public void createControlPoints() {
63
			}
64
65
			@Override
66
			public IGeometry createGeometry() {
67
				return new Line(new Point(), new Point(1, 1));
68
			}
69
70
			@Override
71
			public void drawShape(GC gc) {
72
			}
73
		};
74
	}
75
76
	@Override
77
	protected Point[] computeIntersections(IGeometry g1, IGeometry g2) {
78
		return new Point[] {};
79
	}
80
}
(-)a/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/intersection/ConvexHullExample.java (+82 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2011 itemis AG and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthias Wienand (itemis AG) - initial API and implementation
10
 *     
11
 *******************************************************************************/
12
package org.eclipse.gef4.geometry.examples.intersection;
13
14
import org.eclipse.gef4.geometry.Point;
15
import org.eclipse.gef4.geometry.planar.IGeometry;
16
import org.eclipse.gef4.geometry.planar.Line;
17
import org.eclipse.gef4.geometry.planar.Polygon;
18
import org.eclipse.gef4.geometry.utils.PointListUtils;
19
import org.eclipse.swt.graphics.GC;
20
import org.eclipse.swt.widgets.Canvas;
21
import org.eclipse.swt.widgets.Display;
22
23
public class ConvexHullExample extends AbstractIntersectionExample {
24
	public static void main(String[] args) {
25
		new ConvexHullExample("Convex Hull Example");
26
	}
27
28
	public ConvexHullExample(String title) {
29
		super(title);
30
	}
31
32
	protected AbstractControllableShape createControllableShape1(Canvas canvas) {
33
		return new AbstractControllableShape(canvas) {
34
			@Override
35
			public void createControlPoints() {
36
				addControlPoint(new Point(100, 100));
37
				addControlPoint(new Point(150, 400));
38
				addControlPoint(new Point(200, 300));
39
				addControlPoint(new Point(250, 150));
40
				addControlPoint(new Point(300, 250));
41
				addControlPoint(new Point(350, 200));
42
				addControlPoint(new Point(400, 350));
43
			}
44
45
			@Override
46
			public IGeometry createGeometry() {
47
				Polygon convexHull = new Polygon(
48
						PointListUtils.getConvexHull(getControlPoints()));
49
				return convexHull;
50
			}
51
52
			@Override
53
			public void drawShape(GC gc) {
54
				Polygon convexHull = (Polygon) createGeometry();
55
				gc.drawPath(new org.eclipse.swt.graphics.Path(Display
56
						.getCurrent(), convexHull.toPath().toSWTPathData()));
57
			}
58
		};
59
	}
60
61
	protected AbstractControllableShape createControllableShape2(Canvas canvas) {
62
		return new AbstractControllableShape(canvas) {
63
			@Override
64
			public void createControlPoints() {
65
			}
66
67
			@Override
68
			public IGeometry createGeometry() {
69
				return new Line(-10, -10, -10, -10);
70
			}
71
72
			@Override
73
			public void drawShape(GC gc) {
74
			}
75
		};
76
	}
77
78
	@Override
79
	protected Point[] computeIntersections(IGeometry g1, IGeometry g2) {
80
		return new Point[] {};
81
	}
82
}
(-)a/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/intersection/CubicCurveDeCasteljauExample.java (+134 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2011 itemis AG and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthias Wienand (itemis AG) - initial API and implementation
10
 *     
11
 *******************************************************************************/
12
package org.eclipse.gef4.geometry.examples.intersection;
13
14
import org.eclipse.gef4.geometry.Point;
15
import org.eclipse.gef4.geometry.planar.CubicCurve;
16
import org.eclipse.gef4.geometry.planar.IGeometry;
17
import org.eclipse.gef4.geometry.planar.Line;
18
import org.eclipse.gef4.geometry.planar.Rectangle;
19
import org.eclipse.swt.SWT;
20
import org.eclipse.swt.graphics.GC;
21
import org.eclipse.swt.widgets.Canvas;
22
import org.eclipse.swt.widgets.Display;
23
24
public class CubicCurveDeCasteljauExample extends AbstractIntersectionExample {
25
	public static void main(String[] args) {
26
		new CubicCurveDeCasteljauExample("Cubic Bezier Curve Example");
27
	}
28
29
	public CubicCurveDeCasteljauExample(String title) {
30
		super(title);
31
	}
32
33
	protected AbstractControllableShape createControllableShape1(Canvas canvas) {
34
		return new AbstractControllableShape(canvas) {
35
			@Override
36
			public void createControlPoints() {
37
				addControlPoint(new Point(100, 200));
38
				addControlPoint(new Point(200, 100));
39
				addControlPoint(new Point(300, 300));
40
				addControlPoint(new Point(400, 200));
41
			}
42
43
			@Override
44
			public IGeometry createGeometry() {
45
				Point[] points = getControlPoints();
46
47
				CubicCurve curve = new CubicCurve(points[0], points[1],
48
						points[2], points[3]);
49
50
				return curve;
51
			}
52
53
			@Override
54
			public void drawShape(GC gc) {
55
				CubicCurve curve = (CubicCurve) createGeometry();
56
57
				// draw curve
58
				gc.drawPath(new org.eclipse.swt.graphics.Path(Display
59
						.getCurrent(), curve.toPath().toSWTPathData()));
60
61
				// draw bounds
62
				Rectangle bounds = curve.getBounds();
63
64
				gc.setForeground(Display.getCurrent().getSystemColor(
65
						SWT.COLOR_DARK_GRAY));
66
				gc.drawRectangle(bounds.toSWTRectangle());
67
68
				// draw lerps
69
				Point[] points = getControlPoints();
70
				for (int i = 0; i < 3; i++) {
71
					gc.setForeground(Display.getCurrent().getSystemColor(
72
							SWT.COLOR_DARK_GREEN));
73
					gc.drawLine((int) points[i].x, (int) points[i].y,
74
							(int) points[i + 1].x, (int) points[i + 1].y);
75
					gc.setForeground(Display.getCurrent().getSystemColor(
76
							SWT.COLOR_BLACK));
77
					points[i] = points[i].getTranslated(points[i + 1]
78
							.getTranslated(points[i].getScaled(-1)).getScaled(
79
									0.25));
80
					gc.drawOval((int) (points[i].x - 2),
81
							(int) (points[i].y - 2), 4, 4);
82
				}
83
				for (int i = 0; i < 2; i++) {
84
					gc.setForeground(Display.getCurrent().getSystemColor(
85
							SWT.COLOR_BLUE));
86
					gc.drawLine((int) points[i].x, (int) points[i].y,
87
							(int) points[i + 1].x, (int) points[i + 1].y);
88
					gc.setForeground(Display.getCurrent().getSystemColor(
89
							SWT.COLOR_BLACK));
90
					points[i] = points[i].getTranslated(points[i + 1]
91
							.getTranslated(points[i].getScaled(-1)).getScaled(
92
									0.25));
93
					gc.drawOval((int) (points[i].x - 2),
94
							(int) (points[i].y - 2), 4, 4);
95
				}
96
				for (int i = 0; i < 1; i++) {
97
					gc.setForeground(Display.getCurrent().getSystemColor(
98
							SWT.COLOR_DARK_RED));
99
					gc.drawLine((int) points[i].x, (int) points[i].y,
100
							(int) points[i + 1].x, (int) points[i + 1].y);
101
					gc.setForeground(Display.getCurrent().getSystemColor(
102
							SWT.COLOR_BLACK));
103
					points[i] = points[i].getTranslated(points[i + 1]
104
							.getTranslated(points[i].getScaled(-1)).getScaled(
105
									0.25));
106
					gc.drawOval((int) (points[i].x - 2),
107
							(int) (points[i].y - 2), 4, 4);
108
				}
109
			}
110
		};
111
	}
112
113
	protected AbstractControllableShape createControllableShape2(Canvas canvas) {
114
		return new AbstractControllableShape(canvas) {
115
			@Override
116
			public void createControlPoints() {
117
			}
118
119
			@Override
120
			public IGeometry createGeometry() {
121
				return new Line(new Point(), new Point());
122
			}
123
124
			@Override
125
			public void drawShape(GC gc) {
126
			}
127
		};
128
	}
129
130
	@Override
131
	protected Point[] computeIntersections(IGeometry g1, IGeometry g2) {
132
		return new Point[] {};
133
	}
134
}
(-)a/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/intersection/CubicCurvesIntersection.java (+77 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2011 itemis AG and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthias Wienand (itemis AG) - initial API and implementation
10
 *     
11
 *******************************************************************************/
12
package org.eclipse.gef4.geometry.examples.intersection;
13
14
import org.eclipse.gef4.geometry.Point;
15
import org.eclipse.gef4.geometry.planar.CubicCurve;
16
import org.eclipse.gef4.geometry.planar.IGeometry;
17
import org.eclipse.swt.graphics.GC;
18
import org.eclipse.swt.widgets.Canvas;
19
import org.eclipse.swt.widgets.Display;
20
21
public class CubicCurvesIntersection extends AbstractIntersectionExample {
22
	public static void main(String[] args) {
23
		new CubicCurvesIntersection("Cubic Bezier Curve/Curve Intersection");
24
	}
25
26
	public CubicCurvesIntersection(String title) {
27
		super(title);
28
	}
29
30
	private AbstractControllableShape createControllableCubicBezierCurveShape(
31
			Canvas canvas, Point... points) {
32
		final Point start = points[0];
33
		final Point ctrl1 = points[1];
34
		final Point ctrl2 = points[2];
35
		final Point end = points[3];
36
37
		return new AbstractControllableShape(canvas) {
38
			@Override
39
			public void createControlPoints() {
40
				addControlPoint(start);
41
				addControlPoint(ctrl1);
42
				addControlPoint(ctrl2);
43
				addControlPoint(end);
44
			}
45
46
			@Override
47
			public IGeometry createGeometry() {
48
				return new CubicCurve(getControlPoints());
49
			}
50
51
			@Override
52
			public void drawShape(GC gc) {
53
				CubicCurve curve = (CubicCurve) createGeometry();
54
55
				gc.drawPath(new org.eclipse.swt.graphics.Path(Display
56
						.getCurrent(), curve.toPath().toSWTPathData()));
57
			}
58
		};
59
	}
60
61
	protected AbstractControllableShape createControllableShape1(Canvas canvas) {
62
		return createControllableCubicBezierCurveShape(canvas, new Point(100,
63
				100), new Point(150, 50), new Point(310, 300), new Point(400,
64
				200));
65
	}
66
67
	protected AbstractControllableShape createControllableShape2(Canvas canvas) {
68
		return createControllableCubicBezierCurveShape(canvas, new Point(400,
69
				100), new Point(310, 110), new Point(210, 210), new Point(100,
70
				200));
71
	}
72
73
	@Override
74
	protected Point[] computeIntersections(IGeometry g1, IGeometry g2) {
75
		return ((CubicCurve) g1).getIntersections((CubicCurve) g2);
76
	}
77
}
(-)a/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/intersection/EllipseCubicCurveIntersection.java (+64 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2011 itemis AG and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthias Wienand (itemis AG) - initial API and implementation
10
 *     
11
 *******************************************************************************/
12
package org.eclipse.gef4.geometry.examples.intersection;
13
14
import org.eclipse.gef4.geometry.Point;
15
import org.eclipse.gef4.geometry.planar.CubicCurve;
16
import org.eclipse.gef4.geometry.planar.Ellipse;
17
import org.eclipse.gef4.geometry.planar.IGeometry;
18
import org.eclipse.swt.graphics.GC;
19
import org.eclipse.swt.widgets.Canvas;
20
import org.eclipse.swt.widgets.Display;
21
22
public class EllipseCubicCurveIntersection extends
23
		AbstractEllipseIntersectionExample {
24
25
	/**
26
	 * @param args
27
	 */
28
	public static void main(String[] args) {
29
		new EllipseCubicCurveIntersection();
30
	}
31
32
	public EllipseCubicCurveIntersection() {
33
		super("Ellipse/CubicCurve Intersection");
34
	}
35
36
	protected Point[] computeIntersections(IGeometry g1, IGeometry g2) {
37
		return ((Ellipse) g1).getIntersections((CubicCurve) g2);
38
	}
39
40
	@Override
41
	protected AbstractControllableShape createControllableShape2(Canvas canvas) {
42
		return new AbstractControllableShape(canvas) {
43
			@Override
44
			public void createControlPoints() {
45
				addControlPoint(new Point(100, 150));
46
				addControlPoint(new Point(400, 200));
47
				addControlPoint(new Point(300, 400));
48
				addControlPoint(new Point(550, 300));
49
			}
50
51
			@Override
52
			public CubicCurve createGeometry() {
53
				return new CubicCurve(getControlPoints());
54
			}
55
56
			@Override
57
			public void drawShape(GC gc) {
58
				CubicCurve c = createGeometry();
59
				gc.drawPath(new org.eclipse.swt.graphics.Path(Display
60
						.getCurrent(), c.toPath().toSWTPathData()));
61
			}
62
		};
63
	}
64
}
(-)a/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/intersection/EllipseEllipseIntersection.java (+70 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2011 itemis AG and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthias Wienand (itemis AG) - initial API and implementation
10
 *     
11
 *******************************************************************************/
12
package org.eclipse.gef4.geometry.examples.intersection;
13
14
import org.eclipse.gef4.geometry.Point;
15
import org.eclipse.gef4.geometry.planar.Ellipse;
16
import org.eclipse.gef4.geometry.planar.IGeometry;
17
import org.eclipse.swt.graphics.GC;
18
import org.eclipse.swt.widgets.Canvas;
19
import org.eclipse.swt.widgets.Display;
20
21
public class EllipseEllipseIntersection extends
22
		AbstractEllipseIntersectionExample {
23
24
	/**
25
	 * @param args
26
	 */
27
	public static void main(String[] args) {
28
		new EllipseEllipseIntersection();
29
	}
30
31
	public EllipseEllipseIntersection() {
32
		super("Ellipse/Ellipse Intersection");
33
	}
34
35
	@Override
36
	protected Point[] computeIntersections(IGeometry g1, IGeometry g2) {
37
		return ((Ellipse) g1).getIntersections((Ellipse) g2);
38
	}
39
40
	@Override
41
	protected AbstractControllableShape createControllableShape2(Canvas canvas) {
42
		return new AbstractControllableShape(canvas) {
43
			@Override
44
			public void createControlPoints() {
45
				ControlPoint center = addControlPoint(new Point(300, 300));
46
				ControlPoint a = addControlPoint(new Point(400, 300));
47
				ControlPoint b = addControlPoint(new Point(300, 200));
48
				a.setYLink(center);
49
				b.setXLink(center);
50
			}
51
52
			@Override
53
			public Ellipse createGeometry() {
54
				Point[] points = getControlPoints();
55
				double a = Math.abs(points[0].x - points[1].x);
56
				double b = Math.abs(points[0].y - points[2].y);
57
				return new Ellipse(points[0].x - a, points[0].y - b, 2 * a,
58
						2 * b);
59
			}
60
61
			@Override
62
			public void drawShape(GC gc) {
63
				Ellipse ellipse = createGeometry();
64
				gc.drawPath(new org.eclipse.swt.graphics.Path(Display
65
						.getCurrent(), ellipse.toPath().toSWTPathData()));
66
			}
67
		};
68
	}
69
70
}
(-)a/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/intersection/EllipseLineIntersection.java (+64 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2011 itemis AG and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthias Wienand (itemis AG) - initial API and implementation
10
 *     
11
 *******************************************************************************/
12
package org.eclipse.gef4.geometry.examples.intersection;
13
14
import org.eclipse.gef4.geometry.Point;
15
import org.eclipse.gef4.geometry.planar.Ellipse;
16
import org.eclipse.gef4.geometry.planar.IGeometry;
17
import org.eclipse.gef4.geometry.planar.Line;
18
import org.eclipse.swt.graphics.GC;
19
import org.eclipse.swt.widgets.Canvas;
20
21
/**
22
 * Simple example demonstrating the intersection of an {@link Ellipse} and a
23
 * {@link Line}.
24
 * 
25
 * @author Matthias Wienand (matthias.wienand@itemis.de)
26
 * 
27
 */
28
public class EllipseLineIntersection extends AbstractEllipseIntersectionExample {
29
30
	public static void main(String[] args) {
31
		new EllipseLineIntersection();
32
	}
33
34
	public EllipseLineIntersection() {
35
		super("Ellipse/Line Intersection");
36
	}
37
38
	protected Point[] computeIntersections(IGeometry g1, IGeometry g2) {
39
		return ((Ellipse) g1).getIntersections((Line) g2);
40
	}
41
42
	@Override
43
	protected AbstractControllableShape createControllableShape2(Canvas canvas) {
44
		return new AbstractControllableShape(canvas) {
45
			@Override
46
			public void createControlPoints() {
47
				addControlPoint(new Point(100, 100));
48
				addControlPoint(new Point(300, 300));
49
			}
50
51
			@Override
52
			public Line createGeometry() {
53
				Point[] points = getControlPoints();
54
				return new Line(points[0], points[1]);
55
			}
56
57
			@Override
58
			public void drawShape(GC gc) {
59
				Line line = createGeometry();
60
				gc.drawPolyline(line.toSWTPointArray());
61
			}
62
		};
63
	}
64
}
(-)a/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/intersection/EllipsePolygonIntersection.java (+63 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2011 itemis AG and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthias Wienand (itemis AG) - initial API and implementation
10
 *     
11
 *******************************************************************************/
12
package org.eclipse.gef4.geometry.examples.intersection;
13
14
import org.eclipse.gef4.geometry.Point;
15
import org.eclipse.gef4.geometry.planar.Ellipse;
16
import org.eclipse.gef4.geometry.planar.IGeometry;
17
import org.eclipse.gef4.geometry.planar.Polygon;
18
import org.eclipse.swt.graphics.GC;
19
import org.eclipse.swt.widgets.Canvas;
20
21
public class EllipsePolygonIntersection extends
22
		AbstractEllipseIntersectionExample {
23
24
	public static void main(String[] args) {
25
		new EllipsePolygonIntersection();
26
	}
27
28
	public EllipsePolygonIntersection() {
29
		super("Ellipse/Polygon Intersection");
30
	}
31
32
	@Override
33
	protected Point[] computeIntersections(IGeometry g1, IGeometry g2) {
34
		return ((Ellipse) g1).getOutline().getIntersections(
35
				((Polygon) g2).getOutline());
36
	}
37
38
	@Override
39
	protected AbstractControllableShape createControllableShape2(Canvas canvas) {
40
		return new AbstractControllableShape(canvas) {
41
			@Override
42
			public void createControlPoints() {
43
				addControlPoint(new Point(100, 100));
44
				addControlPoint(new Point(600, 200));
45
				addControlPoint(new Point(100, 300));
46
			}
47
48
			@Override
49
			public Polygon createGeometry() {
50
				Point[] points = getControlPoints();
51
				Polygon polygon = new Polygon(points);
52
				return polygon;
53
			}
54
55
			@Override
56
			public void drawShape(GC gc) {
57
				Polygon polygon = createGeometry();
58
				gc.drawPolygon(polygon.toSWTPointArray());
59
			}
60
		};
61
	}
62
63
}
(-)a/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/intersection/EllipsePolylineIntersection.java (+66 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2011 itemis AG and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthias Wienand (itemis AG) - initial API and implementation
10
 *     
11
 *******************************************************************************/
12
package org.eclipse.gef4.geometry.examples.intersection;
13
14
import org.eclipse.gef4.geometry.Point;
15
import org.eclipse.gef4.geometry.planar.Ellipse;
16
import org.eclipse.gef4.geometry.planar.IGeometry;
17
import org.eclipse.gef4.geometry.planar.Polyline;
18
import org.eclipse.swt.graphics.GC;
19
import org.eclipse.swt.widgets.Canvas;
20
21
public class EllipsePolylineIntersection extends
22
		AbstractEllipseIntersectionExample {
23
24
	/**
25
	 * @param args
26
	 */
27
	public static void main(String[] args) {
28
		new EllipsePolylineIntersection();
29
	}
30
31
	public EllipsePolylineIntersection() {
32
		super("Ellipse/Polyline Intersection");
33
	}
34
35
	@Override
36
	protected Point[] computeIntersections(IGeometry g1, IGeometry g2) {
37
		return ((Ellipse) g1).getIntersections((Polyline) g2);
38
	}
39
40
	@Override
41
	protected AbstractControllableShape createControllableShape2(Canvas canvas) {
42
		return new AbstractControllableShape(canvas) {
43
			@Override
44
			public void createControlPoints() {
45
				addControlPoint(new Point(100, 100));
46
				addControlPoint(new Point(500, 400));
47
				addControlPoint(new Point(100, 400));
48
				addControlPoint(new Point(500, 100));
49
			}
50
51
			@Override
52
			public Polyline createGeometry() {
53
				Point[] points = getControlPoints();
54
				Polyline polyline = new Polyline(points);
55
				return polyline;
56
			}
57
58
			@Override
59
			public void drawShape(GC gc) {
60
				Polyline polyline = createGeometry();
61
				gc.drawPolyline(polyline.toSWTPointArray());
62
			}
63
		};
64
	}
65
66
}
(-)a/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/intersection/EllipseQuadraticCurveIntersection.java (+63 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2011 itemis AG and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthias Wienand (itemis AG) - initial API and implementation
10
 *     
11
 *******************************************************************************/
12
package org.eclipse.gef4.geometry.examples.intersection;
13
14
import org.eclipse.gef4.geometry.Point;
15
import org.eclipse.gef4.geometry.planar.Ellipse;
16
import org.eclipse.gef4.geometry.planar.IGeometry;
17
import org.eclipse.gef4.geometry.planar.QuadraticCurve;
18
import org.eclipse.swt.graphics.GC;
19
import org.eclipse.swt.widgets.Canvas;
20
import org.eclipse.swt.widgets.Display;
21
22
public class EllipseQuadraticCurveIntersection extends
23
		AbstractEllipseIntersectionExample {
24
25
	/**
26
	 * @param args
27
	 */
28
	public static void main(String[] args) {
29
		new EllipseQuadraticCurveIntersection();
30
	}
31
32
	public EllipseQuadraticCurveIntersection() {
33
		super("Ellipse/QuadraticCurve Intersection");
34
	}
35
36
	protected Point[] computeIntersections(IGeometry g1, IGeometry g2) {
37
		return ((Ellipse) g1).getIntersections((QuadraticCurve) g2);
38
	}
39
40
	@Override
41
	protected AbstractControllableShape createControllableShape2(Canvas canvas) {
42
		return new AbstractControllableShape(canvas) {
43
			@Override
44
			public void createControlPoints() {
45
				addControlPoint(new Point(100, 150));
46
				addControlPoint(new Point(400, 200));
47
				addControlPoint(new Point(550, 300));
48
			}
49
50
			@Override
51
			public QuadraticCurve createGeometry() {
52
				return new QuadraticCurve(getControlPoints());
53
			}
54
55
			@Override
56
			public void drawShape(GC gc) {
57
				QuadraticCurve c = createGeometry();
58
				gc.drawPath(new org.eclipse.swt.graphics.Path(Display
59
						.getCurrent(), c.toPath().toSWTPathData()));
60
			}
61
		};
62
	}
63
}
(-)a/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/intersection/EllipseRectangleIntersection.java (+62 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2011 itemis AG and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthias Wienand (itemis AG) - initial API and implementation
10
 *     
11
 *******************************************************************************/
12
package org.eclipse.gef4.geometry.examples.intersection;
13
14
import org.eclipse.gef4.geometry.Point;
15
import org.eclipse.gef4.geometry.planar.Ellipse;
16
import org.eclipse.gef4.geometry.planar.IGeometry;
17
import org.eclipse.gef4.geometry.planar.Rectangle;
18
import org.eclipse.swt.graphics.GC;
19
import org.eclipse.swt.widgets.Canvas;
20
21
public class EllipseRectangleIntersection extends
22
		AbstractEllipseIntersectionExample {
23
24
	/**
25
	 * @param args
26
	 */
27
	public static void main(String[] args) {
28
		new EllipseRectangleIntersection();
29
	}
30
31
	public EllipseRectangleIntersection() {
32
		super("Ellipse/Rectangle Intersection");
33
	}
34
35
	protected Point[] computeIntersections(IGeometry g1, IGeometry g2) {
36
		return ((Ellipse) g1).getOutline().getIntersections(
37
				((Rectangle) g2).getOutline());
38
	}
39
40
	@Override
41
	protected AbstractControllableShape createControllableShape2(Canvas canvas) {
42
		return new AbstractControllableShape(canvas) {
43
			@Override
44
			public void createControlPoints() {
45
				addControlPoint(new Point(100, 150));
46
				addControlPoint(new Point(550, 300));
47
			}
48
49
			@Override
50
			public Rectangle createGeometry() {
51
				Point[] points = getControlPoints();
52
				return new Rectangle(points[0], points[1]);
53
			}
54
55
			@Override
56
			public void drawShape(GC gc) {
57
				Rectangle rect = createGeometry();
58
				gc.drawRectangle(rect.toSWTRectangle());
59
			}
60
		};
61
	}
62
}
(-)a/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/intersection/PolygonCubicCurveIntersection.java (+74 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2011 itemis AG and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthias Wienand (itemis AG) - initial API and implementation
10
 *     
11
 *******************************************************************************/
12
package org.eclipse.gef4.geometry.examples.intersection;
13
14
import org.eclipse.gef4.geometry.Point;
15
import org.eclipse.gef4.geometry.planar.CubicCurve;
16
import org.eclipse.gef4.geometry.planar.Ellipse;
17
import org.eclipse.gef4.geometry.planar.IGeometry;
18
import org.eclipse.gef4.geometry.planar.Line;
19
import org.eclipse.gef4.geometry.planar.Polygon;
20
import org.eclipse.swt.graphics.GC;
21
import org.eclipse.swt.widgets.Canvas;
22
import org.eclipse.swt.widgets.Display;
23
24
/**
25
 * Simple example demonstrating the intersection of an {@link Ellipse} and a
26
 * {@link Line}.
27
 * 
28
 * @author Matthias Wienand (matthias.wienand@itemis.de)
29
 * 
30
 */
31
public class PolygonCubicCurveIntersection extends
32
		AbstractPolygonIntersectionExample {
33
34
	public static void main(String[] args) {
35
		new PolygonCubicCurveIntersection();
36
	}
37
38
	public PolygonCubicCurveIntersection() {
39
		super("Polygon/CubicCurve Intersection");
40
	}
41
42
	protected Point[] computeIntersections(IGeometry g1, IGeometry g2) {
43
		return ((Polygon) g1).getOutline().getIntersections((CubicCurve) g2);
44
	}
45
46
	@Override
47
	protected AbstractControllableShape createControllableShape2(Canvas canvas) {
48
		return new AbstractControllableShape(canvas) {
49
			@Override
50
			public void createControlPoints() {
51
				addControlPoint(new Point(200, 100));
52
				addControlPoint(new Point(190, 310));
53
				addControlPoint(new Point(410, 90));
54
				addControlPoint(new Point(400, 300));
55
			}
56
57
			@Override
58
			public CubicCurve createGeometry() {
59
				Point[] controlPoints = getControlPoints();
60
				System.out.println("new CubicCurve(" + controlPoints[0] + ", "
61
						+ controlPoints[1] + ", " + controlPoints[2] + ", "
62
						+ controlPoints[3] + ")");
63
				return new CubicCurve(controlPoints);
64
			}
65
66
			@Override
67
			public void drawShape(GC gc) {
68
				CubicCurve c = createGeometry();
69
				gc.drawPath(new org.eclipse.swt.graphics.Path(Display
70
						.getCurrent(), c.toPath().toSWTPathData()));
71
			}
72
		};
73
	}
74
}
(-)a/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/intersection/PolygonEllipseIntersection.java (+71 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2011 itemis AG and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthias Wienand (itemis AG) - initial API and implementation
10
 *     
11
 *******************************************************************************/
12
package org.eclipse.gef4.geometry.examples.intersection;
13
14
import org.eclipse.gef4.geometry.Point;
15
import org.eclipse.gef4.geometry.planar.Ellipse;
16
import org.eclipse.gef4.geometry.planar.IGeometry;
17
import org.eclipse.gef4.geometry.planar.Polygon;
18
import org.eclipse.swt.graphics.GC;
19
import org.eclipse.swt.widgets.Canvas;
20
21
public class PolygonEllipseIntersection extends
22
		AbstractPolygonIntersectionExample {
23
24
	/**
25
	 * @param args
26
	 */
27
	public static void main(String[] args) {
28
		new PolygonEllipseIntersection();
29
	}
30
31
	public PolygonEllipseIntersection() {
32
		super("Polygon/Ellipse Intersection");
33
	}
34
35
	@Override
36
	protected Point[] computeIntersections(IGeometry g1, IGeometry g2) {
37
		return ((Polygon) g1).getOutline().getIntersections(
38
				((Ellipse) g2).getOutline());
39
	}
40
41
	@Override
42
	protected AbstractControllableShape createControllableShape2(Canvas canvas) {
43
		return new AbstractControllableShape(canvas) {
44
			@Override
45
			public void createControlPoints() {
46
				ControlPoint center = addControlPoint(new Point(300, 300));
47
				ControlPoint a = addControlPoint(new Point(400, 300));
48
				ControlPoint b = addControlPoint(new Point(300, 200));
49
				a.setYLink(center);
50
				b.setXLink(center);
51
			}
52
53
			@Override
54
			public Ellipse createGeometry() {
55
				Point[] points = getControlPoints();
56
				double a = Math.abs(points[0].x - points[1].x);
57
				double b = Math.abs(points[0].y - points[2].y);
58
				return new Ellipse(points[0].x - a, points[0].y - b, 2 * a,
59
						2 * b);
60
			}
61
62
			@Override
63
			public void drawShape(GC gc) {
64
				Ellipse ellipse = createGeometry();
65
				gc.drawOval((int) ellipse.getX(), (int) ellipse.getY(),
66
						(int) ellipse.getWidth(), (int) ellipse.getHeight());
67
			}
68
		};
69
	}
70
71
}
(-)a/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/intersection/PolygonLineIntersection.java (+65 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2011 itemis AG and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthias Wienand (itemis AG) - initial API and implementation
10
 *     
11
 *******************************************************************************/
12
package org.eclipse.gef4.geometry.examples.intersection;
13
14
import org.eclipse.gef4.geometry.Point;
15
import org.eclipse.gef4.geometry.planar.Ellipse;
16
import org.eclipse.gef4.geometry.planar.IGeometry;
17
import org.eclipse.gef4.geometry.planar.Line;
18
import org.eclipse.gef4.geometry.planar.Polygon;
19
import org.eclipse.swt.graphics.GC;
20
import org.eclipse.swt.widgets.Canvas;
21
22
/**
23
 * Simple example demonstrating the intersection of an {@link Ellipse} and a
24
 * {@link Line}.
25
 * 
26
 * @author Matthias Wienand (matthias.wienand@itemis.de)
27
 * 
28
 */
29
public class PolygonLineIntersection extends AbstractPolygonIntersectionExample {
30
31
	public static void main(String[] args) {
32
		new PolygonLineIntersection();
33
	}
34
35
	public PolygonLineIntersection() {
36
		super("Polygon/Line Intersection");
37
	}
38
39
	protected Point[] computeIntersections(IGeometry g1, IGeometry g2) {
40
		return ((Polygon) g1).getOutline().getIntersections((Line) g2);
41
	}
42
43
	@Override
44
	protected AbstractControllableShape createControllableShape2(Canvas canvas) {
45
		return new AbstractControllableShape(canvas) {
46
			@Override
47
			public void createControlPoints() {
48
				addControlPoint(new Point(100, 100));
49
				addControlPoint(new Point(300, 300));
50
			}
51
52
			@Override
53
			public Line createGeometry() {
54
				Point[] points = getControlPoints();
55
				return new Line(points[0], points[1]);
56
			}
57
58
			@Override
59
			public void drawShape(GC gc) {
60
				Line line = createGeometry();
61
				gc.drawPolyline(line.toSWTPointArray());
62
			}
63
		};
64
	}
65
}
(-)a/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/intersection/PolygonPolygonIntersection.java (+62 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2011 itemis AG and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthias Wienand (itemis AG) - initial API and implementation
10
 *     
11
 *******************************************************************************/
12
package org.eclipse.gef4.geometry.examples.intersection;
13
14
import org.eclipse.gef4.geometry.Point;
15
import org.eclipse.gef4.geometry.planar.IGeometry;
16
import org.eclipse.gef4.geometry.planar.Polygon;
17
import org.eclipse.swt.graphics.GC;
18
import org.eclipse.swt.widgets.Canvas;
19
20
public class PolygonPolygonIntersection extends
21
		AbstractPolygonIntersectionExample {
22
23
	public static void main(String[] args) {
24
		new PolygonPolygonIntersection();
25
	}
26
27
	public PolygonPolygonIntersection() {
28
		super("Polygon/Polygon Intersection");
29
	}
30
31
	@Override
32
	protected Point[] computeIntersections(IGeometry g1, IGeometry g2) {
33
		return ((Polygon) g1).getOutline().getIntersections(
34
				((Polygon) g2).getOutline());
35
	}
36
37
	@Override
38
	protected AbstractControllableShape createControllableShape2(Canvas canvas) {
39
		return new AbstractControllableShape(canvas) {
40
			@Override
41
			public void createControlPoints() {
42
				addControlPoint(new Point(100, 100));
43
				addControlPoint(new Point(600, 200));
44
				addControlPoint(new Point(100, 300));
45
			}
46
47
			@Override
48
			public Polygon createGeometry() {
49
				Point[] points = getControlPoints();
50
				Polygon polygon = new Polygon(points);
51
				return polygon;
52
			}
53
54
			@Override
55
			public void drawShape(GC gc) {
56
				Polygon polygon = createGeometry();
57
				gc.drawPolygon(polygon.toSWTPointArray());
58
			}
59
		};
60
	}
61
62
}
(-)a/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/intersection/PolygonPolylineIntersection.java (+63 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2011 itemis AG and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthias Wienand (itemis AG) - initial API and implementation
10
 *     
11
 *******************************************************************************/
12
package org.eclipse.gef4.geometry.examples.intersection;
13
14
import org.eclipse.gef4.geometry.Point;
15
import org.eclipse.gef4.geometry.planar.IGeometry;
16
import org.eclipse.gef4.geometry.planar.Polygon;
17
import org.eclipse.gef4.geometry.planar.Polyline;
18
import org.eclipse.swt.graphics.GC;
19
import org.eclipse.swt.widgets.Canvas;
20
21
public class PolygonPolylineIntersection extends
22
		AbstractPolygonIntersectionExample {
23
24
	public static void main(String[] args) {
25
		new PolygonPolylineIntersection();
26
	}
27
28
	public PolygonPolylineIntersection() {
29
		super("Polygon/Polyline Intersection");
30
	}
31
32
	@Override
33
	protected Point[] computeIntersections(IGeometry g1, IGeometry g2) {
34
		return ((Polygon) g1).getOutline().getIntersections((Polyline) g2);
35
	}
36
37
	@Override
38
	protected AbstractControllableShape createControllableShape2(Canvas canvas) {
39
		return new AbstractControllableShape(canvas) {
40
			@Override
41
			public void createControlPoints() {
42
				addControlPoint(new Point(100, 100));
43
				addControlPoint(new Point(100, 300));
44
				addControlPoint(new Point(300, 300));
45
				addControlPoint(new Point(300, 100));
46
			}
47
48
			@Override
49
			public Polyline createGeometry() {
50
				Point[] points = getControlPoints();
51
				Polyline polyline = new Polyline(points);
52
				return polyline;
53
			}
54
55
			@Override
56
			public void drawShape(GC gc) {
57
				Polyline polyline = createGeometry();
58
				gc.drawPolyline(polyline.toSWTPointArray());
59
			}
60
		};
61
	}
62
63
}
(-)a/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/intersection/PolygonQuadraticCurveIntersection.java (+70 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2011 itemis AG and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthias Wienand (itemis AG) - initial API and implementation
10
 *     
11
 *******************************************************************************/
12
package org.eclipse.gef4.geometry.examples.intersection;
13
14
import org.eclipse.gef4.geometry.Point;
15
import org.eclipse.gef4.geometry.planar.Ellipse;
16
import org.eclipse.gef4.geometry.planar.IGeometry;
17
import org.eclipse.gef4.geometry.planar.Line;
18
import org.eclipse.gef4.geometry.planar.Polygon;
19
import org.eclipse.gef4.geometry.planar.QuadraticCurve;
20
import org.eclipse.swt.graphics.GC;
21
import org.eclipse.swt.widgets.Canvas;
22
import org.eclipse.swt.widgets.Display;
23
24
/**
25
 * Simple example demonstrating the intersection of an {@link Ellipse} and a
26
 * {@link Line}.
27
 * 
28
 * @author Matthias Wienand (matthias.wienand@itemis.de)
29
 * 
30
 */
31
public class PolygonQuadraticCurveIntersection extends
32
		AbstractPolygonIntersectionExample {
33
34
	public static void main(String[] args) {
35
		new PolygonQuadraticCurveIntersection();
36
	}
37
38
	public PolygonQuadraticCurveIntersection() {
39
		super("Polygon/QuadraticCurve Intersection");
40
	}
41
42
	protected Point[] computeIntersections(IGeometry g1, IGeometry g2) {
43
		return ((Polygon) g1).getOutline()
44
				.getIntersections((QuadraticCurve) g2);
45
	}
46
47
	@Override
48
	protected AbstractControllableShape createControllableShape2(Canvas canvas) {
49
		return new AbstractControllableShape(canvas) {
50
			@Override
51
			public void createControlPoints() {
52
				addControlPoint(new Point(100, 100));
53
				addControlPoint(new Point(300, 100));
54
				addControlPoint(new Point(300, 300));
55
			}
56
57
			@Override
58
			public QuadraticCurve createGeometry() {
59
				return new QuadraticCurve(getControlPoints());
60
			}
61
62
			@Override
63
			public void drawShape(GC gc) {
64
				QuadraticCurve c = createGeometry();
65
				gc.drawPath(new org.eclipse.swt.graphics.Path(Display
66
						.getCurrent(), c.toPath().toSWTPathData()));
67
			}
68
		};
69
	}
70
}
(-)a/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/intersection/PolygonRectangleIntersection.java (+59 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2011 itemis AG and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthias Wienand (itemis AG) - initial API and implementation
10
 *     
11
 *******************************************************************************/
12
package org.eclipse.gef4.geometry.examples.intersection;
13
14
import org.eclipse.gef4.geometry.Point;
15
import org.eclipse.gef4.geometry.planar.IGeometry;
16
import org.eclipse.gef4.geometry.planar.Polygon;
17
import org.eclipse.gef4.geometry.planar.Rectangle;
18
import org.eclipse.swt.graphics.GC;
19
import org.eclipse.swt.widgets.Canvas;
20
21
public class PolygonRectangleIntersection extends
22
		AbstractPolygonIntersectionExample {
23
24
	public static void main(String[] args) {
25
		new PolygonRectangleIntersection();
26
	}
27
28
	public PolygonRectangleIntersection() {
29
		super("Polygon/Rectangle Intersection");
30
	}
31
32
	protected Point[] computeIntersections(IGeometry g1, IGeometry g2) {
33
		return ((Polygon) g1).getOutline().getIntersections(
34
				((Rectangle) g2).getOutline());
35
	}
36
37
	@Override
38
	protected AbstractControllableShape createControllableShape2(Canvas canvas) {
39
		return new AbstractControllableShape(canvas) {
40
			@Override
41
			public void createControlPoints() {
42
				addControlPoint(new Point(100, 150));
43
				addControlPoint(new Point(550, 300));
44
			}
45
46
			@Override
47
			public Rectangle createGeometry() {
48
				Point[] points = getControlPoints();
49
				return new Rectangle(points[0], points[1]);
50
			}
51
52
			@Override
53
			public void drawShape(GC gc) {
54
				Rectangle rect = createGeometry();
55
				gc.drawRectangle(rect.toSWTRectangle());
56
			}
57
		};
58
	}
59
}
(-)a/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/intersection/QuadraticCurvesIntersection.java (+74 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2011 itemis AG and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthias Wienand (itemis AG) - initial API and implementation
10
 *     
11
 *******************************************************************************/
12
package org.eclipse.gef4.geometry.examples.intersection;
13
14
import org.eclipse.gef4.geometry.Point;
15
import org.eclipse.gef4.geometry.planar.IGeometry;
16
import org.eclipse.gef4.geometry.planar.QuadraticCurve;
17
import org.eclipse.swt.graphics.GC;
18
import org.eclipse.swt.widgets.Canvas;
19
import org.eclipse.swt.widgets.Display;
20
21
public class QuadraticCurvesIntersection extends AbstractIntersectionExample {
22
	public static void main(String[] args) {
23
		new QuadraticCurvesIntersection(
24
				"Quadratic Bezier Curve/Curve Intersection");
25
	}
26
27
	public QuadraticCurvesIntersection(String title) {
28
		super(title);
29
	}
30
31
	private AbstractControllableShape createControllableQuadraticBezierCurveShape(
32
			Canvas canvas, Point... points) {
33
		final Point start = points[0];
34
		final Point ctrl = points[1];
35
		final Point end = points[2];
36
37
		return new AbstractControllableShape(canvas) {
38
			@Override
39
			public void createControlPoints() {
40
				addControlPoint(start);
41
				addControlPoint(ctrl);
42
				addControlPoint(end);
43
			}
44
45
			@Override
46
			public IGeometry createGeometry() {
47
				return new QuadraticCurve(getControlPoints());
48
			}
49
50
			@Override
51
			public void drawShape(GC gc) {
52
				QuadraticCurve curve = (QuadraticCurve) createGeometry();
53
54
				gc.drawPath(new org.eclipse.swt.graphics.Path(Display
55
						.getCurrent(), curve.toPath().toSWTPathData()));
56
			}
57
		};
58
	}
59
60
	protected AbstractControllableShape createControllableShape1(Canvas canvas) {
61
		return createControllableQuadraticBezierCurveShape(canvas, new Point(
62
				100, 100), new Point(300, 150), new Point(400, 400));
63
	}
64
65
	protected AbstractControllableShape createControllableShape2(Canvas canvas) {
66
		return createControllableQuadraticBezierCurveShape(canvas, new Point(
67
				400, 100), new Point(310, 290), new Point(100, 400));
68
	}
69
70
	@Override
71
	protected Point[] computeIntersections(IGeometry g1, IGeometry g2) {
72
		return ((QuadraticCurve) g1).getIntersections((QuadraticCurve) g2);
73
	}
74
}
(-)a/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/scalerotate/AbstractScaleRotateExample.java (+160 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2011 itemis AG and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthias Wienand (itemis AG) - initial API and implementation
10
 *     
11
 *******************************************************************************/
12
package org.eclipse.gef4.geometry.examples.scalerotate;
13
14
import org.eclipse.gef4.geometry.Angle;
15
import org.eclipse.gef4.geometry.Point;
16
import org.eclipse.gef4.geometry.euclidean.Vector;
17
import org.eclipse.gef4.geometry.planar.IGeometry;
18
import org.eclipse.swt.SWT;
19
import org.eclipse.swt.events.MouseEvent;
20
import org.eclipse.swt.events.MouseListener;
21
import org.eclipse.swt.events.MouseMoveListener;
22
import org.eclipse.swt.events.MouseWheelListener;
23
import org.eclipse.swt.events.PaintEvent;
24
import org.eclipse.swt.events.PaintListener;
25
import org.eclipse.swt.graphics.GC;
26
import org.eclipse.swt.widgets.Canvas;
27
import org.eclipse.swt.widgets.Display;
28
import org.eclipse.swt.widgets.Event;
29
import org.eclipse.swt.widgets.Listener;
30
import org.eclipse.swt.widgets.Shell;
31
32
public abstract class AbstractScaleRotateExample implements PaintListener,
33
		MouseWheelListener, MouseMoveListener, MouseListener, Listener {
34
35
	// TODO: The new angle interface is easier to use and should be used here!
36
37
	protected abstract class AbstractScaleRotateShape {
38
		private Canvas canvas;
39
		private Angle rotationAngle = Angle.fromDeg(0);
40
		private double zoomFactor = 1;
41
42
		public AbstractScaleRotateShape(Canvas c) {
43
			canvas = c;
44
		}
45
46
		public Canvas getCanvas() {
47
			return canvas;
48
		}
49
50
		public Angle getRotationAngle() {
51
			return rotationAngle;
52
		}
53
54
		public double getZoomFactor() {
55
			return zoomFactor;
56
		}
57
58
		public Point getCenter() {
59
			return new Point(canvas.getClientArea().width / 2,
60
					canvas.getClientArea().height / 2);
61
		}
62
63
		public abstract boolean contains(Point p);
64
65
		public abstract IGeometry createGeometry();
66
67
		public abstract void draw(GC gc);
68
	}
69
70
	private final int GEOMETRY_FILL_COLOR = SWT.COLOR_WHITE;
71
72
	private Shell shell;
73
	private AbstractScaleRotateShape shape;
74
	private Vector dragBegin;
75
	private Angle dragBeginAngle = Angle.fromDeg(0);
76
77
	/**
78
	 * 
79
	 */
80
	public AbstractScaleRotateExample(String title) {
81
		Display display = new Display();
82
		shell = new Shell(display, SWT.SHELL_TRIM | SWT.DOUBLE_BUFFERED);
83
		shell.setText(title);
84
85
		shell.setBounds(0, 0, 640, 480);
86
87
		shape = createShape(shell);
88
89
		shell.addPaintListener(this);
90
		shell.addMouseListener(this);
91
		shell.addMouseMoveListener(this);
92
		shell.addMouseWheelListener(this);
93
		shell.addListener(SWT.Resize, this);
94
		shell.open();
95
96
		while (!shell.isDisposed()) {
97
			if (!display.readAndDispatch()) {
98
				display.sleep();
99
			}
100
		}
101
	}
102
103
	protected abstract AbstractScaleRotateShape createShape(Canvas canvas);
104
105
	@Override
106
	public void paintControl(PaintEvent e) {
107
		e.gc.setAntialias(SWT.ON);
108
		e.gc.setBackground(Display.getCurrent().getSystemColor(
109
				GEOMETRY_FILL_COLOR));
110
		shape.draw(e.gc);
111
	}
112
113
	@Override
114
	public void mouseScrolled(MouseEvent e) {
115
		shape.zoomFactor += (double) e.count / 30;
116
		shell.redraw();
117
	}
118
119
	@Override
120
	public void mouseDown(MouseEvent e) {
121
		if (shape.contains(new Point(e.x, e.y))) {
122
			dragBegin = new Vector(shape.getCenter(), new Point(e.x, e.y));
123
		}// else {
124
			// dragBeginAngle = shape.rotationAngle;
125
			// dragBegin = null;
126
			// }
127
	}
128
129
	@Override
130
	public void mouseUp(MouseEvent e) {
131
		dragBeginAngle = shape.rotationAngle;
132
		dragBegin = null;
133
	}
134
135
	@Override
136
	public void mouseMove(MouseEvent e) {
137
		if (dragBegin != null) {
138
			Point center = shape.getCenter();
139
			Vector toMouse = new Vector(center, new Point(e.x, e.y));
140
			shape.rotationAngle = dragBegin.getAngleCW(toMouse).getAdded(
141
					dragBeginAngle);
142
			shell.redraw();
143
		}
144
	}
145
146
	@Override
147
	public void mouseDoubleClick(MouseEvent e) {
148
		shape.zoomFactor += 0.1;
149
		shell.redraw();
150
	}
151
152
	@Override
153
	public void handleEvent(Event e) {
154
		switch (e.type) {
155
		case SWT.Resize:
156
			shell.redraw();
157
			break;
158
		}
159
	}
160
}
(-)a/org.eclipse.gef4.geometry.examples/src/org/eclipse/gef4/geometry/examples/scalerotate/PolygonScaleRotate.java (+66 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2011 itemis AG and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthias Wienand (itemis AG) - initial API and implementation
10
 *     
11
 *******************************************************************************/
12
package org.eclipse.gef4.geometry.examples.scalerotate;
13
14
import org.eclipse.gef4.geometry.Point;
15
import org.eclipse.gef4.geometry.planar.Polygon;
16
import org.eclipse.swt.graphics.GC;
17
import org.eclipse.swt.widgets.Canvas;
18
19
public class PolygonScaleRotate extends AbstractScaleRotateExample {
20
21
	public static void main(String[] args) {
22
		new PolygonScaleRotate();
23
	}
24
25
	public PolygonScaleRotate() {
26
		super("Scale/Rotate - Polygon");
27
	}
28
29
	@Override
30
	protected AbstractScaleRotateShape createShape(Canvas canvas) {
31
		return new AbstractScaleRotateShape(canvas) {
32
			@Override
33
			public boolean contains(Point p) {
34
				return createGeometry().contains(p);
35
			}
36
37
			@Override
38
			public Polygon createGeometry() {
39
				double w = getCanvas().getClientArea().width;
40
				double h = getCanvas().getClientArea().height;
41
				double w2 = w / 2;
42
				double h2 = h / 2;
43
				double w5 = w / 5;
44
				double h5 = h / 5;
45
46
				Polygon me = new Polygon(new Point(w2 - w5, h2 - h5),
47
						new Point(w2 + w5, h2 - h5),
48
						new Point(w2 + w5, h2 + h5),
49
						new Point(w2 - w5, h2 + h5));
50
51
				me.rotateCW(getRotationAngle(), getCenter());
52
				me.scale(getZoomFactor(), getCenter());
53
54
				return me;
55
			}
56
57
			@Override
58
			public void draw(GC gc) {
59
				Polygon me = createGeometry();
60
				gc.drawPolygon(me.toSWTPointArray());
61
				gc.fillPolygon(me.toSWTPointArray());
62
			}
63
		};
64
	}
65
66
}
(-)a/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/CubicCurveTests.java (+5 lines)
Lines 190-193 public class CubicCurveTests { Link Here
190
		assertEquals(2, cc2.getIntersections(cc1).length);
190
		assertEquals(2, cc2.getIntersections(cc1).length);
191
	}
191
	}
192
192
193
	@Test
194
	public void test_getIntersections_containment() {
195
		// TODO
196
	}
197
193
}
198
}
(-)a/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/CurveUtilsTests.java (-45 / +247 lines)
Lines 16-26 import static org.junit.Assert.assertTrue; Link Here
16
16
17
import java.util.Random;
17
import java.util.Random;
18
18
19
import org.eclipse.gef4.geometry.Angle;
19
import org.eclipse.gef4.geometry.Point;
20
import org.eclipse.gef4.geometry.Point;
21
import org.eclipse.gef4.geometry.euclidean.Straight;
22
import org.eclipse.gef4.geometry.planar.BezierCurve;
20
import org.eclipse.gef4.geometry.planar.CubicCurve;
23
import org.eclipse.gef4.geometry.planar.CubicCurve;
24
import org.eclipse.gef4.geometry.planar.Line;
21
import org.eclipse.gef4.geometry.planar.QuadraticCurve;
25
import org.eclipse.gef4.geometry.planar.QuadraticCurve;
22
import org.eclipse.gef4.geometry.planar.Rectangle;
26
import org.eclipse.gef4.geometry.planar.Rectangle;
23
import org.eclipse.gef4.geometry.utils.CurveUtils;
24
import org.eclipse.gef4.geometry.utils.PrecisionUtils;
27
import org.eclipse.gef4.geometry.utils.PrecisionUtils;
25
import org.junit.Test;
28
import org.junit.Test;
26
29
Lines 32-66 public class CurveUtilsTests { Link Here
32
	public void test_getSignedDistance_direction() {
35
	public void test_getSignedDistance_direction() {
33
		// sign-checks:
36
		// sign-checks:
34
		// first quadrant (y-axis is inverted)
37
		// first quadrant (y-axis is inverted)
35
		assertTrue(CurveUtils.getSignedDistance(new Point(),
38
		assertTrue(Straight.getSignedDistanceCCW(new Point(),
36
				new Point(10, -10), new Point(0, -10)) > 0);
39
				new Point(10, -10), new Point(0, -10)) > 0);
37
		assertTrue(CurveUtils.getSignedDistance(new Point(10, -10),
40
		assertTrue(Straight.getSignedDistanceCCW(new Point(10, -10),
38
				new Point(), new Point(0, -10)) < 0);
41
				new Point(), new Point(0, -10)) < 0);
39
		assertTrue(CurveUtils.getSignedDistance(new Point(),
42
		assertTrue(Straight.getSignedDistanceCCW(new Point(),
40
				new Point(10, -10), new Point(1, -1)) == 0);
43
				new Point(10, -10), new Point(1, -1)) == 0);
41
44
42
		// second quadrant (y-axis is inverted)
45
		// second quadrant (y-axis is inverted)
43
		assertTrue(CurveUtils.getSignedDistance(new Point(),
46
		assertTrue(Straight.getSignedDistanceCCW(new Point(), new Point(-10,
44
				new Point(-10, -10), new Point(0, -10)) < 0);
47
				-10), new Point(0, -10)) < 0);
45
		assertTrue(CurveUtils.getSignedDistance(new Point(-10, -10),
48
		assertTrue(Straight.getSignedDistanceCCW(new Point(-10, -10),
46
				new Point(), new Point(0, -10)) > 0);
49
				new Point(), new Point(0, -10)) > 0);
47
		assertTrue(CurveUtils.getSignedDistance(new Point(),
50
		assertTrue(Straight.getSignedDistanceCCW(new Point(), new Point(-10,
48
				new Point(-10, -10), new Point(-1, -1)) == 0);
51
				-10), new Point(-1, -1)) == 0);
49
52
50
		// third quadrant (y-axis is inverted)
53
		// third quadrant (y-axis is inverted)
51
		assertTrue(CurveUtils.getSignedDistance(new Point(), new Point(10, 10),
54
		assertTrue(Straight.getSignedDistanceCCW(new Point(),
52
				new Point(0, 10)) < 0);
55
				new Point(10, 10), new Point(0, 10)) < 0);
53
		assertTrue(CurveUtils.getSignedDistance(new Point(10, 10), new Point(),
56
		assertTrue(Straight.getSignedDistanceCCW(new Point(10, 10),
54
				new Point(0, 10)) > 0);
57
				new Point(), new Point(0, 10)) > 0);
55
		assertTrue(CurveUtils.getSignedDistance(new Point(), new Point(10, 10),
58
		assertTrue(Straight.getSignedDistanceCCW(new Point(),
56
				new Point(1, 1)) == 0);
59
				new Point(10, 10), new Point(1, 1)) == 0);
57
60
58
		// forth quadrant (y-axis is inverted)
61
		// forth quadrant (y-axis is inverted)
59
		assertTrue(CurveUtils.getSignedDistance(new Point(),
62
		assertTrue(Straight.getSignedDistanceCCW(new Point(),
60
				new Point(-10, 10), new Point(0, 10)) > 0);
63
				new Point(-10, 10), new Point(0, 10)) > 0);
61
		assertTrue(CurveUtils.getSignedDistance(new Point(-10, 10),
64
		assertTrue(Straight.getSignedDistanceCCW(new Point(-10, 10),
62
				new Point(), new Point(0, 10)) < 0);
65
				new Point(), new Point(0, 10)) < 0);
63
		assertTrue(CurveUtils.getSignedDistance(new Point(),
66
		assertTrue(Straight.getSignedDistanceCCW(new Point(),
64
				new Point(-10, 10), new Point(-1, 1)) == 0);
67
				new Point(-10, 10), new Point(-1, 1)) == 0);
65
	}
68
	}
66
69
Lines 68-91 public class CurveUtilsTests { Link Here
68
	public void test_getSignedDistance_abs() {
71
	public void test_getSignedDistance_abs() {
69
		// do only check for the absolute value of the signed distance
72
		// do only check for the absolute value of the signed distance
70
73
71
		assertTrue(PrecisionUtils.equal(Math.abs(CurveUtils.getSignedDistance(
74
		assertTrue(PrecisionUtils.equal(Math.abs(Straight.getSignedDistanceCCW(
72
				new Point(0, -5), new Point(0, 5), new Point(5, 0))), 5));
75
				new Point(0, -5), new Point(0, 5), new Point(5, 0))), 5));
73
		assertTrue(PrecisionUtils.equal(Math.abs(CurveUtils.getSignedDistance(
76
		assertTrue(PrecisionUtils.equal(Math.abs(Straight.getSignedDistanceCCW(
74
				new Point(0, 5), new Point(0, -5), new Point(5, 0))), 5));
77
				new Point(0, 5), new Point(0, -5), new Point(5, 0))), 5));
75
78
76
		assertTrue(PrecisionUtils.equal(Math.abs(CurveUtils.getSignedDistance(
79
		assertTrue(PrecisionUtils.equal(Math.abs(Straight.getSignedDistanceCCW(
77
				new Point(0, -1), new Point(0, 1), new Point(5, 0))), 5));
80
				new Point(0, -1), new Point(0, 1), new Point(5, 0))), 5));
78
		assertTrue(PrecisionUtils.equal(Math.abs(CurveUtils.getSignedDistance(
81
		assertTrue(PrecisionUtils.equal(Math.abs(Straight.getSignedDistanceCCW(
79
				new Point(0, 1), new Point(0, -1), new Point(5, 0))), 5));
82
				new Point(0, 1), new Point(0, -1), new Point(5, 0))), 5));
80
83
81
		assertTrue(PrecisionUtils.equal(Math.abs(CurveUtils.getSignedDistance(
84
		assertTrue(PrecisionUtils.equal(Math.abs(Straight.getSignedDistanceCCW(
82
				new Point(-5, 0), new Point(5, 0), new Point(0, 5))), 5));
85
				new Point(-5, 0), new Point(5, 0), new Point(0, 5))), 5));
83
		assertTrue(PrecisionUtils.equal(Math.abs(CurveUtils.getSignedDistance(
86
		assertTrue(PrecisionUtils.equal(Math.abs(Straight.getSignedDistanceCCW(
84
				new Point(-5, 0), new Point(5, 0), new Point(0, 5))), 5));
87
				new Point(-5, 0), new Point(5, 0), new Point(0, 5))), 5));
85
88
86
		assertTrue(PrecisionUtils.equal(Math.abs(CurveUtils.getSignedDistance(
89
		assertTrue(PrecisionUtils.equal(Math.abs(Straight.getSignedDistanceCCW(
87
				new Point(-1, 0), new Point(1, 0), new Point(0, 5))), 5));
90
				new Point(-1, 0), new Point(1, 0), new Point(0, 5))), 5));
88
		assertTrue(PrecisionUtils.equal(Math.abs(CurveUtils.getSignedDistance(
91
		assertTrue(PrecisionUtils.equal(Math.abs(Straight.getSignedDistanceCCW(
89
				new Point(-1, 0), new Point(1, 0), new Point(0, 5))), 5));
92
				new Point(-1, 0), new Point(1, 0), new Point(0, 5))), 5));
90
	}
93
	}
91
94
Lines 94-128 public class CurveUtilsTests { Link Here
94
		// check both, direction and value:
97
		// check both, direction and value:
95
		// first quadrant (y-axis is inverted)
98
		// first quadrant (y-axis is inverted)
96
		double len = 10d / Math.sqrt(2);
99
		double len = 10d / Math.sqrt(2);
97
		assertTrue(PrecisionUtils.equal(CurveUtils.getSignedDistance(
100
		assertTrue(PrecisionUtils.equal(Straight.getSignedDistanceCCW(
98
				new Point(), new Point(10, -10), new Point(0, -10)), len));
101
				new Point(), new Point(10, -10), new Point(0, -10)), len));
99
		assertTrue(PrecisionUtils.equal(CurveUtils.getSignedDistance(new Point(
102
		assertTrue(PrecisionUtils.equal(Straight.getSignedDistanceCCW(
100
				10, -10), new Point(), new Point(0, -10)), -len));
103
				new Point(10, -10), new Point(), new Point(0, -10)), -len));
101
		assertTrue(PrecisionUtils.equal(CurveUtils.getSignedDistance(
104
		assertTrue(PrecisionUtils.equal(Straight.getSignedDistanceCCW(
102
				new Point(), new Point(10, -10), new Point(1, -1)), 0));
105
				new Point(), new Point(10, -10), new Point(1, -1)), 0));
103
106
104
		// second quadrant (y-axis is inverted)
107
		// second quadrant (y-axis is inverted)
105
		assertTrue(PrecisionUtils.equal(CurveUtils.getSignedDistance(
108
		assertTrue(PrecisionUtils.equal(Straight.getSignedDistanceCCW(
106
				new Point(), new Point(-10, -10), new Point(0, -10)), -len));
109
				new Point(), new Point(-10, -10), new Point(0, -10)), -len));
107
		assertTrue(PrecisionUtils.equal(CurveUtils.getSignedDistance(new Point(
110
		assertTrue(PrecisionUtils.equal(Straight.getSignedDistanceCCW(
108
				-10, -10), new Point(), new Point(0, -10)), len));
111
				new Point(-10, -10), new Point(), new Point(0, -10)), len));
109
		assertTrue(PrecisionUtils.equal(CurveUtils.getSignedDistance(
112
		assertTrue(PrecisionUtils.equal(Straight.getSignedDistanceCCW(
110
				new Point(), new Point(-10, -10), new Point(-1, -1)), 0));
113
				new Point(), new Point(-10, -10), new Point(-1, -1)), 0));
111
114
112
		// third quadrant (y-axis is inverted)
115
		// third quadrant (y-axis is inverted)
113
		assertTrue(PrecisionUtils.equal(CurveUtils.getSignedDistance(
116
		assertTrue(PrecisionUtils.equal(Straight.getSignedDistanceCCW(
114
				new Point(), new Point(10, 10), new Point(0, 10)), -len));
117
				new Point(), new Point(10, 10), new Point(0, 10)), -len));
115
		assertTrue(PrecisionUtils.equal(CurveUtils.getSignedDistance(new Point(
118
		assertTrue(PrecisionUtils.equal(Straight.getSignedDistanceCCW(
116
				10, 10), new Point(), new Point(0, 10)), len));
119
				new Point(10, 10), new Point(), new Point(0, 10)), len));
117
		assertTrue(PrecisionUtils.equal(CurveUtils.getSignedDistance(
120
		assertTrue(PrecisionUtils.equal(Straight.getSignedDistanceCCW(
118
				new Point(), new Point(10, 10), new Point(1, 1)), 0));
121
				new Point(), new Point(10, 10), new Point(1, 1)), 0));
119
122
120
		// forth quadrant (y-axis is inverted)
123
		// forth quadrant (y-axis is inverted)
121
		assertTrue(PrecisionUtils.equal(CurveUtils.getSignedDistance(
124
		assertTrue(PrecisionUtils.equal(Straight.getSignedDistanceCCW(
122
				new Point(), new Point(-10, 10), new Point(0, 10)), len));
125
				new Point(), new Point(-10, 10), new Point(0, 10)), len));
123
		assertTrue(PrecisionUtils.equal(CurveUtils.getSignedDistance(new Point(
126
		assertTrue(PrecisionUtils.equal(Straight.getSignedDistanceCCW(
124
				-10, 10), new Point(), new Point(0, 10)), -len));
127
				new Point(-10, 10), new Point(), new Point(0, 10)), -len));
125
		assertTrue(PrecisionUtils.equal(CurveUtils.getSignedDistance(
128
		assertTrue(PrecisionUtils.equal(Straight.getSignedDistanceCCW(
126
				new Point(), new Point(-10, 10), new Point(-1, 1)), 0));
129
				new Point(), new Point(-10, 10), new Point(-1, 1)), 0));
127
	}
130
	}
128
131
Lines 237-243 public class CurveUtilsTests { Link Here
237
240
238
			QuadraticCurve c = new QuadraticCurve(points);
241
			QuadraticCurve c = new QuadraticCurve(points);
239
			for (double t = 0; t <= 1; t += step) {
242
			for (double t = 0; t <= 1; t += step) {
240
				assertTrue(c.contains(c.get(t)));
243
				assertTrue("t = " + t, c.contains(c.get(t)));
241
			}
244
			}
242
		}
245
		}
243
	}
246
	}
Lines 253-258 public class CurveUtilsTests { Link Here
253
			Point[] points = new Point[numPoints];
256
			Point[] points = new Point[numPoints];
254
			for (int j = 0; j < numPoints; j++) {
257
			for (int j = 0; j < numPoints; j++) {
255
				points[j] = new Point(rng.nextDouble(), rng.nextDouble());
258
				points[j] = new Point(rng.nextDouble(), rng.nextDouble());
259
				assertTrue(!Double.isNaN(points[j].x));
260
				assertTrue(!Double.isNaN(points[j].y));
256
			}
261
			}
257
262
258
			CubicCurve c = new CubicCurve(points);
263
			CubicCurve c = new CubicCurve(points);
Lines 260-265 public class CurveUtilsTests { Link Here
260
				assertTrue(c.contains(c.get(t)));
265
				assertTrue(c.contains(c.get(t)));
261
			}
266
			}
262
		}
267
		}
268
269
		BezierCurve c = new BezierCurve(new Point(0.0, 0.0), new Point(0.05,
270
				0.1), new Point(0.05, 0.1), new Point(0.1, -0.1));
271
272
		assertTrue(!c.contains(new Point(0.1, 0.1)));
273
	}
274
275
	@Test
276
	public void test_getBounds() {
277
		Random rng = new Random(SEED);
278
279
		for (int i = 0; i < 1000; i++) {
280
			double w = Math.abs(rng.nextDouble()), h = Math.abs(rng
281
					.nextDouble());
282
			Rectangle controlBounds = new Rectangle(rng.nextDouble(),
283
					rng.nextDouble(), w, h);
284
285
			for (int n = 2; n < 10; n++) {
286
				Point[] points = new Point[n];
287
288
				points[0] = controlBounds.getBottomLeft();
289
				points[n - 1] = controlBounds.getBottomRight();
290
291
				if (n == 3) {
292
					points[1] = points[0].getTranslated(points[2]).getScaled(
293
							0.5);
294
				}
295
				if (n > 3) {
296
					double span = controlBounds.getWidth() / (n - 3);
297
					double d = 0;
298
					for (int j = 1; j < n - 1; j++, d += span) {
299
						points[j] = new Point(controlBounds.getX() + d,
300
								controlBounds.getY());
301
					}
302
				}
303
304
				BezierCurve c = new BezierCurve(points);
305
306
				// y maximum
307
				double d = controlBounds.getY() + controlBounds.getHeight()
308
						- c.get(0.5).y;
309
310
				Rectangle bounds = new Rectangle(controlBounds.getX(),
311
						controlBounds.getY() + controlBounds.getHeight() - d,
312
						controlBounds.getWidth(), d);
313
314
				assertEquals(bounds, c.getBounds());
315
316
				// x maximum
317
				controlBounds = controlBounds.getRotatedCCW(Angle.fromDeg(90),
318
						new Point()).getBounds();
319
				c.rotateCCW(Angle.fromDeg(90), new Point());
320
321
				d = controlBounds.getX() + controlBounds.getWidth()
322
						- c.get(0.5).x;
323
324
				bounds = new Rectangle(controlBounds.getX()
325
						+ controlBounds.getWidth() - d, controlBounds.getY(),
326
						d, controlBounds.getHeight());
327
328
				if (!bounds.equals(c.getBounds())) {
329
					System.out.println("DEBUG");
330
				}
331
332
				assertEquals(bounds, c.getBounds());
333
			}
334
		}
263
	}
335
	}
264
336
265
	@Test
337
	@Test
Lines 276-282 public class CurveUtilsTests { Link Here
276
			}
348
			}
277
349
278
			CubicCurve c = new CubicCurve(points);
350
			CubicCurve c = new CubicCurve(points);
279
			Rectangle bounds = CurveUtils.getControlBounds(c);
351
			Rectangle bounds = c.getControlBounds();
280
			for (double t = 0; t <= 1; t += step) {
352
			for (double t = 0; t <= 1; t += step) {
281
				assertTrue(bounds.contains(c.get(t)));
353
				assertTrue(bounds.contains(c.get(t)));
282
			}
354
			}
Lines 285-297 public class CurveUtilsTests { Link Here
285
	}
357
	}
286
358
287
	@Test
359
	@Test
288
	public void test_getIntersections_BezierClipping_simple() {
360
	public void test_getIntersections_overlapping() {
361
		// test case from
362
		// http://web.mit.edu/hyperbook/Patrikalakis-Maekawa-Cho/node111.html
363
		CubicCurve c1 = new CubicCurve(new double[] { 0, 0, 0.8, 0.8, 1.6,
364
				0.32, 2.4, 0.608 });
365
		CubicCurve c2 = new CubicCurve(new double[] { 0.6, 0.392, 1.4, 0.68,
366
				2.2, 0.2, 3, 1 });
367
368
		assertEquals(0, c1.getIntersections(c2).length);
369
		assertEquals(0, c2.getIntersections(c1).length);
370
371
		// c1 contains c2 / c2 contains c1
372
		c2 = c1.clip(0.25, 0.75);
373
		assertEquals(0, c1.getIntersections(c2).length);
374
		assertEquals(0, c2.getIntersections(c1).length);
375
376
		// single end-point-intersection
377
		c2 = new CubicCurve(new double[] { 2.4, 0.608, 3, 3, 3, 3, 4, 3 });
378
		assertEquals(1, c1.getIntersections(c2).length);
379
		assertEquals(1, c2.getIntersections(c1).length);
380
	}
381
382
	@Test
383
	public void test_getIntersections_simple() {
289
		CubicCurve c1 = new CubicCurve(new double[] { 100, 200, 200, 100, 300,
384
		CubicCurve c1 = new CubicCurve(new double[] { 100, 200, 200, 100, 300,
290
				300, 400, 200 });
385
				300, 400, 200 });
291
		CubicCurve c2 = new CubicCurve(new double[] { 250, 100, 350, 200, 150,
386
		CubicCurve c2 = new CubicCurve(new double[] { 250, 100, 350, 200, 150,
292
				300, 250, 400 });
387
				300, 250, 400 });
293
388
294
		assertEquals(1, c1.getIntersections(c2).length);
389
		assertEquals(1, c1.getIntersections(c2).length);
390
391
		c1 = new CubicCurve(new Point(201.89274447949526, 106.43015521064301),
392
				new Point(213.0, 325.0), new Point(387.0, 119.0), new Point(
393
						118.0, 310.0));
394
		Line l = new Line(new Point(105.66666666666667, 75.16666666666667),
395
				new Point(528.3333333333334, 375.8333333333333));
396
397
		assertEquals(1, c1.getIntersections(l).length);
398
399
		QuadraticCurve q1 = new QuadraticCurve(new Point(200, 50), new Point(
400
				210, 100), new Point(190, 150));
401
		l = new Line(new Point(100, 100), new Point(300, 100));
402
403
		assertEquals(1, q1.getIntersections(l).length);
404
405
		q1 = new QuadraticCurve(new Point(250, 50), new Point(200, 100),
406
				new Point(200, 150));
407
		l = new Line(new Point(528, 75), new Point(105, 75));
408
409
		assertEquals(1, q1.getIntersections(l).length);
410
411
		q1 = new QuadraticCurve(new Point(500, 50), new Point(300, 150),
412
				new Point(100, 300));
413
414
		assertEquals(1, q1.getIntersections(l).length);
415
416
		q1 = new QuadraticCurve(new Point(171, 409), new Point(302, 106),
417
				new Point(345, 310));
418
		l = new Line(new Point(105.66666666666667, 375.8333333333333),
419
				new Point(528.3333333333334, 375.8333333333333));
420
421
		Point[] testInters = q1.getIntersections(l);
422
		assertEquals(1, testInters.length);
295
	}
423
	}
296
424
425
	@Test
426
	public void test_getIntersections_linear() {
427
		BezierCurve yAxis = new BezierCurve(new Point(0, 0), new Point(1, 0));
428
429
		BezierCurve curve = new BezierCurve(new Point(0, -20), new Point(
430
				0.3333333333333333, -10.0), new Point(0.6666666666666666, 0.0),
431
				new Point(1, -10));
432
433
		assertEquals(0, yAxis.getIntersections(curve).length);
434
	}
435
436
	@Test
437
	public void test_getIntersections_random_containment() {
438
		final int numPoints = 8;
439
440
		Random rng = new Random(SEED);
441
442
		for (int i = 0; i < 1000; i++) {
443
			Point[] points = new Point[numPoints];
444
			for (int j = 0; j < numPoints; j++) {
445
				points[j] = new Point(rng.nextDouble(), rng.nextDouble());
446
				assertTrue(!Double.isNaN(points[j].x));
447
				assertTrue(!Double.isNaN(points[j].y));
448
			}
449
450
			BezierCurve c1 = new BezierCurve(points);
451
452
			for (int j = 0; j < numPoints; j++) {
453
				points[j] = new Point(rng.nextDouble(), rng.nextDouble());
454
				assertTrue(!Double.isNaN(points[j].x));
455
				assertTrue(!Double.isNaN(points[j].y));
456
			}
457
458
			BezierCurve c2 = new BezierCurve(points);
459
460
			for (Point poi : c1.getIntersections(c2)) {
461
				// if (!c1.contains(poi) || !c2.contains(poi)) {
462
				// System.out.println("DEBUG");
463
				// }
464
				// TODO: every failing test has to be sourced out into the
465
				// test_check_contains_difficult_cases() method.
466
				assertTrue(c1.contains(poi));
467
				assertTrue(c2.contains(poi));
468
			}
469
		}
470
	}
471
472
	@Test
473
	public void test_check_contains_difficult_cases() {
474
		BezierCurve c1 = new BezierCurve(new Point(0.5402658595791646,
475
				0.9741509829024984), new Point(0.16279154085195757,
476
				0.6904753002704389), new Point(0.5362586913177897,
477
				0.03544287335013263), new Point(0.34435494116180165,
478
				0.31041629374338775), new Point(0.3850664934271003,
479
				0.5238288178983336), new Point(0.13829366099352602,
480
				0.7410634269933081), new Point(0.8948987750498976,
481
				0.6198888125981984), new Point(0.11349279987471517,
482
				0.3388985501965609));
483
484
		BezierCurve c2 = new BezierCurve(new Point(0.3494484760769454,
485
				0.47795072857018706), new Point(0.9841912220562209,
486
				0.10765341979304721), new Point(0.27977429726230696,
487
				0.7303050467844633), new Point(0.28022390386455787,
488
				0.3313265057575542), new Point(0.3914004373221056,
489
				0.6451799723354514), new Point(0.2875477493472879,
490
				0.5132577259019093), new Point(0.13579952633028602,
491
				0.5665583087171101), new Point(0.09831516780965299,
492
				0.25959706254491044));
493
494
		Point p = new Point(0.3736012772933578, 0.4639965007739409);
495
496
		assertTrue(c1.contains(p));
497
		assertTrue(c2.contains(p));
498
	}
297
}
499
}
(-)a/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/EllipseTests.java (-9 / +98 lines)
Lines 6-16 Link Here
6
 * http://www.eclipse.org/legal/epl-v10.html
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
7
 *
8
 * Contributors:
8
 * Contributors:
9
 *     Alexander Nyßen (itemis AG) - initial API and implementation
9
 *     Alexander Nyßen (itemis AG) - initial API and implementation
10
 *     Matthias Wienand (itemis AG) - contribution for Bugzilla #355997
10
 *     
11
 *     
11
 *******************************************************************************/
12
 *******************************************************************************/
12
package org.eclipse.gef4.geometry.tests;
13
package org.eclipse.gef4.geometry.tests;
13
14
import static org.junit.Assert.assertEquals;
14
import static org.junit.Assert.assertEquals;
15
import static org.junit.Assert.assertFalse;
15
import static org.junit.Assert.assertFalse;
16
import static org.junit.Assert.assertTrue;
16
import static org.junit.Assert.assertTrue;
Lines 21-26 import org.eclipse.gef4.geometry.planar.Line; Link Here
21
import org.eclipse.gef4.geometry.planar.Rectangle;
21
import org.eclipse.gef4.geometry.planar.Rectangle;
22
import org.junit.Test;
22
import org.junit.Test;
23
23
24
24
/**
25
/**
25
 * Unit tests for {@link Ellipse}.
26
 * Unit tests for {@link Ellipse}.
26
 * 
27
 * 
Lines 37-49 public class EllipseTests { Link Here
37
		Rectangle r = new Rectangle(34.3435, 56.458945, 123.3098, 146.578);
38
		Rectangle r = new Rectangle(34.3435, 56.458945, 123.3098, 146.578);
38
		Ellipse e = new Ellipse(r);
39
		Ellipse e = new Ellipse(r);
39
40
40
		assertTrue(e.contains(r.getCenter()));
41
		assertTrue(e.contains(r.getCentroid()));
41
42
42
		assertTrue(e.contains(r.getLeft()));
43
		assertTrue(e.contains(r.getLeft()));
43
		assertTrue(e.contains(r.getLeft().getTranslated(
44
		assertTrue(e.contains(r.getLeft().getTranslated(PRECISION_FRACTION * 1,
44
				PRECISION_FRACTION * 100, 0)));
45
				0)));
45
		assertFalse(e.contains(r.getLeft().getTranslated(
46
		assertFalse(e.contains(r.getLeft().getTranslated(
46
				-PRECISION_FRACTION * 100, 0)));
47
				-PRECISION_FRACTION * 1000, 0)));
47
48
48
		assertTrue(e.contains(r.getTop()));
49
		assertTrue(e.contains(r.getTop()));
49
		assertTrue(e.contains(r.getTop().getTranslated(0,
50
		assertTrue(e.contains(r.getTop().getTranslated(0,
Lines 78-89 public class EllipseTests { Link Here
78
		Rectangle r = new Rectangle(34.3435, 56.458945, 123.3098, 146.578);
79
		Rectangle r = new Rectangle(34.3435, 56.458945, 123.3098, 146.578);
79
		Ellipse e = new Ellipse(r);
80
		Ellipse e = new Ellipse(r);
80
		for (Line l : r.getOutlineSegments()) {
81
		for (Line l : r.getOutlineSegments()) {
81
			assertTrue(e.intersects(l)); // line touches ellipse (tangent)
82
			assertTrue(e.touches(l)); // line touches ellipse (tangent)
82
		}
83
		}
83
	}
84
	}
84
85
85
	@Test
86
	@Test
86
	public void test_get_intersections_with_Ellipse() {
87
	public void test_get_intersections_with_Ellipse_strict() {
87
		Rectangle r = new Rectangle(34.3435, 56.458945, 123.3098, 146.578);
88
		Rectangle r = new Rectangle(34.3435, 56.458945, 123.3098, 146.578);
88
		Ellipse e1 = new Ellipse(r);
89
		Ellipse e1 = new Ellipse(r);
89
		Ellipse e2 = new Ellipse(r);
90
		Ellipse e2 = new Ellipse(r);
Lines 97-102 public class EllipseTests { Link Here
97
		Rectangle r2 = r.getExpanded(0, -10, -10, -10);
98
		Rectangle r2 = r.getExpanded(0, -10, -10, -10);
98
		e2 = new Ellipse(r2);
99
		e2 = new Ellipse(r2);
99
		intersections = e1.getIntersections(e2);
100
		intersections = e1.getIntersections(e2);
101
		for (Point poi : intersections) {
102
			assertTrue(e1.contains(poi));
103
			assertTrue(e2.contains(poi));
104
		}
100
		assertEquals(1, intersections.length);
105
		assertEquals(1, intersections.length);
101
106
102
		// if we create an x-scaled ellipse at the same position as before, they
107
		// if we create an x-scaled ellipse at the same position as before, they
Lines 173-178 public class EllipseTests { Link Here
173
				1, equalsRight);
178
				1, equalsRight);
174
	}
179
	}
175
180
181
	private void intersectionsTolerance(Ellipse e1, Ellipse e2,
182
			Point... expected) {
183
		Point[] intersections = e1.getIntersections(e2);
184
		boolean[] foundExpected = new boolean[expected.length];
185
		for (Point poi : intersections) {
186
			assertTrue(
187
					"All points of intersection have to be contained by the first ellipse.",
188
					e1.contains(poi));
189
			assertTrue(
190
					"All points of intersection have to be contained by the second ellipse.",
191
					e2.contains(poi));
192
			for (int i = 0; i < expected.length; i++) {
193
				if (poi.equals(expected[i])) {
194
					foundExpected[i] = true;
195
				}
196
			}
197
		}
198
		for (int i = 0; i < expected.length; i++) {
199
			assertTrue("An expected point of intersection " + expected[i]
200
					+ " not found in the list of intersections.",
201
					foundExpected[i]);
202
		}
203
	}
204
205
	@Test
206
	public void test_getIntersections_with_Ellipse_tolerance() {
207
		Rectangle r = new Rectangle(34.3435, 56.458945, 123.3098, 146.578);
208
		Ellipse e1 = new Ellipse(r);
209
		Ellipse e2 = new Ellipse(r);
210
211
		// ellipses are identical = returns no intersections, user can check
212
		// this via equals()
213
		Point[] intersections = e1.getIntersections(e2);
214
		assertEquals(0, intersections.length);
215
216
		// touching left
217
		Rectangle r2 = r.getExpanded(0, -10, -10, -10);
218
		e2 = new Ellipse(r2);
219
		intersectionsTolerance(e1, e2, r.getLeft());
220
221
		// if we create an x-scaled ellipse at the same position as before, they
222
		// should have 3 poi (the touching point and two crossing intersections)
223
		r2 = r.getExpanded(0, 0, 100, 0);
224
		e2 = new Ellipse(r2);
225
		intersectionsTolerance(e1, e2, r.getLeft()); // TODO: other two pois
226
227
		// if we create a y-scaled ellipse at the same position as before, they
228
		// should have 3 poi (the touching point and two crossing intersections)
229
		r2 = r.getExpanded(0, 0, 0, 100);
230
		e2 = new Ellipse(r2);
231
		intersectionsTolerance(e1, e2, r.getTop()); // TODO: other two pois
232
233
		// if we create an x-scaled ellipse at the same y-position as before,
234
		// the two should touch at two positions:
235
		r2 = r.getExpanded(50, 0, 50, 0);
236
		e2 = new Ellipse(r2);
237
		intersectionsTolerance(e1, e2, r.getTop(), r.getBottom());
238
239
		// if we create a y-scaled ellipse at the same x-position as before, the
240
		// two should touch at two positions:
241
		r2 = r.getExpanded(0, 50, 0, 50);
242
		e2 = new Ellipse(r2);
243
		intersectionsTolerance(e1, e2, r.getLeft(), r.getRight());
244
	}
245
246
	// @Ignore("This test is too strict. For a liberal test see below: test_getIntersections_with_Ellipse_Bezier_special_tolerance")
176
	@Test
247
	@Test
177
	public void test_getIntersections_with_Ellipse_Bezier_special() {
248
	public void test_getIntersections_with_Ellipse_Bezier_special() {
178
		// 3 nearly tangential intersections
249
		// 3 nearly tangential intersections
Lines 181-187 public class EllipseTests { Link Here
181
		assertEquals(2, e1.getIntersections(e2).length);
252
		assertEquals(2, e1.getIntersections(e2).length);
182
253
183
		e2 = new Ellipse(133, 90, 2 * (315 - 133), 200);
254
		e2 = new Ellipse(133, 90, 2 * (315 - 133), 200);
184
		assertEquals(3, e1.getIntersections(e2).length);
255
		Point[] intersections = e1.getIntersections(e2);
256
		assertEquals(3, intersections.length);
185
257
186
		e2 = new Ellipse(143, 90, 2 * (315 - 143), 200);
258
		e2 = new Ellipse(143, 90, 2 * (315 - 143), 200);
187
		assertEquals(3, e1.getIntersections(e2).length);
259
		assertEquals(3, e1.getIntersections(e2).length);
Lines 190-193 public class EllipseTests { Link Here
190
		assertEquals(3, e1.getIntersections(e2).length);
262
		assertEquals(3, e1.getIntersections(e2).length);
191
	}
263
	}
192
264
265
	@Test
266
	public void test_getIntersections_with_Ellipse_Bezier_special_tolerance() {
267
		// 3 nearly tangential intersections
268
		Ellipse e1 = new Ellipse(126, 90, 378, 270);
269
		Ellipse e2 = new Ellipse(222, 77, 200, 200);
270
		intersectionsTolerance(e1, e2); // TODO: find out the 2 expected points
271
272
		e2 = new Ellipse(133, 90, 2 * (315 - 133), 200);
273
		intersectionsTolerance(e1, e2); // TODO: find out the 3 expected points
274
275
		e2 = new Ellipse(143, 90, 2 * (315 - 143), 200);
276
		intersectionsTolerance(e1, e2); // TODO: find out the 3 expected points
277
278
		e2 = new Ellipse(145, 90, 2 * (315 - 145), 200);
279
		intersectionsTolerance(e1, e2); // TODO: find out the 3 expected points
280
	}
281
193
}
282
}
(-)a/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/LineTests.java (-27 / +117 lines)
Lines 19-24 import static org.junit.Assert.assertTrue; Link Here
19
19
20
import org.eclipse.gef4.geometry.Point;
20
import org.eclipse.gef4.geometry.Point;
21
import org.eclipse.gef4.geometry.euclidean.Straight;
21
import org.eclipse.gef4.geometry.euclidean.Straight;
22
import org.eclipse.gef4.geometry.euclidean.Vector;
22
import org.eclipse.gef4.geometry.planar.Line;
23
import org.eclipse.gef4.geometry.planar.Line;
23
import org.eclipse.gef4.geometry.planar.Rectangle;
24
import org.eclipse.gef4.geometry.planar.Rectangle;
24
import org.eclipse.gef4.geometry.utils.PrecisionUtils;
25
import org.eclipse.gef4.geometry.utils.PrecisionUtils;
Lines 63-75 public class LineTests { Link Here
63
	}
64
	}
64
65
65
	@Test
66
	@Test
66
	public void test_contains_Rect() {
67
		assertFalse(new Line(0, 0, 5, 0).contains(new Rectangle()));
68
		assertFalse(new Line(0, 0, 5, 0).contains(new Rectangle(0, 0, 5, 0)));
69
		assertFalse(new Line(0, 0, 5, 0).contains(new Rectangle(1, 1, 1, 1)));
70
	}
71
72
	@Test
73
	public void test_copy() {
67
	public void test_copy() {
74
		Line l1 = new Line(0, 0, 5, 0);
68
		Line l1 = new Line(0, 0, 5, 0);
75
		assertTrue(l1.equals(l1.getCopy()));
69
		assertTrue(l1.equals(l1.getCopy()));
Lines 131-137 public class LineTests { Link Here
131
		// simple intersection
125
		// simple intersection
132
		Line l1 = new Line(0, 0, 4, 4);
126
		Line l1 = new Line(0, 0, 4, 4);
133
		Line l2 = new Line(0, 4, 4, 0);
127
		Line l2 = new Line(0, 4, 4, 0);
134
		assertTrue(l1.intersects(l2));
128
		assertTrue(l1.touches(l2));
135
		assertTrue(l1.getIntersection(l2).equals(new Point(2, 2)));
129
		assertTrue(l1.getIntersection(l2).equals(new Point(2, 2)));
136
		assertTrue(l2.getIntersection(l1).equals(new Point(2, 2)));
130
		assertTrue(l2.getIntersection(l1).equals(new Point(2, 2)));
137
131
Lines 167-179 public class LineTests { Link Here
167
	}
161
	}
168
162
169
	@Test
163
	@Test
164
	public void test_intersects_specials() {
165
		// degenerated cases
166
		Line degen = new Line(new Point(), new Point());
167
		Line normal = new Line(new Point(-5, 0), new Point(5, 0));
168
		assertTrue(degen.touches(normal));
169
		assertTrue(normal.touches(degen));
170
171
		// identical
172
		assertTrue(normal.touches(normal));
173
174
		// intersection within precision. no real intersection
175
		Line close = new Line(new Point(-5, UNRECOGNIZABLE_FRACTION),
176
				new Point(5, UNRECOGNIZABLE_FRACTION));
177
		assertTrue(normal.touches(close));
178
		assertTrue(close.touches(normal));
179
180
		Line closeSp = new Line(new Point(-5, UNRECOGNIZABLE_FRACTION),
181
				new Point(-5, 10));
182
		assertTrue(normal.touches(closeSp));
183
		assertTrue(closeSp.touches(normal));
184
185
		Line closeEp = new Line(new Point(-5, 10), new Point(-5,
186
				UNRECOGNIZABLE_FRACTION));
187
		assertTrue(normal.touches(closeEp));
188
		assertTrue(closeEp.touches(normal));
189
190
		// intersection within precision, straights do intersect too, but the
191
		// intersection of the straights is out of precision
192
		Line slope = new Line(new Point(-5, UNRECOGNIZABLE_FRACTION),
193
				new Point(5, 2 * UNRECOGNIZABLE_FRACTION));
194
		assertTrue(normal.touches(slope));
195
		assertTrue(slope.touches(normal));
196
197
		// no intersection, straights do intersect
198
		Line elsewhere = new Line(new Point(-5, 1), new Point(5, 10));
199
		assertTrue(!normal.touches(elsewhere));
200
		assertTrue(!elsewhere.touches(normal));
201
202
		// big lines, imprecisely parallel but intersecting
203
		Line bigX = new Line(new Point(-1000, 0), new Point(1000, 0));
204
		Line impreciselyParallel = new Line(new Point(-1000,
205
				-UNRECOGNIZABLE_FRACTION), new Point(1000,
206
				UNRECOGNIZABLE_FRACTION));
207
		assertTrue(new Vector(bigX.getP1(), bigX.getP2())
208
				.isParallelTo(new Vector(impreciselyParallel.getP1(),
209
						impreciselyParallel.getP2())));
210
		assertTrue(bigX.touches(impreciselyParallel));
211
	}
212
213
	@Test
214
	public void test_getIntersection_specials() {
215
		// degenerated cases
216
		Line degen = new Line(new Point(), new Point());
217
		Line normal = new Line(new Point(-5, 0), new Point(5, 0));
218
		assertEquals(new Point(), degen.getIntersection(normal));
219
		assertEquals(new Point(), normal.getIntersection(degen));
220
221
		// identical
222
		assertNull(normal.getIntersection(normal));
223
224
		// intersection within precision, no real intersection
225
		Line close = new Line(new Point(-5, UNRECOGNIZABLE_FRACTION),
226
				new Point(5, UNRECOGNIZABLE_FRACTION));
227
		// parallel so we do not return an intersection point
228
		assertNull(normal.getIntersection(close));
229
		assertNull(close.getIntersection(normal));
230
231
		// non parallel, start point intersection
232
		Line closeSp = new Line(new Point(-5, UNRECOGNIZABLE_FRACTION),
233
				new Point(-5, 10));
234
		assertEquals(new Point(-5, 0), normal.getIntersection(closeSp));
235
		assertEquals(new Point(-5, 0), closeSp.getIntersection(normal));
236
237
		// non parallel, end point intersection
238
		Line closeEp = new Line(new Point(-5, 10), new Point(-5,
239
				UNRECOGNIZABLE_FRACTION));
240
		assertEquals(new Point(-5, 0), normal.getIntersection(closeEp));
241
		assertEquals(new Point(-5, 0), closeEp.getIntersection(normal));
242
243
		// intersection within precision, straights do intersect too, but the
244
		// intersection of the straights is out of precision
245
		Line slope = new Line(new Point(-5, UNRECOGNIZABLE_FRACTION),
246
				new Point(5, 2 * UNRECOGNIZABLE_FRACTION));
247
248
		// no point of intersection can be identified, because both endpoints
249
		// lie on the line. is is assumed to be parallel.
250
		assertNull(normal.getIntersection(slope));
251
		assertNull(slope.getIntersection(normal));
252
253
		// no intersection, straights do intersect
254
		Line elsewhere = new Line(new Point(-5, 1), new Point(5, 10));
255
		assertNull(normal.getIntersection(elsewhere));
256
		assertNull(elsewhere.getIntersection(normal));
257
	}
258
259
	@Test
170
	public void test_getters() {
260
	public void test_getters() {
171
		for (double x1 = -2; x1 <= 2; x1 += 0.2) {
261
		for (double x1 = -2; x1 <= 2; x1 += 0.5) {
172
			for (double y1 = -2; y1 <= 2; y1 += 0.2) {
262
			for (double y1 = -2; y1 <= 2; y1 += 0.5) {
173
				Point p1 = new Point(x1, y1);
263
				Point p1 = new Point(x1, y1);
174
264
175
				for (double x2 = -2; x2 <= 2; x2 += 0.2) {
265
				for (double x2 = -2; x2 <= 2; x2 += 0.5) {
176
					for (double y2 = -2; y2 <= 2; y2 += 0.2) {
266
					for (double y2 = -2; y2 <= 2; y2 += 0.5) {
177
						Point p2 = new Point(x2, y2);
267
						Point p2 = new Point(x2, y2);
178
						Line line = new Line(p1, p2);
268
						Line line = new Line(p1, p2);
179
						assertTrue(line.getP1().equals(p1));
269
						assertTrue(line.getP1().equals(p1));
Lines 214-258 public class LineTests { Link Here
214
		// simple intersection
304
		// simple intersection
215
		Line l1 = new Line(0, 0, 4, 4);
305
		Line l1 = new Line(0, 0, 4, 4);
216
		Line l2 = new Line(0, 4, 4, 0);
306
		Line l2 = new Line(0, 4, 4, 0);
217
		assertTrue(l1.intersects(l2));
307
		assertTrue(l1.touches(l2));
218
		assertTrue(l2.intersects(l1));
308
		assertTrue(l2.touches(l1));
219
309
220
		// lines touch in one point
310
		// lines touch in one point
221
		Line l3 = new Line(4, 4, 7, 9);
311
		Line l3 = new Line(4, 4, 7, 9);
222
		assertTrue(l1.intersects(l3));
312
		assertTrue(l1.touches(l3));
223
		assertTrue(l3.intersects(l1));
313
		assertTrue(l3.touches(l1));
224
314
225
		// lines overlap
315
		// lines overlap
226
		Line l4 = new Line(2, 2, 6, 6);
316
		Line l4 = new Line(2, 2, 6, 6);
227
		assertTrue(l1.intersects(l4));
317
		assertTrue(l1.touches(l4));
228
		assertTrue(l4.intersects(l1));
318
		assertTrue(l4.touches(l1));
229
319
230
		// one line is a single point
320
		// one line is a single point
231
		Line l5 = new Line(1, 1, 1, 1);
321
		Line l5 = new Line(1, 1, 1, 1);
232
		assertTrue(l5.intersects(l1));
322
		assertTrue(l5.touches(l1));
233
		assertTrue(l1.intersects(l5));
323
		assertTrue(l1.touches(l5));
234
324
235
		// straights would intersect, but these lines do not
325
		// straights would intersect, but these lines do not
236
		Line l6 = new Line(4, 0, 5, 4);
326
		Line l6 = new Line(4, 0, 5, 4);
237
		assertFalse(l6.intersects(l1));
327
		assertFalse(l6.touches(l1));
238
		assertFalse(l1.intersects(l6));
328
		assertFalse(l1.touches(l6));
239
	}
329
	}
240
330
241
	@Test
331
	@Test
242
	public void test_intersects_with_Rect() {
332
	public void test_intersects_with_Rect() {
243
		Line l1 = new Line(0, 0, 4, 4);
333
		Line l1 = new Line(0, 0, 4, 4);
244
		Rectangle r1 = new Rectangle(0, 4, 4, 4);
334
		Rectangle r1 = new Rectangle(0, 4, 4, 4);
245
		assertTrue(l1.intersects(r1));
335
		assertTrue(l1.touches(r1));
246
	}
336
	}
247
337
248
	@Test
338
	@Test
249
	public void test_setters() {
339
	public void test_setters() {
250
		for (double x1 = -2; x1 <= 2; x1 += 0.2) {
340
		for (double x1 = -2; x1 <= 2; x1 += 0.5) {
251
			for (double y1 = -2; y1 <= 2; y1 += 0.2) {
341
			for (double y1 = -2; y1 <= 2; y1 += 0.5) {
252
				Point p1 = new Point(x1, y1);
342
				Point p1 = new Point(x1, y1);
253
343
254
				for (double x2 = -2; x2 <= 2; x2 += 0.2) {
344
				for (double x2 = -2; x2 <= 2; x2 += 0.5) {
255
					for (double y2 = -2; y2 <= 2; y2 += 0.2) {
345
					for (double y2 = -2; y2 <= 2; y2 += 0.5) {
256
						Point p2 = new Point(x2, y2);
346
						Point p2 = new Point(x2, y2);
257
347
258
						Line line = new Line(new Point(-5, -5), new Point(-10,
348
						Line line = new Line(new Point(-5, -5), new Point(-10,
(-)a/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/PointListUtilsTests.java (-1 / +1 lines)
Lines 54-60 public class PointListUtilsTests { Link Here
54
			points[i] = new Point(i * i, i + i);
54
			points[i] = new Point(i * i, i + i);
55
		}
55
		}
56
56
57
		Point[] copy = PointListUtils.getCopy(points);
57
		Point[] copy = PointListUtils.copy(points);
58
58
59
		for (int i = 0; i < 10; i++) {
59
		for (int i = 0; i < 10; i++) {
60
			assertTrue(points[i].equals(copy[i]));
60
			assertTrue(points[i].equals(copy[i]));
(-)a/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/PolygonTests.java (-42 / +85 lines)
Lines 388-422 public class PolygonTests { Link Here
388
388
389
	@Test
389
	@Test
390
	public void test_getIntersections_Ellipse() {
390
	public void test_getIntersections_Ellipse() {
391
		assertTrue(new Polygon(RHOMB.getIntersections(new Ellipse(0, 0, 4, 4)))
391
		assertTrue(new Polygon(RHOMB.getOutline().getIntersections(
392
				.getBounds().equals(RHOMB.getBounds()));
392
				new Ellipse(0, 0, 4, 4).getOutline())).getBounds().equals(
393
				RHOMB.getBounds()));
393
	}
394
	}
394
395
395
	@Test
396
	@Test
396
	public void test_getIntersections_Polygon() {
397
	public void test_getIntersections_Polygon() {
397
		// TODO: something's wrong... debug this test
398
		assertEquals(
398
		assertEquals(2, RHOMB.getIntersections(RECTANGLE).length);
399
				2,
399
		assertEquals(4,
400
				RHOMB.getOutline().getIntersections(RECTANGLE.getOutline()).length);
400
				RHOMB.getIntersections(RHOMB.getBounds().toPolygon()).length);
401
		assertEquals(
402
				4,
403
				RHOMB.getOutline().getIntersections(
404
						RHOMB.getBounds().toPolygon().getOutline()).length);
401
	}
405
	}
402
406
403
	@Test
407
	@Test
404
	public void test_getIntersections_Polyline() {
408
	public void test_getIntersections_Polyline() {
405
		assertEquals(1, RHOMB.getIntersections(new Polyline(0, 0, 0, 4)).length);
409
		assertEquals(
406
		assertEquals(2,
410
				1,
407
				RHOMB.getIntersections(new Polyline(0, 0, 0, 4, 4, 4)).length);
411
				RHOMB.getOutline().getIntersections(new Polyline(0, 0, 0, 4)).length);
412
		assertEquals(
413
				2,
414
				RHOMB.getOutline().getIntersections(
415
						new Polyline(0, 0, 0, 4, 4, 4)).length);
408
		assertEquals(
416
		assertEquals(
409
				3,
417
				3,
410
				RHOMB.getIntersections(new Polyline(0, 0, 0, 4, 4, 4, 2, 2)).length);
418
				RHOMB.getOutline().getIntersections(
411
		assertEquals(4, RHOMB.getIntersections(new Polyline(0, 0, 0, 4, 4, 4,
419
						new Polyline(0, 0, 0, 4, 4, 4, 2, 2)).length);
412
				2, 2, 4, 0)).length);
420
		assertEquals(
413
		assertEquals(5, RHOMB.getIntersections(new Polyline(0, 0, 0, 4, 4, 4,
421
				4,
414
				2, 2, 4, 0, 0, 0)).length);
422
				RHOMB.getOutline().getIntersections(
423
						new Polyline(0, 0, 0, 4, 4, 4, 2, 2, 4, 0)).length);
424
		assertEquals(
425
				5,
426
				RHOMB.getOutline().getIntersections(
427
						new Polyline(0, 0, 0, 4, 4, 4, 2, 2, 4, 0, 0, 0)).length);
415
	}
428
	}
416
429
417
	@Test
430
	@Test
418
	public void test_getIntersections_Rectangle() {
431
	public void test_getIntersections_Rectangle() {
419
		assertEquals(4, RHOMB.getIntersections(RHOMB.getBounds()).length);
432
		assertEquals(
433
				4,
434
				RHOMB.getOutline().getIntersections(
435
						RHOMB.getBounds().getOutline()).length);
420
	}
436
	}
421
437
422
	@Test
438
	@Test
Lines 481-556 public class PolygonTests { Link Here
481
497
482
	@Test
498
	@Test
483
	public void test_intersects_Ellipse() {
499
	public void test_intersects_Ellipse() {
484
		assertTrue(RHOMB.intersects(new Ellipse(0, 0, 4, 4)));
500
		assertTrue(RHOMB.touches(new Ellipse(0, 0, 4, 4)));
485
	}
501
	}
486
502
487
	@Test
503
	@Test
488
	public void test_intersects_Line() {
504
	public void test_intersects_Line() {
489
		assertFalse(RHOMB.intersects(new Line(-1, 1, 1, -1)));
505
		assertFalse(RHOMB.touches(new Line(-1, 1, 1, -1)));
490
		assertTrue(RHOMB.intersects(new Line(-1, 2, 2, 2)));
506
		assertTrue(RHOMB.touches(new Line(-1, 2, 2, 2)));
491
		assertTrue(RHOMB.intersects(new Line(2, 2, 5, 2)));
507
		assertTrue(RHOMB.touches(new Line(2, 2, 5, 2)));
492
		assertTrue(RHOMB.intersects(new Line(0, 2, 2, 0)));
508
		assertTrue(RHOMB.touches(new Line(0, 2, 2, 0)));
493
		assertTrue(RHOMB.intersects(new Line(0, 2, 2, 4)));
509
		assertTrue(RHOMB.touches(new Line(0, 2, 2, 4)));
494
		assertTrue(RHOMB.intersects(new Line(0, 2, 2, 2)));
510
		assertTrue(RHOMB.touches(new Line(0, 2, 2, 2)));
495
		assertTrue(RHOMB.intersects(new Line(1, 2, 3, 2)));
511
		assertTrue(RHOMB.touches(new Line(1, 2, 3, 2)));
496
		assertTrue(new Polygon(new Point(), new Point(0, 5), new Point(5, 5),
512
		assertTrue(new Polygon(new Point(), new Point(0, 5), new Point(5, 5),
497
				new Point(5, 0), new Point(2.5, 2.5)).intersects(new Line(1,
513
				new Point(5, 0), new Point(2.5, 2.5)).touches(new Line(1, 2.5,
498
				2.5, 4, 2.5)));
514
				4, 2.5)));
499
		assertTrue(RHOMB.intersects(new Line(-1, 2, 5, 2)));
515
		assertTrue(RHOMB.touches(new Line(-1, 2, 5, 2)));
500
	}
516
	}
501
517
502
	@Test
518
	@Test
503
	public void test_intersects_Polygon_Rhomb() {
519
	public void test_intersects_Polygon_Rhomb() {
504
		assertTrue(
520
		assertTrue(
505
				"The rhomb intersects itself, because it touches/contains itself",
521
				"The rhomb intersects itself, because it touches/contains itself",
506
				RHOMB.intersects(RHOMB));
522
				RHOMB.touches(RHOMB));
507
523
508
		assertTrue(
524
		assertTrue(
509
				"The rhomb intersects its shrinked self, because its shrinked self is fully contained by the rhomb",
525
				"The rhomb intersects its shrinked self, because its shrinked self is fully contained by the rhomb",
510
				RHOMB.intersects(RHOMB.getCopy().scale(0.5, new Point(2, 2))));
526
				RHOMB.touches(RHOMB.getCopy().scale(0.5, new Point(2, 2))));
511
527
512
		assertTrue(
528
		assertTrue(
513
				"The rhomb intersects its expanded self, because its expanded self fully contains the rhomb",
529
				"The rhomb intersects its expanded self, because its expanded self fully contains the rhomb",
514
				RHOMB.intersects(RHOMB.getCopy().scale(2, new Point(2, 2))));
530
				RHOMB.touches(RHOMB.getCopy().scale(2, new Point(2, 2))));
515
531
516
		assertTrue(RHOMB.contains(new Point(4, 2)));
532
		assertTrue(RHOMB.contains(new Point(4, 2)));
517
		assertTrue(RHOMB.getTranslated(new Point(4, 0)).contains(
533
		assertTrue(RHOMB.getTranslated(new Point(4, 0)).contains(
518
				new Point(4, 2)));
534
				new Point(4, 2)));
519
535
520
		assertTrue("The rhomb touches the given one",
536
		assertTrue("The rhomb touches the given one",
521
				RHOMB.intersects(RHOMB.getTranslated(4, 0)));
537
				RHOMB.touches(RHOMB.getTranslated(4, 0)));
522
		assertTrue("The rhomb intersects the given one",
538
		assertTrue("The rhomb intersects the given one",
523
				RHOMB.intersects(RHOMB.getTranslated(2, 0)));
539
				RHOMB.touches(RHOMB.getTranslated(2, 0)));
524
	}
540
	}
525
541
526
	@Test
542
	@Test
527
	public void test_intersects_Polyline() {
543
	public void test_intersects_Polyline() {
528
		assertFalse(RHOMB.intersects(new Polyline(0, 0, -1, -1)));
544
		assertFalse(RHOMB.touches(new Polyline(0, 0, -1, -1)));
529
		assertTrue(RHOMB.intersects(new Polyline(0, 0, 0, 4)));
545
		assertTrue(RHOMB.touches(new Polyline(0, 0, 0, 4)));
530
		assertTrue(RHOMB.intersects(new Polyline(0, 0, 0, 4, 4, 4)));
546
		assertTrue(RHOMB.touches(new Polyline(0, 0, 0, 4, 4, 4)));
531
		assertTrue(RHOMB.intersects(new Polyline(0, 0, 0, 4, 4, 4, 2, 2)));
547
		assertTrue(RHOMB.touches(new Polyline(0, 0, 0, 4, 4, 4, 2, 2)));
532
		assertTrue(RHOMB.intersects(new Polyline(0, 0, 0, 4, 4, 4, 2, 2, 4, 0)));
548
		assertTrue(RHOMB.touches(new Polyline(0, 0, 0, 4, 4, 4, 2, 2, 4, 0)));
533
		assertTrue(RHOMB.intersects(new Polyline(0, 0, 0, 4, 4, 4, 2, 2, 4, 0,
549
		assertTrue(RHOMB.touches(new Polyline(0, 0, 0, 4, 4, 4, 2, 2, 4, 0, 0,
534
				0, 0)));
550
				0)));
535
	}
551
	}
536
552
537
	@Test
553
	@Test
538
	public void test_intersects_Rectangle_Rhomb() {
554
	public void test_intersects_Rectangle_Rhomb() {
539
		assertTrue(
555
		assertTrue(
540
				"This rectangle is inside the rhomb and does therefore intersect it",
556
				"This rectangle is inside the rhomb and does therefore intersect it",
541
				RHOMB.intersects(new Rectangle(1.5, 1.5, 1, 1)));
557
				RHOMB.touches(new Rectangle(1.5, 1.5, 1, 1)));
542
		assertTrue(
558
		assertTrue(
543
				"This rectangle is inside the rhomb and does therefore intersect it",
559
				"This rectangle is inside the rhomb and does therefore intersect it",
544
				RHOMB.intersects(new Rectangle(1, 1, 2, 2)));
560
				RHOMB.touches(new Rectangle(1, 1, 2, 2)));
545
		assertTrue(
561
		assertTrue(
546
				"This rectangle is partly outside the rhomb and intersects it (intersection points are two polygon points)",
562
				"This rectangle is partly outside the rhomb and intersects it (intersection points are two polygon points)",
547
				RHOMB.intersects(new Rectangle(0, 0, 2, 2)));
563
				RHOMB.touches(new Rectangle(0, 0, 2, 2)));
548
564
549
		assertTrue(RHOMB.contains(new Point(0, 2)));
565
		assertTrue(RHOMB.contains(new Point(0, 2)));
550
		assertTrue(new Rectangle(-2, 0, 2, 2).contains(new Point(0, 2)));
566
		assertTrue(new Rectangle(-2, 0, 2, 2).contains(new Point(0, 2)));
551
		assertTrue(
567
		assertTrue(
552
				"This rectangle is outside the rhomb and touches it in Point (0,2), which is contained in both",
568
				"This rectangle is outside the rhomb and touches it in Point (0,2), which is contained in both",
553
				RHOMB.intersects(new Rectangle(-2, 0, 2, 2)));
569
				RHOMB.touches(new Rectangle(-2, 0, 2, 2)));
554
	}
570
	}
555
571
556
	@Test
572
	@Test
Lines 594-597 public class PolygonTests { Link Here
594
		}
610
		}
595
	}
611
	}
596
612
613
	@Test
614
	public void test_contains_imprecision() {
615
		Polygon poly = new Polygon(new Point(0.16384889386958243,
616
				0.5199137157713366), new Point(0.16388083282075672,
617
				0.5199518598437528), new Point(0.1639056804775328,
618
				0.5199687901987595), new Point(0.16381011945655763,
619
				0.5198551130149273));
620
		Point p = new Point(0.16383865075635337, 0.5198962222767928);
621
		assertTrue(poly.contains(p));
622
	}
623
624
	@Test
625
	public void test_contains_Point_special_cases() {
626
		/*
627
		 * TODO: special cases are impossible to test without knowing how the
628
		 * algorithm counts intersections. The special cases are:
629
		 * 
630
		 * 1) the point is inside the polygon. the scan line intersects the
631
		 * polygon in a vertex of the polygon. this is a double intersection in
632
		 * the same point.
633
		 * 
634
		 * 2) the point is inside the polygon. the scan line intersects the
635
		 * polygon in a vertex of the polygon and somewhere else. the vertex
636
		 * intersection is a double intersection in the same point.
637
		 */
638
	}
639
597
}
640
}
(-)a/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/RectangleTests.java (-22 / +45 lines)
Lines 169-176 public class RectangleTests { Link Here
169
169
170
		// test self containment
170
		// test self containment
171
		assertTrue(preciseRect.contains(preciseRect));
171
		assertTrue(preciseRect.contains(preciseRect));
172
		assertTrue(preciseRect.contains(preciseRect.getX(), preciseRect.getY(),
173
				preciseRect.getWidth(), preciseRect.getHeight()));
174
		assertFalse(preciseRect.contains(preciseRect.getExpanded(
172
		assertFalse(preciseRect.contains(preciseRect.getExpanded(
175
				RECOGNIZABLE_FRACTION, RECOGNIZABLE_FRACTION)));
173
				RECOGNIZABLE_FRACTION, RECOGNIZABLE_FRACTION)));
176
174
Lines 317-328 public class RectangleTests { Link Here
317
		assertTrue(PrecisionUtils.equal(25.92755905511811, rect.getHeight()));
315
		assertTrue(PrecisionUtils.equal(25.92755905511811, rect.getHeight()));
318
316
319
		rect = new Rectangle(-9.486614173228347, -34.431496062992125,
317
		rect = new Rectangle(-9.486614173228347, -34.431496062992125,
320
				41.99055118110236, 25.92755905511811).getScaled(2, 0);
318
				2 * 9.486614173228347, 34.431496062992125).getScaled(2, 0);
321
319
322
		assertTrue(PrecisionUtils.equal(-9.486614173228347, rect.getX()));
320
		assertTrue(PrecisionUtils.equal(2 * -9.486614173228347, rect.getX()));
323
		assertTrue(PrecisionUtils.equal(-34.431496062992125, rect.getY()));
321
		assertTrue(PrecisionUtils.equal(0.5 * -34.431496062992125, rect.getY()));
324
		assertTrue(PrecisionUtils.equal(2 * 41.99055118110236, rect.getWidth()));
322
		assertTrue(PrecisionUtils.equal(4 * 9.486614173228347, rect.getWidth()));
325
		assertTrue(PrecisionUtils.equal(0, rect.getHeight()));
323
		assertTrue(PrecisionUtils.equal(0, rect.getHeight()));
324
325
		// TODO: is this the desired behavior?
326
		// assertTrue(PrecisionUtils.equal(-9.486614173228347, rect.getX()));
327
		// assertTrue(PrecisionUtils.equal(-34.431496062992125, rect.getY()));
328
		// assertTrue(PrecisionUtils.equal(2 * 41.99055118110236,
329
		// rect.getWidth()));
330
		// assertTrue(PrecisionUtils.equal(0, rect.getHeight()));
326
	}
331
	}
327
332
328
	@Test
333
	@Test
Lines 435-461 public class RectangleTests { Link Here
435
		Rectangle r1 = new Rectangle(-5, -5, 10, 10);
440
		Rectangle r1 = new Rectangle(-5, -5, 10, 10);
436
441
437
		for (Line seg : r1.getOutlineSegments()) {
442
		for (Line seg : r1.getOutlineSegments()) {
438
			assertTrue(r1.intersects(seg));
443
			assertTrue(r1.touches(seg));
439
		}
444
		}
440
445
441
		assertTrue(r1
446
		assertTrue(r1.touches(new Line(r1.getTopLeft(), r1.getBottomRight())));
442
				.intersects(new Line(r1.getTopLeft(), r1.getBottomRight())));
447
		assertTrue(r1.touches(new Line(r1.getTop(), r1.getBottom())));
443
		assertTrue(r1.intersects(new Line(r1.getTop(), r1.getBottom())));
444
448
445
		assertTrue(r1.intersects(new Line(-10, 0, 10, 0)));
449
		assertTrue(r1.touches(new Line(-10, 0, 10, 0)));
446
		assertFalse(r1.intersects(new Line(-10, 0, -6, 0)));
450
		assertFalse(r1.touches(new Line(-10, 0, -6, 0)));
447
		assertFalse(r1.intersects(new Line(0, -10, 0, -6)));
451
		assertFalse(r1.touches(new Line(0, -10, 0, -6)));
448
		assertFalse(r1.intersects(new Line(10, 0, 6, 0)));
452
		assertFalse(r1.touches(new Line(10, 0, 6, 0)));
449
		assertFalse(r1.intersects(new Line(0, 10, 0, 6)));
453
		assertFalse(r1.touches(new Line(0, 10, 0, 6)));
450
	}
454
	}
451
455
452
	@Test
456
	@Test
453
	public void test_intersects_with_Rectangle() {
457
	public void test_intersects_with_Rectangle() {
454
		forRectanglePairs(new IPairAction() {
458
		assertTrue(new Rectangle(0, 0, 100, 100).touches(new Rectangle(0, 0,
455
			public void action(Rectangle r1, Rectangle r2) {
459
				100, 100)));
456
				assertTrue(r1.intersects(r2) != r1.getIntersected(r2).isEmpty());
460
		assertTrue(new Rectangle(0, 0, 100, 100).touches(new Rectangle(50, 50,
457
			}
461
				100, 100)));
458
		});
462
		assertTrue(new Rectangle(0, 0, 100, 100).touches(new Rectangle(100,
463
				100, 100, 100)));
464
		assertTrue(new Rectangle(0, 0, 100, 100).touches(new Rectangle(-100,
465
				-100, 100, 100)));
466
		assertTrue(new Rectangle(0, 0, 100, 100).touches(new Rectangle(-50, 0,
467
				100, 100)));
468
		assertTrue(new Rectangle(0, 0, 100, 100).touches(new Rectangle(-100, 0,
469
				100, 100)));
470
		assertTrue(new Rectangle(0, 0, 100, 100).touches(new Rectangle(50, 0,
471
				100, 100)));
472
		assertTrue(new Rectangle(0, 0, 100, 100).touches(new Rectangle(100, 0,
473
				100, 100)));
474
		assertTrue(new Rectangle(0, 0, 100, 100).touches(new Rectangle(0, -50,
475
				100, 100)));
476
		assertTrue(new Rectangle(0, 0, 100, 100).touches(new Rectangle(0, -100,
477
				100, 100)));
478
		assertTrue(new Rectangle(0, 0, 100, 100).touches(new Rectangle(0, 50,
479
				100, 100)));
480
		assertTrue(new Rectangle(0, 0, 100, 100).touches(new Rectangle(0, 100,
481
				100, 100)));
459
	}
482
	}
460
483
461
	@Test
484
	@Test
Lines 489-495 public class RectangleTests { Link Here
489
		Rectangle rect4 = new Rectangle(0, 0, 10, 10);
512
		Rectangle rect4 = new Rectangle(0, 0, 10, 10);
490
		assertEquals(rect3, rect4);
513
		assertEquals(rect3, rect4);
491
514
492
		// TODO: how about negative width/height?
515
		// negative width/height?
493
		assertEquals(new Rectangle(), new Rectangle(0, 0, -10, -10));
516
		assertEquals(new Rectangle(), new Rectangle(0, 0, -10, -10));
494
		assertEquals(new Rectangle(5, 5, 0, 10), new Rectangle(5, 5, -10, 10));
517
		assertEquals(new Rectangle(5, 5, 0, 10), new Rectangle(5, 5, -10, 10));
495
		assertEquals(new Rectangle(5, 5, 10, 0), new Rectangle(5, 5, 10, -10));
518
		assertEquals(new Rectangle(5, 5, 10, 0), new Rectangle(5, 5, 10, -10));
Lines 674-680 public class RectangleTests { Link Here
674
				assertEquals(bl, rect.getBottomLeft());
697
				assertEquals(bl, rect.getBottomLeft());
675
				assertEquals(bo, rect.getBottom());
698
				assertEquals(bo, rect.getBottom());
676
				assertEquals(br, rect.getBottomRight());
699
				assertEquals(br, rect.getBottomRight());
677
				assertEquals(ce, rect.getCenter());
700
				assertEquals(ce, rect.getCentroid());
678
			}
701
			}
679
		});
702
		});
680
	}
703
	}
(-)a/org.eclipse.gef4.geometry.tests/src/org/eclipse/gef4/geometry/tests/VectorTests.java (-15 / +14 lines)
Lines 212-224 public class VectorTests { Link Here
212
212
213
		// TODO: normalize the vectors first, so that they get comparable.
213
		// TODO: normalize the vectors first, so that they get comparable.
214
214
215
		// the description of the method is mistakable:
215
		/*
216
		// 1) does it mean that an angle of 180° returns the same dissimilarity
216
		 * the description of the method is mistakable:
217
		// as an angle of 0°
217
		 * 
218
		// 2) or does it mean that an angle of 180° returns the highest
218
		 * 1) does it mean that an angle of 180 degrees returns the same
219
		// dissimilarity?
219
		 * dissimilarity as an angle of 0 degrees?
220
		//
220
		 * 
221
		// the following code expects the first case
221
		 * 2) or does it mean that an angle of 180 degrees returns the highest
222
		 * dissimilarity?
223
		 * 
224
		 * the following code expects the first case
225
		 */
222
226
223
		forVectorPairs(new VectorPairAction() {
227
		forVectorPairs(new VectorPairAction() {
224
			@Override
228
			@Override
Lines 387-400 public class VectorTests { Link Here
387
		forVectorPairs(new VectorPairAction() {
391
		forVectorPairs(new VectorPairAction() {
388
			@Override
392
			@Override
389
			public void action(Vector a, Vector b) {
393
			public void action(Vector a, Vector b) {
390
				Angle alpha = a.getAngle(b);
394
				// TODO: rewrite this test!
391
				alpha.setRad(alpha.rad() * 2d);
395
				assertTrue(a.isParallelTo(b) == PrecisionUtils.equal(
392
396
						a.getDissimilarity(b), 0));
393
				// DEBUG
394
				// if (a.isParallelTo(b) != alpha.equals(Angle.fromRad(0)))
395
				// throw new IllegalStateException("");
396
397
				assertTrue(a.isParallelTo(b) == alpha.equals(Angle.fromRad(0)));
398
			}
397
			}
399
		});
398
		});
400
	}
399
	}
(-)a/org.eclipse.gef4.geometry/README.txt (-1 / +10 lines)
Lines 1-6 Link Here
1
Differences to Draw2d Geometry API
1
Differences to Draw2d Geometry API
2
2
3
3
1) only double precision, with imprecise relations
4
1) only double precision, with imprecise relations
4
2) scale only affects size, not location
5
6
7
2) scaling is performed relative to the geometries center, not the coordinate system origin
8
9
To get the old behavior back, you can use: scale(x, y, new Point());
10
11
5
3) Rectangle contains the right and bottom border
12
3) Rectangle contains the right and bottom border
13
14
6
4) Polygon and Polyline instead of PointList
15
4) Polygon and Polyline instead of PointList
(-)a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/Point.java (-15 / +52 lines)
Lines 33-48 public class Point implements Cloneable, Serializable { Link Here
33
	private static final long serialVersionUID = 1L;
33
	private static final long serialVersionUID = 1L;
34
34
35
	/**
35
	/**
36
	 * The x value.
37
	 */
38
	public double x;
39
40
	/**
41
	 * The y value.
42
	 */
43
	public double y;
44
45
	/**
46
	 * Creates a new Point representing the MAX of two provided Points.
36
	 * Creates a new Point representing the MAX of two provided Points.
47
	 * 
37
	 * 
48
	 * @param p1
38
	 * @param p1
Lines 69-74 public class Point implements Cloneable, Serializable { Link Here
69
	}
59
	}
70
60
71
	/**
61
	/**
62
	 * The x value.
63
	 */
64
	public double x;
65
66
	/**
67
	 * The y value.
68
	 */
69
	public double y;
70
71
	/**
72
	 * Constructs a Point at location (0,0).
72
	 * Constructs a Point at location (0,0).
73
	 * 
73
	 * 
74
	 */
74
	 */
Lines 204-216 public class Point implements Cloneable, Serializable { Link Here
204
	}
204
	}
205
205
206
	/**
206
	/**
207
	 * Creates a new SWT {@link org.eclipse.swt.graphics.Point Point} from this
207
	 * Returns a new {@link Point} scaled by the given scale-factors. The
208
	 * Point.
208
	 * scaling is performed relative to the given {@link Point} center.
209
	 * 
209
	 * 
210
	 * @return A new SWT Point
210
	 * @param factorX
211
	 *            The horizontal scale-factor
212
	 * @param factorY
213
	 *            The vertical scale-factor
214
	 * @param center
215
	 *            The relative {@link Point} for the scaling
216
	 * @return The new, scaled {@link Point}
211
	 */
217
	 */
212
	public org.eclipse.swt.graphics.Point toSWTPoint() {
218
	public Point getScaled(double factorX, double factorY, Point center) {
213
		return new org.eclipse.swt.graphics.Point((int) x, (int) y);
219
		return getTranslated(center.getNegated()).scale(factorX, factorY)
220
				.translate(center);
214
	}
221
	}
215
222
216
	/**
223
	/**
Lines 307-312 public class Point implements Cloneable, Serializable { Link Here
307
	}
314
	}
308
315
309
	/**
316
	/**
317
	 * Scales this {@link Point} by the given scale-factors. The scaling is
318
	 * performed relative to the given {@link Point} center.
319
	 * 
320
	 * @param factorX
321
	 *            The horizontal scale-factor
322
	 * @param factorY
323
	 *            The vertical scale-factor
324
	 * @param center
325
	 *            The relative {@link Point} for the scaling
326
	 * @return <code>this</code> for convenience
327
	 */
328
	public Point scale(double factorX, double factorY, Point center) {
329
		translate(center.getNegated());
330
		scale(factorX, factorY);
331
		translate(center);
332
		return this;
333
	}
334
335
	/**
310
	 * Sets the location of this Point to the provided x and y locations.
336
	 * Sets the location of this Point to the provided x and y locations.
311
	 * 
337
	 * 
312
	 * @return <code>this</code> for convenience
338
	 * @return <code>this</code> for convenience
Lines 367-372 public class Point implements Cloneable, Serializable { Link Here
367
	}
393
	}
368
394
369
	/**
395
	/**
396
	 * Creates a new SWT {@link org.eclipse.swt.graphics.Point Point} from this
397
	 * Point.
398
	 * 
399
	 * @return A new SWT Point
400
	 */
401
	public org.eclipse.swt.graphics.Point toSWTPoint() {
402
		return new org.eclipse.swt.graphics.Point((int) x, (int) y);
403
	}
404
405
	/**
370
	 * Shifts this Point by the values of the Dimension along each axis, and
406
	 * Shifts this Point by the values of the Dimension along each axis, and
371
	 * returns this for convenience.
407
	 * returns this for convenience.
372
	 * 
408
	 * 
Lines 435-438 public class Point implements Cloneable, Serializable { Link Here
435
	public double y() {
471
	public double y() {
436
		return y;
472
		return y;
437
	}
473
	}
474
438
}
475
}
(-)a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/euclidean/Straight.java (-22 / +74 lines)
Lines 17-23 import java.io.Serializable; Link Here
17
17
18
import org.eclipse.gef4.geometry.Angle;
18
import org.eclipse.gef4.geometry.Angle;
19
import org.eclipse.gef4.geometry.Point;
19
import org.eclipse.gef4.geometry.Point;
20
import org.eclipse.gef4.geometry.utils.CurveUtils;
20
import org.eclipse.gef4.geometry.planar.Line;
21
import org.eclipse.gef4.geometry.projective.Straight3D;
22
import org.eclipse.gef4.geometry.projective.Vector3D;
21
import org.eclipse.gef4.geometry.utils.PrecisionUtils;
23
import org.eclipse.gef4.geometry.utils.PrecisionUtils;
22
24
23
/**
25
/**
Lines 58-63 public class Straight implements Cloneable, Serializable { Link Here
58
		this(new Vector(point1), new Vector(point1, point2));
60
		this(new Vector(point1), new Vector(point1, point2));
59
	}
61
	}
60
62
63
	/**
64
	 * Constructs a new {@link Straight} through the start and end {@link Point}
65
	 * of the given {@link Line}.
66
	 * 
67
	 * @param line
68
	 */
69
	public Straight(Line line) {
70
		this(line.getP1(), line.getP2());
71
	}
72
61
	@Override
73
	@Override
62
	public Straight clone() {
74
	public Straight clone() {
63
		return getCopy();
75
		return getCopy();
Lines 74-80 public class Straight implements Cloneable, Serializable { Link Here
74
	 */
86
	 */
75
	public boolean intersects(Straight other) {
87
	public boolean intersects(Straight other) {
76
		return !PrecisionUtils.equal(direction.getDotProduct(other.direction
88
		return !PrecisionUtils.equal(direction.getDotProduct(other.direction
77
				.getOrthogonalComplement()), 0);
89
				.getOrthogonalComplement()), 0, +6);
78
	}
90
	}
79
91
80
	/**
92
	/**
Lines 128-138 public class Straight implements Cloneable, Serializable { Link Here
128
	 *         if no intersection point exists (or the Straights are equal).
140
	 *         if no intersection point exists (or the Straights are equal).
129
	 */
141
	 */
130
	public Vector getIntersection(Straight other) {
142
	public Vector getIntersection(Straight other) {
131
		Point poi = CurveUtils.getIntersection(this, other);
143
		Vector3D l1 = new Vector3D(this.position.toPoint())
132
		if (poi != null) {
144
				.getCrossed(new Vector3D(this.position.getAdded(this.direction)
133
			return new Vector(poi);
145
						.toPoint()));
134
		}
146
		Vector3D l2 = new Vector3D(other.position.toPoint())
135
		return null;
147
				.getCrossed(new Vector3D(other.position.getAdded(
148
						other.direction).toPoint()));
149
150
		Point poi = l1.getCrossed(l2).toPoint();
151
152
		return poi == null ? null : new Vector(poi);
136
	}
153
	}
137
154
138
	/**
155
	/**
Lines 232-251 public class Straight implements Cloneable, Serializable { Link Here
232
	 * @return the signed distance of the given {@link Vector} to this Straight
249
	 * @return the signed distance of the given {@link Vector} to this Straight
233
	 */
250
	 */
234
	public double getSignedDistanceCCW(Vector vector) {
251
	public double getSignedDistanceCCW(Vector vector) {
235
		Vector projected = getProjection(vector);
252
		// TODO: check which implementation is better
236
		Vector d = vector.getSubtracted(projected);
253
237
254
		return Straight.getSignedDistanceCCW(this.position.toPoint(),
238
		double len = d.getLength();
255
				this.position.getAdded(this.direction).toPoint(),
239
256
				vector.toPoint());
240
		if (!d.isNull()) {
257
241
			Angle angleCW = direction.getAngleCW(d);
258
		// Vector projected = getProjection(vector);
242
259
		// Vector d = vector.getSubtracted(projected);
243
			if (angleCW.equals(Angle.fromDeg(90))) {
260
		//
244
				len = -len;
261
		// double len = d.getLength();
245
			}
262
		//
246
		}
263
		// if (!d.isNull()) {
247
264
		// Angle angleCW = direction.getAngleCW(d);
248
		return len;
265
		//
266
		// if (angleCW.equals(Angle.fromDeg(90))) {
267
		// len = -len;
268
		// }
269
		// }
270
		//
271
		// return len;
249
	}
272
	}
250
273
251
	/**
274
	/**
Lines 280-286 public class Straight implements Cloneable, Serializable { Link Here
280
		if (direction.y != 0) {
303
		if (direction.y != 0) {
281
			return (p.y - position.y) / direction.y;
304
			return (p.y - position.y) / direction.y;
282
		}
305
		}
283
		return 0;
306
		return 0; // never get here
284
	}
307
	}
285
308
286
	/**
309
	/**
Lines 419-422 public class Straight implements Cloneable, Serializable { Link Here
419
		return new Straight(position, direction);
442
		return new Straight(position, direction);
420
	}
443
	}
421
444
445
	/**
446
	 * Computes the signed distance of the third {@link Point} to the line
447
	 * through the first two {@link Point}s.
448
	 * 
449
	 * The signed distance is positive if the three {@link Point}s are in
450
	 * counter-clockwise order and negative if the {@link Point}s are in
451
	 * clockwise order. It is zero if the third {@link Point} lies on the line.
452
	 * 
453
	 * If the first two {@link Point}s do not form a line (i.e. they are equal)
454
	 * this function returns the distance of the first and the last
455
	 * {@link Point}.
456
	 * 
457
	 * @param p
458
	 *            the start-{@link Point} of the line
459
	 * @param q
460
	 *            the end-{@link Point} of the line
461
	 * @param r
462
	 *            the relative {@link Point} to test for
463
	 * @return the signed distance of {@link Point} r to the line through
464
	 *         {@link Point}s p and q
465
	 */
466
	public static double getSignedDistanceCCW(Point p, Point q, Point r) {
467
		Straight3D line = Straight3D.through(new Vector3D(p), new Vector3D(q));
468
		if (line == null) {
469
			return 0d;
470
		}
471
		return -line.getSignedDistanceCW(new Vector3D(r));
472
	}
473
422
}
474
}
(-)a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/euclidean/Vector.java (-2 / +4 lines)
Lines 113-119 public class Vector implements Cloneable, Serializable { Link Here
113
113
114
	/**
114
	/**
115
	 * Calculates the magnitude of the cross product of this {@link Vector} with
115
	 * Calculates the magnitude of the cross product of this {@link Vector} with
116
	 * another. Normalized the {@link Vector}s before calculating the cross
116
	 * another. Normalizes the {@link Vector}s before calculating the cross
117
	 * product. Represents the amount by which two {@link Vector}s are
117
	 * product. Represents the amount by which two {@link Vector}s are
118
	 * directionally different. Parallel {@link Vector}s return a value of 0.
118
	 * directionally different. Parallel {@link Vector}s return a value of 0.
119
	 * 
119
	 * 
Lines 135-141 public class Vector implements Cloneable, Serializable { Link Here
135
	 *         otherwise.
135
	 *         otherwise.
136
	 */
136
	 */
137
	public boolean isParallelTo(Vector other) {
137
	public boolean isParallelTo(Vector other) {
138
		return PrecisionUtils.equal(getDissimilarity(other), 0);
138
		Angle alpha = getAngle(other);
139
		alpha.setRad(2d * alpha.rad());
140
		return alpha.equals(Angle.fromRad(0d));
139
	}
141
	}
140
142
141
	/**
143
	/**
(-)a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/AbstractGeometry.java (-2 / +31 lines)
Lines 22-28 abstract class AbstractGeometry implements IGeometry { Link Here
22
	 * {@link Cloneable}.
22
	 * {@link Cloneable}.
23
	 */
23
	 */
24
	@Override
24
	@Override
25
	public final Object clone() {
25
	public Object clone() {
26
		return getCopy();
26
		return getCopy();
27
	}
27
	}
28
28
Lines 44-50 abstract class AbstractGeometry implements IGeometry { Link Here
44
	 * @return a transformed {@link Path} representation of this
44
	 * @return a transformed {@link Path} representation of this
45
	 *         {@link IGeometry}
45
	 *         {@link IGeometry}
46
	 */
46
	 */
47
	public IGeometry getTransformed(AffineTransform t) {
47
	public IGeometry getTransformed(final AffineTransform t) {
48
		return toPath().getTransformed(t);
48
		return toPath().getTransformed(t);
49
	}
49
	}
50
51
	public boolean touches(final IGeometry g) {
52
		if (this instanceof ICurve) {
53
			if (g instanceof ICurve) {
54
				return ((ICurve) this).intersects((ICurve) g)
55
						|| ((ICurve) this).overlaps((ICurve) g);
56
			} else if (g instanceof IShape) {
57
				return ((IShape) g).contains((ICurve) this)
58
						|| this.touches(((IShape) g).getOutline());
59
			} else {
60
				throw new UnsupportedOperationException("Not yet implemented.");
61
			}
62
		} else if (this instanceof IShape) {
63
			if (g instanceof ICurve) {
64
				return ((IShape) this).contains((ICurve) g)
65
						|| ((IShape) this).getOutline().touches((ICurve) g);
66
			} else if (g instanceof IShape) {
67
				return ((IShape) this).contains((IShape) g)
68
						|| ((IShape) g).contains((IShape) this)
69
						|| ((IShape) this).getOutline().touches(
70
								((IShape) g).getOutline());
71
			} else {
72
				throw new UnsupportedOperationException("Not yet implemented.");
73
			}
74
		} else {
75
			throw new UnsupportedOperationException("Not yet implemented.");
76
		}
77
	}
78
50
}
79
}
(-)a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/AbstractPointListBasedGeometry.java (-6 / +326 lines)
Lines 7-29 Link Here
7
 *
7
 *
8
 * Contributors:
8
 * Contributors:
9
 *     Alexander Nyßen (itemis AG) - initial API and implementation
9
 *     Alexander Nyßen (itemis AG) - initial API and implementation
10
 *     Matthias Wienand (itemis AG) - contribution for Bugzilla #355997
10
 *     
11
 *     
11
 *******************************************************************************/
12
 *******************************************************************************/
12
package org.eclipse.gef4.geometry.planar;
13
package org.eclipse.gef4.geometry.planar;
13
14
15
import org.eclipse.gef4.geometry.Angle;
14
import org.eclipse.gef4.geometry.Point;
16
import org.eclipse.gef4.geometry.Point;
17
import org.eclipse.gef4.geometry.euclidean.Vector;
15
import org.eclipse.gef4.geometry.utils.PointListUtils;
18
import org.eclipse.gef4.geometry.utils.PointListUtils;
16
19
17
abstract class AbstractPointListBasedGeometry extends AbstractGeometry {
20
abstract class AbstractPointListBasedGeometry<T extends AbstractPointListBasedGeometry<?>>
21
		extends AbstractGeometry {
18
22
19
	private static final long serialVersionUID = 1L;
23
	private static final long serialVersionUID = 1L;
20
24
21
	Point[] points;
25
	Point[] points;
22
26
23
	public AbstractPointListBasedGeometry(Point... points) {
24
		this.points = PointListUtils.getCopy(points);
25
	}
26
27
	public AbstractPointListBasedGeometry(double... coordinates) {
27
	public AbstractPointListBasedGeometry(double... coordinates) {
28
		points = new Point[coordinates.length / 2];
28
		points = new Point[coordinates.length / 2];
29
		for (int i = 0; i < coordinates.length / 2; i++) {
29
		for (int i = 0; i < coordinates.length / 2; i++) {
Lines 31-36 abstract class AbstractPointListBasedGeometry extends AbstractGeometry { Link Here
31
		}
31
		}
32
	}
32
	}
33
33
34
	public AbstractPointListBasedGeometry(Point... points) {
35
		this.points = PointListUtils.copy(points);
36
	}
37
34
	/**
38
	/**
35
	 * @see IGeometry#getBounds()
39
	 * @see IGeometry#getBounds()
36
	 */
40
	 */
Lines 39-44 abstract class AbstractPointListBasedGeometry extends AbstractGeometry { Link Here
39
	}
43
	}
40
44
41
	/**
45
	/**
46
	 * Computes the centroid of this {@link AbstractPointListBasedGeometry}. The
47
	 * centroid is the "center of gravity", i.e. assuming the {@link Polygon}
48
	 * which is spanned by the {@link Point}s of this
49
	 * {@link AbstractPointListBasedGeometry} is made of a material of constant
50
	 * density, it is in a balanced state, if you put it on a pin that is placed
51
	 * exactly on its centroid.
52
	 * 
53
	 * @return the center {@link Point} (or centroid) of this
54
	 *         {@link AbstractPointListBasedGeometry}
55
	 */
56
	public Point getCentroid() {
57
		if (points.length == 0) {
58
			return null;
59
		} else if (points.length == 1) {
60
			return points[0].getCopy();
61
		}
62
63
		double cx = 0, cy = 0, a, sa = 0;
64
		for (int i = 0; i < points.length - 1; i++) {
65
			a = points[i].x * points[i + 1].y - points[i].y * points[i + 1].x;
66
			sa += a;
67
			cx += (points[i].x + points[i + 1].x) * a;
68
			cy += (points[i].y + points[i + 1].y) * a;
69
		}
70
71
		// closing segment
72
		a = points[points.length - 2].x * points[points.length - 1].y
73
				- points[points.length - 2].y * points[points.length - 1].x;
74
		sa += a;
75
		cx += (points[points.length - 2].x + points[points.length - 1].x) * a;
76
		cy += (points[points.length - 2].x + points[points.length - 1].x) * a;
77
78
		return new Point(cx / (3 * sa), cy / (3 * sa));
79
	}
80
81
	/**
42
	 * Returns a double array, which represents the sequence of coordinates of
82
	 * Returns a double array, which represents the sequence of coordinates of
43
	 * the {@link Point}s that make up this {@link Polygon}.
83
	 * the {@link Point}s that make up this {@link Polygon}.
44
	 * 
84
	 * 
Lines 58-64 abstract class AbstractPointListBasedGeometry extends AbstractGeometry { Link Here
58
	 *         this {@link Polygon}
98
	 *         this {@link Polygon}
59
	 */
99
	 */
60
	public final Point[] getPoints() {
100
	public final Point[] getPoints() {
61
		return PointListUtils.getCopy(points);
101
		return PointListUtils.copy(points);
102
	}
103
104
	/**
105
	 * Returns a new {@link AbstractPointListBasedGeometry} which is rotated
106
	 * counter-clock-wise by the given {@link Angle} around its centroid (see
107
	 * {@link #getCentroid()}).
108
	 * 
109
	 * @see #getCopy()
110
	 * @see #getRotatedCCW(Angle, Point)
111
	 * @param alpha
112
	 *            The rotation {@link Angle}
113
	 * @return The new rotated {@link AbstractPointListBasedGeometry}
114
	 */
115
	public T getRotatedCCW(Angle alpha) {
116
		return getRotatedCCW(alpha, getCentroid());
117
	}
118
119
	/**
120
	 * Returns a new {@link AbstractPointListBasedGeometry} which is rotated
121
	 * counter-clock-wise by the given {@link Angle} around the given
122
	 * {@link Point}.
123
	 * 
124
	 * @see #getCopy()
125
	 * @see #rotateCW(Angle, Point)
126
	 * @param alpha
127
	 *            The rotation {@link Angle}
128
	 * @param center
129
	 *            The {@link Point} to rotate around
130
	 * @return The new rotated {@link AbstractPointListBasedGeometry}
131
	 */
132
	@SuppressWarnings("unchecked")
133
	public T getRotatedCCW(Angle alpha, Point center) {
134
		return (T) ((T) getCopy()).rotateCCW(alpha, center);
135
	}
136
137
	/**
138
	 * Returns a new {@link AbstractPointListBasedGeometry} which is rotated
139
	 * clock-wise by the given {@link Angle} around its centroid (see
140
	 * {@link #getCentroid()}).
141
	 * 
142
	 * @see #getCopy()
143
	 * @see #getRotatedCW(Angle, Point)
144
	 * @param alpha
145
	 *            The rotation {@link Angle}
146
	 * @return The new rotated {@link AbstractPointListBasedGeometry}
147
	 */
148
	public T getRotatedCW(Angle alpha) {
149
		return getRotatedCW(alpha, getCentroid());
150
	}
151
152
	/**
153
	 * Returns a new {@link AbstractPointListBasedGeometry} which is rotated
154
	 * clock-wise by the given {@link Angle} around the given {@link Point}.
155
	 * 
156
	 * @see #getCopy()
157
	 * @see #rotateCW(Angle, Point)
158
	 * @param alpha
159
	 *            The rotation {@link Angle}
160
	 * @param center
161
	 *            The {@link Point} to rotate around
162
	 * @return The new rotated {@link AbstractPointListBasedGeometry}
163
	 */
164
	@SuppressWarnings("unchecked")
165
	public T getRotatedCW(Angle alpha, Point center) {
166
		return (T) ((T) getCopy()).rotateCW(alpha, center);
167
	}
168
169
	/**
170
	 * Returns a new {@link AbstractPointListBasedGeometry} which is scaled by
171
	 * the given factor. The {@link AbstractPointListBasedGeometry} is
172
	 * translated by the negated centroid (see {@link #getCentroid()}) first.
173
	 * The translation is reversed afterwards.
174
	 * 
175
	 * @param factor
176
	 *            The scale-factor
177
	 * @return The new scaled {@link AbstractPointListBasedGeometry}
178
	 * @see #getScaled(double, Point)
179
	 */
180
	@SuppressWarnings("unchecked")
181
	public T getScaled(double factor) {
182
		return (T) ((T) getCopy()).scale(factor);
183
	}
184
185
	@SuppressWarnings("unchecked")
186
	public T getScaled(double factorX, double factorY) {
187
		return (T) ((T) getCopy()).scale(factorX, factorY);
188
	}
189
190
	@SuppressWarnings("unchecked")
191
	public T getScaled(double factorX, double factorY, Point center) {
192
		return (T) ((T) getCopy()).scale(factorX, factorY, center);
193
	}
194
195
	@SuppressWarnings("unchecked")
196
	public T getScaled(double factor, Point center) {
197
		return (T) ((T) getCopy()).scale(factor, center);
198
	}
199
200
	/**
201
	 * Returns a new {@link AbstractPointListBasedGeometry} which is shifted
202
	 * along each axis by the passed values.
203
	 * 
204
	 * @param dx
205
	 *            Displacement along X axis
206
	 * @param dy
207
	 *            Displacement along Y axis
208
	 * @return The new translated {@link AbstractPointListBasedGeometry}
209
	 */
210
	@SuppressWarnings("unchecked")
211
	public T getTranslated(double dx, double dy) {
212
		return (T) ((T) getCopy()).translate(dx, dy);
213
	}
214
215
	/**
216
	 * Returns a new {@link AbstractPointListBasedGeometry} which is shifted by
217
	 * the position of the given {@link Point}.
218
	 * 
219
	 * @param pt
220
	 *            {@link Point} providing the amount of shift along each axis
221
	 * @return The new translated {@link AbstractPointListBasedGeometry}
222
	 */
223
	@SuppressWarnings("unchecked")
224
	public T getTranslated(Point pt) {
225
		return (T) ((T) getCopy()).translate(pt);
226
	}
227
228
	/**
229
	 * Rotates this {@link AbstractPointListBasedGeometry} counter-clock-wise by
230
	 * the given {@link Angle} around its centroid (see {@link #getCentroid()}).
231
	 * 
232
	 * @param alpha
233
	 *            The rotation {@link Angle}
234
	 * @return <code>this</code> for convenience
235
	 * @see #rotateCCW(Angle, Point)
236
	 */
237
	public T rotateCCW(Angle alpha) {
238
		return rotateCCW(alpha, getCentroid());
239
	}
240
241
	/**
242
	 * Rotates this {@link AbstractPointListBasedGeometry} counter-clock-wise by
243
	 * the given {@link Angle} around the given {@link Point}.
244
	 * 
245
	 * The rotation is done by
246
	 * <ol>
247
	 * <li>translating this {@link AbstractPointListBasedGeometry} by the
248
	 * negated {@link Point} center</li>
249
	 * <li>rotating each {@link Point} of this
250
	 * {@link AbstractPointListBasedGeometry} counter-clock-wise by the given
251
	 * {@link Angle}</li>
252
	 * <li>translating this {@link AbstractPointListBasedGeometry} back by the
253
	 * {@link Point} center</li>
254
	 * </ol>
255
	 * 
256
	 * @param alpha
257
	 *            The rotation {@link Angle}.
258
	 * @param center
259
	 *            The {@link Point} to rotate around.
260
	 * @return <code>this</code> for convenience
261
	 */
262
	@SuppressWarnings("unchecked")
263
	public T rotateCCW(Angle alpha, Point center) {
264
		translate(center.getNegated());
265
		for (Point p : points) {
266
			Point np = new Vector(p).rotateCCW(alpha).toPoint();
267
			p.x = np.x;
268
			p.y = np.y;
269
		}
270
		translate(center);
271
		return (T) this;
272
	}
273
274
	/**
275
	 * Rotates this {@link AbstractPointListBasedGeometry} clock-wise by the
276
	 * given {@link Angle} around its centroid (see {@link #getCentroid()}).
277
	 * 
278
	 * @param alpha
279
	 *            The rotation {@link Angle}
280
	 * @return <code>this</code> for convenience
281
	 * @see #rotateCW(Angle, Point)
282
	 */
283
	public T rotateCW(Angle alpha) {
284
		return (T) rotateCW(alpha, getCentroid());
285
	}
286
287
	/**
288
	 * Rotates this {@link AbstractPointListBasedGeometry} clock-wise by the
289
	 * given {@link Angle} around the given {@link Point}.
290
	 * 
291
	 * The rotation is done by
292
	 * <ol>
293
	 * <li>translating this {@link AbstractPointListBasedGeometry} by the
294
	 * negated {@link Point} center</li>
295
	 * <li>rotating each {@link Point} of this
296
	 * {@link AbstractPointListBasedGeometry} clock-wise by the given
297
	 * {@link Angle}</li>
298
	 * <li>translating this {@link AbstractPointListBasedGeometry} back by the
299
	 * {@link Point} center</li>
300
	 * </ol>
301
	 * 
302
	 * @param alpha
303
	 *            The rotation {@link Angle}
304
	 * @param center
305
	 *            The {@link Point} to rotate around
306
	 * @return <code>this</code> for convenience
307
	 */
308
	@SuppressWarnings("unchecked")
309
	public T rotateCW(Angle alpha, Point center) {
310
		translate(center.getNegated());
311
		for (Point p : points) {
312
			Point np = new Vector(p).rotateCW(alpha).toPoint();
313
			p.x = np.x;
314
			p.y = np.y;
315
		}
316
		translate(center);
317
		return (T) this;
318
	}
319
320
	/**
321
	 * Scales this {@link AbstractPointListBasedGeometry} by the given factor.
322
	 * The {@link AbstractPointListBasedGeometry} is translated by its negated
323
	 * centroid (see {@link #getCentroid()}) first. The translation is reversed
324
	 * afterwards.
325
	 * 
326
	 * @see #scale(double, Point)
327
	 * @param factor
328
	 * @return <code>this</code> for convenience
329
	 */
330
	public T scale(double factor) {
331
		return scale(factor, factor);
332
	}
333
334
	public T scale(double factorX, double factorY) {
335
		return scale(factorX, factorY, getCentroid());
336
	}
337
338
	@SuppressWarnings("unchecked")
339
	public T scale(double factorX, double factorY, Point center) {
340
		for (Point p : points) {
341
			Point np = p.getScaled(factorX, factorY, center);
342
			p.x = np.x;
343
			p.y = np.y;
344
		}
345
		return (T) this;
346
	}
347
348
	public T scale(double factor, Point center) {
349
		return scale(factor, factor, center);
62
	}
350
	}
63
351
64
	/**
352
	/**
Lines 72-75 abstract class AbstractPointListBasedGeometry extends AbstractGeometry { Link Here
72
		return PointListUtils.toIntegerArray(PointListUtils
360
		return PointListUtils.toIntegerArray(PointListUtils
73
				.toCoordinatesArray(points));
361
				.toCoordinatesArray(points));
74
	}
362
	}
363
364
	/**
365
	 * Moves this {@link AbstractPointListBasedGeometry} horizontally by dx and
366
	 * vertically by dy, then returns this
367
	 * {@link AbstractPointListBasedGeometry} for convenience.
368
	 * 
369
	 * @param dx
370
	 *            Shift along X axis
371
	 * @param dy
372
	 *            Shift along Y axis
373
	 * @return <code>this</code> for convenience
374
	 */
375
	@SuppressWarnings("unchecked")
376
	public T translate(double dx, double dy) {
377
		PointListUtils.translate(points, dx, dy);
378
		return (T) this;
379
	}
380
381
	/**
382
	 * Moves this {@link AbstractPointListBasedGeometry} horizontally by the x
383
	 * value of the given {@link Point} and vertically by the y value of the
384
	 * given {@link Point}, then returns this
385
	 * {@link AbstractPointListBasedGeometry} for convenience.
386
	 * 
387
	 * @param p
388
	 *            {@link Point} which provides translation information
389
	 * @return <code>this</code> for convenience
390
	 */
391
	public T translate(Point p) {
392
		return (T) translate(p.x, p.y);
393
	}
394
75
}
395
}
(-)a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/AbstractRectangleBasedGeometry.java (-14 / +173 lines)
Lines 7-12 Link Here
7
 *
7
 *
8
 * Contributors:
8
 * Contributors:
9
 *     Alexander Nyßen (itemis AG) - initial API and implementation
9
 *     Alexander Nyßen (itemis AG) - initial API and implementation
10
 *     Matthias Wienand (itemis AG) - contribution for Bugzilla #355997
10
 *     
11
 *     
11
 *******************************************************************************/
12
 *******************************************************************************/
12
package org.eclipse.gef4.geometry.planar;
13
package org.eclipse.gef4.geometry.planar;
Lines 21-27 import org.eclipse.gef4.geometry.Point; Link Here
21
 * @author anyssen
22
 * @author anyssen
22
 * 
23
 * 
23
 */
24
 */
24
abstract class AbstractRectangleBasedGeometry extends AbstractGeometry {
25
abstract class AbstractRectangleBasedGeometry<T extends AbstractRectangleBasedGeometry<?>>
26
		extends AbstractGeometry {
25
27
26
	private static final long serialVersionUID = 1L;
28
	private static final long serialVersionUID = 1L;
27
29
Lines 38-50 abstract class AbstractRectangleBasedGeometry extends AbstractGeometry { Link Here
38
		return new Rectangle(x, y, width, height);
40
		return new Rectangle(x, y, width, height);
39
	}
41
	}
40
42
43
	public Point getCentroid() {
44
		return new Point(x + width / 2, y + height / 2);
45
	}
46
41
	public final double getHeight() {
47
	public final double getHeight() {
42
		return height;
48
		return height;
43
	}
49
	}
44
50
45
	/**
51
	/**
46
	 * Returns the location of this {@link AbstractRectangleBasedGeometry}, which is the
52
	 * Returns the location of this {@link AbstractRectangleBasedGeometry},
47
	 * location of its bounds.
53
	 * which is the location of its bounds.
48
	 * 
54
	 * 
49
	 * @return a {@link Point} representing the location of this
55
	 * @return a {@link Point} representing the location of this
50
	 *         {@link AbstractRectangleBasedGeometry} 's bounds
56
	 *         {@link AbstractRectangleBasedGeometry} 's bounds
Lines 54-59 abstract class AbstractRectangleBasedGeometry extends AbstractGeometry { Link Here
54
	}
60
	}
55
61
56
	/**
62
	/**
63
	 * Returns a new {@link AbstractPointListBasedGeometry} which is scaled by
64
	 * the given factor. The {@link AbstractPointListBasedGeometry} is
65
	 * translated by the negated centroid (see {@link #getCentroid()}) first.
66
	 * The translation is reversed afterwards.
67
	 * 
68
	 * @param factor
69
	 *            The scale-factor
70
	 * @return The new scaled {@link AbstractPointListBasedGeometry}
71
	 * @see #getScaled(double, Point)
72
	 */
73
	@SuppressWarnings("unchecked")
74
	public T getScaled(double factor) {
75
		return (T) ((T) getCopy()).scale(factor);
76
	}
77
78
	@SuppressWarnings("unchecked")
79
	public T getScaled(double factorX, double factorY) {
80
		return (T) ((T) getCopy()).scale(factorX, factorY);
81
	}
82
83
	@SuppressWarnings("unchecked")
84
	public T getScaled(double factorX, double factorY, Point center) {
85
		return (T) ((T) getCopy()).scale(factorX, factorY, center);
86
	}
87
88
	@SuppressWarnings("unchecked")
89
	public T getScaled(double factor, Point center) {
90
		return (T) ((T) getCopy()).scale(factor, center);
91
	}
92
93
	/**
57
	 * Returns the size of this {@link Rectangle}.
94
	 * Returns the size of this {@link Rectangle}.
58
	 * 
95
	 * 
59
	 * @return The current size
96
	 * @return The current size
Lines 62-67 abstract class AbstractRectangleBasedGeometry extends AbstractGeometry { Link Here
62
		return new Dimension(width, height);
99
		return new Dimension(width, height);
63
	}
100
	}
64
101
102
	/**
103
	 * Returns a new {@link AbstractPointListBasedGeometry} which is shifted
104
	 * along each axis by the passed values.
105
	 * 
106
	 * @param dx
107
	 *            Displacement along X axis
108
	 * @param dy
109
	 *            Displacement along Y axis
110
	 * @return The new translated {@link AbstractPointListBasedGeometry}
111
	 */
112
	@SuppressWarnings("unchecked")
113
	public T getTranslated(double dx, double dy) {
114
		return (T) ((T) getCopy()).translate(dx, dy);
115
	}
116
117
	/**
118
	 * Returns a new {@link AbstractPointListBasedGeometry} which is shifted by
119
	 * the position of the given {@link Point}.
120
	 * 
121
	 * @param pt
122
	 *            {@link Point} providing the amount of shift along each axis
123
	 * @return The new translated {@link AbstractPointListBasedGeometry}
124
	 */
125
	@SuppressWarnings("unchecked")
126
	public T getTranslated(Point pt) {
127
		return (T) ((T) getCopy()).translate(pt);
128
	}
129
65
	public final double getWidth() {
130
	public final double getWidth() {
66
		return width;
131
		return width;
67
	}
132
	}
Lines 75-80 abstract class AbstractRectangleBasedGeometry extends AbstractGeometry { Link Here
75
	}
140
	}
76
141
77
	/**
142
	/**
143
	 * Scales this {@link AbstractPointListBasedGeometry} by the given factor.
144
	 * The {@link AbstractPointListBasedGeometry} is translated by its negated
145
	 * centroid (see {@link #getCentroid()}) first. The translation is reversed
146
	 * afterwards.
147
	 * 
148
	 * @see #scale(double, Point)
149
	 * @param factor
150
	 * @return <code>this</code> for convenience
151
	 */
152
	public T scale(double factor) {
153
		return scale(factor, factor);
154
	}
155
156
	public T scale(double factorX, double factorY) {
157
		return scale(factorX, factorY, getCentroid());
158
	}
159
160
	@SuppressWarnings("unchecked")
161
	public T scale(double factorX, double factorY, Point center) {
162
		double nx = (x - center.x) * factorX + center.x;
163
		double ny = (y - center.y) * factorY + center.y;
164
		width = (x + width - center.x) * factorX + center.x - nx;
165
		height = (y + height - center.y) * factorY + center.y - ny;
166
		x = nx;
167
		y = ny;
168
		return (T) this;
169
	}
170
171
	public T scale(double factor, Point center) {
172
		return scale(factor, factor, center);
173
	}
174
175
	/**
78
	 * Sets the x, y, width and height values of this {@link Rectangle} to match
176
	 * Sets the x, y, width and height values of this {@link Rectangle} to match
79
	 * those that are given.
177
	 * those that are given.
80
	 * 
178
	 * 
Lines 86-97 abstract class AbstractRectangleBasedGeometry extends AbstractGeometry { Link Here
86
	 *            the new width
184
	 *            the new width
87
	 * @param h
185
	 * @param h
88
	 *            the new height
186
	 *            the new height
187
	 * @return <code>this</code> for convenience
89
	 */
188
	 */
90
	public final void setBounds(double x, double y, double w, double h) {
189
	@SuppressWarnings("unchecked")
190
	public final T setBounds(double x, double y, double w, double h) {
91
		this.x = x;
191
		this.x = x;
92
		this.y = y;
192
		this.y = y;
93
		this.width = w;
193
		this.width = w;
94
		this.height = h;
194
		this.height = h;
195
		return (T) this;
95
	}
196
	}
96
197
97
	/**
198
	/**
Lines 101-109 abstract class AbstractRectangleBasedGeometry extends AbstractGeometry { Link Here
101
	 *            The new location
202
	 *            The new location
102
	 * @param size
203
	 * @param size
103
	 *            The new size
204
	 *            The new size
205
	 * @return <code>this</code> for convenience
104
	 */
206
	 */
105
	public final void setBounds(Point loc, Dimension size) {
207
	@SuppressWarnings("unchecked")
208
	public final T setBounds(Point loc, Dimension size) {
106
		setBounds(loc.x, loc.y, size.width, size.height);
209
		setBounds(loc.x, loc.y, size.width, size.height);
210
		return (T) this;
107
	}
211
	}
108
212
109
	/**
213
	/**
Lines 113-128 abstract class AbstractRectangleBasedGeometry extends AbstractGeometry { Link Here
113
	 * @param r
217
	 * @param r
114
	 *            The {@link Rectangle} whose location and size are to be
218
	 *            The {@link Rectangle} whose location and size are to be
115
	 *            transferred.
219
	 *            transferred.
220
	 * @return <code>this</code> for convenience
116
	 */
221
	 */
117
	public final void setBounds(Rectangle r) {
222
	@SuppressWarnings("unchecked")
223
	public final T setBounds(Rectangle r) {
118
		setBounds(r.x, r.y, r.width, r.height);
224
		setBounds(r.x, r.y, r.width, r.height);
225
		return (T) this;
119
	}
226
	}
120
227
121
	public final void setHeight(double height) {
228
	@SuppressWarnings("unchecked")
229
	public final T setHeight(double height) {
122
		if (height < 0) {
230
		if (height < 0) {
123
			height = 0;
231
			height = 0;
124
		}
232
		}
125
		this.height = height;
233
		this.height = height;
234
		return (T) this;
126
	}
235
	}
127
236
128
	/**
237
	/**
Lines 133-142 abstract class AbstractRectangleBasedGeometry extends AbstractGeometry { Link Here
133
	 *            The new x-coordinate
242
	 *            The new x-coordinate
134
	 * @param y
243
	 * @param y
135
	 *            The new y-coordinate
244
	 *            The new y-coordinate
245
	 * @return <code>this</code> for convenience
136
	 */
246
	 */
137
	public final void setLocation(double x, double y) {
247
	@SuppressWarnings("unchecked")
248
	public final T setLocation(double x, double y) {
138
		this.x = x;
249
		this.x = x;
139
		this.y = y;
250
		this.y = y;
251
		return (T) this;
140
	}
252
	}
141
253
142
	/**
254
	/**
Lines 145-153 abstract class AbstractRectangleBasedGeometry extends AbstractGeometry { Link Here
145
	 * 
257
	 * 
146
	 * @param p
258
	 * @param p
147
	 *            the new location of this Rectangle
259
	 *            the new location of this Rectangle
260
	 * @return <code>this</code> for convenience
148
	 */
261
	 */
149
	public final void setLocation(Point p) {
262
	@SuppressWarnings("unchecked")
263
	public final T setLocation(Point p) {
150
		setLocation(p.x, p.y);
264
		setLocation(p.x, p.y);
265
		return (T) this;
151
	}
266
	}
152
267
153
	/**
268
	/**
Lines 156-164 abstract class AbstractRectangleBasedGeometry extends AbstractGeometry { Link Here
156
	 * 
271
	 * 
157
	 * @param d
272
	 * @param d
158
	 *            the new size
273
	 *            the new size
274
	 * @return <code>this</code> for convenience
159
	 */
275
	 */
160
	public final void setSize(Dimension d) {
276
	@SuppressWarnings("unchecked")
277
	public final T setSize(Dimension d) {
161
		setSize(d.width, d.height);
278
		setSize(d.width, d.height);
279
		return (T) this;
162
	}
280
	}
163
281
164
	/**
282
	/**
Lines 169-176 abstract class AbstractRectangleBasedGeometry extends AbstractGeometry { Link Here
169
	 *            The new width
287
	 *            The new width
170
	 * @param h
288
	 * @param h
171
	 *            The new height
289
	 *            The new height
290
	 * @return <code>this</code> for convenience
172
	 */
291
	 */
173
	public final void setSize(double w, double h) {
292
	@SuppressWarnings("unchecked")
293
	public final T setSize(double w, double h) {
174
		if (w < 0) {
294
		if (w < 0) {
175
			w = 0;
295
			w = 0;
176
		}
296
		}
Lines 179-199 abstract class AbstractRectangleBasedGeometry extends AbstractGeometry { Link Here
179
		}
299
		}
180
		width = w;
300
		width = w;
181
		height = h;
301
		height = h;
302
		return (T) this;
182
	}
303
	}
183
304
184
	public final void setWidth(double width) {
305
	@SuppressWarnings("unchecked")
306
	public final T setWidth(double width) {
185
		if (width < 0) {
307
		if (width < 0) {
186
			width = 0;
308
			width = 0;
187
		}
309
		}
188
		this.width = width;
310
		this.width = width;
311
		return (T) this;
189
	}
312
	}
190
313
191
	public final void setX(double x) {
314
	@SuppressWarnings("unchecked")
315
	public final T setX(double x) {
192
		this.x = x;
316
		this.x = x;
317
		return (T) this;
193
	}
318
	}
194
319
195
	public final void setY(double y) {
320
	@SuppressWarnings("unchecked")
321
	public final T setY(double y) {
196
		this.y = y;
322
		this.y = y;
323
		return (T) this;
324
	}
325
326
	/**
327
	 * Moves this {@link AbstractPointListBasedGeometry} horizontally by dx and
328
	 * vertically by dy, then returns this
329
	 * {@link AbstractPointListBasedGeometry} for convenience.
330
	 * 
331
	 * @param dx
332
	 *            Shift along X axis
333
	 * @param dy
334
	 *            Shift along Y axis
335
	 * @return <code>this</code> for convenience
336
	 */
337
	@SuppressWarnings("unchecked")
338
	public T translate(double dx, double dy) {
339
		x += dx;
340
		y += dy;
341
		return (T) this;
342
	}
343
344
	/**
345
	 * Moves this {@link AbstractPointListBasedGeometry} horizontally by the x
346
	 * value of the given {@link Point} and vertically by the y value of the
347
	 * given {@link Point}, then returns this
348
	 * {@link AbstractPointListBasedGeometry} for convenience.
349
	 * 
350
	 * @param p
351
	 *            {@link Point} which provides translation information
352
	 * @return <code>this</code> for convenience
353
	 */
354
	public T translate(Point p) {
355
		return (T) translate(p.x, p.y);
197
	}
356
	}
198
357
199
}
358
}
(-)a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Arc.java (-195 / +68 lines)
Lines 12-23 Link Here
12
package org.eclipse.gef4.geometry.planar;
12
package org.eclipse.gef4.geometry.planar;
13
13
14
import java.util.ArrayList;
14
import java.util.ArrayList;
15
import java.util.Arrays;
16
import java.util.HashSet;
17
import java.util.List;
15
import java.util.List;
18
16
19
import org.eclipse.gef4.geometry.Angle;
17
import org.eclipse.gef4.geometry.Angle;
20
import org.eclipse.gef4.geometry.Point;
18
import org.eclipse.gef4.geometry.Point;
19
import org.eclipse.gef4.geometry.utils.CurveUtils;
21
20
22
/**
21
/**
23
 * Represents the geometric shape of an arc, which is defined by its enclosing
22
 * Represents the geometric shape of an arc, which is defined by its enclosing
Lines 27-33 import org.eclipse.gef4.geometry.Point; Link Here
27
 * @author anyssen
26
 * @author anyssen
28
 * 
27
 * 
29
 */
28
 */
30
public final class Arc extends AbstractRectangleBasedGeometry implements ICurve {
29
public final class Arc extends AbstractRectangleBasedGeometry<Arc> implements
30
		ICurve {
31
31
32
	private static final long serialVersionUID = 1L;
32
	private static final long serialVersionUID = 1L;
33
33
Lines 108-285 public final class Arc extends AbstractRectangleBasedGeometry implements ICurve Link Here
108
	}
108
	}
109
109
110
	/**
110
	/**
111
	 * @see IGeometry#contains(Rectangle)
111
	 * Returns the extension {@link Angle} of this {@link Arc}, i.e. the
112
	 * {@link Angle} defining the span of the {@link Arc}.
113
	 * 
114
	 * @return the extension {@link Angle} of this {@link Arc}
112
	 */
115
	 */
113
	public boolean contains(Rectangle r) {
114
		return false;
115
	}
116
117
	public Angle getAngularExtent() {
116
	public Angle getAngularExtent() {
118
		return angularExtent;
117
		return angularExtent;
119
	}
118
	}
120
119
121
	/**
120
	/**
122
	 * Returns the points of intersection between this {@link Arc} and the given
121
	 * @see org.eclipse.gef4.geometry.planar.IGeometry#getCopy()
123
	 * other {@link Arc}.
124
	 * 
125
	 * @param other
126
	 *            The {@link Arc} to test for intersections
127
	 * @return the points of intersection.
128
	 */
122
	 */
129
	public Point[] getIntersections(Arc other) {
123
	public Arc getCopy() {
130
		if (equals(other)) {
124
		return new Arc(x, y, width, height, startAngle, angularExtent);
131
			return new Point[] {};
125
	}
132
		}
133
134
		HashSet<Point> intersections = new HashSet<Point>();
135
136
		for (CubicCurve seg : getSegments()) {
137
			intersections.addAll(Arrays.asList(getIntersections(seg)));
138
		}
139
126
140
		return intersections.toArray(new Point[] {});
127
	public Point[] getIntersections(ICurve g) {
128
		return CurveUtils.getIntersections(this, g);
141
	}
129
	}
142
130
143
	/**
131
	/**
144
	 * Returns the points of intersection between this {@link Arc} and the given
132
	 * Returns a {@link Point} representing the start point of this {@link Arc}.
145
	 * {@link CubicCurve}.
146
	 * 
133
	 * 
147
	 * @param c
134
	 * @return the start {@link Point} of this {@link Arc}
148
	 *            The {@link CubicCurve} to test for intersections
149
	 * @return the points of intersection.
150
	 */
135
	 */
151
	public Point[] getIntersections(CubicCurve c) {
136
	public Point getP1() {
152
		return c.getIntersections(this);
137
		return getPoint(Angle.fromRad(0));
153
	}
138
	}
154
139
155
	/**
140
	public Point getP2() {
156
	 * Returns the points of intersection between this {@link Arc} and the given
141
		return getPoint(angularExtent);
157
	 * {@link Ellipse}.
158
	 * 
159
	 * @param e
160
	 *            The {@link Ellipse} to test for intersections
161
	 * @return the points of intersection.
162
	 */
163
	public Point[] getIntersections(Ellipse e) {
164
		return e.getIntersections(this);
165
	}
142
	}
166
143
167
	/**
144
	/**
168
	 * Returns the points of intersection between this {@link Arc} and the given
145
	 * Computes a {@link Point} on this {@link Arc}. The {@link Point}'s
169
	 * {@link Line}.
146
	 * coordinates are calculated by moving the given {@link Angle} on the
147
	 * {@link Arc} starting at the {@link Arc} start {@link Point}.
170
	 * 
148
	 * 
171
	 * @param l
149
	 * @param angularExtent
172
	 *            The {@link Line} to test for intersections
150
	 * @return the {@link Point} at the given {@link Angle}
173
	 * @return the points of intersection.
174
	 */
151
	 */
175
	public Point[] getIntersections(Line l) {
152
	public Point getPoint(Angle angularExtent) {
176
		HashSet<Point> intersections = new HashSet<Point>();
153
		double a = width / 2;
177
154
		double b = height / 2;
178
		for (CubicCurve seg : getSegments()) {
179
			intersections.addAll(Arrays.asList(seg.getIntersections(l)));
180
		}
181
155
182
		return intersections.toArray(new Point[] {});
156
		// // calculate start and end points of the arc from start to end
157
		return new Point(x + a + a
158
				* Math.cos(startAngle.rad() + angularExtent.rad()), y + b - b
159
				* Math.sin(startAngle.rad() + angularExtent.rad()));
183
	}
160
	}
184
161
185
	/**
162
	/**
186
	 * Returns the points of intersection between this {@link Arc} and the given
163
	 * Returns this {@link Arc}'s start {@link Angle}.
187
	 * {@link Polygon}.
188
	 * 
164
	 * 
189
	 * @param p
165
	 * @return this {@link Arc}'s start {@link Angle}
190
	 *            The {@link Polygon} to test for intersections
191
	 * @return the points of intersection.
192
	 */
166
	 */
193
	public Point[] getIntersections(Polygon p) {
167
	public Angle getStartAngle() {
194
		return p.getIntersections(this);
168
		return startAngle;
195
	}
169
	}
196
170
197
	/**
171
	public double getX1() {
198
	 * Returns the points of intersection between this {@link Arc} and the given
172
		return getP1().x;
199
	 * {@link Polyline}.
173
	}
200
	 * 
201
	 * @param p
202
	 *            The {@link Polyline} to test for intersections
203
	 * @return the points of intersection.
204
	 */
205
	public Point[] getIntersections(Polyline p) {
206
		HashSet<Point> intersections = new HashSet<Point>();
207
174
208
		for (CubicCurve seg : getSegments()) {
175
	public double getX2() {
209
			intersections.addAll(Arrays.asList(seg.getIntersections(p)));
176
		return getP2().x;
210
		}
177
	}
211
178
212
		return intersections.toArray(new Point[] {});
179
	public double getY1() {
180
		return getP1().y;
213
	}
181
	}
214
182
215
	/**
183
	public double getY2() {
216
	 * Returns the points of intersection between this {@link Arc} and the given
184
		return getP2().y;
217
	 * {@link QuadraticCurve}.
185
	}
218
	 * 
219
	 * @param c
220
	 *            The {@link QuadraticCurve} to test for intersections
221
	 * @return the points of intersection.
222
	 */
223
	public Point[] getIntersections(QuadraticCurve c) {
224
		HashSet<Point> intersections = new HashSet<Point>();
225
186
226
		for (CubicCurve seg : getSegments()) {
187
	public boolean intersects(ICurve c) {
227
			intersections.addAll(Arrays.asList(seg.getIntersections(c)));
188
		return CurveUtils.getIntersections(this, c).length > 0;
228
		}
189
	}
229
190
230
		return intersections.toArray(new Point[] {});
191
	public boolean overlaps(ICurve c) {
192
		for (BezierCurve seg1 : toBezier()) {
193
			if (seg1.overlaps(c)) {
194
				return true;
195
			}
196
		}
197
		return false;
231
	}
198
	}
232
199
233
	/**
200
	/**
234
	 * Returns the points of intersection between this {@link Arc} and the given
201
	 * Sets the extension {@link Angle} of this {@link Arc}.
235
	 * {@link Rectangle}.
236
	 * 
202
	 * 
237
	 * @param r
203
	 * @param angularExtent
238
	 *            The {@link Rectangle} to test for intersections
204
	 *            the new extension {@link Angle} for this {@link Arc}
239
	 * @return the points of intersection.
240
	 */
205
	 */
241
	public Point[] getIntersections(Rectangle r) {
206
	public void setAngularExtent(Angle angularExtent) {
242
		HashSet<Point> intersections = new HashSet<Point>();
207
		this.angularExtent = angularExtent;
243
244
		for (CubicCurve seg : getSegments()) {
245
			intersections.addAll(Arrays.asList(seg.getIntersections(r)));
246
		}
247
248
		return intersections.toArray(new Point[] {});
249
	}
208
	}
250
209
251
	/**
210
	/**
252
	 * Returns the points of intersection between this {@link Arc} and the given
211
	 * Sets the start {@link Angle} of this {@link Arc}.
253
	 * {@link RoundedRectangle}.
254
	 * 
212
	 * 
255
	 * @param r
213
	 * @param startAngle
256
	 *            The {@link RoundedRectangle} to test for intersections
214
	 *            the new start {@link Angle} for this {@link Arc}
257
	 * @return the points of intersection.
258
	 */
215
	 */
259
	public Point[] getIntersections(RoundedRectangle r) {
216
	public void setStartAngle(Angle startAngle) {
260
		HashSet<Point> intersections = new HashSet<Point>();
217
		this.startAngle = startAngle;
261
262
		// line segments
263
		intersections.addAll(Arrays.asList(getIntersections(r.getTop())));
264
		intersections.addAll(Arrays.asList(getIntersections(r.getLeft())));
265
		intersections.addAll(Arrays.asList(getIntersections(r.getBottom())));
266
		intersections.addAll(Arrays.asList(getIntersections(r.getRight())));
267
268
		// arc segments
269
		intersections
270
				.addAll(Arrays.asList(getIntersections(r.getTopRightArc())));
271
		intersections
272
				.addAll(Arrays.asList(getIntersections(r.getTopLeftArc())));
273
		intersections.addAll(Arrays.asList(getIntersections(r
274
				.getBottomLeftArc())));
275
		intersections.addAll(Arrays.asList(getIntersections(r
276
				.getBottomRightArc())));
277
278
		return intersections.toArray(new Point[] {});
279
	}
218
	}
280
219
281
	// TODO: rename this method...
220
	public CubicCurve[] toBezier() {
282
	public CubicCurve[] getSegments() {
283
		double start = getStartAngle().rad();
221
		double start = getStartAngle().rad();
284
		double end = getStartAngle().rad() + getAngularExtent().rad();
222
		double end = getStartAngle().rad() + getAngularExtent().rad();
285
223
Lines 317-391 public final class Arc extends AbstractRectangleBasedGeometry implements ICurve Link Here
317
		return segments.toArray(new CubicCurve[] {});
255
		return segments.toArray(new CubicCurve[] {});
318
	}
256
	}
319
257
320
	public Angle getStartAngle() {
321
		return startAngle;
322
	}
323
324
	public Point getPoint(Angle angularExtent) {
325
		double a = width / 2;
326
		double b = height / 2;
327
328
		// // calculate start and end points of the arc from start to end
329
		return new Point(x + a + a
330
				* Math.cos(startAngle.rad() + angularExtent.rad()), y + b - b
331
				* Math.sin(startAngle.rad() + angularExtent.rad()));
332
	}
333
334
	/**
335
	 * Returns a {@link Point} representing the start point of this {@link Arc}.
336
	 * 
337
	 * @return
338
	 */
339
	public Point getP1() {
340
		return getPoint(Angle.fromRad(0));
341
	}
342
343
	public Point getP2() {
344
		return getPoint(angularExtent);
345
	}
346
347
	/**
348
	 * @see IGeometry#intersects(Rectangle)
349
	 */
350
	public boolean intersects(Rectangle r) {
351
		throw new UnsupportedOperationException();
352
	}
353
354
	public void setAngularExtent(Angle angularExtent) {
355
		this.angularExtent = angularExtent;
356
	}
357
358
	public void setStartAngle(Angle startAngle) {
359
		this.startAngle = startAngle;
360
	}
361
362
	/**
258
	/**
363
	 * @see IGeometry#toPath()
259
	 * @see IGeometry#toPath()
364
	 */
260
	 */
365
	public Path toPath() {
261
	public Path toPath() {
366
		return toPath(getSegments());
262
		return toPath(toBezier());
367
	}
368
369
	/**
370
	 * @see org.eclipse.gef4.geometry.planar.IGeometry#getCopy()
371
	 */
372
	public Arc getCopy() {
373
		return new Arc(x, y, width, height, startAngle, angularExtent);
374
	}
375
376
	public double getY2() {
377
		return getP2().y;
378
	}
379
380
	public double getY1() {
381
		return getP1().y;
382
	}
383
384
	public double getX2() {
385
		return getP2().x;
386
	}
387
388
	public double getX1() {
389
		return getP1().x;
390
	}
263
	}
391
}
264
}
(-)a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/BezierCurve.java (-138 / +1955 lines)
Lines 7-18 Link Here
7
 *
7
 *
8
 * Contributors:
8
 * Contributors:
9
 *     Alexander Nyßen (itemis AG) - initial API and implementation
9
 *     Alexander Nyßen (itemis AG) - initial API and implementation
10
 *     Matthias Wienand (itemis AG) - contribution for Bugzilla #355997
10
 *     
11
 *     
11
 *******************************************************************************/
12
 *******************************************************************************/
12
package org.eclipse.gef4.geometry.planar;
13
package org.eclipse.gef4.geometry.planar;
13
14
15
import java.util.ArrayList;
16
import java.util.Arrays;
17
import java.util.HashSet;
18
import java.util.List;
19
import java.util.Set;
20
import java.util.Stack;
21
22
import org.eclipse.gef4.geometry.Angle;
14
import org.eclipse.gef4.geometry.Point;
23
import org.eclipse.gef4.geometry.Point;
24
import org.eclipse.gef4.geometry.euclidean.Vector;
25
import org.eclipse.gef4.geometry.projective.Straight3D;
26
import org.eclipse.gef4.geometry.projective.Vector3D;
15
import org.eclipse.gef4.geometry.utils.PointListUtils;
27
import org.eclipse.gef4.geometry.utils.PointListUtils;
28
import org.eclipse.gef4.geometry.utils.PrecisionUtils;
16
29
17
/**
30
/**
18
 * Abstract base class of Bezier Curves.
31
 * Abstract base class of Bezier Curves.
Lines 23-280 import org.eclipse.gef4.geometry.utils.PointListUtils; Link Here
23
 * @author anyssen
36
 * @author anyssen
24
 * 
37
 * 
25
 */
38
 */
26
public abstract class BezierCurve extends AbstractGeometry implements ICurve {
39
public class BezierCurve extends AbstractGeometry implements ICurve {
27
40
28
	double x1;
41
	private static final long serialVersionUID = 1L;
29
	double y1;
30
	double x2;
31
	double y2;
32
42
33
	// TODO: use point array instead
43
	private static class FatLine {
34
	double[] ctrlCoordinates = null;
44
		public static FatLine from(BezierCurve c, boolean ortho) {
45
			FatLine L = new FatLine();
46
			L.dmin = L.dmax = 0;
35
47
36
	public BezierCurve(double... coordinates) {
48
			L.line = Straight3D.through(c.points[0],
37
		if (coordinates.length < 4) {
49
					c.points[c.points.length - 1]);
38
			throw new IllegalArgumentException(
50
			if (L.line == null) {
39
					"A bezier curve needs at least a start and an end point");
51
				return null;
52
			}
53
54
			if (ortho) {
55
				L.line = L.line.getOrtho();
56
			}
57
			if (L.line == null) {
58
				return null;
59
			}
60
61
			for (int i = 0; i < c.points.length; i++) {
62
				double d = L.line.getSignedDistanceCW(c.points[i]);
63
				if (d < L.dmin)
64
					L.dmin = d;
65
				else if (d > L.dmax)
66
					L.dmax = d;
67
			}
68
69
			return L;
40
		}
70
		}
41
		this.x1 = coordinates[0];
71
42
		this.y1 = coordinates[1];
72
		public Straight3D line;
43
		this.x2 = coordinates[coordinates.length - 2];
73
44
		this.y2 = coordinates[coordinates.length - 1];
74
		public double dmin, dmax;
45
		if (coordinates.length > 4) {
75
46
			this.ctrlCoordinates = new double[coordinates.length - 4];
76
		private FatLine() {
47
			System.arraycopy(coordinates, 2, ctrlCoordinates, 0,
77
			line = null;
48
					coordinates.length - 4);
78
			dmin = dmax = 0;
49
		}
79
		}
50
	}
80
	}
51
81
52
	public BezierCurve(Point... points) {
82
	/**
53
		this(PointListUtils.toCoordinatesArray(points));
83
	 * Representation of an interval [a;b]. It is used to represent sub-curves
84
	 * of a {@link BezierCurve}.
85
	 * 
86
	 * @author wienand
87
	 */
88
	public static final class Interval {
89
		/**
90
		 * Constructs a new {@link Interval} object holding an invalid parameter
91
		 * interval.
92
		 * 
93
		 * @return a new {@link Interval} object holding an invalid parameter
94
		 *         interval
95
		 */
96
		public static Interval getEmpty() {
97
			return new Interval(1, 0);
98
		}
99
100
		/**
101
		 * Constructs a new {@link Interval} object holding the interval [0;1]
102
		 * which is the parameter interval representing a full
103
		 * {@link BezierCurve}.
104
		 * 
105
		 * @return a new {@link Interval} object holding the interval [0;1]
106
		 */
107
		public static Interval getFull() {
108
			return new Interval(0, 1);
109
		}
110
111
		/**
112
		 * Returns the smaller {@link Interval} object, i.e. the one with the
113
		 * smallest parameter range.
114
		 * 
115
		 * @param i
116
		 * @param j
117
		 * @return the {@link Interval} with the smallest parameter range
118
		 */
119
		public static Interval min(Interval i, Interval j) {
120
			return (i.b - i.a) > (j.b - j.a) ? j : i;
121
		}
122
123
		/**
124
		 * An {@link Interval} holds the parameter range [a;b]. Valid parameter
125
		 * ranges require 0 <= a <= b <= 1.
126
		 */
127
		public double a;
128
129
		/**
130
		 * An {@link Interval} holds the parameter range [a;b]. Valid parameter
131
		 * ranges require 0 <= a <= b <= 1.
132
		 */
133
		public double b;
134
135
		/**
136
		 * Constructs a new {@link Interval} object from the given double
137
		 * values. Only the first two double values are of importance as the
138
		 * rest of them are ignored.
139
		 * 
140
		 * The new {@link Interval} holds the parameter range [a;b] if a is the
141
		 * first double value and b is the second double value.
142
		 * 
143
		 * @param ds
144
		 */
145
		public Interval(double... ds) {
146
			if (ds.length > 1) {
147
				a = ds[0];
148
				b = ds[1];
149
			} else {
150
				throw new IllegalArgumentException(
151
						"not enough values to create interval");
152
			}
153
		}
154
155
		/**
156
		 * Tests if this {@link Interval}'s parameter range does converge with
157
		 * default imprecision. Returns <code>true</code> for a ~= b,
158
		 * <code>false</code> otherwise.
159
		 * 
160
		 * @see Interval#converges(int)
161
		 * 
162
		 * @return <code>true</code> if a ~= b (default imprecision),
163
		 *         <code>false</code> otherwise
164
		 */
165
		public boolean converges() {
166
			return converges(0);
167
		}
168
169
		/**
170
		 * Tests if this {@link Interval}'s parameter range does converge with
171
		 * specified imprecision. Returns <code>true</code> for a ~= b,
172
		 * <code>false</code> otherwise.
173
		 * 
174
		 * The imprecision is specified by providing a shift value which shifts
175
		 * the epsilon used for the number comparison. A positive shift demands
176
		 * for a smaller epsilon (higher precision) whereas a negative shift
177
		 * demands for a greater epsilon (lower precision).
178
		 * 
179
		 * @param shift
180
		 *            precision shift
181
		 * @return <code>true</code> if a ~= b (specified imprecision),
182
		 *         <code>false</code> otherwise
183
		 */
184
		public boolean converges(int shift) {
185
			return PrecisionUtils.equal(a, b, shift);
186
		}
187
188
		/**
189
		 * Returns a copy of this {@link Interval}.
190
		 * 
191
		 * @return a copy of this {@link Interval}
192
		 */
193
		public Interval getCopy() {
194
			return new Interval(a, b);
195
		}
196
197
		/**
198
		 * Returns the middle parameter value m = (a+b)/2 of this
199
		 * {@link Interval}.
200
		 * 
201
		 * @return the middle parameter value of this {@link Interval}
202
		 */
203
		public double getMid() {
204
			return (a + b) / 2;
205
		}
206
207
		/**
208
		 * Scales this {@link Interval} to the given {@link Interval}. The given
209
		 * {@link Interval} specifies the new upper and lower bounds of this
210
		 * {@link Interval} in percent.
211
		 * 
212
		 * Returns the ratio of this {@link Interval}'s new parameter range to
213
		 * its old parameter range.
214
		 * 
215
		 * @param interval
216
		 *            the new upper and lower bounds in percent
217
		 * @return the ratio of this {@link Interval}'s new parameter range to
218
		 *         its old parameter range
219
		 */
220
		public double scaleTo(Interval interval) {
221
			double na = a + interval.a * (b - a);
222
			double nb = a + interval.b * (b - a);
223
			double ratio = (nb - na) / (b - a);
224
			a = na;
225
			b = nb;
226
			return ratio;
227
		}
54
	}
228
	}
55
229
56
	public final boolean contains(Rectangle r) {
230
	/**
57
		// TODO: may contain the rectangle only in case the rectangle is
231
	 * An {@link IntervalPair} combines two {@link BezierCurve}s and their
58
		// degenerated...
232
	 * corresponding parameter ranges.
59
		return false;
233
	 * 
234
	 * @author wienand
235
	 */
236
	public static final class IntervalPair {
237
		/**
238
		 * The first {@link BezierCurve}.
239
		 */
240
		public BezierCurve p;
241
		/**
242
		 * The second {@link BezierCurve}.
243
		 */
244
		public BezierCurve q;
245
		/**
246
		 * The parameter {@link Interval} for the first {@link BezierCurve}.
247
		 */
248
		public Interval pi;
249
		/**
250
		 * The parameter {@link Interval} for the second {@link BezierCurve}.
251
		 */
252
		public Interval qi;
253
254
		/**
255
		 * Constructs a new {@link IntervalPair} with the given
256
		 * {@link BezierCurve}s and their corresponding parameter ranges.
257
		 * 
258
		 * @param pp
259
		 *            the first {@link BezierCurve}
260
		 * @param pt
261
		 *            the parameter {@link Interval} for the first
262
		 *            {@link BezierCurve}
263
		 * @param pq
264
		 *            the second {@link BezierCurve}
265
		 * @param pu
266
		 *            the parameter {@link Interval} for the second
267
		 *            {@link BezierCurve}
268
		 */
269
		public IntervalPair(BezierCurve pp, Interval pt, BezierCurve pq,
270
				Interval pu) {
271
			p = pp;
272
			pi = pt;
273
			q = pq;
274
			qi = pu;
275
		}
276
277
		/**
278
		 * Tests if both parameter {@link Interval}s do converge (@see
279
		 * Interval#converges()) or both {@link BezierCurve}s are degenerated,
280
		 * i.e. they are collapsed to a single {@link Point}.
281
		 * 
282
		 * @return <code>true</true> if both parameter {@link Interval}s do converge, <code>false</code>
283
		 *         otherwise
284
		 */
285
		public boolean converges() {
286
			return converges(0);
287
		}
288
289
		/**
290
		 * Tests if both parameter {@link Interval}s do converge (@see
291
		 * Interval#converges(int)) or both {@link BezierCurve}s are
292
		 * degenerated, i.e. they are collapsed to a single {@link Point}.
293
		 * 
294
		 * @param shift
295
		 *            the precision shift
296
		 * @return <code>true</true> if both parameter {@link Interval}s do converge, <code>false</code>
297
		 *         otherwise
298
		 */
299
		public boolean converges(int shift) {
300
			return (pi.converges(shift) || pointsEquals(
301
					p.getHC(pi.a).toPoint(), p.getHC(pi.b).toPoint(), shift))
302
					&& (qi.converges(shift) || pointsEquals(q.getHC(qi.a)
303
							.toPoint(), q.getHC(qi.b).toPoint(), shift));
304
			// return pi.converges(shift) && qi.converges(shift);
305
		}
306
307
		/**
308
		 * Returns a copy of this {@link IntervalPair}. The underlying
309
		 * {@link BezierCurve}s are only shallow copied. The corresponding
310
		 * parameter {@link Interval}s are truly copied.
311
		 * 
312
		 * @return a copy of this {@link IntervalPair}
313
		 */
314
		public IntervalPair getCopy() {
315
			return new IntervalPair(p, pi.getCopy(), q, qi.getCopy());
316
		}
317
318
		/**
319
		 * Returns the first sub-curve of this {@link IntervalPair}. This curve
320
		 * is the first {@link BezierCurve} p over its corresponding parameter
321
		 * {@link Interval} pi.
322
		 * 
323
		 * @return the first sub-curve of this {@link IntervalPair}
324
		 */
325
		public BezierCurve getPClipped() {
326
			return p.getClipped(pi.a, pi.b);
327
		}
328
329
		/**
330
		 * Splits the first parameter {@link Interval} pi at half and returns
331
		 * the resulting {@link IntervalPair}s.
332
		 * 
333
		 * @return two {@link IntervalPair}s representing a split of the first
334
		 *         paramter {@link Interval} in half
335
		 */
336
		public IntervalPair[] getPSplit() {
337
			double pm = (pi.a + pi.b) / 2;
338
			return new IntervalPair[] {
339
					new IntervalPair(p, new Interval(pi.a, pm), q, qi.getCopy()),
340
					new IntervalPair(p, new Interval(pm + 10
341
							* UNRECOGNIZABLE_PRECISION_FRACTION, pi.b), q,
342
							qi.getCopy()) };
343
		}
344
345
		/**
346
		 * Returns the second sub-curve of this {@link IntervalPair}. This curve
347
		 * is the second {@link BezierCurve} q over its corresponding parameter
348
		 * {@link Interval} qi.
349
		 * 
350
		 * @return the second sub-curve of this {@link IntervalPair}
351
		 */
352
		public BezierCurve getQClipped() {
353
			return q.getClipped(qi.a, qi.b);
354
		}
355
356
		/**
357
		 * Splits the second parameter {@link Interval} qi at half and returns
358
		 * the resulting {@link IntervalPair}s.
359
		 * 
360
		 * @return two {@link IntervalPair}s representing a split of the second
361
		 *         paramter {@link Interval} in half
362
		 */
363
		public IntervalPair[] getQSplit() {
364
			double qm = (qi.a + qi.b) / 2;
365
			return new IntervalPair[] {
366
					new IntervalPair(q, new Interval(qi.a, qm), p, pi.getCopy()),
367
					new IntervalPair(q, new Interval(qm + 10
368
							* UNRECOGNIZABLE_PRECISION_FRACTION, qi.b), p,
369
							pi.getCopy()) };
370
		}
371
372
		/**
373
		 * Creates a new {@link IntervalPair} with swapped {@link BezierCurve}s
374
		 * and their parameter {@link Interval}s.
375
		 * 
376
		 * @return a new {@link IntervalPair} with swapped {@link BezierCurve}s
377
		 *         and their parameter {@link Interval}s
378
		 */
379
		public IntervalPair getSwapped() {
380
			return new IntervalPair(q, qi.getCopy(), p, pi.getCopy());
381
		}
382
383
		/**
384
		 * Calculates which {@link BezierCurve}'s parameter {@link Interval} is
385
		 * longer. Returns <code>true</code> if the distance from start
386
		 * parameter to end parameter of the frist parameter {@link Interval} pi
387
		 * is greater than the distance from start parameter to end parameter of
388
		 * the second parameter {@link Interval} qi. Otherwise, returns
389
		 * <code>false</code>.
390
		 * 
391
		 * @return <code>true</code> if the distance from start to end parameter
392
		 *         value of the first parameter {@link Interval} pi is greater
393
		 *         than the distance from start to end parameter value of the
394
		 *         second parameter {@link Interval} qi. Othwise, returns
395
		 *         <code>false</code>.
396
		 */
397
		public boolean isPLonger() {
398
			return (pi.b - pi.a) > (qi.b - qi.a);
399
		}
400
	}
401
402
	private interface IPointCmp {
403
		public boolean pIsBetterThanQ(Point p, Point q);
404
	}
405
406
	// TODO: use constants that limit the number of iterations for the
407
	// different iterative/recursive algorithms:
408
	// INTERSECTIONS_MAX_ITERATIONS, APPROXIMATION_MAX_ITERATIONS
409
410
	private static final int CHUNK_SHIFT = -3;
411
412
	private static final boolean ORTHOGONAL = true;
413
414
	private static final boolean PARALLEL = false;
415
416
	private static final double UNRECOGNIZABLE_PRECISION_FRACTION = PrecisionUtils
417
			.calculateFraction(0) / 10;
418
419
	private static IntervalPair[] clusterChunks(IntervalPair[] intervalPairs,
420
			int shift) {
421
		List<IntervalPair> clusters = new ArrayList<IntervalPair>();
422
423
		// TODO: do something intelligent instead!
424
		boolean isCompletelyClustered = true;
425
426
		for (IntervalPair ip : intervalPairs) {
427
			boolean isExpansion = false;
428
429
			for (IntervalPair cluster : clusters) {
430
				if (isNextTo(cluster, ip, shift)) {
431
					expand(cluster, ip);
432
					isExpansion = true;
433
					break;
434
				}
435
			}
436
437
			if (!isExpansion) {
438
				clusters.add(ip);
439
			} else {
440
				isCompletelyClustered = false;
441
			}
442
		}
443
444
		IntervalPair[] clustersArray = clusters.toArray(new IntervalPair[] {});
445
		return isCompletelyClustered ? clustersArray : clusterChunks(
446
				clustersArray, shift);
447
	}
448
449
	private static void copyIntervalPair(IntervalPair a, IntervalPair b) {
450
		a.p = b.p;
451
		a.q = b.q;
452
		a.pi = b.pi;
453
		a.qi = b.qi;
454
	}
455
456
	private static void expand(IntervalPair group, IntervalPair newcomer) {
457
		if (group.pi.a > newcomer.pi.a) {
458
			group.pi.a = newcomer.pi.a;
459
		}
460
		if (group.pi.b < newcomer.pi.b) {
461
			group.pi.b = newcomer.pi.b;
462
		}
463
		if (group.qi.a > newcomer.qi.a) {
464
			group.qi.a = newcomer.qi.a;
465
		}
466
		if (group.qi.b < newcomer.qi.b) {
467
			group.qi.b = newcomer.qi.b;
468
		}
469
	}
470
471
	/**
472
	 * Returns the convex hull of the given {@link Vector3D}s.
473
	 * 
474
	 * The {@link PointListUtils#getConvexHull(Point[])} method is used to
475
	 * calculate the convex hull. This method does only accept {@link Point} s
476
	 * for input.
477
	 * 
478
	 * @param vectors
479
	 * @return
480
	 */
481
	private static Point[] getConvexHull(Vector3D[] vectors) {
482
		Point[] points = new Point[vectors.length];
483
		for (int i = 0; i < vectors.length; i++) {
484
			points[i] = vectors[i].toPoint();
485
		}
486
		return PointListUtils.getConvexHull(points);
487
	}
488
489
	/**
490
	 * Computes the intersection of the line from {@link Point} p to
491
	 * {@link Point} q with the x-axis-parallel line f(x) = y.
492
	 * 
493
	 * There is always an intersection, because this routine is only called when
494
	 * either the lower or the higher fat line bound is crossed.
495
	 * 
496
	 * The following conditions are fulfilled: (p.x!=q.x) and (p.y!=q.y) and
497
	 * (p.y<y<q.y) or (p.y>y>q.y).
498
	 * 
499
	 * From these values, one can build a function g(x) = m*x + b where
500
	 * m=(q.y-p.y)/(q.x-p.x) and b=p.y-m*p.x.
501
	 * 
502
	 * The point of intersection is given by f(x) = g(x). The x-coordinate of
503
	 * this point is x = (y - b) / m.
504
	 * 
505
	 * @param p
506
	 *            The start point of the {@link Line}
507
	 * @param q
508
	 *            The end point of the {@link Line}
509
	 * @param y
510
	 *            The x-axis-parallel line f(x) = y
511
	 * @return the x coordinate of the intersection point.
512
	 */
513
	private static double intersectXAxisParallel(Point p, Point q, double y) {
514
		double m = (q.y - p.y) / (q.x - p.x);
515
		return (y - p.y + m * p.x) / m;
516
	}
517
518
	private static boolean isNextTo(IntervalPair a, IntervalPair b, int shift) {
519
		boolean isPNeighbour = PrecisionUtils.greaterEqual(a.pi.a, b.pi.a,
520
				shift)
521
				&& PrecisionUtils.smallerEqual(a.pi.a, b.pi.b, shift)
522
				|| PrecisionUtils.smallerEqual(a.pi.a, b.pi.a, shift)
523
				&& PrecisionUtils.greaterEqual(a.pi.b, b.pi.a, shift);
524
		boolean isQNeighbour = PrecisionUtils.greaterEqual(a.qi.a, b.qi.a,
525
				shift)
526
				&& PrecisionUtils.smallerEqual(a.qi.a, b.qi.b, shift)
527
				|| PrecisionUtils.smallerEqual(a.qi.a, b.qi.a, shift)
528
				&& PrecisionUtils.greaterEqual(a.qi.b, b.qi.a, shift);
529
530
		return isPNeighbour && isQNeighbour;
531
	}
532
533
	private static IntervalPair isOverlap(
534
			IntervalPair[] intersectionCandidates, IntervalPair[] endPoints) {
535
		// merge intersection candidates and end points
536
		IntervalPair[] fineChunks = new IntervalPair[intersectionCandidates.length
537
				+ endPoints.length];
538
		for (int i = 0; i < intersectionCandidates.length; i++) {
539
			fineChunks[i] = intersectionCandidates[i];
540
		}
541
		for (int i = 0; i < endPoints.length; i++) {
542
			fineChunks[intersectionCandidates.length + i] = endPoints[i];
543
		}
544
545
		if (fineChunks.length == 0) {
546
			return new IntervalPair(null, null, null, null);
547
		}
548
549
		// recluster chunks
550
		normalizeIntervalPairs(fineChunks);
551
		IntervalPair[] chunks = clusterChunks(fineChunks, CHUNK_SHIFT - 1);
552
553
		// we should have a single chunk now
554
		if (chunks.length != 1) {
555
			return new IntervalPair(null, null, null, null);
556
		}
557
558
		IntervalPair overlap = chunks[0];
559
560
		/*
561
		 * if they do overlap in a single point, the point of intersection has
562
		 * to be an end-point of both curves. therefore, we do not have to
563
		 * consider this case here, because it is already checked in the main
564
		 * intersection method.
565
		 * 
566
		 * if they overlap, the chunk has to start/end in a start-/endpoint of
567
		 * the curves.
568
		 */
569
570
		if (PrecisionUtils.equal(overlap.pi.a, 0)
571
				&& PrecisionUtils.equal(overlap.pi.b, 1)
572
				|| PrecisionUtils.equal(overlap.qi.a, 0)
573
				&& PrecisionUtils.equal(overlap.qi.b, 1)
574
				|| (PrecisionUtils.equal(overlap.pi.a, 0) || PrecisionUtils
575
						.equal(overlap.pi.b, 1))
576
				&& (PrecisionUtils.equal(overlap.qi.a, 0) || PrecisionUtils
577
						.equal(overlap.qi.b, 1))) {
578
			// it overlaps
579
580
			if (PrecisionUtils.equal(overlap.pi.a, 0, CHUNK_SHIFT - 1)
581
					&& PrecisionUtils.equal(overlap.pi.b, 0, CHUNK_SHIFT - 1)
582
					|| PrecisionUtils.equal(overlap.pi.a, 1, CHUNK_SHIFT - 1)
583
					&& PrecisionUtils.equal(overlap.pi.b, 1, CHUNK_SHIFT - 1)
584
					|| PrecisionUtils.equal(overlap.qi.a, 0, CHUNK_SHIFT - 1)
585
					&& PrecisionUtils.equal(overlap.qi.b, 0, CHUNK_SHIFT - 1)
586
					|| PrecisionUtils.equal(overlap.qi.a, 1, CHUNK_SHIFT - 1)
587
					&& PrecisionUtils.equal(overlap.qi.b, 1, CHUNK_SHIFT - 1)) {
588
				// end-point-intersection
589
				return new IntervalPair(null, null, null, null);
590
			}
591
592
			return overlap;
593
		}
594
595
		return new IntervalPair(null, null, null, null);
596
	}
597
598
	private static void normalizeIntervalPairs(IntervalPair[] intervalPairs) {
599
		// in every interval, p and q have to be the same curves
600
		if (intervalPairs.length == 0) {
601
			return;
602
		}
603
604
		BezierCurve pId = intervalPairs[0].p;
605
		BezierCurve qId = intervalPairs[0].q;
606
607
		for (IntervalPair ip : intervalPairs) {
608
			if (ip.p != pId) {
609
				Interval qi = ip.pi;
610
				Interval pi = ip.qi;
611
				ip.p = pId;
612
				ip.q = qId;
613
				ip.pi = pi;
614
				ip.qi = qi;
615
			}
616
		}
60
	}
617
	}
61
618
62
	public Point getCtrl(int i) {
619
	private static boolean pointsEquals(Point p1, Point p2, int shift) {
63
		return new Point(getCtrlX(i), getCtrlY(i));
620
		return PrecisionUtils.equal(p1.x, p2.x, shift)
621
				&& PrecisionUtils.equal(p1.y, p2.y, shift);
622
	}
623
624
	private Vector3D[] points;
625
626
	private static final IPointCmp xminCmp = new IPointCmp() {
627
		public boolean pIsBetterThanQ(Point p, Point q) {
628
			return PrecisionUtils.smallerEqual(p.x, q.x);
629
		}
630
	};
631
632
	private static final IPointCmp xmaxCmp = new IPointCmp() {
633
		public boolean pIsBetterThanQ(Point p, Point q) {
634
			return PrecisionUtils.greaterEqual(p.x, q.x);
635
		}
636
	};
637
638
	private static final IPointCmp yminCmp = new IPointCmp() {
639
		public boolean pIsBetterThanQ(Point p, Point q) {
640
			return PrecisionUtils.smallerEqual(p.y, q.y);
641
		}
642
	};
643
644
	private static final IPointCmp ymaxCmp = new IPointCmp() {
645
		public boolean pIsBetterThanQ(Point p, Point q) {
646
			return PrecisionUtils.greaterEqual(p.y, q.y);
647
		}
648
	};
649
650
	private static boolean containmentParameter(BezierCurve c,
651
			double[] interval, Point p) {
652
		Stack<Interval> parts = new Stack<Interval>();
653
		parts.push(new Interval(interval));
654
		while (!parts.empty()) {
655
			Interval i = parts.pop();
656
657
			if (i.converges(1)) {
658
				interval[0] = i.a;
659
				interval[1] = i.b;
660
				break;
661
			}
662
663
			double iMid = i.getMid();
664
			Interval left = new Interval(i.a, iMid);
665
			Interval right = new Interval(iMid, i.b);
666
667
			BezierCurve clipped = c.getClipped(left.a, left.b);
668
			Rectangle bounds = clipped.getControlBounds();
669
670
			if (bounds.contains(p)) {
671
				parts.push(left);
672
			}
673
674
			clipped = c.getClipped(right.a, right.b);
675
			bounds = clipped.getControlBounds();
676
677
			if (bounds.contains(p)) {
678
				parts.push(right);
679
			}
680
		}
681
		return PrecisionUtils.equal(interval[0], interval[1], 1);
64
	}
682
	}
65
683
66
	/**
684
	/**
67
	 * Returns the point-wise coordinates (i.e. x1, y1, x2, y2, etc.) of the
685
	 * Returns the similarity of the given {@link BezierCurve} to a {@link Line}
68
	 * inner control points of this {@link BezierCurve}, i.e. exclusive of the
686
	 * , which is defined as the absolute distance of its control points to the
69
	 * start and end points.
687
	 * base line connecting its end points.
70
	 * 
688
	 * 
71
	 * @see BezierCurve#getCtrls()
689
	 * A similarity of 0 means that the given {@link BezierCurve}'s control
690
	 * points are on a straight line.
72
	 * 
691
	 * 
73
	 * @return an array containing the inner control points' coordinates
692
	 * @param c
693
	 * @return
74
	 */
694
	 */
75
	public double[] getCtrlCoordinates() {
695
	private static double distanceToBaseLine(BezierCurve c) {
76
		return PointListUtils.getCopy(ctrlCoordinates);
696
		Straight3D baseLine = Straight3D.through(c.points[0],
697
				c.points[c.points.length - 1]);
698
699
		if (baseLine == null) {
700
			return 0d;
701
		}
77
702
703
		double maxDistance = 0d;
704
		for (int i = 1; i < c.points.length - 1; i++) {
705
			maxDistance = Math.max(maxDistance,
706
					Math.abs(baseLine.getSignedDistanceCW(c.points[i])));
707
		}
708
709
		return maxDistance;
78
	}
710
	}
79
711
80
	/**
712
	/**
81
	 * Returns an array of points representing the inner control points of this
713
	 * Constructs a new {@link BezierCurve} from the given {@link CubicCurve}.
82
	 * curve, i.e. excluding the start and end points. In case of s linear
83
	 * curve, no control points will be returned, in case of a quadratic curve,
84
	 * one control point, and so on.
85
	 * 
714
	 * 
86
	 * @return an array of points with the coordinates of the inner control
715
	 * @param c
87
	 *         points of this {@link BezierCurve}, i.e. exclusive of the start
88
	 *         and end point. The number of control points will depend on the
89
	 *         degree ({@link #getDegree()}) of the curve, so in case of a line
90
	 *         (linear curve) the array will be empty, in case of a quadratic
91
	 *         curve, it will be of size <code>1</code>, in case of a cubic
92
	 *         curve of size <code>2</code>, etc..
93
	 */
716
	 */
94
	public Point[] getCtrls() {
717
	public BezierCurve(CubicCurve c) {
95
		return PointListUtils.toPointsArray(ctrlCoordinates);
718
		this(c.getP1(), c.getCtrl1(), c.getCtrl2(), c.getP2());
96
	}
719
	}
97
720
98
	public double getCtrlX(int i) {
721
	/**
99
		return ctrlCoordinates[2 * i];
722
	 * Constructs a new {@link BezierCurve} from the given control point
723
	 * coordinates. The coordinates are expected to be in x, y order, i.e. x1,
724
	 * y1, x2, y2, x3, y3, ...
725
	 * 
726
	 * @param controlPoints
727
	 */
728
	public BezierCurve(double... controlPoints) {
729
		this(PointListUtils.toPointsArray(controlPoints));
100
	}
730
	}
101
731
102
	public double getCtrlY(int i) {
732
	/**
103
		return ctrlCoordinates[2 * i + 1];
733
	 * Constructs a new {@link BezierCurve} object from the given control
734
	 * points.
735
	 * 
736
	 * @param controlPoints
737
	 */
738
	public BezierCurve(Point... controlPoints) {
739
		points = new Vector3D[controlPoints.length];
740
		for (int i = 0; i < points.length; i++) {
741
			points[i] = new Vector3D(controlPoints[i].x, controlPoints[i].y, 1);
742
		}
104
	}
743
	}
105
744
106
	/**
745
	/**
107
	 * Returns the degree of this curve which corresponds to the number of
746
	 * Constructs a new {@link BezierCurve} from the given
108
	 * overall control points (including start and end point) used to define the
747
	 * {@link QuadraticCurve}.
109
	 * curve. The degree is zero-based, so a line (linear curve) will have
110
	 * degree <code>1</code>, a quadratic curve will have degree <code>2</code>,
111
	 * and so on. <code>1</code> in case of a
112
	 * 
748
	 * 
113
	 * @return The degree of this {@link ICurve}, which corresponds to the
749
	 * @param c
114
	 *         zero-based overall number of control points (including start and
115
	 *         end point) used to define this {@link ICurve}.
116
	 */
750
	 */
117
	public int getDegree() {
751
	public BezierCurve(QuadraticCurve c) {
118
		return getCtrls().length + 1;
752
		this(c.getP1(), c.getCtrl(), c.getP2());
119
	}
753
	}
120
754
121
	/**
755
	/**
122
	 * Returns an array of points that represent this {@link BezierCurve}, i.e.
756
	 * Constructs a new {@link BezierCurve} object from the given control
123
	 * the start point, the inner control points, and the end points.
757
	 * points.
758
	 * 
759
	 * Note that a Point(2, 3) is represented by a Vector3D(2, 3, 1). So for a
760
	 * Point(x, y) the corresponding vector is Vector(x, y, 1).
124
	 * 
761
	 * 
125
	 * @return an array of points representing the control points (including
762
	 * @param controlPoints
126
	 *         start and end point) of this {@link BezierCurve}
127
	 */
763
	 */
128
	public Point[] getPoints() {
764
	private BezierCurve(Vector3D... controlPoints) {
129
		Point[] points = new Point[ctrlCoordinates.length / 2 + 2];
765
		points = new Vector3D[controlPoints.length];
130
		points[0] = new Point(x1, y1);
766
		for (int i = 0; i < points.length; i++) {
131
		points[points.length - 1] = new Point(x2, y2);
767
			points[i] = controlPoints[i].getCopy();
132
		for (int i = 1; i < points.length - 1; i++) {
133
			points[i] = new Point(ctrlCoordinates[2 * i - 2],
134
					ctrlCoordinates[2 * i - 1]);
135
		}
768
		}
136
		return points;
137
	}
769
	}
138
770
139
	/**
771
	/**
140
	 * {@inheritDoc}
772
	 * There are three tests, that have to be done.
773
	 * 
774
	 * The inside-fat-line-check has to be done for every point. It adjusts the
775
	 * interval, so that inseparable portions of the curve are detected.
776
	 * 
777
	 * The other two checks have to be done for every segment. They find
778
	 * intersections of the curve's convex hull with the fat-line boundaries.
779
	 * The points of intersection identify points of breakage.
780
	 * 
781
	 * The starting interval is chosen to be invalid. The individual checks move
782
	 * the start and end parameter values past to one another. If everything can
783
	 * be clipped, the resulting interval remains invalid. If the resulting
784
	 * interval I = [a;b] is valid (a <= b), then the portions [0;a] and [b;1]
785
	 * of the curve can be clipped away.
141
	 * 
786
	 * 
142
	 * @see org.eclipse.gef4.geometry.planar.ICurve#getP1()
787
	 * @param L
788
	 *            The {@link FatLine} to clip to
789
	 * @return the new parameter interval for this {@link BezierCurve}.
143
	 */
790
	 */
144
	public Point getP1() {
791
	private double[] clipTo(FatLine L) {
145
		return new Point(x1, y1);
792
		double[] interval = new double[] { 1, 0 };
793
794
		Point[] D = getConvexHull(genDifferencePoints(L.line));
795
796
		for (Point p : D) {
797
			if (Double.isNaN(p.y) || L.dmin <= p.y && p.y <= L.dmax) {
798
				moveInterval(interval, p.x);
799
			}
800
		}
801
802
		for (Line seg : PointListUtils.toSegmentsArray(D, true)) {
803
			if (seg.getP1().y < L.dmin != seg.getP2().y < L.dmin) {
804
				double x = intersectXAxisParallel(seg.getP1(), seg.getP2(),
805
						L.dmin);
806
				moveInterval(interval, x);
807
			}
808
			if (seg.getP1().y < L.dmax != seg.getP2().y < L.dmax) {
809
				double x = intersectXAxisParallel(seg.getP1(), seg.getP2(),
810
						L.dmax);
811
				moveInterval(interval, x);
812
			}
813
		}
814
815
		return interval;
146
	}
816
	}
147
817
148
	/**
818
	/**
149
	 * {@inheritDoc}
819
	 * Returns true if the given {@link Point} lies on this {@link BezierCurve}.
820
	 * Returns false, otherwise.
150
	 * 
821
	 * 
151
	 * @see org.eclipse.gef4.geometry.planar.ICurve#getP2()
822
	 * @param p
823
	 *            the {@link Point} to test for containment
824
	 * @return true if the {@link Point} is contained, false otherwise
152
	 */
825
	 */
826
	public boolean contains(final Point p) {
827
		if (p == null) {
828
			return false;
829
		}
830
831
		return containmentParameter(this, new double[] { 0, 1 }, p);
832
	}
833
834
	private void findEndPointIntersections(IntervalPair ip,
835
			Set<IntervalPair> endPointIntervalPairs, Set<Point> intersections) {
836
		final double CHUNK_SHIFT_EPSILON = PrecisionUtils
837
				.calculateFraction(CHUNK_SHIFT);
838
839
		Point poi = ip.p.points[0].toPoint();
840
		double[] interval = new double[] { 0, 1 };
841
		if (containmentParameter(ip.q, interval, poi)) {
842
			ip.pi.a = CHUNK_SHIFT_EPSILON;
843
			interval[0] = (interval[0] + interval[1]) / 2;
844
			interval[1] = interval[0] + CHUNK_SHIFT_EPSILON / 2;
845
			interval[0] = interval[0] - CHUNK_SHIFT_EPSILON / 2;
846
			endPointIntervalPairs.add(new IntervalPair(ip.p, new Interval(0,
847
					ip.pi.a), ip.q, new Interval(interval)));
848
			intersections.add(poi);
849
		}
850
851
		poi = ip.p.points[ip.p.points.length - 1].toPoint();
852
		interval[0] = 0;
853
		interval[1] = 1;
854
		if (containmentParameter(ip.q, interval, poi)) {
855
			ip.pi.b = 1 - CHUNK_SHIFT_EPSILON;
856
			interval[0] = (interval[0] + interval[1]) / 2;
857
			interval[1] = interval[0] + CHUNK_SHIFT_EPSILON / 2;
858
			interval[0] = interval[0] - CHUNK_SHIFT_EPSILON / 2;
859
			endPointIntervalPairs.add(new IntervalPair(ip.p, new Interval(
860
					ip.pi.b, 1), ip.q, new Interval(interval)));
861
			intersections.add(poi);
862
		}
863
864
		poi = ip.q.points[0].toPoint();
865
		interval[0] = 0;
866
		interval[1] = 1;
867
		if (containmentParameter(ip.p, interval, poi)) {
868
			ip.qi.a = CHUNK_SHIFT_EPSILON;
869
			interval[0] = (interval[0] + interval[1]) / 2;
870
			interval[1] = interval[0] + CHUNK_SHIFT_EPSILON / 2;
871
			interval[0] = interval[0] - CHUNK_SHIFT_EPSILON / 2;
872
			endPointIntervalPairs.add(new IntervalPair(ip.p, new Interval(
873
					interval), ip.q, new Interval(0, ip.qi.a)));
874
			intersections.add(poi);
875
		}
876
877
		poi = ip.q.points[ip.q.points.length - 1].toPoint();
878
		interval[0] = 0;
879
		interval[1] = 1;
880
		if (containmentParameter(ip.p, interval, poi)) {
881
			ip.qi.b = 1 - CHUNK_SHIFT_EPSILON;
882
			interval[0] = (interval[0] + interval[1]) / 2;
883
			interval[1] = interval[0] + CHUNK_SHIFT_EPSILON / 2;
884
			interval[0] = interval[0] - CHUNK_SHIFT_EPSILON / 2;
885
			endPointIntervalPairs.add(new IntervalPair(ip.p, new Interval(
886
					interval), ip.q, new Interval(ip.qi.b, 1)));
887
			intersections.add(poi);
888
		}
889
	}
890
891
	private Point findExtreme(IPointCmp cmp) {
892
		return findExtreme(cmp, Interval.getFull());
893
	}
894
895
	private Point findExtreme(IPointCmp cmp, Interval iStart) {
896
		Stack<Interval> parts = new Stack<Interval>();
897
		parts.push(iStart);
898
899
		Point xtreme = getHC(iStart.a).toPoint();
900
901
		while (!parts.isEmpty()) {
902
			Interval i = parts.pop();
903
			BezierCurve clipped = getClipped(i.a, i.b);
904
905
			Point sp = clipped.points[0].toPoint();
906
			xtreme = cmp.pIsBetterThanQ(sp, xtreme) ? sp : xtreme;
907
			Point ep = clipped.points[clipped.points.length - 1].toPoint();
908
			xtreme = cmp.pIsBetterThanQ(ep, xtreme) ? ep : xtreme;
909
910
			boolean everythingWorse = true;
911
			for (int j = 1; j < clipped.points.length - 1; j++) {
912
				if (!cmp.pIsBetterThanQ(xtreme, clipped.points[j].toPoint())) {
913
					everythingWorse = false;
914
					break;
915
				}
916
			}
917
918
			if (everythingWorse) {
919
				continue;
920
			}
921
922
			// split interval
923
			double im = i.getMid();
924
			parts.push(new Interval(im, i.b));
925
			parts.push(new Interval(i.a, im));
926
		}
927
928
		return xtreme;
929
	}
930
931
	/**
932
	 * Find intersection interval chunks. The chunks are not very precise. We
933
	 * will refine them later.
934
	 * 
935
	 * @param ip
936
	 * @param intervalPairs
937
	 * @param intersections
938
	 */
939
	private void findIntersectionChunks(IntervalPair ip,
940
			Set<IntervalPair> intervalPairs, Set<Point> intersections) {
941
		if (ip.converges(CHUNK_SHIFT)) {
942
			intervalPairs.add(ip.getCopy());
943
			return;
944
		}
945
946
		BezierCurve pClipped = ip.getPClipped();
947
		BezierCurve qClipped = ip.getQClipped();
948
949
		// construct "parallel" and "orthogonal" fat lines
950
		FatLine L1 = FatLine.from(qClipped, PARALLEL);
951
		FatLine L2 = FatLine.from(qClipped, ORTHOGONAL);
952
953
		// curve implosion check
954
		if (L1 == null || L2 == null) {
955
			// q is degenerated
956
			Point poi = ip.q.getHC(ip.qi.getMid()).toPoint();
957
			if (ip.p.contains(poi)) {
958
				intersections.add(poi);
959
			}
960
			return;
961
		}
962
963
		// clip to the fat lines
964
		Interval interval = new Interval(pClipped.clipTo(L1));
965
		Interval intervalOrtho = new Interval(pClipped.clipTo(L2));
966
967
		// pick smaller interval range
968
		interval = Interval.min(interval, intervalOrtho);
969
970
		// re-calculate s and e from the clipped interval
971
		double ratio = ip.pi.scaleTo(interval);
972
973
		if (ratio < 0) {
974
			// no more intersections
975
			return;
976
		} else if (ratio > 0.8) {
977
			/*
978
			 * Split longer curve and find intersections for both halves. Add an
979
			 * unrecognizable fraction to the beginning of the second parameter
980
			 * interval, so that only one of the getIntersection() calls can
981
			 * converge in the middle.
982
			 */
983
			if (ip.isPLonger()) {
984
				IntervalPair[] nip = ip.getPSplit();
985
				findIntersectionChunks(nip[0], intervalPairs, intersections);
986
				findIntersectionChunks(nip[1], intervalPairs, intersections);
987
			} else {
988
				IntervalPair[] nip = ip.getQSplit();
989
				findIntersectionChunks(nip[0], intervalPairs, intersections);
990
				findIntersectionChunks(nip[1], intervalPairs, intersections);
991
			}
992
993
			return;
994
		} else {
995
			findIntersectionChunks(ip.getSwapped(), intervalPairs,
996
					intersections);
997
		}
998
	}
999
1000
	/**
1001
	 * This routine is only called for an interval that has been detected to
1002
	 * contain a single tangential point of intersection. We do now try to find
1003
	 * it.
1004
	 * 
1005
	 * @param ipIO
1006
	 * @param intervalPairs
1007
	 * @param intersections
1008
	 */
1009
	private Point findSinglePreciseIntersection(IntervalPair ipIO) {
1010
		Stack<IntervalPair> partStack = new Stack<IntervalPair>();
1011
		partStack.push(ipIO);
1012
1013
		while (!partStack.isEmpty()) {
1014
			IntervalPair ip = partStack.pop();
1015
1016
			if (ip.converges()) {
1017
				// TODO: do another clipping algorithm here. the one that
1018
				// uses control bounds.
1019
				for (Point pp : ip.p.toPoints(ip.pi)) {
1020
					for (Point qp : ip.q.toPoints(ip.qi)) {
1021
						if (pp.equals(qp)) {
1022
							copyIntervalPair(ipIO, ip);
1023
							return pp;
1024
						}
1025
					}
1026
				}
1027
				continue;
1028
			}
1029
1030
			BezierCurve pClipped = ip.getPClipped();
1031
			BezierCurve qClipped = ip.getQClipped();
1032
1033
			// construct "parallel" and "orthogonal" fat lines
1034
			FatLine L1 = FatLine.from(qClipped, PARALLEL);
1035
			FatLine L2 = FatLine.from(qClipped, ORTHOGONAL);
1036
1037
			// curve implosion check
1038
			if (L1 == null || L2 == null) {
1039
				// q is degenerated
1040
				Point poi = ip.q.getHC(ip.qi.getMid()).toPoint();
1041
				if (ip.p.contains(poi)) {
1042
					copyIntervalPair(ipIO, ip);
1043
					return poi;
1044
				}
1045
				continue;
1046
			}
1047
1048
			// clip to the fat lines
1049
			Interval interval = new Interval(pClipped.clipTo(L1));
1050
			Interval intervalOrtho = new Interval(pClipped.clipTo(L2));
1051
1052
			// pick smaller interval range
1053
			interval = Interval.min(interval, intervalOrtho);
1054
1055
			// re-calculate s and e from the clipped interval
1056
			double ratio = ip.pi.scaleTo(interval);
1057
1058
			if (ratio < 0) {
1059
				// no more intersections
1060
				continue;
1061
			} else if (ratio > 0.8) {
1062
				/*
1063
				 * Split longer curve and find intersections for both halves.
1064
				 * Add an unrecognizable fraction to the beginning of the second
1065
				 * parameter interval, so that only one of the getIntersection()
1066
				 * calls can converge in the middle.
1067
				 */
1068
				IntervalPair[] nip = ip.isPLonger() ? ip.getPSplit() : ip
1069
						.getQSplit();
1070
				partStack.push(nip[1]);
1071
				partStack.push(nip[0]);
1072
			} else {
1073
				partStack.push(ip.getSwapped());
1074
			}
1075
		}
1076
1077
		return null;
1078
	}
1079
1080
	/**
1081
	 * Generates the difference points of this {@link BezierCurve} to the given
1082
	 * line.
1083
	 * 
1084
	 * The difference points are the control points of a Bezier curve that
1085
	 * yields the signed difference of the point on this curve at a determinate
1086
	 * parameter value to the given line.
1087
	 * 
1088
	 * @param line
1089
	 * @return the difference curve's control points
1090
	 */
1091
	private Vector3D[] genDifferencePoints(Straight3D line) {
1092
		Vector3D[] D = new Vector3D[points.length];
1093
		for (int i = 0; i < points.length; i++) {
1094
			double y = line.getSignedDistanceCW(points[i]);
1095
			D[i] = new Vector3D((double) (i) / (double) (points.length - 1), y,
1096
					1);
1097
		}
1098
		return D;
1099
	}
1100
1101
	/**
1102
	 * Computes the {@link Point} on this {@link BezierCurve} at parameter value
1103
	 * t, which is expected to lie in the range [0;1].
1104
	 * 
1105
	 * @param t
1106
	 *            parameter value
1107
	 * @return the {@link Point} on this {@link BezierCurve} at the given
1108
	 *         parameter value
1109
	 */
1110
	public Point get(double t) {
1111
		return getHC(t).toPoint();
1112
	}
1113
1114
	/**
1115
	 * @see IGeometry#getBounds()
1116
	 * @return the bounds of this {@link BezierCurve}.
1117
	 */
1118
	public Rectangle getBounds() {
1119
		double xmin = findExtreme(xminCmp).x;
1120
		double xmax = findExtreme(xmaxCmp).x;
1121
		double ymin = findExtreme(yminCmp).y;
1122
		double ymax = findExtreme(ymaxCmp).y;
1123
1124
		return new Rectangle(new Point(xmin, ymin), new Point(xmax, ymax));
1125
	}
1126
1127
	/**
1128
	 * Returns a new {@link BezierCurve} object representing this bezier curve
1129
	 * on the interval [s;e].
1130
	 * 
1131
	 * @param s
1132
	 * @param e
1133
	 * @return a new {@link BezierCurve} object representing this bezier curve
1134
	 *         on the interval [s;e]
1135
	 */
1136
	public BezierCurve getClipped(double s, double e) {
1137
		if (s == 1) {
1138
			return new BezierCurve(points[points.length - 1]);
1139
		}
1140
		BezierCurve right = split(s)[1];
1141
		double rightT2 = (e - s) / (1 - s);
1142
		return right.split(rightT2)[0];
1143
	}
1144
1145
	/**
1146
	 * Returns the bounds of the control polygon of this {@link BezierCurve} .
1147
	 * 
1148
	 * @return a {@link Rectangle} representing the bounds of the control
1149
	 *         polygon of this {@link BezierCurve}
1150
	 */
1151
	public Rectangle getControlBounds() {
1152
		Point[] realPoints = getPoints();
1153
1154
		double xmin = realPoints[0].x, xmax = realPoints[0].x, ymin = realPoints[0].y, ymax = realPoints[0].y;
1155
1156
		for (int i = 1; i < realPoints.length; i++) {
1157
			if (realPoints[i].x < xmin) {
1158
				xmin = realPoints[i].x;
1159
			} else if (realPoints[i].x > xmax) {
1160
				xmax = realPoints[i].x;
1161
			}
1162
1163
			if (realPoints[i].y < ymin) {
1164
				ymin = realPoints[i].y;
1165
			} else if (realPoints[i].y > ymax) {
1166
				ymax = realPoints[i].y;
1167
			}
1168
		}
1169
1170
		return new Rectangle(xmin, ymin, xmax - xmin, ymax - ymin);
1171
	}
1172
1173
	public BezierCurve getCopy() {
1174
		return new BezierCurve(points);
1175
	}
1176
1177
	/**
1178
	 * Computes the hodograph (first parametric derivative) of this
1179
	 * {@link BezierCurve}.
1180
	 * 
1181
	 * @return the hodograph (first parametric derivative) of this
1182
	 *         {@link BezierCurve}
1183
	 */
1184
	public BezierCurve getDerivative() {
1185
		Vector3D[] controlPoints = new Vector3D[points.length - 1];
1186
1187
		for (int i = 0; i < controlPoints.length; i++) {
1188
			controlPoints[i] = points[i + 1].getSubtracted(points[i])
1189
					.getScaled(points.length - 1);
1190
			// ignore z coordinate:
1191
			controlPoints[i].z = 1;
1192
		}
1193
1194
		return new BezierCurve(controlPoints);
1195
	}
1196
1197
	/**
1198
	 * Returns the {@link Point} at the given parameter value t.
1199
	 * 
1200
	 * @param t
1201
	 *            Parameter value
1202
	 * @return {@link Point} at parameter value t
1203
	 */
1204
	private Vector3D getHC(double t) {
1205
		if (t < 0 || t > 1) {
1206
			throw new IllegalArgumentException("t out of range: " + t);
1207
		}
1208
1209
		// using horner's scheme:
1210
		int n = points.length;
1211
		if (n < 1) {
1212
			return null;
1213
		}
1214
1215
		double bn = 1, tn = 1, d = 1d - t;
1216
		Vector3D pn = points[0].getScaled(bn * tn);
1217
		for (int i = 1; i < n; i++) {
1218
			bn = bn * (n - i) / i;
1219
			tn = tn * t;
1220
			pn = pn.getScaled(d).getAdded(points[i].getScaled(bn * tn));
1221
		}
1222
1223
		return pn;
1224
	}
1225
1226
	/**
1227
	 * Computes {@link IntervalPair}s which do reflect points of intersection
1228
	 * between this and the given other {@link BezierCurve}. Each
1229
	 * {@link IntervalPair} reflects a single point of intersection.
1230
	 * 
1231
	 * For every {@link IntervalPair} a point of intersection is inserted into
1232
	 * the given {@link Set} of {@link Point}s.
1233
	 * 
1234
	 * If there are infinite {@link Point}s of intersection, i.e. the curves do
1235
	 * overlap, an empty set is returned. (see
1236
	 * {@link BezierCurve#overlaps(BezierCurve)})
1237
	 * 
1238
	 * @param other
1239
	 * @param intersections
1240
	 *            the {@link Point}-{@link Set} where points of intersection are
1241
	 *            inserted
1242
	 * @return for a finite number of intersection {@link Point}s, a {@link Set}
1243
	 *         of {@link IntervalPair}s is returned where every
1244
	 *         {@link IntervalPair} represents a single {@link Point} of
1245
	 *         intersection. For an infinite number of intersection
1246
	 *         {@link Point}s, an empty {@link Set} is returned.
1247
	 */
1248
	public Set<IntervalPair> getIntersectionIntervalPairs(BezierCurve other,
1249
			Set<Point> intersections) {
1250
		Set<IntervalPair> intervalPairs = new HashSet<IntervalPair>();
1251
		Set<IntervalPair> endPointIntervalPairs = new HashSet<IntervalPair>();
1252
1253
		IntervalPair ip = new IntervalPair(this, Interval.getFull(), other,
1254
				Interval.getFull());
1255
1256
		findEndPointIntersections(ip, endPointIntervalPairs, intersections);
1257
		findIntersectionChunks(ip, intervalPairs, intersections);
1258
		normalizeIntervalPairs(intervalPairs.toArray(new IntervalPair[] {}));
1259
		IntervalPair[] clusters = clusterChunks(
1260
				intervalPairs.toArray(new IntervalPair[] {}), 0);
1261
1262
		if (isOverlap(clusters,
1263
				endPointIntervalPairs.toArray(new IntervalPair[] {})).p != null) {
1264
			return new HashSet<IntervalPair>(0);
1265
		}
1266
1267
		Set<IntervalPair> results = new HashSet<IntervalPair>();
1268
		results.addAll(endPointIntervalPairs);
1269
1270
		outer: for (IntervalPair cluster : clusters) {
1271
			for (IntervalPair epip : endPointIntervalPairs) {
1272
				if (isNextTo(cluster, epip, CHUNK_SHIFT)) {
1273
					continue outer;
1274
				}
1275
			}
1276
1277
			// a.t.m. assume for every cluster just a single point of
1278
			// intersection:
1279
			Point poi = findSinglePreciseIntersection(cluster);
1280
			if (poi != null) {
1281
				if (cluster.converges()) {
1282
					results.add(cluster.getCopy());
1283
				} else {
1284
					intersections.add(poi);
1285
				}
1286
			}
1287
		}
1288
1289
		return results;
1290
	}
1291
1292
	/**
1293
	 * @see BezierCurve#findEndPointIntersections(IntervalPair, Set, Set)
1294
	 * @see BezierCurve#findIntersectionChunks(IntervalPair, Set, Set)
1295
	 * 
1296
	 * @param other
1297
	 * @return the points of intersection of this {@link BezierCurve} and the
1298
	 *         given other {@link BezierCurve}.
1299
	 */
1300
	public Point[] getIntersections(BezierCurve other) {
1301
		Set<Point> intersections = new HashSet<Point>();
1302
		Set<IntervalPair> intervalPairs = new HashSet<IntervalPair>();
1303
		Set<IntervalPair> endPointIntervalPairs = new HashSet<IntervalPair>();
1304
1305
		IntervalPair ip = new IntervalPair(this, Interval.getFull(), other,
1306
				Interval.getFull());
1307
1308
		findEndPointIntersections(ip, endPointIntervalPairs, intersections);
1309
		findIntersectionChunks(ip, intervalPairs, intersections);
1310
		normalizeIntervalPairs(intervalPairs.toArray(new IntervalPair[] {}));
1311
		IntervalPair[] clusters = clusterChunks(
1312
				intervalPairs.toArray(new IntervalPair[] {}), 0);
1313
1314
		if (isOverlap(clusters,
1315
				endPointIntervalPairs.toArray(new IntervalPair[] {})).p != null) {
1316
			return new Point[] {};
1317
		}
1318
1319
		outer: for (IntervalPair cluster : clusters) {
1320
			for (IntervalPair epip : endPointIntervalPairs) {
1321
				if (isNextTo(cluster, epip, CHUNK_SHIFT)) {
1322
					continue outer;
1323
				}
1324
			}
1325
1326
			// a.t.m. assume for every cluster just a single point of
1327
			// intersection:
1328
			Point poi = findSinglePreciseIntersection(cluster);
1329
			if (poi != null) {
1330
				intersections.add(poi);
1331
			}
1332
		}
1333
1334
		return intersections.toArray(new Point[] {});
1335
	}
1336
1337
	public final Point[] getIntersections(ICurve curve) {
1338
		Set<Point> intersections = new HashSet<Point>();
1339
1340
		for (BezierCurve c : curve.toBezier()) {
1341
			intersections.addAll(Arrays.asList(getIntersections(c)));
1342
		}
1343
1344
		return intersections.toArray(new Point[] {});
1345
	}
1346
1347
	/**
1348
	 * Computes the overlap of this {@link BezierCurve} and the given other
1349
	 * {@link BezierCurve}. If no overlap exists, <code>null</code> is returned.
1350
	 * Otherwise, a {@link BezierCurve}, representing the overlap, is returned.
1351
	 * 
1352
	 * An overlap is identified by an infinite number of intersection points.
1353
	 * 
1354
	 * @param other
1355
	 * @return a {@link BezierCurve} representing the overlap of this and the
1356
	 *         given other {@link BezierCurve} if an overlap exists, otherwise
1357
	 *         <code>null</code>.
1358
	 */
1359
	public BezierCurve getOverlap(BezierCurve other) {
1360
		Set<Point> intersections = new HashSet<Point>();
1361
		Set<IntervalPair> intervalPairs = new HashSet<IntervalPair>();
1362
		Set<IntervalPair> endPointIntervalPairs = new HashSet<IntervalPair>();
1363
1364
		IntervalPair ip = new IntervalPair(this, Interval.getFull(), other,
1365
				Interval.getFull());
1366
1367
		findEndPointIntersections(ip, endPointIntervalPairs, intersections);
1368
		findIntersectionChunks(ip, intervalPairs, intersections);
1369
		IntervalPair[] clusters = clusterChunks(
1370
				intervalPairs.toArray(new IntervalPair[] {}), 0);
1371
1372
		IntervalPair overlap = isOverlap(clusters,
1373
				endPointIntervalPairs.toArray(new IntervalPair[] {}));
1374
1375
		if (overlap.p != null) {
1376
			return overlap.getPClipped();
1377
		}
1378
		return null;
1379
	}
1380
1381
	public Point getP1() {
1382
		return points[0].toPoint();
1383
	}
1384
153
	public Point getP2() {
1385
	public Point getP2() {
154
		return new Point(x2, y2);
1386
		return points[points.length - 1].toPoint();
155
	}
1387
	}
156
1388
157
	/**
1389
	/**
158
	 * {@inheritDoc}
1390
	 * @param p
1391
	 * @return -1 if p not on curve, otherwise the corresponding parameter
1392
	 *         value.
1393
	 */
1394
	public double getParameterAt(Point p) {
1395
		if (p == null) {
1396
			// return -1;
1397
			throw new NullPointerException("Point may not be null.");
1398
		}
1399
1400
		double[] interval = new double[] { 0, 1 };
1401
		if (containmentParameter(this, interval, p)) {
1402
			return (interval[0] + interval[1]) / 2;
1403
		} else {
1404
			// return -1;
1405
			throw new IllegalArgumentException(
1406
					"The given point does not lie on the curve.");
1407
		}
1408
	}
1409
1410
	/**
1411
	 * Returns the {@link Point} at index i in the points array of this
1412
	 * {@link BezierCurve}. The start {@link Point} is at index 0, the first
1413
	 * handle-{@link Point} is at index 1, etc.
159
	 * 
1414
	 * 
160
	 * @see org.eclipse.gef4.geometry.planar.ICurve#getX1()
1415
	 * @param i
1416
	 *            the index of a {@link Point} in the points array of this
1417
	 *            {@link BezierCurve}
1418
	 * @return the {@link Point} at the given index in the points array of this
1419
	 *         {@link BezierCurve}
161
	 */
1420
	 */
162
	public double getX1() {
1421
	public Point getPoint(int i) {
163
		return x1;
1422
		if (i < 0 || i >= points.length) {
1423
			throw new IllegalArgumentException(
1424
					"getPoint("
1425
							+ i
1426
							+ "): You can only index this BezierCurve's points from 0 to "
1427
							+ (points.length - 1) + ".");
1428
		}
1429
		return points[i].toPoint();
164
	}
1430
	}
165
1431
166
	/**
1432
	/**
167
	 * {@inheritDoc}
1433
	 * Computes the real planar {@link Point}s for this {@link BezierCurve}.
168
	 * 
1434
	 * 
169
	 * @see org.eclipse.gef4.geometry.planar.ICurve#getX2()
1435
	 * @return the real planar {@link Point}s for this {@link BezierCurve}
170
	 */
1436
	 */
171
	public double getX2() {
1437
	public Point[] getPoints() {
172
		return x2;
1438
		Point[] realPoints = new Point[points.length];
1439
		for (int i = 0; i < points.length; i++) {
1440
			realPoints[i] = points[i].toPoint();
1441
		}
1442
		return realPoints;
173
	}
1443
	}
174
1444
175
	/**
1445
	/**
176
	 * {@inheritDoc}
1446
	 * Returns a copy of this {@link BezierCurve}'s points.
177
	 * 
1447
	 * 
178
	 * @see org.eclipse.gef4.geometry.planar.ICurve#getY1()
1448
	 * @return a copy of this {@link BezierCurve}'s points
179
	 */
1449
	 */
180
	public double getY1() {
1450
	private Vector3D[] getPointsCopy() {
181
		return y1;
1451
		Vector3D[] copy = new Vector3D[points.length];
1452
		for (int i = 0; i < points.length; i++) {
1453
			copy[i] = points[i].getCopy();
1454
		}
1455
		return copy;
182
	}
1456
	}
183
1457
184
	/**
1458
	/**
185
	 * {@inheritDoc}
1459
	 * Creates a new {@link BezierCurve} with all points translated by the given
1460
	 * {@link Point}.
186
	 * 
1461
	 * 
187
	 * @see org.eclipse.gef4.geometry.planar.ICurve#getY2()
1462
	 * @param p
1463
	 * @return a new {@link BezierCurve} with all points translated by the given
1464
	 *         {@link Point}
188
	 */
1465
	 */
1466
	public BezierCurve getTranslated(Point p) {
1467
		Point[] translated = new Point[points.length];
1468
1469
		for (int i = 0; i < translated.length; i++) {
1470
			translated[i] = points[i].toPoint().getTranslated(p);
1471
		}
1472
1473
		return new BezierCurve(translated);
1474
	}
1475
1476
	public double getX1() {
1477
		return getP1().x;
1478
	}
1479
1480
	public double getX2() {
1481
		return getP2().x;
1482
	}
1483
1484
	public double getY1() {
1485
		return getP1().y;
1486
	}
1487
189
	public double getY2() {
1488
	public double getY2() {
190
		return y2;
1489
		return getP2().y;
191
	}
1490
	}
192
1491
193
	protected void setCtrl(int i, Point p) {
1492
	public final boolean intersects(ICurve c) {
194
		setCtrlX(i, p.x);
1493
		return getIntersections(c).length > 0;
195
		setCtrlY(i, p.y);
196
	}
1494
	}
197
1495
198
	public void setCtrls(Point... ctrls) {
1496
	/**
199
		ctrlCoordinates = PointListUtils.toCoordinatesArray(ctrls);
1497
	 * Moves the interval's start and end values. The start value is set to x if
1498
	 * x is smaller than the start value. The end value is set to x if x is
1499
	 * greater than the end value.
1500
	 * 
1501
	 * @param interval
1502
	 *            The current interval
1503
	 * @param x
1504
	 */
1505
	private void moveInterval(double[] interval, double x) {
1506
		if (interval[0] > x) {
1507
			interval[0] = x;
1508
		}
1509
		if (interval[1] < x) {
1510
			interval[1] = x;
1511
		}
1512
	}
1513
1514
	/**
1515
	 * Returns <code>true</code> if this and the given other {@link BezierCurve}
1516
	 * do overlap. Otherwise, returns <code>false</cdoe>.
1517
	 * 
1518
	 * @param other
1519
	 * @return <code>true</code> if this and the given other {@link BezierCurve}
1520
	 *         overlap, otherwise <code>false</code>
1521
	 */
1522
	public boolean overlaps(BezierCurve other) {
1523
		Set<Point> intersections = new HashSet<Point>();
1524
		Set<IntervalPair> intervalPairs = new HashSet<IntervalPair>();
1525
		Set<IntervalPair> endPointIntervalPairs = new HashSet<IntervalPair>();
1526
1527
		IntervalPair ip = new IntervalPair(this, Interval.getFull(), other,
1528
				Interval.getFull());
1529
1530
		findEndPointIntersections(ip, endPointIntervalPairs, intersections);
1531
		findIntersectionChunks(ip, intervalPairs, intersections);
1532
		IntervalPair[] clusters = clusterChunks(
1533
				intervalPairs.toArray(new IntervalPair[] {}), 0);
1534
1535
		return isOverlap(clusters,
1536
				endPointIntervalPairs.toArray(new IntervalPair[] {})).p != null;
200
	}
1537
	}
201
1538
202
	protected void setCtrlX(int i, double x) {
1539
	public final boolean overlaps(ICurve c) {
203
		// TODO: enlarge array if its too small
1540
		for (BezierCurve seg : c.toBezier()) {
204
		ctrlCoordinates[2 * i] = x;
1541
			if (overlaps(seg)) {
1542
				return true;
1543
			}
1544
		}
1545
		return false;
205
	}
1546
	}
206
1547
207
	protected void setCtrlY(int i, double y) {
1548
	/**
208
		// TODO: enlarge array if its too small
1549
	 * @param alpha
209
		ctrlCoordinates[2 * i + 1] = y;
1550
	 * @param center
1551
	 */
1552
	public void rotateCCW(Angle alpha, Point center) {
1553
		for (int i = 0; i < points.length; i++) {
1554
			points[i] = new Vector3D(new Vector(points[i].toPoint()
1555
					.getTranslated(center.getNegated())).getRotatedCCW(alpha)
1556
					.toPoint().getTranslated(center));
1557
		}
210
	}
1558
	}
211
1559
212
	/**
1560
	/**
213
	 * Sets the start {@link Point} of this {@link BezierCurve} to the given
1561
	 * Sets the start {@link Point} of this {@link BezierCurve} to the given
214
	 * {@link Point} p1.
1562
	 * {@link Point}.
215
	 * 
1563
	 * 
216
	 * @param p1
1564
	 * @param p1
217
	 *            the new start {@link Point}
1565
	 *            the new start {@link Point} of this {@link BezierCurve}
218
	 */
1566
	 */
219
	public void setP1(Point p1) {
1567
	public void setP1(Point p1) {
220
		this.x1 = p1.x;
1568
		setPoint(0, p1);
221
		this.y1 = p1.y;
222
	}
1569
	}
223
1570
224
	/**
1571
	/**
225
	 * Sets the end {@link Point} of this {@link BezierCurve} to the given
1572
	 * Sets the end {@link Point} of this {@link BezierCurve} to the given
226
	 * {@link Point} p2.
1573
	 * {@link Point}.
227
	 * 
1574
	 * 
228
	 * @param p2
1575
	 * @param p2
229
	 *            the new end {@link Point}
1576
	 *            the new end {@link Point} of this {@link BezierCurve}
230
	 */
1577
	 */
231
	public void setP2(Point p2) {
1578
	public void setP2(Point p2) {
232
		this.x2 = p2.x;
1579
		setPoint(points.length - 1, p2);
233
		this.y2 = p2.y;
1580
	}
1581
1582
	/**
1583
	 * Sets the {@link Point} at index i in this {@link BezierCurve}'s
1584
	 * {@link Point}s array to the given {@link Point}. The start {@link Point}
1585
	 * 's index is 0. The index of the first handle {@link Point} is 1, etc.
1586
	 * 
1587
	 * @param i
1588
	 *            the index in this {@link BezierCurve}'s {@link Point}s array
1589
	 * @param p
1590
	 *            the new {@link Point} for the given index
1591
	 */
1592
	public void setPoint(int i, Point p) {
1593
		if (i < 0 || i >= points.length) {
1594
			throw new IllegalArgumentException(
1595
					"setPoint("
1596
							+ i
1597
							+ ", "
1598
							+ p
1599
							+ "): You can only index this BezierCurve's points from 0 to "
1600
							+ (points.length - 1) + ".");
1601
		}
1602
		points[i] = new Vector3D(p);
234
	}
1603
	}
235
1604
236
	/**
1605
	/**
237
	 * Sets the x-coordinate of the start {@link Point} of this
1606
	 * Subdivides this {@link BezierCurve} at the given parameter value t into
238
	 * {@link BezierCurve} to x1.
1607
	 * two new {@link BezierCurve}s. The first one is the curve over [0;t] and
1608
	 * the second one is the curve over [t;1].
239
	 * 
1609
	 * 
240
	 * @param x1
1610
	 * NOTE: One could provide two methods splitLeft() and splitRight() in case
241
	 *            the new start {@link Point}'s x-coordinate
1611
	 * just the left or right part of the curve is needed.
1612
	 * 
1613
	 * @param t
1614
	 *            Parameter value
1615
	 * @return Two curves; to the left and to the right of t.
242
	 */
1616
	 */
243
	public void setX1(double x1) {
1617
	public BezierCurve[] split(double t) {
244
		this.x1 = x1;
1618
		Vector3D[] leftPoints = new Vector3D[points.length];
1619
		Vector3D[] rightPoints = new Vector3D[points.length];
1620
1621
		Vector3D[] ratioPoints = getPointsCopy();
1622
1623
		for (int i = 0; i < points.length; i++) {
1624
			leftPoints[i] = ratioPoints[0];
1625
			rightPoints[points.length - 1 - i] = ratioPoints[points.length - 1
1626
					- i];
1627
1628
			for (int j = 0; j < points.length - i - 1; j++) {
1629
				ratioPoints[j] = ratioPoints[j].getRatio(ratioPoints[j + 1], t);
1630
			}
1631
		}
1632
1633
		return new BezierCurve[] { new BezierCurve(leftPoints),
1634
				new BezierCurve(rightPoints) };
1635
	}
1636
1637
	public BezierCurve[] toBezier() {
1638
		return new BezierCurve[] { this };
245
	}
1639
	}
246
1640
247
	/**
1641
	/**
248
	 * Sets the x-coordinate of the end {@link Point} of this
1642
	 * Returns a hard approximation of this {@link BezierCurve} as a
249
	 * {@link BezierCurve} to x2.
1643
	 * {@link CubicCurve}. The new {@link CubicCurve} is constructed from the
1644
	 * first four {@link Point}s in this {@link BezierCurve}'s {@link Point}s
1645
	 * array. If this {@link BezierCurve} is not of degree four or higher, i.e.
1646
	 * it does not have four or more control {@link Point}s (including start and
1647
	 * end {@link Point}), <code>null</code> is returned.
250
	 * 
1648
	 * 
251
	 * @param x2
1649
	 * @return a new {@link CubicCurve} that is constructed by the first four
252
	 *            the new end {@link Point}'s x-coordinate
1650
	 *         control {@link Point}s of this {@link BezierCurve} or
1651
	 *         <code>null</code> if this {@link BezierCurve} does not have at
1652
	 *         least four control {@link Point}s
253
	 */
1653
	 */
254
	public void setX2(double x2) {
1654
	public CubicCurve toCubic() {
255
		this.x2 = x2;
1655
		if (points.length > 3) {
1656
			return new CubicCurve(points[0].toPoint(), points[1].toPoint(),
1657
					points[2].toPoint(), points[points.length - 1].toPoint());
1658
		}
1659
		return null;
256
	}
1660
	}
257
1661
258
	/**
1662
	/**
259
	 * Sets the y-coordinate of the start {@link Point} of this
1663
	 * Returns a hard approximation of this {@link BezierCurve} as a
260
	 * {@link BezierCurve} to y1.
1664
	 * {@link Line}. The {@link Line} is constructed from the start and end
1665
	 * {@link Point} of this {@link BezierCurve}.
261
	 * 
1666
	 * 
262
	 * @param y1
1667
	 * Sometimes, this {@link Line} is referred to as the base-line of the
263
	 *            the new start {@link Point}'s y-coordinate
1668
	 * {@link BezierCurve}.
1669
	 * 
1670
	 * @return a {@link Line} from start to end {@link Point} of this
1671
	 *         {@link BezierCurve}
264
	 */
1672
	 */
265
	public void setY1(double y1) {
1673
	public Line toLine() {
266
		this.y1 = y1;
1674
		if (points.length > 1) {
1675
			return new Line(points[0].toPoint(),
1676
					points[points.length - 1].toPoint());
1677
		}
1678
		return null;
267
	}
1679
	}
268
1680
269
	/**
1681
	/**
270
	 * Sets the y-coordinate of the end {@link Point} of this
1682
	 * Returns an approximation of this {@link BezierCurve} by a strip of
271
	 * {@link BezierCurve} to y2.
1683
	 * {@link Line}s. For detailed information on how the approximation is
1684
	 * calculated, see {@link BezierCurve#toLineStrip(double, Interval)}.
272
	 * 
1685
	 * 
273
	 * @param y2
1686
	 * @see BezierCurve#toLineStrip(double, Interval)
274
	 *            the new end {@link Point}'s y-coordinate
1687
	 * @param lineSimilarity
1688
	 * @return an approximation of this {@link BezierCurve} by a strip of
1689
	 *         {@link Line}s
275
	 */
1690
	 */
276
	public void setY2(double y2) {
1691
	public Line[] toLineStrip(double lineSimilarity) {
277
		this.y2 = y2;
1692
		return toLineStrip(lineSimilarity, Interval.getFull());
278
	}
1693
	}
279
1694
1695
	/**
1696
	 * Returns {@link Line} segments approximating this {@link BezierCurve}.
1697
	 * 
1698
	 * The {@link BezierCurve} is recursively subdivided until it is "similar"
1699
	 * to a straight line. The similarity check computes the sum of the
1700
	 * distances of the control points to the baseline of the
1701
	 * {@link BezierCurve}. If this sum is smaller than the given
1702
	 * lineSimilarity, the {@link BezierCurve} is assumed to be "similar" to a
1703
	 * straight line.
1704
	 * 
1705
	 * @param lineSimilarity
1706
	 *            The threshold for the sum of the distances of the control
1707
	 *            points to the baseline of this {@link BezierCurve}
1708
	 * @param startInterval
1709
	 *            The interval of the curve that has to be transformed into a
1710
	 *            strip of {@link Line}s.
1711
	 * @return {@link Line} segments approximating this {@link BezierCurve}.
1712
	 */
1713
	public Line[] toLineStrip(double lineSimilarity, Interval startInterval) {
1714
		ArrayList<Line> lines = new ArrayList<Line>();
1715
1716
		Point startPoint = getHC(startInterval.a).toPoint();
1717
1718
		// System.out.println("BEZIER CURVE - LINE APPROXIMATION");
1719
		// System.out.println("---------------------------------");
1720
		// System.out.println("moveTo(" + startPoint.x + ", " + startPoint.y
1721
		// + ")");
1722
1723
		Stack<Interval> parts = new Stack<Interval>();
1724
		parts.push(startInterval);
1725
1726
		while (!parts.isEmpty()) {
1727
			// System.out.println("pop");
1728
			Interval i = parts.pop();
1729
			BezierCurve part = getClipped(i.a, i.b);
1730
1731
			if (distanceToBaseLine(part) < lineSimilarity) {
1732
				Point endPoint = getHC(i.b).toPoint();
1733
				lines.add(new Line(startPoint, endPoint));
1734
				startPoint = endPoint;
1735
				// System.out.println("lineTo(" + endPoint.x + ", "
1736
				// + endPoint.y + ")");
1737
			} else {
1738
				// System.out.println("push'em");
1739
				double im = i.getMid();
1740
				parts.push(new Interval(im, i.b));
1741
				parts.push(new Interval(i.a, im));
1742
			}
1743
		}
1744
1745
		return lines.toArray(new Line[] {});
1746
	}
1747
1748
	/**
1749
	 * Returns a {@link Path} approximating this {@link BezierCurve} using
1750
	 * {@link Line} segments.
1751
	 * 
1752
	 * @return a {@link Path} approximating this {@link BezierCurve} using
1753
	 *         {@link Line} segments.
1754
	 */
1755
	public Path toPath() {
1756
		Path path = new Path();
1757
1758
		Point startPoint = points[0].toPoint();
1759
		path.moveTo(startPoint.x, startPoint.y);
1760
1761
		for (Line seg : toLineStrip(0.25d)) {
1762
			path.lineTo(seg.getX2(), seg.getY2());
1763
		}
1764
1765
		return path;
1766
	}
1767
1768
	/**
1769
	 * Computes {@link Point}s on this {@link BezierCurve} over the given
1770
	 * {@link Interval}. Consecutive returned {@link Point}s are required to be
1771
	 * {@link Point#equals(Object)} to each other.
1772
	 * 
1773
	 * @param startInterval
1774
	 *            the {@link Interval} of this {@link BezierCurve} to calculate
1775
	 *            {@link Point}s for
1776
	 * @return {@link Point}s on this {@link BezierCurve} over the given
1777
	 *         parameter {@link Interval} where consecutive {@link Point}s are
1778
	 *         {@link Point#equals(Object)} to each other
1779
	 */
1780
	public Point[] toPoints(Interval startInterval) {
1781
		ArrayList<Point> points = new ArrayList<Point>();
1782
		points.add(getHC(startInterval.a).toPoint());
1783
1784
		// System.out.println("BEZIER CURVE - LINE APPROXIMATION");
1785
		// System.out.println("---------------------------------");
1786
		// System.out.println("moveTo(" + startPoint.x + ", " + startPoint.y
1787
		// + ")");
1788
1789
		Stack<Interval> parts = new Stack<Interval>();
1790
		parts.push(startInterval);
1791
1792
		while (!parts.isEmpty()) {
1793
			// System.out.println("pop");
1794
			Interval i = parts.pop();
1795
			BezierCurve part = getClipped(i.a, i.b);
1796
1797
			Point[] partPoints = part.getPoints();
1798
1799
			boolean allTogether = true;
1800
			for (int j = 1; j < partPoints.length; j++) {
1801
				if (!partPoints[0].equals(partPoints[j])) {
1802
					allTogether = false;
1803
					break;
1804
				}
1805
			}
1806
1807
			if (allTogether) {
1808
				points.add(partPoints[partPoints.length - 1]);
1809
			} else {
1810
				double im = i.getMid();
1811
				parts.push(new Interval(im, i.b));
1812
				parts.push(new Interval(i.a, im));
1813
			}
1814
		}
1815
1816
		return points.toArray(new Point[] {});
1817
	}
1818
1819
	/**
1820
	 * Returns a hard approximation of this {@link BezierCurve} as a
1821
	 * {@link QuadraticCurve}. The new {@link QuadraticCurve} is constructed
1822
	 * from the first three {@link Point}s in this {@link BezierCurve}'s
1823
	 * {@link Point}s array. If this {@link BezierCurve} is not of degree three
1824
	 * or higher, i.e. it does not have three or more control {@link Point}s
1825
	 * (including start and end {@link Point}), <code>null</code> is returned.
1826
	 * 
1827
	 * @return a new {@link QuadraticCurve} that is constructed by the first
1828
	 *         three control {@link Point}s of this {@link BezierCurve} or
1829
	 *         <code>null</code> if this {@link BezierCurve} does not have at
1830
	 *         least three control {@link Point}s
1831
	 */
1832
	public QuadraticCurve toQuadratic() {
1833
		if (points.length > 2) {
1834
			return new QuadraticCurve(points[0].toPoint(), points[1].toPoint(),
1835
					points[points.length - 1].toPoint());
1836
		}
1837
		return null;
1838
	}
1839
1840
	// double x1;
1841
	// double y1;
1842
	// double x2;
1843
	// double y2;
1844
	//
1845
	// // TODO: use point array instead
1846
	// double[] ctrlCoordinates = null;
1847
	//
1848
	// public BezierCurve(double... coordinates) {
1849
	// if (coordinates.length < 4) {
1850
	// throw new IllegalArgumentException(
1851
	// "A bezier curve needs at least a start and an end point");
1852
	// }
1853
	// this.x1 = coordinates[0];
1854
	// this.y1 = coordinates[1];
1855
	// this.x2 = coordinates[coordinates.length - 2];
1856
	// this.y2 = coordinates[coordinates.length - 1];
1857
	// if (coordinates.length > 4) {
1858
	// this.ctrlCoordinates = new double[coordinates.length - 4];
1859
	// System.arraycopy(coordinates, 2, ctrlCoordinates, 0,
1860
	// coordinates.length - 4);
1861
	// }
1862
	// }
1863
	//
1864
	// public BezierCurve(Point... points) {
1865
	// this(PointListUtils.toCoordinatesArray(points));
1866
	// }
1867
	//
1868
	// public final boolean contains(Rectangle r) {
1869
	// // TODO: may contain the rectangle only in case the rectangle is
1870
	// // degenerated...
1871
	// return false;
1872
	// }
1873
	//
1874
	// public Point getCtrl(int i) {
1875
	// return new Point(getCtrlX(i), getCtrlY(i));
1876
	// }
1877
	//
1878
	// /**
1879
	// * Returns the point-wise coordinates (i.e. x1, y1, x2, y2, etc.) of the
1880
	// * inner control points of this {@link BezierCurve}, i.e. exclusive of the
1881
	// * start and end points.
1882
	// *
1883
	// * @see BezierCurve#getCtrls()
1884
	// *
1885
	// * @return an array containing the inner control points' coordinates
1886
	// */
1887
	// public double[] getCtrlCoordinates() {
1888
	// return PointListUtils.getCopy(ctrlCoordinates);
1889
	//
1890
	// }
1891
	//
1892
	// /**
1893
	// * Returns an array of points representing the inner control points of
1894
	// this
1895
	// * curve, i.e. excluding the start and end points. In case of s linear
1896
	// * curve, no control points will be returned, in case of a quadratic
1897
	// curve,
1898
	// * one control point, and so on.
1899
	// *
1900
	// * @return an array of points with the coordinates of the inner control
1901
	// * points of this {@link BezierCurve}, i.e. exclusive of the start
1902
	// * and end point. The number of control points will depend on the
1903
	// * degree ({@link #getDegree()}) of the curve, so in case of a line
1904
	// * (linear curve) the array will be empty, in case of a quadratic
1905
	// * curve, it will be of size <code>1</code>, in case of a cubic
1906
	// * curve of size <code>2</code>, etc..
1907
	// */
1908
	// public Point[] getCtrls() {
1909
	// return PointListUtils.toPointsArray(ctrlCoordinates);
1910
	// }
1911
	//
1912
	// public double getCtrlX(int i) {
1913
	// return ctrlCoordinates[2 * i];
1914
	// }
1915
	//
1916
	// public double getCtrlY(int i) {
1917
	// return ctrlCoordinates[2 * i + 1];
1918
	// }
1919
	//
1920
	// /**
1921
	// * Returns the degree of this curve which corresponds to the number of
1922
	// * overall control points (including start and end point) used to define
1923
	// the
1924
	// * curve. The degree is zero-based, so a line (linear curve) will have
1925
	// * degree <code>1</code>, a quadratic curve will have degree
1926
	// <code>2</code>,
1927
	// * and so on. <code>1</code> in case of a
1928
	// *
1929
	// * @return The degree of this {@link ICurve}, which corresponds to the
1930
	// * zero-based overall number of control points (including start and
1931
	// * end point) used to define this {@link ICurve}.
1932
	// */
1933
	// public int getDegree() {
1934
	// return getCtrls().length + 1;
1935
	// }
1936
	//
1937
	// /**
1938
	// * Returns an array of points that represent this {@link BezierCurve},
1939
	// i.e.
1940
	// * the start point, the inner control points, and the end points.
1941
	// *
1942
	// * @return an array of points representing the control points (including
1943
	// * start and end point) of this {@link BezierCurve}
1944
	// */
1945
	// public Point[] getPoints() {
1946
	// Point[] points = new Point[ctrlCoordinates.length / 2 + 2];
1947
	// points[0] = new Point(x1, y1);
1948
	// points[points.length - 1] = new Point(x2, y2);
1949
	// for (int i = 1; i < points.length - 1; i++) {
1950
	// points[i] = new Point(ctrlCoordinates[2 * i - 2],
1951
	// ctrlCoordinates[2 * i - 1]);
1952
	// }
1953
	// return points;
1954
	// }
1955
	//
1956
	// /**
1957
	// * {@inheritDoc}
1958
	// *
1959
	// * @see org.eclipse.gef4.geometry.planar.ICurve#getP1()
1960
	// */
1961
	// public Point getP1() {
1962
	// return new Point(x1, y1);
1963
	// }
1964
	//
1965
	// /**
1966
	// * {@inheritDoc}
1967
	// *
1968
	// * @see org.eclipse.gef4.geometry.planar.ICurve#getP2()
1969
	// */
1970
	// public Point getP2() {
1971
	// return new Point(x2, y2);
1972
	// }
1973
	//
1974
	// /**
1975
	// * {@inheritDoc}
1976
	// *
1977
	// * @see org.eclipse.gef4.geometry.planar.ICurve#getX1()
1978
	// */
1979
	// public double getX1() {
1980
	// return x1;
1981
	// }
1982
	//
1983
	// /**
1984
	// * {@inheritDoc}
1985
	// *
1986
	// * @see org.eclipse.gef4.geometry.planar.ICurve#getX2()
1987
	// */
1988
	// public double getX2() {
1989
	// return x2;
1990
	// }
1991
	//
1992
	// /**
1993
	// * {@inheritDoc}
1994
	// *
1995
	// * @see org.eclipse.gef4.geometry.planar.ICurve#getY1()
1996
	// */
1997
	// public double getY1() {
1998
	// return y1;
1999
	// }
2000
	//
2001
	// /**
2002
	// * {@inheritDoc}
2003
	// *
2004
	// * @see org.eclipse.gef4.geometry.planar.ICurve#getY2()
2005
	// */
2006
	// public double getY2() {
2007
	// return y2;
2008
	// }
2009
	//
2010
	// protected void setCtrl(int i, Point p) {
2011
	// setCtrlX(i, p.x);
2012
	// setCtrlY(i, p.y);
2013
	// }
2014
	//
2015
	// public void setCtrls(Point... ctrls) {
2016
	// ctrlCoordinates = PointListUtils.toCoordinatesArray(ctrls);
2017
	// }
2018
	//
2019
	// protected void setCtrlX(int i, double x) {
2020
	// // TODO: enlarge array if its too small
2021
	// ctrlCoordinates[2 * i] = x;
2022
	// }
2023
	//
2024
	// protected void setCtrlY(int i, double y) {
2025
	// // TODO: enlarge array if its too small
2026
	// ctrlCoordinates[2 * i + 1] = y;
2027
	// }
2028
	//
2029
	// /**
2030
	// * Sets the start {@link Point} of this {@link BezierCurve} to the given
2031
	// * {@link Point} p1.
2032
	// *
2033
	// * @param p1
2034
	// * the new start {@link Point}
2035
	// */
2036
	// public void setP1(Point p1) {
2037
	// this.x1 = p1.x;
2038
	// this.y1 = p1.y;
2039
	// }
2040
	//
2041
	// /**
2042
	// * Sets the end {@link Point} of this {@link BezierCurve} to the given
2043
	// * {@link Point} p2.
2044
	// *
2045
	// * @param p2
2046
	// * the new end {@link Point}
2047
	// */
2048
	// public void setP2(Point p2) {
2049
	// this.x2 = p2.x;
2050
	// this.y2 = p2.y;
2051
	// }
2052
	//
2053
	// /**
2054
	// * Sets the x-coordinate of the start {@link Point} of this
2055
	// * {@link BezierCurve} to x1.
2056
	// *
2057
	// * @param x1
2058
	// * the new start {@link Point}'s x-coordinate
2059
	// */
2060
	// public void setX1(double x1) {
2061
	// this.x1 = x1;
2062
	// }
2063
	//
2064
	// /**
2065
	// * Sets the x-coordinate of the end {@link Point} of this
2066
	// * {@link BezierCurve} to x2.
2067
	// *
2068
	// * @param x2
2069
	// * the new end {@link Point}'s x-coordinate
2070
	// */
2071
	// public void setX2(double x2) {
2072
	// this.x2 = x2;
2073
	// }
2074
	//
2075
	// /**
2076
	// * Sets the y-coordinate of the start {@link Point} of this
2077
	// * {@link BezierCurve} to y1.
2078
	// *
2079
	// * @param y1
2080
	// * the new start {@link Point}'s y-coordinate
2081
	// */
2082
	// public void setY1(double y1) {
2083
	// this.y1 = y1;
2084
	// }
2085
	//
2086
	// /**
2087
	// * Sets the y-coordinate of the end {@link Point} of this
2088
	// * {@link BezierCurve} to y2.
2089
	// *
2090
	// * @param y2
2091
	// * the new end {@link Point}'s y-coordinate
2092
	// */
2093
	// public void setY2(double y2) {
2094
	// this.y2 = y2;
2095
	// }
2096
280
}
2097
}
(-)a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/BezierSpline.java (-14 / +61 lines)
Lines 14-52 package org.eclipse.gef4.geometry.planar; Link Here
14
import org.eclipse.gef4.geometry.Point;
14
import org.eclipse.gef4.geometry.Point;
15
import org.eclipse.gef4.geometry.transform.AffineTransform;
15
import org.eclipse.gef4.geometry.transform.AffineTransform;
16
16
17
public class BezierSpline implements IPolyCurve {
17
public class BezierSpline implements ICurve {
18
18
19
	public BezierCurve[] getCurves() {
19
	public boolean contains(Point p) {
20
		// TODO Auto-generated method stub
21
		return false;
22
	}
23
24
	public Rectangle getBounds() {
20
		// TODO Auto-generated method stub
25
		// TODO Auto-generated method stub
21
		return null;
26
		return null;
22
	}
27
	}
23
28
24
	public boolean contains(Point p) {
29
	public IGeometry getCopy() {
25
		throw new UnsupportedOperationException("Not yet implemented.");
30
		// TODO Auto-generated method stub
31
		return null;
32
	}
33
34
	public Point[] getIntersections(ICurve g) {
35
		// TODO Auto-generated method stub
36
		return null;
26
	}
37
	}
27
38
28
	public boolean contains(Rectangle r) {
39
	public Point getP1() {
29
		throw new UnsupportedOperationException("Not yet implemented.");
40
		// TODO Auto-generated method stub
41
		return null;
30
	}
42
	}
31
43
32
	public Rectangle getBounds() {
44
	public Point getP2() {
33
		throw new UnsupportedOperationException("Not yet implemented.");
45
		// TODO Auto-generated method stub
46
		return null;
34
	}
47
	}
35
48
36
	public IGeometry getTransformed(AffineTransform t) {
49
	public IGeometry getTransformed(AffineTransform t) {
37
		throw new UnsupportedOperationException("Not yet implemented.");
50
		// TODO Auto-generated method stub
51
		return null;
52
	}
53
54
	public double getX1() {
55
		// TODO Auto-generated method stub
56
		return 0;
57
	}
58
59
	public double getX2() {
60
		// TODO Auto-generated method stub
61
		return 0;
62
	}
63
64
	public double getY1() {
65
		// TODO Auto-generated method stub
66
		return 0;
67
	}
68
69
	public double getY2() {
70
		// TODO Auto-generated method stub
71
		return 0;
72
	}
73
74
	public boolean intersects(ICurve c) {
75
		// TODO Auto-generated method stub
76
		return false;
38
	}
77
	}
39
78
40
	public boolean intersects(Rectangle r) {
79
	public boolean overlaps(ICurve c) {
41
		throw new UnsupportedOperationException("Not yet implemented.");
80
		// TODO Auto-generated method stub
81
		return false;
82
	}
83
84
	public BezierCurve[] toBezier() {
85
		// TODO Auto-generated method stub
86
		return null;
42
	}
87
	}
43
88
44
	public Path toPath() {
89
	public Path toPath() {
45
		throw new UnsupportedOperationException("Not yet implemented.");
90
		// TODO Auto-generated method stub
91
		return null;
46
	}
92
	}
47
93
48
	public IGeometry getCopy() {
94
	public boolean touches(IGeometry g) {
49
		throw new UnsupportedOperationException("Not yet implemented.");
95
		// TODO Auto-generated method stub
96
		return false;
50
	}
97
	}
51
98
52
}
99
}
(-)a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/CubicCurve.java (-237 / +13 lines)
Lines 12-25 Link Here
12
 *******************************************************************************/
12
 *******************************************************************************/
13
package org.eclipse.gef4.geometry.planar;
13
package org.eclipse.gef4.geometry.planar;
14
14
15
import java.util.Arrays;
16
import java.util.HashSet;
17
18
import org.eclipse.gef4.geometry.Point;
15
import org.eclipse.gef4.geometry.Point;
19
import org.eclipse.gef4.geometry.transform.AffineTransform;
16
import org.eclipse.gef4.geometry.transform.AffineTransform;
20
import org.eclipse.gef4.geometry.utils.CurveUtils;
21
import org.eclipse.gef4.geometry.utils.PolynomCalculationUtils;
17
import org.eclipse.gef4.geometry.utils.PolynomCalculationUtils;
22
import org.eclipse.gef4.geometry.utils.PrecisionUtils;
23
18
24
/**
19
/**
25
 * Represents the geometric shape of a cubic Bézier curve.
20
 * Represents the geometric shape of a cubic Bézier curve.
Lines 31-76 public class CubicCurve extends BezierCurve { Link Here
31
26
32
	private static final long serialVersionUID = 1L;
27
	private static final long serialVersionUID = 1L;
33
28
34
	private static Point[] getIntersections(CubicCurve p, double ps, double pe,
35
			Line l) {
36
		// parameter convergence test
37
		double pm = (ps + pe) / 2;
38
39
		if (PrecisionUtils.equal(ps, pe, -2)) {
40
			return new Point[] { p.get(pm) };
41
		}
42
43
		// no parameter convergence
44
		// clip the curve
45
		CubicCurve pc = p.clip(ps, pe);
46
47
		// check the control polygon
48
		Polygon polygon = pc.getControlPolygon();
49
50
		if (polygon.intersects(l)) {
51
			// area test
52
			if (PrecisionUtils.equal(polygon.getBounds().getArea(), 0, -2)) {
53
				// line/line intersection fallback for such small curves
54
				Point poi = new Line(pc.getP1(), pc.getP2()).getIntersection(l);
55
				if (poi != null) {
56
					return new Point[] { poi };
57
				}
58
				return new Point[] {};
59
			}
60
61
			// "split" the curve to get precise intersections
62
			HashSet<Point> intersections = new HashSet<Point>();
63
64
			intersections.addAll(Arrays.asList(getIntersections(p, ps, pm, l)));
65
			intersections.addAll(Arrays.asList(getIntersections(p, pm, pe, l)));
66
67
			return intersections.toArray(new Point[] {});
68
		}
69
70
		// no intersections
71
		return new Point[] {};
72
	}
73
74
	/**
29
	/**
75
	 * Constructs a new {@link CubicCurve} object with the given sequence of x-
30
	 * Constructs a new {@link CubicCurve} object with the given sequence of x-
76
	 * and y-coordinates of the start-, the first and second control-, and the
31
	 * and y-coordinates of the start-, the first and second control-, and the
Lines 155-192 public class CubicCurve extends BezierCurve { Link Here
155
	 * @return the {@link CubicCurve} on the interval [t1, t2]
110
	 * @return the {@link CubicCurve} on the interval [t1, t2]
156
	 */
111
	 */
157
	public CubicCurve clip(double t1, double t2) {
112
	public CubicCurve clip(double t1, double t2) {
158
		return CurveUtils.clip(this, t1, t2);
113
		return super.getClipped(t1, t2).toCubic();
159
	}
160
161
	/**
162
	 * @see IGeometry#contains(Point)
163
	 */
164
	public boolean contains(Point p) {
165
		return CurveUtils.contains(this, p);
166
	}
114
	}
167
115
168
	@Override
116
	@Override
169
	public boolean equals(Object other) {
117
	public boolean equals(Object other) {
170
		CubicCurve o = (CubicCurve) other;
118
		CubicCurve o = (CubicCurve) other;
171
119
172
		AbstractPointListBasedGeometry myPoly = getControlPolygon();
120
		Polygon myPoly = getControlPolygon();
173
		AbstractPointListBasedGeometry otherPoly = o.getControlPolygon();
121
		Polygon otherPoly = o.getControlPolygon();
174
122
175
		return myPoly.equals(otherPoly);
123
		return myPoly.equals(otherPoly);
176
	}
124
	}
177
125
178
	/**
126
	/**
179
	 * Get a single {@link Point} on this CubicCurve at parameter t.
180
	 * 
181
	 * @param t
182
	 *            in range [0,1]
183
	 * @return the {@link Point} at parameter t
184
	 */
185
	public Point get(double t) {
186
		return CurveUtils.get(this, t);
187
	}
188
189
	/**
190
	 * @see IGeometry#getBounds()
127
	 * @see IGeometry#getBounds()
191
	 */
128
	 */
192
	public Rectangle getBounds() {
129
	public Rectangle getBounds() {
Lines 281-287 public class CubicCurve extends BezierCurve { Link Here
281
	 * @return the first control {@link Point}'s x-coordinate.
218
	 * @return the first control {@link Point}'s x-coordinate.
282
	 */
219
	 */
283
	public double getCtrlX1() {
220
	public double getCtrlX1() {
284
		return getCtrlX(0);
221
		return getPoint(1).x;
285
	}
222
	}
286
223
287
	/**
224
	/**
Lines 290-296 public class CubicCurve extends BezierCurve { Link Here
290
	 * @return the first control {@link Point}'s y-coordinate.
227
	 * @return the first control {@link Point}'s y-coordinate.
291
	 */
228
	 */
292
	public double getCtrlY1() {
229
	public double getCtrlY1() {
293
		return getCtrlY(0);
230
		return getPoint(1).y;
294
	}
231
	}
295
232
296
	/**
233
	/**
Lines 308-314 public class CubicCurve extends BezierCurve { Link Here
308
	 * @return the second control {@link Point}'s x-coordinate.
245
	 * @return the second control {@link Point}'s x-coordinate.
309
	 */
246
	 */
310
	public double getCtrlX2() {
247
	public double getCtrlX2() {
311
		return getCtrlX(1);
248
		return getPoint(2).x;
312
	}
249
	}
313
250
314
	/**
251
	/**
Lines 317-468 public class CubicCurve extends BezierCurve { Link Here
317
	 * @return the second control {@link Point}'s y-coordinate.
254
	 * @return the second control {@link Point}'s y-coordinate.
318
	 */
255
	 */
319
	public double getCtrlY2() {
256
	public double getCtrlY2() {
320
		return getCtrlY(1);
257
		return getPoint(2).y;
321
	}
322
323
	/**
324
	 * Returns the points of intersection between this {@link CubicCurve} and
325
	 * the given {@link Arc}.
326
	 * 
327
	 * @param arc
328
	 *            The {@link Arc} to test for intersections
329
	 * @return the points of intersection.
330
	 */
331
	public Point[] getIntersections(Arc arc) {
332
		HashSet<Point> intersections = new HashSet<Point>();
333
334
		for (CubicCurve seg : arc.getSegments()) {
335
			intersections.addAll(Arrays.asList(getIntersections(seg)));
336
		}
337
338
		return intersections.toArray(new Point[] {});
339
	}
340
341
	/**
342
	 * Returns the points of intersection between this {@link CubicCurve} and
343
	 * the given other {@link CubicCurve}.
344
	 * 
345
	 * @param other
346
	 * @return the points of intersection
347
	 */
348
	public Point[] getIntersections(CubicCurve other) {
349
		if (equals(other)) {
350
			return new Point[] {};
351
		}
352
		return CurveUtils.getIntersections(this, other);
353
	}
354
355
	/**
356
	 * Returns the points of intersection between this {@link CubicCurve} and
357
	 * the given {@link Ellipse}.
358
	 * 
359
	 * @param e
360
	 *            The {@link Ellipse} to test for intersections
361
	 * @return the points of intersection.
362
	 */
363
	public Point[] getIntersections(Ellipse e) {
364
		return e.getIntersections(this);
365
	}
366
367
	/**
368
	 * Returns the points of intersection between this {@link CubicCurve} and
369
	 * the given {@link Line} l.
370
	 * 
371
	 * @param l
372
	 * @return the points of intersection
373
	 */
374
	public Point[] getIntersections(Line l) {
375
		return getIntersections(this, 0, 1, l);
376
	}
377
378
	/**
379
	 * Returns the points of intersection between this {@link CubicCurve} and
380
	 * the given {@link Polygon}.
381
	 * 
382
	 * @param p
383
	 *            The {@link Polygon} to test for intersections
384
	 * @return the points of intersection.
385
	 */
386
	public Point[] getIntersections(Polygon p) {
387
		return p.getIntersections(this);
388
	}
389
390
	/**
391
	 * Returns the points of intersection between this {@link CubicCurve} and
392
	 * the given {@link Polyline}.
393
	 * 
394
	 * @param p
395
	 *            The {@link Polyline} to test for intersections
396
	 * @return the points of intersection.
397
	 */
398
	public Point[] getIntersections(Polyline p) {
399
		HashSet<Point> intersections = new HashSet<Point>();
400
401
		for (Line seg : p.getCurves()) {
402
			intersections.addAll(Arrays.asList(getIntersections(seg)));
403
		}
404
405
		return intersections.toArray(new Point[] {});
406
	}
407
408
	/**
409
	 * Returns the points of intersection between this {@link CubicCurve} and
410
	 * the given {@link QuadraticCurve}.
411
	 * 
412
	 * @param c
413
	 *            The {@link QuadraticCurve} to test for intersections
414
	 * @return the points of intersection.
415
	 */
416
	public Point[] getIntersections(QuadraticCurve c) {
417
		return getIntersections(c.getElevated());
418
	}
419
420
	/**
421
	 * Returns the points of intersection between this {@link CubicCurve} and
422
	 * the given {@link Rectangle}.
423
	 * 
424
	 * @param r
425
	 *            The {@link Rectangle} to test for intersections
426
	 * @return the points of intersection.
427
	 */
428
	public Point[] getIntersections(Rectangle r) {
429
		HashSet<Point> intersections = new HashSet<Point>();
430
431
		for (Line seg : r.getOutlineSegments()) {
432
			intersections.addAll(Arrays.asList(getIntersections(seg)));
433
		}
434
435
		return intersections.toArray(new Point[] {});
436
	}
437
438
	/**
439
	 * Returns the points of intersection between this {@link CubicCurve} and
440
	 * the given {@link RoundedRectangle}.
441
	 * 
442
	 * @param rr
443
	 *            The {@link RoundedRectangle} to test for intersections
444
	 * @return the points of intersection.
445
	 */
446
	public Point[] getIntersections(RoundedRectangle rr) {
447
		HashSet<Point> intersections = new HashSet<Point>();
448
449
		// line segments
450
		intersections.addAll(Arrays.asList(getIntersections(rr.getTop())));
451
		intersections.addAll(Arrays.asList(getIntersections(rr.getLeft())));
452
		intersections.addAll(Arrays.asList(getIntersections(rr.getBottom())));
453
		intersections.addAll(Arrays.asList(getIntersections(rr.getRight())));
454
455
		// arc segments
456
		intersections
457
				.addAll(Arrays.asList(getIntersections(rr.getTopRightArc())));
458
		intersections
459
				.addAll(Arrays.asList(getIntersections(rr.getTopLeftArc())));
460
		intersections.addAll(Arrays.asList(getIntersections(rr
461
				.getBottomLeftArc())));
462
		intersections.addAll(Arrays.asList(getIntersections(rr
463
				.getBottomRightArc())));
464
465
		return intersections.toArray(new Point[] {});
466
	}
258
	}
467
259
468
	/**
260
	/**
Lines 475-497 public class CubicCurve extends BezierCurve { Link Here
475
	}
267
	}
476
268
477
	/**
269
	/**
478
	 * Tests if this {@link CubicCurve} intersects the given {@link Line} r.
479
	 * 
480
	 * @param r
481
	 * @return true if they intersect, false otherwise
482
	 */
483
	public boolean intersects(Line r) {
484
		return getIntersections(r).length > 0;
485
	}
486
487
	/**
488
	 * @see org.eclipse.gef4.geometry.planar.IGeometry#intersects(Rectangle)
489
	 */
490
	public boolean intersects(Rectangle r) {
491
		return false;
492
	}
493
494
	/**
495
	 * Sets the first control {@link Point} to the given {@link Point} ctrl1.
270
	 * Sets the first control {@link Point} to the given {@link Point} ctrl1.
496
	 * 
271
	 * 
497
	 * @param ctrl1
272
	 * @param ctrl1
Lines 510-516 public class CubicCurve extends BezierCurve { Link Here
510
	 *            the new first control {@link Point}'s x-coordinate
285
	 *            the new first control {@link Point}'s x-coordinate
511
	 */
286
	 */
512
	public void setCtrl1X(double ctrl1x) {
287
	public void setCtrl1X(double ctrl1x) {
513
		setCtrlX(0, ctrl1x);
288
		setPoint(1, new Point(ctrl1x, getCtrlY1()));
514
	}
289
	}
515
290
516
	/**
291
	/**
Lines 521-527 public class CubicCurve extends BezierCurve { Link Here
521
	 *            the new first control {@link Point}'s y-coordinate
296
	 *            the new first control {@link Point}'s y-coordinate
522
	 */
297
	 */
523
	public void setCtrl1Y(double ctrl1y) {
298
	public void setCtrl1Y(double ctrl1y) {
524
		setCtrlY(0, ctrl1y);
299
		setPoint(1, new Point(getCtrlX1(), ctrl1y));
525
	}
300
	}
526
301
527
	/**
302
	/**
Lines 543-549 public class CubicCurve extends BezierCurve { Link Here
543
	 *            the new second control {@link Point}'s x-coordinate
318
	 *            the new second control {@link Point}'s x-coordinate
544
	 */
319
	 */
545
	public void setCtrl2X(double ctrl2x) {
320
	public void setCtrl2X(double ctrl2x) {
546
		setCtrlX(1, ctrl2x);
321
		setPoint(2, new Point(ctrl2x, getCtrlY2()));
547
	}
322
	}
548
323
549
	/**
324
	/**
Lines 554-560 public class CubicCurve extends BezierCurve { Link Here
554
	 *            the new second control {@link Point}'s y-coordinate
329
	 *            the new second control {@link Point}'s y-coordinate
555
	 */
330
	 */
556
	public void setCtrl2Y(double ctrl2y) {
331
	public void setCtrl2Y(double ctrl2y) {
557
		setCtrlY(1, ctrl2y);
332
		setPoint(2, new Point(getCtrlX2(), ctrl2y));
558
	}
333
	}
559
334
560
	/**
335
	/**
Lines 586-592 public class CubicCurve extends BezierCurve { Link Here
586
	 * @return the two {@link CubicCurve}s
361
	 * @return the two {@link CubicCurve}s
587
	 */
362
	 */
588
	public CubicCurve[] split(double t) {
363
	public CubicCurve[] split(double t) {
589
		return CurveUtils.split(this, t);
364
		BezierCurve[] split = super.split(t);
365
		return new CubicCurve[] { split[0].toCubic(), split[1].toCubic() };
590
	}
366
	}
591
367
592
	/**
368
	/**
(-)a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Ellipse.java (-305 / +34 lines)
Lines 20-25 import java.util.List; Link Here
20
20
21
import org.eclipse.gef4.geometry.Point;
21
import org.eclipse.gef4.geometry.Point;
22
import org.eclipse.gef4.geometry.transform.AffineTransform;
22
import org.eclipse.gef4.geometry.transform.AffineTransform;
23
import org.eclipse.gef4.geometry.utils.CurveUtils;
23
import org.eclipse.gef4.geometry.utils.PrecisionUtils;
24
import org.eclipse.gef4.geometry.utils.PrecisionUtils;
24
25
25
/**
26
/**
Lines 33-39 import org.eclipse.gef4.geometry.utils.PrecisionUtils; Link Here
33
 * @author anyssen
34
 * @author anyssen
34
 * @author Matthias Wienand
35
 * @author Matthias Wienand
35
 */
36
 */
36
public class Ellipse extends AbstractRectangleBasedGeometry implements IShape {
37
public class Ellipse extends AbstractRectangleBasedGeometry<Ellipse> implements
38
		IShape {
37
39
38
	private static final long serialVersionUID = 1L;
40
	private static final long serialVersionUID = 1L;
39
41
Lines 70-107 public class Ellipse extends AbstractRectangleBasedGeometry implements IShape { Link Here
70
	}
72
	}
71
73
72
	/**
74
	/**
73
	 * Tests whether the given {@link CubicCurve} is fully contained within this
74
	 * {@link Ellipse}.
75
	 * 
76
	 * @param c
77
	 *            the {@link CubicCurve} to test for containment
78
	 * @return <code>true</code> in case the given {@link CubicCurve} is fully
79
	 *         contained, <code>false</code> otherwise
80
	 */
81
	public boolean contains(CubicCurve c) {
82
		return contains(c.getP1()) && contains(c.getP2())
83
				&& getIntersections(c).length == 0;
84
	}
85
86
	/**
87
	 * Tests whether the given {@link Ellipse} is fully contained within this
88
	 * {@link Ellipse}.
89
	 * 
90
	 * @param o
91
	 *            the {@link Ellipse} to test for containment
92
	 * @return <code>true</code> in case the given {@link Ellipse} is fully
93
	 *         contained, <code>false</code> otherwise
94
	 */
95
	public boolean contains(Ellipse o) {
96
		for (CubicCurve seg : o.getOutlineSegments()) {
97
			if (!contains(seg)) {
98
				return false;
99
			}
100
		}
101
		return true;
102
	}
103
104
	/**
105
	 * Tests whether the given {@link Line} is fully contained within this
75
	 * Tests whether the given {@link Line} is fully contained within this
106
	 * {@link Ellipse}.
76
	 * {@link Ellipse}.
107
	 * 
77
	 * 
Lines 118-187 public class Ellipse extends AbstractRectangleBasedGeometry implements IShape { Link Here
118
	 * @see IGeometry#contains(Point)
88
	 * @see IGeometry#contains(Point)
119
	 */
89
	 */
120
	public boolean contains(Point p) {
90
	public boolean contains(Point p) {
121
		// point has to fulfill (x/a)^2 + (y/b)^2 <= 1, where a = width/2 and b
91
		/*
122
		// = height/2, if ellipse is centered around origin, so we have to
92
		 * point has to fulfill (x/a)^2 + (y/b)^2 <= 1, where a = width/2 and b
123
		// normalize point p by subtracting the center
93
		 * = height/2, if ellipse is centered around origin, so we have to
94
		 * normalize point p by subtracting the center
95
		 */
124
		double normalizedX = p.x - (x + width / 2);
96
		double normalizedX = p.x - (x + width / 2);
125
		double normalizedY = p.y - (y + height / 2);
97
		double normalizedY = p.y - (y + height / 2);
98
126
		// then check if it fulfills the ellipse equation
99
		// then check if it fulfills the ellipse equation
127
		return PrecisionUtils.smallerEqual(normalizedX * normalizedX
100
		if (PrecisionUtils.smallerEqual(normalizedX * normalizedX
128
				/ (width * width * 0.25d) + normalizedY * normalizedY
101
				/ (width * width * 0.25d) + normalizedY * normalizedY
129
				/ (height * height * 0.25d), 1.0d);
102
				/ (height * height * 0.25d), 1d)) {
130
	}
103
			return true;
131
132
	/**
133
	 * Tests if this {@link Ellipse} contains the given {@link Polygon} polygon.
134
	 * 
135
	 * @param polygon
136
	 * @return true if it is contained, false otherwise
137
	 */
138
	public boolean contains(Polygon polygon) {
139
		for (Line segment : polygon.getOutlineSegments()) {
140
			if (!contains(segment)) {
141
				return false;
142
			}
143
		}
104
		}
144
		return true;
145
	}
146
105
147
	/**
106
		// is it "on" the outline?
148
	 * Tests if this {@link Ellipse} contains the given {@link Polyline}
107
		for (CubicCurve seg : getOutlineSegments()) {
149
	 * polyline.
108
			if (seg.contains(p)) {
150
	 * 
109
				return true;
151
	 * @param polyline
152
	 * @return true if it is contained, false otherwise
153
	 */
154
	public boolean contains(Polyline polyline) {
155
		for (Line segment : polyline.getCurves()) {
156
			if (!contains(segment)) {
157
				return false;
158
			}
110
			}
159
		}
111
		}
160
		return true;
161
	}
162
112
163
	/**
113
		// TODO: what about the small space between outline and ellipse area?
164
	 * Tests whether the given {@link QuadraticCurve} is fully contained within
114
165
	 * this {@link Ellipse}.
115
		return false;
166
	 * 
167
	 * @param c
168
	 *            the {@link QuadraticCurve} to test for containment
169
	 * @return <code>true</code> in case the given {@link QuadraticCurve} is
170
	 *         fully contained, <code>false</code> otherwise
171
	 */
172
	public boolean contains(QuadraticCurve c) {
173
		return contains(c.getP1()) && contains(c.getP2())
174
				&& getIntersections(c).length == 0;
175
	}
116
	}
176
117
177
	/**
118
	/**
178
	 * @see IGeometry#contains(Rectangle)
119
	 * @see IShape#contains(IGeometry)
179
	 */
120
	 */
180
	public boolean contains(Rectangle r) {
121
	public boolean contains(IGeometry g) {
181
		// has to contain all border points of the rectangle, to contain it
122
		return CurveUtils.contains(this, g);
182
		// completely
183
		return contains(r.getTopLeft()) && contains(r.getTopRight())
184
				&& contains(r.getBottomLeft()) && contains(r.getBottomRight());
185
	}
123
	}
186
124
187
	/**
125
	/**
Lines 248-289 public class Ellipse extends AbstractRectangleBasedGeometry implements IShape { Link Here
248
	}
186
	}
249
187
250
	/**
188
	/**
251
	 * Calculates the points of intersection of this {@link Ellipse} and the
189
	 * Calculates the intersections of this {@link Ellipse} with the given
252
	 * given {@link Arc}.
190
	 * {@link ICurve}.
253
	 * 
191
	 * 
254
	 * @param arc
192
	 * @param c
255
	 *            The {@link Arc} to compute intersection points with
193
	 * @return {@link Point}s of intersection
256
	 * @return the points of intersection of this {@link Ellipse} and the given
257
	 *         {@link Arc}.
258
	 */
259
	public Point[] getIntersections(Arc arc) {
260
		HashSet<Point> intersections = new HashSet<Point>();
261
262
		for (CubicCurve mySeg : getOutlineSegments()) {
263
			for (CubicCurve arcSeg : arc.getSegments()) {
264
				intersections.addAll(Arrays.asList(mySeg
265
						.getIntersections(arcSeg)));
266
			}
267
		}
268
269
		return intersections.toArray(new Point[] {});
270
	}
271
272
	/**
273
	 * Calculates the points of intersection of this {@link Ellipse} and the
274
	 * given {@link CubicCurve}.
275
	 * 
276
	 * @param curve
277
	 * @return points of intersection
278
	 */
194
	 */
279
	public Point[] getIntersections(CubicCurve curve) {
195
	public Point[] getIntersections(ICurve c) {
280
		HashSet<Point> intersections = new HashSet<Point>();
196
		if (c instanceof Line) {
281
197
			return getIntersections((Line) c);
282
		for (CubicCurve seg : getOutlineSegments()) {
283
			intersections.addAll(Arrays.asList(curve.getIntersections(seg)));
284
		}
198
		}
285
199
		return CurveUtils.getIntersections(c, this);
286
		return intersections.toArray(new Point[] {});
287
	}
200
	}
288
201
289
	/**
202
	/**
Lines 291-297 public class Ellipse extends AbstractRectangleBasedGeometry implements IShape { Link Here
291
	 * {@link Ellipse}.
204
	 * {@link Ellipse}.
292
	 * 
205
	 * 
293
	 * @param e2
206
	 * @param e2
294
	 * @return points of intersection
207
	 * @return {@link Point}s of intersection
295
	 */
208
	 */
296
	public Point[] getIntersections(Ellipse e2) {
209
	public Point[] getIntersections(Ellipse e2) {
297
		if (equals(e2)) {
210
		if (equals(e2)) {
Lines 419-535 public class Ellipse extends AbstractRectangleBasedGeometry implements IShape { Link Here
419
		return intersections.toArray(new Point[] {});
332
		return intersections.toArray(new Point[] {});
420
	}
333
	}
421
334
422
	/**
335
	public IPolyCurve getOutline() {
423
	 * Returns the intersection points of this {@link Ellipse}'s outline with
336
		return CurveUtils.getOutline(this);
424
	 * the given {@link Polygon}'s outline.
425
	 * 
426
	 * @param polygon
427
	 *            the {@link Polygon} to test for intersection
428
	 * @return an array containing the intersection points of this
429
	 *         {@link Ellipse}'s outline with the given {@link Polyline}'s
430
	 *         outline in case such intersection points exist, an empty array
431
	 *         otherwise
432
	 */
433
	public Point[] getIntersections(Polygon polygon) {
434
		HashSet<Point> intersections = new HashSet<Point>();
435
436
		for (Line segment : polygon.getOutlineSegments()) {
437
			for (Point intersection : getIntersections(segment)) {
438
				intersections.add(intersection);
439
			}
440
		}
441
442
		return intersections.toArray(new Point[] {});
443
	}
444
445
	/**
446
	 * Returns the intersection points of this {@link Ellipse}'s outline with
447
	 * the given {@link Polyline}.
448
	 * 
449
	 * @param polyline
450
	 *            the {@link Polyline} to test for intersection
451
	 * @return an array containing the intersection points of this
452
	 *         {@link Ellipse}'s outline with the given {@link Polyline} in case
453
	 *         such intersection points exist, an empty array otherwise
454
	 */
455
	public Point[] getIntersections(Polyline polyline) {
456
		List<Point> intersections = new ArrayList<Point>();
457
458
		for (Line segment : polyline.getCurves()) {
459
			for (Point intersection : getIntersections(segment)) {
460
				intersections.add(intersection);
461
			}
462
		}
463
464
		return intersections.toArray(new Point[] {});
465
	}
466
467
	/**
468
	 * Computes and returns the points of intersection of this {@link Ellipse}'s
469
	 * outline with the given {@link QuadraticCurve}.
470
	 * 
471
	 * @param c
472
	 * @return the points of intersection of this {@link Ellipse}'s outline with
473
	 *         the given {@link QuadraticCurve}
474
	 */
475
	public Point[] getIntersections(QuadraticCurve c) {
476
		return getIntersections(c.getElevated());
477
	}
478
479
	/**
480
	 * Returns the intersection points of this {@link Ellipse}'s outline with
481
	 * the given {@link Rectangle}'s outline.
482
	 * 
483
	 * @param rectangle
484
	 *            the {@link Rectangle} to test for intersection
485
	 * @return an array containing the intersection points of this
486
	 *         {@link Ellipse}'s outline with the given {@link Rectangle}'s
487
	 *         outline in case such intersection points exist, an empty array
488
	 *         otherwise
489
	 */
490
	public Point[] getIntersections(Rectangle rectangle) {
491
		HashSet<Point> intersections = new HashSet<Point>();
492
493
		for (Line segment : rectangle.getOutlineSegments()) {
494
			for (Point intersection : getIntersections(segment)) {
495
				intersections.add(intersection);
496
			}
497
		}
498
499
		return intersections.toArray(new Point[] {});
500
	}
501
502
	/**
503
	 * Returns the intersection points of this {@link Ellipse}'s outline with
504
	 * the given {@link RoundedRectangle}'s outline.
505
	 * 
506
	 * @param rr
507
	 *            The {@link RoundedRectangle} to test for intersection
508
	 * @return an array containing the {@link Point}s of intersection of this
509
	 *         {@link Ellipse}'s outline with the given {@link RoundedRectangle}
510
	 *         's outline in case such intersection points exist, an empty array
511
	 *         otherwise.
512
	 */
513
	public Point[] getIntersections(RoundedRectangle rr) {
514
		HashSet<Point> intersections = new HashSet<Point>();
515
516
		// line segments
517
		intersections.addAll(Arrays.asList(getIntersections(rr.getTop())));
518
		intersections.addAll(Arrays.asList(getIntersections(rr.getLeft())));
519
		intersections.addAll(Arrays.asList(getIntersections(rr.getBottom())));
520
		intersections.addAll(Arrays.asList(getIntersections(rr.getRight())));
521
522
		// arc segments
523
		intersections
524
				.addAll(Arrays.asList(getIntersections(rr.getTopRightArc())));
525
		intersections
526
				.addAll(Arrays.asList(getIntersections(rr.getTopLeftArc())));
527
		intersections.addAll(Arrays.asList(getIntersections(rr
528
				.getBottomLeftArc())));
529
		intersections.addAll(Arrays.asList(getIntersections(rr
530
				.getBottomRightArc())));
531
532
		return intersections.toArray(new Point[] {});
533
	}
337
	}
534
338
535
	/**
339
	/**
Lines 585-666 public class Ellipse extends AbstractRectangleBasedGeometry implements IShape { Link Here
585
	}
389
	}
586
390
587
	/**
391
	/**
588
	 * Does the given {@link CubicCurve} intersect (including containment) this
589
	 * {@link Ellipse}?
590
	 * 
591
	 * @param c
592
	 * @return true if they intersect, false otherwise
593
	 */
594
	public boolean intersects(CubicCurve c) {
595
		return contains(c.getP1()) && contains(c.getP2())
596
				|| getIntersections(c).length > 0;
597
	}
598
599
	/**
600
	 * At least one common point, which includes containment (check
601
	 * getIntersections() to check if this is a true intersection).
602
	 * 
603
	 * @param l
604
	 *            The {@link Line} to test for intersection.
605
	 * @return <code>true</code> in case this {@link Ellipse} and the given
606
	 *         {@link Line} share at least one common point, <code>false</code>
607
	 *         otherwise
608
	 */
609
	public boolean intersects(Line l) {
610
		return contains(l) || getIntersections(l).length > 0;
611
	}
612
613
	/**
614
	 * Tests if this {@link Ellipse} intersects the given {@link Polygon}
615
	 * polygon.
616
	 * 
617
	 * @param polygon
618
	 * @return true if they intersect, false otherwise
619
	 */
620
	public boolean intersects(Polygon polygon) {
621
		for (Line segment : polygon.getOutlineSegments()) {
622
			if (intersects(segment)) {
623
				return true;
624
			}
625
		}
626
		return false;
627
	}
628
629
	/**
630
	 * Tests if this {@link Ellipse} intersects the given {@link Polyline}
631
	 * polyline.
632
	 * 
633
	 * @param polyline
634
	 * @return true if they intersect, false otherwise
635
	 */
636
	public boolean intersects(Polyline polyline) {
637
		for (Line segment : polyline.getCurves()) {
638
			if (intersects(segment)) {
639
				return true;
640
			}
641
		}
642
		return false;
643
	}
644
645
	/**
646
	 * @see IGeometry#intersects(Rectangle)
647
	 */
648
	public boolean intersects(Rectangle r) {
649
		// quick rejection test
650
		if (!getBounds().intersects(r)) {
651
			return false;
652
		}
653
654
		Line[] segments = r.getOutlineSegments();
655
		for (int i = 0; i < segments.length; i++) {
656
			if (intersects(segments[i])) {
657
				return true;
658
			}
659
		}
660
		return false;
661
	}
662
663
	/**
664
	 * Returns a path representation of this {@link Ellipse}, which is an
392
	 * Returns a path representation of this {@link Ellipse}, which is an
665
	 * approximation of the four segments by means of cubic Bezier curves.
393
	 * approximation of the four segments by means of cubic Bezier curves.
666
	 * 
394
	 * 
Lines 690-693 public class Ellipse extends AbstractRectangleBasedGeometry implements IShape { Link Here
690
		return "Ellipse: (" + x + ", " + y + ", " + //$NON-NLS-3$//$NON-NLS-2$//$NON-NLS-1$
418
		return "Ellipse: (" + x + ", " + y + ", " + //$NON-NLS-3$//$NON-NLS-2$//$NON-NLS-1$
691
				width + ", " + height + ")";//$NON-NLS-2$//$NON-NLS-1$
419
				width + ", " + height + ")";//$NON-NLS-2$//$NON-NLS-1$
692
	}
420
	}
421
693
}
422
}
(-)a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/ICurve.java (+47 lines)
Lines 7-12 Link Here
7
 *
7
 *
8
 * Contributors:
8
 * Contributors:
9
 *     Alexander Nyßen (itemis AG) - initial API and implementation
9
 *     Alexander Nyßen (itemis AG) - initial API and implementation
10
 *     Matthias Wienand (itemis AG) - contribution for Bugzilla #355997
10
 *     
11
 *     
11
 *******************************************************************************/
12
 *******************************************************************************/
12
package org.eclipse.gef4.geometry.planar;
13
package org.eclipse.gef4.geometry.planar;
Lines 68-73 public interface ICurve extends IGeometry { Link Here
68
	 */
69
	 */
69
	public Point getP1();
70
	public Point getP1();
70
71
72
	/**
73
	 * A list of {@link BezierCurve}s that approximate the {@link ICurve}. For
74
	 * example, a {@link Line} or a {@link BezierCurve} in general, could return
75
	 * a list with the curve itself as its only element. But an {@link Ellipse}
76
	 * or an {@link Arc} may return a list of consecutive {@link BezierCurve}s
77
	 * which approximate the curve.
78
	 * 
79
	 * @return a list of {@link BezierCurve}s that approximate the
80
	 *         {@link ICurve}
81
	 */
82
	public BezierCurve[] toBezier();
83
84
	/**
85
	 * Returns the points of intersection between this {@link ICurve} and the
86
	 * given {@link ICurve}.
87
	 * 
88
	 * @param c
89
	 *            the {@link ICurve} to compute intersection points for
90
	 * @return the points of intersection.
91
	 */
92
	public Point[] getIntersections(final ICurve c);
93
94
	/**
95
	 * Tests if this {@link ICurve} and the given {@link ICurve} intersect, i.e.
96
	 * whether a final set of intersection points exists. Two curves intersect
97
	 * if they touch (see {@link IGeometry#touches(IGeometry)}) but not overlap
98
	 * (see {@link ICurve#overlaps(ICurve)}).
99
	 * 
100
	 * @param c
101
	 *            the {@link ICurve} to test for intersections
102
	 * @return <code>true</code> if they intersect, <code>false</code> otherwise
103
	 */
104
	boolean intersects(final ICurve c);
105
106
	/**
107
	 * Tests if this {@link ICurve} and the given {@link ICurve} overlap, i.e.
108
	 * whether an infinite set of intersection points exists. Two curves overlap
109
	 * if they touch (see {@link IGeometry#touches(IGeometry)}) but not
110
	 * intersect (see {@link ICurve#intersects(ICurve)}).
111
	 * 
112
	 * @param c
113
	 *            the {@link ICurve} to test for overlap
114
	 * @return <code>true</code> if they overlap, <code>false</code> otherwise
115
	 */
116
	boolean overlaps(final ICurve c);
117
71
	// start point, end point, control points (optional)
118
	// start point, end point, control points (optional)
72
	// TODO: need to elevate
119
	// TODO: need to elevate
73
}
120
}
(-)a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/IGeometry.java (-23 / +14 lines)
Lines 7-12 Link Here
7
 *
7
 *
8
 * Contributors:
8
 * Contributors:
9
 *     Alexander Nyßen (itemis AG) - initial API and implementation
9
 *     Alexander Nyßen (itemis AG) - initial API and implementation
10
 *     Matthias Wienand (itemis AG) - contribution for Bugzilla #355997
10
 *     
11
 *     
11
 *******************************************************************************/
12
 *******************************************************************************/
12
package org.eclipse.gef4.geometry.planar;
13
package org.eclipse.gef4.geometry.planar;
Lines 37-53 public interface IGeometry extends Cloneable, Serializable { Link Here
37
	boolean contains(final Point p);
38
	boolean contains(final Point p);
38
39
39
	/**
40
	/**
40
	 * Returns <code>true</code> if the given {@link Rectangle} is contained
41
	 * within {@link IGeometry}, <code>false</code> otherwise.
42
	 * 
43
	 * @param r
44
	 *            The {@link Rectangle} to test
45
	 * @return <code>true</code> if the {@link Rectangle} is fully contained
46
	 *         within this {@link IGeometry}
47
	 */
48
	boolean contains(final Rectangle r);
49
50
	/**
51
	 * Returns the smallest {@link Rectangle} fully enclosing this
41
	 * Returns the smallest {@link Rectangle} fully enclosing this
52
	 * {@link IGeometry}.
42
	 * {@link IGeometry}.
53
	 * 
43
	 * 
Lines 56-61 public interface IGeometry extends Cloneable, Serializable { Link Here
56
	 */
46
	 */
57
	Rectangle getBounds();
47
	Rectangle getBounds();
58
48
49
	// TODO: getTightBounds() : Polygon
50
51
	/**
52
	 * Returns <code>true</code> if the input {@link IGeometry} touches this
53
	 * {@link IGeometry}, i.e. there is at least one common point.
54
	 * 
55
	 * @param g
56
	 *            The {@link IGeometry} for the intersection test
57
	 * @return <code>true</code> if the input {@link IGeometry} and this
58
	 *         {@link IGeometry} have at least one common point.
59
	 */
60
	boolean touches(IGeometry g);
61
59
	/**
62
	/**
60
	 * Returns a new {@link IGeometry}, which represents the given
63
	 * Returns a new {@link IGeometry}, which represents the given
61
	 * {@link IGeometry} after the application of the given
64
	 * {@link IGeometry} after the application of the given
Lines 72-89 public interface IGeometry extends Cloneable, Serializable { Link Here
72
	IGeometry getTransformed(final AffineTransform t);
75
	IGeometry getTransformed(final AffineTransform t);
73
76
74
	/**
77
	/**
75
	 * Returns <code>true</code> if the input Rectangle intersects this
76
	 * Geometry, i.e. there is at least one common point. This includes the case
77
	 * that the given rectangle is fully contained.
78
	 * 
79
	 * @param r
80
	 *            The {@link Rectangle} for the intersection test
81
	 * @return <code>true</code> if the input {@link Rectangle} and this
82
	 *         {@link IGeometry} have at least one common point.
83
	 */
84
	boolean intersects(final Rectangle r);
85
86
	/**
87
	 * Converts this {@link IGeometry} into a {@link Path} representation.
78
	 * Converts this {@link IGeometry} into a {@link Path} representation.
88
	 * 
79
	 * 
89
	 * @return A new {@link Path} representation for this {@link IGeometry}.
80
	 * @return A new {@link Path} representation for this {@link IGeometry}.
(-)a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/IPolyCurve.java (-3 / +9 lines)
Lines 11-24 Link Here
11
 *******************************************************************************/
11
 *******************************************************************************/
12
package org.eclipse.gef4.geometry.planar;
12
package org.eclipse.gef4.geometry.planar;
13
13
14
public interface IPolyCurve extends IGeometry {
14
/**
15
 * An {@link IPolyCurve} is an {@link ICurve} that is constituted by multiple
16
 * connected {@link ICurve} segments.
17
 */
18
public interface IPolyCurve extends ICurve {
15
19
16
	/**
20
	/**
17
	 * Returns a sequence of {@link ICurve}s, representing the curve segments of
21
	 * Returns the {@link ICurve} segments of this {@link IPolyCurve}.
18
	 * this {@link IPolyCurve}.
19
	 * 
22
	 * 
20
	 * @return an array of {@link ICurve}s, representing the segments that make
23
	 * @return an array of {@link ICurve}s, representing the segments that make
21
	 *         up this {@link IPolyCurve}
24
	 *         up this {@link IPolyCurve}
22
	 */
25
	 */
23
	public ICurve[] getCurves();
26
	public ICurve[] getCurves();
27
28
	// TODO: intersects(), getIntersections(), overlaps()
29
24
}
30
}
(-)a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/IPolyShape.java (+13 lines)
Lines 11-17 Link Here
11
 *******************************************************************************/
11
 *******************************************************************************/
12
package org.eclipse.gef4.geometry.planar;
12
package org.eclipse.gef4.geometry.planar;
13
13
14
/**
15
 * An {@link IPolyShape} is the common abstraction over all {@link IGeometry}s
16
 * that are constituted by several {@link IShape} parts.
17
 */
14
public interface IPolyShape extends IGeometry {
18
public interface IPolyShape extends IGeometry {
15
19
20
	/**
21
	 * Returns the {@link IShape}s that constitute this {@link IPolyShape}.
22
	 * 
23
	 * @return an array of {@link IShape}s, representing the parts that make up
24
	 *         this {@link IPolyShape}.
25
	 */
16
	IShape[] getShapes();
26
	IShape[] getShapes();
27
28
	// contains()
29
17
}
30
}
(-)a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/IShape.java (-2 / +31 lines)
Lines 7-19 Link Here
7
 *
7
 *
8
 * Contributors:
8
 * Contributors:
9
 *     Alexander Nyßen (itemis AG) - initial API and implementation
9
 *     Alexander Nyßen (itemis AG) - initial API and implementation
10
 *     Matthias Wienand (itemis AG) - contribution for Bugzilla #355997
10
 *     
11
 *     
11
 *******************************************************************************/
12
 *******************************************************************************/
12
package org.eclipse.gef4.geometry.planar;
13
package org.eclipse.gef4.geometry.planar;
13
14
15
/**
16
 * An {@link IShape} is an {@link IGeometry} with a closed outline consisting of
17
 * multiple {@link ICurve} segments.
18
 */
14
public interface IShape extends IGeometry {
19
public interface IShape extends IGeometry {
15
20
16
	// IPolyCurve getOutline();
21
	/**
22
	 * Returns an {@link IPolyCurve} representing the outline of this
23
	 * {@link IShape}.
24
	 * 
25
	 * @return an {@link IPolyCurve} representing the outline
26
	 */
27
	public IPolyCurve getOutline();
28
29
	/**
30
	 * Returns the individual {@link ICurve} segments, which constitute the
31
	 * outline of this {@link IShape}.
32
	 * 
33
	 * @return the {@link ICurve} segments of this {@link IShape}'s outline
34
	 */
35
	public ICurve[] getOutlineSegments();
36
37
	/**
38
	 * Tests whether the given {@link IGeometry} is fully contained by this
39
	 * {@link IShape}.
40
	 * 
41
	 * @param g
42
	 *            the {@link IGeometry} to test for containment
43
	 * @return <code>true</code> if the given {@link IGeometry} is fully
44
	 *         contained by this {@link IShape}, <code>false</code> otherwise
45
	 */
46
	public boolean contains(final IGeometry g);
17
47
18
	ICurve[] getOutlineSegments();
19
}
48
}
(-)a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Line.java (-122 / +177 lines)
Lines 12-23 Link Here
12
 *******************************************************************************/
12
 *******************************************************************************/
13
package org.eclipse.gef4.geometry.planar;
13
package org.eclipse.gef4.geometry.planar;
14
14
15
import java.util.Arrays;
16
import java.util.HashSet;
17
18
import org.eclipse.gef4.geometry.Point;
15
import org.eclipse.gef4.geometry.Point;
19
import org.eclipse.gef4.geometry.euclidean.Straight;
16
import org.eclipse.gef4.geometry.euclidean.Straight;
20
import org.eclipse.gef4.geometry.euclidean.Vector;
17
import org.eclipse.gef4.geometry.euclidean.Vector;
18
import org.eclipse.gef4.geometry.projective.Vector3D;
21
import org.eclipse.gef4.geometry.transform.AffineTransform;
19
import org.eclipse.gef4.geometry.transform.AffineTransform;
22
import org.eclipse.gef4.geometry.utils.PointListUtils;
20
import org.eclipse.gef4.geometry.utils.PointListUtils;
23
import org.eclipse.gef4.geometry.utils.PrecisionUtils;
21
import org.eclipse.gef4.geometry.utils.PrecisionUtils;
Lines 66-81 public class Line extends BezierCurve { Link Here
66
	}
64
	}
67
65
68
	public boolean contains(Point p) {
66
	public boolean contains(Point p) {
69
		// TODO: optimize w.r.t object creation
67
		if (p == null) {
70
		Point p1 = getP1();
68
			return false;
71
		Point p2 = getP2();
72
73
		if (p1.equals(p2)) {
74
			return p.equals(p1);
75
		}
69
		}
76
70
77
		return new Straight(p1, p2).containsWithinSegment(new Vector(p1),
71
		double distance = Math.abs(new Straight(getP1(), getP2())
78
				new Vector(p2), new Vector(p));
72
				.getSignedDistanceCCW(new Vector(p)));
73
		return PrecisionUtils.equal(distance, 0) && getBounds().contains(p);
79
	}
74
	}
80
75
81
	/**
76
	/**
Lines 133-201 public class Line extends BezierCurve { Link Here
133
	 * @return a new {@link Line} with the same start and end point coordinates
128
	 * @return a new {@link Line} with the same start and end point coordinates
134
	 */
129
	 */
135
	public Line getCopy() {
130
	public Line getCopy() {
136
		return new Line(getX1(), getY1(), getX2(), getY2());
131
		return new Line(getP1(), getP2());
137
	}
138
139
	public Point[] getIntersections(Arc a) {
140
		return a.getIntersections(this);
141
	}
142
143
	public Point[] getIntersections(CubicCurve c) {
144
		return c.getIntersections(this);
145
	}
146
147
	public Point[] getIntersections(Ellipse e) {
148
		return e.getIntersections(this);
149
	}
150
151
	public Point[] getIntersections(Polygon p) {
152
		return p.getIntersections(this);
153
	}
154
155
	public Point[] getIntersections(Polyline p) {
156
		HashSet<Point> intersections = new HashSet<Point>();
157
158
		for (Line seg : p.getCurves()) {
159
			intersections.add(getIntersection(seg));
160
		}
161
162
		return intersections.toArray(new Point[] {});
163
	}
164
165
	public Point[] getIntersections(QuadraticCurve c) {
166
		return c.getIntersections(this);
167
	}
168
169
	public Point[] getIntersections(Rectangle r) {
170
		HashSet<Point> intersections = new HashSet<Point>();
171
172
		for (Line seg : r.getOutlineSegments()) {
173
			intersections.addAll(Arrays.asList(getIntersection(seg)));
174
		}
175
176
		return intersections.toArray(new Point[] {});
177
	}
178
179
	public Point[] getIntersections(RoundedRectangle rr) {
180
		HashSet<Point> intersections = new HashSet<Point>();
181
182
		// line segments
183
		intersections.add(getIntersection(rr.getTop()));
184
		intersections.add(getIntersection(rr.getLeft()));
185
		intersections.add(getIntersection(rr.getBottom()));
186
		intersections.add(getIntersection(rr.getRight()));
187
188
		// arc segments
189
		intersections
190
				.addAll(Arrays.asList(getIntersections(rr.getTopRightArc())));
191
		intersections
192
				.addAll(Arrays.asList(getIntersections(rr.getTopLeftArc())));
193
		intersections.addAll(Arrays.asList(getIntersections(rr
194
				.getBottomLeftArc())));
195
		intersections.addAll(Arrays.asList(getIntersections(rr
196
				.getBottomRightArc())));
197
198
		return intersections.toArray(new Point[] {});
199
	}
132
	}
200
133
201
	/**
134
	/**
Lines 210-241 public class Line extends BezierCurve { Link Here
210
	 *         given one, in case it intersects, <code>null</code> instead
143
	 *         given one, in case it intersects, <code>null</code> instead
211
	 */
144
	 */
212
	public Point getIntersection(Line l) {
145
	public Point getIntersection(Line l) {
213
		// if there is no intersection we may not return an intersection point
146
		Point p1 = getP1();
214
		if (!intersects(l)) {
147
		Point p2 = getP2();
148
149
		// degenerated case
150
		if (p1.equals(p2)) {
151
			if (l.contains(p1)) {
152
				return p1;
153
			} else if (l.contains(p2)) {
154
				return p2;
155
			}
215
			return null;
156
			return null;
216
		}
157
		}
217
158
218
		Point p1 = getP1();
219
		Point p2 = getP2();
220
		Straight s1 = new Straight(p1, p2);
221
		Point lp1 = l.getP1();
159
		Point lp1 = l.getP1();
222
		Point lp2 = l.getP2();
160
		Point lp2 = l.getP2();
161
162
		// degenerated case
163
		if (lp1.equals(lp2)) {
164
			if (contains(lp1)) {
165
				return lp1;
166
			} else if (contains(lp2)) {
167
				return lp2;
168
			}
169
			return null;
170
		}
171
172
		Straight s1 = new Straight(p1, p2);
223
		Straight s2 = new Straight(lp1, lp2);
173
		Straight s2 = new Straight(lp1, lp2);
224
		// handle overlap in exactly one point
174
225
		if (s1.equals(s2)) {
175
		if (s1.isParallelTo(s2)) {
226
			// lines may overlap in exactly one end point
176
			if (s1.contains(new Vector(lp1))) {
227
			if ((p1.equals(lp1) || p1.equals(lp2))
177
				// end-point-intersection? (no overlap)
228
					&& !(p2.equals(lp1) || p2.equals(lp2))) {
178
				double u1 = s1.getParameterAt(lp1);
229
				return p1;
179
				double u2 = s1.getParameterAt(lp2);
230
			} else if (p2.equals(lp1) || p2.equals(lp2)
180
231
					&& !(p1.equals(lp1) || p1.equals(lp2))) {
181
				if (PrecisionUtils.equal(u1, 0) && u2 < u1
232
				return p2;
182
						|| PrecisionUtils.equal(u1, 1) && u2 > u1) {
233
			} else {
183
					return lp1;
234
				return null;
184
				} else if (PrecisionUtils.equal(u2, 0) && u1 < u2
185
						|| PrecisionUtils.equal(u2, 1) && u1 > u2) {
186
					return lp2;
187
				}
188
			}
189
190
			return null;
191
		}
192
193
		Point intersection = s1.getIntersection(s2).toPoint();
194
		return contains(intersection) && l.contains(intersection) ? intersection
195
				: null;
196
	}
197
198
	@Override
199
	public Point[] getIntersections(BezierCurve curve) {
200
		if (curve instanceof Line) {
201
			Point poi = getIntersection((Line) curve);
202
			if (poi != null) {
203
				return new Point[] { poi };
235
			}
204
			}
205
			return new Point[] {};
236
		}
206
		}
237
		// return the single intersection point
207
		return super.getIntersections(curve);
238
		return s1.getIntersection(s2).toPoint();
239
	}
208
	}
240
209
241
	/**
210
	/**
Lines 257-308 public class Line extends BezierCurve { Link Here
257
		return new Line(transformed[0], transformed[1]);
226
		return new Line(transformed[0], transformed[1]);
258
	}
227
	}
259
228
229
	public boolean touches(IGeometry g) {
230
		if (g instanceof Line) {
231
			return touches((Line) g);
232
		}
233
		return super.touches(g);
234
	}
235
260
	/**
236
	/**
261
	 * Tests whether this {@link Line} and the given one intersect, i.e. if they
237
	 * Tests whether this {@link Line} and the given one share at least one
262
	 * share at least one common point.
238
	 * common point.
263
	 * 
239
	 * 
264
	 * @param l
240
	 * @param l
265
	 *            The {@link Line} to test.
241
	 *            The {@link Line} to test.
266
	 * @return <code>true</code> if this {@link Line} and the given one share at
242
	 * @return <code>true</code> if this {@link Line} and the given one share at
267
	 *         least one common point, <code>false</code> otherwise.
243
	 *         least one common point, <code>false</code> otherwise.
268
	 */
244
	 */
269
	public boolean intersects(Line l) {
245
	public boolean touches(Line l) {
270
		// TODO: optimize w.r.t. object creation
246
		// TODO: optimize w.r.t. object creation
247
248
		/*
249
		 * 1) check degenerated (the start and end point imprecisely fall
250
		 * together) and special cases where the lines have to be regarded as
251
		 * intersecting, because they touch within the used imprecision, though
252
		 * they would not intersect with absolute precision.
253
		 */
271
		Point p1 = getP1();
254
		Point p1 = getP1();
272
		Point p2 = getP2();
255
		Point p2 = getP2();
273
256
274
		if (p1.equals(p2)) {
257
		boolean touches = l.contains(p1) || l.contains(p2);
275
			return l.contains(p1);
258
		if (touches || p1.equals(p2)) {
259
			return touches;
276
		}
260
		}
277
261
278
		Point lp1 = l.getP1();
262
		Point lp1 = l.getP1();
279
		Point lp2 = l.getP2();
263
		Point lp2 = l.getP2();
280
264
281
		if (lp1.equals(lp2)) {
265
		touches = contains(lp1) || contains(lp2);
282
			return contains(lp1);
266
		if (touches || lp1.equals(lp2)) {
267
			return touches;
283
		}
268
		}
284
269
		Vector3D l1 = new Vector3D(p1).getCrossed(new Vector3D(p2));
285
		Straight s1 = new Straight(p1, p2);
270
		Vector3D l2 = new Vector3D(lp1).getCrossed(new Vector3D(lp2));
286
		Straight s2 = new Straight(lp1, lp2);
271
287
		Vector v1 = new Vector(p1);
272
		/*
288
		Vector v2 = new Vector(p2);
273
		 * 2) non-degenerated case. If the two respective straight lines
289
		Vector lv1 = new Vector(lp1);
274
		 * intersect, the intersection has to be contained by both line segments
290
		Vector lv2 = new Vector(lp2);
275
		 * for the segments to intersect. If the two respective straight lines
291
		// intersection within Straight does not cover overlap, therefore we
276
		 * do not intersect, because they are parallel, the getIntersection()
292
		// have to check this separately here (via equality and containment of
277
		 * method returns null.
293
		// the end points)
278
		 */
294
		return s1.equals(s2)
279
		Point intersection = l1.getCrossed(l2).toPoint();
295
				&& (s1.containsWithinSegment(v1, v2, lv1) || s1
280
		return intersection != null && contains(intersection)
296
						.containsWithinSegment(v1, v2, lv2))
281
				&& l.contains(intersection);
297
				|| s1.intersectsWithinSegment(v1, v2, s2)
298
				&& s2.intersectsWithinSegment(lv1, lv2, s1);
299
	}
282
	}
300
283
301
	/**
284
	/**
302
	 * @see IGeometry#intersects(Rectangle)
285
	 * Tests whether this {@link Line} and the given other {@link Line} overlap,
286
	 * i.e. they share an infinite number of {@link Point}s.
287
	 * 
288
	 * @param l
289
	 *            the other {@link Line} to test for overlap with this
290
	 *            {@link Line}
291
	 * @return <code>true</code> if this {@link Line} and the other {@link Line}
292
	 *         overlap, otherwise <code>false</code>
293
	 * @see ICurve#overlaps(ICurve)
303
	 */
294
	 */
304
	public boolean intersects(Rectangle r) {
295
	public boolean overlaps(Line l) {
305
		return r.intersects(this);
296
		return touches(l) && !intersects(l);
297
	}
298
299
	@Override
300
	public boolean overlaps(BezierCurve c) {
301
		if (c instanceof Line) {
302
			return overlaps((Line) c);
303
		}
304
305
		// BezierCurve: in order to overlap, all control points have to lie on a
306
		// straight through its base line
307
		Straight s = new Straight(this);
308
		for (Line seg : PointListUtils.toSegmentsArray(c.getPoints(), false)) {
309
			if (!s.equals(new Straight(seg))) {
310
				return false;
311
			}
312
		}
313
314
		// if the base line overlaps, we are done
315
		if (overlaps(new Line(c.getP1(), c.getP2()))) {
316
			return true;
317
		} else {
318
			// otherwise, we have to delegate to the general implementation for
319
			// Bezier curves to take care of a degenerated curve, where the
320
			// handle points are outside the base line of the Bezier curve.
321
			return super.touches(c);
322
		}
306
	}
323
	}
307
324
308
	/**
325
	/**
Lines 319-328 public class Line extends BezierCurve { Link Here
319
	 *            the y-coordinate of the end point
336
	 *            the y-coordinate of the end point
320
	 */
337
	 */
321
	public void setLine(double x1, double y1, double x2, double y2) {
338
	public void setLine(double x1, double y1, double x2, double y2) {
322
		setX1(x1);
339
		setP1(new Point(x1, y1));
323
		setY1(y1);
340
		setP2(new Point(x2, y2));
324
		setX2(x2);
325
		setY2(y2);
326
	}
341
	}
327
342
328
	/**
343
	/**
Lines 353-364 public class Line extends BezierCurve { Link Here
353
	}
368
	}
354
369
355
	/**
370
	/**
371
	 * Sets the x-coordinate of the start {@link Point} of this {@link Line} to
372
	 * the given value.
373
	 * 
374
	 * @param x1
375
	 */
376
	public void setX1(double x1) {
377
		setP1(new Point(x1, getY1()));
378
	}
379
380
	/**
381
	 * Sets the y-coordinate of the start {@link Point} of this {@link Line} to
382
	 * the given value.
383
	 * 
384
	 * @param y1
385
	 */
386
	public void setY1(double y1) {
387
		setP1(new Point(getX1(), y1));
388
	}
389
390
	/**
391
	 * Sets the x-coordinate of the end {@link Point} of this {@link Line} to
392
	 * the given value.
393
	 * 
394
	 * @param x2
395
	 */
396
	public void setX2(double x2) {
397
		setP2(new Point(x2, getY2()));
398
	}
399
400
	/**
401
	 * Sets the y-coordinate of the end {@link Point} of this {@link Line} to
402
	 * the given value.
403
	 * 
404
	 * @param y2
405
	 */
406
	public void setY2(double y2) {
407
		setP2(new Point(getX2(), y2));
408
	}
409
410
	/**
356
	 * @see IGeometry#toPath()
411
	 * @see IGeometry#toPath()
357
	 */
412
	 */
358
	public Path toPath() {
413
	public Path toPath() {
359
		Path path = new Path();
414
		Path path = new Path();
360
		path.moveTo(getX1(), getY1());
415
		path.moveTo(getX1(), getY1());
361
		path.lineTo(getX2(), getY1());
416
		path.lineTo(getX2(), getY2());
362
		return path;
417
		return path;
363
	}
418
	}
364
419
(-)a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Path.java (-3 / +19 lines)
Lines 121-127 public class Path extends AbstractGeometry implements IGeometry { Link Here
121
	}
121
	}
122
122
123
	/**
123
	/**
124
	 * @see IGeometry#contains(Rectangle)
124
	 * Returns <code>true</code> if the given {@link Rectangle} is contained
125
	 * within {@link IGeometry}, <code>false</code> otherwise.
126
	 * 
127
	 * TODO: Generalize to arbitrary {@link IGeometry} objects.
128
	 * 
129
	 * @param r
130
	 *            The {@link Rectangle} to test
131
	 * @return <code>true</code> if the {@link Rectangle} is fully contained
132
	 *         within this {@link IGeometry}
125
	 */
133
	 */
126
	public boolean contains(Rectangle r) {
134
	public boolean contains(Rectangle r) {
127
		return delegate.contains(Geometry2AWT.toAWTRectangle(r));
135
		return delegate.contains(Geometry2AWT.toAWTRectangle(r));
Lines 177-185 public class Path extends AbstractGeometry implements IGeometry { Link Here
177
	}
185
	}
178
186
179
	/**
187
	/**
180
	 * {@link IGeometry#intersects(Rectangle)}
188
	 * Tests whether this {@link Path} and the given {@link Rectangle} touch,
189
	 * i.e. they have at least one {@link Point} in common.
190
	 * 
191
	 * @param r
192
	 *            the {@link Rectangle} to test for at least one {@link Point}
193
	 *            in common with this {@link Path}
194
	 * @return <code>true</code> if this {@link Path} and the {@link Rectangle}
195
	 *         touch, otherwise <code>false</code>
196
	 * @see IGeometry#touches(IGeometry)
181
	 */
197
	 */
182
	public boolean intersects(Rectangle r) {
198
	public boolean touches(Rectangle r) {
183
		return delegate.intersects(Geometry2AWT.toAWTRectangle(r));
199
		return delegate.intersects(Geometry2AWT.toAWTRectangle(r));
184
	}
200
	}
185
201
(-)a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Pie.java (-8 lines)
Lines 19-36 public class Pie extends AbstractGeometry implements IGeometry { Link Here
19
		throw new UnsupportedOperationException("Not yet implemented.");
19
		throw new UnsupportedOperationException("Not yet implemented.");
20
	}
20
	}
21
21
22
	public boolean contains(Rectangle r) {
23
		throw new UnsupportedOperationException("Not yet implemented.");
24
	}
25
26
	public Rectangle getBounds() {
22
	public Rectangle getBounds() {
27
		throw new UnsupportedOperationException("Not yet implemented.");
23
		throw new UnsupportedOperationException("Not yet implemented.");
28
	}
24
	}
29
25
30
	public boolean intersects(Rectangle r) {
31
		throw new UnsupportedOperationException("Not yet implemented.");
32
	}
33
34
	public Path toPath() {
26
	public Path toPath() {
35
		throw new UnsupportedOperationException("Not yet implemented.");
27
		throw new UnsupportedOperationException("Not yet implemented.");
36
	}
28
	}
(-)a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/PolyBezier.java (+120 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2011 itemis AG and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthias Wienand (itemis AG) - initial API and implementation
10
 *     
11
 *******************************************************************************/
12
package org.eclipse.gef4.geometry.planar;
13
14
import org.eclipse.gef4.geometry.Point;
15
import org.eclipse.gef4.geometry.utils.CurveUtils;
16
17
/**
18
 * A {@link PolyBezier} is an {@link IPolyCurve} which consists of one or more
19
 * connected {@link BezierCurve}s.
20
 */
21
public class PolyBezier extends AbstractGeometry implements IPolyCurve {
22
23
	private static final long serialVersionUID = 1L;
24
	private BezierCurve[] beziers;
25
26
	/**
27
	 * Constructs a new {@link PolyBezier} of the given {@link BezierCurve}s.
28
	 * The {@link BezierCurve}s are expected to be connected with each other.
29
	 * 
30
	 * @param beziers
31
	 *            the {@link BezierCurve}s which will constitute this
32
	 *            {@link PolyBezier}
33
	 */
34
	public PolyBezier(BezierCurve... beziers) {
35
		this.beziers = copy(beziers);
36
	}
37
38
	public boolean contains(Point p) {
39
		for (BezierCurve c : beziers) {
40
			if (c.contains(p)) {
41
				return true;
42
			}
43
		}
44
		return false;
45
	}
46
47
	public Rectangle getBounds() {
48
		Rectangle bounds = new Rectangle();
49
50
		for (BezierCurve c : beziers) {
51
			bounds.union(c.getBounds());
52
		}
53
54
		return bounds;
55
	}
56
57
	public Path toPath() {
58
		// TODO: need a Path.append(Path)
59
		throw new UnsupportedOperationException("Not yet implemented.");
60
	}
61
62
	public IGeometry getCopy() {
63
		return new PolyBezier(beziers);
64
	}
65
66
	public double getY2() {
67
		return getP2().y;
68
	}
69
70
	public double getY1() {
71
		return getP1().y;
72
	}
73
74
	public double getX2() {
75
		return getP2().x;
76
	}
77
78
	public double getX1() {
79
		return getP1().x;
80
	}
81
82
	public Point getP2() {
83
		return beziers[beziers.length - 1].getP2();
84
	}
85
86
	public Point getP1() {
87
		return beziers[0].getP1();
88
	}
89
90
	public BezierCurve[] toBezier() {
91
		return copy(beziers);
92
	}
93
94
	public Point[] getIntersections(ICurve g) {
95
		return CurveUtils.getIntersections(g, this);
96
	}
97
98
	public boolean intersects(ICurve c) {
99
		return CurveUtils.intersects(c, this);
100
	}
101
102
	public boolean overlaps(ICurve c) {
103
		return CurveUtils.overlaps(c, this);
104
	}
105
106
	public BezierCurve[] getCurves() {
107
		return copy(beziers);
108
	}
109
110
	private static BezierCurve[] copy(BezierCurve... beziers) {
111
		BezierCurve[] copy = new BezierCurve[beziers.length];
112
113
		for (int i = 0; i < beziers.length; i++) {
114
			copy[i] = beziers[i].getCopy();
115
		}
116
117
		return copy;
118
	}
119
120
}
(-)a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Polygon.java (-560 / +172 lines)
Lines 13-26 Link Here
13
package org.eclipse.gef4.geometry.planar;
13
package org.eclipse.gef4.geometry.planar;
14
14
15
import java.util.ArrayList;
15
import java.util.ArrayList;
16
import java.util.Arrays;
17
import java.util.HashSet;
18
16
19
import org.eclipse.gef4.geometry.Angle;
20
import org.eclipse.gef4.geometry.Point;
17
import org.eclipse.gef4.geometry.Point;
21
import org.eclipse.gef4.geometry.euclidean.Straight;
22
import org.eclipse.gef4.geometry.euclidean.Vector;
23
import org.eclipse.gef4.geometry.transform.AffineTransform;
18
import org.eclipse.gef4.geometry.transform.AffineTransform;
19
import org.eclipse.gef4.geometry.utils.CurveUtils;
24
import org.eclipse.gef4.geometry.utils.PointListUtils;
20
import org.eclipse.gef4.geometry.utils.PointListUtils;
25
import org.eclipse.gef4.geometry.utils.PrecisionUtils;
21
import org.eclipse.gef4.geometry.utils.PrecisionUtils;
26
22
Lines 35-41 import org.eclipse.gef4.geometry.utils.PrecisionUtils; Link Here
35
 * @author anyssen
31
 * @author anyssen
36
 * 
32
 * 
37
 */
33
 */
38
public class Polygon extends AbstractPointListBasedGeometry implements IShape {
34
public class Polygon extends AbstractPointListBasedGeometry<Polygon> implements
35
		IShape {
39
36
40
	/**
37
	/**
41
	 * Pair of {@link Line} segment and integer counter to count segments of
38
	 * Pair of {@link Line} segment and integer counter to count segments of
Lines 105-129 public class Polygon extends AbstractPointListBasedGeometry implements IShape { Link Here
105
	}
102
	}
106
103
107
	/**
104
	/**
108
	 * Tests if the given {@link CubicCurve} curve is contained in this
109
	 * {@link Polygon}.
110
	 * 
111
	 * @param curve
112
	 * @return true if it is contained, false otherwise
113
	 */
114
	public boolean contains(CubicCurve curve) {
115
		if (contains(curve.getP1()) && contains(curve.getP2())) {
116
			for (Line seg : getOutlineSegments()) {
117
				if (curve.intersects(seg)) {
118
					return false;
119
				}
120
			}
121
			return true;
122
		}
123
		return false;
124
	}
125
126
	/**
127
	 * Checks whether the point that is represented by its x- and y-coordinates
105
	 * Checks whether the point that is represented by its x- and y-coordinates
128
	 * is contained within this {@link Polygon}.
106
	 * is contained within this {@link Polygon}.
129
	 * 
107
	 * 
Lines 140-198 public class Polygon extends AbstractPointListBasedGeometry implements IShape { Link Here
140
	}
118
	}
141
119
142
	/**
120
	/**
143
	 * Tests if the given {@link Ellipse} e is contained in this {@link Polygon}
144
	 * .
145
	 * 
146
	 * @param e
147
	 * @return true if it is contained, false otherwise
148
	 */
149
	public boolean contains(Ellipse e) {
150
		for (CubicCurve curve : e.getOutlineSegments()) {
151
			if (!contains(curve)) {
152
				return false;
153
			}
154
		}
155
		return true;
156
	}
157
158
	/**
159
	 * Checks whether the given {@link Line} is fully contained within this
160
	 * {@link Polygon}.
161
	 * 
162
	 * @param line
163
	 *            The {@link Line} to test for containment
164
	 * @return <code>true</code> if the given {@link Line} is fully contained,
165
	 *         <code>false</code> otherwise
166
	 */
167
	public boolean contains(Line line) {
168
		// quick rejection test: if the end points are not contained, the line
169
		// may not be contained
170
		if (!contains(line.getP1()) || !contains(line.getP2())) {
171
			return false;
172
		}
173
174
		// check for intersections with the segments of this polygon
175
		for (int i = 0; i < points.length; i++) {
176
			Point p1 = points[i];
177
			Point p2 = i + 1 < points.length ? points[i + 1] : points[0];
178
			Line segment = new Line(p1, p2);
179
			if (line.intersects(segment)) {
180
				Point intersection = line.getIntersection(segment);
181
				if (intersection != null && !line.getP1().equals(intersection)
182
						&& !line.getP2().equals(intersection)
183
						&& !segment.getP1().equals(intersection)
184
						&& !segment.getP2().equals(intersection)) {
185
					// if we have a single intersection point and this does not
186
					// match one of the end points of the line, the line is not
187
					// contained
188
					return false;
189
				}
190
			}
191
		}
192
		return true;
193
	}
194
195
	/**
196
	 * @see IGeometry#contains(Point)
121
	 * @see IGeometry#contains(Point)
197
	 */
122
	 */
198
	public boolean contains(Point p) {
123
	public boolean contains(Point p) {
Lines 209-319 public class Polygon extends AbstractPointListBasedGeometry implements IShape { Link Here
209
				return false;
134
				return false;
210
			}
135
			}
211
136
212
			// choose a point p' outside the polygon not too close to its bounds
137
			/*
213
			// (have it outside at 1% of its width and height) and construct a
138
			 * choose a point p' outside the polygon:
214
			// straight through p and p'
139
			 */
215
			Vector u1 = new Vector(p);
140
			Point pp = new Point(p.x + bounds.getWidth() + 1, p.y);
216
			Vector u2 = new Vector(bounds.getTopLeft().x
141
217
					- (bounds.getWidth() / 100), bounds.getTopLeft().y
142
			/*
218
					- (bounds.getHeight() / 100));
143
			 * construct a line from p to p':
219
			Straight s1 = new Straight(u1, u2.getSubtracted((u1)));
144
			 */
220
145
			Line testLine = new Line(p, pp);
221
			// compute if there is an even or odd number of intersection of
146
222
			// p->p' with all sides of the polygon; handle the special case the
147
			/*
223
			// point is located on one of the sides
148
			 * compute if there is an even or odd number of intersection of the
149
			 * test line with all sides of the polygon; handle the special case
150
			 * the point is located on one of the sides
151
			 */
224
			boolean odd = false;
152
			boolean odd = false;
225
			for (int i = 0; i < points.length; i++) {
153
			for (int i = 0; i < points.length; i++) {
226
				Vector v1 = new Vector(points[i]);
154
				Point p1 = points[i];
227
				Vector v2 = new Vector(
155
				Point p2 = points[i + 1 < points.length ? i + 1 : 0];
228
						points[i + 1 < points.length ? i + 1 : 0]);
229
				Vector direction = v2.getSubtracted(v1);
230
156
231
				if (direction.isNull()) {
157
				// check whether the point is located on the current side
232
					if (v1.equals(u1)) {
158
				if (p1.equals(p2)) {
159
					if (p1.equals(p)) {
233
						return true;
160
						return true;
234
					}
161
					}
235
					continue;
162
					continue;
236
				}
163
				}
237
164
238
				Straight s2 = new Straight(v1, direction);
165
				Line link = new Line(p1, p2);
239
166
240
				// check whether the point is located on the current side
167
				if (link.contains(p)) {
241
				if (s2.containsWithinSegment(v1, v2, u1)) {
242
					return true;
168
					return true;
243
				}
169
				}
244
170
245
				// check if there is an intersection within the given segment
171
				/*
246
				if (s2.intersectsWithinSegment(v1, v2, s1)
172
				 * check if one of the two vertices of the link line is
247
						&& s1.intersectsWithinSegment(u1, u2, s2)) {
173
				 * contained by the test line. the containment test is done to
248
					odd = !odd;
174
				 * handle special cases where the intersection has to be counted
175
				 * appropriately.
176
				 * 
177
				 * 1) if the vertex is above (greater y-component) the other
178
				 * point of the line, it is counted once.
179
				 * 
180
				 * 2) if the vertex is below (lower y-component) or on the same
181
				 * height as the other point of the line, it is omitted.
182
				 */
183
				boolean p1contained = testLine.contains(p1);
184
				boolean p2contained = testLine.contains(p2);
185
186
				// TODO: is imprecision needed for this test?
187
				if (p1contained || p2contained) {
188
					if (p1contained) {
189
						if (p1.y > p2.y) {
190
							odd = !odd;
191
						}
192
					}
193
					if (p2contained) {
194
						if (p2.y > p1.y) {
195
							odd = !odd;
196
						}
197
					}
198
					continue;
249
				}
199
				}
250
			}
251
			return odd;
252
		}
253
	}
254
200
255
	/**
201
				/*
256
	 * Checks whether the given {@link Polygon} is fully contained within this
202
				 * check the current link for an intersection with the test
257
	 * {@link Polygon}.
203
				 * line. if there is an intersection, change state.
258
	 * 
204
				 */
259
	 * @param p
205
				Point poi = testLine.getIntersection(link);
260
	 *            The {@link Polygon} to test for containment
206
				if (poi != null) {
261
	 * @return <code>true</code> if the given {@link Polygon} is fully
207
					odd = !odd;
262
	 *         contained, <code>false</code> otherwise.
263
	 */
264
	public boolean contains(Polygon p) {
265
		// all segments of the given polygon have to be contained
266
		Line[] otherSegments = p.getOutlineSegments();
267
		for (int i = 0; i < otherSegments.length; i++) {
268
			if (!contains(otherSegments[i])) {
269
				return false;
270
			}
271
		}
272
		return true;
273
	}
274
275
	/**
276
	 * Tests if the given {@link Polyline} p is contained in this
277
	 * {@link Polygon}.
278
	 * 
279
	 * @param p
280
	 * @return true if it is contained, false otherwise
281
	 */
282
	public boolean contains(Polyline p) {
283
		// all segments of the given polygon have to be contained
284
		Line[] otherSegments = p.getCurves();
285
		for (int i = 0; i < otherSegments.length; i++) {
286
			if (!contains(otherSegments[i])) {
287
				return false;
288
			}
289
		}
290
		return true;
291
	}
292
293
	/**
294
	 * Tests if the given {@link QuadraticCurve} curve is contained in this
295
	 * {@link Polygon}.
296
	 * 
297
	 * @param curve
298
	 * @return true if it is contained, false otherwise
299
	 */
300
	public boolean contains(QuadraticCurve curve) {
301
		if (contains(curve.getP1()) && contains(curve.getP2())) {
302
			for (Line seg : getOutlineSegments()) {
303
				if (curve.intersects(seg)) {
304
					return false;
305
				}
208
				}
306
			}
209
			}
307
			return true;
210
			return odd;
308
		}
211
		}
309
		return false;
310
	}
311
312
	/**
313
	 * @see IGeometry#contains(Rectangle)
314
	 */
315
	public boolean contains(Rectangle rect) {
316
		return contains(rect.toPolygon());
317
	}
212
	}
318
213
319
	@Override
214
	@Override
Lines 372-562 public class Polygon extends AbstractPointListBasedGeometry implements IShape { Link Here
372
	}
267
	}
373
268
374
	/**
269
	/**
375
	 * Returns a copy of this {@link Polygon}, which is made up by the same
270
	 * Computes the area of this {@link Polygon}.
376
	 * points.
377
	 * 
378
	 * @return a new {@link Polygon} with an identical set of points.
379
	 */
380
	public Polygon getCopy() {
381
		return new Polygon(getPoints());
382
	}
383
384
	/**
385
	 * Returns the points of intersection between this {@link Polygon} and the
386
	 * given {@link Arc} arc.
387
	 * 
271
	 * 
388
	 * @param arc
272
	 * @return the area of this {@link Polygon}
389
	 *            The {@link Arc} to test for intersections
390
	 * @return the points of intersection.
391
	 */
273
	 */
392
	public Point[] getIntersections(Arc arc) {
274
	public double getArea() {
393
		HashSet<Point> intersections = new HashSet<Point>();
275
		if (points.length < 3) {
394
276
			return 0;
395
		for (CubicCurve seg : arc.getSegments()) {
396
			intersections.addAll(Arrays.asList(getIntersections(seg)));
397
		}
277
		}
398
278
399
		return intersections.toArray(new Point[] {});
279
		double area = 0;
400
	}
280
		for (int i = 0; i < points.length - 1; i++) {
401
281
			area += points[i].x * points[i + 1].y - points[i].y
402
	/**
282
					* points[i + 1].x;
403
	 * Returns the points of intersection between this {@link Polygon} and the
404
	 * given {@link CubicCurve} c.
405
	 * 
406
	 * @param c
407
	 * @return The points of intersection.
408
	 */
409
	public Point[] getIntersections(CubicCurve c) {
410
		HashSet<Point> intersections = new HashSet<Point>();
411
412
		for (Line seg : getOutlineSegments()) {
413
			for (Point poi : c.getIntersections(seg)) {
414
				intersections.add(poi);
415
			}
416
		}
283
		}
417
284
418
		return intersections.toArray(new Point[] {});
285
		// closing segment
419
	}
286
		area += points[points.length - 2].x * points[points.length - 1].y
287
				- points[points.length - 2].y * points[points.length - 1].x;
420
288
421
	/**
289
		return Math.abs(area) * 0.5;
422
	 * Returns the points of intersection between this {@link Polygon} and the
423
	 * given {@link Ellipse} e.
424
	 * 
425
	 * @param e
426
	 * @return The points of intersection.
427
	 */
428
	public Point[] getIntersections(Ellipse e) {
429
		return e.getIntersections(this);
430
	}
290
	}
431
291
432
	/**
292
	/**
433
	 * Returns the points of intersection between this {@link Polygon} and the
293
	 * Returns a copy of this {@link Polygon}, which is made up by the same
434
	 * given {@link Line} l.
294
	 * points.
435
	 * 
436
	 * @param l
437
	 * @return The points of intersection.
438
	 */
439
	public Point[] getIntersections(Line l) {
440
		HashSet<Point> intersections = new HashSet<Point>();
441
442
		for (Line segment : getOutlineSegments()) {
443
			Point poi = segment.getIntersection(l);
444
			if (poi != null) {
445
				intersections.add(poi);
446
			}
447
		}
448
449
		return intersections.toArray(new Point[] {});
450
	}
451
452
	/**
453
	 * Returns the points of intersection between this {@link Polygon} and the
454
	 * given other {@link Polygon} polygon.
455
	 * 
456
	 * @param polygon
457
	 * @return The points of intersection.
458
	 */
459
	public Point[] getIntersections(Polygon polygon) {
460
		HashSet<Point> intersections = new HashSet<Point>();
461
462
		for (Line segment : polygon.getOutlineSegments()) {
463
			for (Point poi : getIntersections(segment)) {
464
				intersections.add(poi);
465
			}
466
		}
467
468
		return intersections.toArray(new Point[] {});
469
	}
470
471
	/**
472
	 * Returns the points of intersection between this {@link Polygon} and the
473
	 * given {@link Polyline} polyline.
474
	 * 
475
	 * @param polyline
476
	 * @return The points of intersection.
477
	 */
478
	public Point[] getIntersections(Polyline polyline) {
479
		HashSet<Point> intersections = new HashSet<Point>();
480
481
		for (Line segment : polyline.getCurves()) {
482
			for (Point poi : getIntersections(segment)) {
483
				intersections.add(poi);
484
			}
485
		}
486
487
		return intersections.toArray(new Point[] {});
488
	}
489
490
	/**
491
	 * Returns the points of intersection between this {@link Polygon} and the
492
	 * given {@link QuadraticCurve} c.
493
	 * 
494
	 * @param c
495
	 * @return The points of intersection.
496
	 */
497
	public Point[] getIntersections(QuadraticCurve c) {
498
		HashSet<Point> intersections = new HashSet<Point>();
499
500
		for (Line seg : getOutlineSegments()) {
501
			for (Point poi : c.getIntersections(seg)) {
502
				intersections.add(poi);
503
			}
504
		}
505
506
		return intersections.toArray(new Point[] {});
507
	}
508
509
	/**
510
	 * Returns the points of intersection between this {@link Polygon} and the
511
	 * given {@link Rectangle} rect.
512
	 * 
513
	 * @param rect
514
	 * @return The points of intersection.
515
	 */
516
	public Point[] getIntersections(Rectangle rect) {
517
		HashSet<Point> intersections = new HashSet<Point>();
518
519
		for (Line segment : rect.getOutlineSegments()) {
520
			for (Point poi : getIntersections(segment)) {
521
				intersections.add(poi);
522
			}
523
		}
524
525
		return intersections.toArray(new Point[] {});
526
	}
527
528
	/**
529
	 * Calculates the points of intersection of this {@link Polygon} and the
530
	 * given {@link RoundedRectangle}.
531
	 * 
295
	 * 
532
	 * @param r
296
	 * @return a new {@link Polygon} with an identical set of points.
533
	 *            The {@link RoundedRectangle} to compute intersection points
534
	 *            with
535
	 * @return an array containing the points of intersection of this
536
	 *         {@link Polygon} and the given {@link RoundedRectangle}. An empty
537
	 *         array if there are no points of intersection or indefinitely many
538
	 *         ones.
539
	 */
297
	 */
540
	public Point[] getIntersections(RoundedRectangle r) {
298
	public Polygon getCopy() {
541
		HashSet<Point> intersections = new HashSet<Point>();
299
		return new Polygon(getPoints());
542
543
		// line segments
544
		intersections.addAll(Arrays.asList(getIntersections(r.getTop())));
545
		intersections.addAll(Arrays.asList(getIntersections(r.getLeft())));
546
		intersections.addAll(Arrays.asList(getIntersections(r.getBottom())));
547
		intersections.addAll(Arrays.asList(getIntersections(r.getRight())));
548
549
		// arc segments
550
		intersections
551
				.addAll(Arrays.asList(getIntersections(r.getTopRightArc())));
552
		intersections
553
				.addAll(Arrays.asList(getIntersections(r.getTopLeftArc())));
554
		intersections.addAll(Arrays.asList(getIntersections(r
555
				.getBottomLeftArc())));
556
		intersections.addAll(Arrays.asList(getIntersections(r
557
				.getBottomRightArc())));
558
559
		return intersections.toArray(new Point[] {});
560
	}
300
	}
561
301
562
	/**
302
	/**
Lines 580-846 public class Polygon extends AbstractPointListBasedGeometry implements IShape { Link Here
580
	}
320
	}
581
321
582
	/**
322
	/**
583
	 * Returns a new Polygon which is shifted along each axis by the passed
323
	 * @see IGeometry#toPath()
584
	 * values.
585
	 * 
586
	 * @param dx
587
	 *            Displacement along X axis
588
	 * @param dy
589
	 *            Displacement along Y axis
590
	 * @return The new translated rectangle
591
	 * @since 2.0
592
	 */
324
	 */
593
	public Polygon getTranslated(double dx, double dy) {
325
	public Path toPath() {
594
		return getCopy().translate(dx, dy);
326
		Path path = new Path();
327
		if (points.length > 0) {
328
			path.moveTo(points[0].x, points[0].y);
329
			for (int i = 1; i < points.length; i++) {
330
				path.lineTo(points[i].x, points[i].y);
331
			}
332
			path.close();
333
		}
334
		return path;
595
	}
335
	}
596
336
597
	/**
337
	@Override
598
	 * Returns a new Polygon which is shifted by the position of the given
338
	public String toString() {
599
	 * Point.
339
		StringBuffer stringBuffer = new StringBuffer("Polygon: ");
600
	 * 
340
		if (points.length > 0) {
601
	 * @param pt
341
			for (int i = 0; i < points.length; i++) {
602
	 *            Point providing the amount of shift along each axis
342
				stringBuffer.append("(" + points[i].x + ", " + points[i].y
603
	 * @return The new translated Polygon
343
						+ ")");
604
	 */
344
				stringBuffer.append(" -> ");
605
	public Polygon getTranslated(Point pt) {
345
			}
606
		return getCopy().translate(pt);
346
			stringBuffer.append("(" + points[0].x + ", " + points[0].y + ")");
347
		} else {
348
			stringBuffer.append("<no points>");
349
		}
350
		return stringBuffer.toString();
607
	}
351
	}
608
352
609
	/**
353
	public Polyline getOutline() {
610
	 * Tests if this {@link Polygon} intersects the given {@link Ellipse} e.
354
		return new Polyline(PointListUtils.toSegmentsArray(points, true));
611
	 * 
612
	 * @param e
613
	 * @return true if they intersect, false otherwise
614
	 */
615
	public boolean intersects(Ellipse e) {
616
		return e.intersects(this);
617
	}
355
	}
618
356
619
	/**
357
	/**
620
	 * Checks if there is at least one common point between this {@link Polygon}
358
	 * Checks whether the given {@link Line} is fully contained within this
621
	 * and the given {@link Line}, which includes the case that the given
359
	 * {@link Polygon}.
622
	 * {@link Line} is fully contained.
623
	 * 
360
	 * 
624
	 * @param line
361
	 * @param line
625
	 *            The {@link Line} to test for intersection
362
	 *            The {@link Line} to test for containment
626
	 * @return <code>true</code> if this {@link Polygon} and the given
363
	 * @return <code>true</code> if the given {@link Line} is fully contained,
627
	 *         {@link Line} share at least one common point, <code>false</code>
364
	 *         <code>false</code> otherwise
628
	 *         otherwise.
629
	 */
365
	 */
630
	public boolean intersects(Line line) {
366
	public boolean contains(Line line) {
631
		// quick acceptance test: if the end points of the line are contained,
367
		// quick rejection test: if the end points are not contained, the line
632
		// we already have a common point and may return
368
		// may not be contained
633
		if (contains(line.getP1()) || contains(line.getP2())) {
369
		if (!contains(line.getP1()) || !contains(line.getP2())) {
634
			return true;
370
			return false;
635
		}
371
		}
636
372
637
		// check for intersection with the segments
373
		// check for intersections with the segments of this polygon
638
		Line[] segments = getOutlineSegments();
374
		for (int i = 0; i < points.length; i++) {
639
		for (int i = 0; i < segments.length; i++) {
375
			Point p1 = points[i];
640
			if (segments[i].intersects(line)) {
376
			Point p2 = i + 1 < points.length ? points[i + 1] : points[0];
641
				return true;
377
			Line segment = new Line(p1, p2);
378
			if (line.intersects(segment)) {
379
				Point intersection = line.getIntersection(segment);
380
				if (intersection != null && !line.getP1().equals(intersection)
381
						&& !line.getP2().equals(intersection)
382
						&& !segment.getP1().equals(intersection)
383
						&& !segment.getP2().equals(intersection)) {
384
					// if we have a single intersection point and this does not
385
					// match one of the end points of the line, the line is not
386
					// contained
387
					return false;
388
				}
642
			}
389
			}
643
		}
390
		}
644
		return false;
391
		return true;
645
	}
392
	}
646
393
647
	/**
394
	/**
648
	 * Checks if there is at least a common point between this {@link Polygon}
395
	 * Checks whether the given {@link Polygon} is fully contained within this
649
	 * and the given {@link Polygon}, which includes the case that the given
396
	 * {@link Polygon}.
650
	 * {@link Polygon} is fully contained within this {@link Polygon} or vice
651
	 * versa.
652
	 * 
397
	 * 
653
	 * @param p
398
	 * @param p
654
	 *            the {@link Polygon} to test for intersection
399
	 *            The {@link Polygon} to test for containment
655
	 * @return <code>true</code> in case this {@link Polygon} and the given
400
	 * @return <code>true</code> if the given {@link Polygon} is fully
656
	 *         {@link Polygon} share at least one common point.
401
	 *         contained, <code>false</code> otherwise.
657
	 */
402
	 */
658
	public boolean intersects(Polygon p) {
403
	public boolean contains(Polygon p) {
659
		// reduce to segment intersection test
404
		// all segments of the given polygon have to be contained
660
		Line[] otherSegments = p.getOutlineSegments();
405
		Line[] otherSegments = p.getOutlineSegments();
661
		for (int i = 0; i < otherSegments.length; i++) {
406
		for (int i = 0; i < otherSegments.length; i++) {
662
			if (intersects(otherSegments[i])) {
407
			if (!contains(otherSegments[i])) {
663
				return true;
408
				return false;
664
			}
665
		}
666
		// no intersection, so we still need to check for containment
667
		return p.contains(this);
668
	}
669
670
	/**
671
	 * Tests if this {@link Polygon} intersects with the given {@link Polyline}
672
	 * p.
673
	 * 
674
	 * @param p
675
	 * @return true if they intersect, false otherwise
676
	 */
677
	public boolean intersects(Polyline p) {
678
		// reduce to segment intersection test
679
		Line[] otherSegments = p.getCurves();
680
		for (int i = 0; i < otherSegments.length; i++) {
681
			if (intersects(otherSegments[i])) {
682
				return true;
683
			}
409
			}
684
		}
410
		}
685
		// no intersection, so we still need to check for containment
411
		return true;
686
		return contains(p);
687
	}
688
689
	/**
690
	 * Checks if there is at least a common point between this {@link Polygon}
691
	 * and the given {@link Rectangle}, which includes the case that the given
692
	 * {@link Rectangle} is fully contained within this {@link Polygon} or vice
693
	 * versa.
694
	 * 
695
	 * @see IGeometry#intersects(Rectangle)
696
	 */
697
	public boolean intersects(Rectangle rect) {
698
		return intersects(rect.toPolygon());
699
	}
700
701
	/**
702
	 * Rotates this {@link Polygon} counter-clock-wise by the given
703
	 * {@link Angle} alpha around the given {@link Point} center.
704
	 * 
705
	 * The rotation is done by
706
	 * <ol>
707
	 * <li>translating this {@link Polygon} by the negated {@link Point} center</li>
708
	 * <li>rotating each {@link Point} of this {@link Polygon}
709
	 * counter-clock-wise by the given {@link Angle} angle</li>
710
	 * <li>translating this {@link Polygon} back by the {@link Point} center</li>
711
	 * </ol>
712
	 * 
713
	 * @param alpha
714
	 *            The rotation {@link Angle}.
715
	 * @param center
716
	 *            The {@link Point} to rotate around.
717
	 * @return This (counter-clock-wise-rotated) {@link Polygon} object.
718
	 */
719
	public Polygon rotateCCW(Angle alpha, Point center) {
720
		translate(center.getNegated());
721
		for (Point p : points) {
722
			Point np = new Vector(p).rotateCCW(alpha).toPoint();
723
			p.x = np.x;
724
			p.y = np.y;
725
		}
726
		translate(center);
727
		return this;
728
	}
412
	}
729
413
730
	/**
414
	/**
731
	 * Rotates this {@link Polygon} clock-wise by the given {@link Angle} alpha
415
	 * Checks whether the given {@link Rectangle} is fully contained within this
732
	 * around the given {@link Point} center.
416
	 * {@link Polygon}.
733
	 * 
734
	 * The rotation is done by
735
	 * <ol>
736
	 * <li>translating this {@link Polygon} by the negated {@link Point} center</li>
737
	 * <li>rotating each {@link Point} of this {@link Polygon} clock-wise by the
738
	 * given {@link Angle} angle</li>
739
	 * <li>translating this {@link Polygon} back by the {@link Point} center</li>
740
	 * </ol>
741
	 * 
417
	 * 
742
	 * @param alpha
418
	 * @param r
743
	 *            The rotation {@link Angle}.
419
	 *            the {@link Rectangle} to test for containment
744
	 * @param center
420
	 * @return <code>true</code> if the given {@link Rectangle} is fully
745
	 *            The {@link Point} to rotate around.
421
	 *         contained, <code>false</code> otherwise.
746
	 * @return This (clock-wise-rotated) {@link Polygon} object.
747
	 */
422
	 */
748
	public Polygon rotateCW(Angle alpha, Point center) {
423
	public boolean contains(Rectangle r) {
749
		translate(center.getNegated());
424
		return contains(r.toPolygon());
750
		for (Point p : points) {
751
			Point np = new Vector(p).rotateCW(alpha).toPoint();
752
			p.x = np.x;
753
			p.y = np.y;
754
		}
755
		translate(center);
756
		return this;
757
	}
425
	}
758
426
759
	/**
427
	/**
760
	 * Scales this {@link Polygon} object by the given factor from the given
428
	 * Tests if the given {@link Polyline} p is contained in this
761
	 * center {@link Point}.
429
	 * {@link Polygon}.
762
	 * 
763
	 * The scaling is done by
764
	 * <ol>
765
	 * <li>translating this {@link Polygon} by the negated center {@link Point}</li>
766
	 * <li>scaling the individual {@link Polygon} {@link Point}s</li>
767
	 * <li>translating this {@link Polygon} back</li>
768
	 * </ol>
769
	 * 
430
	 * 
770
	 * @param factor
431
	 * @param p
771
	 *            The scale-factor.
432
	 * @return true if it is contained, false otherwise
772
	 * @param center
773
	 *            The rotation {@link Point}.
774
	 * @return This (scaled) {@link Polygon} object.
775
	 */
776
	public Polygon scale(double factor, Point center) {
777
		translate(center.getNegated());
778
		for (Point p : points) {
779
			Point np = p.getScaled(factor);
780
			p.x = np.x;
781
			p.y = np.y;
782
		}
783
		translate(center);
784
		return this;
785
	}
786
787
	/**
788
	 * @see IGeometry#toPath()
789
	 */
433
	 */
790
	public Path toPath() {
434
	public boolean contains(Polyline p) {
791
		Path path = new Path();
435
		// all segments of the given polygon have to be contained
792
		if (points.length > 0) {
436
		Line[] otherSegments = p.getCurves();
793
			path.moveTo(points[0].x, points[0].y);
437
		for (int i = 0; i < otherSegments.length; i++) {
794
			for (int i = 1; i < points.length; i++) {
438
			if (!contains(otherSegments[i])) {
795
				path.lineTo(points[i].x, points[i].y);
439
				return false;
796
			}
440
			}
797
			path.close();
798
		}
441
		}
799
		return path;
442
		return true;
800
	}
443
	}
801
444
802
	@Override
445
	public boolean contains(IGeometry g) {
803
	public String toString() {
446
		if (g instanceof Line) {
804
		StringBuffer stringBuffer = new StringBuffer("Polygon: ");
447
			return contains((Line) g);
805
		if (points.length > 0) {
448
		} else if (g instanceof Polygon) {
806
			for (int i = 0; i < points.length; i++) {
449
			return contains((Polygon) g);
807
				stringBuffer.append("(" + points[i].x + ", " + points[i].y
450
		} else if (g instanceof Polyline) {
808
						+ ")");
451
			return contains((Polyline) g);
809
				stringBuffer.append(" -> ");
452
		} else if (g instanceof Rectangle) {
810
			}
453
			return contains((Rectangle) g);
811
			stringBuffer.append("(" + points[0].x + ", " + points[0].y + ")");
812
		} else {
813
			stringBuffer.append("<no points>");
814
		}
454
		}
815
		return stringBuffer.toString();
455
		return CurveUtils.contains(this, g);
816
	}
817
818
	/**
819
	 * Moves this Polygon horizontally by dx and vertically by dy, then returns
820
	 * this Rectangle for convenience.
821
	 * 
822
	 * @param dx
823
	 *            Shift along X axis
824
	 * @param dy
825
	 *            Shift along Y axis
826
	 * @return <code>this</code> for convenience
827
	 */
828
	public Polygon translate(double dx, double dy) {
829
		PointListUtils.translate(points, dx, dy);
830
		return this;
831
	}
832
833
	/**
834
	 * Moves this Polygon horizontally by the x value of the given Point and
835
	 * vertically by the y value of the given Point, then returns this Rectangle
836
	 * for convenience.
837
	 * 
838
	 * @param p
839
	 *            Point which provides translation information
840
	 * @return <code>this</code> for convenience
841
	 */
842
	public Polygon translate(Point p) {
843
		return translate(p.x, p.y);
844
	}
456
	}
845
457
846
	// TODO: union point, rectangle, polygon, etc.
458
	// TODO: union point, rectangle, polygon, etc.
(-)a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Polyline.java (-11 / +72 lines)
Lines 14-19 package org.eclipse.gef4.geometry.planar; Link Here
14
14
15
import org.eclipse.gef4.geometry.Point;
15
import org.eclipse.gef4.geometry.Point;
16
import org.eclipse.gef4.geometry.transform.AffineTransform;
16
import org.eclipse.gef4.geometry.transform.AffineTransform;
17
import org.eclipse.gef4.geometry.utils.CurveUtils;
17
import org.eclipse.gef4.geometry.utils.PointListUtils;
18
import org.eclipse.gef4.geometry.utils.PointListUtils;
18
import org.eclipse.gef4.geometry.utils.PrecisionUtils;
19
import org.eclipse.gef4.geometry.utils.PrecisionUtils;
19
20
Lines 27-33 import org.eclipse.gef4.geometry.utils.PrecisionUtils; Link Here
27
 * 
28
 * 
28
 * @author anyssen
29
 * @author anyssen
29
 */
30
 */
30
public class Polyline extends AbstractPointListBasedGeometry implements IPolyCurve {
31
public class Polyline extends AbstractPointListBasedGeometry<Polyline>
32
		implements IPolyCurve {
31
33
32
	private static final long serialVersionUID = 1L;
34
	private static final long serialVersionUID = 1L;
33
35
Lines 61-66 public class Polyline extends AbstractPointListBasedGeometry implements IPolyCur Link Here
61
	}
63
	}
62
64
63
	/**
65
	/**
66
	 * Constructs a new {@link Polyline} from the given array of {@link Line}
67
	 * segments.
68
	 * 
69
	 * @param segmentsArray
70
	 */
71
	public Polyline(Line[] segmentsArray) {
72
		super(PointListUtils.toPointsArray(segmentsArray, false));
73
	}
74
75
	/**
64
	 * Checks whether the point that is represented by its x- and y-coordinates
76
	 * Checks whether the point that is represented by its x- and y-coordinates
65
	 * is contained within this {@link Polyline}.
77
	 * is contained within this {@link Polyline}.
66
	 * 
78
	 * 
Lines 90-103 public class Polyline extends AbstractPointListBasedGeometry implements IPolyCur Link Here
90
		return false;
102
		return false;
91
	}
103
	}
92
104
93
	/**
94
	 * @see IGeometry#contains(Rectangle)
95
	 */
96
	public boolean contains(Rectangle r) {
97
		// may contain the rectangle only in case it is degenerated
98
		return false;
99
	}
100
101
	@Override
105
	@Override
102
	public boolean equals(Object o) {
106
	public boolean equals(Object o) {
103
		if (this == o)
107
		if (this == o)
Lines 148-156 public class Polyline extends AbstractPointListBasedGeometry implements IPolyCur Link Here
148
	}
152
	}
149
153
150
	/**
154
	/**
151
	 * @see IGeometry#intersects(Rectangle)
155
	 * Tests whether this {@link Polyline} and the given {@link Rectangle}
156
	 * touch, i.e. they have at least one {@link Point} in common.
157
	 * 
158
	 * @param rect
159
	 *            the {@link Rectangle} to test
160
	 * @return <code>true</code> if this {@link Polyline} and the
161
	 *         {@link Rectangle} touch, otherwise <code>false</code>
162
	 * @see IGeometry#touches(IGeometry)
152
	 */
163
	 */
153
	public boolean intersects(Rectangle rect) {
164
	public boolean touches(Rectangle rect) {
154
		throw new UnsupportedOperationException("Not yet implemented.");
165
		throw new UnsupportedOperationException("Not yet implemented.");
155
	}
166
	}
156
167
Lines 168-173 public class Polyline extends AbstractPointListBasedGeometry implements IPolyCur Link Here
168
		return path;
179
		return path;
169
	}
180
	}
170
181
182
	/**
183
	 * Transforms this {@link Polyline} into a {@link PolyBezier}.
184
	 * 
185
	 * @return a {@link PolyBezier} representing this {@link Polyline}
186
	 */
187
	public PolyBezier toPolyBezier() {
188
		Line[] segments = PointListUtils.toSegmentsArray(points, false);
189
		return new PolyBezier(segments);
190
	}
191
171
	@Override
192
	@Override
172
	public String toString() {
193
	public String toString() {
173
		StringBuffer stringBuffer = new StringBuffer("Polyline: ");
194
		StringBuffer stringBuffer = new StringBuffer("Polyline: ");
Lines 192-195 public class Polyline extends AbstractPointListBasedGeometry implements IPolyCur Link Here
192
		return new Polyline(getPoints());
213
		return new Polyline(getPoints());
193
	}
214
	}
194
215
216
	public double getY2() {
217
		return getP2().y;
218
	}
219
220
	public double getY1() {
221
		return getP1().y;
222
	}
223
224
	public double getX2() {
225
		return getP2().x;
226
	}
227
228
	public double getX1() {
229
		return getP1().x;
230
	}
231
232
	public Point getP2() {
233
		return points[points.length - 1].getCopy();
234
	}
235
236
	public Point getP1() {
237
		return points[0].getCopy();
238
	}
239
240
	public Line[] toBezier() {
241
		return PointListUtils.toSegmentsArray(points, false);
242
	}
243
244
	public Point[] getIntersections(ICurve c) {
245
		return CurveUtils.getIntersections(c, this);
246
	}
247
248
	public boolean intersects(ICurve c) {
249
		return CurveUtils.intersects(c, this);
250
	}
251
252
	public boolean overlaps(ICurve c) {
253
		return CurveUtils.overlaps(c, this);
254
	}
255
195
}
256
}
(-)a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/QuadraticCurve.java (-193 / +10 lines)
Lines 12-24 Link Here
12
 *******************************************************************************/
12
 *******************************************************************************/
13
package org.eclipse.gef4.geometry.planar;
13
package org.eclipse.gef4.geometry.planar;
14
14
15
import java.util.Arrays;
16
import java.util.HashSet;
17
18
import org.eclipse.gef4.geometry.Point;
15
import org.eclipse.gef4.geometry.Point;
19
import org.eclipse.gef4.geometry.utils.CurveUtils;
20
import org.eclipse.gef4.geometry.utils.PolynomCalculationUtils;
16
import org.eclipse.gef4.geometry.utils.PolynomCalculationUtils;
21
import org.eclipse.gef4.geometry.utils.PrecisionUtils;
22
17
23
/**
18
/**
24
 * Represents the geometric shape of a quadratic Bézier curve.
19
 * Represents the geometric shape of a quadratic Bézier curve.
Lines 30-139 public class QuadraticCurve extends BezierCurve { Link Here
30
25
31
	private static final long serialVersionUID = 1L;
26
	private static final long serialVersionUID = 1L;
32
27
33
	private static Point[] getIntersections(QuadraticCurve p, double ps,
34
			double pe, Line l) {
35
		// parameter convergence test
36
		double pm = (ps + pe) / 2;
37
38
		if (PrecisionUtils.equal(ps, pe, +2)) {
39
			return new Point[] { p.get(pm) };
40
		}
41
42
		// no parameter convergence
43
44
		// clip the curve
45
		QuadraticCurve pc = p.clip(ps, pe);
46
47
		// check the control polygon
48
		Polygon polygon = pc.getControlPolygon();
49
50
		if (polygon.intersects(l)) {
51
			// area test
52
			if (PrecisionUtils.equal(polygon.getBounds().getArea(), 0, +2)) {
53
				// line/line intersection fallback for such small curves
54
				Point poi = new Line(pc.getP1(), pc.getP2()).getIntersection(l);
55
				if (poi != null) {
56
					return new Point[] { poi };
57
				}
58
				return new Point[] {};
59
			}
60
61
			// individually test the curves left and right sides for points of
62
			// intersection
63
			HashSet<Point> intersections = new HashSet<Point>();
64
65
			intersections.addAll(Arrays.asList(getIntersections(p, ps, pm, l)));
66
			intersections.addAll(Arrays.asList(getIntersections(p, pm, pe, l)));
67
68
			return intersections.toArray(new Point[] {});
69
		}
70
71
		// no intersections
72
		return new Point[] {};
73
	}
74
75
	private static Point[] getIntersections(QuadraticCurve p, double ps,
76
			double pe, QuadraticCurve q, double qs, double qe) {
77
		// parameter convergence test
78
		double pm = (ps + pe) / 2;
79
		double qm = (qs + qe) / 2;
80
81
		if (PrecisionUtils.equal(ps, pe)) {
82
			return new Point[] { p.get(pm) };
83
		}
84
85
		if (PrecisionUtils.equal(qs, qe)) {
86
			return new Point[] { q.get(qm) };
87
		}
88
89
		// no parameter convergence
90
91
		// clip to parameter ranges
92
		QuadraticCurve pc = p.clip(ps, pe);
93
		QuadraticCurve qc = q.clip(qs, qe);
94
95
		// check the control polygons
96
		Polygon pPoly = pc.getControlPolygon();
97
		Polygon qPoly = qc.getControlPolygon();
98
99
		if (pPoly.intersects(qPoly)) {
100
			// check the polygon's areas
101
			double pArea = pPoly.getBounds().getArea();
102
			double qArea = qPoly.getBounds().getArea();
103
104
			if (PrecisionUtils.equal(pArea, 0, -2)
105
					&& PrecisionUtils.equal(qArea, 0, -2)) {
106
				// return line/line intersection
107
				Point poi = new Line(pc.getP1(), pc.getP2())
108
						.getIntersection(new Line(qc.getP1(), qc.getP2()));
109
				if (poi != null) {
110
					return new Point[] { poi };
111
				}
112
				return new Point[] {};
113
			}
114
115
			// areas not small enough
116
117
			// individually test the left and right parts of the curves for
118
			// points of intersection
119
			HashSet<Point> intersections = new HashSet<Point>();
120
121
			intersections.addAll(Arrays.asList(getIntersections(p, ps, pm, q,
122
					qs, qm)));
123
			intersections.addAll(Arrays.asList(getIntersections(p, ps, pm, q,
124
					qm, qe)));
125
			intersections.addAll(Arrays.asList(getIntersections(p, pm, pe, q,
126
					qs, qm)));
127
			intersections.addAll(Arrays.asList(getIntersections(p, pm, pe, q,
128
					qm, qe)));
129
130
			return intersections.toArray(new Point[] {});
131
		}
132
133
		// no intersections
134
		return new Point[] {};
135
	}
136
137
	/**
28
	/**
138
	 * Constructs a new {@link QuadraticCurve} from the given sequence of x- and
29
	 * Constructs a new {@link QuadraticCurve} from the given sequence of x- and
139
	 * y-coordinates of the start-, the control-, and the end-point.
30
	 * y-coordinates of the start-, the control-, and the end-point.
Lines 207-247 public class QuadraticCurve extends BezierCurve { Link Here
207
	 * @return the {@link QuadraticCurve} on the interval [t1, t2]
98
	 * @return the {@link QuadraticCurve} on the interval [t1, t2]
208
	 */
99
	 */
209
	public QuadraticCurve clip(double t1, double t2) {
100
	public QuadraticCurve clip(double t1, double t2) {
210
		return CurveUtils.clip(this, t1, t2);
101
		return super.getClipped(t1, t2).toQuadratic();
211
	}
212
213
	/**
214
	 * Check if the given {@link Point} (approximately) lies on the curve.
215
	 * 
216
	 * @param p
217
	 *            the {@link Point} in question
218
	 * @return true if p lies on the curve, false otherwise
219
	 */
220
	public boolean contains(Point p) {
221
		return CurveUtils.contains(this, p);
222
	}
102
	}
223
103
224
	public boolean equals(Object other) {
104
	public boolean equals(Object other) {
225
		QuadraticCurve o = (QuadraticCurve) other;
105
		QuadraticCurve o = (QuadraticCurve) other;
226
106
227
		AbstractPointListBasedGeometry myPoly = getControlPolygon();
107
		Polygon myPoly = getControlPolygon();
228
		AbstractPointListBasedGeometry otherPoly = o.getControlPolygon();
108
		Polygon otherPoly = o.getControlPolygon();
229
109
230
		return myPoly.equals(otherPoly);
110
		return myPoly.equals(otherPoly);
231
	}
111
	}
232
112
233
	/**
113
	/**
234
	 * Get a single {@link Point} on this QuadraticCurve at parameter t.
235
	 * 
236
	 * @param t
237
	 *            in range [0,1]
238
	 * @return the {@link Point} at parameter t
239
	 */
240
	public Point get(double t) {
241
		return CurveUtils.get(this, t);
242
	}
243
244
	/**
245
	 * Returns the bounds of this QuadraticCurve. The bounds are calculated by
114
	 * Returns the bounds of this QuadraticCurve. The bounds are calculated by
246
	 * examining the extreme points of the x(t) and y(t) function
115
	 * examining the extreme points of the x(t) and y(t) function
247
	 * representations of this QuadraticCurve.
116
	 * representations of this QuadraticCurve.
Lines 335-341 public class QuadraticCurve extends BezierCurve { Link Here
335
	 * @return the control point's x-coordinate
204
	 * @return the control point's x-coordinate
336
	 */
205
	 */
337
	public double getCtrlX() {
206
	public double getCtrlX() {
338
		return getCtrlX(0);
207
		return getPoint(1).x;
339
	}
208
	}
340
209
341
	/**
210
	/**
Lines 344-350 public class QuadraticCurve extends BezierCurve { Link Here
344
	 * @return the control point's y-coordinate
213
	 * @return the control point's y-coordinate
345
	 */
214
	 */
346
	public double getCtrlY() {
215
	public double getCtrlY() {
347
		return getCtrlY(0);
216
		return getPoint(1).y;
348
	}
217
	}
349
218
350
	/**
219
	/**
Lines 369-428 public class QuadraticCurve extends BezierCurve { Link Here
369
	}
238
	}
370
239
371
	/**
240
	/**
372
	 * Returns the points of intersection between this {@link QuadraticCurve}
373
	 * and the given {@link Line} l.
374
	 * 
375
	 * @param l
376
	 * @return The points of intersection.
377
	 */
378
	public Point[] getIntersections(Line l) {
379
		return getIntersections(this, 0, 1, l);
380
	}
381
382
	/**
383
	 * Calculates the intersections of two {@link QuadraticCurve}s using the
384
	 * subdivision algorithm.
385
	 * 
386
	 * @param other
387
	 * @return points of intersection
388
	 */
389
	public Point[] getIntersections(QuadraticCurve other) {
390
		return getIntersections(this, 0, 1, other, 0, 1);
391
	}
392
393
	/**
394
	 * Checks if this {@link QuadraticCurve} intersects with the given line.
395
	 * (Costly)
396
	 * 
397
	 * TODO: implement a faster algorithm for this intersection-test.
398
	 * 
399
	 * @param l
400
	 * @return true if they intersect, false otherwise.
401
	 */
402
	public boolean intersects(Line l) {
403
		return getIntersections(l).length > 0;
404
	}
405
406
	/**
407
	 * Checks if two {@link QuadraticCurve}s intersect each other. (Costly)
408
	 * 
409
	 * @param other
410
	 * @return true if the two curves intersect. false otherwise
411
	 */
412
	public boolean intersects(QuadraticCurve other) {
413
		return getIntersections(other).length > 0;
414
	}
415
416
	public boolean intersects(Rectangle r) {
417
		for (Line l : r.getOutlineSegments()) {
418
			if (intersects(l)) {
419
				return true;
420
			}
421
		}
422
		return false;
423
	}
424
425
	/**
426
	 * Sets the curve's control point.
241
	 * Sets the curve's control point.
427
	 * 
242
	 * 
428
	 * @param ctrl
243
	 * @param ctrl
Lines 438-444 public class QuadraticCurve extends BezierCurve { Link Here
438
	 * @param ctrlX
253
	 * @param ctrlX
439
	 */
254
	 */
440
	public void setCtrlX(double ctrlX) {
255
	public void setCtrlX(double ctrlX) {
441
		setCtrlX(0, ctrlX);
256
		setPoint(1, new Point(ctrlX, getCtrlY()));
442
	}
257
	}
443
258
444
	/**
259
	/**
Lines 447-453 public class QuadraticCurve extends BezierCurve { Link Here
447
	 * @param ctrlY
262
	 * @param ctrlY
448
	 */
263
	 */
449
	public void setCtrlY(double ctrlY) {
264
	public void setCtrlY(double ctrlY) {
450
		setCtrlY(0, ctrlY);
265
		setPoint(1, new Point(getCtrlX(), ctrlY));
451
	}
266
	}
452
267
453
	/**
268
	/**
Lines 461-467 public class QuadraticCurve extends BezierCurve { Link Here
461
	 *         [0, t] 2. [t, 1]
276
	 *         [0, t] 2. [t, 1]
462
	 */
277
	 */
463
	public QuadraticCurve[] split(double t) {
278
	public QuadraticCurve[] split(double t) {
464
		return CurveUtils.split(this, t);
279
		BezierCurve[] split = super.split(t);
280
		return new QuadraticCurve[] { split[0].toQuadratic(),
281
				split[1].toQuadratic() };
465
	}
282
	}
466
283
467
	/**
284
	/**
(-)a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Rectangle.java (-191 / +121 lines)
Lines 17-22 import org.eclipse.gef4.geometry.Angle; Link Here
17
import org.eclipse.gef4.geometry.Dimension;
17
import org.eclipse.gef4.geometry.Dimension;
18
import org.eclipse.gef4.geometry.Point;
18
import org.eclipse.gef4.geometry.Point;
19
import org.eclipse.gef4.geometry.transform.AffineTransform;
19
import org.eclipse.gef4.geometry.transform.AffineTransform;
20
import org.eclipse.gef4.geometry.utils.CurveUtils;
21
import org.eclipse.gef4.geometry.utils.PointListUtils;
20
import org.eclipse.gef4.geometry.utils.PrecisionUtils;
22
import org.eclipse.gef4.geometry.utils.PrecisionUtils;
21
23
22
/**
24
/**
Lines 37-44 import org.eclipse.gef4.geometry.utils.PrecisionUtils; Link Here
37
 * @author ahunter
39
 * @author ahunter
38
 * @author anyssen
40
 * @author anyssen
39
 */
41
 */
40
public final class Rectangle extends AbstractRectangleBasedGeometry implements
42
public final class Rectangle extends AbstractRectangleBasedGeometry<Rectangle>
41
		IShape {
43
		implements IShape {
42
44
43
	private static final long serialVersionUID = 1L;
45
	private static final long serialVersionUID = 1L;
44
46
Lines 147-175 public final class Rectangle extends AbstractRectangleBasedGeometry implements Link Here
147
	}
149
	}
148
150
149
	/**
151
	/**
150
	 * Returns true in case the rectangle specified by (x, y, width, height) is
151
	 * contained within this {@link Rectangle}.
152
	 * 
153
	 * @param x
154
	 *            The x coordinate of the rectangle to be tested for containment
155
	 * @param y
156
	 *            The y coordinate of the rectangle to be tested for containment
157
	 * @param width
158
	 *            The width of the rectangle to be tested for containment
159
	 * @param height
160
	 *            The height of the rectangle to be tested for containment
161
	 * @return <code>true</code> if the rectangle characterized by (x,y, width,
162
	 *         height) is (imprecisely) fully contained within this
163
	 *         {@link Rectangle}, <code>false</code> otherwise
164
	 */
165
	public boolean contains(double x, double y, double width, double height) {
166
		return PrecisionUtils.smallerEqual(this.x, x)
167
				&& PrecisionUtils.smallerEqual(this.y, y)
168
				&& PrecisionUtils.greaterEqual(this.width, width)
169
				&& PrecisionUtils.greaterEqual(this.height, height);
170
	}
171
172
	/**
173
	 * Returns whether the given point is within the boundaries of this
152
	 * Returns whether the given point is within the boundaries of this
174
	 * Rectangle. The boundaries are inclusive of the top and left edges, but
153
	 * Rectangle. The boundaries are inclusive of the top and left edges, but
175
	 * exclusive of the bottom and right edges.
154
	 * exclusive of the bottom and right edges.
Lines 184-196 public final class Rectangle extends AbstractRectangleBasedGeometry implements Link Here
184
	}
163
	}
185
164
186
	/**
165
	/**
187
	 * @see IGeometry#contains(Rectangle)
188
	 */
189
	public boolean contains(Rectangle r) {
190
		return contains(r.x, r.y, r.width, r.height);
191
	}
192
193
	/**
194
	 * Returns <code>true</code> if this Rectangle's x, y, width, and height
166
	 * Returns <code>true</code> if this Rectangle's x, y, width, and height
195
	 * values are identical to the provided ones.
167
	 * values are identical to the provided ones.
196
	 * 
168
	 * 
Lines 297-340 public final class Rectangle extends AbstractRectangleBasedGeometry implements Link Here
297
	}
269
	}
298
270
299
	/**
271
	/**
300
	 * Returns a new {@link Rectangle}, whose location is identical to the
301
	 * location of this {@link Rectangle}, but whose size is scaled by the given
302
	 * factor.
303
	 * 
304
	 * @param scale
305
	 *            the scale factor, with which to multiply the height and width
306
	 *            of this {@link Rectangle} when computing the size of the new
307
	 *            {@link Rectangle} to be returned
308
	 * @return a new {@link Rectangle} with a location identical to this one's
309
	 *         and a size that is computed by multiplying width and height of
310
	 *         this {@link Rectangle} by the given factor.
311
	 */
312
	public Rectangle getScaled(double scale) {
313
		return getScaled(scale, scale);
314
	}
315
316
	/**
317
	 * Returns a new {@link Rectangle}, whose location is identical to the
318
	 * location of this {@link Rectangle}, but whose size is scaled by the given
319
	 * factors.
320
	 * 
321
	 * @param scaleX
322
	 *            the factor, with which to multiply the width of this
323
	 *            {@link Rectangle}, when computing the size of the new
324
	 *            {@link Rectangle} to be returned
325
	 * @param scaleY
326
	 *            the factor, with which to multiply the height of this
327
	 *            {@link Rectangle} when computing the size of the new
328
	 *            {@link Rectangle} to be returned
329
	 * @return a new {@link Rectangle} with a location identical to this one's
330
	 *         and a size that is computed by multiplying width and height of
331
	 *         this {@link Rectangle} by the given factor.
332
	 */
333
	public Rectangle getScaled(double scaleX, double scaleY) {
334
		return getCopy().scale(scaleX, scaleY);
335
	}
336
337
	/**
338
	 * Returns an array of {@link Line}s representing the top, right, bottom,
272
	 * Returns an array of {@link Line}s representing the top, right, bottom,
339
	 * and left borders of this {@link Rectangle}.
273
	 * and left borders of this {@link Rectangle}.
340
	 * 
274
	 * 
Lines 380-394 public final class Rectangle extends AbstractRectangleBasedGeometry implements Link Here
380
	}
314
	}
381
315
382
	/**
316
	/**
383
	 * Returns a new point representing the center of this Rectangle.
384
	 * 
385
	 * @return Point at the center of the rectangle
386
	 */
387
	public Point getCenter() {
388
		return new Point(x + width / 2, y + height / 2);
389
	}
390
391
	/**
392
	 * Returns a new Rectangle which has the exact same parameters as this
317
	 * Returns a new Rectangle which has the exact same parameters as this
393
	 * Rectangle.
318
	 * Rectangle.
394
	 * 
319
	 * 
Lines 458-472 public final class Rectangle extends AbstractRectangleBasedGeometry implements Link Here
458
	}
383
	}
459
384
460
	/**
385
	/**
461
	 * Returns the location of this {@link Rectangle}.
462
	 * 
463
	 * @return The current location
464
	 */
465
	public Point getLocation() {
466
		return new Point(x, y);
467
	}
468
469
	/**
470
	 * Returns a new Point which represents the middle point of the right hand
386
	 * Returns a new Point which represents the middle point of the right hand
471
	 * side of this Rectangle.
387
	 * side of this Rectangle.
472
	 * 
388
	 * 
Lines 478-515 public final class Rectangle extends AbstractRectangleBasedGeometry implements Link Here
478
394
479
	/**
395
	/**
480
	 * Rotates this {@link Rectangle} clock-wise by the given {@link Angle}
396
	 * Rotates this {@link Rectangle} clock-wise by the given {@link Angle}
481
	 * alpha around the {@link Point} center.
397
	 * around the center ({@link #getCentroid()}) of this {@link Rectangle}.
398
	 * 
399
	 * @see #getRotatedCW(Angle, Point)
400
	 * @param alpha
401
	 *            the rotation {@link Angle}
402
	 * @return the resulting {@link Polygon}
403
	 */
404
	public Polygon getRotatedCW(Angle alpha) {
405
		return getRotatedCW(alpha, getCentroid());
406
	}
407
408
	/**
409
	 * Rotates this {@link Rectangle} clock-wise by the given {@link Angle}
410
	 * alpha around the given {@link Point}.
482
	 * 
411
	 * 
483
	 * If the rotation {@link Angle} is not an integer multiple 90\u00b0, the
412
	 * If the rotation {@link Angle} is not an integer multiple of 90 degrees,
484
	 * resulting figure cannot be expressed as a {@link Rectangle} object.
413
	 * the resulting figure cannot be expressed as a {@link Rectangle} object.
485
	 * That's why this method returns a {@link Polygon} instead.
414
	 * That's why this method returns a {@link Polygon} instead.
486
	 * 
415
	 * 
487
	 * @param alpha
416
	 * @param alpha
488
	 *            the rotation angle
417
	 *            the rotation angle
489
	 * @param center
418
	 * @param center
490
	 *            the center point of the rotation
419
	 *            the center point for the rotation
491
	 * @return the rotated rectangle ({@link Polygon})
420
	 * @return the resulting {@link Polygon}
492
	 */
421
	 */
493
	public Polygon getRotatedCW(Angle alpha, Point center) {
422
	public Polygon getRotatedCW(Angle alpha, Point center) {
494
		return toPolygon().rotateCW(alpha, center);
423
		return (Polygon) toPolygon().rotateCW(alpha, center);
424
	}
425
426
	/**
427
	 * Rotates this {@link Rectangle} counter-clock-wise by the given
428
	 * {@link Angle} around the center {@link Point} of this {@link Rectangle}
429
	 * (see {@link #getCentroid()}).
430
	 * 
431
	 * @see #getRotatedCCW(Angle, Point)
432
	 * @param alpha
433
	 * @return the resulting {@link Polygon}
434
	 */
435
	public Polygon getRotatedCCW(Angle alpha) {
436
		return getRotatedCCW(alpha, getCentroid());
495
	}
437
	}
496
438
497
	/**
439
	/**
498
	 * Rotates this {@link Rectangle} counter-clock-wise by the given
440
	 * Rotates this {@link Rectangle} counter-clock-wise by the given
499
	 * {@link Angle} alpha around the {@link Point} center.
441
	 * {@link Angle} around the given {@link Point}.
500
	 * 
442
	 * 
501
	 * If the rotation {@link Angle} is not an integer multiple 90\u00b0, the
443
	 * If the rotation {@link Angle} is not an integer multiple of 90 degrees,
502
	 * resulting figure cannot be expressed as a {@link Rectangle} object.
444
	 * the resulting figure cannot be expressed as a {@link Rectangle} object.
503
	 * That's why this method returns a {@link Polygon} instead.
445
	 * That's why this method returns a {@link Polygon} instead.
504
	 * 
446
	 * 
505
	 * @param alpha
447
	 * @param alpha
506
	 *            the rotation angle
448
	 *            the rotation angle
507
	 * @param center
449
	 * @param center
508
	 *            the center point of the rotation
450
	 *            the center point for the rotation
509
	 * @return the rotated rectangle ({@link Polygon})
451
	 * @return the resulting {@link Polygon}
510
	 */
452
	 */
511
	public Polygon getRotatedCCW(Angle alpha, Point center) {
453
	public Polygon getRotatedCCW(Angle alpha, Point center) {
512
		return toPolygon().rotateCCW(alpha, center);
454
		return (Polygon) toPolygon().rotateCCW(alpha, center);
513
	}
455
	}
514
456
515
	/**
457
	/**
Lines 589-622 public final class Rectangle extends AbstractRectangleBasedGeometry implements Link Here
589
	}
531
	}
590
532
591
	/**
533
	/**
592
	 * Returns a new Rectangle which is shifted along each axis by the passed
593
	 * values.
594
	 * 
595
	 * @param dx
596
	 *            Displacement along X axis
597
	 * @param dy
598
	 *            Displacement along Y axis
599
	 * @return The new translated rectangle
600
	 * 
601
	 */
602
	public Rectangle getTranslated(double dx, double dy) {
603
		return getCopy().translate(dx, dy);
604
	}
605
606
	/**
607
	 * Returns a new Rectangle which is shifted by the position of the given
608
	 * Point.
609
	 * 
610
	 * @param pt
611
	 *            Point providing the amount of shift along each axis
612
	 * @return The new translated Rectangle
613
	 * 
614
	 */
615
	public Rectangle getTranslated(Point pt) {
616
		return getCopy().translate(pt);
617
	}
618
619
	/**
620
	 * Returns a new rectangle whose width and height have been interchanged, as
534
	 * Returns a new rectangle whose width and height have been interchanged, as
621
	 * well as its x and y values. This can be useful in orientation changes.
535
	 * well as its x and y values. This can be useful in orientation changes.
622
	 * 
536
	 * 
Lines 676-683 public final class Rectangle extends AbstractRectangleBasedGeometry implements Link Here
676
	}
590
	}
677
591
678
	/**
592
	/**
679
	 * Tests whether this {@link Rectangle} and the given {@link Line}
593
	 * Tests whether this {@link Rectangle} and the given {@link Line} touch,
680
	 * intersect, i.e. whether they have at least one point in common.
594
	 * i.e. whether they have at least one point in common.
681
	 * 
595
	 * 
682
	 * @param l
596
	 * @param l
683
	 *            The {@link Line} to test.
597
	 *            The {@link Line} to test.
Lines 685-691 public final class Rectangle extends AbstractRectangleBasedGeometry implements Link Here
685
	 *         {@link Line} share at least one common point, <code>false</code>
599
	 *         {@link Line} share at least one common point, <code>false</code>
686
	 *         otherwise.
600
	 *         otherwise.
687
	 */
601
	 */
688
	public boolean intersects(Line l) {
602
	public boolean touches(Line l) {
689
		if (contains(l.getP1()) || contains(l.getP2())) {
603
		if (contains(l.getP1()) || contains(l.getP2())) {
690
			return true;
604
			return true;
691
		}
605
		}
Lines 699-708 public final class Rectangle extends AbstractRectangleBasedGeometry implements Link Here
699
	}
613
	}
700
614
701
	/**
615
	/**
702
	 * @see IGeometry#intersects(Rectangle)
616
	 * Tests whether this {@link Rectangle} and the given other
703
	 */
617
	 * {@link Rectangle} touch, i.e. whether they have at least one point in
704
	public boolean intersects(Rectangle r) {
618
	 * common.
705
		return !getIntersected(r).isEmpty();
619
	 * 
620
	 * @param r
621
	 *            The {@link Rectangle} to test
622
	 * @return <code>true</code> if this {@link Rectangle} and the given
623
	 *         {@link Rectangle} share at least one common point,
624
	 *         <code>false</code> otherwise.
625
	 * @see IGeometry#touches(IGeometry)
626
	 */
627
	public boolean touches(Rectangle r) {
628
		return PrecisionUtils.smallerEqual(r.x, x + width)
629
				&& PrecisionUtils.smallerEqual(r.y, y + height)
630
				&& PrecisionUtils.greaterEqual(r.x + r.width, x)
631
				&& PrecisionUtils.greaterEqual(r.y + r.height, y);
632
	}
633
634
	public boolean touches(IGeometry g) {
635
		if (g instanceof Line) {
636
			return touches((Line) g);
637
		} else if (g instanceof Rectangle) {
638
			return touches((Rectangle) g);
639
		}
640
		return super.touches(g);
706
	}
641
	}
707
642
708
	/**
643
	/**
Lines 719-752 public final class Rectangle extends AbstractRectangleBasedGeometry implements Link Here
719
	}
654
	}
720
655
721
	/**
656
	/**
722
	 * Scales the size of this Rectangle by the given scale and returns this for
723
	 * convenience.
724
	 * 
725
	 * @param scaleFactor
726
	 *            The factor by which this size will be scaled
727
	 * @return <code>this</code> Rectangle for convenience
728
	 */
729
	public Rectangle scale(double scaleFactor) {
730
		return scale(scaleFactor, scaleFactor);
731
	}
732
733
	/**
734
	 * Scales the size of this Rectangle by the given scales and returns this
735
	 * for convenience.
736
	 * 
737
	 * @param scaleX
738
	 *            the factor by which the width has to be scaled
739
	 * @param scaleY
740
	 *            the factor by which the height has to be scaled
741
	 * @return <code>this</code> Rectangle for convenience
742
	 */
743
	public Rectangle scale(double scaleX, double scaleY) {
744
		width *= scaleX;
745
		height *= scaleY;
746
		return this;
747
	}
748
749
	/**
750
	 * Shrinks the sides of this Rectangle by the horizontal and vertical values
657
	 * Shrinks the sides of this Rectangle by the horizontal and vertical values
751
	 * provided as input, and returns this Rectangle for convenience. The center
658
	 * provided as input, and returns this Rectangle for convenience. The center
752
	 * of this Rectangle is kept constant.
659
	 * of this Rectangle is kept constant.
Lines 807-813 public final class Rectangle extends AbstractRectangleBasedGeometry implements Link Here
807
	 * @return A {@link Polygon} representation for this {@link Rectangle}
714
	 * @return A {@link Polygon} representation for this {@link Rectangle}
808
	 */
715
	 */
809
	public Polygon toPolygon() {
716
	public Polygon toPolygon() {
810
		return new Polygon(getPoints());
717
		return new Polygon(PointListUtils.copy(getPoints()));
811
	}
718
	}
812
719
813
	@Override
720
	@Override
Lines 835-867 public final class Rectangle extends AbstractRectangleBasedGeometry implements Link Here
835
	}
742
	}
836
743
837
	/**
744
	/**
838
	 * Moves this {@link Rectangle} horizontally by dx and vertically by dy.
839
	 * 
840
	 * @param dx
841
	 *            Shift along X axis
842
	 * @param dy
843
	 *            Shift along Y axis
844
	 * @return <code>this</code> for convenience
845
	 */
846
	public Rectangle translate(double dx, double dy) {
847
		x += dx;
848
		y += dy;
849
		return this;
850
	}
851
852
	/**
853
	 * Moves this {@link Rectangle} horizontally by the x value of the given
854
	 * {@link Point} and vertically by the y value of the given {@link Point}.
855
	 * 
856
	 * @param p
857
	 *            The {@link Point} which provides the translation information
858
	 * @return <code>this</code> for convenience
859
	 */
860
	public Rectangle translate(Point p) {
861
		return translate(p.x, p.y);
862
	}
863
864
	/**
865
	 * Switches the x and y values, as well as the width and height of this
745
	 * Switches the x and y values, as well as the width and height of this
866
	 * Rectangle. Useful for orientation changes.
746
	 * Rectangle. Useful for orientation changes.
867
	 * 
747
	 * 
Lines 953-956 public final class Rectangle extends AbstractRectangleBasedGeometry implements Link Here
953
		return union(r.x, r.y, r.width, r.height);
833
		return union(r.x, r.y, r.width, r.height);
954
	}
834
	}
955
835
836
	public Polyline getOutline() {
837
		return new Polyline(x, y, x + width, y, x + width, y + height, x, y
838
				+ height, x, y);
839
	}
840
841
	public boolean contains(IGeometry g) {
842
		if (g instanceof Rectangle) {
843
			return contains((Rectangle) g);
844
		}
845
		return CurveUtils.contains(this, g);
846
	}
847
848
	/**
849
	 * Returns true in case the rectangle specified by (x, y, width, height) is
850
	 * contained within this {@link Rectangle}.
851
	 * 
852
	 * @param x
853
	 *            The x coordinate of the rectangle to be tested for containment
854
	 * @param y
855
	 *            The y coordinate of the rectangle to be tested for containment
856
	 * @param width
857
	 *            The width of the rectangle to be tested for containment
858
	 * @param height
859
	 *            The height of the rectangle to be tested for containment
860
	 * @return <code>true</code> if the rectangle characterized by (x,y, width,
861
	 *         height) is (imprecisely) fully contained within this
862
	 *         {@link Rectangle}, <code>false</code> otherwise
863
	 */
864
	public boolean contains(double x, double y, double width, double height) {
865
		return PrecisionUtils.smallerEqual(this.x, x)
866
				&& PrecisionUtils.smallerEqual(this.y, y)
867
				&& PrecisionUtils.greaterEqual(this.width, width)
868
				&& PrecisionUtils.greaterEqual(this.height, height);
869
	}
870
871
	/**
872
	 * Tests whether this {@link Rectangle} fully contains the given other
873
	 * {@link Rectangle}.
874
	 * 
875
	 * @param r
876
	 *            the other {@link Rectangle} to test for being contained by
877
	 *            this {@link Rectangle}
878
	 * @return <code>true</code> if this {@link Rectangle} contains the other
879
	 *         {@link Rectangle}, otherwise <code>false</code>
880
	 * @see IShape#contains(IGeometry)
881
	 */
882
	public boolean contains(Rectangle r) {
883
		return contains(r.x, r.y, r.width, r.height);
884
	}
885
956
}
886
}
(-)a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Region.java (-1 / +1 lines)
Lines 37-43 public class Region extends AbstractGeometry implements IPolyShape { Link Here
37
		throw new UnsupportedOperationException("Not yet implemented.");
37
		throw new UnsupportedOperationException("Not yet implemented.");
38
	}
38
	}
39
39
40
	public boolean intersects(Rectangle r) {
40
	public boolean touches(Rectangle r) {
41
		throw new UnsupportedOperationException("Not yet implemented.");
41
		throw new UnsupportedOperationException("Not yet implemented.");
42
	}
42
	}
43
43
(-)a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/Ring.java (-1 / +2 lines)
Lines 38-44 public class Ring extends AbstractGeometry implements IPolyShape { Link Here
38
		throw new UnsupportedOperationException("Not yet implemented.");
38
		throw new UnsupportedOperationException("Not yet implemented.");
39
	}
39
	}
40
40
41
	public boolean intersects(Rectangle r) {
41
	public boolean touches(Rectangle r) {
42
		throw new UnsupportedOperationException("Not yet implemented.");
42
		throw new UnsupportedOperationException("Not yet implemented.");
43
	}
43
	}
44
44
Lines 49-52 public class Ring extends AbstractGeometry implements IPolyShape { Link Here
49
	public IGeometry getCopy() {
49
	public IGeometry getCopy() {
50
		throw new UnsupportedOperationException("Not yet implemented.");
50
		throw new UnsupportedOperationException("Not yet implemented.");
51
	}
51
	}
52
52
}
53
}
(-)a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/planar/RoundedRectangle.java (-52 / +12 lines)
Lines 14-19 package org.eclipse.gef4.geometry.planar; Link Here
14
14
15
import org.eclipse.gef4.geometry.Angle;
15
import org.eclipse.gef4.geometry.Angle;
16
import org.eclipse.gef4.geometry.Point;
16
import org.eclipse.gef4.geometry.Point;
17
import org.eclipse.gef4.geometry.utils.CurveUtils;
17
import org.eclipse.gef4.geometry.utils.PrecisionUtils;
18
import org.eclipse.gef4.geometry.utils.PrecisionUtils;
18
19
19
/**
20
/**
Lines 27-34 import org.eclipse.gef4.geometry.utils.PrecisionUtils; Link Here
27
 * 
28
 * 
28
 * @author anyssen
29
 * @author anyssen
29
 */
30
 */
30
public final class RoundedRectangle extends AbstractRectangleBasedGeometry implements
31
public final class RoundedRectangle extends
31
		IShape {
32
		AbstractRectangleBasedGeometry<Rectangle> implements IShape {
32
33
33
	private static final long serialVersionUID = 1L;
34
	private static final long serialVersionUID = 1L;
34
35
Lines 126-140 public final class RoundedRectangle extends AbstractRectangleBasedGeometry imple Link Here
126
	}
127
	}
127
128
128
	/**
129
	/**
129
	 * @see IGeometry#contains(Rectangle)
130
	 */
131
	public boolean contains(final Rectangle r) {
132
		// check that all border points of the rectangle are contained.
133
		return contains(r.getTopLeft()) && contains(r.getTopRight())
134
				&& contains(r.getBottomLeft()) && contains(r.getBottomRight());
135
	}
136
137
	/**
138
	 * Returns the arc height of this {@link RoundedRectangle}, which is the
130
	 * Returns the arc height of this {@link RoundedRectangle}, which is the
139
	 * height of the arc used to define its rounded corners.
131
	 * height of the arc used to define its rounded corners.
140
	 * 
132
	 * 
Lines 155-201 public final class RoundedRectangle extends AbstractRectangleBasedGeometry imple Link Here
155
	}
147
	}
156
148
157
	/**
149
	/**
158
	 * @see IGeometry#intersects(Rectangle)
159
	 */
160
	public boolean intersects(final Rectangle r) {
161
		// quick rejection via bounds
162
		final Rectangle testRect = getBounds();
163
		if (!testRect.intersects(r)) {
164
			return false;
165
		}
166
167
		// check for intersection within the two inner rectangles
168
		testRect.setBounds(x, y + arcHeight, width, height - 2 * arcHeight);
169
		if (testRect.intersects(r)) {
170
			return true;
171
		}
172
		testRect.setBounds(x + arcWidth, y, width - 2 * arcWidth, height);
173
		if (testRect.contains(r)) {
174
			return true;
175
		}
176
177
		// check the arcs
178
		final Ellipse e = new Ellipse(x, y, 2 * arcWidth, 2 * arcHeight);
179
		if (e.intersects(r)) {
180
			return true;
181
		}
182
		e.setBounds(x, y + height - 2 * arcHeight, 2 * arcWidth, 2 * arcHeight);
183
		if (e.intersects(r)) {
184
			return true;
185
		}
186
		e.setBounds(x + width - 2 * arcWidth, y, 2 * arcWidth, 2 * arcHeight);
187
		if (e.intersects(r)) {
188
			return true;
189
		}
190
		e.setBounds(x + width - 2 * arcWidth, y + height - 2 * arcHeight,
191
				2 * arcWidth, 2 * arcHeight);
192
		if (e.intersects(r)) {
193
			return true;
194
		}
195
		return false;
196
	}
197
198
	/**
199
	 * Sets the arc height of this {@link RoundedRectangle}, which is the height
150
	 * Sets the arc height of this {@link RoundedRectangle}, which is the height
200
	 * of the arc used to define its rounded corners.
151
	 * of the arc used to define its rounded corners.
201
	 * 
152
	 * 
Lines 344-347 public final class RoundedRectangle extends AbstractRectangleBasedGeometry imple Link Here
344
	public RoundedRectangle getCopy() {
295
	public RoundedRectangle getCopy() {
345
		return new RoundedRectangle(x, y, width, height, arcWidth, arcHeight);
296
		return new RoundedRectangle(x, y, width, height, arcWidth, arcHeight);
346
	}
297
	}
298
299
	public IPolyCurve getOutline() {
300
		return CurveUtils.getOutline(this);
301
	}
302
303
	public boolean contains(IGeometry g) {
304
		return CurveUtils.contains(this, g);
305
	}
306
347
}
307
}
(-)a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/projective/Straight3D.java (+116 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2011 itemis AG and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthias Wienand (itemis AG) - initial API and implementation
10
 *     
11
 *******************************************************************************/
12
package org.eclipse.gef4.geometry.projective;
13
14
import org.eclipse.gef4.geometry.Point;
15
16
/**
17
 * <p>
18
 * A two-dimensional infinite line that is defined by three coordinates of which
19
 * the third is a so called homogeneous coordinate. Calculations are easier to
20
 * do on such lines:
21
 * <ul>
22
 * <li>the point of intersection between two lines is the cross product of their
23
 * respective three dimensional vectors</li>
24
 * <li>the distance from a point to the line is the scalar product of both three
25
 * dimensional vectors</li>
26
 * </ul>
27
 * </p>
28
 * <p>
29
 * This is the complement to the {@link Vector3D} which represents a
30
 * {@link Point} with a third, homogeneous coordinate.
31
 * </p>
32
 * 
33
 * @author wienand
34
 */
35
public final class Straight3D {
36
	private Vector3D sp, line;
37
	private double f;
38
39
	private Straight3D() {
40
	}
41
42
	/**
43
	 * Constructs a new {@link Straight3D} through the given start and end
44
	 * {@link Vector3D}s.
45
	 * 
46
	 * @param start
47
	 * @param end
48
	 * @return a new {@link Straight3D} through start and end {@link Vector3D}s
49
	 */
50
	public static Straight3D through(Vector3D start, Vector3D end) {
51
		Straight3D self = new Straight3D();
52
		self.sp = start;
53
		self.line = self.sp.getCrossed(end);
54
55
		self.f = Math.sqrt(self.line.x * self.line.x + self.line.y
56
				* self.line.y);
57
		if (self.f == 0d) {
58
			return null;
59
		}
60
61
		return self;
62
	}
63
64
	/**
65
	 * Returns the orthogonal {@link Straight3D} through this {@link Straight3D}
66
	 * 's start {@link Vector3D}.
67
	 * 
68
	 * @return the orthogonal {@link Straight3D} through this {@link Straight3D}
69
	 *         's start {@link Vector3D}
70
	 */
71
	public Straight3D getOrtho() {
72
		return getOrtho(sp);
73
	}
74
75
	/**
76
	 * Returns the orthogonal {@link Straight3D} through the given
77
	 * {@link Vector3D}.
78
	 * 
79
	 * @param vp
80
	 * @return the orthogonal {@link Straight3D} through the given
81
	 *         {@link Vector3D}
82
	 */
83
	public Straight3D getOrtho(Vector3D vp) {
84
		return Straight3D.through(vp, new Vector3D(vp.x + line.x,
85
				vp.y + line.y, vp.z));
86
	}
87
88
	/**
89
	 * Returns the clock-wise signed distance of the given {@link Vector3D} to
90
	 * this {@link Straight3D}. The clock-wise signed distance is the dot
91
	 * product of the both {@link Vector3D}s divided by the length of the line's
92
	 * (x,y) vector: <code>|(x,y)|</code>.
93
	 * 
94
	 * @param vp
95
	 * @return the clock-wise signed distance of the {@link Vector3D} to this
96
	 *         {@link Straight3D}
97
	 */
98
	public double getSignedDistanceCW(Vector3D vp) {
99
		Point p = vp.toPoint();
100
		return (line.x * p.x + line.y * p.y + line.z) / f;
101
	}
102
103
	/**
104
	 * Returns the intersection between this and the given other
105
	 * {@link Straight3D}. The intersection is the cross product of both
106
	 * {@link Vector3D}s.
107
	 * 
108
	 * @param other
109
	 * @return the intersection between this and the given other
110
	 *         {@link Straight3D}
111
	 */
112
	public Vector3D getIntersection(Straight3D other) {
113
		return line.getCrossed(other.line);
114
	}
115
116
}
(-)a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/projective/Vector3D.java (+195 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2011 itemis AG and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Matthias Wienand (itemis AG) - initial API and implementation
10
 *     
11
 *******************************************************************************/
12
package org.eclipse.gef4.geometry.projective;
13
14
import org.eclipse.gef4.geometry.Point;
15
16
/**
17
 * The Vector3D class implements a three dimensional vector (components x, y, z)
18
 * with its standard operations: addition and multiplication (scalar,
19
 * dot-product, cross-product).
20
 * 
21
 * It is used to represent planar lines and planar points which are represented
22
 * by three dimensional planes and three dimensional lines through the origin,
23
 * respectively.
24
 * 
25
 * @author wienand
26
 */
27
public final class Vector3D {
28
	/**
29
	 * the x-coordinate of this {@link Vector3D}.
30
	 */
31
	public double x;
32
33
	/**
34
	 * the y-coordinate of this {@link Vector3D}.
35
	 */
36
	public double y;
37
38
	/**
39
	 * the homogeneous coordinate of this {@link Vector3D}.
40
	 */
41
	public double z;
42
43
	/**
44
	 * Constructs a new {@link Vector3D} from the given {@link Point}, setting z
45
	 * to 1.
46
	 * 
47
	 * @param p
48
	 */
49
	public Vector3D(Point p) {
50
		this(p.x, p.y, 1);
51
	}
52
53
	/**
54
	 * Constructs a new {@link Vector3D} object with the given component values.
55
	 * 
56
	 * @param px
57
	 * @param py
58
	 * @param pz
59
	 */
60
	public Vector3D(double px, double py, double pz) {
61
		x = px;
62
		y = py;
63
		z = pz;
64
	}
65
66
	/**
67
	 * Returns a copy of this {@link Vector3D}.
68
	 * 
69
	 * @return a copy of this {@link Vector3D}
70
	 */
71
	public Vector3D getCopy() {
72
		return new Vector3D(x, y, z);
73
	}
74
75
	@Override
76
	public boolean equals(Object other) {
77
		if (other instanceof Vector3D) {
78
			Vector3D o = (Vector3D) other;
79
			Point tmp = this.toPoint();
80
			if (tmp == null) {
81
				return o.toPoint() == null;
82
			}
83
			return tmp.equals(o.toPoint());
84
		}
85
		return false;
86
	}
87
88
	/**
89
	 * Returns a new {@link Vector3D} object with its components set to the sum
90
	 * of the individual x, y and z components of this {@link Vector3D} and the
91
	 * given other {@link Vector3D}.
92
	 * 
93
	 * @param other
94
	 * @return a new {@link Vector3D} object representing the sum of this
95
	 *         {@link Vector3D} and the given other {@link Vector3D}
96
	 */
97
	public Vector3D getAdded(Vector3D other) {
98
		return new Vector3D(this.x + other.x, this.y + other.y, this.z
99
				+ other.z);
100
	}
101
102
	/**
103
	 * Returns a new {@link Vector3D} object with its components set to the
104
	 * difference of the individual x, y and z components of this
105
	 * {@link Vector3D} and the given other {@link Vector3D}.
106
	 * 
107
	 * @param other
108
	 * @return a new {@link Vector3D} object representing the difference of this
109
	 *         {@link Vector3D} and the given other {@link Vector3D}
110
	 */
111
	public Vector3D getSubtracted(Vector3D other) {
112
		return new Vector3D(this.x - other.x, this.y - other.y, this.z
113
				- other.z);
114
	}
115
116
	/**
117
	 * Returns a new {@link Vector3D} object with its components set to the x, y
118
	 * and z components of this {@link Vector3D} scaled by the given factor.
119
	 * 
120
	 * @param f
121
	 *            The scaling factor.
122
	 * @return a new {@link Vector3D} object with its components set to the x, y
123
	 *         and z components of this {@link Vector3D} scaled by the given
124
	 *         factor
125
	 */
126
	public Vector3D getScaled(double f) {
127
		return new Vector3D(x * f, y * f, z * f);
128
	}
129
130
	/**
131
	 * Returns a new {@link Vector3D} object with its components set to the
132
	 * given ratio between this {@link Vector3D} and the given other
133
	 * {@link Vector3D}.
134
	 * 
135
	 * @param other
136
	 *            The other {@link Vector3D}.
137
	 * @param t
138
	 *            The ratio.
139
	 * @return a new {@link Vector3D} object with its components set to the
140
	 *         given ratio between this {@link Vector3D} and the given other
141
	 *         {@link Vector3D}
142
	 */
143
	public Vector3D getRatio(Vector3D other, double t) {
144
		return getAdded(other.getSubtracted(this).getScaled(t));
145
	}
146
147
	/**
148
	 * Returns a new {@link Vector3D} object that is the cross product of this
149
	 * and the given other {@link Vector3D}.
150
	 * 
151
	 * @param other
152
	 * @return a new {@link Vector3D} object that is the cross product of this
153
	 *         and the given other {@link Vector3D}
154
	 */
155
	public Vector3D getCrossed(Vector3D other) {
156
		return new Vector3D(this.y * other.z - this.z * other.y, this.z
157
				* other.x - this.x * other.z, this.x * other.y - this.y
158
				* other.x);
159
	}
160
161
	/**
162
	 * Returns the dot-product of this and the given other {@link Vector3D}.
163
	 * 
164
	 * @param other
165
	 * @return the dot-product of this and the given other {@link Vector3D}
166
	 */
167
	public double getDot(Vector3D other) {
168
		return this.x * other.x + this.y * other.y + this.z * other.z;
169
	}
170
171
	/**
172
	 * Returns a new {@link Point} object that is represented by this
173
	 * {@link Vector3D}.
174
	 * 
175
	 * @return a new {@link Point} object that is represented by this
176
	 *         {@link Vector3D}
177
	 */
178
	public Point toPoint() {
179
		if (this.z == 0) {
180
			return null;
181
		}
182
		return new Point(this.x / this.z, this.y / this.z);
183
	}
184
185
	public String toString() {
186
		return "Vector3D (" + x + ", " + y + ", " + z + ")";
187
	}
188
189
	@Override
190
	public int hashCode() {
191
		// cannot generate a good hash-code because of the imprecise
192
		// comparisons
193
		return 0;
194
	}
195
}
(-)a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/transform/AffineTransform.java (+108 lines)
Lines 17-87 import org.eclipse.gef4.geometry.Point; Link Here
17
import org.eclipse.gef4.geometry.convert.AWT2Geometry;
17
import org.eclipse.gef4.geometry.convert.AWT2Geometry;
18
import org.eclipse.gef4.geometry.convert.Geometry2AWT;
18
import org.eclipse.gef4.geometry.convert.Geometry2AWT;
19
19
20
/**
21
 * TODO
22
 */
20
public class AffineTransform {
23
public class AffineTransform {
21
	// TODO: implement affine transform locally to get rid of dependency on
24
	// TODO: implement affine transform locally to get rid of dependency on
22
	// awt.geom.
25
	// awt.geom.
23
26
24
	private java.awt.geom.AffineTransform delegate = new java.awt.geom.AffineTransform();
27
	private java.awt.geom.AffineTransform delegate = new java.awt.geom.AffineTransform();
25
28
29
	/**
30
	 * TODO
31
	 * 
32
	 * @return
33
	 */
26
	public double[] getMatrix() {
34
	public double[] getMatrix() {
27
		double[] flatmatrix = new double[6];
35
		double[] flatmatrix = new double[6];
28
		delegate.getMatrix(flatmatrix);
36
		delegate.getMatrix(flatmatrix);
29
		return flatmatrix;
37
		return flatmatrix;
30
	}
38
	}
31
39
40
	/**
41
	 * TODO
42
	 */
32
	public AffineTransform() {
43
	public AffineTransform() {
33
	}
44
	}
34
45
46
	/**
47
	 * TODO
48
	 * 
49
	 * @param m00
50
	 * @param m10
51
	 * @param m01
52
	 * @param m11
53
	 * @param m02
54
	 * @param m12
55
	 */
35
	public AffineTransform(double m00, double m10, double m01, double m11,
56
	public AffineTransform(double m00, double m10, double m01, double m11,
36
			double m02, double m12) {
57
			double m02, double m12) {
37
		delegate = new java.awt.geom.AffineTransform(m00, m10, m01, m11, m02,
58
		delegate = new java.awt.geom.AffineTransform(m00, m10, m01, m11, m02,
38
				m12);
59
				m12);
39
	}
60
	}
40
61
62
	/**
63
	 * TODO
64
	 * 
65
	 * @param flatmatrix
66
	 */
41
	public AffineTransform(double[] flatmatrix) {
67
	public AffineTransform(double[] flatmatrix) {
42
		delegate = new java.awt.geom.AffineTransform(flatmatrix);
68
		delegate = new java.awt.geom.AffineTransform(flatmatrix);
43
	}
69
	}
44
70
71
	/**
72
	 * TODO
73
	 * 
74
	 * @see java.awt.geom.AffineTransform#getType()
75
	 * 
76
	 * @return
77
	 */
45
	public int getType() {
78
	public int getType() {
46
		return delegate.getType();
79
		return delegate.getType();
47
	}
80
	}
48
81
82
	/**
83
	 * Computes the determinant of the transformation matrix of this
84
	 * {@link AffineTransform}.
85
	 * 
86
	 * @return the determinant of the transformation matrix of this
87
	 *         {@link AffineTransform}
88
	 */
49
	public double getDeterminant() {
89
	public double getDeterminant() {
50
		return delegate.getDeterminant();
90
		return delegate.getDeterminant();
51
	}
91
	}
52
92
93
	/**
94
	 * Returns the x-coordinate scaling of this {@link AffineTransform}'s
95
	 * transformation matrix.
96
	 * 
97
	 * @return the x-coordinate scaling of this {@link AffineTransform}'s
98
	 *         transformation matrix
99
	 */
53
	public double getScaleX() {
100
	public double getScaleX() {
54
		return delegate.getScaleX();
101
		return delegate.getScaleX();
55
	}
102
	}
56
103
104
	/**
105
	 * Returns the y-coordinate scaling of this {@link AffineTransform}'s
106
	 * transformation matrix.
107
	 * 
108
	 * @return the y-coordinate scaling of this {@link AffineTransform}'s
109
	 *         transformation matrix
110
	 */
57
	public double getScaleY() {
111
	public double getScaleY() {
58
		return delegate.getScaleY();
112
		return delegate.getScaleY();
59
	}
113
	}
60
114
115
	/**
116
	 * Returns the x-coordinate shearing of this {@link AffineTransform}'s
117
	 * transformation matrix.
118
	 * 
119
	 * @return the x-coordinate shearing of this {@link AffineTransform}'s
120
	 *         transformation matrix
121
	 */
61
	public double getShearX() {
122
	public double getShearX() {
62
		return delegate.getShearX();
123
		return delegate.getShearX();
63
	}
124
	}
64
125
126
	/**
127
	 * Returns the y-coordinate shearing of this {@link AffineTransform}'s
128
	 * transformation matrix.
129
	 * 
130
	 * @return the y-coordinate shearing of this {@link AffineTransform}'s
131
	 *         transformation matrix
132
	 */
65
	public double getShearY() {
133
	public double getShearY() {
66
		return delegate.getShearY();
134
		return delegate.getShearY();
67
	}
135
	}
68
136
137
	/**
138
	 * Returns the x-coordinate translation of this {@link AffineTransform}'s
139
	 * transformation matrix.
140
	 * 
141
	 * @return the x-coordinate translation of this {@link AffineTransform}'s
142
	 *         transformation matrix
143
	 */
69
	public double getTranslateX() {
144
	public double getTranslateX() {
70
		return delegate.getTranslateX();
145
		return delegate.getTranslateX();
71
	}
146
	}
72
147
148
	/**
149
	 * Returns the y-coordinate translation of this {@link AffineTransform}'s
150
	 * transformation matrix.
151
	 * 
152
	 * @return the y-coordinate translation of this {@link AffineTransform}'s
153
	 *         transformation matrix
154
	 */
73
	public double getTranslateY() {
155
	public double getTranslateY() {
74
		return delegate.getTranslateY();
156
		return delegate.getTranslateY();
75
	}
157
	}
76
158
159
	/**
160
	 * Sets the translation values of the x- and y-coordinates in the
161
	 * transformation matrix of this {@link AffineTransform}.
162
	 * 
163
	 * @param tx
164
	 *            the x-coordinate translation
165
	 * @param ty
166
	 *            the y-coordinate translation
167
	 */
77
	public void translate(double tx, double ty) {
168
	public void translate(double tx, double ty) {
78
		delegate.translate(tx, ty);
169
		delegate.translate(tx, ty);
79
	}
170
	}
80
171
172
	/**
173
	 * Adds a rotation with the given angle (in radians) to the transformation
174
	 * matrix of this {@link AffineTransform}.
175
	 * 
176
	 * @param theta
177
	 *            the rotation angle in radians
178
	 */
81
	public void rotate(double theta) {
179
	public void rotate(double theta) {
82
		delegate.rotate(theta);
180
		delegate.rotate(theta);
83
	}
181
	}
84
182
183
	/**
184
	 * Adds a rotation with the given angle (in radians) to the transformation
185
	 * matrix of this {@link AffineTransform}.
186
	 * 
187
	 * TODO
188
	 * 
189
	 * @param theta
190
	 * @param anchorx
191
	 * @param anchory
192
	 */
85
	public void rotate(double theta, double anchorx, double anchory) {
193
	public void rotate(double theta, double anchorx, double anchory) {
86
		delegate.rotate(theta, anchorx, anchory);
194
		delegate.rotate(theta, anchorx, anchory);
87
	}
195
	}
(-)a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/utils/CurveUtils.java (-1119 / +359 lines)
Lines 11-25 Link Here
11
 *******************************************************************************/
11
 *******************************************************************************/
12
package org.eclipse.gef4.geometry.utils;
12
package org.eclipse.gef4.geometry.utils;
13
13
14
import java.util.ArrayList;
15
import java.util.Arrays;
16
import java.util.Comparator;
14
import java.util.HashSet;
17
import java.util.HashSet;
15
import java.util.Set;
18
import java.util.Set;
16
19
17
import org.eclipse.gef4.geometry.Point;
20
import org.eclipse.gef4.geometry.Point;
18
import org.eclipse.gef4.geometry.euclidean.Straight;
21
import org.eclipse.gef4.geometry.euclidean.Straight;
19
import org.eclipse.gef4.geometry.planar.CubicCurve;
22
import org.eclipse.gef4.geometry.planar.BezierCurve;
23
import org.eclipse.gef4.geometry.planar.BezierCurve.IntervalPair;
24
import org.eclipse.gef4.geometry.planar.ICurve;
25
import org.eclipse.gef4.geometry.planar.IGeometry;
26
import org.eclipse.gef4.geometry.planar.IPolyCurve;
27
import org.eclipse.gef4.geometry.planar.IPolyShape;
28
import org.eclipse.gef4.geometry.planar.IShape;
20
import org.eclipse.gef4.geometry.planar.Line;
29
import org.eclipse.gef4.geometry.planar.Line;
21
import org.eclipse.gef4.geometry.planar.QuadraticCurve;
30
import org.eclipse.gef4.geometry.planar.PolyBezier;
22
import org.eclipse.gef4.geometry.planar.Rectangle;
23
31
24
/**
32
/**
25
 * The {@link CurveUtils} class provides functionality that can be used for
33
 * The {@link CurveUtils} class provides functionality that can be used for
Lines 32-1250 import org.eclipse.gef4.geometry.planar.Rectangle; Link Here
32
public class CurveUtils {
40
public class CurveUtils {
33
41
34
	/**
42
	/**
35
	 * The Vector3D class implements a three dimensional vector (components x,
43
	 * Delegates to the BezierCurve.getIntersections(ICurve) method.
36
	 * y, z) with its standard operations: addition and multiplication (scalar,
37
	 * dot-product, cross-product).
38
	 * 
44
	 * 
39
	 * It is used to represent planar lines and planar points which are
45
	 * @param curve1
40
	 * represented by three dimensional planes and three dimensional lines
46
	 * @param curve2
41
	 * through the origin, respectively.
47
	 * @return points of intersection
42
	 * 
43
	 * @author wienand
44
	 */
48
	 */
45
	private static final class Vector3D {
49
	public static Point[] getIntersections(ICurve curve1, ICurve curve2) {
46
		public double x, y, z;
50
		Set<Point> intersections = new HashSet<Point>();
47
48
		/**
49
		 * Constructs a new {@link Vector3D} from the given {@link Point},
50
		 * setting z to 1.
51
		 * 
52
		 * @param p
53
		 */
54
		public Vector3D(Point p) {
55
			this(p.x, p.y, 1);
56
		}
57
58
		/**
59
		 * Constructs a new {@link Vector3D} object with the given component
60
		 * values.
61
		 * 
62
		 * @param px
63
		 * @param py
64
		 * @param pz
65
		 */
66
		public Vector3D(double px, double py, double pz) {
67
			x = px;
68
			y = py;
69
			z = pz;
70
		}
71
51
72
		/**
52
		for (BezierCurve bezier : curve1.toBezier()) {
73
		 * Returns a copy of this {@link Vector3D}.
53
			intersections
74
		 * 
54
					.addAll(Arrays.asList(bezier.getIntersections(curve2)));
75
		 * @return a copy of this {@link Vector3D}
76
		 */
77
		public Vector3D getCopy() {
78
			return new Vector3D(x, y, z);
79
		}
80
81
		@Override
82
		public boolean equals(Object other) {
83
			if (other instanceof Vector3D) {
84
				Vector3D o = (Vector3D) other;
85
				Point tmp = this.toPoint();
86
				if (tmp == null) {
87
					return o.toPoint() == null;
88
				}
89
				return tmp.equals(o.toPoint());
90
			}
91
			return false;
92
		}
93
94
		/**
95
		 * Returns a new {@link Vector3D} object with its components set to the
96
		 * sum of the individual x, y and z components of this {@link Vector3D}
97
		 * and the given other {@link Vector3D}.
98
		 * 
99
		 * @param other
100
		 * @return a new {@link Vector3D} object representing the sum of this
101
		 *         {@link Vector3D} and the given other {@link Vector3D}
102
		 */
103
		public Vector3D getAdded(Vector3D other) {
104
			return new Vector3D(this.x + other.x, this.y + other.y, this.z
105
					+ other.z);
106
		}
107
108
		/**
109
		 * Returns a new {@link Vector3D} object with its components set to the
110
		 * difference of the individual x, y and z components of this
111
		 * {@link Vector3D} and the given other {@link Vector3D}.
112
		 * 
113
		 * @param other
114
		 * @return a new {@link Vector3D} object representing the difference of
115
		 *         this {@link Vector3D} and the given other {@link Vector3D}
116
		 */
117
		public Vector3D getSubtracted(Vector3D other) {
118
			return new Vector3D(this.x - other.x, this.y - other.y, this.z
119
					- other.z);
120
		}
121
122
		/**
123
		 * Returns a new {@link Vector3D} object with its components set to the
124
		 * x, y and z components of this {@link Vector3D} scaled by the given
125
		 * factor.
126
		 * 
127
		 * @param f
128
		 *            The scaling factor.
129
		 * @return a new {@link Vector3D} object with its components set to the
130
		 *         x, y and z components of this {@link Vector3D} scaled by the
131
		 *         given factor
132
		 */
133
		public Vector3D getScaled(double f) {
134
			return new Vector3D(x * f, y * f, z * f);
135
		}
136
137
		/**
138
		 * Returns a new {@link Vector3D} object with its components set to the
139
		 * given ratio between this {@link Vector3D} and the given other
140
		 * {@link Vector3D}.
141
		 * 
142
		 * @param other
143
		 *            The other {@link Vector3D}.
144
		 * @param t
145
		 *            The ratio.
146
		 * @return a new {@link Vector3D} object with its components set to the
147
		 *         given ratio between this {@link Vector3D} and the given other
148
		 *         {@link Vector3D}
149
		 */
150
		public Vector3D getRatio(Vector3D other, double t) {
151
			return getAdded(other.getSubtracted(this).getScaled(t));
152
		}
153
154
		/**
155
		 * Returns a new {@link Vector3D} object that has the same direction as
156
		 * this {@link Vector3D} but the x- and y-components are normalized so
157
		 * that <code>x*x + y*y = 1</code>.
158
		 * 
159
		 * @return a new {@link Vector3D} object with x- and y-components
160
		 *         normalized to fulfill x*x + y*y = 1.
161
		 */
162
		public Vector3D getLineNormalized() {
163
			double f = Math.sqrt(x * x + y * y);
164
			if (f == 0) {
165
				return null;
166
			}
167
			return new Vector3D(x / f, y / f, z / f);
168
		}
169
170
		/**
171
		 * Returns a new {@link Vector3D} object that is the cross product of
172
		 * this and the given other {@link Vector3D}.
173
		 * 
174
		 * @param other
175
		 * @return a new {@link Vector3D} object that is the cross product of
176
		 *         this and the given other {@link Vector3D}
177
		 */
178
		public Vector3D getCrossed(Vector3D other) {
179
			return new Vector3D(this.y * other.z - this.z * other.y, this.z
180
					* other.x - this.x * other.z, this.x * other.y - this.y
181
					* other.x);
182
		}
183
184
		/**
185
		 * Returns the dot-product of this and the given other {@link Vector3D}.
186
		 * 
187
		 * @param other
188
		 * @return the dot-product of this and the given other {@link Vector3D}
189
		 */
190
		public double getDot(Vector3D other) {
191
			return this.x * other.x + this.y * other.y + this.z * other.z;
192
		}
55
		}
193
56
194
		/**
57
		return intersections.toArray(new Point[] {});
195
		 * Returns a new {@link Point} object that is represented by this
196
		 * {@link Vector3D}.
197
		 * 
198
		 * @return a new {@link Point} object that is represented by this
199
		 *         {@link Vector3D}
200
		 */
201
		public Point toPoint() {
202
			if (this.z == 0) {
203
				return null;
204
			}
205
			return new Point(this.x / this.z, this.y / this.z);
206
		}
207
208
		public String toString() {
209
			return "Vector3D (" + x + ", " + y + ", " + z + ")";
210
		}
211
212
		@Override
213
		public int hashCode() {
214
			// cannot generate a good hash-code because of the imprecise
215
			// comparisons
216
			return 0;
217
		}
218
	}
58
	}
219
59
220
	/**
60
	/**
221
	 * The {@link BezierCurve} provides a common representation for arbitrary
61
	 * Delegates to the getIntersections(ICurve, ICurve) method.
222
	 * Bezier curves.
223
	 * 
224
	 * It can evaluate points on the curve, check points for containment and
225
	 * compute intersection points for one curve with another.
226
	 * 
62
	 * 
227
	 * It uses homogeneous coordinates (represented by {@link Vector3D} objects)
63
	 * @param curve
228
	 * to represent planar lines and points and to compute line/line
64
	 * @param shape
229
	 * intersections and point/line distances.
65
	 * @return points of intersection
230
	 * 
231
	 * @author wienand
232
	 */
66
	 */
233
	private static final class BezierCurve {
67
	public static Point[] getIntersections(ICurve curve, IShape shape) {
234
68
		Set<Point> intersections = new HashSet<Point>();
235
		private static final double UNRECOGNIZABLE_PRECISION_FRACTION = PrecisionUtils
236
				.calculateFraction(0) / 10;
237
69
238
		private Vector3D[] points;
70
		for (ICurve curve2 : shape.getOutlineSegments()) {
239
71
			intersections
240
		/**
72
					.addAll(Arrays.asList(getIntersections(curve, curve2)));
241
		 * Constructs a new {@link BezierCurve} object from the given control
242
		 * points.
243
		 * 
244
		 * @param controlPoints
245
		 */
246
		public BezierCurve(Point... controlPoints) {
247
			points = new Vector3D[controlPoints.length];
248
			for (int i = 0; i < points.length; i++) {
249
				points[i] = new Vector3D(controlPoints[i].x,
250
						controlPoints[i].y, 1);
251
			}
252
		}
73
		}
253
74
254
		/**
75
		return intersections.toArray(new Point[] {});
255
		 * Constructs a new {@link BezierCurve} object from the given control
76
	}
256
		 * points.
257
		 * 
258
		 * Note that a Point(2, 3) is represented by a Vector3D(2, 3, 1). So for
259
		 * a Point(x, y) the corresponding vector is Vector(x, y, 1).
260
		 * 
261
		 * @param controlPoints
262
		 */
263
		public BezierCurve(Vector3D... controlPoints) {
264
			points = new Vector3D[controlPoints.length];
265
			for (int i = 0; i < points.length; i++) {
266
				points[i] = controlPoints[i].getCopy();
267
			}
268
		}
269
270
		/**
271
		 * Constructs a new {@link BezierCurve} from the given
272
		 * {@link QuadraticCurve}.
273
		 * 
274
		 * @param c
275
		 */
276
		public BezierCurve(QuadraticCurve c) {
277
			this(c.getP1(), c.getCtrl(), c.getP2());
278
		}
279
77
280
		/**
78
	/**
281
		 * Constructs a new {@link BezierCurve} from the given
79
	 * Delegates to the getIntersections(ICurve, ICurve) method.
282
		 * {@link CubicCurve}.
80
	 * 
283
		 * 
81
	 * @param curve
284
		 * @param c
82
	 * @param polyCurve
285
		 */
83
	 * @return points of intersection
286
		public BezierCurve(CubicCurve c) {
84
	 */
287
			this(c.getP1(), c.getCtrl1(), c.getCtrl2(), c.getP2());
85
	public static Point[] getIntersections(ICurve curve, IPolyCurve polyCurve) {
288
		}
86
		Set<Point> intersections = new HashSet<Point>();
289
87
290
		/**
88
		for (ICurve curve2 : polyCurve.getCurves()) {
291
		 * Returns a copy of this {@link BezierCurve}'s points.
89
			intersections
292
		 * 
90
					.addAll(Arrays.asList(getIntersections(curve, curve2)));
293
		 * @return a copy of this {@link BezierCurve}'s points
294
		 */
295
		private Vector3D[] getPointsCopy() {
296
			Vector3D[] copy = new Vector3D[points.length];
297
			for (int i = 0; i < points.length; i++) {
298
				copy[i] = points[i].getCopy();
299
			}
300
			return copy;
301
		}
91
		}
302
92
303
		/**
93
		return intersections.toArray(new Point[] {});
304
		 * Constructs the explicit Bézier curve for this curve's x-components.
94
	}
305
		 * 
306
		 * @return the explicit Bézier curve for this curve's x-components
307
		 */
308
		public BezierCurve getExplicitX() {
309
			Vector3D[] explicit = new Vector3D[points.length];
310
95
311
			for (int i = 0; i < points.length; i++) {
96
	/**
312
				explicit[i] = new Vector3D((double) i
97
	 * Delegates to the getIntersections(ICurve, IShape) method.
313
						/ ((double) points.length - 1d), points[i].toPoint().x,
98
	 * 
314
						1);
99
	 * @param curve
315
			}
100
	 * @param polyShape
101
	 * @return points of intersection
102
	 */
103
	public static Point[] getIntersections(ICurve curve, IPolyShape polyShape) {
104
		Set<Point> intersections = new HashSet<Point>();
316
105
317
			return new BezierCurve(explicit);
106
		for (IShape shape : polyShape.getShapes()) {
107
			intersections.addAll(Arrays.asList(getIntersections(curve, shape)));
318
		}
108
		}
319
109
320
		/**
110
		return intersections.toArray(new Point[] {});
321
		 * Constructs the explicit Bézier curve for this curve's y-components.
111
	}
322
		 * 
323
		 * @return the explicit Bézier curve for this curve's y-components
324
		 */
325
		public BezierCurve getExplicitY() {
326
			Vector3D[] explicit = new Vector3D[points.length];
327
328
			for (int i = 0; i < points.length; i++) {
329
				explicit[i] = new Vector3D((double) i
330
						/ ((double) points.length - 1d), points[i].toPoint().y,
331
						1);
332
			}
333
112
334
			return new BezierCurve(explicit);
113
	/**
114
	 * Delegates to one of the above getIntersections() methods.
115
	 * 
116
	 * @param curve
117
	 * @param geom
118
	 * @return points of intersection
119
	 */
120
	public static Point[] getIntersections(ICurve curve, IGeometry geom) {
121
		if (geom instanceof ICurve) {
122
			return getIntersections(curve, (ICurve) geom);
123
		} else if (geom instanceof IShape) {
124
			return getIntersections(curve, (IShape) geom);
125
		} else if (geom instanceof IPolyCurve) {
126
			return getIntersections(curve, (IPolyCurve) geom);
127
		} else if (geom instanceof IPolyShape) {
128
			return getIntersections(curve, (IPolyShape) geom);
129
		} else {
130
			throw new UnsupportedOperationException("Not yet implemented.");
335
		}
131
		}
132
	}
336
133
337
		/**
134
	/**
338
		 * Checks if all y-components of this {@link BezierCurve}'s points have
135
	 * Delegates to one of the above getIntersections() methods.
339
		 * the same sign.
136
	 * 
340
		 * 
137
	 * @param geom1
341
		 * Returns true if either all y-components are positive or all
138
	 * @param geom2
342
		 * y-components are negative.
139
	 * @return points of intersection
343
		 * 
140
	 */
344
		 * Returns false, otherwise.
141
	public static Point[] getIntersections(IGeometry geom1, IGeometry geom2) {
345
		 * 
142
		if (geom1 instanceof ICurve) {
346
		 * @param c
143
			return getIntersections((ICurve) geom1, geom2);
347
		 * @return true if all y-components are either positive or negative,
144
		} else {
348
		 *         otherwise false
145
			Set<Point> intersections = new HashSet<Point>();
349
		 */
146
350
		private static boolean sameSign(BezierCurve c) {
147
			if (geom1 instanceof IPolyCurve) {
351
			double sign = c.points[0].toPoint().y;
148
				for (ICurve curve : ((IPolyCurve) geom1).getCurves()) {
352
			if (sign == 0) {
149
					intersections.addAll(Arrays.asList(getIntersections(curve,
353
				return false;
150
							geom2)));
354
			}
355
			for (int i = 1; i < c.points.length; i++) {
356
				if (sign < 0) {
357
					if (c.points[i].toPoint().y >= 0) {
358
						return false;
359
					}
360
				} else if (sign > 0) {
361
					if (c.points[i].toPoint().y <= 0) {
362
						return false;
363
					}
364
				}
151
				}
365
			}
152
			} else if (geom1 instanceof IShape) {
366
			return true;
153
				for (ICurve curve : ((IShape) geom1).getOutlineSegments()) {
367
		}
154
					intersections.addAll(Arrays.asList(getIntersections(curve,
368
155
							geom2)));
369
		/**
370
		 * Used to store parameter values in a HashSet with an imprecise
371
		 * equals() operation.
372
		 * 
373
		 * @author wienand
374
		 */
375
		private static final class ImpreciseDouble {
376
			private double value;
377
			private int shift;
378
379
			public ImpreciseDouble(double a) {
380
				value = a;
381
				shift = 1;
382
			}
383
384
			/**
385
			 * Returns the double value represented by this
386
			 * {@link ImpreciseDouble}.
387
			 * 
388
			 * @return the double value represented by this
389
			 *         {@link ImpreciseDouble}
390
			 */
391
			public double getValue() {
392
				return value;
393
			}
394
395
			@Override
396
			public boolean equals(Object obj) {
397
				if (obj instanceof ImpreciseDouble) {
398
					ImpreciseDouble o = (ImpreciseDouble) obj;
399
					return PrecisionUtils.equal(value, o.value, shift);
400
				}
156
				}
401
				return false;
157
			} else if (geom1 instanceof IPolyShape) {
402
			}
158
				for (IShape shape : ((IPolyShape) geom1).getShapes()) {
403
		}
159
					for (ICurve curve : shape.getOutlineSegments()) {
404
160
						intersections.addAll(Arrays.asList(getIntersections(
405
		/**
161
								curve, geom2)));
406
		 * Calculates the roots of the given explicit {@link BezierCurve} on the
407
		 * interval [a;b].
408
		 * 
409
		 * You can get an explicit {@link BezierCurve} from an arbitrary
410
		 * {@link BezierCurve} for either its x- or y-components using the
411
		 * {@link BezierCurve#getExplicitX()} or
412
		 * {@link BezierCurve#getExplicitY()} methods, respectively.
413
		 * 
414
		 * @param c
415
		 * @param a
416
		 *            start of the parameter interval
417
		 * @param b
418
		 *            end of the parameter interval
419
		 * @return the roots of the given explicit {@link BezierCurve} on the
420
		 *         interval [a;b]
421
		 */
422
		private static HashSet<ImpreciseDouble> getRoots(BezierCurve c,
423
				double a, double b) {
424
			BezierCurve clipped = c.getClipped(a, b);
425
426
			if (sameSign(clipped)) {
427
				return new HashSet<ImpreciseDouble>();
428
			}
429
430
			if (PrecisionUtils.equal(a, b, +2)) {
431
				HashSet<ImpreciseDouble> root = new HashSet<ImpreciseDouble>();
432
				root.add(new ImpreciseDouble((a + b) / 2));
433
				return root;
434
			}
435
436
			HashSet<ImpreciseDouble> left = getRoots(c, a, (a + b) / 2);
437
			HashSet<ImpreciseDouble> right = getRoots(c, (a + b) / 2, b);
438
439
			left.addAll(right);
440
441
			return left;
442
		}
443
444
		/**
445
		 * Calculates the roots of the given {@link BezierCurve} which is
446
		 * expected to be explicit.
447
		 * 
448
		 * You can get an explicit {@link BezierCurve} from an arbitrary
449
		 * {@link BezierCurve} for either its x- or y-components using the
450
		 * {@link BezierCurve#getExplicitX()} or
451
		 * {@link BezierCurve#getExplicitY()} methods, respectively.
452
		 * 
453
		 * @param c
454
		 * @return the roots of the given explicit {@link BezierCurve}
455
		 */
456
		private static double[] getRoots(BezierCurve c) {
457
			// TODO: check that the given BezierCurve is explicit
458
			HashSet<ImpreciseDouble> roots = getRoots(c, 0, 1);
459
			ImpreciseDouble[] rootsFuzzyDouble = roots
460
					.toArray(new ImpreciseDouble[] {});
461
			double[] rootsDouble = new double[rootsFuzzyDouble.length];
462
			for (int i = 0; i < rootsDouble.length; i++) {
463
				rootsDouble[i] = rootsFuzzyDouble[i].getValue();
464
			}
465
			return rootsDouble;
466
		}
467
468
		/**
469
		 * Computes all real roots of this {@link BezierCurve}'s x(t) function.
470
		 * 
471
		 * @return all real roots of this {@link BezierCurve}'s x(t) function
472
		 */
473
		public double[] getRootsX() {
474
			return getRoots(getExplicitX());
475
		}
476
477
		/**
478
		 * Computes all real roots of this {@link BezierCurve}'s y(t) function.
479
		 * 
480
		 * @return all real roots of this {@link BezierCurve}'s y(t) function
481
		 */
482
		public double[] getRootsY() {
483
			return getRoots(getExplicitY());
484
		}
485
486
		/**
487
		 * Computes the real planar {@link Point}s for this {@link BezierCurve}.
488
		 * 
489
		 * @return the real planar {@link Point}s for this {@link BezierCurve}
490
		 */
491
		public Point[] getRealPoints() {
492
			Point[] realPoints = new Point[points.length];
493
			for (int i = 0; i < points.length; i++) {
494
				realPoints[i] = points[i].toPoint();
495
			}
496
			return realPoints;
497
		}
498
499
		/**
500
		 * Returns the {@link Point} at the given parameter value t.
501
		 * 
502
		 * @param t
503
		 *            Parameter value
504
		 * @return {@link Point} at parameter value t
505
		 */
506
		public Vector3D get(double t) {
507
			if (t < 0 || t > 1) {
508
				throw new IllegalArgumentException("t out of range");
509
			}
510
511
			// using horner's scheme:
512
			int n = points.length;
513
			if (n < 1) {
514
				return null;
515
			}
516
517
			double bn = 1, tn = 1, d = 1d - t;
518
			Vector3D pn = points[0].getScaled(bn * tn);
519
			for (int i = 1; i < n; i++) {
520
				bn = bn * (n - i) / i;
521
				tn = tn * t;
522
				pn = pn.getScaled(d).getAdded(points[i].getScaled(bn * tn));
523
			}
524
525
			return pn;
526
		}
527
528
		/**
529
		 * Creates a new {@link BezierCurve} with all points translated by the
530
		 * given {@link Point}.
531
		 * 
532
		 * @param p
533
		 * @return a new {@link BezierCurve} with all points translated by the
534
		 *         given {@link Point}
535
		 */
536
		public BezierCurve getTranslated(Point p) {
537
			Point[] translated = new Point[points.length];
538
539
			for (int i = 0; i < translated.length; i++) {
540
				translated[i] = points[i].toPoint().getTranslated(p);
541
			}
542
543
			return new BezierCurve(translated);
544
		}
545
546
		/**
547
		 * Returns true if the given {@link Point} lies on this
548
		 * {@link BezierCurve}. Returns false, otherwise.
549
		 * 
550
		 * @param p
551
		 *            the {@link Point} to test for containment
552
		 * @return true if the {@link Point} is contained, false otherwise
553
		 */
554
		public boolean contains(Point p) {
555
			if (p == null) {
556
				return false;
557
			}
558
559
			BezierCurve test = this.getTranslated(p.getNegated());
560
			double[] xts = test.getRootsX();
561
			double[] yts = test.getRootsY();
562
563
			for (double xt : xts) {
564
				for (double yt : yts) {
565
					if (PrecisionUtils.equal(xt, yt)) {
566
						return true;
567
					} else {
568
						Point qx = get(xt).toPoint();
569
						Point qy = get(yt).toPoint();
570
						// qx != null && qy != null &&
571
						if (qx.equals(qy)) {
572
							return true;
573
						}
574
					}
162
					}
575
				}
163
				}
164
			} else {
165
				throw new UnsupportedOperationException("Not yet implemented.");
576
			}
166
			}
577
			return false;
578
		}
579
580
		/**
581
		 * Subdivides this {@link BezierCurve} at the given parameter value t
582
		 * into two new {@link BezierCurve}. The left-of t and the right-of t
583
		 * {@link BezierCurve} objects.
584
		 * 
585
		 * @param t
586
		 *            Parameter value
587
		 * @return The left-of t and right-of t {@link BezierCurve} objects
588
		 */
589
		public BezierCurve[] split(double t) {
590
			Vector3D[] leftPoints = new Vector3D[points.length];
591
			Vector3D[] rightPoints = new Vector3D[points.length];
592
593
			Vector3D[] ratioPoints = getPointsCopy();
594
595
			for (int i = 0; i < points.length; i++) {
596
				leftPoints[i] = ratioPoints[0];
597
				rightPoints[points.length - 1 - i] = ratioPoints[points.length
598
						- 1 - i];
599
600
				for (int j = 0; j < points.length - i - 1; j++) {
601
					ratioPoints[j] = ratioPoints[j].getRatio(
602
							ratioPoints[j + 1], t);
603
				}
604
			}
605
606
			return new BezierCurve[] { new BezierCurve(leftPoints),
607
					new BezierCurve(rightPoints) };
608
		}
609
167
610
		/**
168
			return intersections.toArray(new Point[] {});
611
		 * Returns a new {@link BezierCurve} object representing this bezier
612
		 * curve on the interval [s;e].
613
		 * 
614
		 * @param s
615
		 * @param e
616
		 * @return a new {@link BezierCurve} object representing this bezier
617
		 *         curve on the interval [s;e]
618
		 */
619
		public BezierCurve getClipped(double s, double e) {
620
			BezierCurve right = split(s)[1];
621
			double rightT2 = (e - s) / (1 - s);
622
			return right.split(rightT2)[0];
623
		}
169
		}
170
	}
624
171
625
		/**
172
	/**
626
		 * Checks if the parameters are considered equal on both curves and adds
173
	 * Tests the given {@link ICurve}s for a finite number of intersections.
627
		 * the point of intersection of the mid lines of the curves.
174
	 * Returns <code>true</code> if the given {@link ICurve}s have a finite
628
		 * 
175
	 * number of intersection points. Otherwise, returns <code>false</code>.
629
		 * @param p
176
	 * 
630
		 * @param q
177
	 * @param c1
631
		 * @param intersections
178
	 * @param c2
632
		 * @return true if the parameters are considered equal and false
179
	 * @return <code>true</code> if both {@link ICurve}s have a finite set of
633
		 *         otherwise
180
	 *         intersection points, otherwise <code>false</code>
634
		 */
181
	 */
635
		private static Vector3D parameterConvergence(BezierCurve p, double ps,
182
	public static boolean intersects(ICurve c1, ICurve c2) {
636
				double pe, BezierCurve q, double qs, double qe) {
183
		return getIntersections(c1, c2).length > 0;
637
			// localEndPointsCheck();
184
	}
638
			if (PrecisionUtils.equal(ps, pe, +2)) {
639
				Vector3D poi = p.get((ps + pe) / 2);
640
				return poi;
641
			}
642
			if (PrecisionUtils.equal(qs, qe, +2)) {
643
				Vector3D poi = q.get((qs + qe) / 2);
644
				return poi;
645
			}
646
			return null;
647
		}
648
649
		/**
650
		 * Returns the bounds of the control polygon of this {@link BezierCurve}
651
		 * .
652
		 * 
653
		 * @return a {@link Rectangle} representing the bounds of the control
654
		 *         polygon of this {@link BezierCurve}
655
		 */
656
		public Rectangle getControlBounds() {
657
			Point[] realPoints = getRealPoints();
658
659
			double xmin = realPoints[0].x, xmax = realPoints[0].x, ymin = realPoints[0].y, ymax = realPoints[0].y;
660
661
			for (int i = 1; i < realPoints.length; i++) {
662
				if (realPoints[i].x < xmin) {
663
					xmin = realPoints[i].x;
664
				} else if (realPoints[i].x > xmax) {
665
					xmax = realPoints[i].x;
666
				}
667
185
668
				if (realPoints[i].y < ymin) {
186
	/**
669
					ymin = realPoints[i].y;
187
	 * Tests the given {@link ICurve}s for an infinite number of intersections.
670
				} else if (realPoints[i].y > ymax) {
188
	 * Returns <code>true</code> if the given {@link ICurve}s have an infinite
671
					ymax = realPoints[i].y;
189
	 * number of intersection points. Otherwise, returns <code>false</code>.
190
	 * 
191
	 * @param c1
192
	 * @param c2
193
	 * @return <code>true</code> if both {@link ICurve}s have an infinite set of
194
	 *         intersection points, otherwise <code>false</code>
195
	 */
196
	public static boolean overlaps(ICurve c1, ICurve c2) {
197
		for (BezierCurve seg1 : c1.toBezier()) {
198
			for (BezierCurve seg2 : c2.toBezier()) {
199
				if (seg1.overlaps(seg2)) {
200
					return true;
672
				}
201
				}
673
			}
202
			}
674
675
			return new Rectangle(xmin, ymin, xmax - xmin, ymax - ymin);
676
		}
677
678
		/**
679
		 * Generates the difference points of this {@link BezierCurve} to the
680
		 * given line.
681
		 * 
682
		 * The difference points are the control points of a Bezier curve that
683
		 * yields the signed difference of the point on this curve at a
684
		 * determinate parameter value to the given line.
685
		 * 
686
		 * @param line
687
		 * @return the difference curve's control points
688
		 */
689
		private Vector3D[] genDifferencePoints(Vector3D line) {
690
			Vector3D[] D = new Vector3D[points.length];
691
			for (int i = 0; i < points.length; i++) {
692
				double y = line.getDot(points[i]);
693
				D[i] = new Vector3D(
694
						(double) (i) / (double) (points.length - 1), y, 1);
695
			}
696
			return D;
697
		}
698
699
		public Set<Vector3D> getIntersections(BezierCurve other) {
700
			// end point intersections
701
			Set<Vector3D> endPointIntersections = getEndPointIntersections(
702
					this, other);
703
704
			// TODO: tangential intersections
705
706
			// simple intersections
707
			// TODO: recursion => iteration
708
			Set<Vector3D> intersections = getIntersections(
709
					endPointIntersections, this, 0, 1, other, 0, 1);
710
711
			intersections.addAll(endPointIntersections);
712
			return intersections;
713
		}
203
		}
204
		return false;
205
	}
714
206
715
		private Set<Vector3D> getEndPointIntersections(BezierCurve p,
207
	/**
716
				BezierCurve q) {
208
	 * <p>
717
			Set<Vector3D> intersections = new HashSet<Vector3D>();
209
	 * Tests if the given {@link BezierCurve} is fully contained by the given
718
			for (int i : new int[] { 0, p.points.length - 1 }) {
210
	 * {@link IShape}. Returns <code>true</code> if the given
719
				if (q.contains(p.points[i].toPoint())) {
211
	 * {@link BezierCurve} is fully contained by the given {@link IShape}.
720
					intersections.add(p.points[i]);
212
	 * Otherwise, returns <code>false</code>.
721
				}
213
	 * </p>
722
			}
214
	 * 
723
			for (int i : new int[] { 0, q.points.length - 1 }) {
215
	 * <p>
724
				if (p.contains(q.points[i].toPoint())) {
216
	 * At first, the algorithm checks if start and end {@link Point} of the
725
					intersections.add(q.points[i]);
217
	 * {@link BezierCurve} are contained by the {@link IShape}. If this is not
726
				}
218
	 * the case, <code>false</code> is returned.
727
			}
219
	 * </p>
728
			return intersections;
220
	 * 
221
	 * <p>
222
	 * Subsequently, the {@link Point}s of intersection of the
223
	 * {@link BezierCurve} and the {@link IShape} are computed. If there are
224
	 * less then two intersection {@link Point}s, <code>true</code> is returned.
225
	 * </p>
226
	 * 
227
	 * <p>
228
	 * Alternatively, the {@link BezierCurve}'s parameter values for the
229
	 * individual {@link Point}s of intersection are sorted. For every two
230
	 * consecutive parameter values, a {@link Point} on the {@link BezierCurve}
231
	 * between those two parameter values is computed. If any of those
232
	 * {@link Point}s is not contained by the {@link IShape}, <code>false</code>
233
	 * is returned. Otherwise, <code>true</code> is returned.
234
	 * </p>
235
	 * 
236
	 * <p>
237
	 * Self-intersection-problem: If the {@link BezierCurve} has a
238
	 * self-intersection p and p lies on an outline segment of the
239
	 * {@link IShape} ( {@link IShape#getOutlineSegments()}), <code>true</code>
240
	 * is returned, although <code>false</code> would be the right answer.
241
	 * </p>
242
	 * 
243
	 * @param shape
244
	 *            the {@link IShape} that is tested to contain the given
245
	 *            {@link BezierCurve}
246
	 * @param c
247
	 *            the {@link BezierCurve} that is tested to be contained by the
248
	 *            given {@link IShape}
249
	 * @return <code>true</code> if the given {@link BezierCurve} is fully
250
	 *         contained by the given {@link IShape}
251
	 */
252
	public static boolean contains(IShape shape, BezierCurve c) {
253
		if (!(shape.contains(c.getP1()) && shape.contains(c.getP2()))) {
254
			return false;
729
		}
255
		}
730
256
731
		private static class FatLine {
257
		Set<Double> intersectionParams = new HashSet<Double>();
732
			public Vector3D line;
733
			public double dmin, dmax;
734
735
			private FatLine() {
736
				line = new Vector3D(0, 0, 0);
737
				dmin = dmax = 0;
738
			}
739
740
			public static FatLine from(BezierCurve c, boolean ortho) {
741
				FatLine L = new FatLine();
742
				L.dmin = L.dmax = 0;
743
744
				L.line = c.points[0].getCrossed(c.points[c.points.length - 1]);
745
258
746
				if (ortho) {
259
		for (ICurve segC : shape.getOutlineSegments()) {
747
					L.line = c.points[0].getCrossed(c.points[0]
260
			for (BezierCurve seg : segC.toBezier()) {
748
							.getAdded(new Vector3D(L.line.x, L.line.y, 0)));
261
				Set<Point> inters = new HashSet<Point>();
262
				Set<IntervalPair> ips = c.getIntersectionIntervalPairs(
263
						new BezierCurve(seg.getP1(), seg.getP2()), inters);
264
				for (IntervalPair ip : ips) {
265
					intersectionParams.add(ip.p == c ? ip.pi.getMid() : ip.qi
266
							.getMid());
749
				}
267
				}
750
268
				for (Point poi : inters) {
751
				L.line = L.line.getLineNormalized();
269
					intersectionParams.add(c.getParameterAt(poi));
752
753
				if (L.line == null) {
754
					return null;
755
				}
270
				}
756
757
				for (int i = 0; i < c.points.length; i++) {
758
					double d = L.line.getDot(c.points[i]);
759
					if (d < L.dmin)
760
						L.dmin = d;
761
					else if (d > L.dmax)
762
						L.dmax = d;
763
				}
764
765
				return L;
766
			}
767
		}
768
769
		private static double intersectXAxisParallel(Point p, Point q, double y) {
770
			// p.y != q.y because this routine is only called when either the
771
			// lower or the higher fat line bound is crossed.
772
			return new Vector3D(p).getCrossed(new Vector3D(q))
773
					.getCrossed(new Vector3D(0, 1, -y)).toPoint().x;
774
			// double dy = q.y - p.y;
775
			// double s = (y - p.y) / dy;
776
			// return (q.x - p.x) * s + p.x;
777
		}
778
779
		private static Point[] getConvexHull(Vector3D[] points) {
780
			Point[] chPoints = new Point[points.length];
781
			for (int i = 0; i < points.length; i++) {
782
				chPoints[i] = points[i].toPoint();
783
			}
784
			return PointListUtils.getConvexHull(chPoints);
785
		}
786
787
		/**
788
		 * @param L
789
		 * @return
790
		 */
791
		private double[] clipTo(FatLine L) {
792
			double[] interval = new double[] { 1, 0 };
793
794
			Point[] D = getConvexHull(genDifferencePoints(L.line));
795
796
			// we do not know which point is returned first by the
797
			// getConvexHull() method. That's why we have to check the "first"
798
			// point, too.
799
			boolean isBelow = D[0].y < L.dmin;
800
			boolean isAbove = D[0].y > L.dmax;
801
			insideFatLineCheck(interval, D, 0, isBelow, isAbove);
802
803
			boolean wasBelow = isBelow, wasAbove = isAbove;
804
805
			for (int i = 1; i < D.length; i++) {
806
				isBelow = D[i].y < L.dmin;
807
				isAbove = D[i].y > L.dmax;
808
809
				insideFatLineCheck(interval, D, i, isBelow, isAbove);
810
				wasBelow = belowFatLineCheck(interval, L, D, i - 1, i, isBelow,
811
						wasBelow);
812
				wasAbove = aboveFatLineCheck(interval, L, D, i - 1, i, isAbove,
813
						wasAbove);
814
			}
815
816
			// closing segment
817
			isBelow = D[0].y < L.dmin;
818
			isAbove = D[0].y > L.dmax;
819
			belowFatLineCheck(interval, L, D, D.length - 1, 0, isBelow,
820
					wasBelow);
821
			aboveFatLineCheck(interval, L, D, D.length - 1, 0, isAbove,
822
					wasAbove);
823
824
			return interval;
825
		}
826
827
		private boolean aboveFatLineCheck(double[] interval, FatLine L,
828
				Point[] D, int i, int j, boolean isAbove, boolean wasAbove) {
829
			if (isAbove != wasAbove) {
830
				// crosses higher
831
				double x = intersectXAxisParallel(D[i], D[j], L.dmax);
832
				moveInterval(interval, x);
833
				wasAbove = isAbove;
834
			}
835
			return wasAbove;
836
		}
837
838
		private boolean belowFatLineCheck(double[] interval, FatLine L,
839
				Point[] D, int i, int j, boolean isBelow, boolean wasBelow) {
840
			if (isBelow != wasBelow) {
841
				// crosses lower
842
				double x = intersectXAxisParallel(D[i], D[j], L.dmin);
843
				moveInterval(interval, x);
844
				wasBelow = isBelow;
845
			}
271
			}
846
			return wasBelow;
847
		}
272
		}
848
273
849
		private void insideFatLineCheck(double[] interval, Point[] D, int i,
274
		/*
850
				boolean isBelow, boolean isAbove) {
275
		 * Start and end point of the curve are guaranteed to lie inside the
851
			if (!(isBelow || isAbove)) {
276
		 * IShape. If the curve would not be contained by the shape, at least
852
				// inside
277
		 * two intersections could be found.
853
				moveInterval(interval, D[i].x);
854
			}
855
		}
856
857
		private void moveInterval(double[] interval, double x) {
858
			if (interval[0] > x)
859
				interval[0] = x;
860
			if (interval[1] < x)
861
				interval[1] = x;
862
		}
863
864
		/**
865
		 * Computes and returns the points of intersection between this
866
		 * {@link BezierCurve} and the given other {@link BezierCurve}.
867
		 * 
278
		 * 
868
		 * @param endPointIntersections
279
		 * TODO: Special case! There is a special case where the Bezier curve
869
		 *            all points of intersections that are end-points of one of
280
		 * leaves and enters the shape in the same point. This is only possible
870
		 *            the curves
281
		 * if the Bezier curve has a self intersections at that point.
871
		 * @param p
872
		 *            first {@link BezierCurve}
873
		 * @param ps
874
		 *            start value of the first {@link BezierCurve}'s parameter
875
		 *            interval
876
		 * @param pe
877
		 *            end value of the first {@link BezierCurve}'s parameter
878
		 *            interval
879
		 * @param q
880
		 *            second {@link BezierCurve}
881
		 * @param qs
882
		 *            start value of the second {@link BezierCurve}'s parameter
883
		 *            interval
884
		 * @param qe
885
		 *            end value of the second {@link BezierCurve}'s parameter
886
		 *            interval
887
		 * @return the intersections between this {@link BezierCurve} and the
888
		 *         given other {@link BezierCurve}
889
		 */
282
		 */
890
		public static Set<Vector3D> getIntersections(
283
		if (intersectionParams.size() <= 1) {
891
				Set<Vector3D> endPointIntersections, BezierCurve p, double ps,
284
			return true;
892
				double pe, BezierCurve q, double qs, double qe) {
285
		}
893
			BezierCurve pClipped = p.getClipped(ps, pe);
894
			BezierCurve qClipped = q.getClipped(qs, qe);
895
896
			// end point intersection check
897
			if (endPointIntersectionConvergence(endPointIntersections,
898
					pClipped, ps, pe, qClipped, qs, qe)) {
899
				Set<Vector3D> no_intersections = new HashSet<Vector3D>(0);
900
				return no_intersections;
901
			}
902
903
			// TODO: tangential intersection check
904
905
			// parameter convergence check
906
			Vector3D poi = parameterConvergence(p, ps, pe, q, qs, qe);
907
			if (poi != null) {
908
				// "exactly" one intersection
909
				if (p.contains(poi.toPoint()) && q.contains(poi.toPoint())) {
910
					Set<Vector3D> intersection = new HashSet<Vector3D>(1);
911
					intersection.add(poi);
912
					return intersection;
913
				}
914
				Set<Vector3D> no_intersections = new HashSet<Vector3D>(0);
915
				return no_intersections;
916
			}
917
918
			Set<Vector3D> intersections = new HashSet<Vector3D>();
919
920
			// construct "parallel" and "orthogonal" fat lines
921
			FatLine L1 = FatLine.from(qClipped, false);
922
			FatLine L2 = FatLine.from(qClipped, true);
923
924
			// curve implosion check
925
			if (L1 == null || L2 == null) {
926
				// qClipped is too small to construct a fat line from it
927
				// therefore, return its mid-point if it is contained by the
928
				// other curve
929
				Vector3D mid = q.get((qs + qe) / 2);
930
				if (p.contains(mid.toPoint())) {
931
					Set<Vector3D> intersection = new HashSet<Vector3D>(1);
932
					intersection.add(mid);
933
					return intersection;
934
				}
935
				Set<Vector3D> no_intersections = new HashSet<Vector3D>(0);
936
				return no_intersections;
937
			}
938
939
			// clip to the fat lines
940
			double[] interval = pClipped.clipTo(L1);
941
			double[] interval_ortho = pClipped.clipTo(L2);
942
286
943
			// pick smaller interval range
287
		Double[] poiParams = intersectionParams.toArray(new Double[] {});
944
			if ((interval[1] - interval[0]) > (interval_ortho[1] - interval_ortho[0])) {
288
		Arrays.sort(poiParams, new Comparator<Double>() {
945
				interval[0] = interval_ortho[0];
289
			public int compare(Double t, Double u) {
946
				interval[1] = interval_ortho[1];
290
				double d = t - u;
291
				return d < 0 ? -1 : d > 0 ? 1 : 0;
947
			}
292
			}
293
		});
948
294
949
			// re-calculate s and e from the clipped interval
295
		// check the points between the intersections for containment
950
			double news = ps + interval[0] * (pe - ps);
296
		if (!shape.contains(c.get(poiParams[0] / 2))) {
951
			double newe = ps + interval[1] * (pe - ps);
297
			return false;
952
			double ratio = (newe - news) / (pe - ps);
953
			ps = news;
954
			pe = newe;
955
956
			if (ratio < 0) {
957
				// no more intersections
958
				return intersections;
959
			} else if (ratio > 0.8) {
960
				// split longer curve and find intersections for both halves
961
				if ((pe - ps) > (qe - qs)) {
962
					double pm = (ps + pe) / 2;
963
					intersections.addAll(getIntersections(
964
							endPointIntersections, p, ps, pm, q, qs, qe));
965
					intersections.addAll(getIntersections(
966
							endPointIntersections, p, pm
967
									+ UNRECOGNIZABLE_PRECISION_FRACTION, pe, q,
968
							qs, qe));
969
				} else {
970
					double qm = (qs + qe) / 2;
971
					intersections.addAll(getIntersections(
972
							endPointIntersections, q, qs, qm, p, ps, pe));
973
					intersections.addAll(getIntersections(
974
							endPointIntersections, q, qm
975
									+ UNRECOGNIZABLE_PRECISION_FRACTION, qe, p,
976
							ps, pe));
977
				}
978
979
				return intersections;
980
			} else {
981
				// clipped more than 20%
982
				return getIntersections(endPointIntersections, q, qs, qe, p,
983
						ps, pe);
984
			}
985
		}
298
		}
986
299
		for (int i = 0; i < poiParams.length - 1; i++) {
987
		private static boolean endPointIntersectionConvergence(
300
			if (!shape.contains(c.get((poiParams[i] + poiParams[i + 1]) / 2))) {
988
				Set<Vector3D> endPointIntersections, BezierCurve pClipped,
301
				return false;
989
				double ps, double pe, BezierCurve qClipped, double qs, double qe) {
990
			if (PrecisionUtils.equal(ps, pe, -3)) {
991
				if (endPointIntersections.contains(pClipped.points[0])
992
						|| endPointIntersections
993
								.contains(pClipped.points[pClipped.points.length - 1])) {
994
					return true;
995
				}
996
			}
997
			if (PrecisionUtils.equal(qs, qe, -3)) {
998
				if (endPointIntersections.contains(qClipped.points[0])
999
						|| endPointIntersections
1000
								.contains(qClipped.points[qClipped.points.length - 1])) {
1001
					return true;
1002
				}
1003
			}
302
			}
1004
			return false;
1005
		}
303
		}
304
		return shape.contains(c.get((poiParams[poiParams.length - 1] + 1) / 2));
1006
	}
305
	}
1007
306
1008
	/**
307
	/**
1009
	 * Computes and returns the points of intersection between two
308
	 * Returns <code>true</code> if the given {@link IShape} fully contains the
1010
	 * {@link CubicCurve}s.
309
	 * given {@link ICurve}. Otherwise, <code>false</code> is returned. A
310
	 * {@link ICurve} is contained by a {@link IShape} if the {@link ICurve}'s
311
	 * Bezier approximation ({@link ICurve#toBezier()}) is contained by the
312
	 * {@link IShape} ({@link CurveUtils#contains(IShape, BezierCurve)}).
1011
	 * 
313
	 * 
1012
	 * @param p
314
	 * @param shape
1013
	 *            the first {@link CubicCurve} to intersect
315
	 *            the {@link IShape} that is tested to contain the
1014
	 * @param q
316
	 *            {@link ICurve}
1015
	 *            the second {@link CubicCurve} to intersect
317
	 * @param curve
1016
	 * @return the intersections between two {@link CubicCurve}s
318
	 *            the {@link ICurve} that is tested to be contained by the
319
	 *            {@link IShape}
320
	 * @return <code>true</code> if the given {@link IShape} contains the
321
	 *         {@link ICurve}, otherwise <code>false</code>
1017
	 */
322
	 */
1018
	public static Point[] getIntersections(CubicCurve p, CubicCurve q) {
323
	public static boolean contains(IShape shape, ICurve curve) {
1019
		Set<CurveUtils.Vector3D> intersections = new BezierCurve(p)
324
		for (BezierCurve seg : curve.toBezier()) {
1020
				.getIntersections(new BezierCurve(q));
325
			if (!contains(shape, seg)) {
1021
326
				return false;
1022
		Set<Point> pois = new HashSet<Point>();
1023
		for (CurveUtils.Vector3D poi : intersections) {
1024
			if (poi.z != 0) {
1025
				pois.add(poi.toPoint());
1026
			}
327
			}
1027
		}
328
		}
1028
329
		return true;
1029
		return pois.toArray(new Point[] {});
1030
	}
330
	}
1031
331
1032
	/**
332
	/**
1033
	 * Computes the {@link Point} of intersection of two {@link Straight}s.
333
	 * Returns <code>true</code> if the second {@link IShape} is fully contained
334
	 * by the first {@link IShape}. Otherwise, <code>false</code> is returned.
1034
	 * 
335
	 * 
1035
	 * @param s1
336
	 * A {@link IShape} is contained by another {@link IShape} if all of its
1036
	 *            the first {@link Straight} to test for intersection
337
	 * outline segments ({@link IShape#getOutlineSegments()}) are contained by
1037
	 * @param s2
338
	 * the other {@link IShape} ({@link CurveUtils#contains(IShape, ICurve)}).
1038
	 *            the second {@link Straight} to test for intersection
1039
	 * @return the {@link Point} of intersection if it exists, <code>null</code>
1040
	 *         otherwise
1041
	 */
1042
	public static Point getIntersection(Straight s1, Straight s2) {
1043
		Vector3D l1 = new Vector3D(s1.position.toPoint())
1044
				.getCrossed(new Vector3D(s1.position.getAdded(s1.direction)
1045
						.toPoint()));
1046
		Vector3D l2 = new Vector3D(s2.position.toPoint())
1047
				.getCrossed(new Vector3D(s2.position.getAdded(s2.direction)
1048
						.toPoint()));
1049
1050
		return l1.getCrossed(l2).toPoint();
1051
	}
1052
1053
	/**
1054
	 * Computes the signed distance of the third {@link Point} to the line
1055
	 * through the first two {@link Point}s.
1056
	 * 
1057
	 * The signed distance is positive if the three {@link Point}s are in
1058
	 * counter-clockwise order and negative if the {@link Point}s are in
1059
	 * clockwise order. It is zero if the third {@link Point} lies on the line.
1060
	 * 
1061
	 * If the first two {@link Point}s do not form a line (i.e. they are equal)
1062
	 * this function returns the distance of the first and the last
1063
	 * {@link Point}.
1064
	 * 
339
	 * 
1065
	 * @param p
340
	 * @param shape1
1066
	 *            the start-{@link Point} of the line
341
	 *            the {@link IShape} that is tested to contain the other
1067
	 * @param q
342
	 *            {@link IShape}
1068
	 *            the end-{@link Point} of the line
343
	 * @param shape2
1069
	 * @param r
344
	 *            the {@link IShape} that is tested to be contained by the other
1070
	 *            the relative {@link Point} to test for
345
	 *            {@link IShape}
1071
	 * @return the signed distance of {@link Point} r to the line through
346
	 * @return <code>true</code> if the second {@link IShape} is contained by
1072
	 *         {@link Point}s p and q
347
	 *         the first {@link IShape}, otherwise <code>false</code>
1073
	 */
348
	 */
1074
	public static double getSignedDistance(Point p, Point q, Point r) {
349
	public static boolean contains(IShape shape1, IShape shape2) {
1075
		Vector3D normalizedLine = new Vector3D(p).getCrossed(new Vector3D(q))
350
		for (ICurve seg : shape2.getOutlineSegments()) {
1076
				.getLineNormalized();
351
			if (!contains(shape1, seg)) {
1077
352
				return false;
1078
		if (normalizedLine == null) {
353
			}
1079
			return p.getDistance(r);
1080
		}
354
		}
1081
355
		return true;
1082
		double dot = normalizedLine.getDot(new Vector3D(r));
1083
		return -dot;
1084
	}
356
	}
1085
357
1086
	/**
358
	/**
1087
	 * Returns the signed distance of the {@link Point} to the {@link Straight}.
359
	 * Returns <code>true</code> if the given {@link IPolyCurve} is fully
1088
	 * 
360
	 * contained by the given {@link IShape}. Otherwise, <code>false</code> is
1089
	 * {@link Point}s that are to the left of the {@link Straight} in the
361
	 * returned.
1090
	 * direction of the {@link Straight}'s direction vector have a positive
1091
	 * distance whereas {@link Point}s to the right of the {@link Straight} in
1092
	 * the direction of the {@link Straight}'s direction vector have a negative
1093
	 * distance.
1094
	 * 
362
	 * 
1095
	 * The absolute value of the signed distance is the actual distance of the
363
	 * A {@link IPolyCurve} is contained by a {@link IShape} if all of its sub-
1096
	 * {@link Point} to the {@link Straight}.
364
	 * {@link ICurve}s are contained by the shape (see
365
	 * {@link IPolyCurve#getCurves()} and
366
	 * {@link CurveUtils#contains(IShape, ICurve)}).
1097
	 * 
367
	 * 
1098
	 * @param s
368
	 * @param shape
1099
	 * @param p
369
	 *            the {@link IShape} that is tested to contain the
1100
	 * @return the signed distance of the {@link Point} to the {@link Straight}
370
	 *            {@link IPolyCurve}
1101
	 * 
371
	 * @param polyCurve
1102
	 * @see CurveUtils#getSignedDistance(Point, Point, Point)
372
	 *            the {@link IPolyCurve} that is tested to be contained by the
373
	 *            {@link IShape}
374
	 * @return <code>true</code> if the {@link IShape} contains the
375
	 *         {@link IPolyCurve}, otherwise <code>false</code>
1103
	 */
376
	 */
1104
	public static double getSignedDistance(Straight s, Point p) {
377
	public static boolean contains(IShape shape, IPolyCurve polyCurve) {
1105
		return getSignedDistance(s.position.toPoint(),
378
		for (ICurve seg : polyCurve.getCurves()) {
1106
				s.position.getAdded(s.direction).toPoint(), p);
379
			if (!contains(shape, seg)) {
380
				return false;
381
			}
382
		}
383
		return true;
1107
	}
384
	}
1108
385
1109
	/**
386
	/**
1110
	 * Tests if the given {@link Point} lies on the given {@link CubicCurve}.
387
	 * Returns <code>true</code> if the given {@link IShape} fully contains the
388
	 * given {@link IPolyShape}. Otherwise, <code>false</code> is returned.
1111
	 * 
389
	 * 
1112
	 * @param c
390
	 * A {@link IPolyShape} is contained by a {@link IShape} if all of its sub-
1113
	 *            the {@link CubicCurve} to test
391
	 * {@link IShape}s are contained by the {@link IShape} (see
1114
	 * @param p
392
	 * {@link IPolyShape#getShapes()} and
1115
	 *            the {@link Point} to test for containment
393
	 * {@link CurveUtils#contains(IShape, IShape)}).
1116
	 * @return true if the {@link Point} lies on the {@link CubicCurve}, false
1117
	 *         otherwise
1118
	 */
1119
	public static boolean contains(CubicCurve c, Point p) {
1120
		return new BezierCurve(c).contains(p);
1121
	}
1122
1123
	/**
1124
	 * Tests if the given {@link Point} lies on the given {@link QuadraticCurve}
1125
	 * .
1126
	 * 
394
	 * 
1127
	 * @param c
395
	 * @param shape
1128
	 *            the {@link QuadraticCurve} to test
396
	 *            the {@link IShape} that is tested to contain the
1129
	 * @param p
397
	 *            {@link IPolyShape}
1130
	 *            the {@link Point} to test for containment
398
	 * @param polyShape
1131
	 * @return true if the {@link Point} lies on the {@link QuadraticCurve},
399
	 *            the {@link IPolyShape} that is tested to be contained by the
1132
	 *         false otherwise
400
	 *            {@link IShape}
401
	 * @return <code>true</code> if the {@link IShape} contains the
402
	 *         {@link IPolyShape}, otherwise <code>false</code>
1133
	 */
403
	 */
1134
	public static boolean contains(QuadraticCurve c, Point p) {
404
	public static boolean contains(IShape shape, IPolyShape polyShape) {
1135
		return new BezierCurve(c).contains(p);
405
		for (IShape seg : polyShape.getShapes()) {
406
			if (!contains(shape, seg)) {
407
				return false;
408
			}
409
		}
410
		return true;
1136
	}
411
	}
1137
412
1138
	/**
413
	/**
1139
	 * Subdivides the given {@link CubicCurve} at parameter value t in the left
414
	 * Returns <code>true</code> if the given {@link IShape} fully contains the
1140
	 * and right sub-curves.
415
	 * given {@link IGeometry}. Otherwise, <code>false</code> is returned.
1141
	 * 
416
	 * 
1142
	 * @param c
417
	 * An <code>instanceof</code> test delegates to the appropriate method.
1143
	 *            the {@link CubicCurve} to subdivide
1144
	 * @param t
1145
	 *            the parameter value to subdivide at
1146
	 * @return the left and right sub-curves as an array of {@link CubicCurve}
1147
	 */
1148
	public static CubicCurve[] split(CubicCurve c, double t) {
1149
		BezierCurve[] split = new BezierCurve(c).split(t);
1150
		return new CubicCurve[] { new CubicCurve(split[0].getRealPoints()),
1151
				new CubicCurve(split[1].getRealPoints()) };
1152
	}
1153
1154
	/**
1155
	 * Subdivides the given {@link QuadraticCurve} at parameter value t in the
1156
	 * left and right sub-curves.
1157
	 * 
418
	 * 
1158
	 * @param c
419
	 * @see CurveUtils#contains(IShape, ICurve)
1159
	 *            the {@link QuadraticCurve} to subdivide
420
	 * @see CurveUtils#contains(IShape, IPolyCurve)
1160
	 * @param t
421
	 * @see CurveUtils#contains(IShape, IShape)
1161
	 *            the parameter value to subdivide at
422
	 * @see CurveUtils#contains(IShape, IPolyShape)
1162
	 * @return the left and right sub-curves as an array of
423
	 * @param shape
1163
	 *         {@link QuadraticCurve}
424
	 *            the {@link IShape} that is tested to contain the
425
	 *            {@link IGeometry}
426
	 * @param geom
427
	 *            the {@link IGeometry} that is tested to be contained by the
428
	 *            {@link IShape}
429
	 * @return <code>true</code> if the {@link IShape} contains the
430
	 *         {@link IGeometry}, otherwise <code>false</code>
1164
	 */
431
	 */
1165
	public static QuadraticCurve[] split(QuadraticCurve c, double t) {
432
	public static boolean contains(IShape shape, IGeometry geom) {
1166
		BezierCurve[] split = new BezierCurve(c).split(t);
433
		if (geom instanceof ICurve) {
1167
		return new QuadraticCurve[] {
434
			return contains(shape, (ICurve) geom);
1168
				new QuadraticCurve(split[0].getRealPoints()),
435
		} else if (geom instanceof IPolyCurve) {
1169
				new QuadraticCurve(split[1].getRealPoints()) };
436
			return contains(shape, (IPolyCurve) geom);
437
		} else if (geom instanceof IShape) {
438
			return contains(shape, (IShape) geom);
439
		} else if (geom instanceof IPolyShape) {
440
			return contains(shape, (IPolyShape) geom);
441
		} else {
442
			throw new UnsupportedOperationException("Not yet implemented.");
443
		}
1170
	}
444
	}
1171
445
1172
	/**
446
	/**
1173
	 * Returns a new {@link QuadraticCurve} that represents the given
447
	 * Returns <code>true</code> if the second {@link IGeometry} is fully
1174
	 * {@link QuadraticCurve} on the parameter interval [t1;t2].
448
	 * contained by the first {@link IGeometry}. Otherwise, <code>false</code>
449
	 * is returned.
1175
	 * 
450
	 * 
1176
	 * @param c
451
	 * @param geom1
1177
	 *            the {@link QuadraticCurve} to clip
452
	 * @param geom2
1178
	 * @param t1
453
	 * @return <code>true</code> if the first {@link IGeometry} contains the
1179
	 *            lower parameter bound
454
	 *         second {@link IGeometry}, otherwise <code>false</code>
1180
	 * @param t2
1181
	 *            upper parameter bound
1182
	 * @return a new {@link QuadraticCurve} that represents the given
1183
	 *         {@link QuadraticCurve} on the interval [t1;t2]
1184
	 */
455
	 */
1185
	public static QuadraticCurve clip(QuadraticCurve c, double t1, double t2) {
456
	public static boolean contains(IGeometry geom1, IGeometry geom2) {
1186
		BezierCurve bc = new BezierCurve(c);
457
		if (geom1 instanceof IShape) {
1187
		return new QuadraticCurve(bc.getClipped(t1, t2).getRealPoints());
458
			return contains((IShape) geom1, geom2);
459
		} else if (geom1 instanceof IPolyShape) {
460
			throw new UnsupportedOperationException("Not yet implemented.");
461
		} else {
462
			return false;
463
		}
1188
	}
464
	}
1189
465
1190
	/**
466
	/**
1191
	 * Returns a new {@link CubicCurve} that represents the given
467
	 * Returns a {@link PolyBezier} that is constructed from the outline
1192
	 * {@link CubicCurve} on the parameter interval [t1;t2].
468
	 * segments ( {@link IShape#getOutlineSegments()}) of the given
469
	 * {@link IShape}.
1193
	 * 
470
	 * 
1194
	 * @param c
471
	 * @param shape
1195
	 *            the {@link CubicCurve} to clip
472
	 *            the {@link IShape} to compute the outline for
1196
	 * @param t1
473
	 * @return a {@link PolyBezier} that is constructed from the outline
1197
	 *            lower parameter bound
474
	 *         segments of the {@link IShape}
1198
	 * @param t2
1199
	 *            upper parameter bound
1200
	 * @return a new {@link CubicCurve} that represents the given
1201
	 *         {@link CubicCurve} on the interval [t1;t2]
1202
	 */
475
	 */
1203
	public static CubicCurve clip(CubicCurve c, double t1, double t2) {
476
	public static PolyBezier getOutline(IShape shape) {
1204
		BezierCurve bc = new BezierCurve(c);
477
		ICurve[] curves = shape.getOutlineSegments();
1205
		return new CubicCurve(bc.getClipped(t1, t2).getRealPoints());
1206
	}
1207
478
1208
	/**
479
		ArrayList<BezierCurve> beziers = new ArrayList<BezierCurve>(
1209
	 * Evaluates the {@link Point} on the given {@link CubicCurve} at parameter
480
				curves.length);
1210
	 * value t.
1211
	 * 
1212
	 * @param c
1213
	 *            the {@link CubicCurve}
1214
	 * @param t
1215
	 *            the parameter value
1216
	 * @return the {@link Point} on the given {@link CubicCurve} at parameter
1217
	 *         value t
1218
	 */
1219
	public static Point get(CubicCurve c, double t) {
1220
		return new BezierCurve(c).get(t).toPoint();
1221
	}
1222
481
1223
	/**
482
		for (ICurve c : curves) {
1224
	 * Evaluates the {@link Point} on the given {@link QuadraticCurve} at
483
			for (BezierCurve b : c.toBezier()) {
1225
	 * parameter value t.
484
				beziers.add(b);
1226
	 * 
485
			}
1227
	 * @param c
486
		}
1228
	 *            the {@link QuadraticCurve}
1229
	 * @param t
1230
	 *            the parameter value
1231
	 * @return the {@link Point} on the given {@link QuadraticCurve} at
1232
	 *         parameter value t
1233
	 */
1234
	public static Point get(QuadraticCurve c, double t) {
1235
		return new BezierCurve(c).get(t).toPoint();
1236
	}
1237
487
1238
	/**
488
		return new PolyBezier(beziers.toArray(new BezierCurve[] {}));
1239
	 * Computes and returns the bounds of the control polygon of the given
1240
	 * {@link CubicCurve}. The control polygon is the convex hull of the start-,
1241
	 * end-, and control points of the {@link CubicCurve}.
1242
	 * 
1243
	 * @param c
1244
	 *            the {@link CubicCurve} to compute the control bounds for
1245
	 * @return the bounds of the control polygon of the given {@link CubicCurve}
1246
	 */
1247
	public static Rectangle getControlBounds(CubicCurve c) {
1248
		return new BezierCurve(c).getControlBounds();
1249
	}
489
	}
1250
}
490
}
(-)a/org.eclipse.gef4.geometry/src/org/eclipse/gef4/geometry/utils/PointListUtils.java (-5 / +37 lines)
Lines 7-12 Link Here
7
 *
7
 *
8
 * Contributors:
8
 * Contributors:
9
 *     Alexander Nyßen (itemis AG) - initial API and implementation
9
 *     Alexander Nyßen (itemis AG) - initial API and implementation
10
 *     Matthias Wienand (itemis AG) - contribution for Bugzilla #355997
10
 *     
11
 *     
11
 *******************************************************************************/
12
 *******************************************************************************/
12
package org.eclipse.gef4.geometry.utils;
13
package org.eclipse.gef4.geometry.utils;
Lines 16-21 import java.util.Arrays; Link Here
16
import java.util.Comparator;
17
import java.util.Comparator;
17
18
18
import org.eclipse.gef4.geometry.Point;
19
import org.eclipse.gef4.geometry.Point;
20
import org.eclipse.gef4.geometry.euclidean.Straight;
19
import org.eclipse.gef4.geometry.planar.Line;
21
import org.eclipse.gef4.geometry.planar.Line;
20
import org.eclipse.gef4.geometry.planar.Polygon;
22
import org.eclipse.gef4.geometry.planar.Polygon;
21
import org.eclipse.gef4.geometry.planar.Polyline;
23
import org.eclipse.gef4.geometry.planar.Polyline;
Lines 87-93 public class PointListUtils { Link Here
87
	 * @return a new array, which contains copies of the given {@link Point}s at
89
	 * @return a new array, which contains copies of the given {@link Point}s at
88
	 *         the respective index positions
90
	 *         the respective index positions
89
	 */
91
	 */
90
	public static final Point[] getCopy(Point[] points) {
92
	public static final Point[] copy(Point[] points) {
91
		Point[] copy = new Point[points.length];
93
		Point[] copy = new Point[points.length];
92
		for (int i = 0; i < points.length; i++) {
94
		for (int i = 0; i < points.length; i++) {
93
			copy[i] = points[i].getCopy();
95
			copy[i] = points[i].getCopy();
Lines 102-108 public class PointListUtils { Link Here
102
	 *            the array of coordinates to copy
104
	 *            the array of coordinates to copy
103
	 * @return a new array containing identical coordinates
105
	 * @return a new array containing identical coordinates
104
	 */
106
	 */
105
	public static final double[] getCopy(double[] coordinates) {
107
	public static final double[] copy(double[] coordinates) {
106
		double[] copy = new double[coordinates.length];
108
		double[] copy = new double[coordinates.length];
107
		for (int i = 0; i < coordinates.length; i++) {
109
		for (int i = 0; i < coordinates.length; i++) {
108
			copy[i] = coordinates[i];
110
			copy[i] = coordinates[i];
Lines 158-165 public class PointListUtils { Link Here
158
		for (int i = 3; i < points.length; i++) {
160
		for (int i = 3; i < points.length; i++) {
159
			// do always turn right
161
			// do always turn right
160
			while (stack.size() > 2
162
			while (stack.size() > 2
161
					&& CurveUtils.getSignedDistance(stack.get(1), stack.get(0),
163
					&& Straight.getSignedDistanceCCW(stack.get(1),
162
							points[i]) > 0) {
164
							stack.get(0), points[i]) > 0) {
163
				stack.remove(0);
165
				stack.remove(0);
164
			}
166
			}
165
			stack.add(0, points[i]);
167
			stack.add(0, points[i]);
Lines 290-293 public class PointListUtils { Link Here
290
		// this class should not be instantiated by clients
292
		// this class should not be instantiated by clients
291
	}
293
	}
292
294
295
	/**
296
	 * Transforms a sequence of {@link Line}s into a list of {@link Point}s.
297
	 * Consecutive {@link Line}s are expected to share one of their end
298
	 * {@link Point}s. The start {@link Point}s of the {@link Line}s are
299
	 * returned. Additionally, the end {@link Point} of the last {@link Line} is
300
	 * returned, too if the given boolean flag <code>open</code> is set to
301
	 * <code>false</code>.
302
	 * 
303
	 * @param segmentsArray
304
	 * @param open
305
	 *            indicates whether to omit the end {@link Point} of the last
306
	 *            {@link Line}
307
	 * @return the start {@link Point}s of the {@link Line}s and the end
308
	 *         {@link Point} of the last {@link Line} according to
309
	 *         <code>open</code>
310
	 */
311
	public static Point[] toPointsArray(Line[] segmentsArray, boolean open) {
312
		Point[] points = new Point[segmentsArray.length + (open ? 0 : 1)];
313
314
		for (int i = 0; i < segmentsArray.length; i++) {
315
			points[i] = segmentsArray[i].getP1();
316
		}
317
318
		if (!open) {
319
			points[points.length - 1] = segmentsArray[segmentsArray.length - 1]
320
					.getP2();
321
		}
322
323
		return points;
324
	}
325
293
}
326
}
294
- 

Return to bug 355997