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

Collapse All | Expand All

(-)src/org/eclipse/gef4/geometry/Angle.java (-1 / +2 lines)
Lines 207-213 Link Here
207
	 */
207
	 */
208
	@Override
208
	@Override
209
	public int hashCode() {
209
	public int hashCode() {
210
		// calculating a better hashCode is not possible
210
		// calculating a better hashCode is not possible, because due to the
211
		// imprecision, equals() is no longer transitive
211
		return 0;
212
		return 0;
212
	}
213
	}
213
214
(-)src/org/eclipse/gef4/geometry/Dimension.java (-2 / +2 lines)
Lines 349-357 Link Here
349
	 */
349
	 */
350
	@Override
350
	@Override
351
	public int hashCode() {
351
	public int hashCode() {
352
		// calculating a better hashCode is not possible
352
		// calculating a better hashCode is not possible, because due to the
353
		// imprecision, equals() is no longer transitive
353
		return 0;
354
		return 0;
354
		// return (int) (width * height) ^ (int) (width + height);
355
	}
355
	}
356
356
357
	/**
357
	/**
(-)src/org/eclipse/gef4/geometry/Point.java (-2 / +2 lines)
Lines 278-286 Link Here
278
	 */
278
	 */
279
	@Override
279
	@Override
280
	public int hashCode() {
280
	public int hashCode() {
281
		// calculating a better hashCode is not possible
281
		// calculating a better hashCode is not possible, because due to the
282
		// imprecision, equals() is no longer transitive
282
		return 0;
283
		return 0;
283
		// return (int) (x * y) ^ (int) (x + y);
284
	}
284
	}
285
285
286
	/**
286
	/**
(-)src/org/eclipse/gef4/geometry/euclidean/Straight.java (-73 / +17 lines)
Lines 17-22 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.utils.PrecisionUtils;
21
import org.eclipse.gef4.geometry.utils.PrecisionUtils;
21
22
22
/**
23
/**
Lines 41-50 Link Here
41
	 * @param direction
42
	 * @param direction
42
	 */
43
	 */
43
	public Straight(Vector position, Vector direction) {
44
	public Straight(Vector position, Vector direction) {
44
		// if (direction.isNull()) {
45
		// throw new IllegalArgumentException(
46
		//					"direction has to be unequal to (0,0)"); //$NON-NLS-1$
47
		// }
48
		this.position = position.clone();
45
		this.position = position.clone();
49
		this.direction = direction.clone();
46
		this.direction = direction.clone();
50
	}
47
	}
Lines 122-176 Link Here
122
	}
119
	}
123
120
124
	/**
121
	/**
125
	 * Converts a given {@link Vector} into a 3-dimensional vector in
126
	 * homogeneous coordinates represented as an array.
127
	 * 
128
	 * @param v
129
	 *            the {@link Vector} to convert
130
	 * @return an array representation of the 3-dimensional homogeneous vector
131
	 */
132
	private double[] toHomogeneousVector(Vector v) {
133
		return new double[] { v.x, v.y, 1 };
134
	}
135
136
	/**
137
	 * Projects the given 3-dimensional homogeneous-coordinate vector,
138
	 * represented as an array, into the 2D space.
139
	 * 
140
	 * @param v
141
	 *            the array representation of a 3-dimensional
142
	 *            homogeneous-coordinate vector to project
143
	 * @return the projected {@link Vector}
144
	 */
145
	private Vector fromHomogeneousVector(double[] v) {
146
		if (v[2] == 0) {
147
			return null;
148
		}
149
		return new Vector(v[0] / v[2], v[1] / v[2]);
150
	}
151
152
	/**
153
	 * Calculates the cross product of two 3-dimensional vectors.
154
	 * 
155
	 * Used in calculations based on 2D homogeneous coordinates.
156
	 * 
157
	 * @param a
158
	 *            an array representation of the first 3-dimensional input
159
	 *            vector
160
	 * @param b
161
	 *            an array representation of the second 3-deminsional input
162
	 *            vector
163
	 * @return an array representation of the 3-dimensional cross product
164
	 */
165
	private double[] getCrossProduct(double[] a, double[] b) {
166
		double[] r = new double[3];
167
		r[0] = a[1] * b[2] - a[2] * b[1];
168
		r[1] = a[2] * b[0] - a[0] * b[2];
169
		r[2] = a[0] * b[1] - a[1] * b[0];
170
		return r;
171
	}
172
173
	/**
174
	 * Computes the intersection point of this Straight and the provided one, if
122
	 * Computes the intersection point of this Straight and the provided one, if
175
	 * it exists.
123
	 * it exists.
176
	 * 
124
	 * 
Lines 180-195 Link Here
180
	 *         if no intersection point exists (or the Straights are equal).
128
	 *         if no intersection point exists (or the Straights are equal).
181
	 */
129
	 */
182
	public Vector getIntersection(Straight other) {
130
	public Vector getIntersection(Straight other) {
183
		// method using homogeneous coordinates
131
		Point poi = CurveUtils.getIntersection(this, other);
184
		double[] p11 = toHomogeneousVector(position);
132
		if (poi != null) {
185
		double[] p12 = toHomogeneousVector(position.getAdded(direction));
133
			return new Vector(poi);
186
		double[] p21 = toHomogeneousVector(other.position);
134
		}
187
		double[] p22 = toHomogeneousVector(other.position
135
		return null;
188
				.getAdded(other.direction));
189
		double[] l1 = getCrossProduct(p11, p12);
190
		double[] l2 = getCrossProduct(p21, p22);
191
		double[] poi = getCrossProduct(l1, l2);
192
		return fromHomogeneousVector(poi);
193
	}
136
	}
194
137
195
	/**
138
	/**
Lines 420-441 Link Here
420
	}
363
	}
421
364
422
	/**
365
	/**
423
	 * Checks whether this Straight and the provided one are parallel to each
366
	 * Checks whether this {@link Straight} and the provided one are parallel to
424
	 * other.
367
	 * each other.
425
	 * 
368
	 * 
426
	 * @param other
369
	 * @param other
427
	 *            The Straight to test for parallelism.
370
	 *            The {@link Straight} to test for parallelism.
428
	 * @return true if the direction vectors of this Straight and the provided
371
	 * @return true if the direction {@link Vector}s of this {@link Straight}
429
	 *         one are parallel, false otherwise.
372
	 *         and the provided one are parallel, false otherwise.
430
	 */
373
	 */
431
	public boolean isParallelTo(Straight other) {
374
	public boolean isParallelTo(Straight other) {
432
		return direction.isParallelTo(other.direction);
375
		return direction.isParallelTo(other.direction);
433
	}
376
	}
434
377
435
	/**
378
	/**
436
	 * Checks whether this Straight is equal to the provided Straight. Two
379
	 * Checks whether this {@link Straight} is equal to the provided
437
	 * Straights s1 and s2 are equal, if the position vector of s2 is a point on
380
	 * {@link Straight}. Two {@link Straight}s s1 and s2 are equal, if the
438
	 * s1 and the direction vectors of s1 and s2 are parallel.
381
	 * position {@link Vector} of s2 is a point on s1 and the direction
382
	 * {@link Vector}s of s1 and s2 are parallel.
439
	 * 
383
	 * 
440
	 * @see java.lang.Object#equals(java.lang.Object)
384
	 * @see java.lang.Object#equals(java.lang.Object)
441
	 */
385
	 */
Lines 454-462 Link Here
454
	 */
398
	 */
455
	@Override
399
	@Override
456
	public int hashCode() {
400
	public int hashCode() {
457
		// calculating a better hashCode is not possible
401
		// calculating a better hashCode is not possible, because due to the
402
		// imprecision, equals() is no longer transitive
458
		return 0;
403
		return 0;
459
		// return position.hashCode() + direction.hashCode();
460
	}
404
	}
461
405
462
	/**
406
	/**
(-)src/org/eclipse/gef4/geometry/euclidean/Vector.java (-2 / +2 lines)
Lines 410-418 Link Here
410
	 */
410
	 */
411
	@Override
411
	@Override
412
	public int hashCode() {
412
	public int hashCode() {
413
		// calculating a better hashCode is not possible
413
		// calculating a better hashCode is not possible, because due to the
414
		// imprecision, equals() is no longer transitive
414
		return 0;
415
		return 0;
415
		// return (int) x + (int) y;
416
	}
416
	}
417
417
418
	/**
418
	/**
(-)src/org/eclipse/gef4/geometry/shapes/Arc.java (-1 / +165 lines)
Lines 12-17 Link Here
12
package org.eclipse.gef4.geometry.shapes;
12
package org.eclipse.gef4.geometry.shapes;
13
13
14
import java.util.ArrayList;
14
import java.util.ArrayList;
15
import java.util.Arrays;
16
import java.util.HashSet;
15
import java.util.List;
17
import java.util.List;
16
18
17
import org.eclipse.gef4.geometry.Angle;
19
import org.eclipse.gef4.geometry.Angle;
Lines 125-130 Link Here
125
		return height;
127
		return height;
126
	}
128
	}
127
129
130
	/**
131
	 * Returns the points of intersection between this {@link Arc} and the given
132
	 * {@link CubicCurve}.
133
	 * 
134
	 * @param c
135
	 *            The {@link CubicCurve} to test for intersections
136
	 * @return the points of intersection.
137
	 */
138
	public Point[] getIntersections(CubicCurve c) {
139
		return c.getIntersections(this);
140
	}
141
142
	/**
143
	 * Returns the points of intersection between this {@link Arc} and the given
144
	 * other {@link Arc}.
145
	 * 
146
	 * @param other
147
	 *            The {@link Arc} to test for intersections
148
	 * @return the points of intersection.
149
	 */
150
	public Point[] getIntersections(Arc other) {
151
		if (equals(other)) {
152
			return new Point[] {};
153
		}
154
155
		HashSet<Point> intersections = new HashSet<Point>();
156
157
		for (CubicCurve seg : getBorderSegments()) {
158
			intersections.addAll(Arrays.asList(getIntersections(seg)));
159
		}
160
161
		return intersections.toArray(new Point[] {});
162
	}
163
164
	/**
165
	 * Returns the points of intersection between this {@link Arc} and the given
166
	 * {@link Ellipse}.
167
	 * 
168
	 * @param e
169
	 *            The {@link Ellipse} to test for intersections
170
	 * @return the points of intersection.
171
	 */
172
	public Point[] getIntersections(Ellipse e) {
173
		return e.getIntersections(this);
174
	}
175
176
	/**
177
	 * Returns the points of intersection between this {@link Arc} and the given
178
	 * {@link Line}.
179
	 * 
180
	 * @param l
181
	 *            The {@link Line} to test for intersections
182
	 * @return the points of intersection.
183
	 */
184
	public Point[] getIntersections(Line l) {
185
		HashSet<Point> intersections = new HashSet<Point>();
186
187
		for (CubicCurve seg : getBorderSegments()) {
188
			intersections.addAll(Arrays.asList(seg.getIntersections(l)));
189
		}
190
191
		return intersections.toArray(new Point[] {});
192
	}
193
194
	/**
195
	 * Returns the points of intersection between this {@link Arc} and the given
196
	 * {@link Polygon}.
197
	 * 
198
	 * @param p
199
	 *            The {@link Polygon} to test for intersections
200
	 * @return the points of intersection.
201
	 */
202
	public Point[] getIntersections(Polygon p) {
203
		return p.getIntersections(this);
204
	}
205
206
	/**
207
	 * Returns the points of intersection between this {@link Arc} and the given
208
	 * {@link Polyline}.
209
	 * 
210
	 * @param p
211
	 *            The {@link Polyline} to test for intersections
212
	 * @return the points of intersection.
213
	 */
214
	public Point[] getIntersections(Polyline p) {
215
		HashSet<Point> intersections = new HashSet<Point>();
216
217
		for (CubicCurve seg : getBorderSegments()) {
218
			intersections.addAll(Arrays.asList(seg.getIntersections(p)));
219
		}
220
221
		return intersections.toArray(new Point[] {});
222
	}
223
224
	/**
225
	 * Returns the points of intersection between this {@link Arc} and the given
226
	 * {@link QuadraticCurve}.
227
	 * 
228
	 * @param c
229
	 *            The {@link QuadraticCurve} to test for intersections
230
	 * @return the points of intersection.
231
	 */
232
	public Point[] getIntersections(QuadraticCurve c) {
233
		HashSet<Point> intersections = new HashSet<Point>();
234
235
		for (CubicCurve seg : getBorderSegments()) {
236
			intersections.addAll(Arrays.asList(seg.getIntersections(c)));
237
		}
238
239
		return intersections.toArray(new Point[] {});
240
	}
241
242
	/**
243
	 * Returns the points of intersection between this {@link Arc} and the given
244
	 * {@link Rectangle}.
245
	 * 
246
	 * @param r
247
	 *            The {@link Rectangle} to test for intersections
248
	 * @return the points of intersection.
249
	 */
250
	public Point[] getIntersections(Rectangle r) {
251
		HashSet<Point> intersections = new HashSet<Point>();
252
253
		for (CubicCurve seg : getBorderSegments()) {
254
			intersections.addAll(Arrays.asList(seg.getIntersections(r)));
255
		}
256
257
		return intersections.toArray(new Point[] {});
258
	}
259
260
	/**
261
	 * Returns the points of intersection between this {@link Arc} and the given
262
	 * {@link RoundedRectangle}.
263
	 * 
264
	 * @param rr
265
	 *            The {@link RoundedRectangle} to test for intersections
266
	 * @return the points of intersection.
267
	 */
268
	public Point[] getIntersections(RoundedRectangle rr) {
269
		HashSet<Point> intersections = new HashSet<Point>();
270
271
		// line segments
272
		intersections.addAll(Arrays.asList(getIntersections(rr.getTop())));
273
		intersections.addAll(Arrays.asList(getIntersections(rr.getLeft())));
274
		intersections.addAll(Arrays.asList(getIntersections(rr.getBottom())));
275
		intersections.addAll(Arrays.asList(getIntersections(rr.getRight())));
276
277
		// arc segments
278
		intersections.addAll(Arrays.asList(getIntersections(rr.getTopRight())));
279
		intersections.addAll(Arrays.asList(getIntersections(rr.getTopLeft())));
280
		intersections
281
				.addAll(Arrays.asList(getIntersections(rr.getBottomLeft())));
282
		intersections
283
				.addAll(Arrays.asList(getIntersections(rr.getBottomRight())));
284
285
		return intersections.toArray(new Point[] {});
286
	}
287
128
	public void setHeight(double height) {
288
	public void setHeight(double height) {
129
		this.height = height;
289
		this.height = height;
130
	}
290
	}
Lines 149-155 Link Here
149
	 * @see Geometry#toPath()
309
	 * @see Geometry#toPath()
150
	 */
310
	 */
151
	public Path toPath() {
311
	public Path toPath() {
312
		CubicCurve[] segments = getBorderSegments();
313
		return toPath(segments);
314
	}
152
315
316
	public CubicCurve[] getBorderSegments() {
153
		double start = getStartAngle().rad();
317
		double start = getStartAngle().rad();
154
		double end = getStartAngle().rad() + getAngularExtent().rad();
318
		double end = getStartAngle().rad() + getAngularExtent().rad();
155
319
Lines 184-190 Link Here
184
				}
348
				}
185
			}
349
			}
186
		}
350
		}
187
		return toPath(segments.toArray(new CubicCurve[] {}));
351
		return segments.toArray(new CubicCurve[] {});
188
	}
352
	}
189
353
190
	private CubicCurve computeApproximation(double start, double end) {
354
	private CubicCurve computeApproximation(double start, double end) {
(-)src/org/eclipse/gef4/geometry/shapes/CubicCurve.java (-138 / +156 lines)
Lines 18-23 Link Here
18
18
19
import org.eclipse.gef4.geometry.Point;
19
import org.eclipse.gef4.geometry.Point;
20
import org.eclipse.gef4.geometry.transform.AffineTransform;
20
import org.eclipse.gef4.geometry.transform.AffineTransform;
21
import org.eclipse.gef4.geometry.utils.CurveUtils;
21
import org.eclipse.gef4.geometry.utils.PolynomCalculationUtils;
22
import org.eclipse.gef4.geometry.utils.PolynomCalculationUtils;
22
import org.eclipse.gef4.geometry.utils.PrecisionUtils;
23
import org.eclipse.gef4.geometry.utils.PrecisionUtils;
23
24
Lines 67-72 Link Here
67
	}
68
	}
68
69
69
	/**
70
	/**
71
	 * Constructs a new {@link CubicCurve} object with the given sequence of x-
72
	 * and y-coordinates of the start-, the first and second control-, and the
73
	 * end-point.
74
	 * 
75
	 * @param coordinates
76
	 *            a sequence of coordinates
77
	 * 
78
	 * @see CubicCurve#CubicCurve(double, double, double, double, double,
79
	 *      double, double, double)
80
	 */
81
	public CubicCurve(double... coordinates) {
82
		x1 = coordinates[0];
83
		y1 = coordinates[1];
84
		ctrl1X = coordinates[2];
85
		ctrl1Y = coordinates[3];
86
		ctrl2X = coordinates[4];
87
		ctrl2Y = coordinates[5];
88
		x2 = coordinates[6];
89
		y2 = coordinates[7];
90
	}
91
92
	/**
70
	 * Constructs a new {@link CubicCurve} object with the given control
93
	 * Constructs a new {@link CubicCurve} object with the given control
71
	 * {@link Point}s.
94
	 * {@link Point}s.
72
	 * 
95
	 * 
Lines 115-136 Link Here
115
	 * @see Geometry#contains(Point)
138
	 * @see Geometry#contains(Point)
116
	 */
139
	 */
117
	public boolean contains(Point p) {
140
	public boolean contains(Point p) {
118
		// find roots of the x(t) - p.x function:
141
		return CurveUtils.contains(this, p);
119
		double D = getX1() - p.x;
120
		double C = 3 * (getCtrl1X() - getX1());
121
		double B = 3 * (getCtrl2X() - getCtrl1X()) - C;
122
		double A = getX2() - getX1() - B - C;
123
		double[] xts = PolynomCalculationUtils.getCubicRoots(A, B, C, D);
124
125
		for (double t : xts) {
126
			// t = PrecisionUtils.round(t);
127
			if (PrecisionUtils.greaterEqual(t, 0)
128
					&& PrecisionUtils.smallerEqual(t, 1)
129
					&& PrecisionUtils.equal(get(t).y, p.y)) {
130
				return true;
131
			}
132
		}
133
		return false;
134
	}
142
	}
135
143
136
	/**
144
	/**
Lines 215-224 Link Here
215
		return new Rectangle(new Point(xmin, ymin), new Point(xmax, ymax));
223
		return new Rectangle(new Point(xmin, ymin), new Point(xmax, ymax));
216
	}
224
	}
217
225
218
	private Point ratioPoint(Point p, Point q, double ratio) {
219
		return p.getTranslated(q.getTranslated(p.getNegated()).getScaled(ratio));
220
	}
221
222
	/**
226
	/**
223
	 * Subdivides this {@link CubicCurve} into two {@link CubicCurve}s on the
227
	 * Subdivides this {@link CubicCurve} into two {@link CubicCurve}s on the
224
	 * intervals [0, t] and [t, 1] using the de-Casteljau-algorithm.
228
	 * intervals [0, t] and [t, 1] using the de-Casteljau-algorithm.
Lines 228-249 Link Here
228
	 * @return the two {@link CubicCurve}s
232
	 * @return the two {@link CubicCurve}s
229
	 */
233
	 */
230
	public CubicCurve[] split(double t) {
234
	public CubicCurve[] split(double t) {
231
		if (t < 0 || t > 1) {
235
		return CurveUtils.split(this, t);
232
			throw new IllegalArgumentException(
233
					"Paramter t is out of range! t = " + t + " !in_range(0,1)");
234
		}
235
236
		Point p10 = ratioPoint(getP1(), getCtrl1(), t);
237
		Point p11 = ratioPoint(getCtrl1(), getCtrl2(), t);
238
		Point p12 = ratioPoint(getCtrl2(), getP2(), t);
239
		Point p20 = ratioPoint(p10, p11, t);
240
		Point p21 = ratioPoint(p11, p12, t);
241
		Point p30 = ratioPoint(p20, p21, t);
242
243
		CubicCurve left = new CubicCurve(getP1(), p10, p20, p30);
244
		CubicCurve right = new CubicCurve(p30, p21, p12, getP2());
245
246
		return new CubicCurve[] { left, right };
247
	}
236
	}
248
237
249
	/**
238
	/**
Lines 256-275 Link Here
256
	 * @return the {@link CubicCurve} on the interval [t1, t2]
245
	 * @return the {@link CubicCurve} on the interval [t1, t2]
257
	 */
246
	 */
258
	public CubicCurve clip(double t1, double t2) {
247
	public CubicCurve clip(double t1, double t2) {
259
		if (t1 < 0 || t1 > 1) {
248
		return CurveUtils.clip(this, t1, t2);
260
			throw new IllegalArgumentException(
261
					"Paramter t1 is out of range! t1 = " + t1
262
							+ " !in_range(0,1)");
263
		}
264
		if (t2 < 0 || t2 > 1) {
265
			throw new IllegalArgumentException(
266
					"Paramter t2 is out of range! t2 = " + t2
267
							+ " !in_range(0,1)");
268
		}
269
270
		CubicCurve right = split(t1)[1];
271
		double rightT2 = (t2 - t1) / (1 - t1);
272
		return right.split(rightT2)[0];
273
	}
249
	}
274
250
275
	private static double getArea(Polygon p) {
251
	private static double getArea(Polygon p) {
Lines 321-397 Link Here
321
		return new Point[] {};
297
		return new Point[] {};
322
	}
298
	}
323
299
324
	private static Point[] getIntersections(CubicCurve p, double ps, double pe,
300
	/**
325
			CubicCurve q, double qs, double qe) {
301
	 * Returns the points of intersection between this {@link CubicCurve} and
326
		double pm = (ps + pe) / 2;
302
	 * the given {@link Arc}.
327
		double qm = (qs + qe) / 2;
303
	 * 
328
304
	 * @param arc
329
		// point convergence test
305
	 *            The {@link Arc} to test for intersections
330
		Point pPoi = p.get(pm);
306
	 * @return the points of intersection.
331
		Point qPoi = q.get(qm);
307
	 */
332
308
	public Point[] getIntersections(Arc arc) {
333
		if (pPoi != null && qPoi != null && pPoi.equals(qPoi)) {
309
		HashSet<Point> intersections = new HashSet<Point>();
334
			return new Point[] { pPoi };
335
		}
336
337
		// no point convergence yet
338
		// clip to parameter ranges
339
		CubicCurve pc = p.clip(ps, pe);
340
		CubicCurve qc = q.clip(qs, qe);
341
342
		// check the control polygons
343
		Polygon pPoly = pc.getControlPolygon();
344
		Polygon qPoly = qc.getControlPolygon();
345
346
		if (pPoly.intersects(qPoly)) {
347
			// check the polygon's areas
348
			double pArea = getArea(pPoly);
349
			double qArea = getArea(qPoly);
350
351
			if (PrecisionUtils.equal(pArea, 0, +2)
352
					&& PrecisionUtils.equal(qArea, 0, +2)) {
353
				// return line/line intersection
354
				Point poi = new Line(pc.getP1(), pc.getP2())
355
						.getIntersection(new Line(qc.getP1(), qc.getP2()));
356
				if (poi != null) {
357
					return new Point[] { poi };
358
				}
359
				return new Point[] {};
360
			}
361
362
			// areas not small enough
363
364
			// do not try to find the intersections of two equal curves
365
			if (pc.equals(qc)) {
366
				return new Point[] {};
367
			}
368
369
			// "split" the curves and do the intersection test recursively
370
			HashSet<Point> intersections = new HashSet<Point>();
371
372
			intersections.addAll(Arrays.asList(getIntersections(p.clip(ps, pm),
373
					0, 1, q.clip(qs, qm), 0, 1)));
374
			intersections.addAll(Arrays.asList(getIntersections(p.clip(ps, pm),
375
					0, 1, q.clip(qm, qe), 0, 1)));
376
			intersections.addAll(Arrays.asList(getIntersections(p.clip(pm, pe),
377
					0, 1, q.clip(qs, qm), 0, 1)));
378
			intersections.addAll(Arrays.asList(getIntersections(p.clip(pm, pe),
379
					0, 1, q.clip(qm, qe), 0, 1)));
380
381
			// intersections.addAll(Arrays.asList(getIntersections(p, ps, pm, q,
382
			// qs, qm)));
383
			// intersections.addAll(Arrays.asList(getIntersections(p, ps, pm, q,
384
			// qm, qe)));
385
			// intersections.addAll(Arrays.asList(getIntersections(p, pm, pe, q,
386
			// qs, qm)));
387
			// intersections.addAll(Arrays.asList(getIntersections(p, pm, pe, q,
388
			// qm, qe)));
389
310
390
			return intersections.toArray(new Point[] {});
311
		for (CubicCurve seg : arc.getBorderSegments()) {
312
			intersections.addAll(Arrays.asList(getIntersections(seg)));
391
		}
313
		}
392
314
393
		// no intersections
315
		return intersections.toArray(new Point[] {});
394
		return new Point[] {};
395
	}
316
	}
396
317
397
	/**
318
	/**
Lines 402-408 Link Here
402
	 * @return the points of intersection
323
	 * @return the points of intersection
403
	 */
324
	 */
404
	public Point[] getIntersections(CubicCurve other) {
325
	public Point[] getIntersections(CubicCurve other) {
405
		return getIntersections(this, 0, 1, other, 0, 1);
326
		if (equals(other)) {
327
			return new Point[] {};
328
		}
329
		return CurveUtils.getIntersections(this, other);
330
	}
331
332
	/**
333
	 * Returns the points of intersection between this {@link CubicCurve} and
334
	 * the given {@link Ellipse}.
335
	 * 
336
	 * @param e
337
	 *            The {@link Ellipse} to test for intersections
338
	 * @return the points of intersection.
339
	 */
340
	public Point[] getIntersections(Ellipse e) {
341
		return e.getIntersections(this);
406
	}
342
	}
407
343
408
	/**
344
	/**
Lines 417-422 Link Here
417
	}
353
	}
418
354
419
	/**
355
	/**
356
	 * Returns the points of intersection between this {@link CubicCurve} and
357
	 * the given {@link Polygon}.
358
	 * 
359
	 * @param p
360
	 *            The {@link Polygon} to test for intersections
361
	 * @return the points of intersection.
362
	 */
363
	public Point[] getIntersections(Polygon p) {
364
		return p.getIntersections(this);
365
	}
366
367
	/**
368
	 * Returns the points of intersection between this {@link CubicCurve} and
369
	 * the given {@link Polyline}.
370
	 * 
371
	 * @param p
372
	 *            The {@link Polyline} to test for intersections
373
	 * @return the points of intersection.
374
	 */
375
	public Point[] getIntersections(Polyline p) {
376
		HashSet<Point> intersections = new HashSet<Point>();
377
378
		for (Line seg : p.getSegments()) {
379
			intersections.addAll(Arrays.asList(getIntersections(seg)));
380
		}
381
382
		return intersections.toArray(new Point[] {});
383
	}
384
385
	/**
386
	 * Returns the points of intersection between this {@link CubicCurve} and
387
	 * the given {@link QuadraticCurve}.
388
	 * 
389
	 * @param c
390
	 *            The {@link QuadraticCurve} to test for intersections
391
	 * @return the points of intersection.
392
	 */
393
	public Point[] getIntersections(QuadraticCurve c) {
394
		return getIntersections(c.getElevated());
395
	}
396
397
	/**
398
	 * Returns the points of intersection between this {@link CubicCurve} and
399
	 * the given {@link Rectangle}.
400
	 * 
401
	 * @param r
402
	 *            The {@link Rectangle} to test for intersections
403
	 * @return the points of intersection.
404
	 */
405
	public Point[] getIntersections(Rectangle r) {
406
		HashSet<Point> intersections = new HashSet<Point>();
407
408
		for (Line seg : r.getSegments()) {
409
			intersections.addAll(Arrays.asList(getIntersections(seg)));
410
		}
411
412
		return intersections.toArray(new Point[] {});
413
	}
414
415
	/**
416
	 * Returns the points of intersection between this {@link CubicCurve} and
417
	 * the given {@link RoundedRectangle}.
418
	 * 
419
	 * @param rr
420
	 *            The {@link RoundedRectangle} to test for intersections
421
	 * @return the points of intersection.
422
	 */
423
	public Point[] getIntersections(RoundedRectangle rr) {
424
		HashSet<Point> intersections = new HashSet<Point>();
425
426
		// line segments
427
		intersections.addAll(Arrays.asList(getIntersections(rr.getTop())));
428
		intersections.addAll(Arrays.asList(getIntersections(rr.getLeft())));
429
		intersections.addAll(Arrays.asList(getIntersections(rr.getBottom())));
430
		intersections.addAll(Arrays.asList(getIntersections(rr.getRight())));
431
432
		// arc segments
433
		intersections.addAll(Arrays.asList(getIntersections(rr.getTopRight())));
434
		intersections.addAll(Arrays.asList(getIntersections(rr.getTopLeft())));
435
		intersections
436
				.addAll(Arrays.asList(getIntersections(rr.getBottomLeft())));
437
		intersections
438
				.addAll(Arrays.asList(getIntersections(rr.getBottomRight())));
439
440
		return intersections.toArray(new Point[] {});
441
	}
442
443
	/**
420
	 * Returns the first control {@link Point}.
444
	 * Returns the first control {@link Point}.
421
	 * 
445
	 * 
422
	 * @return the first control {@link Point}.
446
	 * @return the first control {@link Point}.
Lines 536-542 Link Here
536
	 */
560
	 */
537
	@Override
561
	@Override
538
	public int hashCode() {
562
	public int hashCode() {
539
		// calculating a better hashCode is not possible
563
		// calculating a better hashCode is not possible, because due to the
564
		// imprecision, equals() is no longer transitive
540
		return 0;
565
		return 0;
541
	}
566
	}
542
567
Lines 721-726 Link Here
721
		return p;
746
		return p;
722
	}
747
	}
723
748
749
	public String toString() {
750
		return "CubicCurve(x1 = " + x1 + ", y1 = " + y1 + ", ctrl1X = "
751
				+ ctrl1X + ", ctrl1Y = " + ctrl1Y + ", ctrl2X = " + ctrl2X
752
				+ ", ctrl2Y = " + ctrl2Y + ", x2 = " + x2 + ", y2 = " + y2;
753
	}
754
724
	/**
755
	/**
725
	 * Get a single {@link Point} on this CubicCurve at parameter t.
756
	 * Get a single {@link Point} on this CubicCurve at parameter t.
726
	 * 
757
	 * 
Lines 729-752 Link Here
729
	 * @return the {@link Point} at parameter t
760
	 * @return the {@link Point} at parameter t
730
	 */
761
	 */
731
	public Point get(double t) {
762
	public Point get(double t) {
732
		if (!(PrecisionUtils.greaterEqual(t, 0) && PrecisionUtils.smallerEqual(
763
		return CurveUtils.get(this, t);
733
				t, 1))) {
764
	}
734
			throw new IllegalArgumentException(
765
735
					"Paramter t is out of range! t = " + t + " !in_range(0,1)");
766
	public CubicCurve getCopy() {
736
		}
767
		return new CubicCurve(getP1(), getCtrl1(), getCtrl2(), getP2());
737
738
		// compensate rounding effects
739
		if (t < 0) {
740
			t = 0;
741
		} else if (t > 1) {
742
			t = 1;
743
		}
744
745
		double d = 1 - t;
746
		return getP1().getScaled(d * d * d)
747
				.getTranslated(getCtrl1().getScaled(3 * t * d * d))
748
				.getTranslated(getCtrl2().getScaled(3 * t * t * d))
749
				.getTranslated(getP2().getScaled(t * t * t));
750
	}
768
	}
751
769
752
}
770
}
(-)src/org/eclipse/gef4/geometry/shapes/Ellipse.java (-6 / +127 lines)
Lines 24-31 Link Here
24
import org.eclipse.gef4.geometry.utils.PrecisionUtils;
24
import org.eclipse.gef4.geometry.utils.PrecisionUtils;
25
25
26
/**
26
/**
27
 * Represents the geometric shape of an ellipse, which is defined by means of
27
 * Represents the geometric shape of an ellipse.
28
 * its enclosing framing rectangle.
29
 * 
28
 * 
30
 * Note that while all manipulations (e.g. within shrink, expand) within this
29
 * Note that while all manipulations (e.g. within shrink, expand) within this
31
 * class are based on double precision, all comparisons (e.g. within contains,
30
 * class are based on double precision, all comparisons (e.g. within contains,
Lines 86-91 Link Here
86
	}
85
	}
87
86
88
	/**
87
	/**
88
	 * Tests whether the given {@link CubicCurve} is fully contained within this
89
	 * {@link Ellipse}.
90
	 * 
91
	 * @param c
92
	 *            the {@link CubicCurve} to test for containment
93
	 * @return <code>true</code> in case the given {@link CubicCurve} is fully
94
	 *         contained, <code>false</code> otherwise
95
	 */
96
	public boolean contains(CubicCurve c) {
97
		return contains(c.getP1()) && contains(c.getP2())
98
				&& getIntersections(c).length == 0;
99
	}
100
101
	/**
102
	 * Tests whether the given {@link Ellipse} is fully contained within this
103
	 * {@link Ellipse}.
104
	 * 
105
	 * @param o
106
	 *            the {@link Ellipse} to test for containment
107
	 * @return <code>true</code> in case the given {@link Ellipse} is fully
108
	 *         contained, <code>false</code> otherwise
109
	 */
110
	public boolean contains(Ellipse o) {
111
		for (CubicCurve seg : o.getBorderSegments()) {
112
			if (!contains(seg)) {
113
				return false;
114
			}
115
		}
116
		return true;
117
	}
118
119
	/**
120
	 * Tests whether the given {@link QuadraticCurve} is fully contained within
121
	 * this {@link Ellipse}.
122
	 * 
123
	 * @param c
124
	 *            the {@link QuadraticCurve} to test for containment
125
	 * @return <code>true</code> in case the given {@link QuadraticCurve} is
126
	 *         fully contained, <code>false</code> otherwise
127
	 */
128
	public boolean contains(QuadraticCurve c) {
129
		return contains(c.getP1()) && contains(c.getP2())
130
				&& getIntersections(c).length == 0;
131
	}
132
133
	/**
89
	 * Tests whether the given {@link Line} is fully contained within this
134
	 * Tests whether the given {@link Line} is fully contained within this
90
	 * {@link Ellipse}.
135
	 * {@link Ellipse}.
91
	 * 
136
	 * 
Lines 291-297 Link Here
291
	 *         otherwise
336
	 *         otherwise
292
	 */
337
	 */
293
	public Point[] getIntersections(Rectangle rectangle) {
338
	public Point[] getIntersections(Rectangle rectangle) {
294
		List<Point> intersections = new ArrayList<Point>(8); // 2*4
339
		HashSet<Point> intersections = new HashSet<Point>();
295
340
296
		for (Line segment : rectangle.getSegments()) {
341
		for (Line segment : rectangle.getSegments()) {
297
			for (Point intersection : getIntersections(segment)) {
342
			for (Point intersection : getIntersections(segment)) {
Lines 304-309 Link Here
304
349
305
	/**
350
	/**
306
	 * Returns the intersection points of this {@link Ellipse}'s outline with
351
	 * Returns the intersection points of this {@link Ellipse}'s outline with
352
	 * the given {@link RoundedRectangle}'s outline.
353
	 * 
354
	 * @param rr
355
	 *            The {@link RoundedRectangle} to test for intersection
356
	 * @return an array containing the {@link Point}s of intersection of this
357
	 *         {@link Ellipse}'s outline with the given {@link RoundedRectangle}
358
	 *         's outline in case such intersection points exist, an empty array
359
	 *         otherwise.
360
	 */
361
	public Point[] getIntersections(RoundedRectangle rr) {
362
		HashSet<Point> intersections = new HashSet<Point>();
363
364
		// line segments
365
		intersections.addAll(Arrays.asList(getIntersections(rr.getTop())));
366
		intersections.addAll(Arrays.asList(getIntersections(rr.getLeft())));
367
		intersections.addAll(Arrays.asList(getIntersections(rr.getBottom())));
368
		intersections.addAll(Arrays.asList(getIntersections(rr.getRight())));
369
370
		// arc segments
371
		intersections.addAll(Arrays.asList(getIntersections(rr.getTopRight())));
372
		intersections.addAll(Arrays.asList(getIntersections(rr.getTopLeft())));
373
		intersections
374
				.addAll(Arrays.asList(getIntersections(rr.getBottomLeft())));
375
		intersections
376
				.addAll(Arrays.asList(getIntersections(rr.getBottomRight())));
377
378
		return intersections.toArray(new Point[] {});
379
	}
380
381
	/**
382
	 * Returns the intersection points of this {@link Ellipse}'s outline with
307
	 * the given {@link Line}.
383
	 * the given {@link Line}.
308
	 * 
384
	 * 
309
	 * @param line
385
	 * @param line
Lines 479-488 Link Here
479
	 */
555
	 */
480
	@Override
556
	@Override
481
	public int hashCode() {
557
	public int hashCode() {
482
		// calculating a better hashCode is not possible
558
		// calculating a better hashCode is not possible, because due to the
559
		// imprecision, equals() is no longer transitive
483
		return 0;
560
		return 0;
484
		// return (int) ((x + height + 1) * (y + width + 1)) ^ (int) x ^ (int)
485
		// y;
486
	}
561
	}
487
562
488
	/**
563
	/**
Lines 532-537 Link Here
532
	}
607
	}
533
608
534
	/**
609
	/**
610
	 * Does the given {@link CubicCurve} intersect (including containment) this
611
	 * {@link Ellipse}?
612
	 * 
613
	 * @param c
614
	 * @return true if they intersect, false otherwise
615
	 */
616
	public boolean intersects(CubicCurve c) {
617
		return contains(c.getP1()) && contains(c.getP2())
618
				|| getIntersections(c).length > 0;
619
	}
620
621
	/**
535
	 * @see Geometry#intersects(Rectangle)
622
	 * @see Geometry#intersects(Rectangle)
536
	 */
623
	 */
537
	public boolean intersects(Rectangle r) {
624
	public boolean intersects(Rectangle r) {
Lines 626-631 Link Here
626
713
627
	/**
714
	/**
628
	 * Calculates the points of intersection of this {@link Ellipse} and the
715
	 * Calculates the points of intersection of this {@link Ellipse} and the
716
	 * given {@link Arc}.
717
	 * 
718
	 * @param arc
719
	 *            The {@link Arc} to compute intersection points with
720
	 * @return the points of intersection of this {@link Ellipse} and the given
721
	 *         {@link Arc}.
722
	 */
723
	public Point[] getIntersections(Arc arc) {
724
		HashSet<Point> intersections = new HashSet<Point>();
725
726
		for (CubicCurve mySeg : getBorderSegments()) {
727
			for (CubicCurve arcSeg : arc.getBorderSegments()) {
728
				intersections.addAll(Arrays.asList(mySeg
729
						.getIntersections(arcSeg)));
730
			}
731
		}
732
733
		return intersections.toArray(new Point[] {});
734
	}
735
736
	/**
737
	 * Calculates the points of intersection of this {@link Ellipse} and the
629
	 * given {@link CubicCurve}.
738
	 * given {@link CubicCurve}.
630
	 * 
739
	 * 
631
	 * @param curve
740
	 * @param curve
Lines 684-687 Link Here
684
793
685
		return segs;
794
		return segs;
686
	}
795
	}
796
797
	/**
798
	 * Computes and returns the points of intersection of this {@link Ellipse}'s
799
	 * outline with the given {@link QuadraticCurve}.
800
	 * 
801
	 * @param c
802
	 * @return the points of intersection of this {@link Ellipse}'s outline with
803
	 *         the given {@link QuadraticCurve}
804
	 */
805
	public Point[] getIntersections(QuadraticCurve c) {
806
		return getIntersections(c.getElevated());
807
	}
687
}
808
}
(-)src/org/eclipse/gef4/geometry/shapes/Line.java (-2 / +65 lines)
Lines 12-17 Link Here
12
 *******************************************************************************/
12
 *******************************************************************************/
13
package org.eclipse.gef4.geometry.shapes;
13
package org.eclipse.gef4.geometry.shapes;
14
14
15
import java.util.Arrays;
16
import java.util.HashSet;
17
15
import org.eclipse.gef4.geometry.Point;
18
import org.eclipse.gef4.geometry.Point;
16
import org.eclipse.gef4.geometry.euclidean.Straight;
19
import org.eclipse.gef4.geometry.euclidean.Straight;
17
import org.eclipse.gef4.geometry.euclidean.Vector;
20
import org.eclipse.gef4.geometry.euclidean.Vector;
Lines 149-154 Link Here
149
		return new Line(x1, y1, x2, y2);
152
		return new Line(x1, y1, x2, y2);
150
	}
153
	}
151
154
155
	public Point[] getIntersections(Arc arc) {
156
		return arc.getIntersections(this);
157
	}
158
159
	public Point[] getIntersections(CubicCurve c) {
160
		return c.getIntersections(this);
161
	}
162
163
	public Point[] getIntersections(Ellipse e) {
164
		return e.getIntersections(this);
165
	}
166
167
	public Point[] getIntersections(Polygon p) {
168
		return p.getIntersections(this);
169
	}
170
171
	public Point[] getIntersections(Polyline p) {
172
		HashSet<Point> intersections = new HashSet<Point>();
173
174
		for (Line seg : p.getSegments()) {
175
			intersections.add(getIntersection(seg));
176
		}
177
178
		return intersections.toArray(new Point[] {});
179
	}
180
181
	public Point[] getIntersections(QuadraticCurve c) {
182
		return c.getIntersections(this);
183
	}
184
185
	public Point[] getIntersections(Rectangle r) {
186
		HashSet<Point> intersections = new HashSet<Point>();
187
188
		for (Line seg : r.getSegments()) {
189
			intersections.addAll(Arrays.asList(getIntersection(seg)));
190
		}
191
192
		return intersections.toArray(new Point[] {});
193
	}
194
195
	public Point[] getIntersections(RoundedRectangle rr) {
196
		HashSet<Point> intersections = new HashSet<Point>();
197
198
		// line segments
199
		intersections.add(getIntersection(rr.getTop()));
200
		intersections.add(getIntersection(rr.getLeft()));
201
		intersections.add(getIntersection(rr.getBottom()));
202
		intersections.add(getIntersection(rr.getRight()));
203
204
		// arc segments
205
		intersections.addAll(Arrays.asList(getIntersections(rr.getTopRight())));
206
		intersections.addAll(Arrays.asList(getIntersections(rr.getTopLeft())));
207
		intersections
208
				.addAll(Arrays.asList(getIntersections(rr.getBottomLeft())));
209
		intersections
210
				.addAll(Arrays.asList(getIntersections(rr.getBottomRight())));
211
212
		return intersections.toArray(new Point[] {});
213
	}
214
152
	/**
215
	/**
153
	 * Returns the single intersection point between this {@link Line} and the
216
	 * Returns the single intersection point between this {@link Line} and the
154
	 * given one, in case it exists. Note that even in case
217
	 * given one, in case it exists. Note that even in case
Lines 269-277 Link Here
269
	 */
332
	 */
270
	@Override
333
	@Override
271
	public int hashCode() {
334
	public int hashCode() {
272
		// calculating a better hashCode is not possible
335
		// calculating a better hashCode is not possible, because due to the
336
		// imprecision, equals() is no longer transitive
273
		return 0;
337
		return 0;
274
		// return (int) x1 + (int) y1 + (int) x2 + (int) y2;
275
	}
338
	}
276
339
277
	/**
340
	/**
(-)src/org/eclipse/gef4/geometry/shapes/Polygon.java (-2 / +79 lines)
Lines 13-18 Link Here
13
package org.eclipse.gef4.geometry.shapes;
13
package org.eclipse.gef4.geometry.shapes;
14
14
15
import java.util.ArrayList;
15
import java.util.ArrayList;
16
import java.util.Arrays;
16
import java.util.HashSet;
17
import java.util.HashSet;
17
18
18
import org.eclipse.gef4.geometry.Angle;
19
import org.eclipse.gef4.geometry.Angle;
Lines 209-214 Link Here
209
		return intersections.toArray(new Point[] {});
210
		return intersections.toArray(new Point[] {});
210
	}
211
	}
211
212
213
	public Point[] getIntersections(RoundedRectangle rr) {
214
		HashSet<Point> intersections = new HashSet<Point>();
215
216
		// line segments
217
		intersections.addAll(Arrays.asList(getIntersections(rr.getTop())));
218
		intersections.addAll(Arrays.asList(getIntersections(rr.getLeft())));
219
		intersections.addAll(Arrays.asList(getIntersections(rr.getBottom())));
220
		intersections.addAll(Arrays.asList(getIntersections(rr.getRight())));
221
222
		// arc segments
223
		intersections.addAll(Arrays.asList(getIntersections(rr.getTopRight())));
224
		intersections.addAll(Arrays.asList(getIntersections(rr.getTopLeft())));
225
		intersections
226
				.addAll(Arrays.asList(getIntersections(rr.getBottomLeft())));
227
		intersections
228
				.addAll(Arrays.asList(getIntersections(rr.getBottomRight())));
229
230
		return intersections.toArray(new Point[] {});
231
	}
232
212
	/**
233
	/**
213
	 * Returns the points of intersection between this {@link Polygon} and the
234
	 * Returns the points of intersection between this {@link Polygon} and the
214
	 * given {@link Ellipse} e.
235
	 * given {@link Ellipse} e.
Lines 650-658 Link Here
650
	 */
671
	 */
651
	@Override
672
	@Override
652
	public int hashCode() {
673
	public int hashCode() {
653
		// calculating a better hashCode is not possible
674
		// calculating a better hashCode is not possible, because due to the
675
		// imprecision, equals() is no longer transitive
654
		return 0;
676
		return 0;
655
		// return PointListUtils.getHashCode(points);
656
	}
677
	}
657
678
658
	/**
679
	/**
Lines 817-821 Link Here
817
		return translate(p.x, p.y);
838
		return translate(p.x, p.y);
818
	}
839
	}
819
840
841
	/**
842
	 * Returns the points of intersection between this {@link Polygon} and the
843
	 * given {@link QuadraticCurve} c.
844
	 * 
845
	 * @param c
846
	 * @return The points of intersection.
847
	 */
848
	public Point[] getIntersections(QuadraticCurve c) {
849
		HashSet<Point> intersections = new HashSet<Point>();
850
851
		for (Line seg : getSegments()) {
852
			for (Point poi : c.getIntersections(seg)) {
853
				intersections.add(poi);
854
			}
855
		}
856
857
		return intersections.toArray(new Point[] {});
858
	}
859
860
	/**
861
	 * Returns the points of intersection between this {@link Polygon} and the
862
	 * given {@link Arc} arc.
863
	 * 
864
	 * @param arc
865
	 *            The {@link Arc} to test for intersections
866
	 * @return the points of intersection.
867
	 */
868
	public Point[] getIntersections(Arc arc) {
869
		HashSet<Point> intersections = new HashSet<Point>();
870
871
		for (CubicCurve seg : arc.getBorderSegments()) {
872
			intersections.addAll(Arrays.asList(getIntersections(seg)));
873
		}
874
875
		return intersections.toArray(new Point[] {});
876
	}
877
878
	/**
879
	 * Returns the points of intersection between this {@link Polygon} and the
880
	 * given {@link CubicCurve} c.
881
	 * 
882
	 * @param c
883
	 * @return The points of intersection.
884
	 */
885
	public Point[] getIntersections(CubicCurve c) {
886
		HashSet<Point> intersections = new HashSet<Point>();
887
888
		for (Line seg : getSegments()) {
889
			for (Point poi : c.getIntersections(seg)) {
890
				intersections.add(poi);
891
			}
892
		}
893
894
		return intersections.toArray(new Point[] {});
895
	}
896
820
	// TODO: union point, rectangle, polygon, etc.
897
	// TODO: union point, rectangle, polygon, etc.
821
}
898
}
(-)src/org/eclipse/gef4/geometry/shapes/Polyline.java (-1 / +2 lines)
Lines 188-194 Link Here
188
	 */
188
	 */
189
	@Override
189
	@Override
190
	public int hashCode() {
190
	public int hashCode() {
191
		// calculating a better hashCode is not possible
191
		// calculating a better hashCode is not possible, because due to the
192
		// imprecision, equals() is no longer transitive
192
		return 0;
193
		return 0;
193
	}
194
	}
194
195
(-)src/org/eclipse/gef4/geometry/shapes/QuadraticCurve.java (-68 / +40 lines)
Lines 17-22 Link Here
17
17
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;
20
import org.eclipse.gef4.geometry.utils.PolynomCalculationUtils;
21
import org.eclipse.gef4.geometry.utils.PolynomCalculationUtils;
21
import org.eclipse.gef4.geometry.utils.PrecisionUtils;
22
import org.eclipse.gef4.geometry.utils.PrecisionUtils;
22
23
Lines 48-53 Link Here
48
	}
49
	}
49
50
50
	/**
51
	/**
52
	 * Constructs a new {@link QuadraticCurve} from the given sequence of
53
	 * {@link Point}s formed by start-, control-, and end-point.
54
	 * 
55
	 * @param points
56
	 *            the control {@link Point}s
57
	 * 
58
	 * @see QuadraticCurve#QuadraticCurve(Point, Point, Point)
59
	 */
60
	public QuadraticCurve(Point... points) {
61
		this(points[0], points[1], points[2]);
62
	}
63
64
	/**
51
	 * Constructs a new QuadraticCurve object from the given point coordinates.
65
	 * Constructs a new QuadraticCurve object from the given point coordinates.
52
	 * 
66
	 * 
53
	 * @param x1
67
	 * @param x1
Lines 71-76 Link Here
71
	}
85
	}
72
86
73
	/**
87
	/**
88
	 * Constructs a new {@link QuadraticCurve} from the given sequence of x- and
89
	 * y-coordinates of the start-, the control-, and the end-point.
90
	 * 
91
	 * @param coordinates
92
	 *            a sequence containing the x- and y-coordinates
93
	 * 
94
	 * @see QuadraticCurve#QuadraticCurve(double, double, double, double,
95
	 *      double, double)
96
	 */
97
	public QuadraticCurve(double... coordinates) {
98
		this(coordinates[0], coordinates[1], coordinates[2], coordinates[3],
99
				coordinates[4], coordinates[5]);
100
	}
101
102
	/**
74
	 * Get the control point.
103
	 * Get the control point.
75
	 * 
104
	 * 
76
	 * @return a Point object representing the control point
105
	 * @return a Point object representing the control point
Lines 161-167 Link Here
161
	 */
190
	 */
162
	@Override
191
	@Override
163
	public int hashCode() {
192
	public int hashCode() {
164
		// calculating a better hashCode is not possible
193
		// calculating a better hashCode is not possible, because due to the
194
		// imprecision, equals() is no longer transitive
165
		return 0;
195
		return 0;
166
	}
196
	}
167
197
Lines 270-292 Link Here
270
	 * @return the {@link Point} at parameter t
300
	 * @return the {@link Point} at parameter t
271
	 */
301
	 */
272
	public Point get(double t) {
302
	public Point get(double t) {
273
		if (!(PrecisionUtils.greaterEqual(t, 0) && PrecisionUtils.smallerEqual(
303
		return CurveUtils.get(this, t);
274
				t, 1))) {
275
			throw new IllegalArgumentException(
276
					"Paramter t is out of range! t = " + t + " !in_range(0,1)");
277
		}
278
279
		// compensate rounding effects
280
		if (t < 0) {
281
			t = 0;
282
		} else if (t > 1) {
283
			t = 1;
284
		}
285
286
		return new Point(t * (t * (getP1().x - 2 * getCtrl().x + getP2().x))
287
				+ 2 * (t * (getCtrl().x - getP1().x)) + getP1().x, t
288
				* (t * (getP1().y - 2 * getCtrl().y + getP2().y)) + 2
289
				* (t * (getCtrl().y - getP1().y)) + getP1().y);
290
	}
304
	}
291
305
292
	/**
306
	/**
Lines 297-315 Link Here
297
	 * @return true if p lies on the curve, false otherwise
311
	 * @return true if p lies on the curve, false otherwise
298
	 */
312
	 */
299
	public boolean contains(Point p) {
313
	public boolean contains(Point p) {
300
		// find roots of the x(t) - p.x function:
314
		return CurveUtils.contains(this, p);
301
		double[] xts = PolynomCalculationUtils.getQuadraticRoots(getX1() - 2
302
				* getCtrlX() + getX2(), 2 * getCtrlX() - 2 * getX1(), getX1()
303
				- p.x);
304
305
		for (double t : xts) {
306
			// t = PrecisionUtils.round(t);
307
			if (PrecisionUtils.greaterEqual(t, 0)
308
					&& PrecisionUtils.smallerEqual(t, 1)) {
309
				return true;
310
			}
311
		}
312
		return false;
313
	}
315
	}
314
316
315
	/**
317
	/**
Lines 344-357 Link Here
344
		// "Curves and Surfaces for Computer Aided Geometric Design" by Farin,
346
		// "Curves and Surfaces for Computer Aided Geometric Design" by Farin,
345
		// Gerald E., Academic Press 1988
347
		// Gerald E., Academic Press 1988
346
		controlPoints[0] = getP1();
348
		controlPoints[0] = getP1();
347
		controlPoints[1] = getP1().getScaled(1 / 3).getTranslated(
349
		controlPoints[1] = getP1().getScaled(1d / 3d).getTranslated(
348
				getCtrl().getScaled(2 / 3));
350
				getCtrl().getScaled(2d / 3d));
349
		controlPoints[2] = getCtrl().getScaled(2 / 3).getTranslated(
351
		controlPoints[2] = getCtrl().getScaled(2d / 3d).getTranslated(
350
				getP2().getScaled(1 / 3));
352
				getP2().getScaled(1d / 3d));
351
		controlPoints[3] = getP2();
353
		controlPoints[3] = getP2();
352
354
353
		return new CubicCurve(controlPoints[0], controlPoints[1],
355
		return new CubicCurve(controlPoints);
354
				controlPoints[2], controlPoints[3]);
355
	}
356
	}
356
357
357
	/**
358
	/**
Lines 585-594 Link Here
585
		return false;
586
		return false;
586
	}
587
	}
587
588
588
	private Point ratioPoint(Point p, Point q, double ratio) {
589
		return p.getTranslated(q.getTranslated(p.getNegated()).getScaled(ratio));
590
	}
591
592
	/**
589
	/**
593
	 * Splits this QuadraticCurve using the de Casteljau algorithm at parameter
590
	 * Splits this QuadraticCurve using the de Casteljau algorithm at parameter
594
	 * t into two separate QuadraticCurve objects. The returned
591
	 * t into two separate QuadraticCurve objects. The returned
Lines 600-618 Link Here
600
	 *         [0, t] 2. [t, 1]
597
	 *         [0, t] 2. [t, 1]
601
	 */
598
	 */
602
	public QuadraticCurve[] split(double t) {
599
	public QuadraticCurve[] split(double t) {
603
		if (t < 0 || t > 1) {
600
		return CurveUtils.split(this, t);
604
			throw new IllegalArgumentException(
605
					"Paramter t is out of range! t = " + t + " !in_range(0,1)");
606
		}
607
608
		Point left1 = ratioPoint(getP1(), getCtrl(), t);
609
		Point right1 = ratioPoint(getCtrl(), getP2(), t);
610
		Point split = ratioPoint(left1, right1, t);
611
612
		QuadraticCurve left = new QuadraticCurve(getP1(), left1, split);
613
		QuadraticCurve right = new QuadraticCurve(split, right1, getP2());
614
615
		return new QuadraticCurve[] { left, right };
616
	}
601
	}
617
602
618
	/**
603
	/**
Lines 625-644 Link Here
625
	 * @return the {@link QuadraticCurve} on the interval [t1, t2]
610
	 * @return the {@link QuadraticCurve} on the interval [t1, t2]
626
	 */
611
	 */
627
	public QuadraticCurve clip(double t1, double t2) {
612
	public QuadraticCurve clip(double t1, double t2) {
628
		if (t1 < 0 || t1 > 1) {
613
		return CurveUtils.clip(this, t1, t2);
629
			throw new IllegalArgumentException(
630
					"Paramter t1 is out of range! t1 = " + t1
631
							+ " !in_range(0,1)");
632
		}
633
		if (t2 < 0 || t2 > 1) {
634
			throw new IllegalArgumentException(
635
					"Paramter t2 is out of range! t2 = " + t2
636
							+ " !in_range(0,1)");
637
		}
638
639
		QuadraticCurve right = split(t1)[1];
640
		double rightT2 = (t2 - t1) / (1 - t1);
641
		return right.split(rightT2)[0];
642
	}
614
	}
643
615
644
}
616
}
(-)src/org/eclipse/gef4/geometry/shapes/Rectangle.java (-8 / +7 lines)
Lines 714-723 Link Here
714
	 */
714
	 */
715
	@Override
715
	@Override
716
	public int hashCode() {
716
	public int hashCode() {
717
		// calculating a better hashCode is not possible
717
		// calculating a better hashCode is not possible, because due to the
718
		// imprecision, equals() is no longer transitive
718
		return 0;
719
		return 0;
719
		// return (int) ((x + height + 1) * (y + width + 1)) ^ (int) x ^ (int)
720
		// y;
721
	}
720
	}
722
721
723
	/**
722
	/**
Lines 734-745 Link Here
734
		double x2 = Math.min(x + width, r.x + r.width);
733
		double x2 = Math.min(x + width, r.x + r.width);
735
		double y1 = Math.max(y, r.y);
734
		double y1 = Math.max(y, r.y);
736
		double y2 = Math.min(y + height, r.y + r.height);
735
		double y2 = Math.min(y + height, r.y + r.height);
737
		if (PrecisionUtils.smaller(x2 - x1, 0)
736
		if (PrecisionUtils.greaterEqual(x2 - x1, 0)
738
				|| PrecisionUtils.smaller(y2 - y1, 0))
737
				&& PrecisionUtils.greaterEqual(y2 - y1, 0)) {
739
			return setBounds(0, 0, 0, 0); // no intersection
738
			return setBounds(x1, y1, x2 - x1 < 0 ? 0 : x2 - x1, y2 - y1 < 0 ? 0
740
		else {
739
					: y2 - y1);
741
			return setBounds(x1, y1, x2 - x1, y2 - y1);
742
		}
740
		}
741
		return setBounds(0, 0, 0, 0); // no intersection
743
	}
742
	}
744
743
745
	/**
744
	/**
(-)src/org/eclipse/gef4/geometry/shapes/RoundedRectangle.java (-1 / +81 lines)
Lines 12-17 Link Here
12
 *******************************************************************************/
12
 *******************************************************************************/
13
package org.eclipse.gef4.geometry.shapes;
13
package org.eclipse.gef4.geometry.shapes;
14
14
15
import org.eclipse.gef4.geometry.Angle;
15
import org.eclipse.gef4.geometry.Point;
16
import org.eclipse.gef4.geometry.Point;
16
import org.eclipse.gef4.geometry.transform.AffineTransform;
17
import org.eclipse.gef4.geometry.transform.AffineTransform;
17
import org.eclipse.gef4.geometry.utils.PrecisionUtils;
18
import org.eclipse.gef4.geometry.utils.PrecisionUtils;
Lines 214-220 Link Here
214
	 */
215
	 */
215
	@Override
216
	@Override
216
	public int hashCode() {
217
	public int hashCode() {
217
		// calculating a better hashCode is not possible
218
		// calculating a better hashCode is not possible, because due to the
219
		// imprecision, equals() is no longer transitive
218
		return 0;
220
		return 0;
219
	}
221
	}
220
222
Lines 340-345 Link Here
340
		return path;
342
		return path;
341
	}
343
	}
342
344
345
	/**
346
	 * Returns the top edge of this {@link RoundedRectangle}.
347
	 * 
348
	 * @return the top edge of this {@link RoundedRectangle}.
349
	 */
350
	public Line getTop() {
351
		return new Line(x + arcWidth, y, x + width - arcWidth, y);
352
	}
353
354
	/**
355
	 * Returns the bottom edge of this {@link RoundedRectangle}.
356
	 * 
357
	 * @return the bottom edge of this {@link RoundedRectangle}.
358
	 */
359
	public Line getBottom() {
360
		return new Line(x + arcWidth, y + height - arcHeight, x + width
361
				- arcWidth, y + height - arcHeight);
362
	}
363
364
	/**
365
	 * Returns the right edge of this {@link RoundedRectangle}.
366
	 * 
367
	 * @return the right edge of this {@link RoundedRectangle}.
368
	 */
369
	public Line getRight() {
370
		return new Line(x + width, y + arcHeight, x + width, y + height
371
				- arcHeight);
372
	}
373
374
	/**
375
	 * Returns the left edge of this {@link RoundedRectangle}.
376
	 * 
377
	 * @return the left edge of this {@link RoundedRectangle}.
378
	 */
379
	public Line getLeft() {
380
		return new Line(x, y + arcHeight, x, y + height - arcHeight);
381
	}
382
383
	/**
384
	 * Returns the top left {@link Arc} of this {@link RoundedRectangle}.
385
	 * 
386
	 * @return the top left {@link Arc} of this {@link RoundedRectangle}.
387
	 */
388
	public Arc getTopLeft() {
389
		return new Arc(x, y, arcWidth, arcHeight, Angle.fromDeg(90),
390
				Angle.fromDeg(90));
391
	}
392
393
	/**
394
	 * Returns the top right {@link Arc} of this {@link RoundedRectangle}.
395
	 * 
396
	 * @return the top right {@link Arc} of this {@link RoundedRectangle}.
397
	 */
398
	public Arc getTopRight() {
399
		return new Arc(x + width - arcWidth, y, arcWidth, arcHeight,
400
				Angle.fromDeg(0), Angle.fromDeg(90));
401
	}
402
403
	/**
404
	 * Returns the bottom left {@link Arc} of this {@link RoundedRectangle}.
405
	 * 
406
	 * @return the bottom left {@link Arc} of this {@link RoundedRectangle}.
407
	 */
408
	public Arc getBottomLeft() {
409
		return new Arc(x, y + height - arcHeight, arcWidth, arcHeight,
410
				Angle.fromDeg(180), Angle.fromDeg(90));
411
	}
412
413
	/**
414
	 * Returns the bottom right {@link Arc} of this {@link RoundedRectangle}.
415
	 * 
416
	 * @return the bottom right {@link Arc} of this {@link RoundedRectangle}.
417
	 */
418
	public Arc getBottomRight() {
419
		return new Arc(x + width - arcWidth, y + height - arcHeight, arcWidth,
420
				arcHeight, Angle.fromDeg(270), Angle.fromDeg(90));
421
	}
422
343
	@Override
423
	@Override
344
	public String toString() {
424
	public String toString() {
345
		return "RoundedRectangle: (" + x + ", " + y + ", " + width + ", " + height + ", " + arcWidth + ", " + arcHeight; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$
425
		return "RoundedRectangle: (" + x + ", " + y + ", " + width + ", " + height + ", " + arcWidth + ", " + arcHeight; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$
(-)src/org/eclipse/gef4/geometry/utils/CurveUtils.java (+1253 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.utils;
13
14
import java.util.HashSet;
15
import java.util.Set;
16
17
import org.eclipse.gef4.geometry.Point;
18
import org.eclipse.gef4.geometry.euclidean.Straight;
19
import org.eclipse.gef4.geometry.shapes.CubicCurve;
20
import org.eclipse.gef4.geometry.shapes.Line;
21
import org.eclipse.gef4.geometry.shapes.QuadraticCurve;
22
import org.eclipse.gef4.geometry.shapes.Rectangle;
23
24
/**
25
 * The {@link CurveUtils} class provides functionality that can be used for
26
 * linear curves ({@link Line}, {@link Straight}), as well as quadratic and
27
 * cubic Bezier curves.
28
 * 
29
 * @author wienand
30
 * 
31
 */
32
public class CurveUtils {
33
34
	/**
35
	 * The Vector3D class implements a three dimensional vector (components x,
36
	 * y, z) with its standard operations: addition and multiplication (scalar,
37
	 * dot-product, cross-product).
38
	 * 
39
	 * It is used to represent planar lines and planar points which are
40
	 * represented by three dimensional planes and three dimensional lines
41
	 * through the origin, respectively.
42
	 * 
43
	 * @author wienand
44
	 */
45
	private static final class Vector3D {
46
		public double x, y, z;
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
72
		/**
73
		 * Returns a copy of this {@link Vector3D}.
74
		 * 
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
		}
193
194
		/**
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
	}
219
220
	/**
221
	 * The {@link BezierCurve} provides a common representation for arbitrary
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
	 * 
227
	 * It uses homogeneous coordinates (represented by {@link Vector3D} objects)
228
	 * to represent planar lines and points and to compute line/line
229
	 * intersections and point/line distances.
230
	 * 
231
	 * @author wienand
232
	 */
233
	private static final class BezierCurve {
234
235
		private static final double UNRECOGNIZABLE_PRECISION_FRACTION = PrecisionUtils
236
				.calculateFraction(0) / 10;
237
238
		private static final double END_POINTS_CHECK_AREA = 1;
239
240
		private Vector3D[] points;
241
242
		/**
243
		 * Constructs a new {@link BezierCurve} object from the given control
244
		 * points.
245
		 * 
246
		 * @param controlPoints
247
		 */
248
		public BezierCurve(Point... controlPoints) {
249
			points = new Vector3D[controlPoints.length];
250
			for (int i = 0; i < points.length; i++) {
251
				points[i] = new Vector3D(controlPoints[i].x,
252
						controlPoints[i].y, 1);
253
			}
254
		}
255
256
		/**
257
		 * Constructs a new {@link BezierCurve} object from the given control
258
		 * points.
259
		 * 
260
		 * Note that a Point(2, 3) is represented by a Vector3D(2, 3, 1). So for
261
		 * a Point(x, y) the corresponding vector is Vector(x, y, 1).
262
		 * 
263
		 * @param controlPoints
264
		 */
265
		public BezierCurve(Vector3D... controlPoints) {
266
			points = new Vector3D[controlPoints.length];
267
			for (int i = 0; i < points.length; i++) {
268
				points[i] = controlPoints[i].getCopy();
269
			}
270
		}
271
272
		/**
273
		 * Constructs a new {@link BezierCurve} from the given
274
		 * {@link QuadraticCurve}.
275
		 * 
276
		 * @param c
277
		 */
278
		public BezierCurve(QuadraticCurve c) {
279
			this(c.getP1(), c.getCtrl(), c.getP2());
280
		}
281
282
		/**
283
		 * Constructs a new {@link BezierCurve} from the given
284
		 * {@link CubicCurve}.
285
		 * 
286
		 * @param c
287
		 */
288
		public BezierCurve(CubicCurve c) {
289
			this(c.getP1(), c.getCtrl1(), c.getCtrl2(), c.getP2());
290
		}
291
292
		/**
293
		 * Returns a copy of this {@link BezierCurve}'s points.
294
		 * 
295
		 * @return a copy of this {@link BezierCurve}'s points
296
		 */
297
		private Vector3D[] getPointsCopy() {
298
			Vector3D[] copy = new Vector3D[points.length];
299
			for (int i = 0; i < points.length; i++) {
300
				copy[i] = points[i].getCopy();
301
			}
302
			return copy;
303
		}
304
305
		/**
306
		 * Constructs the explicit Bézier curve for this curve's x-components.
307
		 * 
308
		 * @return the explicit Bézier curve for this curve's x-components
309
		 */
310
		public BezierCurve getExplicitX() {
311
			Vector3D[] explicit = new Vector3D[points.length];
312
313
			for (int i = 0; i < points.length; i++) {
314
				explicit[i] = new Vector3D((double) i
315
						/ ((double) points.length - 1d), points[i].toPoint().x,
316
						1);
317
			}
318
319
			return new BezierCurve(explicit);
320
		}
321
322
		/**
323
		 * Constructs the explicit Bézier curve for this curve's y-components.
324
		 * 
325
		 * @return the explicit Bézier curve for this curve's y-components
326
		 */
327
		public BezierCurve getExplicitY() {
328
			Vector3D[] explicit = new Vector3D[points.length];
329
330
			for (int i = 0; i < points.length; i++) {
331
				explicit[i] = new Vector3D((double) i
332
						/ ((double) points.length - 1d), points[i].toPoint().y,
333
						1);
334
			}
335
336
			return new BezierCurve(explicit);
337
		}
338
339
		/**
340
		 * Checks if all y-components of this {@link BezierCurve}'s points have
341
		 * the same sign.
342
		 * 
343
		 * Returns true if either all y-components are positive or all
344
		 * y-components are negative.
345
		 * 
346
		 * Returns false, otherwise.
347
		 * 
348
		 * @param c
349
		 * @return true if all y-components are either positive or negative,
350
		 *         otherwise false
351
		 */
352
		private static boolean sameSign(BezierCurve c) {
353
			double sign = c.points[0].toPoint().y;
354
			if (sign == 0) {
355
				return false;
356
			}
357
			for (int i = 1; i < c.points.length; i++) {
358
				if (sign < 0) {
359
					if (c.points[i].toPoint().y >= 0) {
360
						return false;
361
					}
362
				} else if (sign > 0) {
363
					if (c.points[i].toPoint().y <= 0) {
364
						return false;
365
					}
366
				}
367
			}
368
			return true;
369
		}
370
371
		/**
372
		 * Used to store parameter values in a HashSet with an imprecise
373
		 * equals() operation.
374
		 * 
375
		 * @author wienand
376
		 */
377
		private static final class ImpreciseDouble {
378
			private double value;
379
			private int shift;
380
381
			public ImpreciseDouble(double a) {
382
				value = a;
383
				shift = 1;
384
			}
385
386
			/**
387
			 * Returns the double value represented by this
388
			 * {@link ImpreciseDouble}.
389
			 * 
390
			 * @return the double value represented by this
391
			 *         {@link ImpreciseDouble}
392
			 */
393
			public double getValue() {
394
				return value;
395
			}
396
397
			@Override
398
			public boolean equals(Object obj) {
399
				if (obj instanceof ImpreciseDouble) {
400
					ImpreciseDouble o = (ImpreciseDouble) obj;
401
					return PrecisionUtils.equal(value, o.value, shift);
402
				}
403
				return false;
404
			}
405
		}
406
407
		/**
408
		 * Calculates the roots of the given explicit {@link BezierCurve} on the
409
		 * interval [a;b].
410
		 * 
411
		 * You can get an explicit {@link BezierCurve} from an arbitrary
412
		 * {@link BezierCurve} for either its x- or y-components using the
413
		 * {@link BezierCurve#getExplicitX()} or
414
		 * {@link BezierCurve#getExplicitY()} methods, respectively.
415
		 * 
416
		 * @param c
417
		 * @param a
418
		 *            start of the parameter interval
419
		 * @param b
420
		 *            end of the parameter interval
421
		 * @return the roots of the given explicit {@link BezierCurve} on the
422
		 *         interval [a;b]
423
		 */
424
		private static HashSet<ImpreciseDouble> getRoots(BezierCurve c,
425
				double a, double b) {
426
			BezierCurve clipped = c.getClipped(a, b);
427
428
			if (sameSign(clipped)) {
429
				return new HashSet<ImpreciseDouble>();
430
			}
431
432
			if (PrecisionUtils.equal(a, b, +2)) {
433
				HashSet<ImpreciseDouble> root = new HashSet<ImpreciseDouble>();
434
				root.add(new ImpreciseDouble((a + b) / 2));
435
				return root;
436
			}
437
438
			HashSet<ImpreciseDouble> left = getRoots(c, a, (a + b) / 2);
439
			HashSet<ImpreciseDouble> right = getRoots(c, (a + b) / 2, b);
440
441
			left.addAll(right);
442
443
			return left;
444
		}
445
446
		/**
447
		 * Calculates the roots of the given {@link BezierCurve} which is
448
		 * expected to be explicit.
449
		 * 
450
		 * You can get an explicit {@link BezierCurve} from an arbitrary
451
		 * {@link BezierCurve} for either its x- or y-components using the
452
		 * {@link BezierCurve#getExplicitX()} or
453
		 * {@link BezierCurve#getExplicitY()} methods, respectively.
454
		 * 
455
		 * @param c
456
		 * @return the roots of the given explicit {@link BezierCurve}
457
		 */
458
		private static double[] getRoots(BezierCurve c) {
459
			// TODO: check that the given BezierCurve is explicit
460
			HashSet<ImpreciseDouble> roots = getRoots(c, 0, 1);
461
			ImpreciseDouble[] rootsFuzzyDouble = roots
462
					.toArray(new ImpreciseDouble[] {});
463
			double[] rootsDouble = new double[rootsFuzzyDouble.length];
464
			for (int i = 0; i < rootsDouble.length; i++) {
465
				rootsDouble[i] = rootsFuzzyDouble[i].getValue();
466
			}
467
			return rootsDouble;
468
		}
469
470
		/**
471
		 * Computes all real roots of this {@link BezierCurve}'s x(t) function.
472
		 * 
473
		 * @return all real roots of this {@link BezierCurve}'s x(t) function
474
		 */
475
		public double[] getRootsX() {
476
			return getRoots(getExplicitX());
477
		}
478
479
		/**
480
		 * Computes all real roots of this {@link BezierCurve}'s y(t) function.
481
		 * 
482
		 * @return all real roots of this {@link BezierCurve}'s y(t) function
483
		 */
484
		public double[] getRootsY() {
485
			return getRoots(getExplicitY());
486
		}
487
488
		/**
489
		 * Computes the real planar {@link Point}s for this {@link BezierCurve}.
490
		 * 
491
		 * @return the real planar {@link Point}s for this {@link BezierCurve}
492
		 */
493
		public Point[] getRealPoints() {
494
			Point[] realPoints = new Point[points.length];
495
			for (int i = 0; i < points.length; i++) {
496
				realPoints[i] = points[i].toPoint();
497
			}
498
			return realPoints;
499
		}
500
501
		/**
502
		 * Returns the {@link Point} at the given parameter value t.
503
		 * 
504
		 * @param t
505
		 *            Parameter value
506
		 * @return {@link Point} at parameter value t
507
		 */
508
		public Vector3D get(double t) {
509
			if (t < 0 || t > 1) {
510
				throw new IllegalArgumentException("t out of range");
511
			}
512
513
			// using horner's scheme:
514
			int n = points.length;
515
			if (n < 1) {
516
				return null;
517
			}
518
519
			double bn = 1, tn = 1, d = 1d - t;
520
			Vector3D pn = points[0].getScaled(bn * tn);
521
			for (int i = 1; i < n; i++) {
522
				bn = bn * (n - i) / i;
523
				tn = tn * t;
524
				pn = pn.getScaled(d).getAdded(points[i].getScaled(bn * tn));
525
			}
526
527
			return pn;
528
		}
529
530
		/**
531
		 * Creates a new {@link BezierCurve} with all points translated by the
532
		 * given {@link Point}.
533
		 * 
534
		 * @param p
535
		 * @return a new {@link BezierCurve} with all points translated by the
536
		 *         given {@link Point}
537
		 */
538
		public BezierCurve getTranslated(Point p) {
539
			Point[] translated = new Point[points.length];
540
541
			for (int i = 0; i < translated.length; i++) {
542
				translated[i] = points[i].toPoint().getTranslated(p);
543
			}
544
545
			return new BezierCurve(translated);
546
		}
547
548
		/**
549
		 * Returns true if the given {@link Point} lies on this
550
		 * {@link BezierCurve}. Returns false, otherwise.
551
		 * 
552
		 * @param p
553
		 *            the {@link Point} to test for containment
554
		 * @return true if the {@link Point} is contained, false otherwise
555
		 */
556
		public boolean contains(Point p) {
557
			if (p == null) {
558
				return false;
559
			}
560
561
			BezierCurve test = this.getTranslated(p.getNegated());
562
			double[] xts = test.getRootsX();
563
			double[] yts = test.getRootsY();
564
565
			for (double xt : xts) {
566
				for (double yt : yts) {
567
					if (PrecisionUtils.equal(xt, yt)) {
568
						return true;
569
					} else {
570
						Point qx = get(xt).toPoint();
571
						Point qy = get(yt).toPoint();
572
						// qx != null && qy != null &&
573
						if (qx.equals(qy)) {
574
							return true;
575
						}
576
					}
577
				}
578
			}
579
			return false;
580
		}
581
582
		/**
583
		 * Subdivides this {@link BezierCurve} at the given parameter value t
584
		 * into two new {@link BezierCurve}. The left-of t and the right-of t
585
		 * {@link BezierCurve} objects.
586
		 * 
587
		 * @param t
588
		 *            Parameter value
589
		 * @return The left-of t and right-of t {@link BezierCurve} objects
590
		 */
591
		public BezierCurve[] split(double t) {
592
			Vector3D[] leftPoints = new Vector3D[points.length];
593
			Vector3D[] rightPoints = new Vector3D[points.length];
594
595
			Vector3D[] ratioPoints = getPointsCopy();
596
597
			for (int i = 0; i < points.length; i++) {
598
				leftPoints[i] = ratioPoints[0];
599
				rightPoints[points.length - 1 - i] = ratioPoints[points.length
600
						- 1 - i];
601
602
				for (int j = 0; j < points.length - i - 1; j++) {
603
					ratioPoints[j] = ratioPoints[j].getRatio(
604
							ratioPoints[j + 1], t);
605
				}
606
			}
607
608
			return new BezierCurve[] { new BezierCurve(leftPoints),
609
					new BezierCurve(rightPoints) };
610
		}
611
612
		/**
613
		 * Returns a new {@link BezierCurve} object representing this bezier
614
		 * curve on the interval [s;e].
615
		 * 
616
		 * @param s
617
		 * @param e
618
		 * @return a new {@link BezierCurve} object representing this bezier
619
		 *         curve on the interval [s;e]
620
		 */
621
		public BezierCurve getClipped(double s, double e) {
622
			BezierCurve right = split(s)[1];
623
			double rightT2 = (e - s) / (1 - s);
624
			return right.split(rightT2)[0];
625
		}
626
627
		/**
628
		 * Checks if the parameters are considered equal on both curves and adds
629
		 * the point of intersection of the mid lines of the curves.
630
		 * 
631
		 * @param p
632
		 * @param q
633
		 * @param intersections
634
		 * @return true if the parameters are considered equal and false
635
		 *         otherwise
636
		 */
637
		private static Vector3D parameterConvergence(BezierCurve p, double ps,
638
				double pe, BezierCurve q, double qs, double qe) {
639
			// localEndPointsCheck();
640
			if (PrecisionUtils.equal(ps, pe, +2)) {
641
				Vector3D poi = p.get((ps + pe) / 2);
642
				return poi;
643
			}
644
			if (PrecisionUtils.equal(qs, qe, +2)) {
645
				Vector3D poi = q.get((qs + qe) / 2);
646
				return poi;
647
			}
648
			return null;
649
		}
650
651
		/**
652
		 * Returns the bounds of the control polygon of this {@link BezierCurve}
653
		 * .
654
		 * 
655
		 * @return a {@link Rectangle} representing the bounds of the control
656
		 *         polygon of this {@link BezierCurve}
657
		 */
658
		public Rectangle getControlBounds() {
659
			Point[] realPoints = getRealPoints();
660
661
			double xmin = realPoints[0].x, xmax = realPoints[0].x, ymin = realPoints[0].y, ymax = realPoints[0].y;
662
663
			for (int i = 1; i < realPoints.length; i++) {
664
				if (realPoints[i].x < xmin) {
665
					xmin = realPoints[i].x;
666
				} else if (realPoints[i].x > xmax) {
667
					xmax = realPoints[i].x;
668
				}
669
670
				if (realPoints[i].y < ymin) {
671
					ymin = realPoints[i].y;
672
				} else if (realPoints[i].y > ymax) {
673
					ymax = realPoints[i].y;
674
				}
675
			}
676
677
			return new Rectangle(xmin, ymin, xmax - xmin, ymax - ymin);
678
		}
679
680
		/**
681
		 * Generates the difference points of this {@link BezierCurve} to the
682
		 * given line.
683
		 * 
684
		 * The difference points are the control points of a Bezier curve that
685
		 * yields the signed difference of the point on this curve at a
686
		 * determinate parameter value to the given line.
687
		 * 
688
		 * @param line
689
		 * @return the difference curve's control points
690
		 */
691
		private Vector3D[] genDifferencePoints(Vector3D line) {
692
			Vector3D[] D = new Vector3D[points.length];
693
			for (int i = 0; i < points.length; i++) {
694
				double y = line.getDot(points[i]);
695
				D[i] = new Vector3D(
696
						(double) (i) / (double) (points.length - 1), y, 1);
697
			}
698
			return D;
699
		}
700
701
		public Set<Vector3D> getIntersections(BezierCurve other) {
702
			// end point intersections
703
			Set<Vector3D> endPointIntersections = getEndPointIntersections(
704
					this, other);
705
706
			// TODO: tangential intersections
707
708
			// simple intersections
709
			// TODO: recursion => iteration
710
			Set<Vector3D> intersections = getIntersections(
711
					endPointIntersections, this, 0, 1, other, 0, 1);
712
713
			intersections.addAll(endPointIntersections);
714
			return intersections;
715
		}
716
717
		private Set<Vector3D> getEndPointIntersections(BezierCurve p,
718
				BezierCurve q) {
719
			Set<Vector3D> intersections = new HashSet<Vector3D>();
720
			for (int i : new int[] { 0, p.points.length - 1 }) {
721
				if (q.contains(p.points[i].toPoint())) {
722
					intersections.add(p.points[i]);
723
				}
724
			}
725
			for (int i : new int[] { 0, q.points.length - 1 }) {
726
				if (p.contains(q.points[i].toPoint())) {
727
					intersections.add(q.points[i]);
728
				}
729
			}
730
			return intersections;
731
		}
732
733
		private static class FatLine {
734
			public Vector3D line;
735
			public double dmin, dmax;
736
737
			private FatLine() {
738
				line = new Vector3D(0, 0, 0);
739
				dmin = dmax = 0;
740
			}
741
742
			public static FatLine from(BezierCurve c, boolean ortho) {
743
				FatLine L = new FatLine();
744
				L.dmin = L.dmax = 0;
745
746
				L.line = c.points[0].getCrossed(c.points[c.points.length - 1]);
747
748
				if (ortho) {
749
					L.line = c.points[0].getCrossed(c.points[0]
750
							.getAdded(new Vector3D(L.line.x, L.line.y, 0)));
751
				}
752
753
				L.line = L.line.getLineNormalized();
754
755
				if (L.line == null) {
756
					return null;
757
				}
758
759
				for (int i = 0; i < c.points.length; i++) {
760
					double d = L.line.getDot(c.points[i]);
761
					if (d < L.dmin)
762
						L.dmin = d;
763
					else if (d > L.dmax)
764
						L.dmax = d;
765
				}
766
767
				return L;
768
			}
769
		}
770
771
		private static double intersectXAxisParallel(Point p, Point q, double y) {
772
			// p.y != q.y because this routine is only called when either the
773
			// lower or the higher fat line bound is crossed.
774
			return new Vector3D(p).getCrossed(new Vector3D(q))
775
					.getCrossed(new Vector3D(0, 1, -y)).toPoint().x;
776
			// double dy = q.y - p.y;
777
			// double s = (y - p.y) / dy;
778
			// return (q.x - p.x) * s + p.x;
779
		}
780
781
		private static Point[] getConvexHull(Vector3D[] points) {
782
			Point[] chPoints = new Point[points.length];
783
			for (int i = 0; i < points.length; i++) {
784
				chPoints[i] = points[i].toPoint();
785
			}
786
			return PointListUtils.getConvexHull(chPoints);
787
		}
788
789
		/**
790
		 * @param L
791
		 * @return
792
		 */
793
		private double[] clipTo(FatLine L) {
794
			double[] interval = new double[] { 1, 0 };
795
796
			Point[] D = getConvexHull(genDifferencePoints(L.line));
797
798
			// we do not know which point is returned first by the
799
			// getConvexHull() method. That's why we have to check the "first"
800
			// point, too.
801
			boolean isBelow = D[0].y < L.dmin;
802
			boolean isAbove = D[0].y > L.dmax;
803
			insideFatLineCheck(interval, D, 0, isBelow, isAbove);
804
805
			boolean wasBelow = isBelow, wasAbove = isAbove;
806
807
			for (int i = 1; i < D.length; i++) {
808
				isBelow = D[i].y < L.dmin;
809
				isAbove = D[i].y > L.dmax;
810
811
				insideFatLineCheck(interval, D, i, isBelow, isAbove);
812
				wasBelow = belowFatLineCheck(interval, L, D, i - 1, i, isBelow,
813
						wasBelow);
814
				wasAbove = aboveFatLineCheck(interval, L, D, i - 1, i, isAbove,
815
						wasAbove);
816
			}
817
818
			// closing segment
819
			isBelow = D[0].y < L.dmin;
820
			isAbove = D[0].y > L.dmax;
821
			belowFatLineCheck(interval, L, D, D.length - 1, 0, isBelow,
822
					wasBelow);
823
			aboveFatLineCheck(interval, L, D, D.length - 1, 0, isAbove,
824
					wasAbove);
825
826
			return interval;
827
		}
828
829
		private boolean aboveFatLineCheck(double[] interval, FatLine L,
830
				Point[] D, int i, int j, boolean isAbove, boolean wasAbove) {
831
			if (isAbove != wasAbove) {
832
				// crosses higher
833
				double x = intersectXAxisParallel(D[i], D[j], L.dmax);
834
				moveInterval(interval, x);
835
				wasAbove = isAbove;
836
			}
837
			return wasAbove;
838
		}
839
840
		private boolean belowFatLineCheck(double[] interval, FatLine L,
841
				Point[] D, int i, int j, boolean isBelow, boolean wasBelow) {
842
			if (isBelow != wasBelow) {
843
				// crosses lower
844
				double x = intersectXAxisParallel(D[i], D[j], L.dmin);
845
				moveInterval(interval, x);
846
				wasBelow = isBelow;
847
			}
848
			return wasBelow;
849
		}
850
851
		private void insideFatLineCheck(double[] interval, Point[] D, int i,
852
				boolean isBelow, boolean isAbove) {
853
			if (!(isBelow || isAbove)) {
854
				// inside
855
				moveInterval(interval, D[i].x);
856
			}
857
		}
858
859
		private void moveInterval(double[] interval, double x) {
860
			if (interval[0] > x)
861
				interval[0] = x;
862
			if (interval[1] < x)
863
				interval[1] = x;
864
		}
865
866
		/**
867
		 * Computes and returns the points of intersection between this
868
		 * {@link BezierCurve} and the given other {@link BezierCurve}.
869
		 * 
870
		 * @param endPointIntersections
871
		 *            all points of intersections that are end-points of one of
872
		 *            the curves
873
		 * @param p
874
		 *            first {@link BezierCurve}
875
		 * @param ps
876
		 *            start value of the first {@link BezierCurve}'s parameter
877
		 *            interval
878
		 * @param pe
879
		 *            end value of the first {@link BezierCurve}'s parameter
880
		 *            interval
881
		 * @param q
882
		 *            second {@link BezierCurve}
883
		 * @param qs
884
		 *            start value of the second {@link BezierCurve}'s parameter
885
		 *            interval
886
		 * @param qe
887
		 *            end value of the second {@link BezierCurve}'s parameter
888
		 *            interval
889
		 * @return the intersections between this {@link BezierCurve} and the
890
		 *         given other {@link BezierCurve}
891
		 */
892
		@SuppressWarnings("unchecked")
893
		public static Set<Vector3D> getIntersections(
894
				Set<Vector3D> endPointIntersections, BezierCurve p, double ps,
895
				double pe, BezierCurve q, double qs, double qe) {
896
			BezierCurve pClipped = p.getClipped(ps, pe);
897
			BezierCurve qClipped = q.getClipped(qs, qe);
898
899
			// end point intersection check
900
			if (endPointIntersectionConvergence(endPointIntersections,
901
					pClipped, ps, pe, qClipped, qs, qe)) {
902
				Set<Vector3D> no_intersections = new HashSet<Vector3D>(0);
903
				return no_intersections;
904
			}
905
906
			// TODO: tangential intersection check
907
908
			// parameter convergence check
909
			Vector3D poi = parameterConvergence(p, ps, pe, q, qs, qe);
910
			if (poi != null) {
911
				// "exactly" one intersection
912
				if (p.contains(poi.toPoint()) && q.contains(poi.toPoint())) {
913
					Set<Vector3D> intersection = new HashSet<Vector3D>(1);
914
					intersection.add(poi);
915
					return intersection;
916
				}
917
				Set<Vector3D> no_intersections = new HashSet<Vector3D>(0);
918
				return no_intersections;
919
			}
920
921
			Set<Vector3D> intersections = new HashSet<Vector3D>();
922
923
			// construct "parallel" and "orthogonal" fat lines
924
			FatLine L1 = FatLine.from(qClipped, false);
925
			FatLine L2 = FatLine.from(qClipped, true);
926
927
			// curve implosion check
928
			if (L1 == null || L2 == null) {
929
				// qClipped is too small to construct a fat line from it
930
				// therefore, return its mid-point if it is contained by the
931
				// other curve
932
				Vector3D mid = q.get((qs + qe) / 2);
933
				if (p.contains(mid.toPoint())) {
934
					Set<Vector3D> intersection = new HashSet<Vector3D>(1);
935
					intersection.add(mid);
936
					return intersection;
937
				}
938
				Set<Vector3D> no_intersections = new HashSet<Vector3D>(0);
939
				return no_intersections;
940
			}
941
942
			// clip to the fat lines
943
			double[] interval = pClipped.clipTo(L1);
944
			double[] interval_ortho = pClipped.clipTo(L2);
945
946
			// pick smaller interval range
947
			if ((interval[1] - interval[0]) > (interval_ortho[1] - interval_ortho[0])) {
948
				interval[0] = interval_ortho[0];
949
				interval[1] = interval_ortho[1];
950
			}
951
952
			// re-calculate s and e from the clipped interval
953
			double news = ps + interval[0] * (pe - ps);
954
			double newe = ps + interval[1] * (pe - ps);
955
			double ratio = (newe - news) / (pe - ps);
956
			ps = news;
957
			pe = newe;
958
959
			if (ratio < 0) {
960
				// no more intersections
961
				return intersections;
962
			} else if (ratio > 0.8) {
963
				// split longer curve and find intersections for both halves
964
				if ((pe - ps) > (qe - qs)) {
965
					double pm = (ps + pe) / 2;
966
					intersections.addAll(getIntersections(
967
							endPointIntersections, p, ps, pm, q, qs, qe));
968
					intersections.addAll(getIntersections(
969
							endPointIntersections, p, pm
970
									+ UNRECOGNIZABLE_PRECISION_FRACTION, pe, q,
971
							qs, qe));
972
				} else {
973
					double qm = (qs + qe) / 2;
974
					intersections.addAll(getIntersections(
975
							endPointIntersections, q, qs, qm, p, ps, pe));
976
					intersections.addAll(getIntersections(
977
							endPointIntersections, q, qm
978
									+ UNRECOGNIZABLE_PRECISION_FRACTION, qe, p,
979
							ps, pe));
980
				}
981
982
				return intersections;
983
			} else {
984
				// clipped more than 20%
985
				return getIntersections(endPointIntersections, q, qs, qe, p,
986
						ps, pe);
987
			}
988
		}
989
990
		private static boolean endPointIntersectionConvergence(
991
				Set<Vector3D> endPointIntersections, BezierCurve pClipped,
992
				double ps, double pe, BezierCurve qClipped, double qs, double qe) {
993
			if (PrecisionUtils.equal(ps, pe, -3)) {
994
				if (endPointIntersections.contains(pClipped.points[0])
995
						|| endPointIntersections
996
								.contains(pClipped.points[pClipped.points.length - 1])) {
997
					return true;
998
				}
999
			}
1000
			if (PrecisionUtils.equal(qs, qe, -3)) {
1001
				if (endPointIntersections.contains(qClipped.points[0])
1002
						|| endPointIntersections
1003
								.contains(qClipped.points[qClipped.points.length - 1])) {
1004
					return true;
1005
				}
1006
			}
1007
			return false;
1008
		}
1009
	}
1010
1011
	/**
1012
	 * Computes and returns the points of intersection between two
1013
	 * {@link CubicCurve}s.
1014
	 * 
1015
	 * @param p
1016
	 *            the first {@link CubicCurve} to intersect
1017
	 * @param q
1018
	 *            the second {@link CubicCurve} to intersect
1019
	 * @return the intersections between two {@link CubicCurve}s
1020
	 */
1021
	public static Point[] getIntersections(CubicCurve p, CubicCurve q) {
1022
		Set<CurveUtils.Vector3D> intersections = new BezierCurve(p)
1023
				.getIntersections(new BezierCurve(q));
1024
1025
		Set<Point> pois = new HashSet<Point>();
1026
		for (CurveUtils.Vector3D poi : intersections) {
1027
			if (poi.z != 0) {
1028
				pois.add(poi.toPoint());
1029
			}
1030
		}
1031
1032
		return pois.toArray(new Point[] {});
1033
	}
1034
1035
	/**
1036
	 * Computes the {@link Point} of intersection of two {@link Straight}s.
1037
	 * 
1038
	 * @param s1
1039
	 *            the first {@link Straight} to test for intersection
1040
	 * @param s2
1041
	 *            the second {@link Straight} to test for intersection
1042
	 * @return the {@link Point} of intersection if it exists, <code>null</code>
1043
	 *         otherwise
1044
	 */
1045
	public static Point getIntersection(Straight s1, Straight s2) {
1046
		Vector3D l1 = new Vector3D(s1.position.toPoint())
1047
				.getCrossed(new Vector3D(s1.position.getAdded(s1.direction)
1048
						.toPoint()));
1049
		Vector3D l2 = new Vector3D(s2.position.toPoint())
1050
				.getCrossed(new Vector3D(s2.position.getAdded(s2.direction)
1051
						.toPoint()));
1052
1053
		return l1.getCrossed(l2).toPoint();
1054
	}
1055
1056
	/**
1057
	 * Computes the signed distance of the third {@link Point} to the line
1058
	 * through the first two {@link Point}s.
1059
	 * 
1060
	 * The signed distance is positive if the three {@link Point}s are in
1061
	 * counter-clockwise order and negative if the {@link Point}s are in
1062
	 * clockwise order. It is zero if the third {@link Point} lies on the line.
1063
	 * 
1064
	 * If the first two {@link Point}s do not form a line (i.e. they are equal)
1065
	 * this function returns the distance of the first and the last
1066
	 * {@link Point}.
1067
	 * 
1068
	 * @param p
1069
	 *            the start-{@link Point} of the line
1070
	 * @param q
1071
	 *            the end-{@link Point} of the line
1072
	 * @param r
1073
	 *            the relative {@link Point} to test for
1074
	 * @return the signed distance of {@link Point} r to the line through
1075
	 *         {@link Point}s p and q
1076
	 */
1077
	public static double getSignedDistance(Point p, Point q, Point r) {
1078
		Vector3D normalizedLine = new Vector3D(p).getCrossed(new Vector3D(q))
1079
				.getLineNormalized();
1080
1081
		if (normalizedLine == null) {
1082
			return p.getDistance(r);
1083
		}
1084
1085
		double dot = normalizedLine.getDot(new Vector3D(r));
1086
		return -dot;
1087
	}
1088
1089
	/**
1090
	 * Returns the signed distance of the {@link Point} to the {@link Straight}.
1091
	 * 
1092
	 * {@link Point}s that are to the left of the {@link Straight} in the
1093
	 * direction of the {@link Straight}'s direction vector have a positive
1094
	 * distance whereas {@link Point}s to the right of the {@link Straight} in
1095
	 * the direction of the {@link Straight}'s direction vector have a negative
1096
	 * distance.
1097
	 * 
1098
	 * The absolute value of the signed distance is the actual distance of the
1099
	 * {@link Point} to the {@link Straight}.
1100
	 * 
1101
	 * @param s
1102
	 * @param p
1103
	 * @return the signed distance of the {@link Point} to the {@link Straight}
1104
	 * 
1105
	 * @see CurveUtils#getSignedDistance(Point, Point, Point)
1106
	 */
1107
	public static double getSignedDistance(Straight s, Point p) {
1108
		return getSignedDistance(s.position.toPoint(),
1109
				s.position.getAdded(s.direction).toPoint(), p);
1110
	}
1111
1112
	/**
1113
	 * Tests if the given {@link Point} lies on the given {@link CubicCurve}.
1114
	 * 
1115
	 * @param c
1116
	 *            the {@link CubicCurve} to test
1117
	 * @param p
1118
	 *            the {@link Point} to test for containment
1119
	 * @return true if the {@link Point} lies on the {@link CubicCurve}, false
1120
	 *         otherwise
1121
	 */
1122
	public static boolean contains(CubicCurve c, Point p) {
1123
		return new BezierCurve(c).contains(p);
1124
	}
1125
1126
	/**
1127
	 * Tests if the given {@link Point} lies on the given {@link QuadraticCurve}
1128
	 * .
1129
	 * 
1130
	 * @param c
1131
	 *            the {@link QuadraticCurve} to test
1132
	 * @param p
1133
	 *            the {@link Point} to test for containment
1134
	 * @return true if the {@link Point} lies on the {@link QuadraticCurve},
1135
	 *         false otherwise
1136
	 */
1137
	public static boolean contains(QuadraticCurve c, Point p) {
1138
		return new BezierCurve(c).contains(p);
1139
	}
1140
1141
	/**
1142
	 * Subdivides the given {@link CubicCurve} at parameter value t in the left
1143
	 * and right sub-curves.
1144
	 * 
1145
	 * @param c
1146
	 *            the {@link CubicCurve} to subdivide
1147
	 * @param t
1148
	 *            the parameter value to subdivide at
1149
	 * @return the left and right sub-curves as an array of {@link CubicCurve}
1150
	 */
1151
	public static CubicCurve[] split(CubicCurve c, double t) {
1152
		BezierCurve[] split = new BezierCurve(c).split(t);
1153
		return new CubicCurve[] { new CubicCurve(split[0].getRealPoints()),
1154
				new CubicCurve(split[1].getRealPoints()) };
1155
	}
1156
1157
	/**
1158
	 * Subdivides the given {@link QuadraticCurve} at parameter value t in the
1159
	 * left and right sub-curves.
1160
	 * 
1161
	 * @param c
1162
	 *            the {@link QuadraticCurve} to subdivide
1163
	 * @param t
1164
	 *            the parameter value to subdivide at
1165
	 * @return the left and right sub-curves as an array of
1166
	 *         {@link QuadraticCurve}
1167
	 */
1168
	public static QuadraticCurve[] split(QuadraticCurve c, double t) {
1169
		BezierCurve[] split = new BezierCurve(c).split(t);
1170
		return new QuadraticCurve[] {
1171
				new QuadraticCurve(split[0].getRealPoints()),
1172
				new QuadraticCurve(split[1].getRealPoints()) };
1173
	}
1174
1175
	/**
1176
	 * Returns a new {@link QuadraticCurve} that represents the given
1177
	 * {@link QuadraticCurve} on the parameter interval [t1;t2].
1178
	 * 
1179
	 * @param c
1180
	 *            the {@link QuadraticCurve} to clip
1181
	 * @param t1
1182
	 *            lower parameter bound
1183
	 * @param t2
1184
	 *            upper parameter bound
1185
	 * @return a new {@link QuadraticCurve} that represents the given
1186
	 *         {@link QuadraticCurve} on the interval [t1;t2]
1187
	 */
1188
	public static QuadraticCurve clip(QuadraticCurve c, double t1, double t2) {
1189
		BezierCurve bc = new BezierCurve(c);
1190
		return new QuadraticCurve(bc.getClipped(t1, t2).getRealPoints());
1191
	}
1192
1193
	/**
1194
	 * Returns a new {@link CubicCurve} that represents the given
1195
	 * {@link CubicCurve} on the parameter interval [t1;t2].
1196
	 * 
1197
	 * @param c
1198
	 *            the {@link CubicCurve} to clip
1199
	 * @param t1
1200
	 *            lower parameter bound
1201
	 * @param t2
1202
	 *            upper parameter bound
1203
	 * @return a new {@link CubicCurve} that represents the given
1204
	 *         {@link CubicCurve} on the interval [t1;t2]
1205
	 */
1206
	public static CubicCurve clip(CubicCurve c, double t1, double t2) {
1207
		BezierCurve bc = new BezierCurve(c);
1208
		return new CubicCurve(bc.getClipped(t1, t2).getRealPoints());
1209
	}
1210
1211
	/**
1212
	 * Evaluates the {@link Point} on the given {@link CubicCurve} at parameter
1213
	 * value t.
1214
	 * 
1215
	 * @param c
1216
	 *            the {@link CubicCurve}
1217
	 * @param t
1218
	 *            the parameter value
1219
	 * @return the {@link Point} on the given {@link CubicCurve} at parameter
1220
	 *         value t
1221
	 */
1222
	public static Point get(CubicCurve c, double t) {
1223
		return new BezierCurve(c).get(t).toPoint();
1224
	}
1225
1226
	/**
1227
	 * Evaluates the {@link Point} on the given {@link QuadraticCurve} at
1228
	 * parameter value t.
1229
	 * 
1230
	 * @param c
1231
	 *            the {@link QuadraticCurve}
1232
	 * @param t
1233
	 *            the parameter value
1234
	 * @return the {@link Point} on the given {@link QuadraticCurve} at
1235
	 *         parameter value t
1236
	 */
1237
	public static Point get(QuadraticCurve c, double t) {
1238
		return new BezierCurve(c).get(t).toPoint();
1239
	}
1240
1241
	/**
1242
	 * Computes and returns the bounds of the control polygon of the given
1243
	 * {@link CubicCurve}. The control polygon is the convex hull of the start-,
1244
	 * end-, and control points of the {@link CubicCurve}.
1245
	 * 
1246
	 * @param c
1247
	 *            the {@link CubicCurve} to compute the control bounds for
1248
	 * @return the bounds of the control polygon of the given {@link CubicCurve}
1249
	 */
1250
	public static Rectangle getControlBounds(CubicCurve c) {
1251
		return new BezierCurve(c).getControlBounds();
1252
	}
1253
}
(-)src/org/eclipse/gef4/geometry/utils/PointListUtils.java (+97 lines)
Lines 11-16 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;
17
14
import org.eclipse.gef4.geometry.Point;
18
import org.eclipse.gef4.geometry.Point;
15
import org.eclipse.gef4.geometry.shapes.Line;
19
import org.eclipse.gef4.geometry.shapes.Line;
16
import org.eclipse.gef4.geometry.shapes.Polygon;
20
import org.eclipse.gef4.geometry.shapes.Polygon;
Lines 108-113 Link Here
108
		return hashCode;
112
		return hashCode;
109
	}
113
	}
110
114
115
	private static void swapLowestYPointToStart(Point[] points) {
116
		int minIdx = 0;
117
		Point min = points[minIdx];
118
		for (int i = 1; i < points.length; i++) {
119
			if (points[i].y < min.y || points[i].y == min.y
120
					&& points[i].x < min.x) {
121
				min = points[i];
122
				minIdx = i;
123
			}
124
		}
125
		Point tmp = points[0];
126
		points[0] = points[minIdx];
127
		points[minIdx] = tmp;
128
	}
129
130
	private static void sortPointsByAngleToStart(Point[] points) {
131
		final Point p0 = points[0];
132
		Arrays.sort(points, 1, points.length, new Comparator<Point>() {
133
			public int compare(Point p1, Point p2) {
134
				double m1 = (p1.x - p0.x) / (-p1.y + p0.y);
135
				double m2 = (p2.x - p0.x) / (-p2.y + p0.y);
136
				if (m1 < m2) {
137
					return -1;
138
				}
139
				return 1;
140
			}
141
		});
142
	}
143
144
	private static ArrayList<Point> initializeStack(Point[] points) {
145
		ArrayList<Point> stack = new ArrayList<Point>();
146
		if (points.length > 0) {
147
			stack.add(0, points[0]);
148
			if (points.length > 1) {
149
				stack.add(0, points[1]);
150
				if (points.length > 2) {
151
					stack.add(0, points[2]);
152
				}
153
			}
154
		}
155
		return stack;
156
	}
157
158
	private static void expandToFullConvexHull(ArrayList<Point> stack,
159
			Point[] points) {
160
		for (int i = 3; i < points.length; i++) {
161
			// do always turn right
162
			while (stack.size() > 2
163
					&& CurveUtils.getSignedDistance(stack.get(1), stack.get(0),
164
							points[i]) > 0) {
165
				stack.remove(0);
166
			}
167
			stack.add(0, points[i]);
168
		}
169
	}
170
171
	/**
172
	 * Computes the convex hull of the given set of {@link Point}s using the
173
	 * Graham scan algorithm.
174
	 * 
175
	 * @param points
176
	 *            the set of {@link Point}s to calculate the convex hull for
177
	 * @return the convex hull of the given set of {@link Point}s
178
	 */
179
	public static Point[] getConvexHull(Point[] points) {
180
		// do a graham scan to find the convex hull of the given set of points
181
		swapLowestYPointToStart(points);
182
		sortPointsByAngleToStart(points);
183
		ArrayList<Point> convexHull = initializeStack(points);
184
		expandToFullConvexHull(convexHull, points);
185
		return convexHull.toArray(new Point[] {});
186
	}
187
111
	/**
188
	/**
112
	 * Converts a given array of {@link Point} into an array of doubles
189
	 * Converts a given array of {@link Point} into an array of doubles
113
	 * containing the x and y coordinates of the given points, where the x and y
190
	 * containing the x and y coordinates of the given points, where the x and y
Lines 129-134 Link Here
129
	}
206
	}
130
207
131
	/**
208
	/**
209
	 * Converts a given array of x/y coordinate values into an array of
210
	 * {@link Point}s.
211
	 * 
212
	 * @param coordinates
213
	 * @return a new array of {@link Point}s, representing the given x and y
214
	 *         coordinates
215
	 */
216
	public static Point[] toPointsArray(double[] coordinates) {
217
		if (coordinates.length % 2 != 0) {
218
			throw new IllegalArgumentException(
219
					"The coordinates array may not have an odd number of items.");
220
		}
221
		Point[] points = new Point[coordinates.length / 2];
222
		for (int i = 0; i < points.length; i++) {
223
			points[i] = new Point(coordinates[2 * i], coordinates[2 * i + 1]);
224
		}
225
		return points;
226
	}
227
228
	/**
132
	 * Converts an array of double values into an array of integer values by
229
	 * Converts an array of double values into an array of integer values by
133
	 * casting them.
230
	 * casting them.
134
	 * 
231
	 * 
(-)src/org/eclipse/gef4/geometry/utils/PolynomCalculationUtils.java (-25 / +20 lines)
Lines 79-112 Link Here
79
	 */
79
	 */
80
	public static final double[] getCubicRoots(double A, double B, double C,
80
	public static final double[] getCubicRoots(double A, double B, double C,
81
			double D) {
81
			double D) {
82
		// TODO: use an algorithm that abstracts the polynom's order. A
83
		// possibility would be to use the CurveUtils$BezierCurve#contains(Point
84
		// p) method.
85
82
		if (A == 0) {
86
		if (A == 0) {
83
			return getQuadraticRoots(B, C, D);
87
			return getQuadraticRoots(B, C, D);
84
		}
88
		}
85
89
86
		double a = B / A;
90
		double b = B / A;
87
		double b = C / A;
91
		double c = C / A;
88
		double c = D / A;
92
		double d = D / A;
89
93
90
		// reduce to z^3 + pz + q = 0 by substituting x = z - a/3
94
		// reduce to t^3 + pt + q = 0 by substituting x = t - b/3
91
		// (http://en.wikipedia.org/wiki/Cubic_function#Cardano.27s_method)
95
		// (http://en.wikipedia.org/wiki/Cubic_function#Cardano.27s_method)
96
		double p = c - b * b / 3;
97
		double q = 2 * b * b * b / 27 - b * c / 3 + d;
92
98
93
		double p = b - a * a / 3;
99
		// short-cut for p = 0
94
		double q = 2 * a * a * a / 27 - a * b / 3 + c;
95
96
		// short-cuts for p = 0, q = 0:
97
		if (PrecisionUtils.equal(p, 0, +4)) {
100
		if (PrecisionUtils.equal(p, 0, +4)) {
98
			// z^3 = -q => z = cbrt(-q) => x = cbrt(-q) - a / 3
101
			// t^3 + q = 0 => t^3 = -q => t = cbrt(-q) => t - b/3 = cbrt(-q) -
99
			return new double[] { Math.cbrt(-q) - a / 3 };
102
			// b/3 => x = cbrt(-q) - b/3
100
		}
103
			return new double[] { Math.cbrt(-q) - b / 3 };
101
		if (PrecisionUtils.equal(q, 0, +4)) {
102
			// z^3 - pz = 0 <=> z(z^2 - p) = 0 => z = 0 v z^2 = p => z =
103
			// +-sqrt(p), p >= 0 (p is not zero, because otherwise we would not
104
			// be here)
105
			if (p > 0) {
106
				return new double[] { 0, Math.sqrt(p), -Math.sqrt(p) };
107
			} else {
108
				return new double[] { 0 };
109
			}
110
		}
104
		}
111
105
112
		double p_3 = p / 3;
106
		double p_3 = p / 3;
Lines 116-128 Link Here
116
110
117
		if (PrecisionUtils.equal(D, 0, +4)) {
111
		if (PrecisionUtils.equal(D, 0, +4)) {
118
			// two real solutions
112
			// two real solutions
119
			return new double[] { 3 * q / p - a / 3, -3 * q / (2 * p) - a / 3 };
113
			return new double[] { 3 * q / p - b / 3, -3 * q / (2 * p) - b / 3 };
120
		} else if (D > 0) {
114
		} else if (D > 0) {
121
			// one real solution
115
			// one real solution
122
			double u = Math.cbrt(-q_2 + Math.sqrt(D));
116
			double u = Math.cbrt(-q_2 + Math.sqrt(D));
123
			double v = Math.cbrt(-q_2 - Math.sqrt(D));
117
			double v = Math.cbrt(-q_2 - Math.sqrt(D));
124
118
125
			return new double[] { u + v - a / 3 };
119
			return new double[] { u + v - b / 3 };
126
		} else {
120
		} else {
127
			// three real solutions
121
			// three real solutions
128
			double r = Math.sqrt(-p_3 * p_3 * p_3);
122
			double r = Math.sqrt(-p_3 * p_3 * p_3);
Lines 130-138 Link Here
130
			double co = 2 * Math.cbrt(r);
124
			double co = 2 * Math.cbrt(r);
131
125
132
			// co * cos((phi + k * pi)/3) - a/3, k = 2n, n in N
126
			// co * cos((phi + k * pi)/3) - a/3, k = 2n, n in N
133
			return new double[] { co * Math.cos(phi / 3) - a / 3,
127
			return new double[] { co * Math.cos(phi / 3) - b / 3,
134
					co * Math.cos((phi + 2 * Math.PI) / 3) - a / 3,
128
					co * Math.cos((phi + 2 * Math.PI) / 3) - b / 3,
135
					co * Math.cos((phi + 4 * Math.PI) / 3) - a / 3 };
129
					co * Math.cos((phi + 4 * Math.PI) / 3) - b / 3 };
136
		}
130
		}
137
	}
131
	}
132
138
}
133
}
(-)src/org/eclipse/gef4/geometry/utils/PrecisionUtils.java (-1 / +1 lines)
Lines 28-34 Link Here
28
	 */
28
	 */
29
	private static final int DEFAULT_SCALE = 6;
29
	private static final int DEFAULT_SCALE = 6;
30
30
31
	private static final double calculateFraction(int shift) {
31
	public static final double calculateFraction(int shift) {
32
		return 1 / Math.pow(10, DEFAULT_SCALE + shift);
32
		return 1 / Math.pow(10, DEFAULT_SCALE + shift);
33
	}
33
	}
34
34
(-)src/org/eclipse/gef4/geometry/tests/AllTests.java (-6 / +6 lines)
Lines 17-28 Link Here
17
import org.junit.runners.Suite.SuiteClasses;
17
import org.junit.runners.Suite.SuiteClasses;
18
18
19
@RunWith(Suite.class)
19
@RunWith(Suite.class)
20
@SuiteClasses({ AngleTests.class, CubicCurveTests.class, DimensionTests.class,
20
@SuiteClasses({ AngleTests.class, CubicCurveTests.class, CurveUtilsTests.class,
21
		EllipseTests.class, LineTests.class, PointListUtilsTests.class,
21
		DimensionTests.class, EllipseTests.class, LineTests.class,
22
		PointTests.class, PolygonTests.class, PolylineTests.class,
22
		PointListUtilsTests.class, PointTests.class, PolygonTests.class,
23
		PolynomCalculationUtilsTests.class, PrecisionUtilsTests.class,
23
		PolylineTests.class, PolynomCalculationUtilsTests.class,
24
		QuadraticCurveTests.class, RectangleTests.class, StraightTests.class,
24
		PrecisionUtilsTests.class, QuadraticCurveTests.class,
25
		VectorTests.class })
25
		RectangleTests.class, StraightTests.class, VectorTests.class })
26
public class AllTests {
26
public class AllTests {
27
27
28
}
28
}
(-)src/org/eclipse/gef4/geometry/tests/CubicCurveTests.java (-1 / +90 lines)
Lines 72-82 Link Here
72
	}
72
	}
73
73
74
	@Test
74
	@Test
75
	public void test_get_Bounds() {
75
	public void test_getBounds() {
76
		CubicCurve curve = new CubicCurve(p, c1, c2, q);
76
		CubicCurve curve = new CubicCurve(p, c1, c2, q);
77
77
78
		// p is the top-left point: (y-coordinates are inverted)
78
		// p is the top-left point: (y-coordinates are inverted)
79
		assertEquals(curve.getBounds().getTopLeft(), p);
79
		assertEquals(curve.getBounds().getTopLeft(), p);
80
	}
80
	}
81
81
82
	@Test
83
	public void test_getIntersections_with_CubicCurve() {
84
		CubicCurve cc1 = new CubicCurve(new Point(-10, -10), new Point(),
85
				new Point(), new Point(5, 5));
86
		CubicCurve cc2 = new CubicCurve(new Point(5, -5), new Point(),
87
				new Point(), new Point(-10, 10));
88
		assertEquals(1, cc1.getIntersections(cc2).length);
89
		assertEquals(1, cc2.getIntersections(cc1).length);
90
91
		// same end point
92
		cc1 = new CubicCurve(103.0, 401.0, 400.0, 200.0, 300.0, 300.0, 390.0,
93
				208.0);
94
		cc2 = new CubicCurve(584.0, 12.0, 200.0, 200.0, 300.0, 100.0, 390.0,
95
				208.0);
96
		assertEquals(1, cc1.getIntersections(cc2).length);
97
		assertEquals(1, cc2.getIntersections(cc1).length);
98
99
		cc1 = new CubicCurve(198.0, 103.0, 410.0, 215.0, 305.0, 320.0, 542.0,
100
				246.0);
101
		cc2 = new CubicCurve(101.0, 107.0, 197.0, 218.0, 302.0, 106.0, 542.0,
102
				246.0);
103
		assertEquals(2, cc1.getIntersections(cc2).length);
104
		assertEquals(2, cc2.getIntersections(cc1).length);
105
106
		cc1 = new CubicCurve(200.0, 100.0, 400.0, 200.0, 300.0, 300.0, 432.0,
107
				62.0);
108
		cc2 = new CubicCurve(100.0, 100.0, 200.0, 200.0, 300.0, 100.0, 432.0,
109
				62.0);
110
		assertEquals(2, cc1.getIntersections(cc2).length);
111
		assertEquals(2, cc2.getIntersections(cc1).length);
112
113
		cc1 = new CubicCurve(200.0, 100.0, 400.0, 200.0, 300.0, 300.0, 208.0,
114
				35.0);
115
		cc2 = new CubicCurve(100.0, 100.0, 200.0, 200.0, 300.0, 100.0, 208.0,
116
				35.0);
117
		assertEquals(3, cc1.getIntersections(cc2).length);
118
		assertEquals(3, cc2.getIntersections(cc1).length);
119
120
		cc1 = new CubicCurve(201.89274447949526, 106.43015521064301,
121
				403.7854889589905, 212.86031042128602, 302.8391167192429,
122
				319.290465631929, 81.0, 22.0);
123
		cc2 = new CubicCurve(100.94637223974763, 106.43015521064301,
124
				201.89274447949526, 212.86031042128602, 302.8391167192429,
125
				106.43015521064301, 81.0, 22.0);
126
		assertEquals(3, cc1.getIntersections(cc2).length);
127
		assertEquals(3, cc2.getIntersections(cc1).length);
128
129
		// torture tests from http://www.truetex.com/bezint.htm
130
		cc1 = new CubicCurve(1.0, 1.5, 15.5, 0.5, -8.0, 3.5, 5.0, 1.5);
131
		cc2 = new CubicCurve(4.0, 0.5, 5.0, 15.0, 2.0, -8.5, 4.0, 4.5);
132
		assertEquals(9, cc1.getIntersections(cc2).length);
133
		assertEquals(9, cc2.getIntersections(cc1).length);
134
135
		cc1 = new CubicCurve(664.00168, 0, 726.11545, 124.22757, 736.89069,
136
				267.89743, 694.0017, 400.0002);
137
		cc2 = new CubicCurve(850.66843, 115.55563, 728.515, 115.55563,
138
				725.21347, 275.15309, 694.0017, 400.0002);
139
		assertEquals(2, cc1.getIntersections(cc2).length);
140
		assertEquals(2, cc2.getIntersections(cc1).length);
141
142
		cc1 = new CubicCurve(1, 1, 12.5, 6.5, -4, 6.5, 7.5, 1);
143
		cc2 = new CubicCurve(1, 6.5, 12.5, 1, -4, 1, 7.5, 6);
144
		assertEquals(6, cc1.getIntersections(cc2).length);
145
		assertEquals(6, cc2.getIntersections(cc1).length);
146
147
		// not sure about these: (getIntersections() returns 3)
148
		// cc1 = new CubicCurve(315.748, 312.84, 312.644, 318.134, 305.836,
149
		// 319.909, 300.542, 316.804);
150
		// cc2 = new CubicCurve(317.122, 309.05, 316.112, 315.102, 310.385,
151
		// 319.19, 304.332, 318.179);
152
153
		// not sure about these: (getIntrsections() returns 0)
154
		// cc1 = new CubicCurve(125.79356, 199.57382, 51.16556, 128.93575,
155
		// 87.494,
156
		// 16.67848, 167.29361, 16.67848);
157
		// cc2 = new CubicCurve(167.29361, 55.81876, 100.36128, 55.81876,
158
		// 68.64099, 145.4755, 125.7942, 199.57309);
159
	}
160
161
	@Test
162
	public void test_getIntersections_with_CubicCurve_endPointsCheck() {
163
		CubicCurve cc1 = new CubicCurve(new Point(0, 0), new Point(0.1, 0),
164
				new Point(0.1, 0), new Point(0.1, 0.1));
165
		CubicCurve cc2 = new CubicCurve(new Point(0, 0), new Point(0.05, 0.1),
166
				new Point(0.05, 0.1), new Point(0.1, -0.1));
167
		assertEquals(2, cc1.getIntersections(cc2).length);
168
		assertEquals(2, cc2.getIntersections(cc1).length);
169
	}
170
82
}
171
}
(-)src/org/eclipse/gef4/geometry/tests/CurveUtilsTests.java (+297 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.tests;
13
14
import static org.junit.Assert.assertEquals;
15
import static org.junit.Assert.assertTrue;
16
17
import java.util.Random;
18
19
import org.eclipse.gef4.geometry.Point;
20
import org.eclipse.gef4.geometry.shapes.CubicCurve;
21
import org.eclipse.gef4.geometry.shapes.QuadraticCurve;
22
import org.eclipse.gef4.geometry.shapes.Rectangle;
23
import org.eclipse.gef4.geometry.utils.CurveUtils;
24
import org.eclipse.gef4.geometry.utils.PrecisionUtils;
25
import org.junit.Test;
26
27
public class CurveUtilsTests {
28
29
	private static final long SEED = 123;
30
31
	@Test
32
	public void test_getSignedDistance_direction() {
33
		// sign-checks:
34
		// first quadrant (y-axis is inverted)
35
		assertTrue(CurveUtils.getSignedDistance(new Point(),
36
				new Point(10, -10), new Point(0, -10)) > 0);
37
		assertTrue(CurveUtils.getSignedDistance(new Point(10, -10),
38
				new Point(), new Point(0, -10)) < 0);
39
		assertTrue(CurveUtils.getSignedDistance(new Point(),
40
				new Point(10, -10), new Point(1, -1)) == 0);
41
42
		// second quadrant (y-axis is inverted)
43
		assertTrue(CurveUtils.getSignedDistance(new Point(),
44
				new Point(-10, -10), new Point(0, -10)) < 0);
45
		assertTrue(CurveUtils.getSignedDistance(new Point(-10, -10),
46
				new Point(), new Point(0, -10)) > 0);
47
		assertTrue(CurveUtils.getSignedDistance(new Point(),
48
				new Point(-10, -10), new Point(-1, -1)) == 0);
49
50
		// third quadrant (y-axis is inverted)
51
		assertTrue(CurveUtils.getSignedDistance(new Point(), new Point(10, 10),
52
				new Point(0, 10)) < 0);
53
		assertTrue(CurveUtils.getSignedDistance(new Point(10, 10), new Point(),
54
				new Point(0, 10)) > 0);
55
		assertTrue(CurveUtils.getSignedDistance(new Point(), new Point(10, 10),
56
				new Point(1, 1)) == 0);
57
58
		// forth quadrant (y-axis is inverted)
59
		assertTrue(CurveUtils.getSignedDistance(new Point(),
60
				new Point(-10, 10), new Point(0, 10)) > 0);
61
		assertTrue(CurveUtils.getSignedDistance(new Point(-10, 10),
62
				new Point(), new Point(0, 10)) < 0);
63
		assertTrue(CurveUtils.getSignedDistance(new Point(),
64
				new Point(-10, 10), new Point(-1, 1)) == 0);
65
	}
66
67
	@Test
68
	public void test_getSignedDistance_abs() {
69
		// do only check for the absolute value of the signed distance
70
71
		assertTrue(PrecisionUtils.equal(Math.abs(CurveUtils.getSignedDistance(
72
				new Point(0, -5), new Point(0, 5), new Point(5, 0))), 5));
73
		assertTrue(PrecisionUtils.equal(Math.abs(CurveUtils.getSignedDistance(
74
				new Point(0, 5), new Point(0, -5), new Point(5, 0))), 5));
75
76
		assertTrue(PrecisionUtils.equal(Math.abs(CurveUtils.getSignedDistance(
77
				new Point(0, -1), new Point(0, 1), new Point(5, 0))), 5));
78
		assertTrue(PrecisionUtils.equal(Math.abs(CurveUtils.getSignedDistance(
79
				new Point(0, 1), new Point(0, -1), new Point(5, 0))), 5));
80
81
		assertTrue(PrecisionUtils.equal(Math.abs(CurveUtils.getSignedDistance(
82
				new Point(-5, 0), new Point(5, 0), new Point(0, 5))), 5));
83
		assertTrue(PrecisionUtils.equal(Math.abs(CurveUtils.getSignedDistance(
84
				new Point(-5, 0), new Point(5, 0), new Point(0, 5))), 5));
85
86
		assertTrue(PrecisionUtils.equal(Math.abs(CurveUtils.getSignedDistance(
87
				new Point(-1, 0), new Point(1, 0), new Point(0, 5))), 5));
88
		assertTrue(PrecisionUtils.equal(Math.abs(CurveUtils.getSignedDistance(
89
				new Point(-1, 0), new Point(1, 0), new Point(0, 5))), 5));
90
	}
91
92
	@Test
93
	public void test_getSignedDistance() {
94
		// check both, direction and value:
95
		// first quadrant (y-axis is inverted)
96
		double len = 10d / Math.sqrt(2);
97
		assertTrue(PrecisionUtils.equal(CurveUtils.getSignedDistance(
98
				new Point(), new Point(10, -10), new Point(0, -10)), len));
99
		assertTrue(PrecisionUtils.equal(CurveUtils.getSignedDistance(new Point(
100
				10, -10), new Point(), new Point(0, -10)), -len));
101
		assertTrue(PrecisionUtils.equal(CurveUtils.getSignedDistance(
102
				new Point(), new Point(10, -10), new Point(1, -1)), 0));
103
104
		// second quadrant (y-axis is inverted)
105
		assertTrue(PrecisionUtils.equal(CurveUtils.getSignedDistance(
106
				new Point(), new Point(-10, -10), new Point(0, -10)), -len));
107
		assertTrue(PrecisionUtils.equal(CurveUtils.getSignedDistance(new Point(
108
				-10, -10), new Point(), new Point(0, -10)), len));
109
		assertTrue(PrecisionUtils.equal(CurveUtils.getSignedDistance(
110
				new Point(), new Point(-10, -10), new Point(-1, -1)), 0));
111
112
		// third quadrant (y-axis is inverted)
113
		assertTrue(PrecisionUtils.equal(CurveUtils.getSignedDistance(
114
				new Point(), new Point(10, 10), new Point(0, 10)), -len));
115
		assertTrue(PrecisionUtils.equal(CurveUtils.getSignedDistance(new Point(
116
				10, 10), new Point(), new Point(0, 10)), len));
117
		assertTrue(PrecisionUtils.equal(CurveUtils.getSignedDistance(
118
				new Point(), new Point(10, 10), new Point(1, 1)), 0));
119
120
		// forth quadrant (y-axis is inverted)
121
		assertTrue(PrecisionUtils.equal(CurveUtils.getSignedDistance(
122
				new Point(), new Point(-10, 10), new Point(0, 10)), len));
123
		assertTrue(PrecisionUtils.equal(CurveUtils.getSignedDistance(new Point(
124
				-10, 10), new Point(), new Point(0, 10)), -len));
125
		assertTrue(PrecisionUtils.equal(CurveUtils.getSignedDistance(
126
				new Point(), new Point(-10, 10), new Point(-1, 1)), 0));
127
	}
128
129
	@Test
130
	public void test_split_with_QuadraticCurve() {
131
		final int numPoints = 4;
132
		final double step = 0.123456789;
133
134
		Random rng = new Random(SEED);
135
136
		for (int i = 0; i < 1000; i++) {
137
			Point[] points = new Point[numPoints];
138
			for (int j = 0; j < numPoints; j++) {
139
				points[j] = new Point(rng.nextDouble(), rng.nextDouble());
140
			}
141
142
			QuadraticCurve c = new QuadraticCurve(points);
143
			for (double t = 0; t <= 1; t += step) {
144
				QuadraticCurve[] cs = c.split(t);
145
				assertEquals(c.get(t), cs[0].get(1));
146
				assertEquals(c.get(t), cs[1].get(0));
147
				assertEquals(c.get(0), cs[0].get(0));
148
				assertEquals(c.get(1), cs[1].get(1));
149
			}
150
		}
151
	}
152
153
	@Test
154
	public void test_clip_with_QuadraticCurve() {
155
		final int numPoints = 4;
156
		final double step = 0.123456789;
157
158
		Random rng = new Random(SEED);
159
160
		for (int i = 0; i < 1000; i++) {
161
			Point[] points = new Point[numPoints];
162
			for (int j = 0; j < numPoints; j++) {
163
				points[j] = new Point(rng.nextDouble(), rng.nextDouble());
164
			}
165
166
			QuadraticCurve c = new QuadraticCurve(points);
167
			for (double t1 = 0; t1 <= 1; t1 += step) {
168
				for (double t2 = 0; t2 <= 1; t2 += step) {
169
					QuadraticCurve cc = c.clip(t1, t2);
170
					assertEquals(c.get(t1), cc.get(0));
171
					assertEquals(c.get(t2), cc.get(1));
172
				}
173
			}
174
		}
175
	}
176
177
	@Test
178
	public void test_split_with_CubicCurve() {
179
		final int numPoints = 6;
180
		final double step = 0.123456789;
181
182
		Random rng = new Random(SEED);
183
184
		for (int i = 0; i < 1000; i++) {
185
			Point[] points = new Point[numPoints];
186
			for (int j = 0; j < numPoints; j++) {
187
				points[j] = new Point(rng.nextDouble(), rng.nextDouble());
188
			}
189
190
			CubicCurve c = new CubicCurve(points);
191
			for (double t = 0; t <= 1; t += step) {
192
				CubicCurve[] cs = c.split(t);
193
				assertEquals(c.get(t), cs[0].get(1));
194
				assertEquals(c.get(t), cs[1].get(0));
195
				assertEquals(c.get(0), cs[0].get(0));
196
				assertEquals(c.get(1), cs[1].get(1));
197
			}
198
		}
199
	}
200
201
	@Test
202
	public void test_clip_with_CubicCurve() {
203
		final int numPoints = 6;
204
		final double step = 0.123456789;
205
206
		Random rng = new Random(SEED);
207
208
		for (int i = 0; i < 1000; i++) {
209
			Point[] points = new Point[numPoints];
210
			for (int j = 0; j < numPoints; j++) {
211
				points[j] = new Point(rng.nextDouble(), rng.nextDouble());
212
			}
213
214
			CubicCurve c = new CubicCurve(points);
215
			for (double t1 = 0; t1 <= 1; t1 += step) {
216
				for (double t2 = 0; t2 <= 1; t2 += step) {
217
					CubicCurve cc = c.clip(t1, t2);
218
					assertEquals(c.get(t1), cc.get(0));
219
					assertEquals(c.get(t2), cc.get(1));
220
				}
221
			}
222
		}
223
	}
224
225
	@Test
226
	public void test_contains_with_QuadraticCurve() {
227
		final int numPoints = 4;
228
		final double step = 0.123456789;
229
230
		Random rng = new Random(SEED);
231
232
		for (int i = 0; i < 1000; i++) {
233
			Point[] points = new Point[numPoints];
234
			for (int j = 0; j < numPoints; j++) {
235
				points[j] = new Point(rng.nextDouble(), rng.nextDouble());
236
			}
237
238
			QuadraticCurve c = new QuadraticCurve(points);
239
			for (double t = 0; t <= 1; t += step) {
240
				assertTrue(c.contains(c.get(t)));
241
			}
242
		}
243
	}
244
245
	@Test
246
	public void test_contains_with_CubicCurve() {
247
		final int numPoints = 6;
248
		final double step = 0.123456789;
249
250
		Random rng = new Random(SEED);
251
252
		for (int i = 0; i < 1000; i++) {
253
			Point[] points = new Point[numPoints];
254
			for (int j = 0; j < numPoints; j++) {
255
				points[j] = new Point(rng.nextDouble(), rng.nextDouble());
256
			}
257
258
			CubicCurve c = new CubicCurve(points);
259
			for (double t = 0; t <= 1; t += step) {
260
				assertTrue(c.contains(c.get(t)));
261
			}
262
		}
263
	}
264
265
	@Test
266
	public void test_getControlBounds() {
267
		final int numPoints = 6;
268
		final double step = 0.123456789;
269
270
		Random rng = new Random(SEED);
271
272
		for (int i = 0; i < 1000; i++) {
273
			Point[] points = new Point[numPoints];
274
			for (int j = 0; j < numPoints; j++) {
275
				points[j] = new Point(rng.nextDouble(), rng.nextDouble());
276
			}
277
278
			CubicCurve c = new CubicCurve(points);
279
			Rectangle bounds = CurveUtils.getControlBounds(c);
280
			for (double t = 0; t <= 1; t += step) {
281
				assertTrue(bounds.contains(c.get(t)));
282
			}
283
			assertTrue(bounds.contains(c.get(1)));
284
		}
285
	}
286
287
	@Test
288
	public void test_getIntersections_BezierClipping_simple() {
289
		CubicCurve c1 = new CubicCurve(new double[] { 100, 200, 200, 100, 300,
290
				300, 400, 200 });
291
		CubicCurve c2 = new CubicCurve(new double[] { 250, 100, 350, 200, 150,
292
				300, 250, 400 });
293
294
		assertEquals(1, c1.getIntersections(c2).length);
295
	}
296
297
}
(-)src/org/eclipse/gef4/geometry/tests/EllipseTests.java (-9 / +32 lines)
Lines 19-25 Link Here
19
import org.eclipse.gef4.geometry.shapes.Ellipse;
19
import org.eclipse.gef4.geometry.shapes.Ellipse;
20
import org.eclipse.gef4.geometry.shapes.Line;
20
import org.eclipse.gef4.geometry.shapes.Line;
21
import org.eclipse.gef4.geometry.shapes.Rectangle;
21
import org.eclipse.gef4.geometry.shapes.Rectangle;
22
import org.junit.Ignore;
23
import org.junit.Test;
22
import org.junit.Test;
24
23
25
/**
24
/**
Lines 74-80 Link Here
74
		}
73
		}
75
	}
74
	}
76
75
77
	@Ignore
76
	@Test
77
	public void test_intersects_with_Line() {
78
		Rectangle r = new Rectangle(34.3435, 56.458945, 123.3098, 146.578);
79
		Ellipse e = new Ellipse(r);
80
		for (Line l : r.getSegments()) {
81
			assertTrue(e.intersects(l)); // line touches ellipse (tangent)
82
		}
83
	}
84
85
	@Test
78
	public void test_get_intersections_with_Ellipse() {
86
	public void test_get_intersections_with_Ellipse() {
79
		Rectangle r = new Rectangle(34.3435, 56.458945, 123.3098, 146.578);
87
		Rectangle r = new Rectangle(34.3435, 56.458945, 123.3098, 146.578);
80
		Ellipse e1 = new Ellipse(r);
88
		Ellipse e1 = new Ellipse(r);
Lines 85-93 Link Here
85
		Point[] intersections = e1.getIntersections(e2);
93
		Point[] intersections = e1.getIntersections(e2);
86
		assertEquals(0, intersections.length);
94
		assertEquals(0, intersections.length);
87
95
96
		// touching left
97
		Rectangle r2 = r.getExpanded(0, -10, -10, -10);
98
		e2 = new Ellipse(r2);
99
		intersections = e1.getIntersections(e2);
100
		assertEquals(1, intersections.length);
101
88
		// if we create an x-scaled ellipse at the same position as before, they
102
		// if we create an x-scaled ellipse at the same position as before, they
89
		// should have 3 poi (the touching point and two crossing intersections)
103
		// should have 3 poi (the touching point and two crossing intersections)
90
		Rectangle r2 = r.getExpanded(0, 0, 100, 0);
104
		r2 = r.getExpanded(0, 0, 100, 0);
91
		e2 = new Ellipse(r2);
105
		e2 = new Ellipse(r2);
92
		intersections = e1.getIntersections(e2);
106
		intersections = e1.getIntersections(e2);
93
		assertEquals(3, intersections.length);
107
		assertEquals(3, intersections.length);
Lines 160-170 Link Here
160
	}
174
	}
161
175
162
	@Test
176
	@Test
163
	public void test_intersects_with_Line() {
177
	public void test_getIntersections_with_Ellipse_Bezier_special() {
164
		Rectangle r = new Rectangle(34.3435, 56.458945, 123.3098, 146.578);
178
		// 3 nearly tangential intersections
165
		Ellipse e = new Ellipse(r);
179
		Ellipse e1 = new Ellipse(126, 90, 378, 270);
166
		for (Line l : r.getSegments()) {
180
		Ellipse e2 = new Ellipse(222, 77, 200, 200);
167
			assertTrue(e.intersects(l)); // line touches ellipse (tangent)
181
		assertEquals(2, e1.getIntersections(e2).length);
168
		}
182
183
		e2 = new Ellipse(133, 90, 2 * (315 - 133), 200);
184
		assertEquals(3, e1.getIntersections(e2).length);
185
186
		e2 = new Ellipse(143, 90, 2 * (315 - 143), 200);
187
		assertEquals(3, e1.getIntersections(e2).length);
188
189
		e2 = new Ellipse(145, 90, 2 * (315 - 145), 200);
190
		assertEquals(3, e1.getIntersections(e2).length);
169
	}
191
	}
192
170
}
193
}
(-)src/org/eclipse/gef4/geometry/tests/PointListUtilsTests.java (+46 lines)
Lines 19-24 Link Here
19
19
20
import org.eclipse.gef4.geometry.Point;
20
import org.eclipse.gef4.geometry.Point;
21
import org.eclipse.gef4.geometry.shapes.Line;
21
import org.eclipse.gef4.geometry.shapes.Line;
22
import org.eclipse.gef4.geometry.shapes.Polygon;
22
import org.eclipse.gef4.geometry.shapes.Rectangle;
23
import org.eclipse.gef4.geometry.shapes.Rectangle;
23
import org.eclipse.gef4.geometry.utils.PointListUtils;
24
import org.eclipse.gef4.geometry.utils.PointListUtils;
24
import org.eclipse.gef4.geometry.utils.PrecisionUtils;
25
import org.eclipse.gef4.geometry.utils.PrecisionUtils;
Lines 161-164 Link Here
161
			assertTrue(PrecisionUtils.equal(points[i].y, i - 1));
162
			assertTrue(PrecisionUtils.equal(points[i].y, i - 1));
162
		}
163
		}
163
	}
164
	}
165
166
	@Test
167
	public void test_getConvexHull() {
168
		// test case from
169
		// http://stackoverflow.com/questions/482278/test-case-data-for-convex-hull
170
171
		Point[] points = PointListUtils.toPointsArray(new double[] {
172
				0.3215348546593775, 0.03629583077160248, 0.02402358131857918,
173
				-0.2356728797179394, 0.04590851212470659, -0.4156409924995536,
174
				0.3218384001607433, 0.1379850698988746, 0.11506479756447,
175
				-0.1059521474930943, 0.2622539999543261, -0.29702873322836,
176
				-0.161920957418085, -0.4055339716426413, 0.1905378631228002,
177
				0.3698601009043493, 0.2387090918968516, -0.01629827079949742,
178
				0.07495888748668034, -0.1659825110491202, 0.3319341836794598,
179
				-0.1821814101954749, 0.07703635755650362, -0.2499430638271785,
180
				0.2069242999022122, -0.2232970760420869, 0.04604079532068295,
181
				-0.1923573186549892, 0.05054295812784038, 0.4754929463150845,
182
				-0.3900589168910486, 0.2797829520700341, 0.3120693385713448,
183
				-0.0506329867529059, 0.01138812723698857, 0.4002504701728471,
184
				0.009645149586391732, 0.1060251100976254, -0.03597933197019559,
185
				0.2953639456959105, 0.1818290866742182, 0.001454397571696298,
186
				0.444056063372694, 0.2502497166863175, -0.05301752458607545,
187
				-0.06553921621808712, 0.4823896228171788, -0.4776170002088109,
188
				-0.3089226845734964, -0.06356112199235814, -0.271780741188471,
189
				0.1810810595574612, 0.4293626522918815, 0.2980897964891882,
190
				-0.004796652127799228, 0.382663812844701, 0.430695573269106,
191
				-0.2995073500084759, 0.1799668387323309, -0.2973467472915973,
192
				0.4932166845474547, 0.4928094162538735, -0.3521487911717489,
193
				0.4352656197131292, -0.4907368011686362, 0.1865826865533206,
194
				-0.1047924716070224, -0.247073392148198, 0.4374961861758457,
195
				-0.001606279519951237, 0.003256207800708899,
196
				-0.2729194320486108, 0.04310378203457577, 0.4452604050238248,
197
				0.4916198379282093, -0.345391701297268, 0.001675087028811806,
198
				0.1531837672490476, -0.4404289572876217, -0.2894855991839297 });
199
200
		Point[] convexHull = PointListUtils.getConvexHull(points);
201
202
		assertTrue(new Polygon(PointListUtils.toPointsArray(new double[] {
203
				-0.161920957418085, -0.4055339716426413, -0.4404289572876217,
204
				-0.2894855991839297, -0.4907368011686362, 0.1865826865533206,
205
				-0.3521487911717489, 0.4352656197131292, 0.05054295812784038,
206
				0.4754929463150845, 0.4932166845474547, 0.4928094162538735,
207
				0.4916198379282093, -0.345391701297268, 0.4823896228171788,
208
				-0.4776170002088109 })).equals(new Polygon(convexHull)));
209
	}
164
}
210
}
(-)src/org/eclipse/gef4/geometry/tests/PolynomCalculationUtilsTests.java (-5 / +63 lines)
Lines 30-45 Link Here
30
30
31
		solutions = PolynomCalculationUtils.getCubicRoots(1, 0, 1, 0);
31
		solutions = PolynomCalculationUtils.getCubicRoots(1, 0, 1, 0);
32
		Arrays.sort(solutions);
32
		Arrays.sort(solutions);
33
		assertEquals("one real solution", 1, solutions.length);
34
		assertTrue("x1 = 0", PrecisionUtils.equal(solutions[0], 0));
35
36
		solutions = PolynomCalculationUtils.getCubicRoots(1, 0, -1, 0);
37
		Arrays.sort(solutions);
33
		assertEquals("three real solutions", 3, solutions.length);
38
		assertEquals("three real solutions", 3, solutions.length);
34
		assertTrue("x1 = -1", PrecisionUtils.equal(solutions[0], -1));
39
		assertTrue("x1 = -1", PrecisionUtils.equal(solutions[0], -1));
35
		assertTrue("x2 = 0", PrecisionUtils.equal(solutions[1], 0));
40
		assertTrue("x2 = 0", PrecisionUtils.equal(solutions[1], 0));
36
		assertTrue("x3 = 1", PrecisionUtils.equal(solutions[2], 1));
41
		assertTrue("x3 = 1", PrecisionUtils.equal(solutions[2], 1));
37
42
38
		solutions = PolynomCalculationUtils.getCubicRoots(1, 0, -1, 0);
39
		Arrays.sort(solutions);
40
		assertEquals("one real solution", 1, solutions.length);
41
		assertTrue("x1 = 0", PrecisionUtils.equal(solutions[0], 0));
42
43
		solutions = PolynomCalculationUtils.getCubicRoots(1, 1, 1, 0);
43
		solutions = PolynomCalculationUtils.getCubicRoots(1, 1, 1, 0);
44
		Arrays.sort(solutions);
44
		Arrays.sort(solutions);
45
		assertEquals("one real solution", 1, solutions.length);
45
		assertEquals("one real solution", 1, solutions.length);
Lines 66-71 Link Here
66
				PrecisionUtils.equal(-0.15524041215, solutions[1]));
66
				PrecisionUtils.equal(-0.15524041215, solutions[1]));
67
		assertTrue("x3 = 1.48705072",
67
		assertTrue("x3 = 1.48705072",
68
				PrecisionUtils.equal(1.487050722353, solutions[2]));
68
				PrecisionUtils.equal(1.487050722353, solutions[2]));
69
70
		// q = 0
71
		solutions = PolynomCalculationUtils.getCubicRoots(-10, 30, 0, -20);
72
		Arrays.sort(solutions);
73
		assertEquals(3, solutions.length);
74
		assertTrue(PrecisionUtils.equal(-0.7320508075688774, solutions[0]));
75
		assertTrue(PrecisionUtils.equal(1, solutions[1]));
76
		assertTrue(PrecisionUtils.equal(2.7320508075688776, solutions[2]));
69
	}
77
	}
70
78
71
	@Test
79
	@Test
Lines 170-173 Link Here
170
					true);
178
					true);
171
		}
179
		}
172
	}
180
	}
181
182
	// @Test
183
	// public void test_getRoots_arbitrary() {
184
	// // double[] solutions = PolynomCalculationUtils.getRoots(1, 0, 1, 0);
185
	// // Arrays.sort(solutions);
186
	// // assertEquals("one real solution", 1, solutions.length);
187
	// // assertTrue("x1 = 0", PrecisionUtils.equal(solutions[0], 0));
188
	//
189
	// solutions = PolynomCalculationUtils.getCubicRoots(1, 0, -1, 0);
190
	// Arrays.sort(solutions);
191
	// assertEquals("three real solutions", 3, solutions.length);
192
	// assertTrue("x1 = -1", PrecisionUtils.equal(solutions[0], -1));
193
	// assertTrue("x2 = 0", PrecisionUtils.equal(solutions[1], 0));
194
	// assertTrue("x3 = 1", PrecisionUtils.equal(solutions[2], 1));
195
	//
196
	// solutions = PolynomCalculationUtils.getCubicRoots(1, 1, 1, 0);
197
	// Arrays.sort(solutions);
198
	// assertEquals("one real solution", 1, solutions.length);
199
	// assertTrue("x^3 + x^2 + x = 0 <=> x(x^2 + x + 1) = 0 => x = 0",
200
	// PrecisionUtils.equal(0, solutions[0]));
201
	//
202
	// solutions = PolynomCalculationUtils.getCubicRoots(1, -6, 12, -8);
203
	// assertEquals("one real solution", 1, solutions.length);
204
	// assertTrue("x = 2 solves the polynom",
205
	// PrecisionUtils.equal(2, solutions[0]));
206
	//
207
	// solutions = PolynomCalculationUtils.getCubicRoots(1, 1, -33, 63);
208
	// Arrays.sort(solutions);
209
	// assertEquals("two real solutions", 2, solutions.length);
210
	// assertTrue("x1 = -7", PrecisionUtils.equal(-7, solutions[0]));
211
	// assertTrue("x2 = 3", PrecisionUtils.equal(3, solutions[1]));
212
	//
213
	// solutions = PolynomCalculationUtils.getCubicRoots(1, 3, -6, -1);
214
	// Arrays.sort(solutions);
215
	// assertEquals("three real solutions", 3, solutions.length);
216
	// assertTrue("x1 = -4.33181031",
217
	// PrecisionUtils.equal(-4.331810310203, solutions[0]));
218
	// assertTrue("x2 = -0.15524041",
219
	// PrecisionUtils.equal(-0.15524041215, solutions[1]));
220
	// assertTrue("x3 = 1.48705072",
221
	// PrecisionUtils.equal(1.487050722353, solutions[2]));
222
	//
223
	// // q = 0
224
	// solutions = PolynomCalculationUtils.getCubicRoots(-10, 30, 0, -20);
225
	// Arrays.sort(solutions);
226
	// assertEquals(3, solutions.length);
227
	// assertTrue(PrecisionUtils.equal(-0.7320508075688774, solutions[0]));
228
	// assertTrue(PrecisionUtils.equal(1, solutions[1]));
229
	// assertTrue(PrecisionUtils.equal(2.7320508075688776, solutions[2]));
230
	// }
173
}
231
}
(-)src/org/eclipse/gef4/geometry/tests/QuadraticCurveTests.java (+24 lines)
Lines 14-24 Link Here
14
import static org.junit.Assert.assertEquals;
14
import static org.junit.Assert.assertEquals;
15
import static org.junit.Assert.assertTrue;
15
import static org.junit.Assert.assertTrue;
16
16
17
import java.util.Random;
18
17
import org.eclipse.gef4.geometry.Point;
19
import org.eclipse.gef4.geometry.Point;
20
import org.eclipse.gef4.geometry.shapes.CubicCurve;
18
import org.eclipse.gef4.geometry.shapes.QuadraticCurve;
21
import org.eclipse.gef4.geometry.shapes.QuadraticCurve;
19
import org.junit.Test;
22
import org.junit.Test;
20
23
21
public class QuadraticCurveTests {
24
public class QuadraticCurveTests {
25
	private static final int SEED = 123;
22
	private final Point p = new Point(-10, -10), c = new Point(10, 0),
26
	private final Point p = new Point(-10, -10), c = new Point(10, 0),
23
			q = new Point(0, 10);
27
			q = new Point(0, 10);
24
28
Lines 198-201 Link Here
198
	public void test_intersects_Rectangle() {
202
	public void test_intersects_Rectangle() {
199
		// TODO!
203
		// TODO!
200
	}
204
	}
205
206
	@Test
207
	public void test_getElevated() {
208
		Random rng = new Random(SEED);
209
210
		for (int i = 0; i < 100; i++) {
211
			Point[] points = new Point[3];
212
			for (int j = 0; j < 3; j++) {
213
				points[j] = new Point(rng.nextDouble(), rng.nextDouble());
214
			}
215
			QuadraticCurve qc = new QuadraticCurve(points);
216
			CubicCurve cc = qc.getElevated();
217
218
			for (double t = 0; t <= 1; t += 0.0123456789) {
219
				assertTrue(cc.contains(qc.get(t)));
220
				assertTrue(qc.contains(cc.get(t)));
221
			}
222
		}
223
	}
224
201
}
225
}
(-)src/org/eclipse/gef4/geometry/tests/RectangleTests.java (-13 / +18 lines)
Lines 360-365 Link Here
360
		assertEquals(new Rectangle(0, 3, 3, 4), r1.getTranslated(-1, 1));
360
		assertEquals(new Rectangle(0, 3, 3, 4), r1.getTranslated(-1, 1));
361
	}
361
	}
362
362
363
	@Test
363
	public void test_getTransposed() {
364
	public void test_getTransposed() {
364
		forRectangles(new IAction() {
365
		forRectangles(new IAction() {
365
			public void action(Rectangle rect, Point tl, Point br) {
366
			public void action(Rectangle rect, Point tl, Point br) {
Lines 553-571 Link Here
553
	}
554
	}
554
555
555
	@Test
556
	@Test
556
	public void test_setSize() {
557
		Rectangle r = new Rectangle();
558
559
		r.setSize(new Dimension(10, 20));
560
		assertEquals(new Point(), r.getTopLeft());
561
		assertEquals(new Point(10, 20), r.getBottomRight());
562
563
		r.setSize(5, -10);
564
		assertEquals(new Point(), r.getTopLeft());
565
		assertEquals(new Point(5, 0), r.getBottomRight());
566
	}
567
568
	@Test
569
	public void test_setters() {
557
	public void test_setters() {
570
		Rectangle r = new Rectangle();
558
		Rectangle r = new Rectangle();
571
559
Lines 691-694 Link Here
691
		});
679
		});
692
	}
680
	}
693
681
682
	@Test
683
	public void test_setSize() {
684
		Rectangle r = new Rectangle();
685
686
		r.setSize(new Dimension(10, 20));
687
		assertEquals(new Point(), r.getTopLeft());
688
		assertEquals(new Point(10, 20), r.getBottomRight());
689
690
		r.setSize(5, -10);
691
		assertEquals(new Point(), r.getTopLeft());
692
		assertEquals(new Point(5, 0), r.getBottomRight());
693
694
		r.setSize(-5, 10);
695
		assertEquals(new Point(), r.getTopLeft());
696
		assertEquals(new Point(0, 10), r.getBottomRight());
697
	}
698
694
}
699
}
(-)src/org/eclipse/gef4/geometry/tests/TestUtils.java (-11 / +2 lines)
Lines 12-19 Link Here
12
 *******************************************************************************/
12
 *******************************************************************************/
13
package org.eclipse.gef4.geometry.tests;
13
package org.eclipse.gef4.geometry.tests;
14
14
15
import java.lang.reflect.Method;
16
17
import org.eclipse.gef4.geometry.utils.PrecisionUtils;
15
import org.eclipse.gef4.geometry.utils.PrecisionUtils;
18
16
19
/**
17
/**
Lines 25-39 Link Here
25
public class TestUtils {
23
public class TestUtils {
26
24
27
	public static double getPrecisionFraction() {
25
	public static double getPrecisionFraction() {
28
		Method f;
26
		// TODO: remove TestUtils
29
		try {
27
		return PrecisionUtils.calculateFraction(0);
30
			f = PrecisionUtils.class.getDeclaredMethod("calculateFraction",
31
					int.class);
32
			f.setAccessible(true);
33
			return ((Double) f.invoke(PrecisionUtils.class, 0)).doubleValue();
34
		} catch (Exception e) {
35
			throw new IllegalArgumentException(e);
36
		}
37
	}
28
	}
38
29
39
	private TestUtils() {
30
	private TestUtils() {

Return to bug 355997