-
Notifications
You must be signed in to change notification settings - Fork 1
/
metaballs.zig
93 lines (80 loc) · 3.11 KB
/
metaballs.zig
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
//! These are metaballs, not to be confused with meatballs: <https://en.wikipedia.org/wiki/Metaballs>.
//! For brevity we will call them balls in this file.
const std = @import("std");
const builtin = @import("builtin");
const soft = @import("soft");
const backend = @import("root"); // TODO: https://github.com/ziglang/zig/issues/14708: @import("backend");
const Grid = backend.Grid;
const grid = &backend.grid;
const Color = Grid.Cell;
const Ball = struct {
x: isize,
y: isize,
dx: isize,
dy: isize,
};
/// The balls. Adjust the length for more or less balls!
var balls: [10]Ball = undefined;
/// The smaller this value, the larger the balls.
/// Note that radii individual to balls are not possible.
const ball_threshold: f32 = 100;
/// This adds a distinctly visible border made of horizontal lines around the balls.
/// Make this 0 to turn it off.
const ball_border_radius: f32 = 25;
pub fn init() !void {
var prng = std.rand.DefaultPrng.init(backend.seed);
const random = prng.random();
for (&balls) |*ball| {
ball.x = random.intRangeAtMost(isize, 0, @as(isize, @intCast(grid.width)));
ball.y = random.intRangeAtMost(isize, 0, @as(isize, @intCast(grid.height)));
ball.dx = if (random.boolean()) -1 else 1;
ball.dy = if (random.boolean()) -1 else 1;
}
}
pub const clear_color = Color.white;
pub fn tick(time: anytype) !void {
// move the balls
for (&balls) |*ball| {
if (ball.x + ball.dx >= grid.width or ball.x + ball.dx < 0)
ball.dx *= -1;
if (ball.y + ball.dy >= grid.height or ball.y + ball.dy < 0)
ball.dy *= -1;
// TODO: time.mul?
ball.x += ball.dx;
ball.y += ball.dy;
}
const fluctuation = 15;
const fluctuation_speed = 5;
const size = @as(f32, @floatFromInt(grid.width * grid.height));
const current_ball_threshold = @as(f32, @floatCast((ball_threshold + @sin(time.elapsed * fluctuation_speed) * fluctuation) / size));
const current_ball_border_radius = @as(f32, @floatCast((ball_border_radius + @sin(time.elapsed * fluctuation_speed) * fluctuation) / size));
var x: isize = 0;
while (x < grid.width) : (x += 1) {
var y: isize = 0;
while (y < grid.height) : (y += 1) {
var sum: f32 = 0;
for (balls) |ball| {
sum += 1 / @as(
f32,
@floatFromInt((x - ball.x) * (x - ball.x) + (y - ball.y) * (y - ball.y)),
);
}
const current_threshold: f32 =
if (@rem(x, 2) == 0)
current_ball_threshold
else
current_ball_threshold + current_ball_border_radius;
if (sum > current_threshold) {
grid.set(
x,
y,
Color.rgb(
@as(f32, @floatFromInt(x)) / @as(f32, @floatFromInt(grid.width)),
@as(f32, @floatFromInt(y)) / @as(f32, @floatFromInt(grid.height)),
@fabs(@sin(time.elapsed)),
),
);
}
}
}
}