package org.robolectric.shadows;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.graphics.RectF;
import android.os.Build;
import org.assertj.core.data.Offset;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.TestRunners;
import org.robolectric.annotation.Config;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
import static org.robolectric.Shadows.shadowOf;
@RunWith(TestRunners.MultiApiSelfTest.class)
public class ShadowMatrixTest {
private static final float EPSILON = 1e-7f;
@Test
public void preOperationsAreStacked() {
Matrix m = new Matrix();
m.preRotate(4, 8, 15);
m.preTranslate(16, 23);
m.preSkew(42, 108);
assertThat(shadowOf(m).getPreOperations()).containsExactly(
"skew 42.0 108.0",
"translate 16.0 23.0",
"rotate 4.0 8.0 15.0"
);
}
@Test
public void postOperationsAreQueued() {
Matrix m = new Matrix();
m.postRotate(4, 8, 15);
m.postTranslate(16, 23);
m.postSkew(42, 108);
assertThat(shadowOf(m).getPostOperations()).containsExactly(
"rotate 4.0 8.0 15.0",
"translate 16.0 23.0",
"skew 42.0 108.0"
);
}
@Test
public void setOperationsOverride() {
Matrix m = new Matrix();
m.setRotate(4);
m.setRotate(8);
m.setRotate(15);
m.setRotate(16);
m.setRotate(23);
m.setRotate(42);
m.setRotate(108);
assertThat(shadowOf(m).getSetOperations()).contains(entry("rotate", "108.0"));
}
@Test
public void set_shouldAddOpsToMatrix() {
final Matrix matrix = new Matrix();
matrix.setScale(1, 1);
matrix.preScale(2, 2, 2, 2);
matrix.postScale(3, 3, 3, 3);
final ShadowMatrix shadow = shadowOf(matrix);
assertThat(shadow.getSetOperations().get("scale")).isEqualTo("1.0 1.0");
assertThat(shadow.getPreOperations().get(0)).isEqualTo("scale 2.0 2.0 2.0 2.0");
assertThat(shadow.getPostOperations().get(0)).isEqualTo("scale 3.0 3.0 3.0 3.0");
}
@Test
public void setScale_shouldAddOpsToMatrix() {
final Matrix matrix = new Matrix();
matrix.setScale(1, 2, 3, 4);
final ShadowMatrix shadow = shadowOf(matrix);
assertThat(shadow.getSetOperations().get("scale")).isEqualTo("1.0 2.0 3.0 4.0");
}
@Test
public void set_shouldOverrideValues(){
final Matrix matrix1 = new Matrix();
matrix1.setScale(1, 2);
final Matrix matrix2 = new Matrix();
matrix2.setScale(3, 4);
matrix2.set(matrix1);
final ShadowMatrix shadow = shadowOf(matrix2);
assertThat(shadow.getSetOperations().get("scale")).isEqualTo("1.0 2.0");
}
@Test
public void set_whenNull_shouldReset() {
final Matrix matrix1 = new Matrix();
matrix1.setScale(1, 2);
final Matrix matrix2 = new Matrix();
matrix2.set(matrix1);
matrix2.set(null);
final ShadowMatrix shadow = shadowOf(matrix2);
assertThat(shadow.getSetOperations()).isEmpty();
}
@Test
public void testIsIdentity() {
final Matrix matrix = new Matrix();
assertThat(matrix.isIdentity())
.isTrue();
matrix.postScale(2.0f, 2.0f);
assertThat(matrix.isIdentity())
.isFalse();
}
@Test
@Config(sdk = {
Build.VERSION_CODES.LOLLIPOP,
Build.VERSION_CODES.LOLLIPOP_MR1,
Build.VERSION_CODES.M
})
public void testIsAffine() {
final Matrix matrix = new Matrix();
assertThat(matrix.isAffine())
.isTrue();
matrix.postScale(2.0f, 2.0f);
assertThat(matrix.isAffine())
.isTrue();
matrix.postTranslate(1.0f, 2.0f);
assertThat(matrix.isAffine())
.isTrue();
matrix.postRotate(45.0f);
assertThat(matrix.isAffine())
.isTrue();
matrix.setValues(new float[] { 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 2.0f });
assertThat(matrix.isAffine())
.isFalse();
}
@Test
public void testRectStaysRect() {
final Matrix matrix = new Matrix();
assertThat(matrix.rectStaysRect())
.isTrue();
matrix.postScale(2.0f, 2.0f);
assertThat(matrix.rectStaysRect())
.isTrue();
matrix.postTranslate(1.0f, 2.0f);
assertThat(matrix.rectStaysRect())
.isTrue();
matrix.postRotate(45.0f);
assertThat(matrix.rectStaysRect())
.isFalse();
matrix.postRotate(45.0f);
assertThat(matrix.rectStaysRect())
.isTrue();
}
@Test
public void testGetSetValues() {
final Matrix matrix = new Matrix();
final float[] values = { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f };
matrix.setValues(values);
final float[] matrixValues = new float[9];
matrix.getValues(matrixValues);
assertThat(matrixValues)
.isEqualTo(values);
}
@Test
public void testSet() {
final Matrix matrix1 = new Matrix();
matrix1.postScale(2.0f, 2.0f);
matrix1.postTranslate(1.0f, 2.0f);
matrix1.postRotate(45.0f);
final Matrix matrix2 = new Matrix();
matrix2.set(matrix1);
assertThat(matrix1)
.isEqualTo(matrix2);
matrix2.set(null);
assertThat(matrix2.isIdentity())
.isTrue();
}
@Test
public void testReset() {
final Matrix matrix = new Matrix();
matrix.postScale(2.0f, 2.0f);
matrix.postTranslate(1.0f, 2.0f);
matrix.postRotate(45.0f);
matrix.reset();
assertThat(matrix.isIdentity())
.isTrue();
}
@Test
public void testSetTranslate() {
final Matrix matrix = new Matrix();
matrix.setTranslate(2.0f, 2.0f);
assertPointsEqual(mapPoint(matrix, 1.0f, 1.0f), new PointF(3.0f, 3.0f));
matrix.setTranslate(-2.0f, -2.0f);
assertPointsEqual(mapPoint(matrix, 1.0f, 1.0f), new PointF(-1.0f, -1.0f));
}
@Test
public void testPostTranslate() {
final Matrix matrix1 = new Matrix();
matrix1.postTranslate(1.0f, 1.0f);
assertPointsEqual(mapPoint(matrix1, 1.0f, 1.0f), new PointF(2.0f, 2.0f));
matrix1.postTranslate(2.0f, 2.0f);
assertPointsEqual(mapPoint(matrix1, 1.0f, 1.0f), new PointF(4.0f, 4.0f));
final Matrix matrix2 = new Matrix();
matrix2.setScale(2.0f, 2.0f);
matrix2.postTranslate(-5.0f, 10.0f);
assertPointsEqual(mapPoint(matrix2, 1.0f, 1.0f), new PointF(-3.0f, 12.0f));
}
@Test
public void testPreTranslate() {
final Matrix matrix1 = new Matrix();
matrix1.preTranslate(1.0f, 1.0f);
assertPointsEqual(mapPoint(matrix1, 1.0f, 1.0f), new PointF(2.0f, 2.0f));
matrix1.preTranslate(2.0f, 2.0f);
assertPointsEqual(mapPoint(matrix1, 1.0f, 1.0f), new PointF(4.0f, 4.0f));
final Matrix matrix2 = new Matrix();
matrix2.setScale(2.0f, 2.0f);
matrix2.preTranslate(-5.0f, 10.0f);
assertPointsEqual(mapPoint(matrix2, 1.0f, 1.0f), new PointF(-8.0f, 22.0f));
}
@Test
public void testSetScale() {
final Matrix matrix = new Matrix();
matrix.setScale(2.0f, 2.0f);
assertPointsEqual(mapPoint(matrix, 1.0f, 1.0f), new PointF(2.0f, 2.0f));
matrix.setScale(-2.0f, -3.0f);
assertPointsEqual(mapPoint(matrix, 2.0f, 3.0f), new PointF(-4.0f, -9.0f));
matrix.setScale(-2.0f, -3.0f, 1.0f, 1.0f);
assertPointsEqual(mapPoint(matrix, 2.0f, 3.0f), new PointF(-1.0f, -5.0f));
}
@Test
public void testPostScale() {
final Matrix matrix1 = new Matrix();
matrix1.postScale(2.0f, 2.0f);
assertPointsEqual(mapPoint(matrix1, 1.0f, 1.0f), new PointF(2.0f, 2.0f));
matrix1.postScale(2.0f, 2.0f);
assertPointsEqual(mapPoint(matrix1, 1.0f, 1.0f), new PointF(4.0f, 4.0f));
final Matrix matrix2 = new Matrix();
matrix2.postScale(2.0f, 2.0f, 1.0f, 1.0f);
assertPointsEqual(mapPoint(matrix2, 1.0f, 1.0f), new PointF(1.0f, 1.0f));
matrix2.setTranslate(1.0f, 2.0f);
matrix2.postScale(2.0f, 2.0f, 1.0f, 1.0f);
assertPointsEqual(mapPoint(matrix2, 1.0f, 1.0f), new PointF(3.0f, 5.0f));
}
@Test
public void testPreScale() {
final Matrix matrix1 = new Matrix();
matrix1.preScale(2.0f, 2.0f);
assertPointsEqual(mapPoint(matrix1, 1.0f, 1.0f), new PointF(2.0f, 2.0f));
matrix1.preScale(2.0f, 2.0f);
assertPointsEqual(mapPoint(matrix1, 1.0f, 1.0f), new PointF(4.0f, 4.0f));
final Matrix matrix2 = new Matrix();
matrix2.preScale(2.0f, 2.0f, 1.0f, 1.0f);
assertPointsEqual(mapPoint(matrix2, 1.0f, 1.0f), new PointF(1.0f, 1.0f));
matrix2.setTranslate(1.0f, 2.0f);
matrix2.preScale(2.0f, 2.0f, 1.0f, 1.0f);
assertPointsEqual(mapPoint(matrix2, 1.0f, 1.0f), new PointF(2.0f, 3.0f));
}
@Test
public void testSetRotate() {
final Matrix matrix = new Matrix();
matrix.setRotate(90.0f);
assertPointsEqual(mapPoint(matrix, 0.0f, 1.0f), new PointF(-1.0f, 0.0f));
matrix.setRotate(180.0f);
assertPointsEqual(mapPoint(matrix, 0.0f, 1.0f), new PointF(0.0f, -1.0f));
matrix.setRotate(270.0f);
assertPointsEqual(mapPoint(matrix, 0.0f, 1.0f), new PointF(1.0f, 0.0f));
matrix.setRotate(360.0f);
assertPointsEqual(mapPoint(matrix, 0.0f, 1.0f), new PointF(0.0f, 1.0f));
matrix.setRotate(45.0f, 0.0f, 1.0f);
assertPointsEqual(mapPoint(matrix, 0.0f, 1.0f), new PointF(0.0f, 1.0f));
}
@Test
public void testPostRotate() {
final Matrix matrix = new Matrix();
matrix.postRotate(90.0f);
assertPointsEqual(mapPoint(matrix, 0.0f, 1.0f), new PointF(-1.0f, 0.0f));
matrix.postRotate(90.0f);
assertPointsEqual(mapPoint(matrix, 0.0f, 1.0f), new PointF(0.0f, -1.0f));
matrix.postRotate(90.0f);
assertPointsEqual(mapPoint(matrix, 0.0f, 1.0f), new PointF(1.0f, 0.0f));
matrix.postRotate(90.0f);
assertPointsEqual(mapPoint(matrix, 0.0f, 1.0f), new PointF(0.0f, 1.0f));
matrix.setTranslate(1.0f, 2.0f);
matrix.postRotate(45.0f, 0.0f, 1.0f);
assertPointsEqual(mapPoint(matrix, 0.0f, 1.0f), new PointF(-0.70710677f, 3.1213202f));
}
@Test
public void testPreRotate() {
final Matrix matrix = new Matrix();
matrix.preRotate(90.0f);
assertPointsEqual(mapPoint(matrix, 0.0f, 1.0f), new PointF(-1.0f, 0.0f));
matrix.preRotate(90.0f);
assertPointsEqual(mapPoint(matrix, 0.0f, 1.0f), new PointF(0.0f, -1.0f));
matrix.preRotate(90.0f);
assertPointsEqual(mapPoint(matrix, 0.0f, 1.0f), new PointF(1.0f, 0.0f));
matrix.preRotate(90.0f);
assertPointsEqual(mapPoint(matrix, 0.0f, 1.0f), new PointF(0.0f, 1.0f));
matrix.setTranslate(1.0f, 2.0f);
matrix.preRotate(45.0f, 0.0f, 1.0f);
assertPointsEqual(mapPoint(matrix, 0.0f, 1.0f), new PointF(1.0f, 3.0f));
}
@Test
public void testSetSinCos() {
final Matrix matrix = new Matrix();
matrix.setSinCos(1.0f, 0.0f);
assertPointsEqual(mapPoint(matrix, 0.0f, 1.0f), new PointF(-1.0f, 0.0f));
matrix.setSinCos(0.0f, -1.0f);
assertPointsEqual(mapPoint(matrix, 0.0f, 1.0f), new PointF(0.0f, -1.0f));
matrix.setSinCos(-1.0f, 0.0f);
assertPointsEqual(mapPoint(matrix, 0.0f, 1.0f), new PointF(1.0f, 0.0f));
matrix.setSinCos(0.0f, 1.0f);
assertPointsEqual(mapPoint(matrix, 0.0f, 1.0f), new PointF(0.0f, 1.0f));
final float sinCos = (float) Math.sqrt(2) / 2;
matrix.setSinCos(sinCos, sinCos, 0.0f, 1.0f);
assertPointsEqual(mapPoint(matrix, 0.0f, 1.0f), new PointF(0.0f, 1.0f));
}
@Test
public void testSetSkew() {
final Matrix matrix = new Matrix();
matrix.setSkew(2.0f, 2.0f);
assertPointsEqual(mapPoint(matrix, 1.0f, 1.0f), new PointF(3.0f, 3.0f));
matrix.setSkew(-2.0f, -3.0f);
assertPointsEqual(mapPoint(matrix, 2.0f, 3.0f), new PointF(-4.0f, -3.0f));
matrix.setSkew(-2.0f, -3.0f, 1.0f, 1.0f);
assertPointsEqual(mapPoint(matrix, 2.0f, 3.0f), new PointF(-2.0f, 0.0f));
}
@Test
public void testPostSkew() {
final Matrix matrix1 = new Matrix();
matrix1.postSkew(2.0f, 2.0f);
assertPointsEqual(mapPoint(matrix1, 1.0f, 1.0f), new PointF(3.0f, 3.0f));
matrix1.postSkew(2.0f, 2.0f);
assertPointsEqual(mapPoint(matrix1, 1.0f, 1.0f), new PointF(9.0f, 9.0f));
final Matrix matrix2 = new Matrix();
matrix2.postSkew(2.0f, 2.0f, 1.0f, 1.0f);
assertPointsEqual(mapPoint(matrix2, 1.0f, 1.0f), new PointF(1.0f, 1.0f));
matrix2.setTranslate(1.0f, 2.0f);
matrix2.postSkew(2.0f, 2.0f, 1.0f, 1.0f);
assertPointsEqual(mapPoint(matrix2, 1.0f, 1.0f), new PointF(6.0f, 5.0f));
}
@Test
public void testPreSkew() {
final Matrix matrix1 = new Matrix();
matrix1.preSkew(2.0f, 2.0f);
assertPointsEqual(mapPoint(matrix1, 1.0f, 1.0f), new PointF(3.0f, 3.0f));
matrix1.preSkew(2.0f, 2.0f);
assertPointsEqual(mapPoint(matrix1, 1.0f, 1.0f), new PointF(9.0f, 9.0f));
final Matrix matrix2 = new Matrix();
matrix2.preSkew(2.0f, 2.0f, 1.0f, 1.0f);
assertPointsEqual(mapPoint(matrix2, 1.0f, 1.0f), new PointF(1.0f, 1.0f));
matrix2.setTranslate(1.0f, 2.0f);
matrix2.preSkew(2.0f, 2.0f, 1.0f, 1.0f);
assertPointsEqual(mapPoint(matrix2, 1.0f, 1.0f), new PointF(2.0f, 3.0f));
}
@Test
public void testSetConcat() {
final Matrix scaleMatrix = new Matrix();
scaleMatrix.setScale(2.0f, 3.0f);
final Matrix translateMatrix = new Matrix();
translateMatrix.postTranslate(5.0f, 7.0f);
final Matrix matrix = new Matrix();
matrix.setConcat(translateMatrix, scaleMatrix);
assertPointsEqual(mapPoint(matrix, 2.0f, 2.0f), new PointF(9.0f, 13.0f));
final Matrix rotateMatrix = new Matrix();
rotateMatrix.postRotate(90.0f);
matrix.setConcat(rotateMatrix, matrix);
assertPointsEqual(mapPoint(matrix, 2.0f, 2.0f), new PointF(-13.0f, 9.0f));
}
@Test
public void testPostConcat() {
final Matrix matrix = new Matrix();
matrix.postScale(2.0f, 3.0f);
final Matrix translateMatrix = new Matrix();
translateMatrix.postTranslate(5.0f, 7.0f);
matrix.postConcat(translateMatrix);
assertPointsEqual(mapPoint(matrix, 2.0f, 2.0f), new PointF(9.0f, 13.0f));
final Matrix rotateMatrix = new Matrix();
rotateMatrix.postRotate(90.0f);
matrix.postConcat(rotateMatrix);
assertPointsEqual(mapPoint(matrix, 2.0f, 2.0f), new PointF(-13.0f, 9.0f));
}
@Test
public void testPreConcat() {
final Matrix matrix = new Matrix();
matrix.preScale(2.0f, 3.0f);
final Matrix translateMatrix = new Matrix();
translateMatrix.setTranslate(5.0f, 7.0f);
matrix.preConcat(translateMatrix);
assertPointsEqual(mapPoint(matrix, 2.0f, 2.0f), new PointF(14.0f, 27.0f));
final Matrix rotateMatrix = new Matrix();
rotateMatrix.setRotate(90.0f);
matrix.preConcat(rotateMatrix);
assertPointsEqual(mapPoint(matrix, 2.0f, 2.0f), new PointF(6.0f, 27.0f));
}
@Test
public void testInvert() {
final Matrix matrix = new Matrix();
final Matrix inverse = new Matrix();
matrix.setScale(0.0f, 1.0f);
assertThat(matrix.invert(inverse))
.isFalse();
matrix.setScale(1.0f, 0.0f);
assertThat(matrix.invert(inverse))
.isFalse();
matrix.setScale(1.0f, 1.0f);
checkInverse(matrix);
matrix.setScale(-3.0f, 5.0f);
checkInverse(matrix);
matrix.setTranslate(5.0f, 2.0f);
checkInverse(matrix);
matrix.setScale(-3.0f, 5.0f);
matrix.postTranslate(5.0f, 2.0f);
checkInverse(matrix);
matrix.setScale(-3.0f, 5.0f);
matrix.postRotate(-30f, 1.0f, 2.0f);
matrix.postTranslate(5.0f, 2.0f);
checkInverse(matrix);
}
@Test
public void testMapRect() {
final Matrix matrix = new Matrix();
matrix.postScale(2.0f, 3.0f);
final RectF input = new RectF(1.0f, 1.0f, 2.0f, 2.0f);
final RectF output1 = new RectF();
matrix.mapRect(output1, input);
assertThat(output1)
.isEqualTo(new RectF(2.0f, 3.0f, 4.0f, 6.0f));
matrix.postScale(-1.0f, -1.0f);
final RectF output2 = new RectF();
matrix.mapRect(output2, input);
assertThat(output2)
.isEqualTo(new RectF(-4.0f, -6.0f, -2.0f, -3.0f));
}
private static PointF mapPoint(Matrix matrix, float x, float y) {
return shadowOf(matrix).mapPoint(x, y);
}
private static void assertPointsEqual(PointF actual, PointF expected) {
assertThat(actual.x)
.isCloseTo(expected.x, Offset.offset(EPSILON));
assertThat(actual.y)
.isCloseTo(expected.y, Offset.offset(EPSILON));
}
private static void checkInverse(Matrix matrix) {
final Matrix inverse = new Matrix();
assertThat(matrix.invert(inverse))
.isTrue();
matrix.postConcat(inverse);
assertThat(matrix.isIdentity())
.isTrue();
}
}