1
1
/*!
2
- * melonJS Game Engine - v15.15 .0
2
+ * melonJS Game Engine - v16.0 .0
3
3
* http://www.melonjs.org
4
4
* melonjs is licensed under the MIT License.
5
5
* http://www.opensource.org/licenses/mit-license
6
- * @copyright (C) 2011 - 2023 Olivier Biot (AltByte Pte Ltd)
6
+ * @copyright (C) 2011 - 2024 Olivier Biot (AltByte Pte Ltd)
7
7
*/
8
8
import pool from '../system/pooling.js' ;
9
9
import { TAU } from '../math/math.js' ;
10
10
import earcut from '../node_modules/earcut/src/earcut.js' ;
11
+ import { endpointToCenterParameterization } from './toarccanvas.js' ;
11
12
12
13
/**
13
14
* @classdesc
14
15
* a simplified path2d implementation, supporting only one path
15
16
*/
16
17
class Path2D {
17
- constructor ( ) {
18
+ constructor ( svgPath ) {
18
19
/**
19
20
* the points defining the current path
20
21
* @type {Point[] }
@@ -36,8 +37,75 @@ class Path2D {
36
37
37
38
/* @ignore */
38
39
this . isDirty = false ;
40
+
41
+ if ( typeof svgPath === "string" ) {
42
+ this . parseSVGPath ( svgPath ) ;
43
+ }
39
44
}
40
45
46
+ /**
47
+ * Parses an SVG path string and adds the points to the current path.
48
+ * @param {string } svgPath - The SVG path string to parse.
49
+ */
50
+ parseSVGPath ( svgPath ) {
51
+ // Split path into commands and coordinates
52
+ const pathCommands = svgPath . match ( / ( [ a - d f - z ] ) [ ^ a - d f - z ] * / gi) ;
53
+ const points = this . points ;
54
+ const startPoint = this . startPoint ;
55
+ let lastPoint = startPoint ;
56
+
57
+ this . beginPath ( ) ;
58
+
59
+ // Process each command and corresponding coordinates
60
+ for ( let i = 0 ; i < pathCommands . length ; i ++ ) {
61
+ const pathCommand = pathCommands [ i ] ;
62
+ const command = pathCommand [ 0 ] . toUpperCase ( ) ;
63
+ const coordinates = pathCommand . slice ( 1 ) . trim ( ) . split ( / [ \s , ] + / ) . map ( parseFloat ) ;
64
+
65
+ switch ( command ) {
66
+ case "A" : {
67
+ // A command takes 5 coordinates
68
+ const p = endpointToCenterParameterization ( ...coordinates ) ;
69
+ this . arc ( p . x , p . y , p . radiusX , p . radiusY , p . rotation , p . startAngle , p . endAngle , p . applyanticlockwise ) ;
70
+ }
71
+ break ;
72
+ case "H" :
73
+ // H take 1 coordinate
74
+ lastPoint = points . length === 0 ? startPoint : points [ points . length - 1 ] ;
75
+ this . lineTo ( lastPoint . x + coordinates [ 0 ] , lastPoint . y ) ;
76
+ break ;
77
+ case "V" :
78
+ // V take 1 coordinate
79
+ lastPoint = points . length === 0 ? startPoint : points [ points . length - 1 ] ;
80
+ this . lineTo ( lastPoint . x , lastPoint . y + coordinates [ 0 ] ) ;
81
+ break ;
82
+ case "M" :
83
+ // M takes 2 coordinates
84
+ this . moveTo ( ...coordinates ) ;
85
+ break ;
86
+ case "L" :
87
+ // L takes 2 coordinates
88
+ this . lineTo ( ...coordinates ) ;
89
+ break ;
90
+ case "Q" :
91
+ // Q takes 4 coordinates
92
+ this . quadraticCurveTo ( ...coordinates ) ;
93
+ break ;
94
+ case "C" :
95
+ // C takes 6 coordinates
96
+ this . bezierCurveTo ( ...coordinates ) ;
97
+ break ;
98
+ case "Z" :
99
+ this . closePath ( ) ;
100
+ break ;
101
+ default :
102
+ console . warn ( "Unsupported command:" , command ) ;
103
+ break ;
104
+ }
105
+ }
106
+ }
107
+
108
+
41
109
/**
42
110
* begin a new path
43
111
*/
@@ -197,9 +265,11 @@ class Path2D {
197
265
*/
198
266
arcTo ( x1 , y1 , x2 , y2 , radius ) {
199
267
let points = this . points ;
200
- // based on from https://github.com/karellodewijk/canvas-webgl/blob/master/canvas-webgl.js
201
- let x0 = points [ points . length - 1 ] . x , y0 = points [ points . length - 1 ] . y ;
268
+ let startPoint = this . startPoint ;
269
+ let lastPoint = points . length === 0 ? startPoint : points [ points . length - 1 ] ;
202
270
271
+ // based on from https://github.com/karellodewijk/canvas-webgl/blob/master/canvas-webgl.js
272
+ let x0 = lastPoint . x , y0 = lastPoint . y ;
203
273
//a = -incoming vector, b = outgoing vector to x1, y1
204
274
let a0 = x0 - x1 , a1 = y0 - y1 ;
205
275
let b0 = x2 - x1 , b1 = y2 - y1 ;
@@ -293,6 +363,62 @@ class Path2D {
293
363
this . isDirty = true ;
294
364
}
295
365
366
+ /**
367
+ * Adds a quadratic Bézier curve to the path.
368
+ * @param {number } cpX - The x-coordinate of the control point.
369
+ * @param {number } cpY - The y-coordinate of the control point.
370
+ * @param {number } x - The x-coordinate of the end point of the curve.
371
+ * @param {number } y - The y-coordinate of the end point of the curve.
372
+ */
373
+ quadraticCurveTo ( cpX , cpY , x , y ) {
374
+ const points = this . points ;
375
+ const startPoint = this . startPoint ;
376
+ const lastPoint = points . length === 0 ? startPoint : points [ points . length - 1 ] ;
377
+ const endPoint = pool . pull ( "Point" ) . set ( x , y ) ;
378
+ const controlPoint = pool . pull ( "Point" ) . set ( cpX , cpY ) ;
379
+ const resolution = this . arcResolution ;
380
+
381
+ const t = 1 / resolution ;
382
+ for ( let i = 1 ; i <= resolution ; i ++ ) {
383
+ this . lineTo (
384
+ lastPoint . x * Math . pow ( 1 - t * i , 2 ) + controlPoint . x * 2 * ( 1 - t * i ) * t * i + endPoint . x * Math . pow ( t * i , 2 ) ,
385
+ lastPoint . y * Math . pow ( 1 - t * i , 2 ) + controlPoint . y * 2 * ( 1 - t * i ) * t * i + endPoint . y * Math . pow ( t * i , 2 )
386
+ ) ;
387
+ }
388
+ pool . push ( endPoint , controlPoint ) ;
389
+ this . isDirty = true ;
390
+ }
391
+
392
+ /**
393
+ * Adds a cubic Bézier curve to the path.
394
+ * @param {number } cp1X - The x-coordinate of the first control point.
395
+ * @param {number } cp1Y - The y-coordinate of the first control point.
396
+ * @param {number } cp2X - The x-coordinate of the second control point.
397
+ * @param {number } cp2Y - The y-coordinate of the second control point.
398
+ * @param {number } x - The x-coordinate of the end point of the curve.
399
+ * @param {number } y - The y-coordinate of the end point of the curve.
400
+ */
401
+ bezierCurveTo ( cp1X , cp1Y , cp2X , cp2Y , x , y ) {
402
+ const points = this . points ;
403
+ const startPoint = this . startPoint ;
404
+ const lastPoint = points . length === 0 ? startPoint : points [ points . length - 1 ] ;
405
+ const endPoint = pool . pull ( "Point" ) . set ( x , y ) ;
406
+ const controlPoint1 = pool . pull ( "Point" ) . set ( cp1X , cp1Y ) ;
407
+ const controlPoint2 = pool . pull ( "Point" ) . set ( cp2X , cp2Y ) ;
408
+ const resolution = this . arcResolution ;
409
+
410
+ const t = 1 / resolution ;
411
+ for ( let i = 1 ; i <= resolution ; i ++ ) {
412
+ this . lineTo (
413
+ lastPoint . x * Math . pow ( 1 - t * i , 3 ) + controlPoint1 . x * 3 * Math . pow ( 1 - t * i , 2 ) * t * i + controlPoint2 . x * 3 * ( 1 - t * i ) * Math . pow ( t * i , 2 ) + endPoint . x * Math . pow ( t * i , 3 ) ,
414
+ lastPoint . y * Math . pow ( 1 - t * i , 3 ) + controlPoint1 . y * 3 * Math . pow ( 1 - t * i , 2 ) * t * i + controlPoint2 . y * 3 * ( 1 - t * i ) * Math . pow ( t * i , 2 ) + endPoint . y * Math . pow ( t * i , 3 )
415
+ ) ;
416
+ }
417
+
418
+ pool . push ( endPoint , controlPoint1 , controlPoint2 ) ;
419
+ this . isDirty = true ;
420
+ }
421
+
296
422
/**
297
423
* creates a path for a rectangle at position (x, y) with a size that is determined by width and height.
298
424
* @param {number } x - the x-axis coordinate of the rectangle's starting point.
0 commit comments