@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.
package/719.min.js ADDED
@@ -0,0 +1,2 @@
1
+ /*! For license information please see 719.min.js.LICENSE.txt */
2
+ (this.webpackChunk_tsparticles_plugin_polygon_mask=this.webpackChunk_tsparticles_plugin_polygon_mask||[]).push([[719],{719:(t,i,e)=>{e.d(i,{PolygonMaskInstance:()=>c});var n=e(533);const s=`${n.errorPrefix} No polygon data loaded.`,a=`${n.errorPrefix} No polygon found, you need to specify SVG url in config.`,o=0,h=0,r=.5;class c{constructor(t,i){this._checkInsidePolygon=t=>{const i=this._container,e=i.actualOptions.polygon;if(!e?.enable||"none"===e.type||"inline"===e.type)return!0;if(!this.raw)throw new Error(a);const s=i.canvas.size,o=t?.x??(0,n.getRandom)()*s.width,h=t?.y??(0,n.getRandom)()*s.height;let r=!1;for(let t=0,i=this.raw.length-1;t<this.raw.length;i=t++){const e=this.raw[t],n=this.raw[i];e.y>h!=n.y>h&&o<(n.x-e.x)*(h-e.y)/(n.y-e.y)+e.x&&(r=!r)}return"inside"===e.type?r:"outside"===e.type&&!r},this._createPath2D=()=>{if(this._container.actualOptions.polygon&&this.paths?.length)for(const t of this.paths){const i=t.element?.getAttribute("d");if(i){const e=new Path2D(i),n=document.createElementNS("http://www.w3.org/2000/svg","svg").createSVGMatrix(),s=new Path2D,a=n.scale(this._scale);s.addPath?(s.addPath(e,a),t.path2d=s):delete t.path2d}else delete t.path2d;if(t.path2d??!this.raw)continue;t.path2d=new Path2D;const e=0,n=this.raw[e];t.path2d.moveTo(n.x,n.y),this.raw.forEach(((i,n)=>{n>e&&t.path2d?.lineTo(i.x,i.y)})),t.path2d.closePath()}},this._downloadSvgPath=async(t,i)=>{const e=this._container.actualOptions.polygon;if(!e)return;const s=t??e.url,a=i??!1;if(!s||void 0!==this.paths&&!a)return this.raw;const o=await fetch(s);if(!o.ok)throw new Error(`${n.errorPrefix} occurred during polygon mask download`);return await this._parseSvgPath(await o.text(),i)},this._drawPoints=()=>{if(this.raw)for(const t of this.raw)this._container.particles.addParticle({x:t.x,y:t.y})},this._getEquidistantPointByIndex=t=>{const i=this._container.actualOptions;if(!i.polygon)return;if(!this.raw?.length||!this.paths?.length)throw new Error(s);let e,n=0;const a=this.paths.reduce(((t,i)=>t+i.length),0)/i.particles.number.value;for(const i of this.paths){const s=a*t-n;if(s<=i.length){e=i.element.getPointAtLength(s);break}n+=i.length}const r=this._scale;return{x:(e?.x??o)*r+(this.offset?.x??o),y:(e?.y??h)*r+(this.offset?.y??h)}},this._getPointByIndex=t=>{if(!this.raw?.length)throw new Error(s);const i=this.raw[t%this.raw.length];return{x:i.x,y:i.y}},this._getRandomPoint=()=>{if(!this.raw?.length)throw new Error(s);const t=(0,n.itemFromArray)(this.raw);return{x:t.x,y:t.y}},this._getRandomPointByLength=()=>{if(!this._container.actualOptions.polygon)return;if(!this.raw?.length||!this.paths?.length)throw new Error(s);const t=(0,n.itemFromArray)(this.paths),i=Math.floor((0,n.getRandom)()*t.length)+1,e=t.element.getPointAtLength(i),a=this._scale;return{x:e.x*a+(this.offset?.x??o),y:e.y*a+(this.offset?.y??h)}},this._initRawData=async t=>{const i=this._container.actualOptions.polygon;if(i){if(i.url)this.raw=await this._downloadSvgPath(i.url,t);else if(i.data){const e=i.data;let s;if((0,n.isString)(e))s=e;else{const t=t=>`<path d="${t}" />`,i=(0,n.isArray)(e.path)?e.path.map(t).join(""):t(e.path);s=`<svg ${'xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"'} width="${e.size.width}" height="${e.size.height}">${i}</svg>`}this.raw=await this._parseSvgPath(s,t)}this._createPath2D(),this._engine.dispatchEvent("polygonMaskLoaded",{container:this._container})}},this._parseSvgPath=async(t,i)=>{const s=i??!1;if(void 0!==this.paths&&!s)return this.raw;const a=this._container,o=a.actualOptions.polygon;if(!o)return;const h=(new DOMParser).parseFromString(t,"image/svg+xml"),c=h.getElementsByTagName("svg")[0];let l=c.getElementsByTagName("path");l.length||(l=h.getElementsByTagName("path")),this.paths=[];for(let t=0;t<l.length;t++){const i=l.item(t);i&&this.paths.push({element:i,length:i.getTotalLength()})}const g=this._scale;this.dimension.width=parseFloat(c.getAttribute("width")??"0")*g,this.dimension.height=parseFloat(c.getAttribute("height")??"0")*g;const p=o.position??{x:50,y:50},d=a.canvas.size;this.offset={x:d.width*p.x/n.percentDenominator-this.dimension.width*r,y:d.height*p.y/n.percentDenominator-this.dimension.height*r};const{parsePaths:y}=await e.e(856).then(e.bind(e,856));return y(this.paths,g,this.offset)},this._polygonBounce=async(t,i,s)=>{const a=this._container.actualOptions.polygon;if(!this.raw||!a?.enable||"top"!==s)return!1;if("inside"===a.type||"outside"===a.type){let i,s,a;const o=t.getPosition(),h=t.getRadius(),r=1;for(let c=0,l=this.raw.length-r;c<this.raw.length;l=c++){const r=this.raw[c],g=this.raw[l],{calcClosestPointOnSegment:p}=await e.e(856).then(e.bind(e,856));i=p(r,g,o);const d=(0,n.getDistances)(o,i);if([s,a]=[d.dx,d.dy],d.distance<h){const{segmentBounce:i}=await e.e(856).then(e.bind(e,856));return i(r,g,t.velocity),!0}}if(i&&void 0!==s&&void 0!==a&&!this._checkInsidePolygon(o)){const e={x:1,y:1},n=2*h,s=-1;return o.x>=i.x&&(e.x=-1),o.y>=i.y&&(e.y=-1),t.position.x=i.x+n*e.x,t.position.y=i.y+n*e.y,t.velocity.mult(s),!0}}else if("inline"===a.type&&t.initialPosition){const i=(0,n.getDistance)(t.initialPosition,t.getPosition()),{velocity:e}=t;if(i>this._moveRadius)return e.x=e.y*r-e.x,e.y=e.x*r-e.y,!0}return!1},this._randomPoint=()=>{const t=this._container,i=t.actualOptions.polygon;if(!i)return;let e;if("inline"===i.type)switch(i.inline.arrangement){case"random-point":e=this._getRandomPoint();break;case"random-length":e=this._getRandomPointByLength();break;case"equidistant":e=this._getEquidistantPointByIndex(t.particles.count);break;default:e=this._getPointByIndex(t.particles.count)}else{const i=t.canvas.size;e={x:(0,n.getRandom)()*i.width,y:(0,n.getRandom)()*i.height}}return this._checkInsidePolygon(e)?e:this._randomPoint()},this._container=t,this._engine=i,this.dimension={height:0,width:0},this._moveRadius=0,this._scale=1}clickPositionValid(t){const i=this._container.actualOptions.polygon;return!!i?.enable&&"none"!==i.type&&"inline"!==i.type&&this._checkInsidePolygon(t)}async draw(t){if(!this.paths?.length)return;const i=this._container.actualOptions.polygon;if(!i?.enable)return;const n=i.draw;if(!n.enable)return;const s=this.raw;for(const i of this.paths){const a=i.path2d;if(t)if(a&&this.offset){const{drawPolygonMaskPath:i}=await e.e(856).then(e.bind(e,856));i(t,a,n.stroke,this.offset)}else if(s){const{drawPolygonMask:i}=await e.e(856).then(e.bind(e,856));i(t,s,n.stroke)}}}async init(){const t=this._container,i=t.actualOptions.polygon,e=t.retina.pixelRatio;i&&(this._moveRadius=i.move.radius*e,this._scale=i.scale*e,i.enable&&await this._initRawData())}async particleBounce(t,i,e){return await this._polygonBounce(t,i,e)}particlePosition(t){const i=this._container.actualOptions.polygon;if(i?.enable&&(this.raw?.length??0)>0)return(0,n.deepExtend)({},t||this._randomPoint())}particlesInitialization(){const t=this._container.actualOptions.polygon;return!(!t?.enable||"inline"!==t.type||"one-per-point"!==t.inline.arrangement&&"per-point"!==t.inline.arrangement)&&(this._drawPoints(),!0)}resize(){const t=this._container,i=t.actualOptions.polygon;if(!i?.enable||"none"===i.type)return;this.redrawTimeout&&clearTimeout(this.redrawTimeout);this.redrawTimeout=window.setTimeout((()=>{(async()=>{await this._initRawData(!0),await t.particles.redraw()})()}),250)}stop(){delete this.raw,delete this.paths}}}}]);
@@ -0,0 +1 @@
1
+ /*! tsParticles Polygon Mask Plugin v3.2.0 by Matteo Bruni */
package/787.min.js ADDED
@@ -0,0 +1,2 @@
1
+ /*! For license information please see 787.min.js.LICENSE.txt */
2
+ (this.webpackChunk_tsparticles_plugin_polygon_mask=this.webpackChunk_tsparticles_plugin_polygon_mask||[]).push([[787],{787:(t,i,s)=>{s.d(i,{PolygonMaskPlugin:()=>r});var o=s(533);class e{constructor(){this.color=new o.OptionsColor,this.width=.5,this.opacity=1}load(t){t&&(this.color=o.OptionsColor.create(this.color,t.color),(0,o.isString)(this.color.value)&&(this.opacity=(0,o.stringToAlpha)(this.color.value)??this.opacity),void 0!==t.opacity&&(this.opacity=t.opacity),void 0!==t.width&&(this.width=t.width))}}class n{constructor(){this.enable=!1,this.stroke=new e}load(t){if(!t)return;void 0!==t.enable&&(this.enable=t.enable);const i=t.stroke;this.stroke.load(i)}}class a{constructor(){this.arrangement="one-per-point"}load(t){t&&void 0!==t.arrangement&&(this.arrangement=t.arrangement)}}class h{constructor(){this.path=[],this.size={height:0,width:0}}load(t){t&&(void 0!==t.path&&(this.path=t.path),void 0!==t.size&&(void 0!==t.size.width&&(this.size.width=t.size.width),void 0!==t.size.height&&(this.size.height=t.size.height)))}}class l{constructor(){this.radius=10,this.type="path"}load(t){t&&(void 0!==t.radius&&(this.radius=t.radius),void 0!==t.type&&(this.type=t.type))}}class d{constructor(){this.draw=new n,this.enable=!1,this.inline=new a,this.move=new l,this.scale=1,this.type="none"}load(t){t&&(this.draw.load(t.draw),this.inline.load(t.inline),this.move.load(t.move),void 0!==t.scale&&(this.scale=t.scale),void 0!==t.type&&(this.type=t.type),void 0!==t.enable?this.enable=t.enable:this.enable="none"!==this.type,void 0!==t.url&&(this.url=t.url),void 0!==t.data&&((0,o.isString)(t.data)?this.data=t.data:(this.data=new h,this.data.load(t.data))),void 0!==t.position&&(this.position=(0,o.deepExtend)({},t.position)))}}class r{constructor(t){this.id="polygonMask",this._engine=t}async getPlugin(t){const{PolygonMaskInstance:i}=await s.e(719).then(s.bind(s,719));return new i(t,this._engine)}loadOptions(t,i){if(!this.needsPlugin(t)&&!this.needsPlugin(i))return;let s=t.polygon;void 0===s?.load&&(t.polygon=s=new d),s.load(i?.polygon)}needsPlugin(t){return t?.polygon?.enable??(void 0!==t?.polygon?.type&&"none"!==t.polygon.type)}}}}]);
@@ -0,0 +1 @@
1
+ /*! tsParticles Polygon Mask Plugin v3.2.0 by Matteo Bruni */
package/856.min.js ADDED
@@ -0,0 +1,2 @@
1
+ /*! For license information please see 856.min.js.LICENSE.txt */
2
+ (this.webpackChunk_tsparticles_plugin_polygon_mask=this.webpackChunk_tsparticles_plugin_polygon_mask||[]).push([[856],{856:(e,t,s)=>{s.d(t,{calcClosestPointOnSegment:()=>A,drawPolygonMask:()=>c,drawPolygonMaskPath:()=>T,parsePaths:()=>E,segmentBounce:()=>S});var o=s(533);const a=2,n={min:0,max:1},_=2;function c(e,t,s){const a=(0,o.rangeColorToRgb)(s.color);if(!a)return;const n=t[0];e.beginPath(),e.moveTo(n.x,n.y);for(const s of t)e.lineTo(s.x,s.y);e.closePath(),e.strokeStyle=(0,o.getStyleFromRgb)(a),e.lineWidth=s.width,e.stroke()}function T(e,t,s,a){const n=1,_=0,c=0,T=1;e.setTransform(n,_,c,T,a.x,a.y);const E=(0,o.rangeColorToRgb)(s.color);E&&(e.strokeStyle=(0,o.getStyleFromRgb)(E,s.opacity),e.lineWidth=s.width,e.stroke(t),e.resetTransform())}function E(e,t,s){const o=[];for(const a of e){const e=a.element.pathSegList,n=e?.numberOfItems??0,_={x:0,y:0};for(let a=0;a<n;a++){const n=e?.getItem(a),c=window.SVGPathSeg;switch(n?.pathSegType){case c.PATHSEG_MOVETO_ABS:case c.PATHSEG_LINETO_ABS:case c.PATHSEG_CURVETO_CUBIC_ABS:case c.PATHSEG_CURVETO_QUADRATIC_ABS:case c.PATHSEG_ARC_ABS:case c.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS:case c.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS:{const e=n;_.x=e.x,_.y=e.y;break}case c.PATHSEG_LINETO_HORIZONTAL_ABS:_.x=n.x;break;case c.PATHSEG_LINETO_VERTICAL_ABS:_.y=n.y;break;case c.PATHSEG_LINETO_REL:case c.PATHSEG_MOVETO_REL:case c.PATHSEG_CURVETO_CUBIC_REL:case c.PATHSEG_CURVETO_QUADRATIC_REL:case c.PATHSEG_ARC_REL:case c.PATHSEG_CURVETO_CUBIC_SMOOTH_REL:case c.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL:{const e=n;_.x+=e.x,_.y+=e.y;break}case c.PATHSEG_LINETO_HORIZONTAL_REL:_.x+=n.x;break;case c.PATHSEG_LINETO_VERTICAL_REL:_.y+=n.y;break;case c.PATHSEG_UNKNOWN:case c.PATHSEG_CLOSEPATH:continue}o.push({x:_.x*t+s.x,y:_.y*t+s.y})}}return o}function A(e,t,s){const{dx:_,dy:c}=(0,o.getDistances)(s,e),{dx:T,dy:E}=(0,o.getDistances)(t,e),A=(_*T+c*E)/(T**a+E**a),S={x:e.x+T*A,y:e.y+E*A,isOnSegment:A>=n.min&&A<=n.max};return A<n.min?(S.x=e.x,S.y=e.y):A>n.max&&(S.x=t.x,S.y=t.y),S}function S(e,t,s){const{dx:a,dy:n}=(0,o.getDistances)(e,t),c=Math.atan2(n,a),T=o.Vector.create(Math.sin(c),-Math.cos(c)),E=_*(s.x*T.x+s.y*T.y);T.multTo(E),s.subFrom(T)}}}]);
@@ -0,0 +1 @@
1
+ /*! tsParticles Polygon Mask Plugin v3.2.0 by Matteo Bruni */
@@ -1,6 +1,8 @@
1
- import { deepExtend, errorPrefix, getDistance, getDistances, getRandom, isArray, isString, itemFromArray, } from "@tsparticles/engine";
2
- import { calcClosestPtOnSegment, drawPolygonMask, drawPolygonMaskPath, parsePaths, segmentBounce } from "./utils.js";
3
- const noPolygonDataLoaded = `${errorPrefix} No polygon data loaded.`, noPolygonFound = `${errorPrefix} No polygon found, you need to specify SVG url in config.`;
1
+ import { deepExtend, errorPrefix, getDistance, getDistances, getRandom, isArray, isString, itemFromArray, percentDenominator, } from "@tsparticles/engine";
2
+ const noPolygonDataLoaded = `${errorPrefix} No polygon data loaded.`, noPolygonFound = `${errorPrefix} No polygon found, you need to specify SVG url in config.`, origin = {
3
+ x: 0,
4
+ y: 0,
5
+ }, half = 0.5, double = 2;
4
6
  export class PolygonMaskInstance {
5
7
  constructor(container, engine) {
6
8
  this._checkInsidePolygon = (position) => {
@@ -11,19 +13,20 @@ export class PolygonMaskInstance {
11
13
  if (!this.raw) {
12
14
  throw new Error(noPolygonFound);
13
15
  }
14
- const canvasSize = container.canvas.size, x = position?.x ?? getRandom() * canvasSize.width, y = position?.y ?? getRandom() * canvasSize.height;
16
+ const canvasSize = container.canvas.size, x = position?.x ?? getRandom() * canvasSize.width, y = position?.y ?? getRandom() * canvasSize.height, indexOffset = 1;
15
17
  let inside = false;
16
- for (let i = 0, j = this.raw.length - 1; i < this.raw.length; j = i++) {
18
+ for (let i = 0, j = this.raw.length - indexOffset; i < this.raw.length; j = i++) {
17
19
  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;
18
20
  if (intersect) {
19
21
  inside = !inside;
20
22
  }
21
23
  }
22
- return options.type === "inside"
23
- ? inside
24
- : options.type === "outside"
25
- ? !inside
26
- : false;
24
+ if (options.type === "inside") {
25
+ return inside;
26
+ }
27
+ else {
28
+ return options.type === "outside" ? !inside : false;
29
+ }
27
30
  };
28
31
  this._createPath2D = () => {
29
32
  const container = this._container, options = container.actualOptions.polygon;
@@ -45,13 +48,14 @@ export class PolygonMaskInstance {
45
48
  else {
46
49
  delete path.path2d;
47
50
  }
48
- if (path.path2d || !this.raw) {
51
+ if (path.path2d ?? !this.raw) {
49
52
  continue;
50
53
  }
51
54
  path.path2d = new Path2D();
52
- path.path2d.moveTo(this.raw[0].x, this.raw[0].y);
55
+ const firstIndex = 0, firstPoint = this.raw[firstIndex];
56
+ path.path2d.moveTo(firstPoint.x, firstPoint.y);
53
57
  this.raw.forEach((pos, i) => {
54
- if (i > 0) {
58
+ if (i > firstIndex) {
55
59
  path.path2d?.lineTo(pos.x, pos.y);
56
60
  }
57
61
  });
@@ -63,7 +67,7 @@ export class PolygonMaskInstance {
63
67
  if (!options) {
64
68
  return;
65
69
  }
66
- const url = svgUrl || options.url, forceDownload = force ?? false;
70
+ const url = svgUrl ?? options.url, forceDownload = force ?? false;
67
71
  if (!url || (this.paths !== undefined && !forceDownload)) {
68
72
  return this.raw;
69
73
  }
@@ -71,14 +75,14 @@ export class PolygonMaskInstance {
71
75
  if (!req.ok) {
72
76
  throw new Error(`${errorPrefix} occurred during polygon mask download`);
73
77
  }
74
- return this._parseSvgPath(await req.text(), force);
78
+ return await this._parseSvgPath(await req.text(), force);
75
79
  };
76
80
  this._drawPoints = () => {
77
81
  if (!this.raw) {
78
82
  return;
79
83
  }
80
84
  for (const item of this.raw) {
81
- this._container.particles.addParticle({
85
+ void this._container.particles.addParticle({
82
86
  x: item.x,
83
87
  y: item.y,
84
88
  });
@@ -89,11 +93,11 @@ export class PolygonMaskInstance {
89
93
  if (!polygonMaskOptions) {
90
94
  return;
91
95
  }
92
- if (!this.raw || !this.raw.length || !this.paths?.length) {
96
+ if (!this.raw?.length || !this.paths?.length) {
93
97
  throw new Error(noPolygonDataLoaded);
94
98
  }
95
99
  let offset = 0, point;
96
- const totalLength = this.paths.reduce((tot, path) => tot + path.length, 0), distance = totalLength / options.particles.number.value;
100
+ const baseAccumulator = 0, totalLength = this.paths.reduce((tot, path) => tot + path.length, baseAccumulator), distance = totalLength / options.particles.number.value;
97
101
  for (const path of this.paths) {
98
102
  const pathDistance = distance * index - offset;
99
103
  if (pathDistance <= path.length) {
@@ -106,12 +110,12 @@ export class PolygonMaskInstance {
106
110
  }
107
111
  const scale = this._scale;
108
112
  return {
109
- x: (point?.x ?? 0) * scale + (this.offset?.x ?? 0),
110
- y: (point?.y ?? 0) * scale + (this.offset?.y ?? 0),
113
+ x: (point?.x ?? origin.x) * scale + (this.offset?.x ?? origin.x),
114
+ y: (point?.y ?? origin.y) * scale + (this.offset?.y ?? origin.y),
111
115
  };
112
116
  };
113
117
  this._getPointByIndex = (index) => {
114
- if (!this.raw || !this.raw.length) {
118
+ if (!this.raw?.length) {
115
119
  throw new Error(noPolygonDataLoaded);
116
120
  }
117
121
  const coords = this.raw[index % this.raw.length];
@@ -121,7 +125,7 @@ export class PolygonMaskInstance {
121
125
  };
122
126
  };
123
127
  this._getRandomPoint = () => {
124
- if (!this.raw || !this.raw.length) {
128
+ if (!this.raw?.length) {
125
129
  throw new Error(noPolygonDataLoaded);
126
130
  }
127
131
  const coords = itemFromArray(this.raw);
@@ -135,13 +139,13 @@ export class PolygonMaskInstance {
135
139
  if (!options) {
136
140
  return;
137
141
  }
138
- if (!this.raw || !this.raw.length || !this.paths?.length) {
142
+ if (!this.raw?.length || !this.paths?.length) {
139
143
  throw new Error(noPolygonDataLoaded);
140
144
  }
141
- const path = itemFromArray(this.paths), distance = Math.floor(getRandom() * path.length) + 1, point = path.element.getPointAtLength(distance), scale = this._scale;
145
+ const path = itemFromArray(this.paths), offset = 1, distance = Math.floor(getRandom() * path.length) + offset, point = path.element.getPointAtLength(distance), scale = this._scale;
142
146
  return {
143
- x: point.x * scale + (this.offset?.x || 0),
144
- y: point.y * scale + (this.offset?.y || 0),
147
+ x: point.x * scale + (this.offset?.x ?? origin.x),
148
+ y: point.y * scale + (this.offset?.y ?? origin.y),
145
149
  };
146
150
  };
147
151
  this._initRawData = async (force) => {
@@ -163,14 +167,14 @@ export class PolygonMaskInstance {
163
167
  const namespaces = 'xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"';
164
168
  svg = `<svg ${namespaces} width="${data.size.width}" height="${data.size.height}">${path}</svg>`;
165
169
  }
166
- this.raw = this._parseSvgPath(svg, force);
170
+ this.raw = await this._parseSvgPath(svg, force);
167
171
  }
168
172
  this._createPath2D();
169
173
  this._engine.dispatchEvent("polygonMaskLoaded", {
170
174
  container: this._container,
171
175
  });
172
176
  };
173
- this._parseSvgPath = (xml, force) => {
177
+ this._parseSvgPath = async (xml, force) => {
174
178
  const forceDownload = force ?? false;
175
179
  if (this.paths !== undefined && !forceDownload) {
176
180
  return this.raw;
@@ -179,7 +183,7 @@ export class PolygonMaskInstance {
179
183
  if (!options) {
180
184
  return;
181
185
  }
182
- const parser = new DOMParser(), doc = parser.parseFromString(xml, "image/svg+xml"), svg = doc.getElementsByTagName("svg")[0];
186
+ const parser = new DOMParser(), doc = parser.parseFromString(xml, "image/svg+xml"), firstIndex = 0, svg = doc.getElementsByTagName("svg")[firstIndex];
183
187
  let svgPaths = svg.getElementsByTagName("path");
184
188
  if (!svgPaths.length) {
185
189
  svgPaths = doc.getElementsByTagName("path");
@@ -202,31 +206,33 @@ export class PolygonMaskInstance {
202
206
  y: 50,
203
207
  }, canvasSize = container.canvas.size;
204
208
  this.offset = {
205
- x: (canvasSize.width * position.x) / 100 - this.dimension.width / 2,
206
- y: (canvasSize.height * position.y) / 100 - this.dimension.height / 2,
209
+ x: (canvasSize.width * position.x) / percentDenominator - this.dimension.width * half,
210
+ y: (canvasSize.height * position.y) / percentDenominator - this.dimension.height * half,
207
211
  };
212
+ const { parsePaths } = await import("./utils.js");
208
213
  return parsePaths(this.paths, scale, this.offset);
209
214
  };
210
- this._polygonBounce = (particle, _delta, direction) => {
215
+ this._polygonBounce = async (particle, delta, direction) => {
211
216
  const options = this._container.actualOptions.polygon;
212
217
  if (!this.raw || !options?.enable || direction !== "top") {
213
218
  return false;
214
219
  }
215
220
  if (options.type === "inside" || options.type === "outside") {
216
221
  let closest, dx, dy;
217
- const pos = particle.getPosition(), radius = particle.getRadius();
218
- for (let i = 0, j = this.raw.length - 1; i < this.raw.length; j = i++) {
219
- const pi = this.raw[i], pj = this.raw[j];
220
- closest = calcClosestPtOnSegment(pi, pj, pos);
222
+ const pos = particle.getPosition(), radius = particle.getRadius(), offset = 1;
223
+ for (let i = 0, j = this.raw.length - offset; i < this.raw.length; j = i++) {
224
+ const pi = this.raw[i], pj = this.raw[j], { calcClosestPointOnSegment } = await import("./utils.js");
225
+ closest = calcClosestPointOnSegment(pi, pj, pos);
221
226
  const dist = getDistances(pos, closest);
222
227
  [dx, dy] = [dist.dx, dist.dy];
223
228
  if (dist.distance < radius) {
229
+ const { segmentBounce } = await import("./utils.js");
224
230
  segmentBounce(pi, pj, particle.velocity);
225
231
  return true;
226
232
  }
227
233
  }
228
234
  if (closest && dx !== undefined && dy !== undefined && !this._checkInsidePolygon(pos)) {
229
- const factor = { x: 1, y: 1 }, diameter = radius * 2;
235
+ const factor = { x: 1, y: 1 }, diameter = radius * double, inverse = -1;
230
236
  if (pos.x >= closest.x) {
231
237
  factor.x = -1;
232
238
  }
@@ -235,15 +241,15 @@ export class PolygonMaskInstance {
235
241
  }
236
242
  particle.position.x = closest.x + diameter * factor.x;
237
243
  particle.position.y = closest.y + diameter * factor.y;
238
- particle.velocity.mult(-1);
244
+ particle.velocity.mult(inverse);
239
245
  return true;
240
246
  }
241
247
  }
242
248
  else if (options.type === "inline" && particle.initialPosition) {
243
249
  const dist = getDistance(particle.initialPosition, particle.getPosition()), { velocity } = particle;
244
250
  if (dist > this._moveRadius) {
245
- velocity.x = velocity.y / 2 - velocity.x;
246
- velocity.y = velocity.x / 2 - velocity.y;
251
+ velocity.x = velocity.y * half - velocity.x;
252
+ velocity.y = velocity.x * half - velocity.y;
247
253
  return true;
248
254
  }
249
255
  }
@@ -302,7 +308,7 @@ export class PolygonMaskInstance {
302
308
  options.type !== "inline" &&
303
309
  this._checkInsidePolygon(position));
304
310
  }
305
- draw(context) {
311
+ async draw(context) {
306
312
  if (!this.paths?.length) {
307
313
  return;
308
314
  }
@@ -321,9 +327,11 @@ export class PolygonMaskInstance {
321
327
  continue;
322
328
  }
323
329
  if (path2d && this.offset) {
330
+ const { drawPolygonMaskPath } = await import("./utils.js");
324
331
  drawPolygonMaskPath(context, path2d, polygonDraw.stroke, this.offset);
325
332
  }
326
333
  else if (rawData) {
334
+ const { drawPolygonMask } = await import("./utils.js");
327
335
  drawPolygonMask(context, rawData, polygonDraw.stroke);
328
336
  }
329
337
  }
@@ -339,12 +347,12 @@ export class PolygonMaskInstance {
339
347
  await this._initRawData();
340
348
  }
341
349
  }
342
- particleBounce(particle, delta, direction) {
343
- return this._polygonBounce(particle, delta, direction);
350
+ async particleBounce(particle, delta, direction) {
351
+ return await this._polygonBounce(particle, delta, direction);
344
352
  }
345
353
  particlePosition(position) {
346
- const options = this._container.actualOptions.polygon;
347
- if (!(options?.enable && (this.raw?.length ?? 0) > 0)) {
354
+ const options = this._container.actualOptions.polygon, defaultLength = 0;
355
+ if (!(options?.enable && (this.raw?.length ?? defaultLength) > defaultLength)) {
348
356
  return;
349
357
  }
350
358
  return deepExtend({}, position ? position : this._randomPoint());
@@ -368,10 +376,13 @@ export class PolygonMaskInstance {
368
376
  if (this.redrawTimeout) {
369
377
  clearTimeout(this.redrawTimeout);
370
378
  }
371
- this.redrawTimeout = window.setTimeout(async () => {
372
- await this._initRawData(true);
373
- await container.particles.redraw();
374
- }, 250);
379
+ const timeout = 250;
380
+ this.redrawTimeout = window.setTimeout(() => {
381
+ void (async () => {
382
+ await this._initRawData(true);
383
+ await container.particles.redraw();
384
+ })();
385
+ }, timeout);
375
386
  }
376
387
  stop() {
377
388
  delete this.raw;
@@ -0,0 +1,25 @@
1
+ import { PolygonMask } from "./Options/Classes/PolygonMask.js";
2
+ export class PolygonMaskPlugin {
3
+ constructor(engine) {
4
+ this.id = "polygonMask";
5
+ this._engine = engine;
6
+ }
7
+ async getPlugin(container) {
8
+ const { PolygonMaskInstance } = await import("./PolygonMaskInstance.js");
9
+ return new PolygonMaskInstance(container, this._engine);
10
+ }
11
+ loadOptions(options, source) {
12
+ if (!this.needsPlugin(options) && !this.needsPlugin(source)) {
13
+ return;
14
+ }
15
+ let polygonOptions = options.polygon;
16
+ if (polygonOptions?.load === undefined) {
17
+ options.polygon = polygonOptions = new PolygonMask();
18
+ }
19
+ polygonOptions.load(source?.polygon);
20
+ }
21
+ needsPlugin(options) {
22
+ return (options?.polygon?.enable ??
23
+ (options?.polygon?.type !== undefined && options.polygon.type !== "none"));
24
+ }
25
+ }
package/browser/index.js CHANGED
@@ -1,30 +1,6 @@
1
1
  import "./pathseg.js";
2
- import { PolygonMask } from "./Options/Classes/PolygonMask.js";
3
- import { PolygonMaskInstance } from "./PolygonMaskInstance.js";
4
- class PolygonMaskPlugin {
5
- constructor(engine) {
6
- this.id = "polygonMask";
7
- this._engine = engine;
8
- }
9
- getPlugin(container) {
10
- return new PolygonMaskInstance(container, this._engine);
11
- }
12
- loadOptions(options, source) {
13
- if (!this.needsPlugin(options) && !this.needsPlugin(source)) {
14
- return;
15
- }
16
- let polygonOptions = options.polygon;
17
- if (polygonOptions?.load === undefined) {
18
- options.polygon = polygonOptions = new PolygonMask();
19
- }
20
- polygonOptions.load(source?.polygon);
21
- }
22
- needsPlugin(options) {
23
- return (options?.polygon?.enable ??
24
- (options?.polygon?.type !== undefined && options.polygon.type !== "none"));
25
- }
26
- }
27
2
  export async function loadPolygonMaskPlugin(engine, refresh = true) {
3
+ const { PolygonMaskPlugin } = await import("./PolygonMaskPlugin.js");
28
4
  await engine.addPlugin(new PolygonMaskPlugin(engine), refresh);
29
5
  }
30
6
  export * from "./Enums/PolygonMaskInlineArrangement.js";
package/browser/utils.js CHANGED
@@ -1,11 +1,16 @@
1
1
  import { Vector, getDistances, getStyleFromRgb, rangeColorToRgb } from "@tsparticles/engine";
2
+ const squareExp = 2, inSegmentRange = {
3
+ min: 0,
4
+ max: 1,
5
+ }, double = 2;
2
6
  export function drawPolygonMask(context, rawData, stroke) {
3
7
  const color = rangeColorToRgb(stroke.color);
4
8
  if (!color) {
5
9
  return;
6
10
  }
11
+ const firstIndex = 0, firstItem = rawData[firstIndex];
7
12
  context.beginPath();
8
- context.moveTo(rawData[0].x, rawData[0].y);
13
+ context.moveTo(firstItem.x, firstItem.y);
9
14
  for (const item of rawData) {
10
15
  context.lineTo(item.x, item.y);
11
16
  }
@@ -15,7 +20,13 @@ export function drawPolygonMask(context, rawData, stroke) {
15
20
  context.stroke();
16
21
  }
17
22
  export function drawPolygonMaskPath(context, path, stroke, position) {
18
- context.setTransform(1, 0, 0, 1, position.x, position.y);
23
+ const defaultTransform = {
24
+ a: 1,
25
+ b: 0,
26
+ c: 0,
27
+ d: 1,
28
+ };
29
+ context.setTransform(defaultTransform.a, defaultTransform.b, defaultTransform.c, defaultTransform.d, position.x, position.y);
19
30
  const color = rangeColorToRgb(stroke.color);
20
31
  if (!color) {
21
32
  return;
@@ -23,12 +34,12 @@ export function drawPolygonMaskPath(context, path, stroke, position) {
23
34
  context.strokeStyle = getStyleFromRgb(color, stroke.opacity);
24
35
  context.lineWidth = stroke.width;
25
36
  context.stroke(path);
26
- context.setTransform(1, 0, 0, 1, 0, 0);
37
+ context.resetTransform();
27
38
  }
28
39
  export function parsePaths(paths, scale, offset) {
29
- const res = [];
40
+ const res = [], defaultCount = 0;
30
41
  for (const path of paths) {
31
- const segments = path.element.pathSegList, len = segments?.numberOfItems ?? 0, p = {
42
+ const segments = path.element.pathSegList, len = segments?.numberOfItems ?? defaultCount, p = {
32
43
  x: 0,
33
44
  y: 0,
34
45
  };
@@ -83,24 +94,24 @@ export function parsePaths(paths, scale, offset) {
83
94
  }
84
95
  return res;
85
96
  }
86
- export function calcClosestPtOnSegment(s1, s2, pos) {
87
- const { dx: dx1, dy: dy1 } = getDistances(pos, s1), { dx: dx2, dy: dy2 } = getDistances(s2, s1), t = (dx1 * dx2 + dy1 * dy2) / (dx2 ** 2 + dy2 ** 2), res = {
97
+ export function calcClosestPointOnSegment(s1, s2, pos) {
98
+ const { dx: dx1, dy: dy1 } = getDistances(pos, s1), { dx: dx2, dy: dy2 } = getDistances(s2, s1), t = (dx1 * dx2 + dy1 * dy2) / (dx2 ** squareExp + dy2 ** squareExp), res = {
88
99
  x: s1.x + dx2 * t,
89
100
  y: s1.y + dy2 * t,
90
- isOnSegment: t >= 0 && t <= 1,
101
+ isOnSegment: t >= inSegmentRange.min && t <= inSegmentRange.max,
91
102
  };
92
- if (t < 0) {
103
+ if (t < inSegmentRange.min) {
93
104
  res.x = s1.x;
94
105
  res.y = s1.y;
95
106
  }
96
- else if (t > 1) {
107
+ else if (t > inSegmentRange.max) {
97
108
  res.x = s2.x;
98
109
  res.y = s2.y;
99
110
  }
100
111
  return res;
101
112
  }
102
113
  export function segmentBounce(start, stop, velocity) {
103
- const { dx, dy } = getDistances(start, stop), wallAngle = Math.atan2(dy, dx), wallNormal = Vector.create(Math.sin(wallAngle), -Math.cos(wallAngle)), d = 2 * (velocity.x * wallNormal.x + velocity.y * wallNormal.y);
114
+ const { dx, dy } = getDistances(start, stop), wallAngle = Math.atan2(dy, dx), wallNormal = Vector.create(Math.sin(wallAngle), -Math.cos(wallAngle)), d = double * (velocity.x * wallNormal.x + velocity.y * wallNormal.y);
104
115
  wallNormal.multTo(d);
105
116
  velocity.subFrom(wallNormal);
106
117
  }