blob: 65346ca1f800f36ad35afbc4510fad56b62a3bf6 [file] [log] [blame]
- name: 2d.transformation.order
desc: Transformations are applied in the right order
testing:
- 2d.transformation.order
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.scale(2, 1);
ctx.rotate(Math.PI / 2);
ctx.fillStyle = '#0f0';
ctx.fillRect(0, -50, 50, 50);
@assert pixel 75,25 == 0,255,0,255;
expected: green
- name: 2d.transformation.scale.basic
desc: scale() works
testing:
- 2d.transformation.scale
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.scale(2, 4);
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 50, 12.5);
@assert pixel 90,40 == 0,255,0,255;
expected: green
- name: 2d.transformation.scale.zero
desc: scale() with a scale factor of zero works
testing:
- 2d.transformation.scale
code: |
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
ctx.save();
ctx.translate(50, 0);
ctx.scale(0, 1);
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.restore();
ctx.save();
ctx.translate(0, 25);
ctx.scale(1, 0);
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.restore();
canvas.toDataURL();
@assert pixel 50,25 == 0,255,0,255;
expected: green
- name: 2d.transformation.scale.negative
desc: scale() with negative scale factors works
testing:
- 2d.transformation.scale
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.save();
ctx.scale(-1, 1);
ctx.fillStyle = '#0f0';
ctx.fillRect(-50, 0, 50, 50);
ctx.restore();
ctx.save();
ctx.scale(1, -1);
ctx.fillStyle = '#0f0';
ctx.fillRect(50, -50, 50, 50);
ctx.restore();
@assert pixel 25,25 == 0,255,0,255;
@assert pixel 75,25 == 0,255,0,255;
expected: green
- name: 2d.transformation.scale.large
desc: scale() with large scale factors works
notes: Not really that large at all, but it hits the limits in Firefox.
testing:
- 2d.transformation.scale
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.scale(1e5, 1e5);
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 1, 1);
@assert pixel 50,25 == 0,255,0,255;
expected: green
- name: 2d.transformation.scale.nonfinite
desc: scale() with Infinity/NaN is ignored
testing:
- 2d.nonfinite
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.translate(100, 10);
@nonfinite ctx.scale(<0.1 Infinity -Infinity NaN>, <0.1 Infinity -Infinity NaN>);
ctx.fillStyle = '#0f0';
ctx.fillRect(-100, -10, 100, 50);
@assert pixel 50,25 == 0,255,0,255;
expected: green
- name: 2d.transformation.scale.multiple
desc: Multiple scale()s combine
testing:
- 2d.transformation.scale.multiple
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.scale(Math.sqrt(2), Math.sqrt(2));
ctx.scale(Math.sqrt(2), Math.sqrt(2));
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 50, 25);
@assert pixel 90,40 == 0,255,0,255;
expected: green
- name: 2d.transformation.rotate.zero
desc: rotate() by 0 does nothing
testing:
- 2d.transformation.rotate
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.rotate(0);
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
@assert pixel 50,25 == 0,255,0,255;
expected: green
- name: 2d.transformation.rotate.radians
desc: rotate() uses radians
testing:
- 2d.transformation.rotate.radians
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.rotate(Math.PI); // should fail obviously if this is 3.1 degrees
ctx.fillStyle = '#0f0';
ctx.fillRect(-100, -50, 100, 50);
@assert pixel 50,25 == 0,255,0,255;
expected: green
- name: 2d.transformation.rotate.direction
desc: rotate() is clockwise
testing:
- 2d.transformation.rotate.direction
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.rotate(Math.PI / 2);
ctx.fillStyle = '#0f0';
ctx.fillRect(0, -100, 50, 100);
@assert pixel 50,25 == 0,255,0,255;
expected: green
- name: 2d.transformation.rotate.wrap
desc: rotate() wraps large positive values correctly
testing:
- 2d.transformation.rotate
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.rotate(Math.PI * (1 + 4096)); // == pi (mod 2*pi)
// We need about pi +/- 0.001 in order to get correct-looking results
// 32-bit floats can store pi*4097 with precision 2^-10, so that should
// be safe enough on reasonable implementations
ctx.fillStyle = '#0f0';
ctx.fillRect(-100, -50, 100, 50);
@assert pixel 50,25 == 0,255,0,255;
@assert pixel 98,2 == 0,255,0,255;
@assert pixel 98,47 == 0,255,0,255;
expected: green
- name: 2d.transformation.rotate.wrapnegative
desc: rotate() wraps large negative values correctly
testing:
- 2d.transformation.rotate
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.rotate(-Math.PI * (1 + 4096));
ctx.fillStyle = '#0f0';
ctx.fillRect(-100, -50, 100, 50);
@assert pixel 50,25 == 0,255,0,255;
@assert pixel 98,2 == 0,255,0,255;
@assert pixel 98,47 == 0,255,0,255;
expected: green
- name: 2d.transformation.rotate.nonfinite
desc: rotate() with Infinity/NaN is ignored
testing:
- 2d.nonfinite
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.translate(100, 10);
@nonfinite ctx.rotate(<0.1 Infinity -Infinity NaN>);
ctx.fillStyle = '#0f0';
ctx.fillRect(-100, -10, 100, 50);
@assert pixel 50,25 == 0,255,0,255;
expected: green
- name: 2d.transformation.translate.basic
desc: translate() works
testing:
- 2d.transformation.translate
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.translate(100, 50);
ctx.fillStyle = '#0f0';
ctx.fillRect(-100, -50, 100, 50);
@assert pixel 90,40 == 0,255,0,255;
expected: green
- name: 2d.transformation.translate.nonfinite
desc: translate() with Infinity/NaN is ignored
testing:
- 2d.nonfinite
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.translate(100, 10);
@nonfinite ctx.translate(<0.1 Infinity -Infinity NaN>, <0.1 Infinity -Infinity NaN>);
ctx.fillStyle = '#0f0';
ctx.fillRect(-100, -10, 100, 50);
@assert pixel 50,25 == 0,255,0,255;
expected: green
- name: 2d.transformation.transform.identity
desc: transform() with the identity matrix does nothing
testing:
- 2d.transformation.transform
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.transform(1,0, 0,1, 0,0);
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
@assert pixel 50,25 == 0,255,0,255;
expected: green
- name: 2d.transformation.transform.skewed
desc: transform() with skewy matrix transforms correctly
testing:
- 2d.transformation.transform
code: |
// Create green with a red square ring inside it
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
ctx.fillStyle = '#f00';
ctx.fillRect(20, 10, 60, 30);
ctx.fillStyle = '#0f0';
ctx.fillRect(40, 20, 20, 10);
// Draw a skewed shape to fill that gap, to make sure it is aligned correctly
ctx.transform(1,4, 2,3, 5,6);
// Post-transform coordinates:
// [[20,10],[80,10],[80,40],[20,40],[20,10],[40,20],[40,30],[60,30],[60,20],[40,20],[20,10]];
// Hence pre-transform coordinates:
var pts=[[-7.4,11.2],[-43.4,59.2],[-31.4,53.2],[4.6,5.2],[-7.4,11.2],
[-15.4,25.2],[-11.4,23.2],[-23.4,39.2],[-27.4,41.2],[-15.4,25.2],
[-7.4,11.2]];
ctx.beginPath();
ctx.moveTo(pts[0][0], pts[0][1]);
for (var i = 0; i < pts.length; ++i)
ctx.lineTo(pts[i][0], pts[i][1]);
ctx.fill();
@assert pixel 21,11 == 0,255,0,255;
@assert pixel 79,11 == 0,255,0,255;
@assert pixel 21,39 == 0,255,0,255;
@assert pixel 79,39 == 0,255,0,255;
@assert pixel 39,19 == 0,255,0,255;
@assert pixel 61,19 == 0,255,0,255;
@assert pixel 39,31 == 0,255,0,255;
@assert pixel 61,31 == 0,255,0,255;
expected: green
- name: 2d.transformation.transform.multiply
desc: transform() multiplies the CTM
testing:
- 2d.transformation.transform.multiply
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.transform(1,2, 3,4, 5,6);
ctx.transform(-2,1, 3/2,-1/2, 1,-2);
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
@assert pixel 50,25 == 0,255,0,255;
expected: green
- name: 2d.transformation.transform.nonfinite
desc: transform() with Infinity/NaN is ignored
testing:
- 2d.nonfinite
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.translate(100, 10);
@nonfinite ctx.transform(<0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>);
ctx.fillStyle = '#0f0';
ctx.fillRect(-100, -10, 100, 50);
@assert pixel 50,25 == 0,255,0,255;
expected: green
- name: 2d.transformation.setTransform.skewed
testing:
- 2d.transformation.setTransform
code: |
// Create green with a red square ring inside it
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
ctx.fillStyle = '#f00';
ctx.fillRect(20, 10, 60, 30);
ctx.fillStyle = '#0f0';
ctx.fillRect(40, 20, 20, 10);
// Draw a skewed shape to fill that gap, to make sure it is aligned correctly
ctx.setTransform(1,4, 2,3, 5,6);
// Post-transform coordinates:
// [[20,10],[80,10],[80,40],[20,40],[20,10],[40,20],[40,30],[60,30],[60,20],[40,20],[20,10]];
// Hence pre-transform coordinates:
var pts=[[-7.4,11.2],[-43.4,59.2],[-31.4,53.2],[4.6,5.2],[-7.4,11.2],
[-15.4,25.2],[-11.4,23.2],[-23.4,39.2],[-27.4,41.2],[-15.4,25.2],
[-7.4,11.2]];
ctx.beginPath();
ctx.moveTo(pts[0][0], pts[0][1]);
for (var i = 0; i < pts.length; ++i)
ctx.lineTo(pts[i][0], pts[i][1]);
ctx.fill();
@assert pixel 21,11 == 0,255,0,255;
@assert pixel 79,11 == 0,255,0,255;
@assert pixel 21,39 == 0,255,0,255;
@assert pixel 79,39 == 0,255,0,255;
@assert pixel 39,19 == 0,255,0,255;
@assert pixel 61,19 == 0,255,0,255;
@assert pixel 39,31 == 0,255,0,255;
@assert pixel 61,31 == 0,255,0,255;
expected: green
- name: 2d.transformation.setTransform.multiple
testing:
- 2d.transformation.setTransform.identity
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.setTransform(1/2,0, 0,1/2, 0,0);
ctx.setTransform();
ctx.setTransform(2,0, 0,2, 0,0);
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 50, 25);
@assert pixel 75,35 == 0,255,0,255;
expected: green
- name: 2d.transformation.setTransform.nonfinite
desc: setTransform() with Infinity/NaN is ignored
testing:
- 2d.nonfinite
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.translate(100, 10);
@nonfinite ctx.setTransform(<0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>);
ctx.fillStyle = '#0f0';
ctx.fillRect(-100, -10, 100, 50);
@assert pixel 50,25 == 0,255,0,255;
expected: green
- name: 2d.transformation.setTransform.3d
desc: setTransform() with 4x4 matrix keeps all parameters
testing:
- 2d.transformation.setTransform.3d
code: |
const transform = new DOMMatrix([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]);
ctx.setTransform(transform);
const canvasTransform = ctx.getTransform();
@assert transform.toLocaleString() == canvasTransform.toLocaleString();
- name: 2d.transformation.transform.3d
desc: transform() with 4x4 matrix concatenates properly
testing:
- 2d.transformation.transform.3d
code: |
const transform = new DOMMatrix([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]);
ctx.transform(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
let canvasTransform = ctx.getTransform();
@assert transform.toLocaleString() == canvasTransform.toLocaleString();
ctx.transform(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
canvasTransform = ctx.getTransform();
transform.multiplySelf(transform);
@assert transform.toLocaleString() == canvasTransform.toLocaleString();
- name: 2d.transformation.translate.3d
desc: translate() function with three arguments modifies the underlying matrix appropriately
testing:
- 2d.transformation.translate.3d
code: |
const dx = 2;
const dy = 3;
const dz = 4;
ctx.translate(dx, dy, dz);
let canvasTransform = ctx.getTransform();
@assert canvasTransform.m41 = dx;
@assert canvasTransform.m42 = dy;
@assert canvasTransform.m43 = dz;
ctx.translate(dx, dy, dz);
canvasTransform = ctx.getTransform();
@assert canvasTransform.m41 = 2 * dx;
@assert canvasTransform.m42 = 2 * dy;
@assert canvasTransform.m43 = 2 * dz;
- name: 2d.transformation.scale.3d
desc: scale() function with three arguments modifies the underlying matrix appropriately
testing:
- 2d.transformation.scale.3d
code: |
const sx = 2;
const sy = 3;
const sz = 4;
ctx.scale(sx, sy, sz);
let canvasTransform = ctx.getTransform();
@assert canvasTransform.m11 = sx;
@assert canvasTransform.m22 = sy;
@assert canvasTransform.m33 = sz;
ctx.scale(sx, sy, sz);
canvasTransform = ctx.getTransform();
@assert canvasTransform.m11 = 2 * sx;
@assert canvasTransform.m22 = 2 * sy;
@assert canvasTransform.m33 = 2 * sz;
- name: 2d.transformation.rotate3d.X
desc: rotate3d() around the x axis results in the correct transformation matrix
testing:
- 2d.transformation.rotate3d.X
code: |
// angles are in radians, test something that is not a multiple of pi
const angle = 2;
const domMatrix = new DOMMatrix();
ctx.rotate3d(angle, 0, 0);
domMatrix.rotateAxisAngleSelf(1, 0, 0, rad2deg(angle));
_assertMatricesApproxEqual(domMatrix, ctx.getTransform())
ctx.rotate3d(angle, 0, 0);
domMatrix.rotateAxisAngleSelf(1, 0, 0, rad2deg(angle));
_assertMatricesApproxEqual(domMatrix, ctx.getTransform())
- name: 2d.transformation.rotate3d.Y
desc: rotate3d() around the y axis results in the correct transformation matrix
testing:
- 2d.transformation.rotate3d.Y
code: |
// angles are in radians, test something that is not a multiple of pi
const angle = 2;
const domMatrix = new DOMMatrix();
ctx.rotate3d(0, angle, 0);
domMatrix.rotateAxisAngleSelf(0, 1, 0, rad2deg(angle));
_assertMatricesApproxEqual(domMatrix, ctx.getTransform())
ctx.rotate3d(0, angle, 0);
domMatrix.rotateAxisAngleSelf(0, 1, 0, rad2deg(angle));
_assertMatricesApproxEqual(domMatrix, ctx.getTransform())
- name: 2d.transformation.rotate3d.Z
desc: rotate3d() around the z axis results in the correct transformation matrix
testing:
- 2d.transformation.rotate3d.Z
code: |
// angles are in radians, test something that is not a multiple of pi
const angle = 2;
const domMatrix = new DOMMatrix();
ctx.rotate3d(0, 0, angle);
domMatrix.rotateAxisAngleSelf(0, 0, 1, rad2deg(angle));
_assertMatricesApproxEqual(domMatrix, ctx.getTransform())
ctx.rotate3d(0, 0, angle);
domMatrix.rotateAxisAngleSelf(0, 0, 1, rad2deg(angle));
_assertMatricesApproxEqual(domMatrix, ctx.getTransform())
- name: 2d.transformation.rotate3d
desc: rotate3d() results in the correct transformation matrix
testing:
- 2d.transformation.rotate3d
code: |
// angles are in radians, test something that is not a multiple of pi
const angleX = 2;
const angleY = 3;
const angleZ = 4;
const domMatrix = new DOMMatrix();
ctx.rotate3d(angleX, angleY, angleZ);
domMatrix.rotateSelf(rad2deg(angleX), rad2deg(angleY), rad2deg(angleZ));
_assertMatricesApproxEqual(domMatrix, ctx.getTransform());
ctx.rotate3d(angleX, angleY, angleZ);
domMatrix.rotateSelf(rad2deg(angleX), rad2deg(angleY), rad2deg(angleZ));
_assertMatricesApproxEqual(domMatrix, ctx.getTransform());
- name: 2d.transformation.rotateAxis
desc: rotateAxis() results in the correct transformation matrix
testing:
- 2d.transformation.rotateAxis
code: |
// angles are in radians, test something that is not a multiple of pi
const angle = 2;
const axis = {x: 1, y: 2, z: 3}
const domMatrix = new DOMMatrix();
ctx.rotateAxis(axis.x, axis.y, axis.z, angle);
domMatrix.rotateAxisAngleSelf(axis.x, axis.y, axis.z, rad2deg(angle));
_assertMatricesApproxEqual(domMatrix, ctx.getTransform());
ctx.rotateAxis(axis.x, axis.y, axis.z, angle);
domMatrix.rotateAxisAngleSelf(axis.x, axis.y, axis.z, rad2deg(angle));
_assertMatricesApproxEqual(domMatrix, ctx.getTransform());
- name: 2d.transformation.perspective
desc: perspective() results in the correct transformation matrix
testing:
- 2d.transformation.perspective
code: |
const length = 100;
ctx.perspective(length);
const domMatrix = new DOMMatrix();
domMatrix.m34 = -1/length;
_assertMatricesApproxEqual(domMatrix, ctx.getTransform());
ctx.rotateAxis(1, 2, 3, 4);
domMatrix.rotateAxisAngleSelf(1, 2, 3, rad2deg(4));
_assertMatricesApproxEqual(domMatrix, ctx.getTransform());