/*******************************************************************************
* Copyright (c) 2008, 2016 IBM Corporation 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:
* IBM Corporation - initial API and implementation
* Matthias Wienand (itemis AG) - contribution for Bugzilla #355997
*
*******************************************************************************/
package org.eclipse.gef.geometry.tests;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.eclipse.gef.geometry.euclidean.Angle;
import org.eclipse.gef.geometry.euclidean.Vector;
import org.eclipse.gef.geometry.internal.utils.PrecisionUtils;
import org.eclipse.gef.geometry.planar.Point;
import org.junit.Test;
/**
* Unit tests for {@link Vector}.
*
* @author aboyko
* @author anyssen
* @author mwienand
*
*/
public class VectorTests {
private interface VectorAction {
void action(Vector a);
}
private abstract class VectorPairAction {
public void action(Vector a, Vector b) {
}
public Vector alterA(Vector a) {
return a;
}
}
private static final double PRECISION_FRACTION = TestUtils
.getPrecisionFraction();
private static final double RECOGNIZABLE_FRACTION = PRECISION_FRACTION
+ PRECISION_FRACTION / 10;
private static final double UNRECOGNIZABLE_FRACTION = PRECISION_FRACTION
- PRECISION_FRACTION / 10;
private void forVectorPairs(VectorPairAction action) {
for (double x1 = -2; x1 <= 2; x1 += 0.2) {
for (double y1 = -2; y1 <= 2; y1 += 0.2) {
Vector a = action.alterA(new Vector(x1, y1));
for (double x2 = -2; x2 <= 2; x2 += 0.2) {
for (double y2 = -2; y2 <= 2; y2 += 0.2) {
action.action(a, new Vector(x2, y2));
}
}
}
}
}
private void forVectors(VectorAction action) {
for (double x = -2; x <= 2; x += 0.2) {
for (double y = -2; y <= 2; y += 0.2) {
Vector a = new Vector(x, y);
action.action(a);
}
}
}
@Test
public void test_constructors() {
Vector a = new Vector(1, 2);
assertTrue(a.equals(new Vector(new Point(1, 2))));
assertTrue(a.equals(new Vector(new Point(), new Point(1, 2))));
assertTrue(a.equals(new Vector(new Point(1, 2), new Point(2, 4))));
assertTrue(a.equals(new Vector(new Vector(0, 0), new Vector(1, 2))));
assertTrue(a.equals(new Vector(new Vector(-1, -2), new Vector(0, 0))));
}
@Test
public void test_copy() {
Vector a = new Vector(1, 2);
assertTrue(a.getCopy().equals(a));
assertTrue(a.clone().equals(a));
assertTrue(a.getCopy().equals(a.clone()));
}
@Test
public void test_equals() {
Vector a = new Vector(3, 2);
Vector b = new Vector(2, -2);
assertTrue(a.equals(a));
assertFalse(a.equals(b));
assertFalse(a.equals(new Point(3, 2)));
assertTrue(a.equals(a.getAdded(new Vector(UNRECOGNIZABLE_FRACTION / 10,
UNRECOGNIZABLE_FRACTION / 10))));
assertFalse(a.equals(a.getAdded(
new Vector(RECOGNIZABLE_FRACTION, RECOGNIZABLE_FRACTION))));
}
@Test
public void test_getAdded() {
forVectorPairs(new VectorPairAction() {
@Override
public void action(Vector a, Vector b) {
assertTrue(
a.getAdded(b).equals(new Vector(a.x + b.x, a.y + b.y)));
}
});
}
@Test
public void test_getAngle() {
forVectorPairs(new VectorPairAction() {
@Override
public void action(Vector a, Vector b) {
assertTrue(PrecisionUtils.equal(a.getDotProduct(b),
a.getLength() * b.getLength()
* Math.cos(a.getAngle(b).rad())));
}
});
// test for the ArithmeticException is case of a null-vector
for (Vector a : new Vector[] { new Vector(1, 2), new Vector(0, 0) }) {
for (Vector b : new Vector[] { new Vector(0, 0),
new Vector(1, 2) }) {
boolean thrown = a.getLength() != 0 && b.getLength() != 0;
try {
a.getAngle(b);
} catch (ArithmeticException x) {
thrown = true;
}
assertTrue(thrown);
}
}
}
@Test
public void test_getAngleCCW() {
forVectorPairs(new VectorPairAction() {
@Override
public void action(Vector a, Vector b) {
Angle alpha = a.getAngle(b);
Angle alphaCCW = a.getAngleCCW(b);
if (a.getCrossProduct(b) > 0) {
alpha = alpha.getOppositeFull();
}
assertTrue(alpha.equals(alphaCCW));
}
});
}
@Test
public void test_getAngleCW() {
forVectorPairs(new VectorPairAction() {
@Override
public void action(Vector a, Vector b) {
Angle alphaCW = a.getAngleCW(b);
Angle alpha = a.getAngle(b);
if (a.getCrossProduct(b) < 0) {
alpha = alpha.getOppositeFull();
}
assertTrue(alpha.equals(alphaCW));
}
});
}
@Test
public void test_getAveraged() {
forVectorPairs(new VectorPairAction() {
@Override
public void action(Vector a, Vector b) {
assertTrue(a.getAveraged(b)
.equals(a.getAdded(b).getMultiplied(0.5)));
}
});
}
@Test
public void test_getCrossProduct() {
forVectorPairs(new VectorPairAction() {
@Override
public void action(Vector a, Vector b) {
assertTrue(PrecisionUtils.equal(a.getCrossProduct(b),
a.x * b.y - a.y * b.x));
}
});
}
@Test
public void test_getDissimilarity() {
Vector vx = new Vector(5, 0);
Vector vy = new Vector(0, 5);
Vector vxy = new Vector(5, 5);
// cross product uses vectors's lengths!
assertTrue(vx.getDissimilarity(vy) > vx.getDissimilarity(vxy));
// note: no PrecisionUtils here, because we need "is really greater"
// TODO: normalize the vectors first, so that they get comparable.
/*
* the description of the method is mistakable:
*
* 1) does it mean that an angle of 180 degrees returns the same
* dissimilarity as an angle of 0 degrees?
*
* 2) or does it mean that an angle of 180 degrees returns the highest
* dissimilarity?
*
* the following code expects the first case
*/
forVectorPairs(new VectorPairAction() {
@Override
public void action(Vector a, Vector b) {
assertTrue(PrecisionUtils.equal(a.getDissimilarity(b), Math.abs(
a.getNormalized().getCrossProduct(b.getNormalized()))));
}
});
}
@Test
public void test_getDivided() {
forVectors(new VectorAction() {
@Override
public void action(Vector a) {
for (double r = -2; r <= 2; r += 0.2) {
// TODO: no PrecisionUtils in getDivided()
assertTrue(r != 0);
assertTrue(a.getDivided(r)
.equals(new Vector(a.x / r, a.y / r)));
}
boolean thrown = false;
try {
a.getDivided(0);
} catch (ArithmeticException e) {
thrown = true;
}
assertTrue(thrown);
}
});
}
@Test
public void test_getDotProduct() {
forVectorPairs(new VectorPairAction() {
@Override
public void action(Vector a, Vector b) {
assertTrue(PrecisionUtils.equal(a.getDotProduct(b),
a.x * b.x + a.y * b.y));
}
});
}
@Test
public void test_getLength() {
assertEquals(new Vector(3, 4).getLength(), 5.0d, 0.0d);
forVectorPairs(new VectorPairAction() {
@Override
public void action(Vector a, Vector b) {
if (a.isNull()) {
assertTrue(PrecisionUtils.equal(a.getLength(), 0));
} else if (b.isNull()) {
assertTrue(PrecisionUtils.equal(b.getLength(), 0));
} else {
assertTrue(PrecisionUtils.equal(
a.getDivided(a.getLength()).getLength(),
(b.getDivided(b.getLength()).getLength())));
}
}
});
}
@Test
public void test_getMultiplied() {
forVectorPairs(new VectorPairAction() {
@Override
public void action(Vector a, Vector b) {
assertTrue(a.getMultiplied(b.x)
.equals(new Vector(a.x * b.x, a.y * b.x)));
assertTrue(a.getMultiplied(b.y)
.equals(new Vector(a.x * b.y, a.y * b.y)));
}
});
}
@Test
public void test_getNormalized() {
final Vector x = new Vector(1, 0);
forVectors(new VectorAction() {
@Override
public void action(Vector a) {
assertTrue(a.x != 0);
assertTrue(a.y != 0);
Vector normalized = a.getNormalized();
Angle origAlpha = a.getAngleCW(x);
Angle normAlpha = normalized.getAngleCW(x);
assertTrue(PrecisionUtils.equal(normalized.getLength(), 1));
assertTrue(
PrecisionUtils.equal(origAlpha.rad(), normAlpha.rad()));
}
});
}
@Test
public void test_getOrthoComplement() {
Vector a = new Vector(3, -5);
assertTrue(a.getOrthogonalComplement().equals(new Vector(5, 3)));
forVectors(new VectorAction() {
@Override
public void action(Vector a) {
assertTrue(PrecisionUtils.equal(
a.getOrthogonalComplement().getDotProduct(a), 0));
}
});
}
@Test
public void test_getSimilarity() {
forVectorPairs(new VectorPairAction() {
@Override
public void action(Vector a, Vector b) {
assertTrue(PrecisionUtils.equal(a.getSimilarity(b),
Math.abs(a.getDotProduct(b))));
}
});
}
@Test
public void test_getSubtracted() {
forVectorPairs(new VectorPairAction() {
@Override
public void action(Vector a, Vector b) {
assertTrue(a.getSubtracted(b)
.equals(new Vector(a.x - b.x, a.y - b.y)));
}
});
}
@Test
public void test_isHorizontal() {
forVectors(new VectorAction() {
@Override
public void action(Vector a) {
assertTrue(a.isHorizontal() == (!PrecisionUtils.equal(a.x, 0)
&& PrecisionUtils.equal(a.y, 0)));
}
});
}
@Test
public void test_isNull() {
forVectors(new VectorAction() {
@Override
public void action(Vector a) {
assertTrue(a.isNull() == (PrecisionUtils.equal(a.x, 0)
&& PrecisionUtils.equal(a.y, 0)));
}
});
}
@Test
public void test_isOrthogonalTo() {
forVectorPairs(new VectorPairAction() {
@Override
public void action(Vector a, Vector b) {
assertTrue(a.isOrthogonalTo(b) == PrecisionUtils
.equal(a.getDotProduct(b), 0));
}
});
}
@Test
public void test_isParallelTo() {
forVectorPairs(new VectorPairAction() {
@Override
public void action(Vector a, Vector b) {
// TODO: rewrite this test!
assertTrue(a.isParallelTo(b) == PrecisionUtils
.equal(a.getDissimilarity(b), 0));
}
});
}
@Test
public void test_isVertical() {
forVectors(new VectorAction() {
@Override
public void action(Vector a) {
assertTrue(a.isVertical() == (!PrecisionUtils.equal(a.y, 0)
&& PrecisionUtils.equal(a.x, 0)));
}
});
}
@Test
public void test_rotateCCW() {
forVectors(new VectorAction() {
@Override
public void action(Vector a) {
for (double alpha = 0; alpha <= 4 * Math.PI; alpha += 0.1) {
Angle angle = Angle.fromRad(alpha);
Vector rotated = a.getRotatedCCW(angle);
double nAlpha = angle.getOppositeFull().rad();
double x = a.x * Math.cos(nAlpha) - a.y * Math.sin(nAlpha);
double y = a.x * Math.sin(nAlpha) + a.y * Math.cos(nAlpha);
assertTrue(rotated.equals(new Vector(x, y)));
a.rotateCCW(angle);
assertTrue(rotated.equals(a));
}
}
});
}
@Test
public void test_rotateCW() {
forVectors(new VectorAction() {
@Override
public void action(Vector a) {
for (double alpha = 0; alpha <= 4 * Math.PI; alpha += 0.1) {
Angle angle = Angle.fromRad(alpha);
Vector rotated = a.getRotatedCW(angle);
double x = a.x * Math.cos(alpha) - a.y * Math.sin(alpha);
double y = a.x * Math.sin(alpha) + a.y * Math.cos(alpha);
assertTrue(rotated.equals(new Vector(x, y)));
a.rotateCW(angle);
assertTrue(rotated.equals(a));
}
}
});
}
@Test
public void test_toPoint() {
forVectors(new VectorAction() {
@Override
public void action(Vector a) {
Point p = a.toPoint();
assertTrue(PrecisionUtils.equal(a.x, p.x));
assertTrue(PrecisionUtils.equal(a.y, p.y));
}
});
}
@Test
public void test_toString() {
Vector a = new Vector(0, 0);
assertEquals("Vector: [0.0,0.0]", a.toString());
}
}