@tsparticles/plugin-polygon-mask 3.0.3 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,9 +1,34 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
2
25
  Object.defineProperty(exports, "__esModule", { value: true });
3
26
  exports.PolygonMaskInstance = void 0;
4
27
  const engine_1 = require("@tsparticles/engine");
5
- const utils_js_1 = require("./utils.js");
6
- const noPolygonDataLoaded = `${engine_1.errorPrefix} No polygon data loaded.`, noPolygonFound = `${engine_1.errorPrefix} No polygon found, you need to specify SVG url in config.`;
28
+ const noPolygonDataLoaded = `${engine_1.errorPrefix} No polygon data loaded.`, noPolygonFound = `${engine_1.errorPrefix} No polygon found, you need to specify SVG url in config.`, origin = {
29
+ x: 0,
30
+ y: 0,
31
+ }, half = 0.5, double = 2;
7
32
  class PolygonMaskInstance {
8
33
  constructor(container, engine) {
9
34
  this._checkInsidePolygon = (position) => {
@@ -14,19 +39,20 @@ class PolygonMaskInstance {
14
39
  if (!this.raw) {
15
40
  throw new Error(noPolygonFound);
16
41
  }
17
- const canvasSize = container.canvas.size, x = position?.x ?? (0, engine_1.getRandom)() * canvasSize.width, y = position?.y ?? (0, engine_1.getRandom)() * canvasSize.height;
42
+ const canvasSize = container.canvas.size, x = position?.x ?? (0, engine_1.getRandom)() * canvasSize.width, y = position?.y ?? (0, engine_1.getRandom)() * canvasSize.height, indexOffset = 1;
18
43
  let inside = false;
19
- for (let i = 0, j = this.raw.length - 1; i < this.raw.length; j = i++) {
44
+ for (let i = 0, j = this.raw.length - indexOffset; i < this.raw.length; j = i++) {
20
45
  const pi = this.raw[i], pj = this.raw[j], intersect = pi.y > y !== pj.y > y && x < ((pj.x - pi.x) * (y - pi.y)) / (pj.y - pi.y) + pi.x;
21
46
  if (intersect) {
22
47
  inside = !inside;
23
48
  }
24
49
  }
25
- return options.type === "inside"
26
- ? inside
27
- : options.type === "outside"
28
- ? !inside
29
- : false;
50
+ if (options.type === "inside") {
51
+ return inside;
52
+ }
53
+ else {
54
+ return options.type === "outside" ? !inside : false;
55
+ }
30
56
  };
31
57
  this._createPath2D = () => {
32
58
  const container = this._container, options = container.actualOptions.polygon;
@@ -48,13 +74,14 @@ class PolygonMaskInstance {
48
74
  else {
49
75
  delete path.path2d;
50
76
  }
51
- if (path.path2d || !this.raw) {
77
+ if (path.path2d ?? !this.raw) {
52
78
  continue;
53
79
  }
54
80
  path.path2d = new Path2D();
55
- path.path2d.moveTo(this.raw[0].x, this.raw[0].y);
81
+ const firstIndex = 0, firstPoint = this.raw[firstIndex];
82
+ path.path2d.moveTo(firstPoint.x, firstPoint.y);
56
83
  this.raw.forEach((pos, i) => {
57
- if (i > 0) {
84
+ if (i > firstIndex) {
58
85
  path.path2d?.lineTo(pos.x, pos.y);
59
86
  }
60
87
  });
@@ -66,7 +93,7 @@ class PolygonMaskInstance {
66
93
  if (!options) {
67
94
  return;
68
95
  }
69
- const url = svgUrl || options.url, forceDownload = force ?? false;
96
+ const url = svgUrl ?? options.url, forceDownload = force ?? false;
70
97
  if (!url || (this.paths !== undefined && !forceDownload)) {
71
98
  return this.raw;
72
99
  }
@@ -74,14 +101,14 @@ class PolygonMaskInstance {
74
101
  if (!req.ok) {
75
102
  throw new Error(`${engine_1.errorPrefix} occurred during polygon mask download`);
76
103
  }
77
- return this._parseSvgPath(await req.text(), force);
104
+ return await this._parseSvgPath(await req.text(), force);
78
105
  };
79
106
  this._drawPoints = () => {
80
107
  if (!this.raw) {
81
108
  return;
82
109
  }
83
110
  for (const item of this.raw) {
84
- this._container.particles.addParticle({
111
+ void this._container.particles.addParticle({
85
112
  x: item.x,
86
113
  y: item.y,
87
114
  });
@@ -92,11 +119,11 @@ class PolygonMaskInstance {
92
119
  if (!polygonMaskOptions) {
93
120
  return;
94
121
  }
95
- if (!this.raw || !this.raw.length || !this.paths?.length) {
122
+ if (!this.raw?.length || !this.paths?.length) {
96
123
  throw new Error(noPolygonDataLoaded);
97
124
  }
98
125
  let offset = 0, point;
99
- const totalLength = this.paths.reduce((tot, path) => tot + path.length, 0), distance = totalLength / options.particles.number.value;
126
+ const baseAccumulator = 0, totalLength = this.paths.reduce((tot, path) => tot + path.length, baseAccumulator), distance = totalLength / options.particles.number.value;
100
127
  for (const path of this.paths) {
101
128
  const pathDistance = distance * index - offset;
102
129
  if (pathDistance <= path.length) {
@@ -109,12 +136,12 @@ class PolygonMaskInstance {
109
136
  }
110
137
  const scale = this._scale;
111
138
  return {
112
- x: (point?.x ?? 0) * scale + (this.offset?.x ?? 0),
113
- y: (point?.y ?? 0) * scale + (this.offset?.y ?? 0),
139
+ x: (point?.x ?? origin.x) * scale + (this.offset?.x ?? origin.x),
140
+ y: (point?.y ?? origin.y) * scale + (this.offset?.y ?? origin.y),
114
141
  };
115
142
  };
116
143
  this._getPointByIndex = (index) => {
117
- if (!this.raw || !this.raw.length) {
144
+ if (!this.raw?.length) {
118
145
  throw new Error(noPolygonDataLoaded);
119
146
  }
120
147
  const coords = this.raw[index % this.raw.length];
@@ -124,7 +151,7 @@ class PolygonMaskInstance {
124
151
  };
125
152
  };
126
153
  this._getRandomPoint = () => {
127
- if (!this.raw || !this.raw.length) {
154
+ if (!this.raw?.length) {
128
155
  throw new Error(noPolygonDataLoaded);
129
156
  }
130
157
  const coords = (0, engine_1.itemFromArray)(this.raw);
@@ -138,13 +165,13 @@ class PolygonMaskInstance {
138
165
  if (!options) {
139
166
  return;
140
167
  }
141
- if (!this.raw || !this.raw.length || !this.paths?.length) {
168
+ if (!this.raw?.length || !this.paths?.length) {
142
169
  throw new Error(noPolygonDataLoaded);
143
170
  }
144
- const path = (0, engine_1.itemFromArray)(this.paths), distance = Math.floor((0, engine_1.getRandom)() * path.length) + 1, point = path.element.getPointAtLength(distance), scale = this._scale;
171
+ const path = (0, engine_1.itemFromArray)(this.paths), offset = 1, distance = Math.floor((0, engine_1.getRandom)() * path.length) + offset, point = path.element.getPointAtLength(distance), scale = this._scale;
145
172
  return {
146
- x: point.x * scale + (this.offset?.x || 0),
147
- y: point.y * scale + (this.offset?.y || 0),
173
+ x: point.x * scale + (this.offset?.x ?? origin.x),
174
+ y: point.y * scale + (this.offset?.y ?? origin.y),
148
175
  };
149
176
  };
150
177
  this._initRawData = async (force) => {
@@ -166,14 +193,14 @@ class PolygonMaskInstance {
166
193
  const namespaces = 'xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"';
167
194
  svg = `<svg ${namespaces} width="${data.size.width}" height="${data.size.height}">${path}</svg>`;
168
195
  }
169
- this.raw = this._parseSvgPath(svg, force);
196
+ this.raw = await this._parseSvgPath(svg, force);
170
197
  }
171
198
  this._createPath2D();
172
199
  this._engine.dispatchEvent("polygonMaskLoaded", {
173
200
  container: this._container,
174
201
  });
175
202
  };
176
- this._parseSvgPath = (xml, force) => {
203
+ this._parseSvgPath = async (xml, force) => {
177
204
  const forceDownload = force ?? false;
178
205
  if (this.paths !== undefined && !forceDownload) {
179
206
  return this.raw;
@@ -182,7 +209,7 @@ class PolygonMaskInstance {
182
209
  if (!options) {
183
210
  return;
184
211
  }
185
- const parser = new DOMParser(), doc = parser.parseFromString(xml, "image/svg+xml"), svg = doc.getElementsByTagName("svg")[0];
212
+ const parser = new DOMParser(), doc = parser.parseFromString(xml, "image/svg+xml"), firstIndex = 0, svg = doc.getElementsByTagName("svg")[firstIndex];
186
213
  let svgPaths = svg.getElementsByTagName("path");
187
214
  if (!svgPaths.length) {
188
215
  svgPaths = doc.getElementsByTagName("path");
@@ -205,31 +232,33 @@ class PolygonMaskInstance {
205
232
  y: 50,
206
233
  }, canvasSize = container.canvas.size;
207
234
  this.offset = {
208
- x: (canvasSize.width * position.x) / 100 - this.dimension.width / 2,
209
- y: (canvasSize.height * position.y) / 100 - this.dimension.height / 2,
235
+ x: (canvasSize.width * position.x) / engine_1.percentDenominator - this.dimension.width * half,
236
+ y: (canvasSize.height * position.y) / engine_1.percentDenominator - this.dimension.height * half,
210
237
  };
211
- return (0, utils_js_1.parsePaths)(this.paths, scale, this.offset);
238
+ const { parsePaths } = await Promise.resolve().then(() => __importStar(require("./utils.js")));
239
+ return parsePaths(this.paths, scale, this.offset);
212
240
  };
213
- this._polygonBounce = (particle, _delta, direction) => {
241
+ this._polygonBounce = async (particle, delta, direction) => {
214
242
  const options = this._container.actualOptions.polygon;
215
243
  if (!this.raw || !options?.enable || direction !== "top") {
216
244
  return false;
217
245
  }
218
246
  if (options.type === "inside" || options.type === "outside") {
219
247
  let closest, dx, dy;
220
- const pos = particle.getPosition(), radius = particle.getRadius();
221
- for (let i = 0, j = this.raw.length - 1; i < this.raw.length; j = i++) {
222
- const pi = this.raw[i], pj = this.raw[j];
223
- closest = (0, utils_js_1.calcClosestPtOnSegment)(pi, pj, pos);
248
+ const pos = particle.getPosition(), radius = particle.getRadius(), offset = 1;
249
+ for (let i = 0, j = this.raw.length - offset; i < this.raw.length; j = i++) {
250
+ const pi = this.raw[i], pj = this.raw[j], { calcClosestPointOnSegment } = await Promise.resolve().then(() => __importStar(require("./utils.js")));
251
+ closest = calcClosestPointOnSegment(pi, pj, pos);
224
252
  const dist = (0, engine_1.getDistances)(pos, closest);
225
253
  [dx, dy] = [dist.dx, dist.dy];
226
254
  if (dist.distance < radius) {
227
- (0, utils_js_1.segmentBounce)(pi, pj, particle.velocity);
255
+ const { segmentBounce } = await Promise.resolve().then(() => __importStar(require("./utils.js")));
256
+ segmentBounce(pi, pj, particle.velocity);
228
257
  return true;
229
258
  }
230
259
  }
231
260
  if (closest && dx !== undefined && dy !== undefined && !this._checkInsidePolygon(pos)) {
232
- const factor = { x: 1, y: 1 }, diameter = radius * 2;
261
+ const factor = { x: 1, y: 1 }, diameter = radius * double, inverse = -1;
233
262
  if (pos.x >= closest.x) {
234
263
  factor.x = -1;
235
264
  }
@@ -238,15 +267,15 @@ class PolygonMaskInstance {
238
267
  }
239
268
  particle.position.x = closest.x + diameter * factor.x;
240
269
  particle.position.y = closest.y + diameter * factor.y;
241
- particle.velocity.mult(-1);
270
+ particle.velocity.mult(inverse);
242
271
  return true;
243
272
  }
244
273
  }
245
274
  else if (options.type === "inline" && particle.initialPosition) {
246
275
  const dist = (0, engine_1.getDistance)(particle.initialPosition, particle.getPosition()), { velocity } = particle;
247
276
  if (dist > this._moveRadius) {
248
- velocity.x = velocity.y / 2 - velocity.x;
249
- velocity.y = velocity.x / 2 - velocity.y;
277
+ velocity.x = velocity.y * half - velocity.x;
278
+ velocity.y = velocity.x * half - velocity.y;
250
279
  return true;
251
280
  }
252
281
  }
@@ -305,7 +334,7 @@ class PolygonMaskInstance {
305
334
  options.type !== "inline" &&
306
335
  this._checkInsidePolygon(position));
307
336
  }
308
- draw(context) {
337
+ async draw(context) {
309
338
  if (!this.paths?.length) {
310
339
  return;
311
340
  }
@@ -324,10 +353,12 @@ class PolygonMaskInstance {
324
353
  continue;
325
354
  }
326
355
  if (path2d && this.offset) {
327
- (0, utils_js_1.drawPolygonMaskPath)(context, path2d, polygonDraw.stroke, this.offset);
356
+ const { drawPolygonMaskPath } = await Promise.resolve().then(() => __importStar(require("./utils.js")));
357
+ drawPolygonMaskPath(context, path2d, polygonDraw.stroke, this.offset);
328
358
  }
329
359
  else if (rawData) {
330
- (0, utils_js_1.drawPolygonMask)(context, rawData, polygonDraw.stroke);
360
+ const { drawPolygonMask } = await Promise.resolve().then(() => __importStar(require("./utils.js")));
361
+ drawPolygonMask(context, rawData, polygonDraw.stroke);
331
362
  }
332
363
  }
333
364
  }
@@ -342,12 +373,12 @@ class PolygonMaskInstance {
342
373
  await this._initRawData();
343
374
  }
344
375
  }
345
- particleBounce(particle, delta, direction) {
346
- return this._polygonBounce(particle, delta, direction);
376
+ async particleBounce(particle, delta, direction) {
377
+ return await this._polygonBounce(particle, delta, direction);
347
378
  }
348
379
  particlePosition(position) {
349
- const options = this._container.actualOptions.polygon;
350
- if (!(options?.enable && (this.raw?.length ?? 0) > 0)) {
380
+ const options = this._container.actualOptions.polygon, defaultLength = 0;
381
+ if (!(options?.enable && (this.raw?.length ?? defaultLength) > defaultLength)) {
351
382
  return;
352
383
  }
353
384
  return (0, engine_1.deepExtend)({}, position ? position : this._randomPoint());
@@ -371,10 +402,13 @@ class PolygonMaskInstance {
371
402
  if (this.redrawTimeout) {
372
403
  clearTimeout(this.redrawTimeout);
373
404
  }
374
- this.redrawTimeout = window.setTimeout(async () => {
375
- await this._initRawData(true);
376
- await container.particles.redraw();
377
- }, 250);
405
+ const timeout = 250;
406
+ this.redrawTimeout = window.setTimeout(() => {
407
+ void (async () => {
408
+ await this._initRawData(true);
409
+ await container.particles.redraw();
410
+ })();
411
+ }, timeout);
378
412
  }
379
413
  stop() {
380
414
  delete this.raw;
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.PolygonMaskPlugin = void 0;
27
+ const PolygonMask_js_1 = require("./Options/Classes/PolygonMask.js");
28
+ class PolygonMaskPlugin {
29
+ constructor(engine) {
30
+ this.id = "polygonMask";
31
+ this._engine = engine;
32
+ }
33
+ async getPlugin(container) {
34
+ const { PolygonMaskInstance } = await Promise.resolve().then(() => __importStar(require("./PolygonMaskInstance.js")));
35
+ return new PolygonMaskInstance(container, this._engine);
36
+ }
37
+ loadOptions(options, source) {
38
+ if (!this.needsPlugin(options) && !this.needsPlugin(source)) {
39
+ return;
40
+ }
41
+ let polygonOptions = options.polygon;
42
+ if (polygonOptions?.load === undefined) {
43
+ options.polygon = polygonOptions = new PolygonMask_js_1.PolygonMask();
44
+ }
45
+ polygonOptions.load(source?.polygon);
46
+ }
47
+ needsPlugin(options) {
48
+ return (options?.polygon?.enable ??
49
+ (options?.polygon?.type !== undefined && options.polygon.type !== "none"));
50
+ }
51
+ }
52
+ exports.PolygonMaskPlugin = PolygonMaskPlugin;
package/cjs/index.js CHANGED
@@ -10,38 +10,26 @@ var __createBinding = (this && this.__createBinding) || (Object.create ? (functi
10
10
  if (k2 === undefined) k2 = k;
11
11
  o[k2] = m[k];
12
12
  }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
13
25
  var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
26
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
27
  };
16
28
  Object.defineProperty(exports, "__esModule", { value: true });
17
29
  exports.loadPolygonMaskPlugin = void 0;
18
30
  require("./pathseg.js");
19
- const PolygonMask_js_1 = require("./Options/Classes/PolygonMask.js");
20
- const PolygonMaskInstance_js_1 = require("./PolygonMaskInstance.js");
21
- class PolygonMaskPlugin {
22
- constructor(engine) {
23
- this.id = "polygonMask";
24
- this._engine = engine;
25
- }
26
- getPlugin(container) {
27
- return new PolygonMaskInstance_js_1.PolygonMaskInstance(container, this._engine);
28
- }
29
- loadOptions(options, source) {
30
- if (!this.needsPlugin(options) && !this.needsPlugin(source)) {
31
- return;
32
- }
33
- let polygonOptions = options.polygon;
34
- if (polygonOptions?.load === undefined) {
35
- options.polygon = polygonOptions = new PolygonMask_js_1.PolygonMask();
36
- }
37
- polygonOptions.load(source?.polygon);
38
- }
39
- needsPlugin(options) {
40
- return (options?.polygon?.enable ??
41
- (options?.polygon?.type !== undefined && options.polygon.type !== "none"));
42
- }
43
- }
44
31
  async function loadPolygonMaskPlugin(engine, refresh = true) {
32
+ const { PolygonMaskPlugin } = await Promise.resolve().then(() => __importStar(require("./PolygonMaskPlugin.js")));
45
33
  await engine.addPlugin(new PolygonMaskPlugin(engine), refresh);
46
34
  }
47
35
  exports.loadPolygonMaskPlugin = loadPolygonMaskPlugin;
package/cjs/utils.js CHANGED
@@ -1,14 +1,19 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.segmentBounce = exports.calcClosestPtOnSegment = exports.parsePaths = exports.drawPolygonMaskPath = exports.drawPolygonMask = void 0;
3
+ exports.segmentBounce = exports.calcClosestPointOnSegment = exports.parsePaths = exports.drawPolygonMaskPath = exports.drawPolygonMask = void 0;
4
4
  const engine_1 = require("@tsparticles/engine");
5
+ const squareExp = 2, inSegmentRange = {
6
+ min: 0,
7
+ max: 1,
8
+ }, double = 2;
5
9
  function drawPolygonMask(context, rawData, stroke) {
6
10
  const color = (0, engine_1.rangeColorToRgb)(stroke.color);
7
11
  if (!color) {
8
12
  return;
9
13
  }
14
+ const firstIndex = 0, firstItem = rawData[firstIndex];
10
15
  context.beginPath();
11
- context.moveTo(rawData[0].x, rawData[0].y);
16
+ context.moveTo(firstItem.x, firstItem.y);
12
17
  for (const item of rawData) {
13
18
  context.lineTo(item.x, item.y);
14
19
  }
@@ -19,7 +24,13 @@ function drawPolygonMask(context, rawData, stroke) {
19
24
  }
20
25
  exports.drawPolygonMask = drawPolygonMask;
21
26
  function drawPolygonMaskPath(context, path, stroke, position) {
22
- context.setTransform(1, 0, 0, 1, position.x, position.y);
27
+ const defaultTransform = {
28
+ a: 1,
29
+ b: 0,
30
+ c: 0,
31
+ d: 1,
32
+ };
33
+ context.setTransform(defaultTransform.a, defaultTransform.b, defaultTransform.c, defaultTransform.d, position.x, position.y);
23
34
  const color = (0, engine_1.rangeColorToRgb)(stroke.color);
24
35
  if (!color) {
25
36
  return;
@@ -27,13 +38,13 @@ function drawPolygonMaskPath(context, path, stroke, position) {
27
38
  context.strokeStyle = (0, engine_1.getStyleFromRgb)(color, stroke.opacity);
28
39
  context.lineWidth = stroke.width;
29
40
  context.stroke(path);
30
- context.setTransform(1, 0, 0, 1, 0, 0);
41
+ context.resetTransform();
31
42
  }
32
43
  exports.drawPolygonMaskPath = drawPolygonMaskPath;
33
44
  function parsePaths(paths, scale, offset) {
34
- const res = [];
45
+ const res = [], defaultCount = 0;
35
46
  for (const path of paths) {
36
- const segments = path.element.pathSegList, len = segments?.numberOfItems ?? 0, p = {
47
+ const segments = path.element.pathSegList, len = segments?.numberOfItems ?? defaultCount, p = {
37
48
  x: 0,
38
49
  y: 0,
39
50
  };
@@ -89,25 +100,25 @@ function parsePaths(paths, scale, offset) {
89
100
  return res;
90
101
  }
91
102
  exports.parsePaths = parsePaths;
92
- function calcClosestPtOnSegment(s1, s2, pos) {
93
- const { dx: dx1, dy: dy1 } = (0, engine_1.getDistances)(pos, s1), { dx: dx2, dy: dy2 } = (0, engine_1.getDistances)(s2, s1), t = (dx1 * dx2 + dy1 * dy2) / (dx2 ** 2 + dy2 ** 2), res = {
103
+ function calcClosestPointOnSegment(s1, s2, pos) {
104
+ const { dx: dx1, dy: dy1 } = (0, engine_1.getDistances)(pos, s1), { dx: dx2, dy: dy2 } = (0, engine_1.getDistances)(s2, s1), t = (dx1 * dx2 + dy1 * dy2) / (dx2 ** squareExp + dy2 ** squareExp), res = {
94
105
  x: s1.x + dx2 * t,
95
106
  y: s1.y + dy2 * t,
96
- isOnSegment: t >= 0 && t <= 1,
107
+ isOnSegment: t >= inSegmentRange.min && t <= inSegmentRange.max,
97
108
  };
98
- if (t < 0) {
109
+ if (t < inSegmentRange.min) {
99
110
  res.x = s1.x;
100
111
  res.y = s1.y;
101
112
  }
102
- else if (t > 1) {
113
+ else if (t > inSegmentRange.max) {
103
114
  res.x = s2.x;
104
115
  res.y = s2.y;
105
116
  }
106
117
  return res;
107
118
  }
108
- exports.calcClosestPtOnSegment = calcClosestPtOnSegment;
119
+ exports.calcClosestPointOnSegment = calcClosestPointOnSegment;
109
120
  function segmentBounce(start, stop, velocity) {
110
- const { dx, dy } = (0, engine_1.getDistances)(start, stop), wallAngle = Math.atan2(dy, dx), wallNormal = engine_1.Vector.create(Math.sin(wallAngle), -Math.cos(wallAngle)), d = 2 * (velocity.x * wallNormal.x + velocity.y * wallNormal.y);
121
+ const { dx, dy } = (0, engine_1.getDistances)(start, stop), wallAngle = Math.atan2(dy, dx), wallNormal = engine_1.Vector.create(Math.sin(wallAngle), -Math.cos(wallAngle)), d = double * (velocity.x * wallNormal.x + velocity.y * wallNormal.y);
111
122
  wallNormal.multTo(d);
112
123
  velocity.subFrom(wallNormal);
113
124
  }
@@ -0,0 +1,30 @@
1
+ /*!
2
+ * Author : Matteo Bruni
3
+ * MIT license: https://opensource.org/licenses/MIT
4
+ * Demo / Generator : https://particles.js.org/
5
+ * GitHub : https://www.github.com/matteobruni/tsparticles
6
+ * How to use? : Check the GitHub README
7
+ * v3.2.0
8
+ */
9
+ "use strict";
10
+ /*
11
+ * ATTENTION: The "eval" devtool has been used (maybe by default in mode: "development").
12
+ * This devtool is neither made for production nor for readable output files.
13
+ * It uses "eval()" calls to create a separate source file in the browser devtools.
14
+ * If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
15
+ * or disable the default devtool with "devtool: false".
16
+ * If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
17
+ */
18
+ (this["webpackChunk_tsparticles_plugin_polygon_mask"] = this["webpackChunk_tsparticles_plugin_polygon_mask"] || []).push([["dist_browser_PolygonMaskInstance_js"],{
19
+
20
+ /***/ "./dist/browser/PolygonMaskInstance.js":
21
+ /*!*********************************************!*\
22
+ !*** ./dist/browser/PolygonMaskInstance.js ***!
23
+ \*********************************************/
24
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
25
+
26
+ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ PolygonMaskInstance: () => (/* binding */ PolygonMaskInstance)\n/* harmony export */ });\n/* harmony import */ var _tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @tsparticles/engine */ \"@tsparticles/engine\");\n/* harmony import */ var _tsparticles_engine__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__);\n\nconst noPolygonDataLoaded = `${_tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.errorPrefix} No polygon data loaded.`,\n noPolygonFound = `${_tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.errorPrefix} No polygon found, you need to specify SVG url in config.`,\n origin = {\n x: 0,\n y: 0\n },\n half = 0.5,\n double = 2;\nclass PolygonMaskInstance {\n constructor(container, engine) {\n this._checkInsidePolygon = position => {\n const container = this._container,\n options = container.actualOptions.polygon;\n if (!options?.enable || options.type === \"none\" || options.type === \"inline\") {\n return true;\n }\n if (!this.raw) {\n throw new Error(noPolygonFound);\n }\n const canvasSize = container.canvas.size,\n x = position?.x ?? (0,_tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.getRandom)() * canvasSize.width,\n y = position?.y ?? (0,_tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.getRandom)() * canvasSize.height,\n indexOffset = 1;\n let inside = false;\n for (let i = 0, j = this.raw.length - indexOffset; i < this.raw.length; j = i++) {\n const pi = this.raw[i],\n pj = this.raw[j],\n intersect = pi.y > y !== pj.y > y && x < (pj.x - pi.x) * (y - pi.y) / (pj.y - pi.y) + pi.x;\n if (intersect) {\n inside = !inside;\n }\n }\n if (options.type === \"inside\") {\n return inside;\n } else {\n return options.type === \"outside\" ? !inside : false;\n }\n };\n this._createPath2D = () => {\n const container = this._container,\n options = container.actualOptions.polygon;\n if (!options || !this.paths?.length) {\n return;\n }\n for (const path of this.paths) {\n const pathData = path.element?.getAttribute(\"d\");\n if (pathData) {\n const path2d = new Path2D(pathData),\n matrix = document.createElementNS(\"http://www.w3.org/2000/svg\", \"svg\").createSVGMatrix(),\n finalPath = new Path2D(),\n transform = matrix.scale(this._scale);\n if (finalPath.addPath) {\n finalPath.addPath(path2d, transform);\n path.path2d = finalPath;\n } else {\n delete path.path2d;\n }\n } else {\n delete path.path2d;\n }\n if (path.path2d ?? !this.raw) {\n continue;\n }\n path.path2d = new Path2D();\n const firstIndex = 0,\n firstPoint = this.raw[firstIndex];\n path.path2d.moveTo(firstPoint.x, firstPoint.y);\n this.raw.forEach((pos, i) => {\n if (i > firstIndex) {\n path.path2d?.lineTo(pos.x, pos.y);\n }\n });\n path.path2d.closePath();\n }\n };\n this._downloadSvgPath = async (svgUrl, force) => {\n const options = this._container.actualOptions.polygon;\n if (!options) {\n return;\n }\n const url = svgUrl ?? options.url,\n forceDownload = force ?? false;\n if (!url || this.paths !== undefined && !forceDownload) {\n return this.raw;\n }\n const req = await fetch(url);\n if (!req.ok) {\n throw new Error(`${_tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.errorPrefix} occurred during polygon mask download`);\n }\n return await this._parseSvgPath(await req.text(), force);\n };\n this._drawPoints = () => {\n if (!this.raw) {\n return;\n }\n for (const item of this.raw) {\n void this._container.particles.addParticle({\n x: item.x,\n y: item.y\n });\n }\n };\n this._getEquidistantPointByIndex = index => {\n const container = this._container,\n options = container.actualOptions,\n polygonMaskOptions = options.polygon;\n if (!polygonMaskOptions) {\n return;\n }\n if (!this.raw?.length || !this.paths?.length) {\n throw new Error(noPolygonDataLoaded);\n }\n let offset = 0,\n point;\n const baseAccumulator = 0,\n totalLength = this.paths.reduce((tot, path) => tot + path.length, baseAccumulator),\n distance = totalLength / options.particles.number.value;\n for (const path of this.paths) {\n const pathDistance = distance * index - offset;\n if (pathDistance <= path.length) {\n point = path.element.getPointAtLength(pathDistance);\n break;\n } else {\n offset += path.length;\n }\n }\n const scale = this._scale;\n return {\n x: (point?.x ?? origin.x) * scale + (this.offset?.x ?? origin.x),\n y: (point?.y ?? origin.y) * scale + (this.offset?.y ?? origin.y)\n };\n };\n this._getPointByIndex = index => {\n if (!this.raw?.length) {\n throw new Error(noPolygonDataLoaded);\n }\n const coords = this.raw[index % this.raw.length];\n return {\n x: coords.x,\n y: coords.y\n };\n };\n this._getRandomPoint = () => {\n if (!this.raw?.length) {\n throw new Error(noPolygonDataLoaded);\n }\n const coords = (0,_tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.itemFromArray)(this.raw);\n return {\n x: coords.x,\n y: coords.y\n };\n };\n this._getRandomPointByLength = () => {\n const container = this._container,\n options = container.actualOptions.polygon;\n if (!options) {\n return;\n }\n if (!this.raw?.length || !this.paths?.length) {\n throw new Error(noPolygonDataLoaded);\n }\n const path = (0,_tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.itemFromArray)(this.paths),\n offset = 1,\n distance = Math.floor((0,_tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.getRandom)() * path.length) + offset,\n point = path.element.getPointAtLength(distance),\n scale = this._scale;\n return {\n x: point.x * scale + (this.offset?.x ?? origin.x),\n y: point.y * scale + (this.offset?.y ?? origin.y)\n };\n };\n this._initRawData = async force => {\n const options = this._container.actualOptions.polygon;\n if (!options) {\n return;\n }\n if (options.url) {\n this.raw = await this._downloadSvgPath(options.url, force);\n } else if (options.data) {\n const data = options.data;\n let svg;\n if ((0,_tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.isString)(data)) {\n svg = data;\n } else {\n const getPath = p => `<path d=\"${p}\" />`,\n path = (0,_tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.isArray)(data.path) ? data.path.map(getPath).join(\"\") : getPath(data.path);\n const namespaces = 'xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"';\n svg = `<svg ${namespaces} width=\"${data.size.width}\" height=\"${data.size.height}\">${path}</svg>`;\n }\n this.raw = await this._parseSvgPath(svg, force);\n }\n this._createPath2D();\n this._engine.dispatchEvent(\"polygonMaskLoaded\", {\n container: this._container\n });\n };\n this._parseSvgPath = async (xml, force) => {\n const forceDownload = force ?? false;\n if (this.paths !== undefined && !forceDownload) {\n return this.raw;\n }\n const container = this._container,\n options = container.actualOptions.polygon;\n if (!options) {\n return;\n }\n const parser = new DOMParser(),\n doc = parser.parseFromString(xml, \"image/svg+xml\"),\n firstIndex = 0,\n svg = doc.getElementsByTagName(\"svg\")[firstIndex];\n let svgPaths = svg.getElementsByTagName(\"path\");\n if (!svgPaths.length) {\n svgPaths = doc.getElementsByTagName(\"path\");\n }\n this.paths = [];\n for (let i = 0; i < svgPaths.length; i++) {\n const path = svgPaths.item(i);\n if (path) {\n this.paths.push({\n element: path,\n length: path.getTotalLength()\n });\n }\n }\n const scale = this._scale;\n this.dimension.width = parseFloat(svg.getAttribute(\"width\") ?? \"0\") * scale;\n this.dimension.height = parseFloat(svg.getAttribute(\"height\") ?? \"0\") * scale;\n const position = options.position ?? {\n x: 50,\n y: 50\n },\n canvasSize = container.canvas.size;\n this.offset = {\n x: canvasSize.width * position.x / _tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.percentDenominator - this.dimension.width * half,\n y: canvasSize.height * position.y / _tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.percentDenominator - this.dimension.height * half\n };\n const {\n parsePaths\n } = await __webpack_require__.e(/*! import() */ \"dist_browser_utils_js\").then(__webpack_require__.bind(__webpack_require__, /*! ./utils.js */ \"./dist/browser/utils.js\"));\n return parsePaths(this.paths, scale, this.offset);\n };\n this._polygonBounce = async (particle, delta, direction) => {\n const options = this._container.actualOptions.polygon;\n if (!this.raw || !options?.enable || direction !== \"top\") {\n return false;\n }\n if (options.type === \"inside\" || options.type === \"outside\") {\n let closest, dx, dy;\n const pos = particle.getPosition(),\n radius = particle.getRadius(),\n offset = 1;\n for (let i = 0, j = this.raw.length - offset; i < this.raw.length; j = i++) {\n const pi = this.raw[i],\n pj = this.raw[j],\n {\n calcClosestPointOnSegment\n } = await __webpack_require__.e(/*! import() */ \"dist_browser_utils_js\").then(__webpack_require__.bind(__webpack_require__, /*! ./utils.js */ \"./dist/browser/utils.js\"));\n closest = calcClosestPointOnSegment(pi, pj, pos);\n const dist = (0,_tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.getDistances)(pos, closest);\n [dx, dy] = [dist.dx, dist.dy];\n if (dist.distance < radius) {\n const {\n segmentBounce\n } = await __webpack_require__.e(/*! import() */ \"dist_browser_utils_js\").then(__webpack_require__.bind(__webpack_require__, /*! ./utils.js */ \"./dist/browser/utils.js\"));\n segmentBounce(pi, pj, particle.velocity);\n return true;\n }\n }\n if (closest && dx !== undefined && dy !== undefined && !this._checkInsidePolygon(pos)) {\n const factor = {\n x: 1,\n y: 1\n },\n diameter = radius * double,\n inverse = -1;\n if (pos.x >= closest.x) {\n factor.x = -1;\n }\n if (pos.y >= closest.y) {\n factor.y = -1;\n }\n particle.position.x = closest.x + diameter * factor.x;\n particle.position.y = closest.y + diameter * factor.y;\n particle.velocity.mult(inverse);\n return true;\n }\n } else if (options.type === \"inline\" && particle.initialPosition) {\n const dist = (0,_tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.getDistance)(particle.initialPosition, particle.getPosition()),\n {\n velocity\n } = particle;\n if (dist > this._moveRadius) {\n velocity.x = velocity.y * half - velocity.x;\n velocity.y = velocity.x * half - velocity.y;\n return true;\n }\n }\n return false;\n };\n this._randomPoint = () => {\n const container = this._container,\n options = container.actualOptions.polygon;\n if (!options) {\n return;\n }\n let position;\n if (options.type === \"inline\") {\n switch (options.inline.arrangement) {\n case \"random-point\":\n position = this._getRandomPoint();\n break;\n case \"random-length\":\n position = this._getRandomPointByLength();\n break;\n case \"equidistant\":\n position = this._getEquidistantPointByIndex(container.particles.count);\n break;\n case \"one-per-point\":\n case \"per-point\":\n default:\n position = this._getPointByIndex(container.particles.count);\n }\n } else {\n const canvasSize = container.canvas.size;\n position = {\n x: (0,_tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.getRandom)() * canvasSize.width,\n y: (0,_tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.getRandom)() * canvasSize.height\n };\n }\n if (this._checkInsidePolygon(position)) {\n return position;\n } else {\n return this._randomPoint();\n }\n };\n this._container = container;\n this._engine = engine;\n this.dimension = {\n height: 0,\n width: 0\n };\n this._moveRadius = 0;\n this._scale = 1;\n }\n clickPositionValid(position) {\n const options = this._container.actualOptions.polygon;\n return !!options?.enable && options.type !== \"none\" && options.type !== \"inline\" && this._checkInsidePolygon(position);\n }\n async draw(context) {\n if (!this.paths?.length) {\n return;\n }\n const options = this._container.actualOptions.polygon;\n if (!options?.enable) {\n return;\n }\n const polygonDraw = options.draw;\n if (!polygonDraw.enable) {\n return;\n }\n const rawData = this.raw;\n for (const path of this.paths) {\n const path2d = path.path2d;\n if (!context) {\n continue;\n }\n if (path2d && this.offset) {\n const {\n drawPolygonMaskPath\n } = await __webpack_require__.e(/*! import() */ \"dist_browser_utils_js\").then(__webpack_require__.bind(__webpack_require__, /*! ./utils.js */ \"./dist/browser/utils.js\"));\n drawPolygonMaskPath(context, path2d, polygonDraw.stroke, this.offset);\n } else if (rawData) {\n const {\n drawPolygonMask\n } = await __webpack_require__.e(/*! import() */ \"dist_browser_utils_js\").then(__webpack_require__.bind(__webpack_require__, /*! ./utils.js */ \"./dist/browser/utils.js\"));\n drawPolygonMask(context, rawData, polygonDraw.stroke);\n }\n }\n }\n async init() {\n const container = this._container,\n polygonMaskOptions = container.actualOptions.polygon,\n pxRatio = container.retina.pixelRatio;\n if (!polygonMaskOptions) {\n return;\n }\n this._moveRadius = polygonMaskOptions.move.radius * pxRatio;\n this._scale = polygonMaskOptions.scale * pxRatio;\n if (polygonMaskOptions.enable) {\n await this._initRawData();\n }\n }\n async particleBounce(particle, delta, direction) {\n return await this._polygonBounce(particle, delta, direction);\n }\n particlePosition(position) {\n const options = this._container.actualOptions.polygon,\n defaultLength = 0;\n if (!(options?.enable && (this.raw?.length ?? defaultLength) > defaultLength)) {\n return;\n }\n return (0,_tsparticles_engine__WEBPACK_IMPORTED_MODULE_0__.deepExtend)({}, position ? position : this._randomPoint());\n }\n particlesInitialization() {\n const options = this._container.actualOptions.polygon;\n if (options?.enable && options.type === \"inline\" && (options.inline.arrangement === \"one-per-point\" || options.inline.arrangement === \"per-point\")) {\n this._drawPoints();\n return true;\n }\n return false;\n }\n resize() {\n const container = this._container,\n options = container.actualOptions.polygon;\n if (!(options?.enable && options.type !== \"none\")) {\n return;\n }\n if (this.redrawTimeout) {\n clearTimeout(this.redrawTimeout);\n }\n const timeout = 250;\n this.redrawTimeout = window.setTimeout(() => {\n void (async () => {\n await this._initRawData(true);\n await container.particles.redraw();\n })();\n }, timeout);\n }\n stop() {\n delete this.raw;\n delete this.paths;\n }\n}\n\n//# sourceURL=webpack://@tsparticles/plugin-polygon-mask/./dist/browser/PolygonMaskInstance.js?");
27
+
28
+ /***/ })
29
+
30
+ }]);