sandpiles

sandpile art
git clone git://git.rr3.xyz/sandpiles
Log | Files | Refs | README | LICENSE

commit 467873771997901cad4cf011466908ccc724392e
parent ccc7e867e0523ef2355c781804884635f60b6633
Author: Robert Russell <robertrussell.72001@gmail.com>
Date:   Thu, 11 Apr 2024 20:53:17 -0700

Try toppling to 8 neighbours

Diffstat:
M.gitignore | 4+++-
MMakefile | 14++++++++++----
Msp2ff.c | 29++++++++++++++++++++---------
Aspgen.c | 18++++++++++++++++++
Rspstabilize.c -> spstabilize4.c | 0
Aspstabilize8.c | 105+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 156 insertions(+), 14 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -1,3 +1,5 @@ -spstabilize +spgen +spstabilize4 +spstabilize8 sp2ff ff2sp diff --git a/Makefile b/Makefile @@ -3,10 +3,16 @@ CC = gcc CFLAGS = -march=native -O3 -Wall -all: spstabilize sp2ff ff2sp +all: spgen spstabilize4 spstabilize8 sp2ff ff2sp -spstabilize: spstabilize.c common.c common.h - $(CC) -o $@ $(CFLAGS) spstabilize.c common.c -lrcx +spgen: spgen.c common.c common.h + $(CC) -o $@ $(CFLAGS) spgen.c common.c -lrcx + +spstabilize4: spstabilize4.c common.c common.h + $(CC) -o $@ $(CFLAGS) spstabilize4.c common.c -lrcx + +spstabilize8: spstabilize8.c common.c common.h + $(CC) -o $@ $(CFLAGS) spstabilize8.c common.c -lrcx sp2ff: sp2ff.c common.c common.h $(CC) -o $@ $(CFLAGS) sp2ff.c common.c -lrcx @@ -16,4 +22,4 @@ ff2sp: ff2sp.c common.c common.h .PHONY: clean clean: - rm -f spstabilize sp2ff ff2sp + rm -f spgen spstabilize4 spstabilize8 sp2ff ff2sp diff --git a/sp2ff.c b/sp2ff.c @@ -5,7 +5,7 @@ #include "common.h" #define USAGE \ - "usage: %s [COLOR0 COLOR1 COLOR2 COLOR3]\n" \ + "usage: %s [COLOR0 COLOR1 COLOR2 COLOR3 [COLOR4 COLOR5 COLOR6 COLOR7]]\n" \ "where COLORi is a 24 or 48 (32 or 64) bit RGB (RGBA) hex value\n" Rgba @@ -46,7 +46,7 @@ parse_rgba(char *s) { } Image -draw(Sandpile sp, Rgba (*palette)[4]) { +draw(Sandpile sp, Rgba (*palette)[8]) { usize w = sp.w; usize h = sp.h; @@ -54,7 +54,7 @@ draw(Sandpile sp, Rgba (*palette)[4]) { for (usize y = 0; y < h; y++) { for (usize x = 0; x < w; x++) { u32 s = sp.sand[(y + 1) * (w + 2) + (x + 1)]; - pixels[y * w + x] = (*palette)[MIN(s, 3)]; + pixels[y * w + x] = (*palette)[MIN(s, 7)]; } } @@ -65,15 +65,26 @@ int main(int argc, char **argv) { CHECK_FOR_HELP_OPTION(USAGE, argc, argv); - Rgba palette[4]; + Rgba palette[8]; if (argc == 1) { // Default palette - palette[0] = (Rgba){ 0x2222, 0x2727, 0x2525, 0xffff }; // "eerie black" - palette[1] = (Rgba){ 0x8989, 0x9898, 0x7878, 0xffff }; // "moss green" - palette[2] = (Rgba){ 0xe4e4, 0xe6e6, 0xc3c3, 0xffff }; // "beige" - palette[3] = (Rgba){ 0xf7f7, 0xf7f7, 0xf2f2, 0xffff }; // "baby powder" - } else if (argc == 5) { // Custom palette + #define RGB(r, g, b) (Rgba){ 0x##r##r, 0x##g##g, 0x##b##b, 0xffff } + palette[0] = RGB(BE, 80, 1D); + palette[1] = RGB(BE, 1D, A3); + palette[2] = RGB(78, 4E, 71); + palette[3] = RGB(4F, 65, 79); + palette[4] = RGB(38, A2, 18); + palette[5] = RGB(1D, 73, BE); + palette[6] = RGB(40, 6A, 33); + palette[7] = RGB(79, 69, 4F); + #undef RGB + } else if (argc == 5) { // Custom 4 color palette for (usize i = 0; i < 4; i++) palette[i] = parse_rgba(argv[i+1]); + for (usize i = 4; i < 8; i++) + palette[i] = palette[3]; + } else if (argc == 9) { // Custom 8 color palette + for (usize i = 0; i < 8; i++) + palette[i] = parse_rgba(argv[i+1]); } else { fprintf(stderr, USAGE, argv[0]); return 1; diff --git a/spgen.c b/spgen.c @@ -0,0 +1,18 @@ +#include <rcx/all.h> +#include <stdio.h> + +#include "common.h" + +int +main(void) { + usize w = 101; + usize h = 101; + u32 *sand = r_eallocz((w + 2) * (h + 2) * sizeof(u32)); + Sandpile sp = { .w = w, .h = h, .sand = sand }; + + usize x = 50; + usize y = 50; + sand[(y + 1) * (w + 2) + (x + 1)] = 10000; + + sp_fwrite(stdout, sp); +} diff --git a/spstabilize.c b/spstabilize4.c diff --git a/spstabilize8.c b/spstabilize8.c @@ -0,0 +1,105 @@ +#include <rcx/all.h> +#include <rcx/simd.h> +#include <stdio.h> + +#include "common.h" + +#ifndef R_HAVE_AVX2 +#error "AVX2 support required" +#endif + +#define USAGE "usage: %s\n" + +Sandpile +stabilize(Sandpile sp) { + usize w = sp.w; + usize h = sp.h; + + u32 *sand[2]; + sand[0] = sp.sand; + sand[1] = r_eallocz((w + 2) * (h + 2) * sizeof(u32)); + + isize nxv = (isize)w / 8; // Number of x vectors that fit in w + v8u32 v7 = v8u32_fill(7); + for (usize i = 0;; i = !i) { + usize unstable = 0; + + for (isize y = 1; y <= h; y++) { + isize j = y * ((isize)w + 2) + 1; + + for (isize xv = 0; xv < nxv; xv++, j += 8) { + v8u32 a = v8u32_loadu((v8u32a1 *)&sand[i][j]); + a = v8u32_and(a, v7); + + #define ADD(dx, dy) \ + do { \ + isize dj = (dy) * ((isize)w + 2) + (dx); \ + v8u32 b = v8u32_loadu((v8u32a1 *)&sand[i][j + dj]); \ + b = v8u32_sri(b, 3); \ + a = v8u32_add(a, b); \ + } while (0) + ADD(+1, +0); + ADD(+1, +1); + ADD(+0, +1); + ADD(-1, +1); + ADD(-1, +0); + ADD(-1, -1); + ADD(+0, -1); + ADD(+1, -1); + #undef ADD + + v8u32 g = v8u32_cmpgt(a, v7); + unstable += !v8u32_testz(g, g); + + v8u32_storeu((v8u32a1 *)&sand[!i][j], a); + } + + // TODO: Try dealing with tail with masked vector instead? Note + // that this would require a minimum width/height of 3. + for (isize x = 8*nxv; x < (isize)w; x++, j++) { + u32 a = sand[i][j]; + a = a & 3; + + #define ADD(dx, dy) \ + do { \ + isize dj = (dy) * ((isize)w + 2) + (dx); \ + u32 b = sand[i][j + dj]; \ + b = b >> 2; \ + a = a + b; \ + } while (0) + ADD(+1, +0); + ADD(+1, +1); + ADD(+0, +1); + ADD(-1, +1); + ADD(-1, +0); + ADD(-1, -1); + ADD(+0, -1); + ADD(+1, -1); + #undef ADD + + unstable += a > 3; + + sand[!i][j] = a; + } + } + + if (!unstable) { + free(sand[i]); + return (Sandpile){.w = w, .h = h, .sand = sand[!i]}; + } + } +} + +int +main(int argc, char **argv) { + CHECK_FOR_HELP_OPTION(USAGE, argc, argv); + + if (argc != 1) { + fprintf(stderr, USAGE, argv[0]); + return 1; + } + + Sandpile sp = sp_fread(stdin); + sp = stabilize(sp); + sp_fwrite(stdout, sp); +}