| - 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()); |
| |
| - name: 2d.transformation.combined.3d.transforms |
| desc: perspective and rotate3d work together |
| testing: |
| - 2d.transformation.3d.transforms |
| code: | |
| ctx.fillStyle = '#0f0'; |
| ctx.fillRect(0, 0, 100, 50); |
| |
| // Create a perspective transform in combiation with fillRect to draw a red |
| // trapezoid then draw a smaller green trapezoid inside it. |
| ctx.translate(50, 5); |
| ctx.perspective(100); |
| ctx.rotate3d(Math.PI/4, 0, 0); |
| ctx.fillStyle = "#f00"; |
| ctx.fillRect(-30, 0, 60, 40); |
| ctx.fillStyle = "#0f0"; |
| ctx.fillRect(-15, 10, 30, 20); |
| |
| // These are the expected points of those two trapezoids from putting |
| // the corners of the filled rectangles through the perspective transform. |
| const bigTrapezoid = [[81, 5], [93, 45], [7, 45], [19, 5]]; |
| const smallTrapezoid = [[32, 31], [68, 31], [65, 13], [35, 13]]; |
| |
| // Now filling a shape with green at those exact points with an identity |
| // transform should result in a purely green image. |
| ctx.resetTransform(); |
| ctx.beginPath(); |
| ctx.moveTo(bigTrapezoid[3][0], bigTrapezoid[3][1]); |
| for (const point of bigTrapezoid) ctx.lineTo(point[0], point[1]) |
| ctx.lineTo(smallTrapezoid[3][0], smallTrapezoid[3][1]); |
| for (const point of smallTrapezoid) ctx.lineTo(point[0], point[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 |