Particle fountain: Difference between revisions

New post.
(New post.)
 
(7 intermediate revisions by 4 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 755 ⟶ 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}}==
Line 945 ⟶ 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 1,084 ⟶ 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