@cazala/party 0.1.0-next.46.59ce407 → 0.1.0-next.50.347f59f
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/README.md +31 -0
- package/dist/index.js +90 -2
- package/dist/index.js.map +1 -1
- package/dist/spawner.d.ts +18 -2
- package/dist/spawner.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,6 +12,7 @@ A high-performance TypeScript particle physics engine with dual runtime support
|
|
|
12
12
|
- **Advanced Rendering**: Trails, particle instancing, line rendering with multiple color modes
|
|
13
13
|
- **Export/Import Presets**: Export/import module settings (inputs + enabled state)
|
|
14
14
|
- **Cross-platform**: Works in all modern browsers with automatic feature detection
|
|
15
|
+
- **Spawner Utility**: Generate particle shapes, including text
|
|
15
16
|
|
|
16
17
|
## Installation
|
|
17
18
|
|
|
@@ -189,6 +190,36 @@ engine.unpinParticles([0, 1, 2]);
|
|
|
189
190
|
engine.unpinAll();
|
|
190
191
|
```
|
|
191
192
|
|
|
193
|
+
### Spawner
|
|
194
|
+
|
|
195
|
+
Generate particle arrays from common shapes (including text) using `Spawner`:
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
import { Spawner } from "@cazala/party";
|
|
199
|
+
|
|
200
|
+
const spawner = new Spawner();
|
|
201
|
+
const particles = spawner.initParticles({
|
|
202
|
+
count: 5000,
|
|
203
|
+
shape: "text",
|
|
204
|
+
center: { x: 0, y: 0 },
|
|
205
|
+
position: { x: 0, y: 0 },
|
|
206
|
+
align: { horizontal: "center", vertical: "center" },
|
|
207
|
+
text: "Party",
|
|
208
|
+
font: "sans-serif",
|
|
209
|
+
textSize: 80,
|
|
210
|
+
size: 3,
|
|
211
|
+
mass: 1,
|
|
212
|
+
colors: ["#ffffff"],
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
engine.setParticles(particles);
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Notes:
|
|
219
|
+
|
|
220
|
+
- `size` controls particle radius; `textSize` is the font size used to rasterize text.
|
|
221
|
+
- Playground font options: `sans-serif`, `serif`, `monospace`.
|
|
222
|
+
|
|
192
223
|
### Modules
|
|
193
224
|
|
|
194
225
|
Modules are pluggable components that contribute to simulation or rendering:
|
package/dist/index.js
CHANGED
|
@@ -5431,6 +5431,7 @@ class Engine {
|
|
|
5431
5431
|
}
|
|
5432
5432
|
}
|
|
5433
5433
|
|
|
5434
|
+
const TEXT_SPAWNER_FONTS = ["sans-serif", "serif", "monospace"];
|
|
5434
5435
|
function calculateVelocity(position, center, cfg) {
|
|
5435
5436
|
if (!cfg || cfg.speed === 0)
|
|
5436
5437
|
return { vx: 0, vy: 0 };
|
|
@@ -5472,7 +5473,7 @@ function calculateVelocity(position, center, cfg) {
|
|
|
5472
5473
|
}
|
|
5473
5474
|
class Spawner {
|
|
5474
5475
|
initParticles(options) {
|
|
5475
|
-
const { count, shape, center, spacing = 25, radius = 100, innerRadius = 50, squareSize = 200, cornerRadius = 0, size = 5, mass = 1, bounds, velocity, colors, } = options;
|
|
5476
|
+
const { count, shape, center, spacing = 25, radius = 100, innerRadius = 50, squareSize = 200, cornerRadius = 0, size = 5, mass = 1, bounds, velocity, colors, text = "Party", font = "sans-serif", textSize = 64, position, align, } = options;
|
|
5476
5477
|
const particles = [];
|
|
5477
5478
|
if (count <= 0)
|
|
5478
5479
|
return particles;
|
|
@@ -5499,6 +5500,93 @@ class Spawner {
|
|
|
5499
5500
|
return { r: 1, g: 1, b: 1, a: 1 };
|
|
5500
5501
|
return toColor(colors[Math.floor(Math.random() * colors.length)]);
|
|
5501
5502
|
};
|
|
5503
|
+
if (shape === "text") {
|
|
5504
|
+
const textValue = text.trim();
|
|
5505
|
+
if (!textValue)
|
|
5506
|
+
return particles;
|
|
5507
|
+
const createCanvas = () => {
|
|
5508
|
+
if (typeof OffscreenCanvas !== "undefined") {
|
|
5509
|
+
return new OffscreenCanvas(1, 1);
|
|
5510
|
+
}
|
|
5511
|
+
if (typeof document !== "undefined") {
|
|
5512
|
+
return document.createElement("canvas");
|
|
5513
|
+
}
|
|
5514
|
+
return null;
|
|
5515
|
+
};
|
|
5516
|
+
const canvas = createCanvas();
|
|
5517
|
+
if (!canvas)
|
|
5518
|
+
return particles;
|
|
5519
|
+
const ctx = canvas.getContext("2d");
|
|
5520
|
+
if (!ctx)
|
|
5521
|
+
return particles;
|
|
5522
|
+
const fontSize = Math.max(1, Math.floor(textSize));
|
|
5523
|
+
const fontSpec = `${fontSize}px ${font}`;
|
|
5524
|
+
ctx.font = fontSpec;
|
|
5525
|
+
ctx.textBaseline = "alphabetic";
|
|
5526
|
+
ctx.textAlign = "left";
|
|
5527
|
+
const metrics = ctx.measureText(textValue);
|
|
5528
|
+
const ascent = metrics.actualBoundingBoxAscent || fontSize;
|
|
5529
|
+
const descent = metrics.actualBoundingBoxDescent || fontSize * 0.25;
|
|
5530
|
+
const left = metrics.actualBoundingBoxLeft || 0;
|
|
5531
|
+
const right = metrics.actualBoundingBoxRight || metrics.width;
|
|
5532
|
+
const textWidth = Math.max(1, Math.ceil(left + right));
|
|
5533
|
+
const textHeight = Math.max(1, Math.ceil(ascent + descent));
|
|
5534
|
+
const padding = Math.ceil(fontSize * 0.25);
|
|
5535
|
+
canvas.width = textWidth + padding * 2;
|
|
5536
|
+
canvas.height = textHeight + padding * 2;
|
|
5537
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
5538
|
+
ctx.font = fontSpec;
|
|
5539
|
+
ctx.textBaseline = "alphabetic";
|
|
5540
|
+
ctx.textAlign = "left";
|
|
5541
|
+
ctx.fillStyle = "#ffffff";
|
|
5542
|
+
ctx.fillText(textValue, padding + left, padding + ascent);
|
|
5543
|
+
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
|
5544
|
+
const data = imageData.data;
|
|
5545
|
+
const sampleStep = Math.max(1, Math.round(size));
|
|
5546
|
+
const points = [];
|
|
5547
|
+
for (let y = 0; y < canvas.height; y += sampleStep) {
|
|
5548
|
+
for (let x = 0; x < canvas.width; x += sampleStep) {
|
|
5549
|
+
const idx = (y * canvas.width + x) * 4 + 3;
|
|
5550
|
+
if (data[idx] > 0) {
|
|
5551
|
+
points.push({ x, y });
|
|
5552
|
+
}
|
|
5553
|
+
}
|
|
5554
|
+
}
|
|
5555
|
+
const maxCount = Math.min(count, points.length);
|
|
5556
|
+
if (maxCount <= 0)
|
|
5557
|
+
return particles;
|
|
5558
|
+
const textPosition = position ?? center;
|
|
5559
|
+
const horizontal = align?.horizontal ?? "center";
|
|
5560
|
+
const vertical = align?.vertical ?? "center";
|
|
5561
|
+
const originX = horizontal === "left"
|
|
5562
|
+
? textPosition.x
|
|
5563
|
+
: horizontal === "right"
|
|
5564
|
+
? textPosition.x - textWidth
|
|
5565
|
+
: textPosition.x - textWidth / 2;
|
|
5566
|
+
const originY = vertical === "top"
|
|
5567
|
+
? textPosition.y
|
|
5568
|
+
: vertical === "bottom"
|
|
5569
|
+
? textPosition.y - textHeight
|
|
5570
|
+
: textPosition.y - textHeight / 2;
|
|
5571
|
+
const stride = points.length / maxCount;
|
|
5572
|
+
for (let i = 0; i < maxCount; i++) {
|
|
5573
|
+
const idx = Math.floor(i * stride);
|
|
5574
|
+
const point = points[idx];
|
|
5575
|
+
if (!point)
|
|
5576
|
+
continue;
|
|
5577
|
+
const x = originX + (point.x - padding);
|
|
5578
|
+
const y = originY + (point.y - padding);
|
|
5579
|
+
const { vx, vy } = calculateVelocity({ x, y }, textPosition, velocity);
|
|
5580
|
+
particles.push({
|
|
5581
|
+
position: { x, y },
|
|
5582
|
+
velocity: { x: vx, y: vy },
|
|
5583
|
+
size,
|
|
5584
|
+
mass,
|
|
5585
|
+
color: getColor(),
|
|
5586
|
+
});
|
|
5587
|
+
}
|
|
5588
|
+
return particles;
|
|
5589
|
+
}
|
|
5502
5590
|
if (shape === "grid") {
|
|
5503
5591
|
const cols = Math.ceil(Math.sqrt(count));
|
|
5504
5592
|
const rows = Math.ceil(count / cols);
|
|
@@ -10392,5 +10480,5 @@ class Particles extends Module {
|
|
|
10392
10480
|
}
|
|
10393
10481
|
}
|
|
10394
10482
|
|
|
10395
|
-
export { AbstractEngine, Behavior, Boundary, CanvasComposition, Collisions, DEFAULT_BEHAVIOR_ALIGNMENT, DEFAULT_BEHAVIOR_AVOID, DEFAULT_BEHAVIOR_CHASE, DEFAULT_BEHAVIOR_COHESION, DEFAULT_BEHAVIOR_REPULSION, DEFAULT_BEHAVIOR_SEPARATION, DEFAULT_BEHAVIOR_VIEW_ANGLE, DEFAULT_BEHAVIOR_VIEW_RADIUS, DEFAULT_BEHAVIOR_WANDER, DEFAULT_BOUNDARY_FRICTION, DEFAULT_BOUNDARY_MODE, DEFAULT_BOUNDARY_REPEL_DISTANCE, DEFAULT_BOUNDARY_REPEL_STRENGTH, DEFAULT_BOUNDARY_RESTITUTION, DEFAULT_COLLISIONS_RESTITUTION, DEFAULT_ENVIRONMENT_DAMPING, DEFAULT_ENVIRONMENT_FRICTION, DEFAULT_ENVIRONMENT_GRAVITY_ANGLE, DEFAULT_ENVIRONMENT_GRAVITY_DIRECTION, DEFAULT_ENVIRONMENT_GRAVITY_STRENGTH, DEFAULT_ENVIRONMENT_INERTIA, DEFAULT_FLUIDS_ENABLE_NEAR_PRESSURE, DEFAULT_FLUIDS_INFLUENCE_RADIUS, DEFAULT_FLUIDS_MAX_ACCELERATION, DEFAULT_FLUIDS_NEAR_PRESSURE_MULTIPLIER, DEFAULT_FLUIDS_NEAR_THRESHOLD, DEFAULT_FLUIDS_PRESSURE_MULTIPLIER, DEFAULT_FLUIDS_TARGET_DENSITY, DEFAULT_FLUIDS_VISCOSITY, DEFAULT_GRAB_GRABBED_INDEX, DEFAULT_GRAB_POSITION_X, DEFAULT_GRAB_POSITION_Y, DEFAULT_INTERACTION_MODE, DEFAULT_INTERACTION_RADIUS, DEFAULT_INTERACTION_STRENGTH, DEFAULT_JOINTS_ENABLE_JOINT_COLLISIONS, DEFAULT_JOINTS_ENABLE_PARTICLE_COLLISIONS, DEFAULT_JOINTS_FRICTION, DEFAULT_JOINTS_MOMENTUM, DEFAULT_JOINTS_RESTITUTION, DEFAULT_JOINTS_SEPARATION, DEFAULT_JOINTS_STEPS, DEFAULT_PICFLIP_DENSITY, DEFAULT_PICFLIP_FLIP_RATIO, DEFAULT_PICFLIP_INFLUENCE_RADIUS, DEFAULT_PICFLIP_INTERNAL_MAX_ACCELERATION, DEFAULT_PICFLIP_PRESSURE, DEFAULT_PICFLIP_PRESSURE_MULTIPLIER, DEFAULT_PICFLIP_RADIUS, DEFAULT_PICFLIP_TARGET_DENSITY, DEFAULT_SENSORS_COLOR_SIMILARITY_THRESHOLD, DEFAULT_SENSORS_FLEE_ANGLE, DEFAULT_SENSORS_FLEE_BEHAVIOR, DEFAULT_SENSORS_FOLLOW_BEHAVIOR, DEFAULT_SENSORS_SENSOR_ANGLE, DEFAULT_SENSORS_SENSOR_DISTANCE, DEFAULT_SENSORS_SENSOR_RADIUS, DEFAULT_SENSORS_SENSOR_STRENGTH, DEFAULT_SENSORS_SENSOR_THRESHOLD, DEFAULT_TRAILS_TRAIL_DECAY, DEFAULT_TRAILS_TRAIL_DIFFUSE, DataType, Engine, Environment, Fluids, FluidsMethod, Grab, Interaction, Joints, Lines, Module, ModuleRole, OscillatorManager, Particles, ParticlesColorType, RenderPassKind, Sensors, Spawner, Trails, Vector, degToRad, radToDeg };
|
|
10483
|
+
export { AbstractEngine, Behavior, Boundary, CanvasComposition, Collisions, DEFAULT_BEHAVIOR_ALIGNMENT, DEFAULT_BEHAVIOR_AVOID, DEFAULT_BEHAVIOR_CHASE, DEFAULT_BEHAVIOR_COHESION, DEFAULT_BEHAVIOR_REPULSION, DEFAULT_BEHAVIOR_SEPARATION, DEFAULT_BEHAVIOR_VIEW_ANGLE, DEFAULT_BEHAVIOR_VIEW_RADIUS, DEFAULT_BEHAVIOR_WANDER, DEFAULT_BOUNDARY_FRICTION, DEFAULT_BOUNDARY_MODE, DEFAULT_BOUNDARY_REPEL_DISTANCE, DEFAULT_BOUNDARY_REPEL_STRENGTH, DEFAULT_BOUNDARY_RESTITUTION, DEFAULT_COLLISIONS_RESTITUTION, DEFAULT_ENVIRONMENT_DAMPING, DEFAULT_ENVIRONMENT_FRICTION, DEFAULT_ENVIRONMENT_GRAVITY_ANGLE, DEFAULT_ENVIRONMENT_GRAVITY_DIRECTION, DEFAULT_ENVIRONMENT_GRAVITY_STRENGTH, DEFAULT_ENVIRONMENT_INERTIA, DEFAULT_FLUIDS_ENABLE_NEAR_PRESSURE, DEFAULT_FLUIDS_INFLUENCE_RADIUS, DEFAULT_FLUIDS_MAX_ACCELERATION, DEFAULT_FLUIDS_NEAR_PRESSURE_MULTIPLIER, DEFAULT_FLUIDS_NEAR_THRESHOLD, DEFAULT_FLUIDS_PRESSURE_MULTIPLIER, DEFAULT_FLUIDS_TARGET_DENSITY, DEFAULT_FLUIDS_VISCOSITY, DEFAULT_GRAB_GRABBED_INDEX, DEFAULT_GRAB_POSITION_X, DEFAULT_GRAB_POSITION_Y, DEFAULT_INTERACTION_MODE, DEFAULT_INTERACTION_RADIUS, DEFAULT_INTERACTION_STRENGTH, DEFAULT_JOINTS_ENABLE_JOINT_COLLISIONS, DEFAULT_JOINTS_ENABLE_PARTICLE_COLLISIONS, DEFAULT_JOINTS_FRICTION, DEFAULT_JOINTS_MOMENTUM, DEFAULT_JOINTS_RESTITUTION, DEFAULT_JOINTS_SEPARATION, DEFAULT_JOINTS_STEPS, DEFAULT_PICFLIP_DENSITY, DEFAULT_PICFLIP_FLIP_RATIO, DEFAULT_PICFLIP_INFLUENCE_RADIUS, DEFAULT_PICFLIP_INTERNAL_MAX_ACCELERATION, DEFAULT_PICFLIP_PRESSURE, DEFAULT_PICFLIP_PRESSURE_MULTIPLIER, DEFAULT_PICFLIP_RADIUS, DEFAULT_PICFLIP_TARGET_DENSITY, DEFAULT_SENSORS_COLOR_SIMILARITY_THRESHOLD, DEFAULT_SENSORS_FLEE_ANGLE, DEFAULT_SENSORS_FLEE_BEHAVIOR, DEFAULT_SENSORS_FOLLOW_BEHAVIOR, DEFAULT_SENSORS_SENSOR_ANGLE, DEFAULT_SENSORS_SENSOR_DISTANCE, DEFAULT_SENSORS_SENSOR_RADIUS, DEFAULT_SENSORS_SENSOR_STRENGTH, DEFAULT_SENSORS_SENSOR_THRESHOLD, DEFAULT_TRAILS_TRAIL_DECAY, DEFAULT_TRAILS_TRAIL_DIFFUSE, DataType, Engine, Environment, Fluids, FluidsMethod, Grab, Interaction, Joints, Lines, Module, ModuleRole, OscillatorManager, Particles, ParticlesColorType, RenderPassKind, Sensors, Spawner, TEXT_SPAWNER_FONTS, Trails, Vector, degToRad, radToDeg };
|
|
10396
10484
|
//# sourceMappingURL=index.js.map
|