custom-pixi-particles 7.1.1 → 7.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/dist/index.d.ts +15 -0
- package/dist/index.js +45 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/Model.d.ts +32 -0
- package/dist/lib/Model.js +44 -0
- package/dist/lib/Model.js.map +1 -0
- package/dist/lib/Particle.d.ts +234 -0
- package/dist/lib/Particle.js +201 -0
- package/dist/lib/Particle.js.map +1 -0
- package/dist/lib/ParticlePool.d.ts +37 -0
- package/dist/lib/ParticlePool.js +56 -0
- package/dist/lib/ParticlePool.js.map +1 -0
- package/dist/lib/behaviour/AngularVelocityBehaviour.d.ts +61 -0
- package/dist/lib/behaviour/AngularVelocityBehaviour.js +118 -0
- package/dist/lib/behaviour/AngularVelocityBehaviour.js.map +1 -0
- package/dist/lib/behaviour/AttractionRepulsionBehaviour.d.ts +42 -0
- package/dist/lib/behaviour/AttractionRepulsionBehaviour.js +78 -0
- package/dist/lib/behaviour/AttractionRepulsionBehaviour.js.map +1 -0
- package/dist/lib/behaviour/Behaviour.d.ts +33 -0
- package/dist/lib/behaviour/Behaviour.js +45 -0
- package/dist/lib/behaviour/Behaviour.js.map +1 -0
- package/dist/lib/behaviour/BehaviourNames.d.ts +22 -0
- package/dist/lib/behaviour/BehaviourNames.js +23 -0
- package/dist/lib/behaviour/BehaviourNames.js.map +1 -0
- package/dist/lib/behaviour/CollisionBehaviour.d.ts +81 -0
- package/dist/lib/behaviour/CollisionBehaviour.js +136 -0
- package/dist/lib/behaviour/CollisionBehaviour.js.map +1 -0
- package/dist/lib/behaviour/ColorBehaviour.d.ts +67 -0
- package/dist/lib/behaviour/ColorBehaviour.js +209 -0
- package/dist/lib/behaviour/ColorBehaviour.js.map +1 -0
- package/dist/lib/behaviour/EmitDirectionBehaviour.d.ts +52 -0
- package/dist/lib/behaviour/EmitDirectionBehaviour.js +105 -0
- package/dist/lib/behaviour/EmitDirectionBehaviour.js.map +1 -0
- package/dist/lib/behaviour/EmitterBehaviours.d.ts +69 -0
- package/dist/lib/behaviour/EmitterBehaviours.js +116 -0
- package/dist/lib/behaviour/EmitterBehaviours.js.map +1 -0
- package/dist/lib/behaviour/ForceFieldsBehaviour.d.ts +53 -0
- package/dist/lib/behaviour/ForceFieldsBehaviour.js +80 -0
- package/dist/lib/behaviour/ForceFieldsBehaviour.js.map +1 -0
- package/dist/lib/behaviour/GroupingBehaviour.d.ts +57 -0
- package/dist/lib/behaviour/GroupingBehaviour.js +143 -0
- package/dist/lib/behaviour/GroupingBehaviour.js.map +1 -0
- package/dist/lib/behaviour/LifeBehaviour.d.ts +47 -0
- package/dist/lib/behaviour/LifeBehaviour.js +65 -0
- package/dist/lib/behaviour/LifeBehaviour.js.map +1 -0
- package/dist/lib/behaviour/LightEffectBehaviour.d.ts +59 -0
- package/dist/lib/behaviour/LightEffectBehaviour.js +94 -0
- package/dist/lib/behaviour/LightEffectBehaviour.js.map +1 -0
- package/dist/lib/behaviour/NoiseBasedMotionBehaviour.d.ts +28 -0
- package/dist/lib/behaviour/NoiseBasedMotionBehaviour.js +50 -0
- package/dist/lib/behaviour/NoiseBasedMotionBehaviour.js.map +1 -0
- package/dist/lib/behaviour/PositionBehaviour.d.ts +164 -0
- package/dist/lib/behaviour/PositionBehaviour.js +484 -0
- package/dist/lib/behaviour/PositionBehaviour.js.map +1 -0
- package/dist/lib/behaviour/RotationBehaviour.d.ts +59 -0
- package/dist/lib/behaviour/RotationBehaviour.js +100 -0
- package/dist/lib/behaviour/RotationBehaviour.js.map +1 -0
- package/dist/lib/behaviour/SizeBehaviour.d.ts +61 -0
- package/dist/lib/behaviour/SizeBehaviour.js +156 -0
- package/dist/lib/behaviour/SizeBehaviour.js.map +1 -0
- package/dist/lib/behaviour/SoundReactiveBehaviour.d.ts +58 -0
- package/dist/lib/behaviour/SoundReactiveBehaviour.js +132 -0
- package/dist/lib/behaviour/SoundReactiveBehaviour.js.map +1 -0
- package/dist/lib/behaviour/SpawnBehaviour.d.ts +89 -0
- package/dist/lib/behaviour/SpawnBehaviour.js +705 -0
- package/dist/lib/behaviour/SpawnBehaviour.js.map +1 -0
- package/dist/lib/behaviour/StretchBehaviour.d.ts +30 -0
- package/dist/lib/behaviour/StretchBehaviour.js +59 -0
- package/dist/lib/behaviour/StretchBehaviour.js.map +1 -0
- package/dist/lib/behaviour/TemperatureBehaviour.d.ts +47 -0
- package/dist/lib/behaviour/TemperatureBehaviour.js +64 -0
- package/dist/lib/behaviour/TemperatureBehaviour.js.map +1 -0
- package/dist/lib/behaviour/TimelineBehaviour.d.ts +73 -0
- package/dist/lib/behaviour/TimelineBehaviour.js +100 -0
- package/dist/lib/behaviour/TimelineBehaviour.js.map +1 -0
- package/dist/lib/behaviour/TurbulenceBehaviour.d.ts +73 -0
- package/dist/lib/behaviour/TurbulenceBehaviour.js +133 -0
- package/dist/lib/behaviour/TurbulenceBehaviour.js.map +1 -0
- package/dist/lib/behaviour/index.d.ts +23 -0
- package/dist/lib/behaviour/index.js +24 -0
- package/dist/lib/behaviour/index.js.map +1 -0
- package/dist/lib/customPixiParticlesSettingsInterface.d.ts +19 -0
- package/dist/lib/customPixiParticlesSettingsInterface.js +2 -0
- package/dist/lib/customPixiParticlesSettingsInterface.js.map +1 -0
- package/dist/lib/emission/AbstractEmission.d.ts +30 -0
- package/dist/lib/emission/AbstractEmission.js +39 -0
- package/dist/lib/emission/AbstractEmission.js.map +1 -0
- package/dist/lib/emission/EmissionTypes.d.ts +6 -0
- package/dist/lib/emission/EmissionTypes.js +6 -0
- package/dist/lib/emission/EmissionTypes.js.map +1 -0
- package/dist/lib/emission/RandomEmission.d.ts +44 -0
- package/dist/lib/emission/RandomEmission.js +66 -0
- package/dist/lib/emission/RandomEmission.js.map +1 -0
- package/dist/lib/emission/StandardEmission.d.ts +62 -0
- package/dist/lib/emission/StandardEmission.js +87 -0
- package/dist/lib/emission/StandardEmission.js.map +1 -0
- package/dist/lib/emission/UniformEmission.d.ts +56 -0
- package/dist/lib/emission/UniformEmission.js +83 -0
- package/dist/lib/emission/UniformEmission.js.map +1 -0
- package/dist/lib/emission/index.d.ts +6 -0
- package/dist/lib/emission/index.js +7 -0
- package/dist/lib/emission/index.js.map +1 -0
- package/dist/lib/emitter/Duration.d.ts +27 -0
- package/dist/lib/emitter/Duration.js +41 -0
- package/dist/lib/emitter/Duration.js.map +1 -0
- package/dist/lib/emitter/Emitter.d.ts +116 -0
- package/dist/lib/emitter/Emitter.js +240 -0
- package/dist/lib/emitter/Emitter.js.map +1 -0
- package/dist/lib/emitter/index.d.ts +3 -0
- package/dist/lib/emitter/index.js +4 -0
- package/dist/lib/emitter/index.js.map +1 -0
- package/dist/lib/index.d.ts +20 -0
- package/dist/lib/index.js +21 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/parser/BehaviourParser.d.ts +21 -0
- package/dist/lib/parser/BehaviourParser.js +72 -0
- package/dist/lib/parser/BehaviourParser.js.map +1 -0
- package/dist/lib/parser/CompatibilityHelper.d.ts +12 -0
- package/dist/lib/parser/CompatibilityHelper.js +19 -0
- package/dist/lib/parser/CompatibilityHelper.js.map +1 -0
- package/dist/lib/parser/EmitControllerParser.d.ts +27 -0
- package/dist/lib/parser/EmitControllerParser.js +39 -0
- package/dist/lib/parser/EmitControllerParser.js.map +1 -0
- package/dist/lib/parser/EmitterParser.d.ts +67 -0
- package/dist/lib/parser/EmitterParser.js +168 -0
- package/dist/lib/parser/EmitterParser.js.map +1 -0
- package/dist/lib/parser/index.d.ts +5 -0
- package/dist/lib/parser/index.js +6 -0
- package/dist/lib/parser/index.js.map +1 -0
- package/dist/lib/pixi/Renderer.d.ts +125 -0
- package/dist/lib/pixi/Renderer.js +566 -0
- package/dist/lib/pixi/Renderer.js.map +1 -0
- package/dist/lib/pixi/TestRenderer.d.ts +125 -0
- package/dist/lib/pixi/TestRenderer.js +560 -0
- package/dist/lib/pixi/TestRenderer.js.map +1 -0
- package/dist/lib/util/Color.d.ts +133 -0
- package/dist/lib/util/Color.js +173 -0
- package/dist/lib/util/Color.js.map +1 -0
- package/dist/lib/util/List.d.ts +49 -0
- package/dist/lib/util/List.js +87 -0
- package/dist/lib/util/List.js.map +1 -0
- package/dist/lib/util/MinMax.d.ts +34 -0
- package/dist/lib/util/MinMax.js +46 -0
- package/dist/lib/util/MinMax.js.map +1 -0
- package/dist/lib/util/Point.d.ts +34 -0
- package/dist/lib/util/Point.js +46 -0
- package/dist/lib/util/Point.js.map +1 -0
- package/dist/lib/util/Random.d.ts +24 -0
- package/dist/lib/util/Random.js +30 -0
- package/dist/lib/util/Random.js.map +1 -0
- package/dist/lib/util/ThereBack.d.ts +14 -0
- package/dist/lib/util/ThereBack.js +23 -0
- package/dist/lib/util/ThereBack.js.map +1 -0
- package/dist/lib/util/index.d.ts +5 -0
- package/dist/lib/util/index.js +6 -0
- package/dist/lib/util/index.js.map +1 -0
- package/dist/lib/util/maths.d.ts +10 -0
- package/dist/lib/util/maths.js +13 -0
- package/dist/lib/util/maths.js.map +1 -0
- package/dist/lib/util/turbulencePool.d.ts +4 -0
- package/dist/lib/util/turbulencePool.js +7 -0
- package/dist/lib/util/turbulencePool.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,705 @@
|
|
|
1
|
+
import { Point } from '../util';
|
|
2
|
+
import { Behaviour, BehaviourNames } from './index';
|
|
3
|
+
let canvas = null;
|
|
4
|
+
let imageData = null;
|
|
5
|
+
let particleCount = 0;
|
|
6
|
+
let pixelPositions = [];
|
|
7
|
+
export default class SpawnBehaviour extends Behaviour {
|
|
8
|
+
constructor() {
|
|
9
|
+
super(...arguments);
|
|
10
|
+
this.enabled = true;
|
|
11
|
+
this.priority = 0;
|
|
12
|
+
this.overOne = false;
|
|
13
|
+
this.trailProgress = 0; // Progress along the trail (0-1)
|
|
14
|
+
this.trailingEnabled = false; // Enable trailing
|
|
15
|
+
this.spawnAlongTrail = false; // Spawn particles along the entire trail
|
|
16
|
+
this.trailSpeed = 1; // Speed of the trail
|
|
17
|
+
this.trailRepeat = true; // Loop the trail
|
|
18
|
+
this.trailStart = 0; // Start the trail at 20% of its path
|
|
19
|
+
this.currentProgress = 0;
|
|
20
|
+
this.customPoints = [
|
|
21
|
+
{
|
|
22
|
+
spawnType: 'Rectangle',
|
|
23
|
+
word: 'Hello',
|
|
24
|
+
fontSize: 50,
|
|
25
|
+
fontSpacing: 5,
|
|
26
|
+
particleDensity: 1,
|
|
27
|
+
fontMaxWidth: 1334,
|
|
28
|
+
fontMaxHeight: 750,
|
|
29
|
+
textAlign: 'center',
|
|
30
|
+
textBaseline: 'middle',
|
|
31
|
+
radius: 100,
|
|
32
|
+
radiusX: 100,
|
|
33
|
+
radiusY: 100,
|
|
34
|
+
starPoints: 5,
|
|
35
|
+
rows: 10,
|
|
36
|
+
columns: 10,
|
|
37
|
+
cellSize: 20,
|
|
38
|
+
center: { x: 0, y: 0, z: 0 },
|
|
39
|
+
apex: { x: 0, y: 0, z: 0 },
|
|
40
|
+
spread: 360,
|
|
41
|
+
baseRadius: 100,
|
|
42
|
+
coneDirection: 1,
|
|
43
|
+
height: 100,
|
|
44
|
+
coneAngle: 45,
|
|
45
|
+
position: { x: 0, y: 0 },
|
|
46
|
+
positionVariance: { x: 0, y: 0 },
|
|
47
|
+
perspective: 0, // Distance for perspective
|
|
48
|
+
maxZ: 0, // Maximum z distance for effects
|
|
49
|
+
frequency: { x: 3, y: 2 },
|
|
50
|
+
start: { x: 10, y: 10 },
|
|
51
|
+
end: { x: 20, y: 20 },
|
|
52
|
+
control1: { x: 0, y: 0 },
|
|
53
|
+
control2: { x: 0, y: 0 },
|
|
54
|
+
delta: 1,
|
|
55
|
+
pitch: 50, // Vertical distance between consecutive loops
|
|
56
|
+
turns: 5, // Number of turns in the helix
|
|
57
|
+
pathPoints: [],
|
|
58
|
+
},
|
|
59
|
+
];
|
|
60
|
+
this.lastWordSettings = {};
|
|
61
|
+
/**
|
|
62
|
+
* Initialize particles for each custom point.
|
|
63
|
+
* @param {Particle} particle - The particle to initialize.
|
|
64
|
+
*/
|
|
65
|
+
this.init = (particle) => {
|
|
66
|
+
if (this.customPoints.length === 0)
|
|
67
|
+
return;
|
|
68
|
+
// Choose a random custom point
|
|
69
|
+
const point = this.customPoints[Math.floor(Math.random() * this.customPoints.length)];
|
|
70
|
+
if (this.trailingEnabled) {
|
|
71
|
+
const { positions, probabilities } = this.spawnAlongTrail
|
|
72
|
+
? this.calculateTrailRangePositions(point)
|
|
73
|
+
: { positions: [this.calculateTrailPosition(point)], probabilities: [1] };
|
|
74
|
+
// Use weighted random selection for position
|
|
75
|
+
const positionIndex = this.weightedRandomIndex(probabilities);
|
|
76
|
+
const position = positions[positionIndex];
|
|
77
|
+
particle.movement.x = this.calculate(point.position.x, point.positionVariance.x) + position.x;
|
|
78
|
+
particle.movement.y = this.calculate(point.position.y, point.positionVariance.y) + position.y;
|
|
79
|
+
particle.z = Math.random() * point.maxZ;
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
// Normal spawning logic
|
|
83
|
+
this.spawnParticleAtPoint(particle, point);
|
|
84
|
+
}
|
|
85
|
+
if (point.perspective && point.maxZ) {
|
|
86
|
+
// Apply perspective scaling based on z
|
|
87
|
+
const scale = point.perspective / (point.perspective + particle.z);
|
|
88
|
+
particle.movement.x *= scale;
|
|
89
|
+
particle.movement.y *= scale;
|
|
90
|
+
// Adjust particle opacity based on z
|
|
91
|
+
particle.superColorAlphaEnd = 1 - particle.z / point.maxZ;
|
|
92
|
+
particle.size.x = 1 - particle.z / point.maxZ;
|
|
93
|
+
particle.size.y = 1 - particle.z / point.maxZ;
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
this.apply = () => {
|
|
97
|
+
// do nothing
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* Calculate trail positions along the range from trailStart to currentProgress.
|
|
101
|
+
* @param {Object} point - The custom point configuration.
|
|
102
|
+
* @returns {Point[]} List of positions along the trail.
|
|
103
|
+
*/
|
|
104
|
+
this.calculateTrailRangePositions = (point) => {
|
|
105
|
+
const positions = [];
|
|
106
|
+
const segments = 20; // Increase segments for finer granularity
|
|
107
|
+
const trailStart = this.trailStart || 0;
|
|
108
|
+
const weights = []; // Store probabilities for positions
|
|
109
|
+
const startProgress = Math.max(trailStart, 0);
|
|
110
|
+
const endProgress = Math.min(this.currentProgress, 1);
|
|
111
|
+
for (let i = startProgress; i <= endProgress; i += (endProgress - startProgress) / segments) {
|
|
112
|
+
const position = this.calculateTrailPosition(point, i);
|
|
113
|
+
positions.push(position);
|
|
114
|
+
// Assign a weight inversely proportional to distance from trail center
|
|
115
|
+
const weightFactor = 4; // Higher values give steeper drops
|
|
116
|
+
const distanceToTrail = Math.abs(i - this.currentProgress);
|
|
117
|
+
weights.push(Math.exp(-distanceToTrail * weightFactor));
|
|
118
|
+
}
|
|
119
|
+
// Normalize weights to create probabilities
|
|
120
|
+
const totalWeight = weights.reduce((sum, w) => sum + w, 0);
|
|
121
|
+
const probabilities = weights.map((w) => w / totalWeight);
|
|
122
|
+
return { positions, probabilities };
|
|
123
|
+
};
|
|
124
|
+
/**
|
|
125
|
+
* Spawn particle at the specified point configuration.
|
|
126
|
+
* @param {Particle} particle - The particle to be initialized.
|
|
127
|
+
* @param {Object} point - The custom point configuration.
|
|
128
|
+
*/
|
|
129
|
+
this.spawnParticleAtPoint = (particle, point) => {
|
|
130
|
+
// Assign particle z-coordinate within the max range
|
|
131
|
+
// particle.z = Math.random() * point.maxZ
|
|
132
|
+
if (point.spawnType === 'Word' &&
|
|
133
|
+
(this.lastWordSettings.word !== point.word ||
|
|
134
|
+
point.fontSize !== this.lastWordSettings.fontSize ||
|
|
135
|
+
point.fontSpacing !== this.lastWordSettings.fontSpacing ||
|
|
136
|
+
point.particleDensity !== this.lastWordSettings.particleDensity ||
|
|
137
|
+
point.textAlign !== this.lastWordSettings.textAlign ||
|
|
138
|
+
point.textBaseline !== this.lastWordSettings.textBaseline ||
|
|
139
|
+
point.fontMaxWidth !== this.lastWordSettings.fontMaxWidth ||
|
|
140
|
+
point.fontMaxHeight !== this.lastWordSettings.fontMaxHeight)) {
|
|
141
|
+
this.lastWordSettings = {
|
|
142
|
+
word: point.word,
|
|
143
|
+
fontSize: point.fontSize,
|
|
144
|
+
fontSpacing: point.fontSpacing,
|
|
145
|
+
particleDensity: point.particleDensity,
|
|
146
|
+
textAlign: point.textAlign,
|
|
147
|
+
textBaseline: point.textBaseline,
|
|
148
|
+
fontMaxWidth: point.fontMaxWidth,
|
|
149
|
+
fontMaxHeight: point.fontMaxHeight,
|
|
150
|
+
};
|
|
151
|
+
this.calculateCtx(point);
|
|
152
|
+
particle.reset(); // Reset the particle's position and movement
|
|
153
|
+
}
|
|
154
|
+
// Assign a random z-coordinate within a range
|
|
155
|
+
particle.z = Math.random() * point.maxZ;
|
|
156
|
+
if (point.spawnType === 'Rectangle') {
|
|
157
|
+
particle.movement.x = this.calculate(point.position.x, point.positionVariance.x);
|
|
158
|
+
particle.movement.y = this.calculate(point.position.y, point.positionVariance.y);
|
|
159
|
+
}
|
|
160
|
+
else if (point.spawnType === 'Ring') {
|
|
161
|
+
const angle = Math.random() * Math.PI * 2;
|
|
162
|
+
particle.movement.x = this.calculate(point.position.x, point.positionVariance.x) + Math.cos(angle) * point.radius;
|
|
163
|
+
particle.movement.y = this.calculate(point.position.y, point.positionVariance.y) + Math.sin(angle) * point.radius;
|
|
164
|
+
}
|
|
165
|
+
else if (point.spawnType === 'Star') {
|
|
166
|
+
const points = point.starPoints; // Configurable number of star points
|
|
167
|
+
const angle = (Math.PI * 2 * particle.uid) / points;
|
|
168
|
+
const radius = particle.uid % 2 === 0 ? point.radius : point.radius / 2;
|
|
169
|
+
particle.movement.x = this.calculate(point.position.x, point.positionVariance.x) + Math.cos(angle) * radius;
|
|
170
|
+
particle.movement.y = this.calculate(point.position.y, point.positionVariance.y) + Math.sin(angle) * radius;
|
|
171
|
+
}
|
|
172
|
+
else if (point.spawnType === 'FrameRectangle') {
|
|
173
|
+
const w = point.radiusX;
|
|
174
|
+
const h = point.radiusY;
|
|
175
|
+
if (Math.random() < w / (w + h)) {
|
|
176
|
+
particle.movement.x = Math.random() * w + particle.movement.x;
|
|
177
|
+
particle.movement.y = Math.random() < 0.5 ? particle.movement.y : particle.movement.y + h - 1;
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
particle.movement.y = Math.random() * h + particle.movement.y;
|
|
181
|
+
particle.movement.x = Math.random() < 0.5 ? particle.movement.x : particle.movement.x + w - 1;
|
|
182
|
+
}
|
|
183
|
+
particle.movement.x += this.calculate(point.position.x, point.positionVariance.x);
|
|
184
|
+
particle.movement.y += this.calculate(point.position.y, point.positionVariance.y);
|
|
185
|
+
}
|
|
186
|
+
else if (point.spawnType === 'Frame') {
|
|
187
|
+
const w = point.radius;
|
|
188
|
+
const h = point.radius;
|
|
189
|
+
if (Math.random() < w / (w + h)) {
|
|
190
|
+
particle.movement.x = Math.random() * w + particle.movement.x;
|
|
191
|
+
particle.movement.y = Math.random() < 0.5 ? particle.movement.y : particle.movement.y + h - 1;
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
particle.movement.y = Math.random() * h + particle.movement.y;
|
|
195
|
+
particle.movement.x = Math.random() < 0.5 ? particle.movement.x : particle.movement.x + w - 1;
|
|
196
|
+
}
|
|
197
|
+
particle.movement.x += this.calculate(point.position.x, point.positionVariance.x);
|
|
198
|
+
particle.movement.y += this.calculate(point.position.y, point.positionVariance.y);
|
|
199
|
+
}
|
|
200
|
+
else if (point.spawnType === 'Sphere') {
|
|
201
|
+
const phi = Math.random() * Math.PI * 2; // Random azimuthal angle
|
|
202
|
+
const theta = Math.random() * (point.spread / 180) * Math.PI; // Random polar angle
|
|
203
|
+
particle.movement.x =
|
|
204
|
+
this.calculate(point.position.x, point.positionVariance.x) +
|
|
205
|
+
point.center.x +
|
|
206
|
+
point.radius * Math.sin(theta) * Math.cos(phi);
|
|
207
|
+
particle.movement.y =
|
|
208
|
+
this.calculate(point.position.y, point.positionVariance.y) +
|
|
209
|
+
point.center.y +
|
|
210
|
+
point.radius * Math.sin(theta) * Math.sin(phi);
|
|
211
|
+
particle.z = point.center.z + point.radius * Math.cos(theta);
|
|
212
|
+
}
|
|
213
|
+
else if (point.spawnType === 'Cone') {
|
|
214
|
+
const angle = (Math.random() * point.coneAngle - point.coneAngle / 2) * (Math.PI / 180);
|
|
215
|
+
const distance = Math.random() * point.baseRadius; // Random distance from apex
|
|
216
|
+
const localX = Math.cos(angle) * distance; // Local x position within cone
|
|
217
|
+
const localY = Math.sin(angle) * distance; // Local y position within cone
|
|
218
|
+
// Convert coneDirection to radians
|
|
219
|
+
const coneDirectionRad = (point.coneDirection || 0) * (Math.PI / 180);
|
|
220
|
+
// Apply rotation to align the cone to the specified direction
|
|
221
|
+
particle.movement.x =
|
|
222
|
+
this.calculate(point.position.x, point.positionVariance.x) +
|
|
223
|
+
point.apex.x +
|
|
224
|
+
Math.cos(coneDirectionRad) * localX -
|
|
225
|
+
Math.sin(coneDirectionRad) * localY;
|
|
226
|
+
particle.movement.y =
|
|
227
|
+
this.calculate(point.position.y, point.positionVariance.y) +
|
|
228
|
+
point.apex.y +
|
|
229
|
+
Math.sin(coneDirectionRad) * localX +
|
|
230
|
+
Math.cos(coneDirectionRad) * localY;
|
|
231
|
+
particle.z = point.apex.z + Math.random() * point.height;
|
|
232
|
+
}
|
|
233
|
+
else if (point.spawnType === 'Grid') {
|
|
234
|
+
const row = Math.floor(Math.random() * point.rows);
|
|
235
|
+
const column = Math.floor(Math.random() * point.columns);
|
|
236
|
+
particle.movement.x = this.calculate(point.position.x, point.positionVariance.x) + column * point.cellSize;
|
|
237
|
+
particle.movement.y = this.calculate(point.position.y, point.positionVariance.y) + row * point.cellSize;
|
|
238
|
+
}
|
|
239
|
+
else if (point.spawnType === 'Word') {
|
|
240
|
+
if (particleCount > 0 && pixelPositions.length > 0) {
|
|
241
|
+
const selectedPixel = pixelPositions[Math.floor(Math.random() * particleCount)];
|
|
242
|
+
particle.movement.x = point.position.x + selectedPixel.x - canvas.width / 2;
|
|
243
|
+
particle.movement.y = point.position.y + selectedPixel.y - canvas.height / 2;
|
|
244
|
+
// Add a bit of random jitter to make the word more dynamic
|
|
245
|
+
particle.movement.x += Math.random() * point.positionVariance.x - point.positionVariance.x / 2;
|
|
246
|
+
particle.movement.y += Math.random() * point.positionVariance.y - point.positionVariance.y / 2;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
else if (point.spawnType === 'Lissajous') {
|
|
250
|
+
const a = point.frequency.x; // Frequency in x-axis
|
|
251
|
+
const b = point.frequency.y; // Frequency in y-axis
|
|
252
|
+
const delta = point.delta || Math.PI / 2; // Phase difference
|
|
253
|
+
const t = particle.uid * 0.1;
|
|
254
|
+
particle.movement.x =
|
|
255
|
+
this.calculate(point.position.x, point.positionVariance.x) + Math.sin(a * t + delta) * point.radius;
|
|
256
|
+
particle.movement.y = this.calculate(point.position.y, point.positionVariance.y) + Math.sin(b * t) * point.radius;
|
|
257
|
+
}
|
|
258
|
+
else if (point.spawnType === 'Bezier') {
|
|
259
|
+
const t = Math.random(); // Progress along the curve
|
|
260
|
+
const cx1 = point.control1.x;
|
|
261
|
+
const cy1 = point.control1.y;
|
|
262
|
+
const cx2 = point.control2.x;
|
|
263
|
+
const cy2 = point.control2.y;
|
|
264
|
+
const x = this.calculate(point.position.x, point.positionVariance.x) +
|
|
265
|
+
Math.pow((1 - t), 3) * point.start.x +
|
|
266
|
+
3 * Math.pow((1 - t), 2) * t * cx1 +
|
|
267
|
+
3 * (1 - t) * Math.pow(t, 2) * cx2 +
|
|
268
|
+
Math.pow(t, 3) * point.end.x;
|
|
269
|
+
const y = this.calculate(point.position.x, point.positionVariance.x) +
|
|
270
|
+
Math.pow((1 - t), 3) * point.start.y +
|
|
271
|
+
3 * Math.pow((1 - t), 2) * t * cy1 +
|
|
272
|
+
3 * (1 - t) * Math.pow(t, 2) * cy2 +
|
|
273
|
+
Math.pow(t, 3) * point.end.y;
|
|
274
|
+
particle.movement.x = x;
|
|
275
|
+
particle.movement.y = y;
|
|
276
|
+
}
|
|
277
|
+
else if (point.spawnType === 'Heart') {
|
|
278
|
+
// Heart shape formula: (x^2 + y^2 - 1)^3 - x^2 * y^3 = 0
|
|
279
|
+
const t = Math.random() * 2 * Math.PI; // Parametric angle
|
|
280
|
+
const scale = point.radius || 100; // Scale based on radius
|
|
281
|
+
const x = scale * 16 * Math.pow(Math.sin(t), 3); // Parametric x-coordinate
|
|
282
|
+
const y = -scale * (13 * Math.cos(t) - 5 * Math.cos(2 * t) - 2 * Math.cos(3 * t) - Math.cos(4 * t));
|
|
283
|
+
particle.movement.x = this.calculate(point.position.x, point.positionVariance.x) + x;
|
|
284
|
+
particle.movement.y = this.calculate(point.position.y, point.positionVariance.y) + y;
|
|
285
|
+
}
|
|
286
|
+
else if (point.spawnType === 'Helix') {
|
|
287
|
+
const turns = point.turns || 5; // Default number of turns
|
|
288
|
+
const pitch = point.pitch || 50; // Default pitch
|
|
289
|
+
const radius = point.radius || 100; // Default radius
|
|
290
|
+
const height = point.height || turns * pitch; // Default height if not specified
|
|
291
|
+
const t = Math.random() * turns * Math.PI * 2; // Angle along the helix
|
|
292
|
+
const z = Math.random() * height; // Random height within the helix
|
|
293
|
+
const x = radius * Math.cos(t); // X-coordinate based on angle
|
|
294
|
+
const y = radius * Math.sin(t); // Y-coordinate based on angle
|
|
295
|
+
const adjustedZ = z % pitch; // Adjust z for particles within a pitch interval
|
|
296
|
+
particle.movement.x = this.calculate(point.position.x, point.positionVariance.x) + x;
|
|
297
|
+
particle.movement.y = this.calculate(point.position.y, point.positionVariance.y) + y;
|
|
298
|
+
particle.z = point.position.z + adjustedZ; // Assign Z-coordinate to the particle
|
|
299
|
+
}
|
|
300
|
+
else if (point.spawnType === 'Spring') {
|
|
301
|
+
const turns = point.turns || 5; // Number of loops in the spring
|
|
302
|
+
const pitch = point.pitch || 50; // Distance between consecutive loops
|
|
303
|
+
const radius = point.radius || 100; // Radius of the spring
|
|
304
|
+
// Generate random position along the spring
|
|
305
|
+
const t = Math.random() * turns * Math.PI * 2; // Angle in radians along the spring
|
|
306
|
+
const z = (t / (Math.PI * 2)) * pitch; // Z position increases linearly with the angle
|
|
307
|
+
// Compute 3D positions
|
|
308
|
+
const x = Math.cos(t) * radius; // X-coordinate on the circle
|
|
309
|
+
const y = Math.sin(t) * radius; // Y-coordinate on the circle
|
|
310
|
+
// Apply perspective scaling if enabled
|
|
311
|
+
if (point.perspective > 0 && point.maxZ > 0) {
|
|
312
|
+
const scale = point.perspective / (point.perspective + z); // Perspective scaling
|
|
313
|
+
// Apply perspective scaling to coordinates
|
|
314
|
+
particle.movement.x = (this.calculate(point.position.x, point.positionVariance.x) + x) * scale;
|
|
315
|
+
particle.movement.y = (this.calculate(point.position.y, point.positionVariance.y) + y) * scale;
|
|
316
|
+
// Adjust particle size and alpha based on depth
|
|
317
|
+
particle.size.x = scale;
|
|
318
|
+
particle.size.y = scale;
|
|
319
|
+
particle.superColorAlphaEnd = 1 - z / point.maxZ;
|
|
320
|
+
}
|
|
321
|
+
else {
|
|
322
|
+
// No perspective: flat 2D visualization
|
|
323
|
+
particle.movement.x = this.calculate(point.position.x, point.positionVariance.x) + x;
|
|
324
|
+
particle.movement.y = this.calculate(point.position.y, point.positionVariance.y) + y;
|
|
325
|
+
}
|
|
326
|
+
// Assign Z-coordinate for reference
|
|
327
|
+
particle.z = z;
|
|
328
|
+
}
|
|
329
|
+
else if (point.spawnType === 'Path') {
|
|
330
|
+
const pathPoints = point.pathPoints || []; // List of path points [{ x, y, z }]
|
|
331
|
+
if (pathPoints.length < 2)
|
|
332
|
+
return; // Ensure at least two points for a path
|
|
333
|
+
// Choose a random segment along the path
|
|
334
|
+
const segmentIndex = Math.floor(Math.random() * (pathPoints.length - 1));
|
|
335
|
+
const start = pathPoints[segmentIndex];
|
|
336
|
+
const end = pathPoints[segmentIndex + 1];
|
|
337
|
+
// Interpolate between the start and end points
|
|
338
|
+
const t = Math.random(); // Random position along the segment (0 to 1)
|
|
339
|
+
const x = start.x + t * (end.x - start.x);
|
|
340
|
+
const y = start.y + t * (end.y - start.y);
|
|
341
|
+
const z = start.z + t * (end.z - start.z || 0); // Support z if defined
|
|
342
|
+
// Apply perspective scaling if enabled
|
|
343
|
+
if (point.perspective > 0 && point.maxZ > 0) {
|
|
344
|
+
const scale = point.perspective / (point.perspective + z);
|
|
345
|
+
particle.movement.x = (this.calculate(point.position.x, point.positionVariance.x) + x) * scale;
|
|
346
|
+
particle.movement.y = (this.calculate(point.position.y, point.positionVariance.y) + y) * scale;
|
|
347
|
+
// Adjust particle size and alpha based on depth
|
|
348
|
+
particle.size.x = scale;
|
|
349
|
+
particle.size.y = scale;
|
|
350
|
+
particle.superColorAlphaEnd = 1 - z / point.maxZ;
|
|
351
|
+
}
|
|
352
|
+
else {
|
|
353
|
+
// Flat 2D path
|
|
354
|
+
particle.movement.x = this.calculate(point.position.x, point.positionVariance.x) + x;
|
|
355
|
+
particle.movement.y = this.calculate(point.position.y, point.positionVariance.y) + y;
|
|
356
|
+
}
|
|
357
|
+
particle.z = z; // Assign Z-coordinate
|
|
358
|
+
}
|
|
359
|
+
else if (point.spawnType === 'Oval') {
|
|
360
|
+
const angle = Math.random() * Math.PI * 2; // Random angle around the ellipse
|
|
361
|
+
const radiusX = point.radiusX || 100; // Default radiusX
|
|
362
|
+
const radiusY = point.radiusY || 50; // Default radiusY
|
|
363
|
+
particle.movement.x = this.calculate(point.position.x, point.positionVariance.x) + Math.cos(angle) * radiusX;
|
|
364
|
+
particle.movement.y = this.calculate(point.position.y, point.positionVariance.y) + Math.sin(angle) * radiusY;
|
|
365
|
+
}
|
|
366
|
+
};
|
|
367
|
+
/**
|
|
368
|
+
* Calculate canvas context for rendering text-based particles.
|
|
369
|
+
* @param {Object} point - The custom point configuration.
|
|
370
|
+
*/
|
|
371
|
+
this.calculateCtx = (point) => {
|
|
372
|
+
const text = point.word; // The word to render
|
|
373
|
+
const fontSize = point.fontSize; // Font size for rendering
|
|
374
|
+
if (!canvas) {
|
|
375
|
+
canvas = document.createElement('canvas');
|
|
376
|
+
canvas.width = point.fontMaxWidth;
|
|
377
|
+
canvas.height = point.fontMaxHeight;
|
|
378
|
+
}
|
|
379
|
+
const ctx = canvas.getContext('2d');
|
|
380
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height); // Clear the canvas
|
|
381
|
+
ctx.font = `${fontSize}px Arial`;
|
|
382
|
+
ctx.textAlign = point.textAlign;
|
|
383
|
+
ctx.textBaseline = point.textBaseline;
|
|
384
|
+
ctx.fillText(text, canvas.width / 2, canvas.height / 2);
|
|
385
|
+
// Get pixel data
|
|
386
|
+
imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
|
387
|
+
const { data, width, height } = imageData;
|
|
388
|
+
const spacing = point.fontSpacing; // Spacing between pixels
|
|
389
|
+
// Find all non-transparent pixels
|
|
390
|
+
pixelPositions = [];
|
|
391
|
+
for (let y = 0; y < height; y += spacing) {
|
|
392
|
+
for (let x = 0; x < width; x += spacing) {
|
|
393
|
+
const index = (y * width + x) * 4;
|
|
394
|
+
if (data[index + 3] > 128) {
|
|
395
|
+
// Alpha channel > 128 means this pixel is part of the text
|
|
396
|
+
pixelPositions.push(new Point(x, y));
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
pixelPositions = pixelPositions.sort(() => Math.random() - 0.5);
|
|
401
|
+
// Use particleDensity to limit the number of particles
|
|
402
|
+
particleCount = Math.floor(pixelPositions.length * point.particleDensity);
|
|
403
|
+
};
|
|
404
|
+
this.calculateTrailPosition = (point, overrideProgress) => {
|
|
405
|
+
const progress = overrideProgress !== undefined ? overrideProgress : this.currentProgress;
|
|
406
|
+
switch (point.spawnType) {
|
|
407
|
+
case 'Rectangle': {
|
|
408
|
+
const totalSegments = 4; // Rectangle has four edges
|
|
409
|
+
const segmentLength = (2 * point.radiusX + 2 * point.radiusY) * progress;
|
|
410
|
+
const perimeter = 2 * point.radiusX + 2 * point.radiusY;
|
|
411
|
+
const localPosition = segmentLength % perimeter;
|
|
412
|
+
let x = point.position.x;
|
|
413
|
+
let y = point.position.y;
|
|
414
|
+
if (localPosition <= point.radiusX) {
|
|
415
|
+
// Top edge
|
|
416
|
+
x += localPosition - point.radiusX;
|
|
417
|
+
y -= point.radiusY;
|
|
418
|
+
}
|
|
419
|
+
else if (localPosition <= point.radiusX + point.radiusY) {
|
|
420
|
+
// Right edge
|
|
421
|
+
x += point.radiusX;
|
|
422
|
+
y += localPosition - point.radiusX - point.radiusY;
|
|
423
|
+
}
|
|
424
|
+
else if (localPosition <= 2 * point.radiusX + point.radiusY) {
|
|
425
|
+
// Bottom edge
|
|
426
|
+
x += point.radiusX - (localPosition - point.radiusX - point.radiusY);
|
|
427
|
+
y += point.radiusY;
|
|
428
|
+
}
|
|
429
|
+
else {
|
|
430
|
+
// Left edge
|
|
431
|
+
x -= point.radiusX;
|
|
432
|
+
y += point.radiusY - (localPosition - 2 * point.radiusX - point.radiusY);
|
|
433
|
+
}
|
|
434
|
+
return { x, y, z: 0 };
|
|
435
|
+
}
|
|
436
|
+
case 'Ring': {
|
|
437
|
+
const angle = progress * Math.PI * 2; // Progress is proportional to angle
|
|
438
|
+
const x = point.position.x + Math.cos(angle) * point.radius;
|
|
439
|
+
const y = point.position.y + Math.sin(angle) * point.radius;
|
|
440
|
+
return { x, y, z: 0 };
|
|
441
|
+
}
|
|
442
|
+
case 'Star': {
|
|
443
|
+
const totalPoints = point.starPoints * 2; // Include outer and inner points
|
|
444
|
+
const pointIndex = Math.floor(progress * totalPoints);
|
|
445
|
+
const localProgress = (progress * totalPoints) % 1;
|
|
446
|
+
const angle = (Math.PI * 2 * pointIndex) / totalPoints;
|
|
447
|
+
const nextAngle = (Math.PI * 2 * (pointIndex + 1)) / totalPoints;
|
|
448
|
+
const radius = pointIndex % 2 === 0 ? point.radius : point.radius / 2;
|
|
449
|
+
const nextRadius = (pointIndex + 1) % 2 === 0 ? point.radius : point.radius / 2;
|
|
450
|
+
const x = point.position.x +
|
|
451
|
+
Math.cos(angle) * radius * (1 - localProgress) +
|
|
452
|
+
Math.cos(nextAngle) * nextRadius * localProgress;
|
|
453
|
+
const y = point.position.y +
|
|
454
|
+
Math.sin(angle) * radius * (1 - localProgress) +
|
|
455
|
+
Math.sin(nextAngle) * nextRadius * localProgress;
|
|
456
|
+
return { x, y, z: 0 };
|
|
457
|
+
}
|
|
458
|
+
case 'Path': {
|
|
459
|
+
const pathPoints = point.pathPoints || [];
|
|
460
|
+
if (pathPoints.length < 2)
|
|
461
|
+
return { x: 0, y: 0, z: 0 };
|
|
462
|
+
const totalSegments = pathPoints.length - 1;
|
|
463
|
+
const segmentIndex = Math.floor(progress * totalSegments);
|
|
464
|
+
const localProgress = (progress * totalSegments) % 1;
|
|
465
|
+
const start = pathPoints[segmentIndex];
|
|
466
|
+
const end = pathPoints[segmentIndex + 1];
|
|
467
|
+
const x = start.x + localProgress * (end.x - start.x);
|
|
468
|
+
const y = start.y + localProgress * (end.y - start.y);
|
|
469
|
+
const z = start.z + localProgress * (end.z - start.z || 0);
|
|
470
|
+
return { x, y, z };
|
|
471
|
+
}
|
|
472
|
+
case 'Lissajous': {
|
|
473
|
+
const a = point.frequency.x;
|
|
474
|
+
const b = point.frequency.y;
|
|
475
|
+
const delta = point.delta || Math.PI / 2;
|
|
476
|
+
const t = progress * Math.PI * 2;
|
|
477
|
+
const x = point.position.x + Math.sin(a * t + delta) * point.radius;
|
|
478
|
+
const y = point.position.y + Math.sin(b * t) * point.radius;
|
|
479
|
+
return { x, y, z: 0 };
|
|
480
|
+
}
|
|
481
|
+
case 'Helix': {
|
|
482
|
+
const turns = point.turns || 5;
|
|
483
|
+
const pitch = point.pitch || 50;
|
|
484
|
+
const angle = progress * turns * Math.PI * 2;
|
|
485
|
+
const x = point.position.x + Math.cos(angle) * point.radius;
|
|
486
|
+
const y = point.position.y + Math.sin(angle) * point.radius;
|
|
487
|
+
const z = point.position.z + progress * turns * pitch;
|
|
488
|
+
return { x, y, z };
|
|
489
|
+
}
|
|
490
|
+
case 'Heart': {
|
|
491
|
+
const t = progress * Math.PI * 2; // Map progress to parametric angle
|
|
492
|
+
const scale = point.radius || 100; // Scale based on radius
|
|
493
|
+
// Parametric heart shape equations
|
|
494
|
+
const x = scale * 16 * Math.pow(Math.sin(t), 3);
|
|
495
|
+
const y = -scale * (13 * Math.cos(t) - 5 * Math.cos(2 * t) - 2 * Math.cos(3 * t) - Math.cos(4 * t));
|
|
496
|
+
// Apply position offset and variance
|
|
497
|
+
return {
|
|
498
|
+
x: point.position.x + x,
|
|
499
|
+
y: point.position.y + y,
|
|
500
|
+
z: 0,
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
case 'Bezier': {
|
|
504
|
+
const t = progress; // Use progress directly as the parameter for the curve
|
|
505
|
+
const { start, end, control1, control2 } = point;
|
|
506
|
+
// Calculate position along the Bezier curve
|
|
507
|
+
const x = Math.pow((1 - t), 3) * start.x +
|
|
508
|
+
3 * Math.pow((1 - t), 2) * t * control1.x +
|
|
509
|
+
3 * (1 - t) * Math.pow(t, 2) * control2.x +
|
|
510
|
+
Math.pow(t, 3) * end.x;
|
|
511
|
+
const y = Math.pow((1 - t), 3) * start.y +
|
|
512
|
+
3 * Math.pow((1 - t), 2) * t * control1.y +
|
|
513
|
+
3 * (1 - t) * Math.pow(t, 2) * control2.y +
|
|
514
|
+
Math.pow(t, 3) * end.y;
|
|
515
|
+
return { x, y, z: 0 }; // Bezier is 2D, so z is 0
|
|
516
|
+
}
|
|
517
|
+
case 'Frame': {
|
|
518
|
+
const perimeter = 2 * (point.radiusX + point.radiusY); // Total perimeter of the frame
|
|
519
|
+
const segmentLength = perimeter * progress; // Length covered by progress
|
|
520
|
+
const localPosition = segmentLength % perimeter;
|
|
521
|
+
let x = point.position.x;
|
|
522
|
+
let y = point.position.y;
|
|
523
|
+
if (localPosition <= point.radiusX) {
|
|
524
|
+
// Top edge
|
|
525
|
+
x += localPosition;
|
|
526
|
+
y -= point.radiusY;
|
|
527
|
+
}
|
|
528
|
+
else if (localPosition <= point.radiusX + point.radiusY) {
|
|
529
|
+
// Right edge
|
|
530
|
+
x += point.radiusX;
|
|
531
|
+
y += localPosition - point.radiusX;
|
|
532
|
+
}
|
|
533
|
+
else if (localPosition <= 2 * point.radiusX + point.radiusY) {
|
|
534
|
+
// Bottom edge
|
|
535
|
+
x += point.radiusX - (localPosition - point.radiusX - point.radiusY);
|
|
536
|
+
y += point.radiusY;
|
|
537
|
+
}
|
|
538
|
+
else {
|
|
539
|
+
// Left edge
|
|
540
|
+
x -= localPosition - 2 * point.radiusX - point.radiusY;
|
|
541
|
+
y -= point.radiusY;
|
|
542
|
+
}
|
|
543
|
+
return { x, y, z: 0 }; // Frame is 2D, so z is 0
|
|
544
|
+
}
|
|
545
|
+
case 'FrameRectangle': {
|
|
546
|
+
const perimeter = 2 * (point.radiusX + point.radiusY); // Total perimeter of the rectangular frame
|
|
547
|
+
const segmentLength = perimeter * progress; // Length covered by progress
|
|
548
|
+
const localPosition = segmentLength % perimeter;
|
|
549
|
+
let x = point.position.x;
|
|
550
|
+
let y = point.position.y;
|
|
551
|
+
if (localPosition <= point.radiusX) {
|
|
552
|
+
// Top edge
|
|
553
|
+
x += localPosition;
|
|
554
|
+
y -= point.radiusY / 2;
|
|
555
|
+
}
|
|
556
|
+
else if (localPosition <= point.radiusX + point.radiusY) {
|
|
557
|
+
// Right edge
|
|
558
|
+
x += point.radiusX / 2;
|
|
559
|
+
y += localPosition - point.radiusX - point.radiusY / 2;
|
|
560
|
+
}
|
|
561
|
+
else if (localPosition <= 2 * point.radiusX + point.radiusY) {
|
|
562
|
+
// Bottom edge
|
|
563
|
+
x += point.radiusX / 2 - (localPosition - point.radiusX - point.radiusY);
|
|
564
|
+
y += point.radiusY / 2;
|
|
565
|
+
}
|
|
566
|
+
else {
|
|
567
|
+
// Left edge
|
|
568
|
+
x -= localPosition - 2 * point.radiusX - point.radiusY;
|
|
569
|
+
y -= point.radiusY / 2;
|
|
570
|
+
}
|
|
571
|
+
return { x, y, z: 0 }; // FrameRectangle is 2D, so z is 0
|
|
572
|
+
}
|
|
573
|
+
case 'Oval': {
|
|
574
|
+
const angle = progress * Math.PI * 2; // Progress determines angle
|
|
575
|
+
const radiusX = point.radiusX || 100;
|
|
576
|
+
const radiusY = point.radiusY || 50;
|
|
577
|
+
const x = point.position.x + Math.cos(angle) * radiusX;
|
|
578
|
+
const y = point.position.y + Math.sin(angle) * radiusY;
|
|
579
|
+
return { x, y, z: 0 };
|
|
580
|
+
}
|
|
581
|
+
case 'Word': {
|
|
582
|
+
if (!pixelPositions || pixelPositions.length === 0) {
|
|
583
|
+
return { x: point.position.x, y: point.position.y, z: 0 }; // Fallback if no pixel data is available
|
|
584
|
+
}
|
|
585
|
+
// Determine the number of pixels to reveal based on trailProgress
|
|
586
|
+
const maxPixelIndex = Math.floor(progress * pixelPositions.length);
|
|
587
|
+
// Slice the pixels to include only those within the current progress
|
|
588
|
+
const revealedPixels = pixelPositions.slice(0, maxPixelIndex);
|
|
589
|
+
// If no pixels are revealed yet, return the starting position
|
|
590
|
+
if (revealedPixels.length === 0) {
|
|
591
|
+
return { x: point.position.x - canvas.width / 2, y: point.position.y - canvas.height / 2, z: 0 };
|
|
592
|
+
}
|
|
593
|
+
// Choose the first pixel in the revealed portion for a left-to-right effect
|
|
594
|
+
const selectedPixel = revealedPixels[revealedPixels.length - 1] || { x: 0, y: 0 };
|
|
595
|
+
// Map the pixel position to the particle's position
|
|
596
|
+
const x = point.position.x + selectedPixel.x - canvas.width / 2;
|
|
597
|
+
const y = point.position.y + selectedPixel.y - canvas.height / 2;
|
|
598
|
+
return { x, y, z: 0 };
|
|
599
|
+
}
|
|
600
|
+
default:
|
|
601
|
+
return { x: 0, y: 0, z: 0 };
|
|
602
|
+
}
|
|
603
|
+
};
|
|
604
|
+
/**
|
|
605
|
+
* Update trail progress once per frame.
|
|
606
|
+
* @param {number} deltaTime - Time since the last update
|
|
607
|
+
*/
|
|
608
|
+
this.updateTrailProgress = (deltaTime) => {
|
|
609
|
+
if (!this.trailingEnabled)
|
|
610
|
+
return;
|
|
611
|
+
const trailStart = this.trailStart || 0;
|
|
612
|
+
// Increment trail progress
|
|
613
|
+
this.trailProgress += this.trailSpeed * deltaTime;
|
|
614
|
+
if (trailStart > 0) {
|
|
615
|
+
const remainingDistance = 1 - trailStart;
|
|
616
|
+
if (!this.overOne) {
|
|
617
|
+
if (this.trailProgress > remainingDistance) {
|
|
618
|
+
if (this.trailRepeat) {
|
|
619
|
+
this.overOne = true;
|
|
620
|
+
this.trailProgress = 0;
|
|
621
|
+
}
|
|
622
|
+
else {
|
|
623
|
+
this.trailProgress = remainingDistance;
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
this.currentProgress = trailStart + (this.trailProgress / remainingDistance) * remainingDistance;
|
|
627
|
+
}
|
|
628
|
+
else {
|
|
629
|
+
if (this.trailProgress > 1) {
|
|
630
|
+
if (this.trailRepeat) {
|
|
631
|
+
this.trailProgress %= 1;
|
|
632
|
+
}
|
|
633
|
+
else {
|
|
634
|
+
this.trailingEnabled = false;
|
|
635
|
+
this.trailProgress = 1;
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
this.currentProgress = this.trailProgress;
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
else {
|
|
642
|
+
if (this.trailProgress > 1) {
|
|
643
|
+
if (this.trailRepeat) {
|
|
644
|
+
this.trailProgress %= 1;
|
|
645
|
+
}
|
|
646
|
+
else {
|
|
647
|
+
this.trailingEnabled = false;
|
|
648
|
+
this.trailProgress = 1;
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
this.currentProgress = this.trailProgress;
|
|
652
|
+
}
|
|
653
|
+
};
|
|
654
|
+
// Utility function for weighted random selection
|
|
655
|
+
this.weightedRandomIndex = (probabilities) => {
|
|
656
|
+
const cumulative = probabilities.reduce((acc, prob, i) => {
|
|
657
|
+
acc.push(prob + (acc[i - 1] || 0));
|
|
658
|
+
return acc;
|
|
659
|
+
}, []);
|
|
660
|
+
const randomValue = Math.random();
|
|
661
|
+
return cumulative.findIndex((c) => randomValue <= c);
|
|
662
|
+
};
|
|
663
|
+
/**
|
|
664
|
+
* Update method to be called once per frame.
|
|
665
|
+
* @param {number} deltaTime - Time since the last frame
|
|
666
|
+
*/
|
|
667
|
+
this.update = (deltaTime) => {
|
|
668
|
+
this.updateTrailProgress(deltaTime); // Update trail once per frame
|
|
669
|
+
};
|
|
670
|
+
/**
|
|
671
|
+
* Adds a random variance to the given value
|
|
672
|
+
* @param {number} value - The value to calculate
|
|
673
|
+
* @param {number} variance - The random variance to add
|
|
674
|
+
* @returns {number} The calculated value
|
|
675
|
+
*/
|
|
676
|
+
this.calculate = (value, variance) => {
|
|
677
|
+
return value + this.varianceFrom(variance);
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
/**
|
|
681
|
+
* Gets the name of the behaviour
|
|
682
|
+
* @return {string} The name of the behaviour
|
|
683
|
+
*/
|
|
684
|
+
getName() {
|
|
685
|
+
return BehaviourNames.SPAWN_BEHAVIOUR;
|
|
686
|
+
}
|
|
687
|
+
/**
|
|
688
|
+
* Retrieves the properties of the custom points.
|
|
689
|
+
* @returns {Object[]} The array of custom points and their properties.
|
|
690
|
+
*/
|
|
691
|
+
getProps() {
|
|
692
|
+
return {
|
|
693
|
+
enabled: this.enabled,
|
|
694
|
+
priority: this.priority,
|
|
695
|
+
trailingEnabled: this.trailingEnabled,
|
|
696
|
+
spawnAlongTrail: this.spawnAlongTrail,
|
|
697
|
+
trailSpeed: this.trailSpeed,
|
|
698
|
+
trailRepeat: this.trailRepeat,
|
|
699
|
+
trailStart: this.trailStart,
|
|
700
|
+
customPoints: this.customPoints,
|
|
701
|
+
name: this.getName(),
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
//# sourceMappingURL=SpawnBehaviour.js.map
|