Particle fountain: Difference between revisions
m (→{{header|Julia}}: renamed window title) |
(Added Wren) |
||
Line 510:
[https://github.com/thundergnat/rc/blob/master/img/fountain-raku.mp4?raw=true Link to off-site .mp4 video]
=={{header|Wren}}==
{{trans|Julia}}
{{libheader|DOME}}
{{libheader|Wren-dynamic}}
<lang ecmascript>import "dome" for Window, Platform, Process
import "graphics" for Canvas, Color
import "math" for Math, Point
import "random" for Random
import "input" for Keyboard
import "./dynamic" for Struct
var Start = Platform.time
var Rand = Random.new()
var fields = [
"particleNum",
"positions",
"velocities",
"lifetimes",
"points",
"numPoints",
"saturation",
"spread",
"range",
"reciprocate"
]
var ParticleFountain = Struct.create("ParticleFountain", fields)
class ParticleDisplay {
construct new(particleNum, width, height) {
Window.resize(width, height)
Canvas.resize(width, height)
Window.title = "Wren Particle System!"
_pn = particleNum
_w = width
_h = height
_df = 1 / 200 // say
_pf = ParticleFountain.new(
_pn, // particleNum
List.filled(_pn * 2, 0), // positions
List.filled(_pn * 2, 0), // velocities
List.filled(_pn, 0), // lifetimes
List.filled(_pn, null), // points
0, // numPoints
0.4, // saturation
1.5, // spread
1.5, // range
false // reciprocate
)
for (i in 0..._pn) _pf.points[i] = Point.new(0, 0)
}
init() {
Canvas.cls()
_frames = 0
}
updatePF() {
var xidx = 0
var yidx = 1
var pointIdx = 0
var recip = Fn.new { _pf.reciprocate ? _pf.range * Math.sin(Platform.time/1000) : 0 }
for (idx in 0..._pf.particleNum) {
var willDraw = false
if (_pf.lifetimes[idx] <= 0) {
if (Rand.float() < _df) {
_pf.lifetimes[idx] = 2.5 // time to live
_pf.positions[xidx] = _w / 20 // starting position x
_pf.positions[yidx] = _h / 10 // and y
// starting velocities x and y
// randomized slightly so points reach different heights
_pf.velocities[xidx] = 10 * (_pf.spread * Rand.float() - _pf.spread / 2 + recip.call())
_pf.velocities[yidx] = (Rand.float() - 2.9) * _h / 20.5
_willDraw = true
}
} else {
if (_pf.positions[yidx] > _h/10 && _pf.velocities[yidx] > 0) {
_pf.velocities[yidx] = _pf.velocities[yidx] * (-0.3) // bounce
}
_pf.velocities[yidx] = _pf.velocities[yidx] + _df * _h / 10 // adjust velocity
_pf.positions[xidx] = _pf.positions[xidx] + _pf.velocities[xidx] * _df // adjust position x
_pf.positions[yidx] = _pf.positions[yidx] + _pf.velocities[yidx] * _df // and y
_pf.lifetimes[idx] = _pf.lifetimes[idx] - _df
willDraw = true
}
if (willDraw) { // gather all the points that are going to be rendered
_pf.points[pointIdx] = Point.new((_pf.positions[xidx] * 10).floor,
(_pf.positions[yidx] * 10).floor)
pointIdx = pointIdx + 1
}
xidx = xidx + 2
yidx = xidx + 1
_pf.numPoints = pointIdx
}
}
update() {
if (Keyboard["Up"].justPressed) {
_pf.saturation = Math.min(_pf.saturation + 0.1, 1)
} else if (Keyboard["Down"].justPressed) {
_pf.saturation = Math.max(_pf.saturation - 0.1, 0)
} else if (Keyboard["PageUp"].justPressed) {
_pf.spread = Math.min(_pf.spread + 1, 50)
} else if (Keyboard["PageDown"].justPressed) {
_pf.spread = Math.max(_pf.spread - 0.1, 0.2)
} else if (Keyboard["Left"].justPressed) {
_pf.range = Math.min(_pf.range + 0.1, 12)
} else if (Keyboard["Right"].justPressed) {
_pf.range = Math.max(_pf.range - 0.1, 0.1)
} else if (Keyboard["Space"].justPressed) {
_pf.reciprocate = !_pf.reciprocate
} else if (Keyboard["Q"].justPressed) {
Process.exit()
}
updatePF()
}
draw(alpha) {
var c = Color.hsv((Platform.time % 5) * 72, _pf.saturation, 0.5, 0x7f)
for (i in 0..._pf.numPoints) {
Canvas.pset(_pf.points[i].x, _pf.points[i].y, c)
}
_frames = _frames + 1
var now = Platform.time
if (now - Start >= 1) {
Start = now
Window.title = "Wren Particle System! (FPS = %(_frames))"
_frames = 0
}
}
}
System.print("""
Use UP and DOWN arrow keys to modify the saturation of the particle colors.
Use PAGE UP and PAGE DOWN keys to modify the "spread" of the particles.
Toggle reciprocation off / on with the SPACE bar.
Use LEFT and RIGHT arrow keys to modify angle range for reciprocation.
Press the "q" key to quit.
""")
var Game = ParticleDisplay.new(3000, 800, 800)</lang>
|
Revision as of 14:27, 10 September 2021
Implement a particle fountain.
Emulate a fountain of water droplets in a gravitational field being sprayed up and then falling back down.
The particle fountain should be generally ordered but individually chaotic; the particles should be going mostly in the same direction, but should have slightly different vectors.
Your fountain should have at least several hundred particles in motion at any one time, and ideally several thousand.
It is optional to have the individual particle interact with each other.
If at all possible, link to a short video clip of your fountain in action.
Julia
<lang julia>using Dates, Colors, SimpleDirectMediaLayer.LibSDL2
mutable struct ParticleFountain
particlenum::Int positions::Vector{Float64} velocities::Vector{Float64} lifetimes::Vector{Float64} points::Vector{SDL_Point} numpoints::Int saturation::Float64 spread::Float64 range::Float64 reciprocate::Bool ParticleFountain(N) = new(N, zeros(2N), zeros(2N), zeros(N), fill(SDL_Point(0, 0), N), 0, 0.4, 1.5, 1.5, false)
end
function update(pf, w, h, df)
xidx, yidx, pointidx = 1, 2, 0 recip() = pf.reciprocate ? pf.range * sin(Dates.value(now()) / 1000) : 0.0 for idx in 1:pf.particlenum willdraw = false if pf.lifetimes[idx] <= 0.0 if rand() < df pf.lifetimes[idx] = 2.5; # time to live pf.positions[xidx] = (w / 20) # starting position x pf.positions[yidx] = (h / 10) # and y pf.velocities[xidx] = 10 * (pf.spread * rand() - pf.spread / 2 + recip()) # starting velocity x pf.velocities[yidx] = (rand() - 2.9) * h / 20.5; # and y (randomized slightly so points reach different heights) willdraw = true end else if pf.positions[yidx] > h / 10 && pf.velocities[yidx] > 0 pf.velocities[yidx] *= -0.3 # "bounce" end pf.velocities[yidx] += df * h / 10 # adjust velocity pf.positions[xidx] += pf.velocities[xidx] * df # adjust position x pf.positions[yidx] += pf.velocities[yidx] * df # and y pf.lifetimes[idx] -= df willdraw = true end
if willdraw # gather all of the points that are going to be rendered pointidx += 1 pf.points[pointidx] = SDL_Point(Cint(floor(pf.positions[xidx] * 10)), Cint(floor(pf.positions[yidx] * 10))) end xidx += 2 yidx = xidx + 1 pf.numpoints = pointidx end return pf
end
function fountain(particlenum = 3000, w = 800, h = 800)
SDL_Init(SDL_INIT_VIDEO) window = SDL_CreateWindow("Julia Particle System!", SDL_WINDOWPOS_CENTERED_MASK, SDL_WINDOWPOS_CENTERED_MASK, w, h, SDL_WINDOW_RESIZABLE) renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED) SDL_ClearError() df = 0.0001 pf = ParticleFountain(3000) overallstart, close, frames = now(), false, 0 while !close dfstart = now() event_ref = Ref{SDL_Event}() while Bool(SDL_PollEvent(event_ref)) event_type = event_ref[].type evt = event_ref[] if event_type == SDL_QUIT close = true break end if event_type == SDL_WINDOWEVENT if evt.window.event == 5 w = evt.window.data1 h = evt.window.data2 end end if event_type == SDL_KEYDOWN comm = evt.key.keysym.scancode if comm == SDL_SCANCODE_UP saturation = min(pf.saturation + 0.1, 1.0) elseif comm == SDL_SCANCODE_DOWN saturation = max(pf.saturation - 0.1, 0.0) elseif comm == SDL_SCANCODE_PAGEUP spread = min(pf.spread + 1, 50.0) elseif comm == SDL_SCANCODE_PAGEDOWN spread = max(pf.spread - 0.1, 0.2) elseif comm == SDL_SCANCODE_LEFT range = min(pf.range + 0.1, 12.0) elseif comm == SDL_SCANCODE_RIGHT range = max(pf.range - 0.1, 0.1) elseif comm == SDL_SCANCODE_SPACE pf.reciprocate = !pf.reciprocate elseif comm == SDL_SCANCODE_Q close = true break end end end pf = update(pf, w, h, df) SDL_SetRenderDrawColor(renderer, 0x0, 0x0, 0x0, 0xff) SDL_RenderClear(renderer) rgb = parse(UInt32, hex(HSL((Dates.value(now()) % 5) * 72, pf.saturation, 0.5)), base=16) red, green, blue = rgb & 0xff, (rgb >> 8) & 0xff, (rgb >>16) & 0xff SDL_SetRenderDrawColor(renderer, red, green, blue, 0x7f) SDL_RenderDrawPoints(renderer, pf.points, pf.numpoints) SDL_RenderPresent(renderer) frames += 1 df = Float64(Dates.value(now()) - Dates.value(dfstart)) / 1000 elapsed = Float64(Dates.value(now()) - Dates.value(overallstart)) / 1000 elapsed > 0.5 && print("\r", ' '^20, "\rFPS: ", round(frames / elapsed, digits=1)) end SDL_Quit()
end
println("""
Use UP and DOWN arrow keys to modify the saturation of the particle colors. Use PAGE UP and PAGE DOWN keys to modify the "spread" of the particles. Toggle reciprocation off / on with the SPACE bar. Use LEFT and RIGHT arrow keys to modify angle range for reciprocation. Press the "q" key to quit.
""")
fountain() </lang>
Perl
<lang perl>#!/usr/bin/perl
use strict; # https://rosettacode.org/wiki/Particle_fountain use warnings; use Tk;
my $size = 900; my @particles; my $maxparticles = 500; my @colors = qw( red green blue yellow cyan magenta orange white );
my $mw = MainWindow->new; my $c = $mw->Canvas( -width => $size, -height => $size, -bg => 'black',
)->pack;
$mw->Button(-text => 'Exit', -command => sub {$mw->destroy},
)->pack(-fill => 'x');
step(); MainLoop; -M $0 < 0 and exec $0;
sub step
{ $c->delete('all'); $c->createLine($size / 2 - 10, $size, $size / 2, $size - 10, $size / 2 + 10, $size, -fill => 'white' ); for ( @particles ) { my ($ox, $oy, $vx, $vy, $color) = @$_; my $x = $ox + $vx; my $y = $oy + $vy; $c->createRectangle($ox, $oy, $x, $y, -fill => $color, -outline => $color); if( $y < $size ) { $_->[0] = $x; $_->[1] = $y; $_->[3] += 0.006; # gravity :) } else { $_ = undef } } @particles = grep defined, @particles; if( @particles < $maxparticles and --$| ) { push @particles, [ $size >> 1, $size - 10, (1 - rand 2) / 2.5 , -3 - rand 0.05, $colors[rand @colors] ]; } $mw->after(1 => \&step); }</lang>
Phix
You can run this online here.
-- -- demo\rosetta\Particle_fountain.exw -- ================================== -- with javascript_semantics include pGUI.e Ihandle dlg, canvas cdCanvas cddbuffer, cdcanvas constant title = "Particle fountain" constant help_text = """ Uparrow increases the saturation of the particle colors, downarrow decreases saturation until they all become white. PageUp sprays the particles out at a wider angle/spread, PageDown makes the jet narrower. Space toggles reciprocation (wobble) on and off (straight up). Left arrow decreases the angle range for reciprocation, right arrow increases the angle range for reciprocation. Press the "q" key to quit. """ constant particlenum = 3000 -- each particle is {x,y,color,life,dx,dy} sequence particles = repeat({0,0,0,0,0,0},particlenum) atom t1 = time()+1 integer fps = 0 bool reciprocate = true atom range = 1.5, spread = 1.5, saturation = 0.4, start = time(), df = 0.0001 function redraw_cb(Ihandle /*ih*/, integer /*posx*/, /*posy*/) integer {w, h} = IupGetIntInt(canvas, "DRAWSIZE") cdCanvasActivate(cddbuffer) cdCanvasClear(cddbuffer) for i=1 to length(particles) do atom {x,y,color,life} = particles[i] if life>0 then cdCanvasPixel(cddbuffer, x, h/10-y, color) end if end for cdCanvasFlush(cddbuffer) return IUP_DEFAULT end function function map_cb(Ihandle ih) cdcanvas = cdCreateCanvas(CD_IUP, ih) cddbuffer = cdCreateCanvas(CD_DBUFFER, cdcanvas) cdCanvasSetBackground(cddbuffer, CD_BLACK) return IUP_DEFAULT end function function timer_cb(Ihandle /*ih*/) integer {w, h} = IupGetIntInt(canvas, "DRAWSIZE") fps += 1 df = time()-start start = time() for i=1 to particlenum do atom {x,y,color,life,dx,dy} = particles[i] if life<=0 then if rnd()<df then life = 2.5 -- time to live x = w/2 -- starting position x y = h/10 -- and y -- randomize velocity so points reach different heights: atom r = iff(reciprocate?range*sin(time()):0) dx = (spread*rnd()-spread/2+r)*50 -- starting velocity x dy = (rnd()-2.9) * h/20.5 -- and y color = hsv_to_rgb(round(remainder(time(),5)/5,100), saturation, 1) end if else if y>h/10 and dy>0 then dy *= -0.3 -- "bounce" end if dy += (h/10)*df -- adjust velocity x += dx*df -- adjust position x y += dy*df*8 -- and y life -= df end if particles[i] = {x,y,color,life,dx,dy} end for IupRedraw(canvas) if time()>t1 then IupSetStrAttribute(dlg,"TITLE","%s (%d, %d fps/s [%dx%d])",{title,particlenum,fps,w,h}) t1 = time()+1 fps = 0 end if return IUP_DEFAULT end function function key_cb(Ihandle /*dlg*/, atom c) if c=K_ESC or lower(c)='q' then return IUP_CLOSE elsif c=K_F1 then IupMessage(title,help_text) elsif c=K_UP then saturation = min(saturation+0.1,1) elsif c=K_DOWN then saturation = max(saturation-0.1,0) elsif c=K_PGUP then spread = min(spread+0.1,5) elsif c=K_PGDN then spread = max(spread-0.1,0.2) elsif c=K_RIGHT then range = min(range+0.1,2) elsif c=K_LEFT then range = max(range-0.1,0.1) elsif c=K_SP then reciprocate = not reciprocate end if return IUP_CONTINUE end function procedure main() IupOpen() canvas = IupGLCanvas("RASTERSIZE=400x300") IupSetCallbacks({canvas}, {"ACTION", Icallback("redraw_cb"), "MAP_CB", Icallback("map_cb")}) dlg = IupDialog(canvas,`TITLE="%s"`,{title}) IupSetCallback(dlg, "KEY_CB", Icallback("key_cb")) Ihandle timer = IupTimer(Icallback("timer_cb"), 1000/25) IupShowXY(dlg,IUP_CENTER,IUP_CENTER) IupSetAttribute(canvas, "RASTERSIZE", NULL) if platform()!=JS then IupMainLoop() IupClose() end if end procedure main()
Raku
Has options to vary the direction at which the fountain sprays, the "spread" angle and the color of the emitted particles. <lang perl6>use NativeCall; use SDL2::Raw;
my int ($w, $h) = 800, 800; my SDL_Window $window; my SDL_Renderer $renderer;
my int $particlenum = 3000;
SDL_Init(VIDEO);
$window = SDL_CreateWindow(
"Raku Particle System!", SDL_WINDOWPOS_CENTERED_MASK, SDL_WINDOWPOS_CENTERED_MASK, $w, $h, RESIZABLE
); $renderer = SDL_CreateRenderer( $window, -1, ACCELERATED );
SDL_ClearError();
my num @positions = 0e0 xx ($particlenum * 2); my num @velocities = 0e0 xx ($particlenum * 2); my num @lifetimes = 0e0 xx $particlenum;
my CArray[int32] $points .= new; my int $numpoints; my Num $saturation = 4e-1; my Num $spread = 15e-1; my &reciprocate = sub { 0 } my $range = 1.5;
sub update (num \df) {
my int $xidx = 0; my int $yidx = 1; my int $pointidx = 0; loop (my int $idx = 0; $idx < $particlenum; $idx = $idx + 1) { my int $willdraw = 0; if (@lifetimes[$idx] <= 0e0) { if (rand < df) { @lifetimes[$idx] = 25e-1; # time to live @positions[$xidx] = ($w / 20e0).Num; # starting position x @positions[$yidx] = ($h / 10).Num; # and y @velocities[$xidx] = ($spread * rand - $spread/2 + reciprocate()) * 10; # starting velocity x @velocities[$yidx] = (rand - 2.9e0) * $h / 20.5; # and y (randomized slightly so points reach different heights) $willdraw = 1; } } else { if @positions[$yidx] > $h / 10 && @velocities[$yidx] > 0 { @velocities[$yidx] = @velocities[$yidx] * -0.3e0; # "bounce" }
@velocities[$yidx] = @velocities[$yidx] + $h/10.Num * df; # adjust velocity @positions[$xidx] = @positions[$xidx] + @velocities[$xidx] * df; # adjust position x @positions[$yidx] = @positions[$yidx] + @velocities[$yidx] * df; # and y
@lifetimes[$idx] = @lifetimes[$idx] - df; $willdraw = 1; }
if ($willdraw) { $points[$pointidx++] = (@positions[$xidx] * 10).floor; # gather all of the points that $points[$pointidx++] = (@positions[$yidx] * 10).floor; # are still going to be rendered }
$xidx = $xidx + 2; $yidx = $xidx + 1; } $numpoints = ($pointidx - 1) div 2;
}
sub render {
SDL_SetRenderDrawColor($renderer, 0x0, 0x0, 0x0, 0xff); SDL_RenderClear($renderer);
SDL_SetRenderDrawColor($renderer, |hsv2rgb(((now % 5) / 5).round(.01), $saturation, 1), 0x7f); SDL_RenderDrawPoints($renderer, $points, $numpoints);
SDL_RenderPresent($renderer);
}
enum KEY_CODES (
K_UP => 82, K_DOWN => 81, K_LEFT => 80, K_RIGHT => 79, K_SPACE => 44, K_PGUP => 75, K_PGDN => 78, K_Q => 20,
);
say q:to/DOCS/; Use UP and DOWN arrow keys to modify the saturation of the particle colors. Use PAGE UP and PAGE DOWN keys to modify the "spread" of the particles. Toggle reciprocation off / on with the SPACE bar. Use LEFT and RIGHT arrow keys to modify angle range for reciprocation. Press the "q" key to quit. DOCS
my $event = SDL_Event.new;
my num $df = 0.0001e0;
main: loop {
my $start = now;
while SDL_PollEvent($event) { my $casted_event = SDL_CastEvent($event);
given $casted_event { when *.type == QUIT { last main; } when *.type == WINDOWEVENT { if .event == RESIZED { $w = .data1; $h = .data2; } } when *.type == KEYDOWN { if KEY_CODES(.scancode) -> $comm { given $comm { when 'K_UP' { $saturation = (($saturation + .1) min 1e0) } when 'K_DOWN' { $saturation = (($saturation - .1) max 0e0) } when 'K_PGUP' { $spread = (($spread + .1) min 5e0) } when 'K_PGDN' { $spread = (($spread - .1) max 2e-1) } when 'K_RIGHT' { $range = (($range + .1) min 2e0) } when 'K_LEFT' { $range = (($range - .1) max 1e-1) } when 'K_SPACE' { &reciprocate = reciprocate() == 0 ?? sub { $range * sin(now) } !! sub { 0 } } when 'K_Q' { last main } } } } } }
update($df);
render();
$df = (now - $start).Num;
print fps();
}
say ;
sub fps {
state $fps-frames = 0; state $fps-now = now; state $fps = ; $fps-frames++; if now - $fps-now >= 1 { $fps = [~] "\r", ' ' x 20, "\r", sprintf "FPS: %5.1f ", ($fps-frames / (now - $fps-now)); $fps-frames = 0; $fps-now = now; } $fps
}
sub hsv2rgb ( $h, $s, $v ){
state %cache; %cache{"$h|$s|$v"} //= do { my $c = $v * $s; my $x = $c * (1 - abs( (($h*6) % 2) - 1 ) ); my $m = $v - $c; [(do given $h { when 0..^1/6 { $c, $x, 0 } when 1/6..^1/3 { $x, $c, 0 } when 1/3..^1/2 { 0, $c, $x } when 1/2..^2/3 { 0, $x, $c } when 2/3..^5/6 { $x, 0, $c } when 5/6..1 { $c, 0, $x } } ).map: ((*+$m) * 255).Int] }
}</lang>
Wren
<lang ecmascript>import "dome" for Window, Platform, Process import "graphics" for Canvas, Color import "math" for Math, Point import "random" for Random import "input" for Keyboard import "./dynamic" for Struct
var Start = Platform.time var Rand = Random.new()
var fields = [
"particleNum", "positions", "velocities", "lifetimes", "points", "numPoints", "saturation", "spread", "range", "reciprocate"
] var ParticleFountain = Struct.create("ParticleFountain", fields)
class ParticleDisplay {
construct new(particleNum, width, height) { Window.resize(width, height) Canvas.resize(width, height) Window.title = "Wren Particle System!" _pn = particleNum _w = width _h = height _df = 1 / 200 // say _pf = ParticleFountain.new( _pn, // particleNum List.filled(_pn * 2, 0), // positions List.filled(_pn * 2, 0), // velocities List.filled(_pn, 0), // lifetimes List.filled(_pn, null), // points 0, // numPoints 0.4, // saturation 1.5, // spread 1.5, // range false // reciprocate ) for (i in 0..._pn) _pf.points[i] = Point.new(0, 0) }
init() { Canvas.cls() _frames = 0 }
updatePF() { var xidx = 0 var yidx = 1 var pointIdx = 0 var recip = Fn.new { _pf.reciprocate ? _pf.range * Math.sin(Platform.time/1000) : 0 } for (idx in 0..._pf.particleNum) { var willDraw = false if (_pf.lifetimes[idx] <= 0) { if (Rand.float() < _df) { _pf.lifetimes[idx] = 2.5 // time to live _pf.positions[xidx] = _w / 20 // starting position x _pf.positions[yidx] = _h / 10 // and y
// starting velocities x and y // randomized slightly so points reach different heights _pf.velocities[xidx] = 10 * (_pf.spread * Rand.float() - _pf.spread / 2 + recip.call()) _pf.velocities[yidx] = (Rand.float() - 2.9) * _h / 20.5 _willDraw = true } } else { if (_pf.positions[yidx] > _h/10 && _pf.velocities[yidx] > 0) { _pf.velocities[yidx] = _pf.velocities[yidx] * (-0.3) // bounce } _pf.velocities[yidx] = _pf.velocities[yidx] + _df * _h / 10 // adjust velocity _pf.positions[xidx] = _pf.positions[xidx] + _pf.velocities[xidx] * _df // adjust position x _pf.positions[yidx] = _pf.positions[yidx] + _pf.velocities[yidx] * _df // and y _pf.lifetimes[idx] = _pf.lifetimes[idx] - _df willDraw = true } if (willDraw) { // gather all the points that are going to be rendered _pf.points[pointIdx] = Point.new((_pf.positions[xidx] * 10).floor, (_pf.positions[yidx] * 10).floor) pointIdx = pointIdx + 1 } xidx = xidx + 2 yidx = xidx + 1 _pf.numPoints = pointIdx } }
update() { if (Keyboard["Up"].justPressed) { _pf.saturation = Math.min(_pf.saturation + 0.1, 1) } else if (Keyboard["Down"].justPressed) { _pf.saturation = Math.max(_pf.saturation - 0.1, 0) } else if (Keyboard["PageUp"].justPressed) { _pf.spread = Math.min(_pf.spread + 1, 50) } else if (Keyboard["PageDown"].justPressed) { _pf.spread = Math.max(_pf.spread - 0.1, 0.2) } else if (Keyboard["Left"].justPressed) { _pf.range = Math.min(_pf.range + 0.1, 12) } else if (Keyboard["Right"].justPressed) { _pf.range = Math.max(_pf.range - 0.1, 0.1) } else if (Keyboard["Space"].justPressed) { _pf.reciprocate = !_pf.reciprocate } else if (Keyboard["Q"].justPressed) { Process.exit() } updatePF() }
draw(alpha) { var c = Color.hsv((Platform.time % 5) * 72, _pf.saturation, 0.5, 0x7f) for (i in 0..._pf.numPoints) { Canvas.pset(_pf.points[i].x, _pf.points[i].y, c) } _frames = _frames + 1 var now = Platform.time if (now - Start >= 1) { Start = now Window.title = "Wren Particle System! (FPS = %(_frames))" _frames = 0 } }
}
System.print("""
Use UP and DOWN arrow keys to modify the saturation of the particle colors. Use PAGE UP and PAGE DOWN keys to modify the "spread" of the particles. Toggle reciprocation off / on with the SPACE bar. Use LEFT and RIGHT arrow keys to modify angle range for reciprocation. Press the "q" key to quit.
""")
var Game = ParticleDisplay.new(3000, 800, 800)</lang>