File size: 5,180 Bytes
e14f09d | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | """
Example 10: Trapcode Particular Iconic Features Showcase
Demonstrates:
1. Auxiliary Particle System (Aux Trails & Secondary Sparkle Dust)
2. Floor Collision & Bounce Physics (Elasticity & Surface Friction)
3. Custom Curves Over Life (Bell, GrowAndFade, Pulse)
4. Layer Mask Emitter (Particle Birth from Image/Logo with Pixel Color Inheritance)
"""
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
import numpy as np
from PIL import Image, ImageDraw, ImageFont
import particri as pc
output_dir = Path("output")
output_dir.mkdir(exist_ok=True)
print("--- 1. Generating Aux Trails & Floor Bounce Demo ---")
engine_bounce = pc.ParticleEngine(max_particles=35000, width=1920, height=1080)
# Add Floor Bounce Plane at y=920
engine_bounce.add_bounce_plane(coord=920.0, bounce=0.62, friction=0.88, is_horizontal=True, is_max=True)
# Add Gravity & subtle atmospheric drag
engine_bounce.add_gravity(0.0, 180.0)
engine_bounce.add_drag(0.08)
# Add slight curl noise turbulence
engine_bounce.add_curl_noise(seed=42, strength=60.0, frequency=0.002, speed=0.2)
# Primary Emitter: Golden Fountain shooting upwards & outwards
engine_bounce.add_emitter(
shape=pc.EmitterShape.Circle,
position=(960.0, 750.0),
size=(30.0, 30.0),
rate=180.0,
speed_range=(250.0, 520.0),
angle_range=(-2.35, -0.78), # Upward fan
lifetime_range=(1.8, 3.2),
size_start_range=(8.0, 18.0),
size_end_range=(1.0, 3.0),
color_start=(1.0, 0.9, 0.5, 1.0),
color_end=(1.0, 0.35, 0.05, 0.0),
particle_shape=pc.ParticleShape.Star4,
size_curve=pc.CurveType.GrowAndFade,
opacity_curve=pc.CurveType.GrowAndFade,
seed=101,
)
# Auxiliary System: Each falling firework spark leaves shimmering golden dust trails!
engine_bounce.enable_aux_system(
rate=25.0, # 25 child particles per second from each primary particle
lifetime=1.4,
size_range=(4.0, 0.5),
color_start=(1.0, 0.85, 0.3, 0.95),
color_end=(1.0, 0.2, 0.05, 0.0),
inherit_color=True,
velocity_inheritance=0.12, # Child inherits 12% parent speed
spread=18.0, # Slight jitter
particle_shape=pc.ParticleShape.Glitter,
size_curve=pc.CurveType.Bell, # Peaks in middle, gentle fade
opacity_curve=pc.CurveType.GrowAndFade,
emit_at_death=8, # 8 sparks burst on particle death
)
# Simulate 3.5 seconds
print("Simulating physics & particle lifecycles...")
engine_bounce.warmup(duration=3.5, step=1.0 / 60.0)
frame_bounce = engine_bounce.render(blend_mode=pc.BlendMode.Additive, bloom=True, bloom_intensity=0.85, bloom_radius=8)
pc.Exporter.save_image(frame_bounce, output_dir / "10_particular_aux_trails_bounce.png")
print(f"Saved: {output_dir / '10_particular_aux_trails_bounce.png'}")
print("\n--- 2. Generating Layer Mask / Logo Emitter Demo ---")
engine_layer = pc.ParticleEngine(max_particles=30000, width=1920, height=1080)
# Create a vibrant synthetic logo image mask (1920x1080) with PIL
logo_img = Image.new("RGBA", (1920, 1080), (0, 0, 0, 0))
draw = ImageDraw.Draw(logo_img)
# Draw central geometric glowing star emblem with gradient rings
cx, cy = 960, 540
# Outer ring (Cyan / Teal)
draw.ellipse([cx - 280, cy - 280, cx + 280, cy + 280], outline=(0, 220, 255, 255), width=8)
# Middle ring (Magenta / Pink)
draw.ellipse([cx - 200, cy - 200, cx + 200, cy + 200], outline=(255, 60, 180, 255), width=6)
# Inner ring (Warm Gold)
draw.ellipse([cx - 120, cy - 120, cx + 120, cy + 120], outline=(255, 215, 80, 255), width=5)
# Central diamond star polygon
def make_star(center_x, center_y, r_outer, r_inner, num_pts=8):
pts = []
for i in range(num_pts * 2):
r = r_outer if i % 2 == 0 else r_inner
angle = i * np.pi / num_pts - np.pi / 2
pts.append((center_x + r * np.cos(angle), center_y + r * np.sin(angle)))
return pts
star_pts = make_star(cx, cy, 100, 35, num_pts=8)
draw.polygon(star_pts, fill=(255, 240, 200, 255), outline=(255, 180, 50, 255))
# Add Layer Emitter to engine with automatic pixel color inheritance!
mask_np = np.asarray(logo_img, dtype=np.float32) / 255.0
engine_layer.add_layer_emitter(
image=mask_np,
rate=2800.0,
speed_range=(8.0, 45.0),
lifetime_range=(1.2, 3.0),
size_start_range=(5.0, 14.0),
size_end_range=(0.0, 2.0),
particle_shape=pc.ParticleShape.Star8,
size_curve=pc.CurveType.Bell,
opacity_curve=pc.CurveType.GrowAndFade,
inherit_layer_color=True,
drag=0.18,
seed=777,
)
# Add subtle vortex swirl around the logo
engine_layer.add_vortex(center=(cx, cy), strength=70.0, radial_pull=-10.0, radius=600.0)
engine_layer.add_curl_noise(seed=999, strength=50.0, frequency=0.003, speed=0.2)
print("Simulating logo layer emission...")
engine_layer.warmup(duration=2.5, step=1.0 / 60.0)
frame_layer = engine_layer.render(blend_mode=pc.BlendMode.Screen, bloom=True, bloom_intensity=0.9, bloom_radius=7)
pc.Exporter.save_image(frame_layer, output_dir / "10_particular_layer_mask_logo.png")
print(f"Saved: {output_dir / '10_particular_layer_mask_logo.png'}")
print("\nAll Trapcode Particular demos generated successfully!")
|