Kaido Kert | b108943 | 2024-03-18 19:46:49 -0700 | [diff] [blame] | 1 | describe('Path Behavior', () => { |
| 2 | let container; |
Xiaoming Shi | 73dfa20 | 2020-03-12 11:31:35 -0700 | [diff] [blame] | 3 | |
Kaido Kert | b108943 | 2024-03-18 19:46:49 -0700 | [diff] [blame] | 4 | beforeEach(async () => { |
| 5 | await LoadCanvasKit; |
| 6 | container = document.createElement('div'); |
Xiaoming Shi | 73dfa20 | 2020-03-12 11:31:35 -0700 | [diff] [blame] | 7 | container.innerHTML = ` |
| 8 | <canvas width=600 height=600 id=test></canvas> |
| 9 | <canvas width=600 height=600 id=report></canvas>`; |
Kaido Kert | b108943 | 2024-03-18 19:46:49 -0700 | [diff] [blame] | 10 | document.body.appendChild(container); |
Xiaoming Shi | 73dfa20 | 2020-03-12 11:31:35 -0700 | [diff] [blame] | 11 | }); |
| 12 | |
Kaido Kert | b108943 | 2024-03-18 19:46:49 -0700 | [diff] [blame] | 13 | afterEach(() => { |
| 14 | document.body.removeChild(container); |
Xiaoming Shi | 73dfa20 | 2020-03-12 11:31:35 -0700 | [diff] [blame] | 15 | }); |
| 16 | |
Kaido Kert | b108943 | 2024-03-18 19:46:49 -0700 | [diff] [blame] | 17 | gm('path_api_example', (canvas) => { |
| 18 | const paint = new CanvasKit.Paint(); |
| 19 | paint.setStrokeWidth(1.0); |
| 20 | paint.setAntiAlias(true); |
| 21 | paint.setColor(CanvasKit.Color(0, 0, 0, 1.0)); |
| 22 | paint.setStyle(CanvasKit.PaintStyle.Stroke); |
Xiaoming Shi | 73dfa20 | 2020-03-12 11:31:35 -0700 | [diff] [blame] | 23 | |
Kaido Kert | b108943 | 2024-03-18 19:46:49 -0700 | [diff] [blame] | 24 | const path = new CanvasKit.Path(); |
| 25 | path.moveTo(20, 5); |
| 26 | path.lineTo(30, 20); |
| 27 | path.lineTo(40, 10); |
| 28 | path.lineTo(50, 20); |
| 29 | path.lineTo(60, 0); |
| 30 | path.lineTo(20, 5); |
Xiaoming Shi | 73dfa20 | 2020-03-12 11:31:35 -0700 | [diff] [blame] | 31 | |
Kaido Kert | b108943 | 2024-03-18 19:46:49 -0700 | [diff] [blame] | 32 | path.moveTo(20, 80); |
| 33 | path.cubicTo(90, 10, 160, 150, 190, 10); |
Xiaoming Shi | 73dfa20 | 2020-03-12 11:31:35 -0700 | [diff] [blame] | 34 | |
Kaido Kert | b108943 | 2024-03-18 19:46:49 -0700 | [diff] [blame] | 35 | path.moveTo(36, 148); |
| 36 | path.quadTo(66, 188, 120, 136); |
| 37 | path.lineTo(36, 148); |
Xiaoming Shi | 73dfa20 | 2020-03-12 11:31:35 -0700 | [diff] [blame] | 38 | |
Kaido Kert | b108943 | 2024-03-18 19:46:49 -0700 | [diff] [blame] | 39 | path.moveTo(150, 180); |
| 40 | path.arcToTangent(150, 100, 50, 200, 20); |
| 41 | path.lineTo(160, 160); |
Xiaoming Shi | 73dfa20 | 2020-03-12 11:31:35 -0700 | [diff] [blame] | 42 | |
Kaido Kert | b108943 | 2024-03-18 19:46:49 -0700 | [diff] [blame] | 43 | path.moveTo(20, 120); |
| 44 | path.lineTo(20, 120); |
Xiaoming Shi | 73dfa20 | 2020-03-12 11:31:35 -0700 | [diff] [blame] | 45 | |
Kaido Kert | b108943 | 2024-03-18 19:46:49 -0700 | [diff] [blame] | 46 | path.transform([2, 0, 0, |
| 47 | 0, 2, 0, |
| 48 | 0, 0, 1 ]); |
Xiaoming Shi | 73dfa20 | 2020-03-12 11:31:35 -0700 | [diff] [blame] | 49 | |
Kaido Kert | b108943 | 2024-03-18 19:46:49 -0700 | [diff] [blame] | 50 | canvas.drawPath(path, paint); |
Xiaoming Shi | 73dfa20 | 2020-03-12 11:31:35 -0700 | [diff] [blame] | 51 | |
Kaido Kert | b108943 | 2024-03-18 19:46:49 -0700 | [diff] [blame] | 52 | const rrect = CanvasKit.RRectXY([100, 10, 140, 62], 10, 4); |
Xiaoming Shi | 73dfa20 | 2020-03-12 11:31:35 -0700 | [diff] [blame] | 53 | |
Kaido Kert | b108943 | 2024-03-18 19:46:49 -0700 | [diff] [blame] | 54 | const rrectPath = new CanvasKit.Path().addRRect(rrect, true); |
Xiaoming Shi | 73dfa20 | 2020-03-12 11:31:35 -0700 | [diff] [blame] | 55 | |
Kaido Kert | b108943 | 2024-03-18 19:46:49 -0700 | [diff] [blame] | 56 | canvas.drawPath(rrectPath, paint); |
Xiaoming Shi | 73dfa20 | 2020-03-12 11:31:35 -0700 | [diff] [blame] | 57 | |
Kaido Kert | b108943 | 2024-03-18 19:46:49 -0700 | [diff] [blame] | 58 | rrectPath.delete(); |
| 59 | path.delete(); |
| 60 | paint.delete(); |
Xiaoming Shi | 73dfa20 | 2020-03-12 11:31:35 -0700 | [diff] [blame] | 61 | // See PathKit for more tests, since they share implementation |
| 62 | }); |
| 63 | |
Kaido Kert | b108943 | 2024-03-18 19:46:49 -0700 | [diff] [blame] | 64 | it('can create a path from an SVG string', () => { |
| 65 | //.This is a parallelogram from |
| 66 | // https://upload.wikimedia.org/wikipedia/commons/e/e7/Simple_parallelogram.svg |
| 67 | const path = CanvasKit.Path.MakeFromSVGString( |
| 68 | 'M 205,5 L 795,5 L 595,295 L 5,295 L 205,5 z'); |
Xiaoming Shi | 73dfa20 | 2020-03-12 11:31:35 -0700 | [diff] [blame] | 69 | |
Kaido Kert | b108943 | 2024-03-18 19:46:49 -0700 | [diff] [blame] | 70 | const cmds = path.toCmds(); |
| 71 | expect(cmds).toBeTruthy(); |
| 72 | // 1 move, 4 lines, 1 close |
| 73 | // each element in cmds is an array, with index 0 being the verb, and the rest being args |
| 74 | expect(cmds).toEqual(Float32Array.of( |
| 75 | CanvasKit.MOVE_VERB, 205, 5, |
| 76 | CanvasKit.LINE_VERB, 795, 5, |
| 77 | CanvasKit.LINE_VERB, 595, 295, |
| 78 | CanvasKit.LINE_VERB, 5, 295, |
| 79 | CanvasKit.LINE_VERB, 205, 5, |
| 80 | CanvasKit.CLOSE_VERB)); |
| 81 | path.delete(); |
Xiaoming Shi | 73dfa20 | 2020-03-12 11:31:35 -0700 | [diff] [blame] | 82 | }); |
| 83 | |
Kaido Kert | b108943 | 2024-03-18 19:46:49 -0700 | [diff] [blame] | 84 | it('can create a path by combining two other paths', () => { |
| 85 | // Get the intersection of two overlapping squares and verify that it is the smaller square. |
| 86 | const pathOne = new CanvasKit.Path(); |
| 87 | pathOne.addRect([10, 10, 20, 20]); |
Xiaoming Shi | 73dfa20 | 2020-03-12 11:31:35 -0700 | [diff] [blame] | 88 | |
Kaido Kert | b108943 | 2024-03-18 19:46:49 -0700 | [diff] [blame] | 89 | const pathTwo = new CanvasKit.Path(); |
| 90 | pathTwo.addRect([15, 15, 30, 30]); |
| 91 | |
| 92 | const path = CanvasKit.Path.MakeFromOp(pathOne, pathTwo, CanvasKit.PathOp.Intersect); |
| 93 | const cmds = path.toCmds(); |
| 94 | expect(cmds).toBeTruthy(); |
| 95 | expect(cmds).toEqual(Float32Array.of( |
| 96 | CanvasKit.MOVE_VERB, 15, 15, |
| 97 | CanvasKit.LINE_VERB, 20, 15, |
| 98 | CanvasKit.LINE_VERB, 20, 20, |
| 99 | CanvasKit.LINE_VERB, 15, 20, |
| 100 | CanvasKit.CLOSE_VERB)); |
| 101 | path.delete(); |
| 102 | pathOne.delete(); |
| 103 | pathTwo.delete(); |
Xiaoming Shi | 73dfa20 | 2020-03-12 11:31:35 -0700 | [diff] [blame] | 104 | }); |
| 105 | |
Kaido Kert | b108943 | 2024-03-18 19:46:49 -0700 | [diff] [blame] | 106 | it('can create an SVG string from a path', () => { |
| 107 | const cmds = [CanvasKit.MOVE_VERB, 205, 5, |
| 108 | CanvasKit.LINE_VERB, 795, 5, |
| 109 | CanvasKit.LINE_VERB, 595, 295, |
| 110 | CanvasKit.LINE_VERB, 5, 295, |
| 111 | CanvasKit.LINE_VERB, 205, 5, |
| 112 | CanvasKit.CLOSE_VERB]; |
| 113 | const path = CanvasKit.Path.MakeFromCmds(cmds); |
Xiaoming Shi | 73dfa20 | 2020-03-12 11:31:35 -0700 | [diff] [blame] | 114 | |
Kaido Kert | b108943 | 2024-03-18 19:46:49 -0700 | [diff] [blame] | 115 | const svgStr = path.toSVGString(); |
| 116 | // We output it in terse form, which is different than Wikipedia's version |
| 117 | expect(svgStr).toEqual('M205 5L795 5L595 295L5 295L205 5Z'); |
| 118 | path.delete(); |
| 119 | }); |
Xiaoming Shi | 73dfa20 | 2020-03-12 11:31:35 -0700 | [diff] [blame] | 120 | |
Kaido Kert | b108943 | 2024-03-18 19:46:49 -0700 | [diff] [blame] | 121 | it('can create a path with malloced verbs, points, weights', () => { |
| 122 | const mVerbs = CanvasKit.Malloc(Uint8Array, 6); |
| 123 | const mPoints = CanvasKit.Malloc(Float32Array, 18); |
| 124 | const mWeights = CanvasKit.Malloc(Float32Array, 1); |
| 125 | mVerbs.toTypedArray().set([CanvasKit.MOVE_VERB, CanvasKit.LINE_VERB, |
| 126 | CanvasKit.QUAD_VERB, CanvasKit.CONIC_VERB, CanvasKit.CUBIC_VERB, CanvasKit.CLOSE_VERB |
| 127 | ]); |
Xiaoming Shi | 73dfa20 | 2020-03-12 11:31:35 -0700 | [diff] [blame] | 128 | |
Kaido Kert | b108943 | 2024-03-18 19:46:49 -0700 | [diff] [blame] | 129 | mPoints.toTypedArray().set([ |
| 130 | 1,2, // moveTo |
| 131 | 3,4, // lineTo |
| 132 | 5,6,7,8, // quadTo |
| 133 | 9,10,11,12, // conicTo |
| 134 | 13,14,15,16,17,18, // cubicTo |
| 135 | ]); |
Xiaoming Shi | 73dfa20 | 2020-03-12 11:31:35 -0700 | [diff] [blame] | 136 | |
Kaido Kert | b108943 | 2024-03-18 19:46:49 -0700 | [diff] [blame] | 137 | mWeights.toTypedArray().set([117]); |
| 138 | |
| 139 | let path = CanvasKit.Path.MakeFromVerbsPointsWeights(mVerbs, mPoints, mWeights); |
| 140 | |
| 141 | let cmds = path.toCmds(); |
| 142 | expect(cmds).toEqual(Float32Array.of( |
| 143 | CanvasKit.MOVE_VERB, 1, 2, |
| 144 | CanvasKit.LINE_VERB, 3, 4, |
| 145 | CanvasKit.QUAD_VERB, 5, 6, 7, 8, |
| 146 | CanvasKit.CONIC_VERB, 9, 10, 11, 12, 117, |
| 147 | CanvasKit.CUBIC_VERB, 13, 14, 15, 16, 17, 18, |
| 148 | CanvasKit.CLOSE_VERB, |
| 149 | )); |
| 150 | path.delete(); |
| 151 | |
| 152 | // If given insufficient points, it stops early (but doesn't read out of bounds). |
| 153 | path = CanvasKit.Path.MakeFromVerbsPointsWeights(mVerbs, mPoints.subarray(0, 10), mWeights); |
| 154 | |
| 155 | cmds = path.toCmds(); |
| 156 | expect(cmds).toEqual(Float32Array.of( |
| 157 | CanvasKit.MOVE_VERB, 1, 2, |
| 158 | CanvasKit.LINE_VERB, 3, 4, |
| 159 | CanvasKit.QUAD_VERB, 5, 6, 7, 8, |
| 160 | )); |
| 161 | path.delete(); |
| 162 | CanvasKit.Free(mVerbs); |
| 163 | CanvasKit.Free(mPoints); |
| 164 | CanvasKit.Free(mWeights); |
| 165 | }); |
| 166 | |
| 167 | it('can create and update a path with verbs and points (no weights)', () => { |
| 168 | const path = CanvasKit.Path.MakeFromVerbsPointsWeights( |
| 169 | [CanvasKit.MOVE_VERB, CanvasKit.LINE_VERB], |
| 170 | [1,2, 3,4]); |
| 171 | let cmds = path.toCmds(); |
| 172 | expect(cmds).toEqual(Float32Array.of( |
| 173 | CanvasKit.MOVE_VERB, 1, 2, |
| 174 | CanvasKit.LINE_VERB, 3, 4 |
| 175 | )); |
| 176 | |
| 177 | path.addVerbsPointsWeights( |
| 178 | [CanvasKit.QUAD_VERB, CanvasKit.CLOSE_VERB], |
| 179 | [5,6,7,8], |
| 180 | ); |
| 181 | |
| 182 | cmds = path.toCmds(); |
| 183 | expect(cmds).toEqual(Float32Array.of( |
| 184 | CanvasKit.MOVE_VERB, 1, 2, |
| 185 | CanvasKit.LINE_VERB, 3, 4, |
| 186 | CanvasKit.QUAD_VERB, 5, 6, 7, 8, |
| 187 | CanvasKit.CLOSE_VERB |
| 188 | )); |
| 189 | path.delete(); |
| 190 | }); |
| 191 | |
| 192 | |
| 193 | it('can add points to a path in bulk', () => { |
| 194 | const mVerbs = CanvasKit.Malloc(Uint8Array, 6); |
| 195 | const mPoints = CanvasKit.Malloc(Float32Array, 18); |
| 196 | const mWeights = CanvasKit.Malloc(Float32Array, 1); |
| 197 | mVerbs.toTypedArray().set([CanvasKit.MOVE_VERB, CanvasKit.LINE_VERB, |
| 198 | CanvasKit.QUAD_VERB, CanvasKit.CONIC_VERB, CanvasKit.CUBIC_VERB, CanvasKit.CLOSE_VERB |
| 199 | ]); |
| 200 | |
| 201 | mPoints.toTypedArray().set([ |
| 202 | 1,2, // moveTo |
| 203 | 3,4, // lineTo |
| 204 | 5,6,7,8, // quadTo |
| 205 | 9,10,11,12, // conicTo |
| 206 | 13,14,15,16,17,18, // cubicTo |
| 207 | ]); |
| 208 | |
| 209 | mWeights.toTypedArray().set([117]); |
| 210 | |
| 211 | const path = new CanvasKit.Path(); |
| 212 | path.lineTo(77, 88); |
| 213 | path.addVerbsPointsWeights(mVerbs, mPoints, mWeights); |
| 214 | |
| 215 | let cmds = path.toCmds(); |
| 216 | expect(cmds).toEqual(Float32Array.of( |
| 217 | CanvasKit.MOVE_VERB, 0, 0, |
| 218 | CanvasKit.LINE_VERB, 77, 88, |
| 219 | CanvasKit.MOVE_VERB, 1, 2, |
| 220 | CanvasKit.LINE_VERB, 3, 4, |
| 221 | CanvasKit.QUAD_VERB, 5, 6, 7, 8, |
| 222 | CanvasKit.CONIC_VERB, 9, 10, 11, 12, 117, |
| 223 | CanvasKit.CUBIC_VERB, 13, 14, 15, 16, 17, 18, |
| 224 | CanvasKit.CLOSE_VERB, |
| 225 | )); |
| 226 | |
| 227 | path.rewind(); |
| 228 | cmds = path.toCmds(); |
| 229 | expect(cmds).toEqual(new Float32Array(0)); |
| 230 | |
| 231 | path.delete(); |
| 232 | CanvasKit.Free(mVerbs); |
| 233 | CanvasKit.Free(mPoints); |
| 234 | CanvasKit.Free(mWeights); |
| 235 | }); |
| 236 | |
| 237 | it('can retrieve points from a path', () => { |
| 238 | const path = new CanvasKit.Path(); |
| 239 | path.addRect([10, 15, 20, 25]); |
| 240 | |
| 241 | let pt = path.getPoint(0); |
| 242 | expect(pt[0]).toEqual(10); |
| 243 | expect(pt[1]).toEqual(15); |
| 244 | |
| 245 | path.getPoint(2, pt); |
| 246 | expect(pt[0]).toEqual(20); |
| 247 | expect(pt[1]).toEqual(25); |
| 248 | |
| 249 | path.getPoint(1000, pt); // off the end returns (0, 0) as per the docs. |
| 250 | expect(pt[0]).toEqual(0); |
| 251 | expect(pt[1]).toEqual(0); |
| 252 | |
| 253 | path.delete(); |
| 254 | }); |
| 255 | |
| 256 | gm('offset_path', (canvas) => { |
| 257 | const path = starPath(CanvasKit); |
| 258 | |
| 259 | const paint = new CanvasKit.Paint(); |
| 260 | paint.setStyle(CanvasKit.PaintStyle.Stroke); |
| 261 | paint.setStrokeWidth(5.0); |
| 262 | paint.setAntiAlias(true); |
| 263 | paint.setColor(CanvasKit.BLACK); |
| 264 | |
| 265 | canvas.clear(CanvasKit.WHITE); |
| 266 | |
| 267 | canvas.drawPath(path, paint); |
| 268 | path.offset(80, 40); |
| 269 | canvas.drawPath(path, paint); |
| 270 | |
| 271 | path.delete(); |
| 272 | paint.delete(); |
| 273 | }); |
| 274 | |
| 275 | gm('oval_path', (canvas) => { |
| 276 | const paint = new CanvasKit.Paint(); |
| 277 | |
| 278 | paint.setStyle(CanvasKit.PaintStyle.Stroke); |
| 279 | paint.setStrokeWidth(5.0); |
| 280 | paint.setAntiAlias(true); |
| 281 | paint.setColor(CanvasKit.BLACK); |
| 282 | |
| 283 | canvas.clear(CanvasKit.WHITE); |
| 284 | |
| 285 | const path = new CanvasKit.Path(); |
| 286 | path.moveTo(5, 5); |
| 287 | path.lineTo(10, 120); |
| 288 | path.addOval(CanvasKit.LTRBRect(10, 20, 100, 200), false, 3); |
| 289 | path.lineTo(300, 300); |
| 290 | |
| 291 | canvas.drawPath(path, paint); |
| 292 | |
| 293 | path.delete(); |
| 294 | paint.delete(); |
| 295 | }); |
| 296 | |
| 297 | gm('bounds_path', (canvas) => { |
| 298 | const paint = new CanvasKit.Paint(); |
| 299 | |
| 300 | paint.setStyle(CanvasKit.PaintStyle.Stroke); |
| 301 | paint.setStrokeWidth(5.0); |
| 302 | paint.setAntiAlias(true); |
| 303 | paint.setColor(CanvasKit.BLACK); |
| 304 | |
| 305 | canvas.clear(CanvasKit.WHITE); |
| 306 | |
| 307 | const path = new CanvasKit.Path(); |
| 308 | // Arbitrary points to make an interesting curve. |
| 309 | path.moveTo(97, 225); |
| 310 | path.cubicTo(20, 400, 404, 75, 243, 271); |
| 311 | |
| 312 | canvas.drawPath(path, paint); |
| 313 | |
| 314 | const bounds = new Float32Array(4); |
| 315 | path.getBounds(bounds); |
| 316 | |
| 317 | paint.setColor(CanvasKit.BLUE); |
| 318 | paint.setStrokeWidth(3.0); |
| 319 | canvas.drawRect(bounds, paint); |
| 320 | |
| 321 | path.computeTightBounds(bounds); |
| 322 | paint.setColor(CanvasKit.RED); |
| 323 | paint.setStrokeWidth(3.0); |
| 324 | canvas.drawRect(bounds, paint); |
| 325 | |
| 326 | path.delete(); |
| 327 | paint.delete(); |
| 328 | }); |
| 329 | |
| 330 | gm('arcto_path', (canvas) => { |
| 331 | const paint = new CanvasKit.Paint(); |
| 332 | |
| 333 | paint.setStyle(CanvasKit.PaintStyle.Stroke); |
| 334 | paint.setStrokeWidth(5.0); |
| 335 | paint.setAntiAlias(true); |
| 336 | paint.setColor(CanvasKit.BLACK); |
| 337 | |
| 338 | canvas.clear(CanvasKit.WHITE); |
| 339 | |
| 340 | const path = new CanvasKit.Path(); |
| 341 | |
| 342 | // - x1, y1, x2, y2, radius |
| 343 | path.arcToTangent(40, 0, 40, 40, 40); |
| 344 | // - oval (as Rect), startAngle, sweepAngle, forceMoveTo |
| 345 | path.arcToOval(CanvasKit.LTRBRect(90, 10, 120, 200), 30, 300, true); |
| 346 | // - rx, ry, xAxisRotate, useSmallArc, isCCW, x, y |
| 347 | path.moveTo(5, 105); |
| 348 | path.arcToRotated(24, 24, 45, true, false, 82, 156); |
| 349 | |
| 350 | canvas.drawPath(path, paint); |
| 351 | |
| 352 | path.delete(); |
| 353 | paint.delete(); |
| 354 | }); |
| 355 | |
| 356 | gm('path_relative', (canvas) => { |
| 357 | const paint = new CanvasKit.Paint(); |
| 358 | paint.setStrokeWidth(1.0); |
| 359 | paint.setAntiAlias(true); |
| 360 | paint.setColor(CanvasKit.Color(0, 0, 0, 1.0)); |
| 361 | paint.setStyle(CanvasKit.PaintStyle.Stroke); |
| 362 | |
| 363 | const path = new CanvasKit.Path(); |
| 364 | path.rMoveTo(20, 5) |
| 365 | .rLineTo(10, 15) // 30, 20 |
| 366 | .rLineTo(10, -5); // 40, 10 |
| 367 | path.rLineTo(10, 10); // 50, 20 |
| 368 | path.rLineTo(10, -20); // 60, 0 |
| 369 | path.rLineTo(-40, 5); // 20, 5 |
| 370 | |
| 371 | path.moveTo(20, 80) |
| 372 | .rCubicTo(70, -70, 140, 70, 170, -70); // 90, 10, 160, 150, 190, 10 |
| 373 | |
| 374 | path.moveTo(36, 148) |
| 375 | .rQuadTo(30, 40, 84, -12) // 66, 188, 120, 136 |
| 376 | .lineTo(36, 148); |
| 377 | |
| 378 | path.moveTo(150, 180) |
| 379 | .rArcTo(24, 24, 45, true, false, -68, -24); // 82, 156 |
| 380 | path.lineTo(160, 160); |
| 381 | |
| 382 | canvas.drawPath(path, paint); |
| 383 | |
| 384 | path.delete(); |
| 385 | paint.delete(); |
| 386 | }); |
| 387 | |
| 388 | it('can measure the contours of a path', () => { |
| 389 | const path = new CanvasKit.Path(); |
| 390 | path.moveTo(10, 10) |
| 391 | .lineTo(40, 50); // should be length 50 because of the 3/4/5 triangle rule |
| 392 | |
| 393 | path.moveTo(80, 0) |
| 394 | .lineTo(80, 10) |
| 395 | .lineTo(100, 5) |
| 396 | .lineTo(80, 0); |
| 397 | |
| 398 | const meas = new CanvasKit.ContourMeasureIter(path, false, 1); |
| 399 | let cont = meas.next(); |
| 400 | expect(cont).toBeTruthy(); |
| 401 | |
| 402 | expect(cont.length()).toBeCloseTo(50.0, 3); |
| 403 | const pt = cont.getPosTan(28.7); // arbitrary point |
| 404 | expect(pt[0]).toBeCloseTo(27.22, 3); // x |
| 405 | expect(pt[1]).toBeCloseTo(32.96, 3); // y |
| 406 | expect(pt[2]).toBeCloseTo(0.6, 3); // dy |
| 407 | expect(pt[3]).toBeCloseTo(0.8, 3); // dy |
| 408 | |
| 409 | pt.set([-1, -1, -1, -1]); // fill with sentinel values. |
| 410 | cont.getPosTan(28.7, pt); // arbitrary point again, passing in an array to copy into. |
| 411 | expect(pt[0]).toBeCloseTo(27.22, 3); // x |
| 412 | expect(pt[1]).toBeCloseTo(32.96, 3); // y |
| 413 | expect(pt[2]).toBeCloseTo(0.6, 3); // dy |
| 414 | expect(pt[3]).toBeCloseTo(0.8, 3); // dy |
| 415 | |
| 416 | const subpath = cont.getSegment(20, 40, true); // make sure this doesn't crash |
| 417 | |
| 418 | cont.delete(); |
| 419 | cont = meas.next(); |
| 420 | expect(cont).toBeTruthy(); |
| 421 | expect(cont.length()).toBeCloseTo(51.231, 3); |
| 422 | |
| 423 | cont.delete(); |
| 424 | expect(meas.next()).toBeFalsy(); |
| 425 | |
| 426 | meas.delete(); |
| 427 | path.delete(); |
| 428 | }); |
| 429 | |
| 430 | gm('drawpoly_path', (canvas) => { |
| 431 | const paint = new CanvasKit.Paint(); |
| 432 | paint.setStrokeWidth(1.0); |
| 433 | paint.setAntiAlias(true); |
| 434 | paint.setColor(CanvasKit.Color(0, 0, 0, 1.0)); |
| 435 | paint.setStyle(CanvasKit.PaintStyle.Stroke); |
| 436 | |
| 437 | const points = [5, 5, 30, 20, 55, 5, 55, 50, 30, 30, 5, 50]; |
| 438 | |
| 439 | const pointsObj = CanvasKit.Malloc(Float32Array, 6 * 2); |
| 440 | const mPoints = pointsObj.toTypedArray(); |
| 441 | mPoints.set([105, 105, 130, 120, 155, 105, 155, 150, 130, 130, 105, 150]); |
| 442 | |
| 443 | const path = new CanvasKit.Path(); |
| 444 | path.addPoly(points, true) |
| 445 | .moveTo(100, 0) |
| 446 | .addPoly(mPoints, true); |
| 447 | |
| 448 | canvas.drawPath(path, paint); |
| 449 | CanvasKit.Free(pointsObj); |
| 450 | |
| 451 | path.delete(); |
| 452 | paint.delete(); |
| 453 | }); |
| 454 | |
| 455 | // Test trim, adding paths to paths, and a bunch of other path methods. |
| 456 | gm('trim_path', (canvas) => { |
| 457 | canvas.clear(CanvasKit.WHITE); |
| 458 | |
| 459 | const paint = new CanvasKit.Paint(); |
| 460 | paint.setStrokeWidth(1.0); |
| 461 | paint.setAntiAlias(true); |
| 462 | paint.setColor(CanvasKit.Color(0, 0, 0, 1.0)); |
| 463 | paint.setStyle(CanvasKit.PaintStyle.Stroke); |
| 464 | |
| 465 | const arcpath = new CanvasKit.Path(); |
| 466 | arcpath.arc(400, 400, 100, 0, -90, false) // x, y, radius, startAngle, endAngle, ccw |
| 467 | .dash(3, 1, 0) |
| 468 | .conicTo(10, 20, 30, 40, 5) |
| 469 | .rConicTo(60, 70, 80, 90, 5) |
| 470 | .trim(0.2, 1, false); |
| 471 | |
| 472 | const path = new CanvasKit.Path(); |
| 473 | path.addArc(CanvasKit.LTRBRect(10, 20, 100, 200), 30, 300) |
| 474 | .addRect(CanvasKit.LTRBRect(200, 200, 300, 300)) // test single arg, default cw |
| 475 | .addRect(CanvasKit.LTRBRect(240, 240, 260, 260), true) // test two arg, true means ccw |
| 476 | .addRect([260, 260, 290, 290], true) // test five arg, true means ccw |
| 477 | .addRRect([300, 10, 500, 290, // Rect in LTRB order |
| 478 | 60, 60, 60, 60, 60, 60, 60, 60], // all radii are the same |
| 479 | false) // ccw |
| 480 | .addRRect(CanvasKit.RRectXY([350, 60, 450, 240], 20, 80), true) // Rect, rx, ry, ccw |
| 481 | .addPath(arcpath) |
| 482 | .transform(0.54, -0.84, 390.35, |
| 483 | 0.84, 0.54, -114.53, |
| 484 | 0, 0, 1); |
| 485 | |
| 486 | canvas.drawPath(path, paint); |
| 487 | |
| 488 | path.delete(); |
| 489 | paint.delete(); |
| 490 | }); |
| 491 | |
| 492 | gm('winding_example', (canvas) => { |
| 493 | // Inspired by https://fiddle.skia.org/c/@Path_FillType_a |
| 494 | const path = new CanvasKit.Path(); |
| 495 | // Draw overlapping rects on top |
| 496 | path.addRect(CanvasKit.LTRBRect(10, 10, 30, 30), false); |
| 497 | path.addRect(CanvasKit.LTRBRect(20, 20, 40, 40), false); |
| 498 | // Draw overlapping rects on bottom, with different direction lines. |
| 499 | path.addRect(CanvasKit.LTRBRect(10, 60, 30, 80), false); |
| 500 | path.addRect(CanvasKit.LTRBRect(20, 70, 40, 90), true); |
| 501 | |
| 502 | expect(path.getFillType()).toEqual(CanvasKit.FillType.Winding); |
| 503 | |
| 504 | // Draw the two rectangles on the left side. |
| 505 | const paint = new CanvasKit.Paint(); |
| 506 | paint.setStyle(CanvasKit.PaintStyle.Stroke); |
| 507 | canvas.drawPath(path, paint); |
| 508 | |
| 509 | const clipRect = CanvasKit.LTRBRect(0, 0, 51, 100); |
| 510 | paint.setStyle(CanvasKit.PaintStyle.Fill); |
| 511 | |
| 512 | for (const fillType of [CanvasKit.FillType.Winding, CanvasKit.FillType.EvenOdd]) { |
| 513 | canvas.translate(51, 0); |
| 514 | canvas.save(); |
| 515 | canvas.clipRect(clipRect, CanvasKit.ClipOp.Intersect, false); |
| 516 | path.setFillType(fillType); |
Xiaoming Shi | 73dfa20 | 2020-03-12 11:31:35 -0700 | [diff] [blame] | 517 | canvas.drawPath(path, paint); |
Kaido Kert | b108943 | 2024-03-18 19:46:49 -0700 | [diff] [blame] | 518 | canvas.restore(); |
| 519 | } |
Xiaoming Shi | 73dfa20 | 2020-03-12 11:31:35 -0700 | [diff] [blame] | 520 | |
Kaido Kert | b108943 | 2024-03-18 19:46:49 -0700 | [diff] [blame] | 521 | path.delete(); |
| 522 | paint.delete(); |
Xiaoming Shi | 73dfa20 | 2020-03-12 11:31:35 -0700 | [diff] [blame] | 523 | }); |
| 524 | |
Kaido Kert | b108943 | 2024-03-18 19:46:49 -0700 | [diff] [blame] | 525 | gm('as_winding', (canvas) => { |
| 526 | const evenOddPath = new CanvasKit.Path(); |
| 527 | // Draw overlapping rects |
| 528 | evenOddPath.addRect(CanvasKit.LTRBRect(10, 10, 70, 70), false); |
| 529 | evenOddPath.addRect(CanvasKit.LTRBRect(30, 30, 50, 50), false); |
| 530 | evenOddPath.setFillType(CanvasKit.FillType.EvenOdd); |
Xiaoming Shi | 73dfa20 | 2020-03-12 11:31:35 -0700 | [diff] [blame] | 531 | |
Kaido Kert | b108943 | 2024-03-18 19:46:49 -0700 | [diff] [blame] | 532 | const evenOddCmds = evenOddPath.toCmds(); |
| 533 | expect(evenOddCmds).toEqual(Float32Array.of( |
| 534 | CanvasKit.MOVE_VERB, 10, 10, |
| 535 | CanvasKit.LINE_VERB, 70, 10, |
| 536 | CanvasKit.LINE_VERB, 70, 70, |
| 537 | CanvasKit.LINE_VERB, 10, 70, |
| 538 | CanvasKit.CLOSE_VERB, |
| 539 | CanvasKit.MOVE_VERB, 30, 30, // This contour is drawn |
| 540 | CanvasKit.LINE_VERB, 50, 30, // clockwise, as specified. |
| 541 | CanvasKit.LINE_VERB, 50, 50, |
| 542 | CanvasKit.LINE_VERB, 30, 50, |
| 543 | CanvasKit.CLOSE_VERB |
| 544 | )); |
Xiaoming Shi | 73dfa20 | 2020-03-12 11:31:35 -0700 | [diff] [blame] | 545 | |
Kaido Kert | b108943 | 2024-03-18 19:46:49 -0700 | [diff] [blame] | 546 | const windingPath = evenOddPath.makeAsWinding(); |
Xiaoming Shi | 73dfa20 | 2020-03-12 11:31:35 -0700 | [diff] [blame] | 547 | |
Kaido Kert | b108943 | 2024-03-18 19:46:49 -0700 | [diff] [blame] | 548 | expect(windingPath.getFillType()).toBe(CanvasKit.FillType.Winding); |
| 549 | const windingCmds = windingPath.toCmds(); |
| 550 | expect(windingCmds).toEqual(Float32Array.of( |
| 551 | CanvasKit.MOVE_VERB, 10, 10, |
| 552 | CanvasKit.LINE_VERB, 70, 10, |
| 553 | CanvasKit.LINE_VERB, 70, 70, |
| 554 | CanvasKit.LINE_VERB, 10, 70, |
| 555 | CanvasKit.CLOSE_VERB, |
| 556 | CanvasKit.MOVE_VERB, 30, 50, // This contour has been |
| 557 | CanvasKit.LINE_VERB, 50, 50, // re-drawn counter-clockwise |
| 558 | CanvasKit.LINE_VERB, 50, 30, // so that it covers the same |
| 559 | CanvasKit.LINE_VERB, 30, 30, // area, but with the winding fill type. |
| 560 | CanvasKit.CLOSE_VERB |
| 561 | )); |
Xiaoming Shi | 73dfa20 | 2020-03-12 11:31:35 -0700 | [diff] [blame] | 562 | |
Kaido Kert | b108943 | 2024-03-18 19:46:49 -0700 | [diff] [blame] | 563 | const paint = new CanvasKit.Paint(); |
| 564 | paint.setStyle(CanvasKit.PaintStyle.Fill); |
| 565 | const font = new CanvasKit.Font(null, 20); |
Xiaoming Shi | 73dfa20 | 2020-03-12 11:31:35 -0700 | [diff] [blame] | 566 | |
Kaido Kert | b108943 | 2024-03-18 19:46:49 -0700 | [diff] [blame] | 567 | canvas.drawText('Original path (even odd)', 5, 20, paint, font); |
| 568 | canvas.translate(0, 50); |
| 569 | canvas.drawPath(evenOddPath, paint); |
Xiaoming Shi | 73dfa20 | 2020-03-12 11:31:35 -0700 | [diff] [blame] | 570 | |
Kaido Kert | b108943 | 2024-03-18 19:46:49 -0700 | [diff] [blame] | 571 | canvas.translate(300, 0); |
| 572 | canvas.drawPath(windingPath, paint); |
Xiaoming Shi | 73dfa20 | 2020-03-12 11:31:35 -0700 | [diff] [blame] | 573 | |
Kaido Kert | b108943 | 2024-03-18 19:46:49 -0700 | [diff] [blame] | 574 | canvas.translate(0, -50); |
| 575 | canvas.drawText('makeAsWinding path', 5, 20, paint, font); |
Xiaoming Shi | 73dfa20 | 2020-03-12 11:31:35 -0700 | [diff] [blame] | 576 | |
Kaido Kert | b108943 | 2024-03-18 19:46:49 -0700 | [diff] [blame] | 577 | evenOddPath.delete(); |
| 578 | windingPath.delete(); |
Kaido Kert | f9bc013 | 2023-12-04 11:52:41 -0800 | [diff] [blame] | 579 | }); |
Xiaoming Shi | 73dfa20 | 2020-03-12 11:31:35 -0700 | [diff] [blame] | 580 | }); |