/*******************************************************************************
* Copyright (c) 2011, 2015 itemis AG and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Matthias Wienand (itemis AG) - initial API and implementation
*
*******************************************************************************/
package org.eclipse.gef.geometry.tests;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.util.Random;
import org.eclipse.gef.geometry.euclidean.Angle;
import org.eclipse.gef.geometry.euclidean.Straight;
import org.eclipse.gef.geometry.internal.utils.PrecisionUtils;
import org.eclipse.gef.geometry.planar.BezierCurve;
import org.eclipse.gef.geometry.planar.CubicCurve;
import org.eclipse.gef.geometry.planar.Line;
import org.eclipse.gef.geometry.planar.Path;
import org.eclipse.gef.geometry.planar.Point;
import org.eclipse.gef.geometry.planar.Polyline;
import org.eclipse.gef.geometry.planar.QuadraticCurve;
import org.eclipse.gef.geometry.planar.Rectangle;
import org.eclipse.gef.geometry.planar.Path.Segment;
import org.junit.Test;
public class CurveUtilsTests {
private static final long SEED = 123;
@Test
public void test_check_contains_difficult_cases() {
BezierCurve c1 = new BezierCurve(
new Point(0.5402658595791646, 0.9741509829024984),
new Point(0.16279154085195757, 0.6904753002704389),
new Point(0.5362586913177897, 0.03544287335013263),
new Point(0.34435494116180165, 0.31041629374338775),
new Point(0.3850664934271003, 0.5238288178983336),
new Point(0.13829366099352602, 0.7410634269933081),
new Point(0.8948987750498976, 0.6198888125981984),
new Point(0.11349279987471517, 0.3388985501965609));
BezierCurve c2 = new BezierCurve(
new Point(0.3494484760769454, 0.47795072857018706),
new Point(0.9841912220562209, 0.10765341979304721),
new Point(0.27977429726230696, 0.7303050467844633),
new Point(0.28022390386455787, 0.3313265057575542),
new Point(0.3914004373221056, 0.6451799723354514),
new Point(0.2875477493472879, 0.5132577259019093),
new Point(0.13579952633028602, 0.5665583087171101),
new Point(0.09831516780965299, 0.25959706254491044));
Point p = new Point(0.3736012772933578, 0.4639965007739409);
assertTrue(c1.contains(p));
assertTrue(c2.contains(p));
}
@Test
public void test_clip_with_CubicCurve() {
final int numPoints = 4;
final double step = 0.123456789;
Random rng = new Random(SEED);
for (int i = 0; i < 1000; i++) {
Point[] points = new Point[numPoints];
for (int j = 0; j < numPoints; j++) {
points[j] = new Point(rng.nextDouble(), rng.nextDouble());
}
CubicCurve c = new CubicCurve(points);
for (double t1 = 0; t1 <= 1; t1 += step) {
for (double t2 = 0; t2 <= 1; t2 += step) {
CubicCurve cc = c.getClipped(t1, t2).toCubic();
if (cc != null) {
assertEquals(c.get(t1), cc.get(0));
assertEquals(c.get(t2), cc.get(1));
}
}
}
}
}
@Test
public void test_contains_with_CubicCurve() {
final int numPoints = 6;
final double step = 0.123456789;
Random rng = new Random(SEED);
for (int i = 0; i < 1000; i++) {
Point[] points = new Point[numPoints];
for (int j = 0; j < numPoints; j++) {
points[j] = new Point(rng.nextDouble(), rng.nextDouble());
assertTrue(!Double.isNaN(points[j].x));
assertTrue(!Double.isNaN(points[j].y));
}
BezierCurve c = new BezierCurve(points);
for (double t = 0; t <= 1; t += step) {
assertTrue(c.contains(c.get(t)));
}
}
BezierCurve c = new BezierCurve(new Point(0.0, 0.0),
new Point(0.05, 0.1), new Point(0.05, 0.1),
new Point(0.1, -0.1));
assertTrue(!c.contains(new Point(0.1, 0.1)));
}
@Test
public void test_contains_with_QuadraticCurve() {
final int numPoints = 3;
final double step = 0.123456789;
Random rng = new Random(SEED);
for (int i = 0; i < 1000; i++) {
Point[] points = new Point[numPoints];
for (int j = 0; j < numPoints; j++) {
points[j] = new Point(rng.nextDouble(), rng.nextDouble());
}
QuadraticCurve c = new QuadraticCurve(points);
for (double t = 0; t <= 1; t += step) {
assertTrue("t = " + t, c.contains(c.get(t)));
}
}
}
@Test
public void test_getBounds() {
Random rng = new Random(SEED);
for (int i = 0; i < 1000; i++) {
double w = Math.abs(rng.nextDouble()),
h = Math.abs(rng.nextDouble());
Rectangle controlBounds = new Rectangle(rng.nextDouble(),
rng.nextDouble(), w, h);
for (int n = 2; n < 10; n++) {
Point[] points = new Point[n];
points[0] = controlBounds.getBottomLeft();
points[n - 1] = controlBounds.getBottomRight();
if (n == 3) {
points[1] = points[0].getTranslated(points[2])
.getScaled(0.5);
}
if (n > 3) {
double span = controlBounds.getWidth() / (n - 3);
double d = 0;
for (int j = 1; j < n - 1; j++, d += span) {
points[j] = new Point(controlBounds.getX() + d,
controlBounds.getY());
}
}
BezierCurve c = new BezierCurve(points);
// y maximum
double d = controlBounds.getY() + controlBounds.getHeight()
- c.get(0.5).y;
Rectangle bounds = new Rectangle(controlBounds.getX(),
controlBounds.getY() + controlBounds.getHeight() - d,
controlBounds.getWidth(), d);
assertEquals(bounds, c.getBounds());
// x maximum
controlBounds = controlBounds
.getRotatedCCW(Angle.fromDeg(90), new Point())
.getBounds();
c.rotateCCW(Angle.fromDeg(90), new Point());
d = controlBounds.getX() + controlBounds.getWidth()
- c.get(0.5).x;
bounds = new Rectangle(
controlBounds.getX() + controlBounds.getWidth() - d,
controlBounds.getY(), d, controlBounds.getHeight());
if (!bounds.equals(c.getBounds())) {
System.out.println("DEBUG");
}
assertEquals(bounds, c.getBounds());
}
}
}
@Test
public void test_getClipped_with_QuadraticCurve() {
final int numPoints = 3;
final double step = 0.123456789;
Random rng = new Random(SEED);
for (int i = 0; i < 1000; i++) {
Point[] points = new Point[numPoints];
for (int j = 0; j < numPoints; j++) {
points[j] = new Point(rng.nextDouble(), rng.nextDouble());
}
QuadraticCurve c = new QuadraticCurve(points);
for (double t1 = 0; t1 <= 1; t1 += step) {
for (double t2 = 0; t2 <= 1; t2 += step) {
QuadraticCurve cc = c.getClipped(t1, t2).toQuadratic();
if (cc != null) {
assertEquals(c.get(t1), cc.get(0));
assertEquals(c.get(t2), cc.get(1));
}
}
}
}
}
@Test
public void test_getControlBounds() {
final int numPoints = 4;
final double step = 0.123456789;
Random rng = new Random(SEED);
for (int i = 0; i < 1000; i++) {
Point[] points = new Point[numPoints];
for (int j = 0; j < numPoints; j++) {
points[j] = new Point(rng.nextDouble(), rng.nextDouble());
}
CubicCurve c = new CubicCurve(points);
Rectangle bounds = c.getControlBounds();
for (double t = 0; t <= 1; t += step) {
assertTrue(bounds.contains(c.get(t)));
}
assertTrue(bounds.contains(c.get(1)));
}
}
@Test
public void test_getIntersections_linear() {
BezierCurve yAxis = new BezierCurve(new Point(0, 0), new Point(1, 0));
BezierCurve curve = new BezierCurve(new Point(0, -20),
new Point(0.3333333333333333, -10.0),
new Point(0.6666666666666666, 0.0), new Point(1, -10));
assertEquals(0, yAxis.getIntersections(curve).length);
}
@Test
public void test_getIntersections_overlapping() {
/*
* The algorithm is very slow in the case of an overlap. The execution
* speed is depending on the length of the curve. That's why we keep the
* coordinate values small.
*
* TODO: Make the algorithm return quickly for overlapping curves.
*/
CubicCurve baseCurve = new CubicCurve(new Point(), new Point(.1, 0),
new Point(0, .1), new Point(.1, .1));
CubicCurve c1 = baseCurve.getClipped(0, 0.75).toCubic();
CubicCurve c2 = baseCurve.getClipped(0.25, 1).toCubic();
assertEquals(0, c1.getIntersections(c2).length);
assertEquals(0, c2.getIntersections(c1).length);
// c1 contains c2, c2 is contained by c1
c2 = c1.getClipped(0.25, 0.75).toCubic();
assertEquals(0, c1.getIntersections(c2).length);
assertEquals(0, c2.getIntersections(c1).length);
// single end-point-intersection
c2 = baseCurve.getClipped(0.75, 1).toCubic();
assertEquals(1, c1.getIntersections(c2).length);
assertEquals(1, c2.getIntersections(c1).length);
}
@Test
public void test_getIntersections_random_containment() {
final int numPoints = 8;
Random rng = new Random(SEED);
for (int i = 0; i < 1000; i++) {
Point[] points = new Point[numPoints];
for (int j = 0; j < numPoints; j++) {
points[j] = new Point(rng.nextDouble(), rng.nextDouble());
assertTrue(!Double.isNaN(points[j].x));
assertTrue(!Double.isNaN(points[j].y));
}
BezierCurve c1 = new BezierCurve(points);
for (int j = 0; j < numPoints; j++) {
points[j] = new Point(rng.nextDouble(), rng.nextDouble());
assertTrue(!Double.isNaN(points[j].x));
assertTrue(!Double.isNaN(points[j].y));
}
BezierCurve c2 = new BezierCurve(points);
for (Point poi : c1.getIntersections(c2)) {
// if (!c1.contains(poi) || !c2.contains(poi)) {
// System.out.println("DEBUG");
// }
// TODO: every failing test has to be sourced out into the
// test_check_contains_difficult_cases() method.
assertTrue(c1.contains(poi));
assertTrue(c2.contains(poi));
}
}
}
@Test
public void test_getIntersections_simple() {
CubicCurve c1 = new CubicCurve(
new double[] { 100, 200, 200, 100, 300, 300, 400, 200 });
CubicCurve c2 = new CubicCurve(
new double[] { 250, 100, 350, 200, 150, 300, 250, 400 });
assertEquals(1, c1.getIntersections(c2).length);
c1 = new CubicCurve(new Point(201.89274447949526, 106.43015521064301),
new Point(213.0, 325.0), new Point(387.0, 119.0),
new Point(118.0, 310.0));
Line l = new Line(new Point(105.66666666666667, 75.16666666666667),
new Point(528.3333333333334, 375.8333333333333));
assertEquals(1, c1.getIntersections(l).length);
QuadraticCurve q1 = new QuadraticCurve(new Point(200, 50),
new Point(210, 100), new Point(190, 150));
l = new Line(new Point(100, 100), new Point(300, 100));
assertEquals(1, q1.getIntersections(l).length);
q1 = new QuadraticCurve(new Point(250, 50), new Point(200, 100),
new Point(200, 150));
l = new Line(new Point(528, 75), new Point(105, 75));
assertEquals(1, q1.getIntersections(l).length);
q1 = new QuadraticCurve(new Point(500, 50), new Point(300, 150),
new Point(100, 300));
assertEquals(1, q1.getIntersections(l).length);
q1 = new QuadraticCurve(new Point(171, 409), new Point(302, 106),
new Point(345, 310));
l = new Line(new Point(105.66666666666667, 375.8333333333333),
new Point(528.3333333333334, 375.8333333333333));
Point[] testInters = q1.getIntersections(l);
assertEquals(1, testInters.length);
}
@Test
public void test_getSignedDistance() {
// check both, direction and value:
// first quadrant (y-axis is inverted)
double len = 10d / Math.sqrt(2);
assertTrue(PrecisionUtils.equal(Straight.getSignedDistanceCCW(
new Point(), new Point(10, -10), new Point(0, -10)), len));
assertTrue(PrecisionUtils.equal(Straight.getSignedDistanceCCW(
new Point(10, -10), new Point(), new Point(0, -10)), -len));
assertTrue(PrecisionUtils.equal(Straight.getSignedDistanceCCW(
new Point(), new Point(10, -10), new Point(1, -1)), 0));
// second quadrant (y-axis is inverted)
assertTrue(PrecisionUtils.equal(Straight.getSignedDistanceCCW(
new Point(), new Point(-10, -10), new Point(0, -10)), -len));
assertTrue(PrecisionUtils.equal(Straight.getSignedDistanceCCW(
new Point(-10, -10), new Point(), new Point(0, -10)), len));
assertTrue(PrecisionUtils.equal(Straight.getSignedDistanceCCW(
new Point(), new Point(-10, -10), new Point(-1, -1)), 0));
// third quadrant (y-axis is inverted)
assertTrue(PrecisionUtils.equal(Straight.getSignedDistanceCCW(
new Point(), new Point(10, 10), new Point(0, 10)), -len));
assertTrue(PrecisionUtils.equal(Straight.getSignedDistanceCCW(
new Point(10, 10), new Point(), new Point(0, 10)), len));
assertTrue(PrecisionUtils.equal(Straight.getSignedDistanceCCW(
new Point(), new Point(10, 10), new Point(1, 1)), 0));
// forth quadrant (y-axis is inverted)
assertTrue(PrecisionUtils.equal(Straight.getSignedDistanceCCW(
new Point(), new Point(-10, 10), new Point(0, 10)), len));
assertTrue(PrecisionUtils.equal(Straight.getSignedDistanceCCW(
new Point(-10, 10), new Point(), new Point(0, 10)), -len));
assertTrue(PrecisionUtils.equal(Straight.getSignedDistanceCCW(
new Point(), new Point(-10, 10), new Point(-1, 1)), 0));
}
@Test
public void test_getSignedDistance_abs() {
// do only check for the absolute value of the signed distance
assertTrue(PrecisionUtils
.equal(Math.abs(Straight.getSignedDistanceCCW(new Point(0, -5),
new Point(0, 5), new Point(5, 0))), 5));
assertTrue(PrecisionUtils
.equal(Math.abs(Straight.getSignedDistanceCCW(new Point(0, 5),
new Point(0, -5), new Point(5, 0))), 5));
assertTrue(PrecisionUtils
.equal(Math.abs(Straight.getSignedDistanceCCW(new Point(0, -1),
new Point(0, 1), new Point(5, 0))), 5));
assertTrue(PrecisionUtils
.equal(Math.abs(Straight.getSignedDistanceCCW(new Point(0, 1),
new Point(0, -1), new Point(5, 0))), 5));
assertTrue(PrecisionUtils
.equal(Math.abs(Straight.getSignedDistanceCCW(new Point(-5, 0),
new Point(5, 0), new Point(0, 5))), 5));
assertTrue(PrecisionUtils
.equal(Math.abs(Straight.getSignedDistanceCCW(new Point(-5, 0),
new Point(5, 0), new Point(0, 5))), 5));
assertTrue(PrecisionUtils
.equal(Math.abs(Straight.getSignedDistanceCCW(new Point(-1, 0),
new Point(1, 0), new Point(0, 5))), 5));
assertTrue(PrecisionUtils
.equal(Math.abs(Straight.getSignedDistanceCCW(new Point(-1, 0),
new Point(1, 0), new Point(0, 5))), 5));
}
@Test
public void test_getSignedDistance_direction() {
// sign-checks:
// first quadrant (y-axis is inverted)
assertTrue(Straight.getSignedDistanceCCW(new Point(),
new Point(10, -10), new Point(0, -10)) > 0);
assertTrue(Straight.getSignedDistanceCCW(new Point(10, -10),
new Point(), new Point(0, -10)) < 0);
assertTrue(Straight.getSignedDistanceCCW(new Point(),
new Point(10, -10), new Point(1, -1)) == 0);
// second quadrant (y-axis is inverted)
assertTrue(Straight.getSignedDistanceCCW(new Point(),
new Point(-10, -10), new Point(0, -10)) < 0);
assertTrue(Straight.getSignedDistanceCCW(new Point(-10, -10),
new Point(), new Point(0, -10)) > 0);
assertTrue(Straight.getSignedDistanceCCW(new Point(),
new Point(-10, -10), new Point(-1, -1)) == 0);
// third quadrant (y-axis is inverted)
assertTrue(Straight.getSignedDistanceCCW(new Point(), new Point(10, 10),
new Point(0, 10)) < 0);
assertTrue(Straight.getSignedDistanceCCW(new Point(10, 10), new Point(),
new Point(0, 10)) > 0);
assertTrue(Straight.getSignedDistanceCCW(new Point(), new Point(10, 10),
new Point(1, 1)) == 0);
// forth quadrant (y-axis is inverted)
assertTrue(Straight.getSignedDistanceCCW(new Point(),
new Point(-10, 10), new Point(0, 10)) > 0);
assertTrue(Straight.getSignedDistanceCCW(new Point(-10, 10),
new Point(), new Point(0, 10)) < 0);
assertTrue(Straight.getSignedDistanceCCW(new Point(),
new Point(-10, 10), new Point(-1, 1)) == 0);
}
@Test
public void test_split_with_CubicCurve() {
final int numPoints = 4;
final double step = 0.123456789;
Random rng = new Random(SEED);
for (int i = 0; i < 1000; i++) {
Point[] points = new Point[numPoints];
for (int j = 0; j < numPoints; j++) {
points[j] = new Point(rng.nextDouble(), rng.nextDouble());
}
CubicCurve c = new CubicCurve(points);
for (double t = 0; t <= 1; t += step) {
CubicCurve[] cs = c.split(t);
assertEquals(c.get(t), cs[0].get(1));
assertEquals(c.get(t), cs[1].get(0));
assertEquals(c.get(0), cs[0].get(0));
assertEquals(c.get(1), cs[1].get(1));
}
}
}
@Test
public void test_split_with_QuadraticCurve() {
final int numPoints = 3;
final double step = 0.123456789;
Random rng = new Random(SEED);
for (int i = 0; i < 1000; i++) {
Point[] points = new Point[numPoints];
for (int j = 0; j < numPoints; j++) {
points[j] = new Point(rng.nextDouble(), rng.nextDouble());
}
BezierCurve c = new BezierCurve(points);
for (double t = 0; t <= 1; t += step) {
BezierCurve[] cs = c.split(t);
assertEquals(c.get(t), cs[0].get(1));
assertEquals(c.get(t), cs[1].get(0));
assertEquals(c.get(0), cs[0].get(0));
assertEquals(c.get(1), cs[1].get(1));
}
}
}
@Test
public void test_toPath() {
Point start = new Point(20, 30);
Point mid = new Point(20, 70);
Point end = new Point(20, 90);
Path path = new Polyline(start, mid, end).toPolyBezier().toPath();
Segment[] segments = path.getSegments();
assertNotNull(segments);
assertEquals(3, segments.length);
assertEquals(Segment.MOVE_TO, segments[0].getType());
assertEquals(Segment.LINE_TO, segments[1].getType());
assertEquals(Segment.LINE_TO, segments[2].getType());
assertEquals(start, segments[0].getPoints()[0]);
assertEquals(mid, segments[1].getPoints()[0]);
assertEquals(end, segments[2].getPoints()[0]);
}
}