blob: 76445e00471470a6bdbc4113a530a2ae852ef46a [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.geometry.euclidean.twod;
import org.apache.commons.geometry.core.Geometry;
import org.apache.commons.geometry.core.GeometryTestUtils;
import org.apache.commons.geometry.core.exception.IllegalNormException;
import org.apache.commons.geometry.euclidean.EuclideanTestUtils;
import org.apache.commons.geometry.euclidean.exception.NonInvertibleTransformException;
import org.junit.Assert;
import org.junit.Test;
public class AffineTransformMatrix2DTest {
private static final double EPS = 1e-12;
@Test
public void testOf() {
// arrange
double[] arr = {
1, 2, 3,
4, 5, 6
};
// act
AffineTransformMatrix2D transform = AffineTransformMatrix2D.of(arr);
// assert
double[] result = transform.toArray();
Assert.assertNotSame(arr, result);
Assert.assertArrayEquals(arr, result, 0.0);
}
@Test
public void testOf_invalidDimensions() {
// act/assert
GeometryTestUtils.assertThrows(() -> AffineTransformMatrix2D.of(1, 2),
IllegalArgumentException.class, "Dimension mismatch: 2 != 6");
}
@Test
public void testFromColumnVectors_twoVector() {
// arrange
Vector2D u = Vector2D.of(1, 2);
Vector2D v = Vector2D.of(3, 4);
// act
AffineTransformMatrix2D transform = AffineTransformMatrix2D.fromColumnVectors(u, v);
// assert
Assert.assertArrayEquals(new double[] {
1, 3, 0,
2, 4, 0
}, transform.toArray(), 0.0);
}
@Test
public void testFromColumnVectors_threeVectors() {
// arrange
Vector2D u = Vector2D.of(1, 2);
Vector2D v = Vector2D.of(3, 4);
Vector2D t = Vector2D.of(5, 6);
// act
AffineTransformMatrix2D transform = AffineTransformMatrix2D.fromColumnVectors(u, v, t);
// assert
Assert.assertArrayEquals(new double[] {
1, 3, 5,
2, 4, 6
}, transform.toArray(), 0.0);
}
@Test
public void testIdentity() {
// act
AffineTransformMatrix2D transform = AffineTransformMatrix2D.identity();
// assert
double[] expected = {
1, 0, 0,
0, 1, 0
};
Assert.assertArrayEquals(expected, transform.toArray(), 0.0);
}
@Test
public void testCreateTranslation_xy() {
// act
AffineTransformMatrix2D transform = AffineTransformMatrix2D.createTranslation(2, 3);
// assert
double[] expected = {
1, 0, 2,
0, 1, 3
};
Assert.assertArrayEquals(expected, transform.toArray(), 0.0);
}
@Test
public void testCreateTranslation_vector() {
// act
AffineTransformMatrix2D transform = AffineTransformMatrix2D.createTranslation(Vector2D.of(5, 6));
// assert
double[] expected = {
1, 0, 5,
0, 1, 6
};
Assert.assertArrayEquals(expected, transform.toArray(), 0.0);
}
@Test
public void testCreateScale_xy() {
// act
AffineTransformMatrix2D transform = AffineTransformMatrix2D.createScale(2, 3);
// assert
double[] expected = {
2, 0, 0,
0, 3, 0
};
Assert.assertArrayEquals(expected, transform.toArray(), 0.0);
}
@Test
public void testTranslate_xy() {
// arrange
AffineTransformMatrix2D a = AffineTransformMatrix2D.of(
2, 0, 10,
0, 3, 11
);
// act
AffineTransformMatrix2D result = a.translate(4, 5);
// assert
double[] expected = {
2, 0, 14,
0, 3, 16
};
Assert.assertArrayEquals(expected, result.toArray(), 0.0);
}
@Test
public void testTranslate_vector() {
// arrange
AffineTransformMatrix2D a = AffineTransformMatrix2D.of(
2, 0, 10,
0, 3, 11
);
// act
AffineTransformMatrix2D result = a.translate(Vector2D.of(7, 8));
// assert
double[] expected = {
2, 0, 17,
0, 3, 19
};
Assert.assertArrayEquals(expected, result.toArray(), 0.0);
}
@Test
public void testCreateScale_vector() {
// act
AffineTransformMatrix2D transform = AffineTransformMatrix2D.createScale(Vector2D.of(4, 5));
// assert
double[] expected = {
4, 0, 0,
0, 5, 0
};
Assert.assertArrayEquals(expected, transform.toArray(), 0.0);
}
@Test
public void testCreateScale_singleValue() {
// act
AffineTransformMatrix2D transform = AffineTransformMatrix2D.createScale(7);
// assert
double[] expected = {
7, 0, 0,
0, 7, 0
};
Assert.assertArrayEquals(expected, transform.toArray(), 0.0);
}
@Test
public void testScale_xy() {
// arrange
AffineTransformMatrix2D a = AffineTransformMatrix2D.of(
2, 0, 10,
0, 3, 11
);
// act
AffineTransformMatrix2D result = a.scale(4, 5);
// assert
double[] expected = {
8, 0, 40,
0, 15, 55
};
Assert.assertArrayEquals(expected, result.toArray(), 0.0);
}
@Test
public void testScale_vector() {
// arrange
AffineTransformMatrix2D a = AffineTransformMatrix2D.of(
2, 0, 10,
0, 3, 11
);
// act
AffineTransformMatrix2D result = a.scale(Vector2D.of(7, 8));
// assert
double[] expected = {
14, 0, 70,
0, 24, 88
};
Assert.assertArrayEquals(expected, result.toArray(), 0.0);
}
@Test
public void testScale_singleValue() {
// arrange
AffineTransformMatrix2D a = AffineTransformMatrix2D.of(
2, 0, 10,
0, 3, 11
);
// act
AffineTransformMatrix2D result = a.scale(10);
// assert
double[] expected = {
20, 0, 100,
0, 30, 110
};
Assert.assertArrayEquals(expected, result.toArray(), 0.0);
}
@Test
public void testCreateRotation() {
// act
double angle = Geometry.PI * 2.0 / 3.0;
AffineTransformMatrix2D transform = AffineTransformMatrix2D.createRotation(angle);
// assert
double sin = Math.sin(angle);
double cos = Math.cos(angle);
double[] expected = {
cos, -sin, 0,
sin, cos, 0
};
Assert.assertArrayEquals(expected, transform.toArray(), EPS);
}
@Test
public void testCreateRotation_aroundCenter() {
// act
Vector2D center = Vector2D.of(1, 2);
double angle = Geometry.PI * 2.0 / 3.0;
AffineTransformMatrix2D transform = AffineTransformMatrix2D.createRotation(center, angle);
// assert
double sin = Math.sin(angle);
double cos = Math.cos(angle);
double[] expected = {
cos, -sin, -cos + 2*sin + 1,
sin, cos, -sin - 2*cos + 2
};
Assert.assertArrayEquals(expected, transform.toArray(), EPS);
}
@Test
public void testRotate() {
// arrange
AffineTransformMatrix2D a = AffineTransformMatrix2D.of(
1, 2, 3,
4, 5, 6
);
// act
AffineTransformMatrix2D result = a.rotate(Geometry.HALF_PI);
// assert
double[] expected = {
-4, -5, -6,
1, 2, 3
};
Assert.assertArrayEquals(expected, result.toArray(), EPS);
}
@Test
public void testRotate_aroundCenter() {
// arrange
Vector2D center = Vector2D.of(1, 2);
AffineTransformMatrix2D a = AffineTransformMatrix2D.of(
1, 2, 3,
4, 5, 6
);
// act
AffineTransformMatrix2D result = a.rotate(center, Geometry.HALF_PI);
// assert
double[] expected = {
-4, -5, -3,
1, 2, 4
};
Assert.assertArrayEquals(expected, result.toArray(), EPS);
}
@Test
public void testApply_identity() {
// arrange
AffineTransformMatrix2D transform = AffineTransformMatrix2D.identity();
// act/assert
runWithCoordinates((x, y) -> {
Vector2D v = Vector2D.of(x, y);
EuclideanTestUtils.assertCoordinatesEqual(v, transform.apply(v), EPS);
});
}
@Test
public void testApply_translate() {
// arrange
Vector2D translation = Vector2D.of(1.1, -Geometry.PI);
AffineTransformMatrix2D transform = AffineTransformMatrix2D.identity()
.translate(translation);
// act/assert
runWithCoordinates((x, y) -> {
Vector2D vec = Vector2D.of(x, y);
Vector2D expectedVec = vec.add(translation);
EuclideanTestUtils.assertCoordinatesEqual(expectedVec, transform.apply(vec), EPS);
});
}
@Test
public void testApply_scale() {
// arrange
Vector2D factors = Vector2D.of(2.0, -3.0);
AffineTransformMatrix2D transform = AffineTransformMatrix2D.identity()
.scale(factors);
// act/assert
runWithCoordinates((x, y) -> {
Vector2D vec = Vector2D.of(x, y);
Vector2D expectedVec = Vector2D.of(factors.getX() * x, factors.getY() * y);
EuclideanTestUtils.assertCoordinatesEqual(expectedVec, transform.apply(vec), EPS);
});
}
@Test
public void testApply_rotate() {
// arrange
AffineTransformMatrix2D transform = AffineTransformMatrix2D.identity()
.rotate(Geometry.MINUS_HALF_PI);
// act/assert
runWithCoordinates((x, y) -> {
Vector2D vec = Vector2D.of(x, y);
Vector2D expectedVec = Vector2D.of(y, -x);
EuclideanTestUtils.assertCoordinatesEqual(expectedVec, transform.apply(vec), EPS);
});
}
@Test
public void testApply_rotate_aroundCenter_minusHalfPi() {
// arrange
Vector2D center = Vector2D.of(1, 2);
AffineTransformMatrix2D transform = AffineTransformMatrix2D.identity()
.rotate(center, Geometry.MINUS_HALF_PI);
// act/assert
runWithCoordinates((x, y) -> {
Vector2D vec = Vector2D.of(x, y);
Vector2D centered = vec.subtract(center);
Vector2D expectedVec = Vector2D.of(centered.getY(), -centered.getX()).add(center);
EuclideanTestUtils.assertCoordinatesEqual(expectedVec, transform.apply(vec), EPS);
});
}
@Test
public void testApply_rotate_aroundCenter_pi() {
// arrange
Vector2D center = Vector2D.of(1, 2);
AffineTransformMatrix2D transform = AffineTransformMatrix2D.identity()
.rotate(center, Geometry.PI);
// act/assert
runWithCoordinates((x, y) -> {
Vector2D vec = Vector2D.of(x, y);
Vector2D centered = vec.subtract(center);
Vector2D expectedVec = Vector2D.of(-centered.getX(), -centered.getY()).add(center);
EuclideanTestUtils.assertCoordinatesEqual(expectedVec, transform.apply(vec), EPS);
});
}
@Test
public void testApply_translateScaleRotate() {
// arrange
Vector2D translation = Vector2D.of(-2.0, -3.0);
Vector2D scale = Vector2D.of(5.0, 6.0);
AffineTransformMatrix2D transform = AffineTransformMatrix2D.identity()
.translate(translation)
.scale(scale)
.rotate(Geometry.HALF_PI);
// act/assert
EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(12, -5), transform.apply(Vector2D.of(1, 1)), EPS);
runWithCoordinates((x, y) -> {
Vector2D vec = Vector2D.of(x, y);
Vector2D temp = Vector2D.of(
(x + translation.getX()) * scale.getX(),
(y + translation.getY()) * scale.getY()
);
Vector2D expectedVec = Vector2D.of(-temp.getY(), temp.getX());
EuclideanTestUtils.assertCoordinatesEqual(expectedVec, transform.apply(vec), EPS);
});
}
@Test
public void testApply_scaleTranslateRotate() {
// arrange
Vector2D scale = Vector2D.of(5.0, 6.0);
Vector2D translation = Vector2D.of(-2.0, -3.0);
AffineTransformMatrix2D transform = AffineTransformMatrix2D.identity()
.scale(scale)
.translate(translation)
.rotate(Geometry.MINUS_HALF_PI);
// act/assert
runWithCoordinates((x, y) -> {
Vector2D vec = Vector2D.of(x, y);
Vector2D temp = Vector2D.of(
(x * scale.getX()) + translation.getX(),
(y * scale.getY()) + translation.getY()
);
Vector2D expectedVec = Vector2D.of(temp.getY(), -temp.getX());
EuclideanTestUtils.assertCoordinatesEqual(expectedVec, transform.apply(vec), EPS);
});
}
@Test
public void testApplyVector_identity() {
// arrange
AffineTransformMatrix2D transform = AffineTransformMatrix2D.identity();
// act/assert
runWithCoordinates((x, y) -> {
Vector2D v = Vector2D.of(x, y);
EuclideanTestUtils.assertCoordinatesEqual(v, transform.applyVector(v), EPS);
});
}
@Test
public void testApplyVector_translate() {
// arrange
Vector2D translation = Vector2D.of(1.1, -Geometry.PI);
AffineTransformMatrix2D transform = AffineTransformMatrix2D.identity()
.translate(translation);
// act/assert
runWithCoordinates((x, y) -> {
Vector2D vec = Vector2D.of(x, y);
EuclideanTestUtils.assertCoordinatesEqual(vec, transform.applyVector(vec), EPS);
});
}
@Test
public void testApplyVector_scale() {
// arrange
Vector2D factors = Vector2D.of(2.0, -3.0);
AffineTransformMatrix2D transform = AffineTransformMatrix2D.identity()
.scale(factors);
// act/assert
runWithCoordinates((x, y) -> {
Vector2D vec = Vector2D.of(x, y);
Vector2D expectedVec = Vector2D.of(factors.getX() * x, factors.getY() * y);
EuclideanTestUtils.assertCoordinatesEqual(expectedVec, transform.applyVector(vec), EPS);
});
}
@Test
public void testApplyVector_representsDisplacement() {
// arrange
Vector2D p1 = Vector2D.of(2, 3);
AffineTransformMatrix2D transform = AffineTransformMatrix2D.identity()
.scale(1.5)
.translate(4, 6)
.rotate(Geometry.HALF_PI);
// act/assert
runWithCoordinates((x, y) -> {
Vector2D p2 = Vector2D.of(x, y);
Vector2D input = p1.subtract(p2);
Vector2D expected = transform.apply(p1).subtract(transform.apply(p2));
EuclideanTestUtils.assertCoordinatesEqual(expected, transform.applyVector(input), EPS);
});
}
@Test
public void testApplyDirection_identity() {
// arrange
AffineTransformMatrix2D transform = AffineTransformMatrix2D.identity();
// act/assert
EuclideanTestUtils.permuteSkipZero(-5, 5, 0.5, (x, y) -> {
Vector2D v = Vector2D.of(x, y);
EuclideanTestUtils.assertCoordinatesEqual(v.normalize(), transform.applyDirection(v), EPS);
});
}
@Test
public void testApplyDirection_translate() {
// arrange
Vector2D translation = Vector2D.of(1.1, -Geometry.PI);
AffineTransformMatrix2D transform = AffineTransformMatrix2D.identity()
.translate(translation);
// act/assert
EuclideanTestUtils.permuteSkipZero(-5, 5, 0.5, (x, y) -> {
Vector2D vec = Vector2D.of(x, y);
EuclideanTestUtils.assertCoordinatesEqual(vec.normalize(), transform.applyDirection(vec), EPS);
});
}
@Test
public void testApplyDirection_scale() {
// arrange
Vector2D factors = Vector2D.of(2.0, -3.0);
AffineTransformMatrix2D transform = AffineTransformMatrix2D.identity()
.scale(factors);
// act/assert
EuclideanTestUtils.permuteSkipZero(-5, 5, 0.5, (x, y) -> {
Vector2D vec = Vector2D.of(x, y);
Vector2D expectedVec = Vector2D.of(factors.getX() * x, factors.getY() * y).normalize();
EuclideanTestUtils.assertCoordinatesEqual(expectedVec, transform.applyDirection(vec), EPS);
});
}
@Test
public void testApplyDirection_representsNormalizedDisplacement() {
// arrange
Vector2D p1 = Vector2D.of(2.1, 3.2);
AffineTransformMatrix2D transform = AffineTransformMatrix2D.identity()
.scale(1.5)
.translate(4, 6)
.rotate(Geometry.HALF_PI);
// act/assert
EuclideanTestUtils.permute(-5, 5, 0.5, (x, y) -> {
Vector2D p2 = Vector2D.of(x, y);
Vector2D input = p1.subtract(p2);
Vector2D expected = transform.apply(p1).subtract(transform.apply(p2)).normalize();
EuclideanTestUtils.assertCoordinatesEqual(expected, transform.applyDirection(input), EPS);
});
}
@Test
public void testApplyDirection_illegalNorm() {
// act/assert
GeometryTestUtils.assertThrows(() -> AffineTransformMatrix2D.createScale(1, 0).applyDirection(Vector2D.Unit.PLUS_Y),
IllegalNormException.class);
GeometryTestUtils.assertThrows(() -> AffineTransformMatrix2D.createScale(2).applyDirection(Vector2D.ZERO),
IllegalNormException.class);
}
@Test
public void testDeterminant() {
// act/assert
Assert.assertEquals(1.0, AffineTransformMatrix2D.identity().determinant(), EPS);
Assert.assertEquals(6.0, AffineTransformMatrix2D.of(
2, 0, 4,
0, 3, 5
).determinant(), EPS);
Assert.assertEquals(-6.0, AffineTransformMatrix2D.of(
2, 0, 4,
0, -3, 5
).determinant(), EPS);
Assert.assertEquals(-5.0, AffineTransformMatrix2D.of(
1, 3, 0,
2, 1, 0
).determinant(), EPS);
Assert.assertEquals(-0.0, AffineTransformMatrix2D.of(
0, 0, 1,
0, 0, 2
).determinant(), EPS);
}
@Test
public void testPreservesOrientation() {
// act/assert
Assert.assertTrue(AffineTransformMatrix2D.identity().preservesOrientation());
Assert.assertTrue(AffineTransformMatrix2D.of(
2, 0, 4,
0, 3, 5
).preservesOrientation());
Assert.assertFalse(AffineTransformMatrix2D.of(
2, 0, 4,
0, -3, 5
).preservesOrientation());
Assert.assertFalse(AffineTransformMatrix2D.of(
1, 3, 0,
2, 1, 0
).preservesOrientation());
Assert.assertFalse(AffineTransformMatrix2D.of(
0, 0, 1,
0, 0, 2
).preservesOrientation());
}
@Test
public void testToMatrix() {
// arrange
AffineTransformMatrix2D t = AffineTransformMatrix2D.createScale(2.0);
// act/assert
Assert.assertSame(t, t.toMatrix());
}
@Test
public void testMultiply() {
// arrange
AffineTransformMatrix2D a = AffineTransformMatrix2D.of(
1, 2, 3,
5, 6, 7
);
AffineTransformMatrix2D b = AffineTransformMatrix2D.of(
13, 14, 15,
17, 18, 19
);
// act
AffineTransformMatrix2D result = a.multiply(b);
// assert
double[] arr = result.toArray();
Assert.assertArrayEquals(new double[] {
47, 50, 56,
167, 178, 196
}, arr, EPS);
}
@Test
public void testMultiply_combinesTransformOperations() {
// arrange
Vector2D translation1 = Vector2D.of(1, 2);
double scale = 2.0;
Vector2D translation2 = Vector2D.of(4, 5);
AffineTransformMatrix2D a = AffineTransformMatrix2D.createTranslation(translation1);
AffineTransformMatrix2D b = AffineTransformMatrix2D.createScale(scale);
AffineTransformMatrix2D c = AffineTransformMatrix2D.identity();
AffineTransformMatrix2D d = AffineTransformMatrix2D.createTranslation(translation2);
// act
AffineTransformMatrix2D transform = d.multiply(c).multiply(b).multiply(a);
// assert
runWithCoordinates((x, y) -> {
Vector2D vec = Vector2D.of(x, y);
Vector2D expectedVec = vec
.add(translation1)
.multiply(scale)
.add(translation2);
EuclideanTestUtils.assertCoordinatesEqual(expectedVec, transform.apply(vec), EPS);
});
}
@Test
public void testPremultiply() {
// arrange
AffineTransformMatrix2D a = AffineTransformMatrix2D.of(
1, 2, 3,
5, 6, 7
);
AffineTransformMatrix2D b = AffineTransformMatrix2D.of(
13, 14, 15,
17, 18, 19
);
// act
AffineTransformMatrix2D result = b.premultiply(a);
// assert
double[] arr = result.toArray();
Assert.assertArrayEquals(new double[] {
47, 50, 56,
167, 178, 196
}, arr, EPS);
}
@Test
public void testPremultiply_combinesTransformOperations() {
// arrange
Vector2D translation1 = Vector2D.of(1, 2);
double scale = 2.0;
Vector2D translation2 = Vector2D.of(4, 5);
AffineTransformMatrix2D a = AffineTransformMatrix2D.createTranslation(translation1);
AffineTransformMatrix2D b = AffineTransformMatrix2D.createScale(scale);
AffineTransformMatrix2D c = AffineTransformMatrix2D.identity();
AffineTransformMatrix2D d = AffineTransformMatrix2D.createTranslation(translation2);
// act
AffineTransformMatrix2D transform = a.premultiply(b).premultiply(c).premultiply(d);
// assert
runWithCoordinates((x, y) -> {
Vector2D vec = Vector2D.of(x, y);
Vector2D expectedVec = vec
.add(translation1)
.multiply(scale)
.add(translation2);
EuclideanTestUtils.assertCoordinatesEqual(expectedVec, transform.apply(vec), EPS);
});
}
@Test
public void testInverse_identity() {
// act
AffineTransformMatrix2D inverse = AffineTransformMatrix2D.identity().inverse();
// assert
double[] expected = {
1, 0, 0,
0, 1, 0
};
Assert.assertArrayEquals(expected, inverse.toArray(), 0.0);
}
@Test
public void testInverse_multiplyByInverse_producesIdentity() {
// arrange
AffineTransformMatrix2D a = AffineTransformMatrix2D.of(
1, 3, 7,
2, 4, 9
);
AffineTransformMatrix2D inv = a.inverse();
// act
AffineTransformMatrix2D result = inv.multiply(a);
// assert
double[] expected = {
1, 0, 0,
0, 1, 0
};
Assert.assertArrayEquals(expected, result.toArray(), EPS);
}
@Test
public void testInverse_translate() {
// arrange
AffineTransformMatrix2D transform = AffineTransformMatrix2D.createTranslation(1, -2);
// act
AffineTransformMatrix2D inverse = transform.inverse();
// assert
double[] expected = {
1, 0, -1,
0, 1, 2
};
Assert.assertArrayEquals(expected, inverse.toArray(), 0.0);
}
@Test
public void testInverse_scale() {
// arrange
AffineTransformMatrix2D transform = AffineTransformMatrix2D.createScale(10, -2);
// act
AffineTransformMatrix2D inverse = transform.inverse();
// assert
double[] expected = {
0.1, 0, 0,
0, -0.5, 0
};
Assert.assertArrayEquals(expected, inverse.toArray(), 0.0);
}
@Test
public void testInverse_rotate() {
// arrange
AffineTransformMatrix2D transform = AffineTransformMatrix2D.createRotation(Geometry.HALF_PI);
// act
AffineTransformMatrix2D inverse = transform.inverse();
// assert
double[] expected = {
0, 1, 0,
-1, 0, 0
};
Assert.assertArrayEquals(expected, inverse.toArray(), EPS);
}
@Test
public void testInverse_rotate_aroundCenter() {
// arrange
Vector2D center = Vector2D.of(1, 2);
AffineTransformMatrix2D transform = AffineTransformMatrix2D.createRotation(center, Geometry.HALF_PI);
// act
AffineTransformMatrix2D inverse = transform.inverse();
// assert
double[] expected = {
0, 1, -1,
-1, 0, 3
};
Assert.assertArrayEquals(expected, inverse.toArray(), EPS);
}
@Test
public void testInverse_undoesOriginalTransform() {
// arrange
Vector2D v1 = Vector2D.ZERO;
Vector2D v2 = Vector2D.Unit.PLUS_X;
Vector2D v3 = Vector2D.of(1, 1);
Vector2D v4 = Vector2D.of(-2, 3);
Vector2D center = Vector2D.of(-0.5, 2);
// act/assert
runWithCoordinates((x, y) -> {
AffineTransformMatrix2D transform = AffineTransformMatrix2D
.createTranslation(x, y)
.scale(2, 3)
.translate(x / 3, y / 3)
.rotate(x / 4)
.rotate(center, y / 2);
AffineTransformMatrix2D inverse = transform.inverse();
EuclideanTestUtils.assertCoordinatesEqual(v1, inverse.apply(transform.apply(v1)), EPS);
EuclideanTestUtils.assertCoordinatesEqual(v2, inverse.apply(transform.apply(v2)), EPS);
EuclideanTestUtils.assertCoordinatesEqual(v3, inverse.apply(transform.apply(v3)), EPS);
EuclideanTestUtils.assertCoordinatesEqual(v4, inverse.apply(transform.apply(v4)), EPS);
});
}
@Test
public void testInverse_nonInvertible() {
// act/assert
GeometryTestUtils.assertThrows(() -> {
AffineTransformMatrix2D.of(
0, 0, 0,
0, 0, 0).inverse();
}, NonInvertibleTransformException.class, "Transform is not invertible; matrix determinant is 0.0");
GeometryTestUtils.assertThrows(() -> {
AffineTransformMatrix2D.of(
1, 0, 0,
0, Double.NaN, 0).inverse();
}, NonInvertibleTransformException.class, "Transform is not invertible; matrix determinant is NaN");
GeometryTestUtils.assertThrows(() -> {
AffineTransformMatrix2D.of(
1, 0, 0,
0, Double.NEGATIVE_INFINITY, 0).inverse();
}, NonInvertibleTransformException.class, "Transform is not invertible; matrix determinant is -Infinity");
GeometryTestUtils.assertThrows(() -> {
AffineTransformMatrix2D.of(
Double.POSITIVE_INFINITY, 0, 0,
0, 1, 0).inverse();
}, NonInvertibleTransformException.class, "Transform is not invertible; matrix determinant is Infinity");
GeometryTestUtils.assertThrows(() -> {
AffineTransformMatrix2D.of(
1, 0, Double.NaN,
0, 1, 0).inverse();
}, NonInvertibleTransformException.class, "Transform is not invertible; invalid matrix element: NaN");
GeometryTestUtils.assertThrows(() -> {
AffineTransformMatrix2D.of(
1, 0, Double.POSITIVE_INFINITY,
0, 1, 0).inverse();
}, NonInvertibleTransformException.class, "Transform is not invertible; invalid matrix element: Infinity");
GeometryTestUtils.assertThrows(() -> {
AffineTransformMatrix2D.of(
1, 0, Double.NEGATIVE_INFINITY,
0, 1, 0).inverse();
}, NonInvertibleTransformException.class, "Transform is not invertible; invalid matrix element: -Infinity");
}
@Test
public void testHashCode() {
// arrange
double[] values = new double[] {
1, 2, 3,
5, 6, 7
};
// act/assert
int orig = AffineTransformMatrix2D.of(values).hashCode();
int same = AffineTransformMatrix2D.of(values).hashCode();
Assert.assertEquals(orig, same);
double[] temp;
for (int i=0; i<values.length; ++i) {
temp = values.clone();
temp[i] = 0;
int modified = AffineTransformMatrix2D.of(temp).hashCode();
Assert.assertNotEquals(orig, modified);
}
}
@Test
public void testEquals() {
// arrange
double[] values = new double[] {
1, 2, 3,
5, 6, 7
};
AffineTransformMatrix2D a = AffineTransformMatrix2D.of(values);
// act/assert
Assert.assertTrue(a.equals(a));
Assert.assertFalse(a.equals(null));
Assert.assertFalse(a.equals(new Object()));
double[] temp;
for (int i=0; i<values.length; ++i) {
temp = values.clone();
temp[i] = 0;
AffineTransformMatrix2D modified = AffineTransformMatrix2D.of(temp);
Assert.assertFalse(a.equals(modified));
}
}
@Test
public void testToString() {
// arrange
AffineTransformMatrix2D a = AffineTransformMatrix2D.of(
1, 2, 3,
5, 6, 7
);
// act
String result = a.toString();
// assert
Assert.assertEquals("[ 1.0, 2.0, 3.0; "
+ "5.0, 6.0, 7.0 ]", result);
}
@FunctionalInterface
private static interface Coordinate2DTest {
void run(double x, double y);
}
private static void runWithCoordinates(Coordinate2DTest test) {
runWithCoordinates(test, -1e-2, 1e-2, 5e-3);
runWithCoordinates(test, -1e2, 1e2, 5);
}
private static void runWithCoordinates(Coordinate2DTest test, double min, double max, double step)
{
for (double x = min; x <= max; x += step) {
for (double y = min; y <= max; y += step) {
test.run(x, y);
}
}
}
}