Particle fountain: Difference between revisions

New post.
m (syntax highlighting fixup automation)
(New post.)
 
(13 intermediate revisions by 7 users not shown)
Line 255:
return EXIT_SUCCESS;
}</syntaxhighlight>
 
=={{header|EasyLang}}==
[https://easylang.dev/show/#cod=ZVHLDsIgELzzFZN48RErVKtpjF9iOGCphthCQrSxf+8uttVEDrDMzO4ObDQWJ8hM5YXwFO2llGIG0dQer7OGxxEc9xwntPuFuw9+MdX9FsPTW+wOpahCEyLKshTBw3jXmkctAFwJddRE4RGwzRmi5TxbmPOxglqgDVZRzQ/5OhOuiS8ksdF4G9rrwPUj57DBbgC7KWM+qLGm9xULLOmYVFNu/i2bFIoVGW9VU5v4Z3s01oauZneabegBrFykLKpnv/Y1VqfkStOf9eP9JynFWPMUJE0htc/EGw== Run it]
 
<syntaxhighlight>
rad = 0.125
n = 6000
#
len x[] n ; len y[] n
len vx[] n ; len vy[] n
background 479
color 999
on animate
for i = 1 to 32
ind = (ind + 1) mod1 n
x[ind] = 50 + randomf
y[ind] = i / 4
vx[ind] = (randomf - 0.5) * 0.4
vy[ind] = 2 + randomf * 0.1
.
clear
for i = 1 to n
move x[i] y[i]
circle rad
x[i] += vx[i] ; y[i] += vy[i]
vy[i] -= 0.025
.
.
</syntaxhighlight>
 
=={{header|Java}}==
<syntaxhighlight lang="java">
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.image.BufferStrategy;
import java.util.Arrays;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadLocalRandom;
 
import javax.swing.JFrame;
 
public final class ParticleFountainTask {
 
public static void main(String[] args) {
EventQueue.invokeLater( () -> {
JFrame.setDefaultLookAndFeelDecorated(true);
JFrame frame = new JFrame("Particle Fountain");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
ParticleFountain particleFountain = new ParticleFountain(3_000, 1_000, 750);
frame.add(particleFountain);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
particleFountain.start();
} );
}
 
private static final class ParticleFountain extends Canvas {
 
public ParticleFountain(int aParticleCount, int aWidth, int aHeight) {
particleCount = aParticleCount;
width = aWidth;
height = aHeight;
saturation = 0.6;
spread = 1.5;
range = 1.5;
reciprocate = false;
setPreferredSize( new Dimension(width, height) );
addKeyListener( new InputHandler() );
executorService = Executors.newSingleThreadExecutor();
}
 
public void start() {
requestFocus();
createBufferStrategy(2);
executorService.execute( new DrawingCycle() );
}
 
private final class DrawingCycle implements Runnable {
public DrawingCycle() {
positions = new double[2 * particleCount];
velocities = new double[2 * particleCount];
lifetimes = new double[particleCount];
points = new Point[particleCount];
Arrays.fill(points, new Point(0, 0) );
random = ThreadLocalRandom.current();
}
 
@Override
public void run() {
bufferStrategy = getBufferStrategy();
while ( true ) {
update(0.005);
draw();
}
}
 
private void update(double animationSpeed) {
int xIndex = 0;
int yIndex = 1;
pointIndex = 0;
 
for ( int index = 0; index < particleCount; index++ ) {
boolean showParticle = false;
if ( lifetimes[index] <= 0.0 ) {
if ( random.nextDouble() < animationSpeed ) {
lifetimes[index] = 2.5;
positions[xIndex] = width / 20;
positions[yIndex] = height / 10;
velocities[xIndex] =
10 * ( spread * random.nextDouble() - spread / 2 + additionalXSpeed() );
velocities[yIndex] = ( random.nextDouble() - 2.9 ) * height / 20.5;
showParticle = true;
}
} else {
if ( positions[yIndex] > height / 10 && velocities[yIndex] > 0 ) {
velocities[yIndex] *= -0.3; // bounce particle
}
velocities[yIndex] += animationSpeed * height / 10;
positions[xIndex] += velocities[xIndex] * animationSpeed;
positions[yIndex] += velocities[yIndex] * animationSpeed;
lifetimes[index] -= animationSpeed;
showParticle = true;
}
 
if ( showParticle ) {
points[pointIndex] = new Point((int) ( positions[xIndex] * 10 ),
(int) ( positions[yIndex] * 10 ));
pointIndex += 1;
}
xIndex += 2;
yIndex = xIndex + 1;
}
}
 
private void draw() {
Graphics2D graphics2D = (Graphics2D) bufferStrategy.getDrawGraphics();
graphics2D.setColor(Color.BLACK);
graphics2D.fillRect(0, 0, getWidth(), getHeight());
for ( int i = 0; i < pointIndex; i++ ) {
graphics2D.setColor(Color.getHSBColor(random.nextFloat(), (float) saturation, 1.0F));
graphics2D.fillOval(points[i].x, points[i].y, 5, 5);
}
graphics2D.dispose();
bufferStrategy.show();
}
private double additionalXSpeed() {
return ( reciprocate ) ? range * Math.sin(System.currentTimeMillis() / 1_000) : 0.0;
}
private double[] positions;
private double[] velocities;
private double[] lifetimes;
private int pointIndex;
private Point[] points;
private BufferStrategy bufferStrategy;
private ThreadLocalRandom random;
 
} // End DrawingCycle class
private final class InputHandler extends KeyAdapter {
@Override
public void keyPressed(KeyEvent aKeyEvent) {
final int keyCode = aKeyEvent.getKeyCode();
switch ( keyCode ) {
case KeyEvent.VK_UP -> saturation = Math.min(saturation + 0.1, 1.0);
case KeyEvent.VK_DOWN -> saturation = Math.max(saturation - 0.1, 0.0);
case KeyEvent.VK_PAGE_UP -> spread = Math.min(spread + 0.1, 5.0);
case KeyEvent.VK_PAGE_DOWN -> spread = Math.max(spread - 0.1, 0.5);
case KeyEvent.VK_RIGHT -> range = Math.min(range + 0.1, 2.0);
case KeyEvent.VK_LEFT -> range = Math.max(range + 0.1, 0.1);
case KeyEvent.VK_SPACE -> reciprocate = ! reciprocate;
case KeyEvent.VK_Q -> Runtime.getRuntime().exit(0);
default -> { /* Take no action */ }
}
}
} // End InputHandler class
private int particleCount;
private int width;
private int height;
private double saturation;
private double spread;
private double range;
private boolean reciprocate;
private ExecutorService executorService;
} // End ParticleFountain class
 
} // End ParticleFountainTask class
</syntaxhighlight>
 
=={{header|Julia}}==
Line 383 ⟶ 594:
""")
 
fountain()
</syntaxhighlight>
 
=={{header|Lua}}==
{{libheader|LÖVE}}
[https://github.com/offlinesoftware/luafountain/blob/main/fountain.m4v?raw=true Video is here]
<syntaxhighlight lang="lua">-- Returns canvas of given width and height containing a circle
function initCanvas (width, height)
local c = love.graphics.newCanvas(width, height)
love.graphics.setCanvas(c) -- Switch to drawing on canvas 'c'
love.graphics.circle("fill", width / 2, height / 2, 2, 100)
love.graphics.setCanvas() -- Switch back to drawing on main screen
return c
end
 
-- Returns particle system with given canvas
function initPartSys (image, maxParticles)
local ps = love.graphics.newParticleSystem(image, maxParticles)
ps:setParticleLifetime(3, 5) -- (min, max)
ps:setDirection(math.pi * 1.5)
ps:setSpeed(700)
ps:setLinearAcceleration(-100, 500, 100, 700) -- (minX, minY, maxX, maxY)
ps:setEmissionRate(1000)
ps:setPosition(400, 550)
ps:setColors(1, 1, 1, 1, 0, 0, 1, 0) -- Start solid white, fade to transluscent blue
return ps
end
 
-- LÖVE callback that runs on program start
function love.load ()
love.window.setTitle("Lua particle fountain")
local canvas = initCanvas(10, 10)
psystem = initPartSys(canvas, 10000)
end
 
-- LÖVE callback to update values before each frame
function love.update (dt)
psystem:update(dt)
end
 
-- LÖVE callback to draw each frame to the screen
function love.draw ()
love.graphics.draw(psystem)
end</syntaxhighlight>
 
=={{header|Nim}}==
{{trans|Julia}}
{{libheader|SDL2}}
Note that for key events, the scan code doesn’t take in account the keyboard layout. So we check the "sym" value instead.
<syntaxhighlight lang="Nim">import std/[lenientops, math, monotimes, random, times]
import sdl2
 
type ParticleFountain[N: static Positive] = object
positions: array[1..2 * N, float]
velocities: array[1..2 * N, float]
lifetimes: array[1..N, float]
points: array[1..N, Point]
numPoints: int
saturation: float
spread: float
range: float
reciprocate: bool
 
proc initParticleFountain[N: static Positive](): ParticleFountain[N] =
ParticleFountain[N](saturation: 0.4, spread: 1.5, range: 1.5)
 
proc update(pf: var ParticleFountain; w, h: cint; df: float) =
var
xidx = 1
yidx = 2
pointidx = 0
 
template recip(pf: ParticleFountain): float =
if pf.reciprocate: pf.range * sin(epochTime() / 1000) else: 0.0
 
for idx in 1..pf.N:
var willDraw = false
if pf.lifetimes[idx] <= 0:
if rand(1.0) < df:
pf.lifetimes[idx] = 2.5 # Time to live.
# Starting position.
pf.positions[xidx] = w / 20
pf.positions[yidx] = h / 10
# Starting velocity.
pf.velocities[xidx] = 10 * (pf.spread * rand(1.0) - pf.spread / 2 + pf.recip())
pf.velocities[yidx] = (rand(1.0) - 2.9) * h / 20.5
willDraw = true
else:
if pf.positions[yidx] > h / 10 and pf.velocities[yidx] > 0:
pf.velocities[yidx] *= -0.3 # "Bounce".
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 # Adjust position y.
pf.lifetimes[idx] -= df
willDraw = true
 
if willDraw:
# Gather all of the points that are going to be rendered.
inc pointIdx
pf.points[pointidx] = (cint(pf.positions[xidx] * 10), cint(pf.positions[yidx] * 10))
inc xidx, 2
yidx = xidx + 1
pf.numPoints = pointidx
 
func hsvToRgb(h, s, v: float): (byte, byte, byte) =
let hp = h / 60.0
let c = s * v
let x = c * (1 - abs(hp mod 2 - 1))
let m = v - c
var (r, g, b) = if hp <= 1: (c, x, 0.0)
elif hp <= 2: (x, c, 0.0)
elif hp <= 3: (0.0, c, x)
elif hp <= 4: (0.0, x, c)
elif hp <= 5: (x, 0.0, c)
else: (c, 0.0, x)
r += m
g += m
b += m
result = (byte(r * 255), byte(g * 255), byte(b * 255))
 
proc fountain(particleNum = 3000; w = 800; h = 800) =
var w = w.cint
var h = h.cint
discard sdl2.init(INIT_VIDEO or INIT_EVENTS)
let window = createWindow("Nim Particle System!", SDL_WINDOWPOS_CENTERED_MASK,
SDL_WINDOWPOS_CENTERED_MASK, w, h, SDL_WINDOW_RESIZABLE)
let renderer = createRenderer(window, -1, 0)
clearError()
var df = 0.0001
var pf = initParticleFountain[3000]()
var close = false
var frames = 0
block Simulation:
while not close:
let dfStart = getMonoTime()
var event: Event
while bool(pollEvent(event)):
case event.kind
of QuitEvent:
break Simulation
of WindowEvent:
if event.window.event == WindowEvent_Resized:
w = event.window.data1
h = event.window.data2
of KeyDown:
let comm = event.key.keysym.sym
case comm
of K_UP:
pf.saturation = min(pf.saturation + 0.1, 1.0)
of K_DOWN:
pf.saturation = max(pf.saturation - 0.1, 0.0)
of K_PAGEUP:
pf.spread = min(pf.spread + 1.0, 50.0)
of K_PAGEDOWN:
pf.spread = max(pf.spread - 0.1, 0.2)
of K_LEFT:
pf.range = min(pf.range + 0.1, 12.0)
of K_RIGHT:
pf.range = max(pf.range - 0.1, 0.1)
of K_SPACE:
pf.reciprocate = not pf.reciprocate
of K_Q:
break Simulation
else:
discard
else:
discard
 
pf.update(w, h, df)
renderer.setDrawColor(0x0, 0x0, 0x0, 0xff)
renderer.clear()
let (red, green, blue) = hsvToRgb(epochTime() mod 5 * 72, pf.saturation, 1.0)
renderer.setDrawColor(red, green, blue, 0x7f)
renderer.drawPoints(pf.points[1].addr, pf.numPoints.cint)
renderer.present()
inc frames
df = (getMonoTime() - dfStart).inMilliseconds.float / 1000
 
sdl2.quit()
 
randomize()
echo """
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()
</syntaxhighlight>
Line 567 ⟶ 966:
<span style="color: #000000;">main</span><span style="color: #0000FF;">()</span>
<!--</syntaxhighlight>-->
 
=={{header|Python}}==
{{libheader|SDL}}
{{trans|C++}}
 
Use arrow keys, PageUp, PageDown and Space to vary fountain parameters.
<syntaxhighlight lang="python">
# Using SDL2 library: # pip install PySDL2
 
import sys
import random
import time
import math
 
import sdl2
import sdl2.ext
 
FPS = 60
NEW_PARTICLES_PER_FRAME = 10
MAX_PARTICLES = 5_000
GRAVITY = 100
WIDTH = 640
HEIGHT = 480
 
 
def clamp(value, min_, max_):
"""Return value clamped between min and max"""
return max(min_, min(value, max_))
 
 
class Particle:
"""Particle obeying gravity law."""
 
def __init__(self):
self.x = 0
self.y = 0
self.v_x = 0
self.v_y = 0
 
def update(self, dtime: float) -> None:
"""Move particle and update speed with gravity"""
self.x = self.x + self.v_x * dtime
self.y = self.y + self.v_y * dtime
self.v_y = self.v_y + GRAVITY * dtime
 
def set(self, x, y, v_x, v_y):
"""Set particle values"""
self.x = x
self.y = y
self.v_x = v_x
self.v_y = v_y
 
 
class Fountain:
"""The fountain"""
 
def __init__(self, max_particles: int, particles_per_frame: int):
self.particles_per_frame = particles_per_frame
self.max_particles = max_particles
self.spread = 10.0
self.range = math.sqrt(2 * GRAVITY * (HEIGHT - 20 - self.spread))
self.saturation = 155
self.reciprocate = False
self.reciprocating_time = 0.0
self.particles = [
self.init_particle(Particle()) for _ in range(self.particles_per_frame)
]
 
def update(self, dtime) -> None:
"""Update particles"""
if self.reciprocate:
self.reciprocating_time += dtime
 
for particle in self.particles:
particle.update(dtime)
if particle.y > HEIGHT - 10:
self.init_particle(particle)
 
if len(self.particles) < self.max_particles:
for _ in range(self.particles_per_frame):
self.particles.append(self.init_particle(Particle()))
# print(len(particles))
 
def render(self, renderer: sdl2.ext.renderer.Renderer) -> None:
"""Render particles"""
points = [(particle.x, particle.y) for particle in self.particles]
 
renderer.clear()
renderer.draw_point(
points, sdl2.ext.Color(self.saturation, self.saturation, 255)
)
renderer.present()
 
def step_parameter(self, param, step):
"""Change parameters"""
if param == "spread":
self.spread = clamp(self.spread + step, 0, 50)
elif param == "range":
self.range = clamp(self.range + step, 0, 300)
elif param == "color":
self.saturation = clamp(self.saturation + step, 0, 255)
elif param == "reciprocate":
self.reciprocate = not self.reciprocate
self.reciprocating_time = 0.0
 
def init_particle(self, particle: Particle) -> Particle:
"""Move particle at initial position with a random-y speed"""
radius = random.random() * self.spread
direction = random.random() * math.pi * 2
v_x = radius * math.cos(direction) + math.sin(self.reciprocating_time) * 20.0
v_y = -self.range + radius * math.sin(direction)
particle.set(WIDTH // 2, HEIGHT - 10, v_x, v_y)
return particle
 
 
def make_renderer() -> sdl2.ext.renderer.Renderer:
"""Initialise SDL and make renderer"""
sdl2.ext.init()
 
window = sdl2.ext.Window("Particle Fountain", size=(WIDTH, HEIGHT))
window.show()
 
renderer = sdl2.ext.renderer.Renderer(window)
 
return renderer
 
 
def limit_frame_rate(fps: float, cur_time: int) -> bool:
"""Limit frame rate"""
dtime = time.monotonic_ns() - cur_time
frame_duration = 1e9 / fps
if dtime < frame_duration:
time.sleep((frame_duration - dtime) / 1e9)
return True
return False
 
 
def handle_events(fountain: Fountain):
"""Act on events"""
key_actions = {
sdl2.SDL_SCANCODE_PAGEUP: lambda: fountain.step_parameter("color", 5),
sdl2.SDL_SCANCODE_PAGEDOWN: lambda: fountain.step_parameter("color", -5),
sdl2.SDL_SCANCODE_UP: lambda: fountain.step_parameter("range", 1),
sdl2.SDL_SCANCODE_DOWN: lambda: fountain.step_parameter("range", -1),
sdl2.SDL_SCANCODE_LEFT: lambda: fountain.step_parameter("spread", -1),
sdl2.SDL_SCANCODE_RIGHT: lambda: fountain.step_parameter("spread", 1),
sdl2.SDL_SCANCODE_SPACE: lambda: fountain.step_parameter("reciprocate", 1),
}
 
events = sdl2.ext.get_events()
for event in events:
if event.type == sdl2.SDL_QUIT:
return False
if event.type == sdl2.SDL_KEYDOWN:
if event.key.keysym.scancode in key_actions:
key_actions[event.key.keysym.scancode]()
elif event.key.keysym.scancode == sdl2.SDL_SCANCODE_Q:
return False
return True
 
 
def main_loop(renderer: sdl2.ext.renderer.Renderer, fountain: Fountain) -> None:
"""Main animation loop"""
running = True
 
cur_time = time.monotonic_ns()
while running:
running = handle_events(fountain)
 
fountain.render(renderer)
 
if not limit_frame_rate(FPS, cur_time):
print(f"Didn't make it in time with {len(fountain.particles)} particles.")
 
dtime = (time.monotonic_ns() - cur_time) / 1e9 # in seconds
fountain.update(dtime)
cur_time = time.monotonic_ns()
 
sdl2.ext.quit()
 
 
def run():
"""Start!"""
 
renderer = make_renderer()
fountain = Fountain(MAX_PARTICLES, NEW_PARTICLES_PER_FRAME)
 
main_loop(renderer, fountain)
 
return 0
 
 
if __name__ == "__main__":
sys.exit(run())
 
</syntaxhighlight>
 
=={{header|Raku}}==
 
Has options to vary the direction at which the fountain sprays, the "spread" angle and the color of the emitted particles.
<syntaxhighlight lang="raku" line>use NativeCall;
use NativeCallSDL2::Raw;
 
use SDL2::Raw;
my int ($w, $h) = 800, 800;
my SDL_Window $window;
my int ($w, $h) = 800, 800;
my SDL_WindowSDL_Renderer $windowrenderer;
 
my SDL_Renderer $renderer;
my int $particlenum = 3000;
 
my int $particlenum = 3000;
 
SDL_Init(VIDEO);
$window = SDL_CreateWindow(
SDL_Init(VIDEO);
"Raku Particle System!",
$window = SDL_CreateWindow(
SDL_WINDOWPOS_CENTERED_MASK, SDL_WINDOWPOS_CENTERED_MASK,
"Raku Particle System!",
$w, $h,
SDL_WINDOWPOS_CENTERED_MASK, SDL_WINDOWPOS_CENTERED_MASK,
$w, $h,RESIZABLE
);
RESIZABLE
$renderer = SDL_CreateRenderer( $window, -1, ACCELERATED );
);
 
$renderer = SDL_CreateRenderer( $window, -1, ACCELERATED );
SDL_ClearError();
 
SDL_ClearError();
my num @positions = 0e0 xx ($particlenum * 2);
my num @positions velocities = 0e0 xx ($particlenum * 2);
my num @velocitieslifetimes = 0e0 xx ( $particlenum * 2);
 
my num @lifetimes = 0e0 xx $particlenum;
my CArray[int32] $points .= new;
my int $numpoints;
my CArray[int32] $points .= new;
my intNum $numpointssaturation = 4e-1;
my Num $saturationspread = 4e15e-1;
my Num $spread&reciprocate = 15e-1;sub { 0 }
my &reciprocate$range = sub { 0 }1.5;
 
my $range = 1.5;
sub update (num \df) {
my int $xidx = 0;
sub update (num \df) {
my int $xidxyidx = 01;
my int $yidxpointidx = 10;
loop (my int $pointidxidx = 0; $idx < $particlenum; $idx = $idx + 1) {
loop ( my int $idxwilldraw = 0; $idx < $particlenum; $idx = $idx + 1) {
if my int (@lifetimes[$willdrawidx] <= 0;0e0) {
if (@lifetimes[$idx]rand <= 0e0df) {
@lifetimes[$idx] = 25e-1; # time to live
if (rand < df) {
@lifetimespositions[$idxxidx] = 25e-1; ($w / 20e0).Num; # timestarting toposition livex
@positions[$xidxyidx] = ($wh / 20e010).Num; # starting position# and xy
@positionsvelocities[$yidxxidx] = ($hspread /* 10).Num;rand - $spread/2 + reciprocate()) * 10; # starting velocity # and yx
@velocities[$xidxyidx] = ($spread * rand - $spread/2 + reciprocate().9e0) * 10$h / 20.5; # startingand velocityy x(randomized slightly so points reach different heights)
$willdraw = 1;
@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 {
} else {
if @positionsvelocities[$yidx] > $h / 10 &&= @velocities[$yidx] >* -0.3e0; # {"bounce"
}
@velocities[$yidx] = @velocities[$yidx] * -0.3e0; # "bounce"
 
}
@velocities[$yidx] = @velocities[$yidx] + $h/10.Num * df; # adjust velocity
@velocitiespositions[$yidxxidx] = @velocitiespositions[$yidxxidx] + @velocities[$h/10.Numxidx] * df; # adjust velocityposition x
@positions[$xidxyidx] = @positions[$xidxyidx] + @velocities[$xidxyidx] * df; # adjust positionand xy
 
@positions[$yidx] = @positions[$yidx] + @velocities[$yidx] * df; # and y
@lifetimes[$idx] = @lifetimes[$idx] - df;
@lifetimes[$idx] willdraw = @lifetimes[$idx] - df1;
$willdraw = 1;}
 
}
if ($willdraw) {
$points[$pointidx++] = (@positions[$xidx] * 10).floor; # gather all of the points that
if ($willdraw) {
$points[$pointidx++] = (@positions[$xidxyidx] * 10).floor; # gatherare allstill ofgoing theto pointsbe thatrendered
}
$points[$pointidx++] = (@positions[$yidx] * 10).floor; # are still going to be rendered
 
}
$xidx = $xidx + 2;
$xidxyidx = $xidx + 21;
}
$yidx = $xidx + 1;
$numpoints = ($pointidx - 1) div 2;
}
}
$numpoints = ($pointidx - 1) div 2;
 
}
sub render {
SDL_SetRenderDrawColor($renderer, 0x0, 0x0, 0x0, 0xff);
sub render {
SDL_SetRenderDrawColorSDL_RenderClear($renderer, 0x0, 0x0, 0x0, 0xff);
 
SDL_RenderClear($renderer);
SDL_SetRenderDrawColor($renderer, |hsv2rgb(((now % 5) / 5).round(.01), $saturation, 1), 0x7f);
SDL_SetRenderDrawColorSDL_RenderDrawPoints($renderer, |hsv2rgb(((now % 5) / 5).round(.01)$points, $saturation, 1), 0x7fnumpoints);
 
SDL_RenderDrawPoints($renderer, $points, $numpoints);
SDL_RenderPresent($renderer);
}
SDL_RenderPresent($renderer);
 
}
enum KEY_CODES (
K_UP => 82,
enum KEY_CODES (
K_UP K_DOWN => 8281,
K_DOWNK_LEFT => 8180,
K_LEFT K_RIGHT => 8079,
K_RIGHTK_SPACE => 7944,
K_PGUP K_SPACE => 4475,
K_PGUPK_PGDN => 7578,
K_Q K_PGDN => 7820,
);
K_Q => 20,
 
);
say q:to/DOCS/;
Use UP and DOWN arrow keys to modify the saturation of the particle colors.
say q:to/DOCS/;
Use PAGE UP and PAGE DOWN arrow keys to modify the saturation"spread" of the particle colorsparticles.
Toggle reciprocation off / on with the SPACE bar.
Use PAGE UP and PAGE DOWN keys to modify the "spread" of the particles.
Use LEFT and RIGHT arrow keys to modify angle range for reciprocation.
Toggle reciprocation off / on with the SPACE bar.
Press the "q" key to quit.
Use LEFT and RIGHT arrow keys to modify angle range for reciprocation.
DOCS
Press the "q" key to quit.
 
DOCS
my $event = SDL_Event.new;
 
my $event = SDL_Event.new;
my num $df = 0.0001e0;
 
my num $df = 0.0001e0;
main: loop {
my $start = now;
main: loop {
 
my $start = now;
while SDL_PollEvent($event) {
while SDL_PollEvent my $casted_event = SDL_CastEvent($event) {;
 
my $casted_event = SDL_CastEvent($event);
given $casted_event {
given $casted_event when *.type == QUIT {
when *.type == QUITlast {main;
last main;}
when }*.type == WINDOWEVENT {
when * if .typeevent == WINDOWEVENTRESIZED {
if .event == RESIZED$w {= .data1;
$wh = .data1data2;
$h = .data2;}
}
when }*.type == KEYDOWN {
when * if KEY_CODES(.typescancode) ==-> KEYDOWN$comm {
if KEY_CODES(.scancode) -> given $comm {
given $comm when 'K_UP' { $saturation = (($saturation + .1) min 1e0) }
when 'K_UPK_DOWN' { $saturation = (($saturation +- .1) minmax 1e00e0) }
when 'K_DOWNK_PGUP' { $saturationspread = (($saturationspread -+ .1) maxmin 0e05e0) }
when 'K_PGUPK_PGDN' { $spread = (($spread +- .1) minmax 5e02e-1) }
when 'K_PGDNK_RIGHT' { $spreadrange = (($spreadrange -+ .1) maxmin 2e-12e0) }
when 'K_RIGHTK_LEFT' { $range = (($range +- .1) minmax 2e01e-1) }
when 'K_LEFTK_SPACE' { $range&reciprocate = reciprocate(() == 0 ?? sub { $range -* .1sin(now) max} 1e-1)!! sub { 0 } }
when 'K_SPACEK_Q' { &reciprocate = reciprocate() == 0 ?? sub { $range * sin(now) } !! sub { 0last }main }
when 'K_Q' { last main }
}
}
}
}
 
}
update($df);
 
update($df);
render();
 
render();
$df = (now - $start).Num;
 
$df = (now - $start).Num;
print fps();
}
print fps();
 
}
say '';
 
say '';
sub fps {
state $fps-frames = 0;
sub fps {
state $fps-framesnow = 0now;
state $fps-now = now'';
state $fps = ''-frames++;
if now - $fps-frames++;now >= 1 {
if now - $fps-now >= 1[~] {"\r", ' ' x 20, "\r",
$fps = [~] sprintf "\rFPS: %5.1f ", '($fps-frames '/ x(now 20,- "\r",$fps-now));
sprintf "FPS: %5.1f ", ($fps-frames /= (now - $fps-now))0;
$fps-framesnow = 0now;
}
$fps-now = now;
}$fps
}
$fps
 
}
sub hsv2rgb ( $h, $s, $v ){
state %cache;
sub hsv2rgb ( $h, $s, $v ){
state %cache;{"$h|$s|$v"} //= do {
%cache{"$h| my $s|$v"}c //= do$v {* $s;
my $cx = $vc * (1 - abs( (($sh*6) % 2) - 1 ) );
my $xm = $c * (1v - abs( (($h*6) % 2) - 1 ) )c;
[(do mygiven $mh = $v - $c;{
[(do given $h when 0..^1/6 { $c, $x, 0 }
when 01/6..^1/63 { $cx, $xc, 0 }
when 1/63..^1/32 { $x0, $c, 0$x }
when 1/32..^12/23 { 0, $cx, $xc }
when 12/23..^25/36 { 0$x, $x0, $c }
when 2/3..^5/6..1 { $xc, 0, $cx }
} when 5/6)..1 {map: ((*+$c,m) 0,* $x }255).Int]
}
} ).map: ((*+$m) * 255).Int]
}
}
}
</syntaxhighlight>
 
Line 758 ⟶ 1,352:
{{libheader|DOME}}
{{libheader|Wren-dynamic}}
<syntaxhighlight lang="ecmascriptwren">import "dome" for Window, Platform, Process
import "graphics" for Canvas, Color
import "math" for Math, Point
Line 897 ⟶ 1,491:
 
var Game = ParticleDisplay.new(3000, 800, 800)</syntaxhighlight>
 
=={{header|XPL0}}==
{{trans|EasyLang}}
[[File:XPL0_Fountain.gif|right]]
<syntaxhighlight lang "XPL0">func real RandomF;
return float(Ran(1000)) / 1000.;
 
def N = 6000;
real X(N), Y(N);
real VX(N), VY(N);
def Color = 15;
int I, Ind;
[SetVid($12);
Ind:= 0;
repeat
for I:= 1 to 32 do
[Ind:= rem((Ind+1)/N);
X(Ind):= 50. + RandomF;
Y(Ind):= float(I)/4.;
VX(Ind):= (RandomF - 0.5) * 0.4;
VY(Ind):= 2. + RandomF*0.1;
];
WaitForVSync;
Clear;
for I:= 0 to N-1 do
[Point(fix(X(I)), 480-fix(Y(I)), Color);
X(I):= X(I) + VX(I); Y(I):= Y(I) + VY(I);
VY(I):= VY(I) - 0.025;
];
until KeyHit;
]</syntaxhighlight>
871

edits