diff options
| author | Nic Gaffney <gaffney_nic@protonmail.com> | 2024-06-12 17:21:10 -0500 | 
|---|---|---|
| committer | Nic Gaffney <gaffney_nic@protonmail.com> | 2024-06-12 17:21:10 -0500 | 
| commit | 6084001df845815efd9c0eb712acf4fd9311ce36 (patch) | |
| tree | 09a22b240b8d1739064d7d165730e3aeeec27cf2 /src | |
| parent | c7e504f40cca3ceee1734e6d0d9447c92d17b6b1 (diff) | |
| download | particle-sim-6084001df845815efd9c0eb712acf4fd9311ce36.tar.gz | |
Split up files
Diffstat (limited to 'src')
| -rw-r--r-- | src/config.zig | 18 | ||||
| -rw-r--r-- | src/main.zig | 160 | ||||
| -rw-r--r-- | src/particle.zig | 138 | 
3 files changed, 165 insertions, 151 deletions
| diff --git a/src/config.zig b/src/config.zig new file mode 100644 index 0000000..1f72ad4 --- /dev/null +++ b/src/config.zig @@ -0,0 +1,18 @@ +const rl = @import("raylib"); + +pub const screenWidth = 2560; +pub const screenHeight = 1440; +pub const particleMax = 4000; +pub const initialParticles = 3000; +pub const radius = 100.0; +pub const minDistance = 20.0; +pub const colors = [_]rl.Color{ +    rl.Color.red, +    rl.Color.green, +    rl.Color.blue, +    rl.Color.yellow, +    rl.Color.magenta, +    rl.Color.brown, +    rl.Color.orange, +}; +pub const colorAmnt = colors.len; diff --git a/src/main.zig b/src/main.zig index ec8e87c..47906e2 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,37 +1,13 @@  const std = @import("std");  const rl = @import("raylib"); - -const particle = struct { -    colorId: u32, -    attrs: particleAttrs, -    x: i32, -    y: i32, -    xvel: f32, -    yvel: f32, -}; - -const particleAttrs = struct {}; -const screenWidth = 2560; -const screenHeight = 1440; -const particleMax = 5000; -const radius = 100.0; -const minDistance = 20.0; -const colors = [_]rl.Color{ -    rl.Color.red, -    rl.Color.green, -    rl.Color.blue, -    rl.Color.yellow, -    rl.Color.magenta, -    rl.Color.brown, -    rl.Color.orange, -}; -const colorAmnt = colors.len; +const part = @import("particle.zig"); +const cfg = @import("config.zig");  pub fn main() !void { -    const rules = ruleMatrix(colors.len); -    printRules(rules); +    const rules = part.ruleMatrix(); +    part.printRules(rules); -    rl.initWindow(screenWidth, screenHeight, "Particle Simulator"); +    rl.initWindow(cfg.screenWidth, cfg.screenHeight, "Particle Simulator");      defer rl.closeWindow();      rl.setTargetFPS(60); @@ -39,7 +15,7 @@ pub fn main() !void {      var gpa = std.heap.GeneralPurposeAllocator(.{}){};      defer _ = gpa.deinit(); -    var particles = try initParticles(gpa.allocator(), 3000); +    var particles = try part.initParticles(gpa.allocator(), cfg.initialParticles);      defer particles.deinit(gpa.allocator());      while (!rl.windowShouldClose()) { @@ -48,128 +24,10 @@ pub fn main() !void {          defer rl.endDrawing(); -        updateVelocities(particles, rules); - -        for (particles.items(.y), particles.items(.yvel)) |*y, yvel| -            y.* = @mod(@as(i32, @intFromFloat(@round((@as(f32, @floatFromInt(y.*)) + yvel)))), screenHeight); - -        for (particles.items(.x), particles.items(.xvel)) |*x, xvel| -            x.* = @mod(@as(i32, @intFromFloat(@round((@as(f32, @floatFromInt(x.*)) + xvel)))), screenWidth); - -        for (particles.items(.y), particles.items(.x), particles.items(.colorId)) |*y, *x, colorId| -            rl.drawRectangle(x.*, y.*, 5, 5, colors[colorId]); +        part.updateVelocities(particles, rules); +        part.updatePosition(particles); +        part.draw(particles);          rl.clearBackground(rl.Color.black);      }  } - -fn colorToString(c: usize) []const u8 { -    return switch (c) { -        0 => "R", -        1 => "G", -        2 => "Bl", -        3 => "Y", -        4 => "M", -        5 => "Br", -        6 => "O", -        else => " ", -    }; -} - -fn printRules(rules: [colorAmnt][colorAmnt]f32) void { -    std.debug.print("\n|{s:^6}", .{"Rules"}); -    for (0..colors.len) |c| -        std.debug.print("| {s:^4} ", .{colorToString(c)}); - -    std.debug.print("|\n", .{}); -    for (rules, 0..) |row, i| { -        std.debug.print("| {s:^4} ", .{colorToString(i)}); -        for (row) |col| -            std.debug.print("| {d:^4.1} ", .{col}); - -        std.debug.print("|\n", .{}); -    } -} - -fn force(distance: f32, attraction: f32) f32 { -    const beta = minDistance / radius; -    const r: f32 = distance / radius; -    if (r < beta) -        return -(r / beta - 1.0); -    if (beta <= r and r < 1) -        return attraction * (1 - @abs(2.0 * r - 1.0 - beta) / (1.0 - beta)); -    return 0; -} - -fn updateVelocities(particles: std.MultiArrayList(particle), rules: [colorAmnt][colorAmnt]f32) void { -    const colorList = particles.items(.colorId); -    var xvel = particles.items(.xvel); -    var yvel = particles.items(.yvel); -    for (particles.items(.x), particles.items(.y), 0..) |x, y, i| { -        var forceX: f32 = 0.0; -        var forceY: f32 = 0.0; - -        for (particles.items(.x), particles.items(.y), 0..) |x2, y2, j| { -            if (i == j) continue; -            const rx: f32 = @floatFromInt(x - x2); -            const ry: f32 = @floatFromInt(y - y2); -            var r = @sqrt(rx * rx + ry * ry); -            if (r == 0) { -                r = 0.0001; -            } -            if (r > 0 and r < radius) { -                const f = force(r, rules[colorList[i]][colorList[j]]); -                forceX = forceX + rx / r * f; -                forceY = forceY + ry / r * f; -            } -        } - -        forceX = forceX * minDistance / radius; -        forceY = forceY * minDistance / radius; - -        xvel[i] = xvel[i] * 0.95 + forceX; -        yvel[i] = yvel[i] * 0.95 + forceY; -    } -} - -/// Generates a particle with a random Color and Location -pub fn createParticle(attrs: particleAttrs) particle { -    const seed = @as(u64, @truncate(@as(u128, @bitCast(std.time.nanoTimestamp())))); -    var prng = std.rand.DefaultPrng.init(seed); -    const x = prng.random().uintLessThan(u32, screenWidth); -    const y = prng.random().uintLessThan(u32, screenHeight); -    const color = prng.random().uintLessThan(u32, colorAmnt); -    return particle{ -        .colorId = color, -        .attrs = attrs, -        .x = @intCast(x), -        .y = @intCast(y), -        .xvel = 0, -        .yvel = 0, -    }; -} - -fn ruleMatrix(comptime size: u32) [size][size]f32 { -    const seed = @as(u64, @truncate(@as(u128, @bitCast(std.time.nanoTimestamp())))); -    var prng = std.rand.DefaultPrng.init(seed); -    var rules: [size][size]f32 = undefined; -    for (0..size) |i| { -        for (0..size) |j| { -            var val = prng.random().float(f32); -            const isNeg = prng.random().uintAtMost(u8, 1); -            if (isNeg == 1) val = 0 - val; -            rules[i][j] = val; -        } -    } -    return rules; -} - -/// Initialize a MultiArrayList of size amnt with particles created by createParticle -pub fn initParticles(allocator: std.mem.Allocator, amnt: u32) !std.MultiArrayList(particle) { -    var particles = std.MultiArrayList(particle){}; -    try particles.setCapacity(allocator, 10000); -    for (0..amnt) |_| { -        try particles.append(allocator, createParticle(.{})); -    } -    return particles; -} diff --git a/src/particle.zig b/src/particle.zig new file mode 100644 index 0000000..cb1a323 --- /dev/null +++ b/src/particle.zig @@ -0,0 +1,138 @@ +const cfg = @import("config.zig"); +const std = @import("std"); +const rl = @import("raylib"); + +/// Generate the set of rules the particles will abide by +pub fn ruleMatrix() [cfg.colorAmnt][cfg.colorAmnt]f32 { +    const seed = @as(u64, @truncate(@as(u128, @bitCast(std.time.nanoTimestamp())))); +    var prng = std.rand.DefaultPrng.init(seed); +    var rules: [cfg.colorAmnt][cfg.colorAmnt]f32 = undefined; +    for (0..cfg.colorAmnt) |i| { +        for (0..cfg.colorAmnt) |j| { +            var val = prng.random().float(f32); +            const isNeg = prng.random().uintAtMost(u8, 1); +            if (isNeg == 1) val = 0 - val; +            rules[i][j] = val; +        } +    } +    return rules; +} + +/// Prints rules generated from ruleMatrix() +pub fn printRules(rules: [cfg.colorAmnt][cfg.colorAmnt]f32) void { +    std.debug.print("\n|{s:^6}", .{"Rules"}); +    for (0..cfg.colors.len) |c| +        std.debug.print("| {s:^4} ", .{colorToString(c)}); + +    std.debug.print("|\n", .{}); +    for (rules, 0..) |row, i| { +        std.debug.print("| {s:^4} ", .{colorToString(i)}); +        for (row) |col| +            std.debug.print("| {d:^4.1} ", .{col}); + +        std.debug.print("|\n", .{}); +    } +} + +/// Initialize a MultiArrayList of size amnt with particles created by createParticle +pub fn initParticles(allocator: std.mem.Allocator, amnt: u32) !std.MultiArrayList(particle) { +    var particles = std.MultiArrayList(particle){}; +    try particles.setCapacity(allocator, cfg.particleMax); +    for (0..amnt) |_| { +        try particles.append(allocator, createParticle()); +    } +    return particles; +} + +/// Applies forces from the ruleset to each particle +pub fn updateVelocities(particles: std.MultiArrayList(particle), rules: [cfg.colorAmnt][cfg.colorAmnt]f32) void { +    const colorList = particles.items(.colorId); +    var xvel = particles.items(.xvel); +    var yvel = particles.items(.yvel); +    for (particles.items(.x), particles.items(.y), 0..) |x, y, i| { +        var forceX: f32 = 0.0; +        var forceY: f32 = 0.0; + +        for (particles.items(.x), particles.items(.y), 0..) |x2, y2, j| { +            if (i == j) continue; +            const rx: f32 = @floatFromInt(x - x2); +            const ry: f32 = @floatFromInt(y - y2); +            var r = @sqrt(rx * rx + ry * ry); +            if (r == 0) { +                r = 0.0001; +            } +            if (r > 0 and r < cfg.radius) { +                const f = force(r, rules[colorList[i]][colorList[j]]); +                forceX = forceX + rx / r * f; +                forceY = forceY + ry / r * f; +            } +        } + +        forceX = forceX * cfg.minDistance / cfg.radius; +        forceY = forceY * cfg.minDistance / cfg.radius; + +        xvel[i] = xvel[i] * 0.95 + forceX; +        yvel[i] = yvel[i] * 0.95 + forceY; +    } +} + +/// Applies the particles velocity and updates position +pub fn updatePosition(particles: std.MultiArrayList(particle)) void { +    for (particles.items(.y), particles.items(.yvel)) |*y, yvel| +        y.* = @mod(@as(i32, @intFromFloat(@round((@as(f32, @floatFromInt(y.*)) + yvel)))), cfg.screenHeight); + +    for (particles.items(.x), particles.items(.xvel)) |*x, xvel| +        x.* = @mod(@as(i32, @intFromFloat(@round((@as(f32, @floatFromInt(x.*)) + xvel)))), cfg.screenWidth); +} + +/// Draw the particles onto the screen using raylib +pub fn draw(particles: std.MultiArrayList(particle)) void { +    for (particles.items(.y), particles.items(.x), particles.items(.colorId)) |*y, *x, colorId| +        rl.drawRectangle(x.*, y.*, 5, 5, cfg.colors[colorId]); +} + +const particle = struct { +    colorId: u32, +    x: i32, +    y: i32, +    xvel: f32, +    yvel: f32, +}; + +fn colorToString(c: usize) []const u8 { +    return switch (c) { +        0 => "R", +        1 => "G", +        2 => "Bl", +        3 => "Y", +        4 => "M", +        5 => "Br", +        6 => "O", +        else => " ", +    }; +} + +fn force(distance: f32, attraction: f32) f32 { +    const beta = cfg.minDistance / cfg.radius; +    const r: f32 = distance / cfg.radius; +    if (r < beta) +        return -(r / beta - 1.0); +    if (beta <= r and r < 1) +        return attraction * (1 - @abs(2.0 * r - 1.0 - beta) / (1.0 - beta)); +    return 0; +} + +fn createParticle() particle { +    const seed = @as(u64, @truncate(@as(u128, @bitCast(std.time.nanoTimestamp())))); +    var prng = std.rand.DefaultPrng.init(seed); +    const x = prng.random().uintLessThan(u32, cfg.screenWidth); +    const y = prng.random().uintLessThan(u32, cfg.screenHeight); +    const color = prng.random().uintLessThan(u32, cfg.colorAmnt); +    return particle{ +        .colorId = color, +        .x = @intCast(x), +        .y = @intCast(y), +        .xvel = 0, +        .yvel = 0, +    }; +} | 
