diff --git a/CMakeLists.txt b/CMakeLists.txt index 47aaba2..4c3c98d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,6 +44,11 @@ find_package(glfw3) add_subdirectory(colormap) add_executable(cloudlife) -target_sources(cloudlife PUBLIC main.cpp imgui_elements.cpp random.c) +target_sources(cloudlife PUBLIC + main.cpp + settings.cpp + imgui_elements.cpp + random.c + cloudlife.cpp) target_link_libraries(cloudlife IMGUI glfw GL colormap) set_target_properties(cloudlife PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) diff --git a/art.hpp b/art.hpp new file mode 100644 index 0000000..d8c1a22 --- /dev/null +++ b/art.hpp @@ -0,0 +1,45 @@ +#include +#include +#include + +class Art { +public: + Art(std::string _name) + : name(_name) {} + virtual bool render_gui() = 0; + virtual void resize(int _w, int _h) = 0; + virtual void render(uint32_t *p) = 0; + virtual void load(std::string json) {}; + virtual std::string save() { return ""; }; + + void drawdot(int x, int y, double o, uint32_t c) { + if (x+1 > w || y+1 > h) return; + uint32_t *p = data(); + p[ y*w + x ] = c | ((unsigned)(0xff*o)<<24); + } + + void drawdot(int x, int y, uint32_t c) { + if (x+1 > w || y+1 > h) return; + uint32_t *p = data(); + p[ y*w + x ] = c; + } + + virtual void reinit() { resize(w, h); } + +protected: + void default_resize(int _w, int _h) { + w = _w; + h = _h; + //data = (uint8_t *)xrealloc(data, w*h*sizeof(uint32_t)); + pixels.resize(w*h); + clear(); + } + void clear() { + std::fill(pixels.begin(), pixels.end(), 0); + } + int w, h; + //uint8_t *data() { return reinterpret_cast(pixels.data()); } + uint32_t *data() { return pixels.data(); } + std::vector pixels; + std::string name; +}; diff --git a/cloudlife.c b/cloudlife.c deleted file mode 100644 index 123dd13..0000000 --- a/cloudlife.c +++ /dev/null @@ -1,439 +0,0 @@ -/* cloudlife by Don Marti - * - * Based on Conway's Life, but with one rule change to make it a better - * screensaver: cells have a max age. - * - * When a cell exceeds the max age, it counts as 3 for populating the next - * generation. This makes long-lived formations explode instead of just - * sitting there burning a hole in your screen. - * - * Cloudlife only draws one pixel of each cell per tick, whether the cell is - * alive or dead. So gliders look like little comets. - - * 20 May 2003 -- now includes color cycling and a man page. - - * Based on several examples from the hacks directory of: - - * xscreensaver, Copyright (c) 1997, 1998, 2002 Jamie Zawinski - * - * Permission to use, copy, modify, distribute, and sell this software and its - * documentation for any purpose is hereby granted without fee, provided that - * the above copyright notice appear in all copies and that both that - * copyright notice and this permission notice appear in supporting - * documentation. No representations are made about the suitability of this - * software for any purpose. It is provided "as is" without express or - * implied warranty. - */ - -#include "screenhack.h" - -#ifndef MAX_WIDTH -#include -#define MAX_WIDTH SHRT_MAX -#endif - -#ifdef TIME_ME -#include -#endif - -/* this program goes faster if some functions are inline. The following is - * borrowed from ifs.c */ -#if !defined( __GNUC__ ) && !defined(__cplusplus) && !defined(c_plusplus) -#undef inline -#define inline /* */ -#endif - -struct field { - unsigned int height; - unsigned int width; - unsigned int max_age; - unsigned int cell_size; - unsigned char *cells; - unsigned char *new_cells; -}; - -struct state { - Display *dpy; - Window window; - -#ifdef TIME_ME - time_t start_time; -#endif - - unsigned int cycles; - unsigned int colorindex; /* which color in the colormap are we on */ - unsigned int colortimer; /* when this reaches 0, cycle to next color */ - - int cycle_delay; - int cycle_colors; - int ncolors; - int density; - - GC fgc, bgc; - XGCValues gcv; - XWindowAttributes xgwa; - XColor *colors; - - struct field *field; - - XPoint fg_points[MAX_WIDTH]; - XPoint bg_points[MAX_WIDTH]; -}; - - -static void -*xrealloc(void *p, size_t size) -{ - void *ret; - if ((ret = realloc(p, size)) == NULL) { - fprintf(stderr, "%s: out of memory\n", progname); - exit(1); - } - return ret; -} - -static struct field * -init_field(struct state *st) -{ - struct field *f = xrealloc(NULL, sizeof(struct field)); - f->height = 0; - f->width = 0; - f->cell_size = get_integer_resource(st->dpy, "cellSize", "Integer"); - f->max_age = get_integer_resource(st->dpy, "maxAge", "Integer"); - - if (f->max_age > 255) { - fprintf (stderr, "%s: max-age must be < 256 (not %d)\n", progname, - f->max_age); - exit (1); - } - - f->cells = NULL; - f->new_cells = NULL; - return f; -} - -static void -resize_field(struct field * f, unsigned int w, unsigned int h) -{ - int s = w * h * sizeof(unsigned char); - f->width = w; - f->height = h; - - f->cells = xrealloc(f->cells, s); - f->new_cells = xrealloc(f->new_cells, s); - memset(f->cells, 0, s); - memset(f->new_cells, 0, s); -} - -static inline unsigned char -*cell_at(struct field * f, unsigned int x, unsigned int y) -{ - return (f->cells + x * sizeof(unsigned char) + - y * f->width * sizeof(unsigned char)); -} - -static inline unsigned char -*new_cell_at(struct field * f, unsigned int x, unsigned int y) -{ - return (f->new_cells + x * sizeof(unsigned char) + - y * f->width * sizeof(unsigned char)); -} - -static void -draw_field(struct state *st, struct field * f) -{ - unsigned int x, y; - unsigned int rx, ry = 0; /* random amount to offset the dot */ - unsigned int size = 1 << f->cell_size; - unsigned int mask = size - 1; - unsigned int fg_count, bg_count; - - /* columns 0 and width-1 are off screen and not drawn. */ - for (y = 1; y < f->height - 1; y++) { - fg_count = 0; - bg_count = 0; - - /* rows 0 and height-1 are off screen and not drawn. */ - for (x = 1; x < f->width - 1; x++) { - rx = random(); - ry = rx >> f->cell_size; - rx &= mask; - ry &= mask; - - if (*cell_at(f, x, y)) { - st->fg_points[fg_count].x = (short) x *size - rx - 1; - st->fg_points[fg_count].y = (short) y *size - ry - 1; - fg_count++; - } else { - st->bg_points[bg_count].x = (short) x *size - rx - 1; - st->bg_points[bg_count].y = (short) y *size - ry - 1; - bg_count++; - } - } - XDrawPoints(st->dpy, st->window, st->fgc, st->fg_points, fg_count, - CoordModeOrigin); - XDrawPoints(st->dpy, st->window, st->bgc, st->bg_points, bg_count, - CoordModeOrigin); - } -} - -static inline unsigned int -cell_value(unsigned char c, unsigned int age) -{ - if (!c) { - return 0; - } else if (c > age) { - return (3); - } else { - return (1); - } -} - -static inline unsigned int -is_alive(struct field * f, unsigned int x, unsigned int y) -{ - unsigned int count; - unsigned int i, j; - unsigned char *p; - - count = 0; - - for (i = x - 1; i <= x + 1; i++) { - for (j = y - 1; j <= y + 1; j++) { - if (y != j || x != i) { - count += cell_value(*cell_at(f, i, j), f->max_age); - } - } - } - - p = cell_at(f, x, y); - if (*p) { - if (count == 2 || count == 3) { - return ((*p) + 1); - } else { - return (0); - } - } else { - if (count == 3) { - return (1); - } else { - return (0); - } - } -} - -static unsigned int -do_tick(struct field * f) -{ - unsigned int x, y; - unsigned int count = 0; - for (x = 1; x < f->width - 1; x++) { - for (y = 1; y < f->height - 1; y++) { - count += *new_cell_at(f, x, y) = is_alive(f, x, y); - } - } - memcpy(f->cells, f->new_cells, f->width * f->height * - sizeof(unsigned char)); - return count; -} - - -static unsigned int -random_cell(unsigned int p) -{ - int r = random() & 0xff; - - if (r < p) { - return (1); - } else { - return (0); - } -} - -static void -populate_field(struct field * f, unsigned int p) -{ - unsigned int x, y; - - for (x = 0; x < f->width; x++) { - for (y = 0; y < f->height; y++) { - *cell_at(f, x, y) = random_cell(p); - } - } -} - -static void -populate_edges(struct field * f, unsigned int p) -{ - unsigned int i; - - for (i = f->width; i--;) { - *cell_at(f, i, 0) = random_cell(p); - *cell_at(f, i, f->height - 1) = random_cell(p); - } - - for (i = f->height; i--;) { - *cell_at(f, f->width - 1, i) = random_cell(p); - *cell_at(f, 0, i) = random_cell(p); - } -} - -static void * -cloudlife_init (Display *dpy, Window window) -{ - struct state *st = (struct state *) calloc (1, sizeof(*st)); - Bool tmp = True; - - st->dpy = dpy; - st->window = window; - st->field = init_field(st); - -#ifdef TIME_ME - st->start_time = time(NULL); -#endif - - st->cycle_delay = get_integer_resource(st->dpy, "cycleDelay", "Integer"); - st->cycle_colors = get_integer_resource(st->dpy, "cycleColors", "Integer"); - st->ncolors = get_integer_resource(st->dpy, "ncolors", "Integer"); - st->density = (get_integer_resource(st->dpy, "initialDensity", "Integer") - % 100 * 256)/100; - - XGetWindowAttributes(st->dpy, st->window, &st->xgwa); - - if (st->cycle_colors) { - st->colors = (XColor *) xrealloc(st->colors, sizeof(XColor) * (st->ncolors+1)); - make_smooth_colormap (st->xgwa.screen, st->xgwa.visual, - st->xgwa.colormap, st->colors, &st->ncolors, - True, &tmp, True); - } - - st->gcv.foreground = get_pixel_resource(st->dpy, st->xgwa.colormap, - "foreground", "Foreground"); - st->fgc = XCreateGC(st->dpy, st->window, GCForeground, &st->gcv); - - st->gcv.foreground = get_pixel_resource(st->dpy, st->xgwa.colormap, - "background", "Background"); - st->bgc = XCreateGC(st->dpy, st->window, GCForeground, &st->gcv); - - return st; -} - -static unsigned long -cloudlife_draw (Display *dpy, Window window, void *closure) -{ - struct state *st = (struct state *) closure; - - if (st->cycle_colors) { - if (st->colortimer == 0) { - st->colortimer = st->cycle_colors; - if( st->colorindex == 0 ) - st->colorindex = st->ncolors; - st->colorindex--; - XSetForeground(st->dpy, st->fgc, st->colors[st->colorindex].pixel); - } - st->colortimer--; - } - - XGetWindowAttributes(st->dpy, st->window, &st->xgwa); - if (st->field->height != st->xgwa.height / (1 << st->field->cell_size) + 2 || - st->field->width != st->xgwa.width / (1 << st->field->cell_size) + 2) { - - resize_field(st->field, st->xgwa.width / (1 << st->field->cell_size) + 2, - st->xgwa.height / (1 << st->field->cell_size) + 2); - populate_field(st->field, st->density); - } - - draw_field(st, st->field); - - if (do_tick(st->field) < (st->field->height + st->field->width) / 4) { - populate_field(st->field, st->density); - } - - if (st->cycles % (st->field->max_age /2) == 0) { - populate_edges(st->field, st->density); - do_tick(st->field); - populate_edges(st->field, 0); - } - - st->cycles++; - -#ifdef TIME_ME - if (st->cycles % st->field->max_age == 0) { - printf("%g s.\n", - ((time(NULL) - st->start_time) * 1000.0) / st->cycles); - } -#endif - - return (st->cycle_delay); -} - - -static void -cloudlife_reshape (Display *dpy, Window window, void *closure, - unsigned int w, unsigned int h) -{ -} - -static Bool -cloudlife_event (Display *dpy, Window window, void *closure, XEvent *event) -{ - struct state *st = (struct state *) closure; - if (screenhack_event_helper (dpy, window, event)) - { - XClearWindow (dpy, window); - st->cycles = 0; - if (st->field) { - free (st->field->cells); - free (st->field->new_cells); - free (st->field); - } - st->field = init_field(st); - return True; - } - return False; -} - -static void -cloudlife_free (Display *dpy, Window window, void *closure) -{ - struct state *st = (struct state *) closure; - free (st->field->cells); - free (st->field->new_cells); - free (st->field); - free (st->colors); - XFreeGC (dpy, st->fgc); - XFreeGC (dpy, st->bgc); - free (st); -} - - -static const char *cloudlife_defaults[] = { - ".background: black", - ".foreground: blue", - "*fpsSolid: true", - "*cycleDelay: 25000", - "*cycleColors: 2", - "*ncolors: 64", - "*maxAge: 64", - "*initialDensity: 30", - "*cellSize: 3", -#ifdef HAVE_MOBILE - "*ignoreRotation: True", -#endif - 0 -}; - -static XrmOptionDescRec cloudlife_options[] = { - {"-background", ".background", XrmoptionSepArg, 0}, - {"-foreground", ".foreground", XrmoptionSepArg, 0}, - {"-cycle-delay", ".cycleDelay", XrmoptionSepArg, 0}, - {"-cycle-colors", ".cycleColors", XrmoptionSepArg, 0}, - {"-ncolors", ".ncolors", XrmoptionSepArg, 0}, - {"-cell-size", ".cellSize", XrmoptionSepArg, 0}, - {"-initial-density", ".initialDensity", XrmoptionSepArg, 0}, - {"-max-age", ".maxAge", XrmoptionSepArg, 0}, - {0, 0, 0, 0} -}; - - -XSCREENSAVER_MODULE ("CloudLife", cloudlife) diff --git a/cloudlife.cpp b/cloudlife.cpp new file mode 100644 index 0000000..9b47e91 --- /dev/null +++ b/cloudlife.cpp @@ -0,0 +1,248 @@ +#include +#include + +#include "cloudlife.hpp" +#include "imgui_elements.h" +#include "random.h" + +void Cloudlife::resize_field(int fw, int fh) { + int s = fw * fh * sizeof(unsigned char); + f->width = fw; + f->height = fh; + + f->cells.resize(s); + f->new_cells.resize(s); + std::fill(f->cells.begin(), f->cells.end(), 0); + std::fill(f->new_cells.begin(), f->new_cells.end(), 0); + + *pal = (*pal).rescale(0., f->max_age); + //*pal = (*pal).rescale(0., ncolors); +} + +unsigned char *Cloudlife::cell_at(unsigned int x, unsigned int y) +{ + return (f->cells.data() + x * sizeof(unsigned char) + + y * f->width * sizeof(unsigned char)); +} + +unsigned char *Cloudlife::new_cell_at(unsigned int x, unsigned int y) +{ + return (f->new_cells.data() + x * sizeof(unsigned char) + + y * f->width * sizeof(unsigned char)); +} + +static unsigned int +random_cell(unsigned int p) +{ + int r = xoshiro256plus() & 0xff; + + if (r < p) { + return (1); + } else { + return (0); + } +} + +void Cloudlife::populate_field(unsigned int p) +{ + unsigned int x, y; + + for (x = 0; x < f->width; x++) { + for (y = 0; y < f->height; y++) { + *cell_at(x, y) = random_cell(p); + } + } +} + +void Cloudlife::resize(int _w, int _h) { + default_resize(_w, _h); + + if (f->height != h / (1 << f->cell_size) + 2 || + f->width != w / (1 << f->cell_size) + 2) { + refield(); + } +} + +void Cloudlife::refield() { + resize_field(w / (1 << f->cell_size) + 2, + h / (1 << f->cell_size) + 2); + populate_field(density); + default_resize(w, h); // clear pixels +} + +bool Cloudlife::render_gui() { + bool up = false; + + up |= ScrollableSliderInt("Initial density", &density, 8, 256, "%d", 8); + up |= ScrollableSliderUInt("Cell size", &f->cell_size, 1, 32, "%d", 1); + up |= ScrollableSliderUInt("Max age", &f->max_age, 4, 256, "%d", 8); + //up |= ScrollableSliderUInt("ncolors", &ncolors, 0, 1024, "%d", 8); + ScrollableSliderUInt("Ticks per frame", &f->ticks_per_frame, 1, 128, "%d", 1); + up |= ImGui::ColorEdit4("Foreground", (float*)&foreground); + up |= ImGui::ColorEdit4("Backgroud", (float*)&background); + up |= ImGui::ColorEdit4("Clear color", (float*)&clear_color); + up |= pal.RenderGui(); + + if (up) { + refield(); + } + + return up; +} + + +void Cloudlife::populate_edges(unsigned int p) +{ + unsigned int i; + + for (i = f->width; i--;) { + *cell_at(i, 0) = random_cell(p); + *cell_at(i, f->height - 1) = random_cell(p); + } + + for (i = f->height; i--;) { + *cell_at(f->width - 1, i) = random_cell(p); + *cell_at(0, i) = random_cell(p); + } +} + +//-------------------------------------------------------------- + +uint32_t Cloudlife::get_color_age(int age) { + auto c = (*pal)(age); + return 0xff000000 | + c.getRed().getValue() << 0 | + c.getGreen().getValue() << 8 | + c.getBlue().getValue() << 16; +} + +void +Cloudlife::draw_field() +{ + unsigned int x, y; + unsigned int rx, ry = 0; /* random amount to offset the dot */ + unsigned int size = 1 << f->cell_size; + unsigned int mask = size - 1; + unsigned int fg_count, bg_count; + uint32_t fgc, bgc; + + fgc = ImGui::GetColorU32(foreground); + //fgc = get_color_age(colorindex);// original color behaviour? + bgc = ImGui::GetColorU32(background); + + /* columns 0 and width-1 are off screen and not drawn. */ + for (y = 1; y < f->height - 1; y++) { + fg_count = 0; + bg_count = 0; + + /* rows 0 and height-1 are off screen and not drawn. */ + for (x = 1; x < f->width - 1; x++) { + rx = xoshiro256plus(); + ry = rx >> f->cell_size; + rx &= mask; + ry &= mask; + + uint8_t age = *cell_at(x, y); + fgc = get_color_age(age); + if (age) { + drawdot((short) x *size - rx - 1, + (short) y *size - ry - 1, + fgc); + } else { + drawdot((short) x *size - rx - 1, + (short) y *size - ry - 1, + bgc); + } + } + } +} + +static inline unsigned int +cell_value(unsigned char c, unsigned int age) +{ + if (!c) { + return 0; + } else if (c > age) { + return (3); + } else { + return (1); + } +} + +unsigned int Cloudlife::is_alive(unsigned int x, unsigned int y) +{ + unsigned int count; + unsigned int i, j; + unsigned char *p; + + count = 0; + + for (i = x - 1; i <= x + 1; i++) { + for (j = y - 1; j <= y + 1; j++) { + if (y != j || x != i) { + count += cell_value(*cell_at(i, j), f->max_age); + } + } + } + + p = cell_at(x, y); + if (*p) { + if (count == 2 || count == 3) { + return ((*p) + 1); + } else { + return (0); + } + } else { + if (count == 3) { + return (1); + } else { + return (0); + } + } +} + +unsigned int Cloudlife::do_tick() +{ + unsigned int x, y; + unsigned int count = 0; + for (x = 1; x < f->width - 1; x++) { + for (y = 1; y < f->height - 1; y++) { + count += *new_cell_at(x, y) = is_alive(x, y); + } + } + f->cells = f->new_cells; + return count; +} + +void Cloudlife::render(uint32_t *p) { + unsigned int count = 0; + + for (int i=0; iticks_per_frame; ++i) + count = do_tick(); + + if (count < (f->height + f->width) / 4) { + populate_field(density); + } + + if (cycles % (f->max_age / 2) == 0) { + populate_edges(density); + do_tick(); + populate_edges(0); + } +/* + if (ncolors) { + if (colortimer) { + colorindex--; + if (colorindex == 0) + colorindex = ncolors; + } + } +*/ + draw_field(); + cycles++; + + std::copy(pixels.begin(), pixels.end(), p); + +} + + diff --git a/cloudlife.hpp b/cloudlife.hpp new file mode 100644 index 0000000..85fa9bd --- /dev/null +++ b/cloudlife.hpp @@ -0,0 +1,59 @@ +#include "art.hpp" +#include +#include "imgui.h" + +#include "settings.hpp" + +#include +#include + + +struct field { + unsigned int height = 0; + unsigned int width = 0; + unsigned int max_age = 64; + unsigned int cell_size = 3; + unsigned int ticks_per_frame = 1; + std::vector cells, new_cells; +}; + +class Cloudlife : public Art { +public: + Cloudlife() + : Art("Cloudlife") + , f(new field) {} + virtual bool render_gui() override; + virtual void resize(int _w, int _h) override; + virtual void render(uint32_t *p) override; +private: + std::unique_ptr f; + + unsigned ncolors=512; + unsigned int colorindex = ncolors; /* which color in the colormap are we on */ + unsigned int colortimer = 1; /* when this reaches 0, cycle to next color */ + + int density = 32, cycles=0; + ImVec4 clear_color = ImVec4(1, 0, 0, 1.00f); + ImVec4 background = ImVec4(0, 0, 0, 1); + ImVec4 foreground = ImVec4(0, 1, 0, 1); + + //pal_t pal = colormap::palettes.at("inferno"); + int item_current_idx = 0; + + + + uint32_t get_color_age(int age); + unsigned char *cell_at(unsigned int x, unsigned int y); + unsigned char *new_cell_at(unsigned int x, unsigned int y); + void resize_field(int fw, int fh); + void populate_field(unsigned int p); + void populate_edges(unsigned int p); + void draw_field(); + void refield(); + unsigned int is_alive(unsigned int x, unsigned int y); + unsigned int do_tick(); + + PaletteSetting pal; + +}; + diff --git a/main.cpp b/main.cpp index ea82b15..5743f93 100644 --- a/main.cpp +++ b/main.cpp @@ -1,14 +1,6 @@ -#include "colormap/include/colormap/color.hpp" #include -#include -#include -#include +#include -#include - -#include "random.h" - -#include // exp #include // getopt #include @@ -17,7 +9,6 @@ #include "imgui.h" -#include "imgui_elements.h" #include "imgui_impl_glfw.h" #include "imgui_impl_opengl3.h" @@ -28,43 +19,13 @@ #endif #include // Will drag system OpenGL headers -////////////////////////////////////////////////////////////////////////////// -static int item_current_idx = 0; -auto pal = colormap::palettes.at("inferno"); +#include "cloudlife.hpp" -bool draw_pal_combo() { - bool ret = false; - auto pb = colormap::palettes.begin(); - std::advance(pb, item_current_idx); +std::unique_ptr art; - int size = colormap::palettes.size(); - const char* combo_preview_value = pb->first.c_str(); - if (ImGui::BeginCombo("Palette", combo_preview_value, 0)) - { - auto pb = colormap::palettes.begin(); - for (int n = 0; n < size; n++) - { - const bool is_selected = (item_current_idx == n); - const char * name = pb->first.c_str(); - if (ImGui::Selectable(name, is_selected)) - { - item_current_idx = n; - ret = true; - } - - // Set the initial focus when opening the combo (scrolling + keyboard navigation focus) - if (is_selected) - ImGui::SetItemDefaultFocus(); - std::advance(pb, 1); - } - ImGui::EndCombo(); - } - return ret; -} -/////////////////////////////////////////////////////////////////////////////// static void *xrealloc(void *p, size_t size) { @@ -75,7 +36,6 @@ static void return ret; } - static GLFWwindow* window; static int sw = 1024, sh = 1024; @@ -130,292 +90,6 @@ void destroy_pbos() { glDeleteBuffers(2, pboIds); } -// ----------------------------------------------------------------------------- - -void drawdot(uint32_t *p, int x, int y, double o, uint32_t c) { - p[ y*sw + x ] = c | ((unsigned)(0xff*o)<<24); -} - -void drawdot(uint32_t *p, int x, int y, uint32_t c) { - p[ y*sw + x ] = c; -} - - -////////////////////////////////////////////// - -static struct field { - unsigned int height; - unsigned int width; - unsigned int max_age; - unsigned int cell_size; - unsigned char *cells; - unsigned char *new_cells; - unsigned int ticks_per_frame; - uint32_t *image; -} field_; - -struct field *f = &field_; - -ImVec4 clear_color = ImVec4(1, 0, 0, 1.00f); -ImVec4 background = ImVec4(0, 0, 0, 1); -ImVec4 foreground = ImVec4(0, 1, 0, 1); - -int density = 32, cycles=0; - -//////////////////////////////////////////////// - -static void -resize_field(unsigned int w, unsigned int h) -{ - int s = w * h * sizeof(unsigned char); - f->width = w; - f->height = h; - - f->cells = (unsigned char*)xrealloc(f->cells, s); - f->new_cells = (unsigned char*)xrealloc(f->new_cells, s); - f->image = (uint32_t*)xrealloc(f->image, DATA_SIZE); - memset(f->cells, 0, s); - memset(f->new_cells, 0, s); - auto pb = colormap::palettes.begin(); - std::advance(pb, item_current_idx); - pal = pb->second.rescale(0., f->max_age); - //pal = colormap::palettes.at("plasma").rescale(0., f->max_age); -} - -void init_field() -{ - f->height = 0; - f->width = 0; - f->cell_size = 3; - f->max_age = 64; - f->ticks_per_frame = 1; - - f->cells = NULL; - f->new_cells = NULL; - f->image = NULL; -} - - - - -static unsigned int -random_cell(unsigned int p) -{ - int r = xoshiro256plus() & 0xff; - - if (r < p) { - return (1); - } else { - return (0); - } -} - -static inline unsigned char -*cell_at(unsigned int x, unsigned int y) -{ - return (f->cells + x * sizeof(unsigned char) + - y * f->width * sizeof(unsigned char)); -} - -static inline unsigned char -*new_cell_at(unsigned int x, unsigned int y) -{ - return (f->new_cells + x * sizeof(unsigned char) + - y * f->width * sizeof(unsigned char)); -} - -static void -populate_field(unsigned int p) -{ - unsigned int x, y; - - for (x = 0; x < f->width; x++) { - for (y = 0; y < f->height; y++) { - *cell_at(x, y) = random_cell(p); - } - } -} - -static void -refield() { - resize_field(sw / (1 << f->cell_size) + 2, - sh / (1 << f->cell_size) + 2); - populate_field(density); - memset(f->image, 0, DATA_SIZE); -} - -// -------------------------------------------------- - -static void draw_gui() { - bool up = false; - - up |= ScrollableSliderInt("Initial density", &density, 8, 256, "%d", 8); - up |= ScrollableSliderUInt("Cell size", &f->cell_size, 1, 64, "%d", 1); - up |= ScrollableSliderUInt("Max age", &f->max_age, 4, 256, "%d", 8); - ScrollableSliderUInt("Ticks per frame", &f->ticks_per_frame, 1, 128, "%d", 1); - up |= ImGui::ColorEdit4("Foreground", (float*)&foreground); - up |= ImGui::ColorEdit4("Backgroud", (float*)&background); - up |= ImGui::ColorEdit4("Clear color", (float*)&clear_color); - up |= draw_pal_combo(); - - if (up) { - refield(); - } -} - - -static void -populate_edges(unsigned int p) -{ - unsigned int i; - - for (i = f->width; i--;) { - *cell_at(i, 0) = random_cell(p); - *cell_at(i, f->height - 1) = random_cell(p); - } - - for (i = f->height; i--;) { - *cell_at(f->width - 1, i) = random_cell(p); - *cell_at(0, i) = random_cell(p); - } -} - -//-------------------------------------------------------------- - -uint32_t get_color_age(colormap::map> &m, uint8_t age) { - auto c = m(age); - return 0xff000000 | - c.getRed().getValue() << 0 | - c.getGreen().getValue() << 8 | - c.getBlue().getValue() << 16; -} - -static void -draw_field(uint32_t *p) -{ - unsigned int x, y; - unsigned int rx, ry = 0; /* random amount to offset the dot */ - unsigned int size = 1 << f->cell_size; - unsigned int mask = size - 1; - unsigned int fg_count, bg_count; - uint32_t fgc, bgc; - - fgc = ImGui::GetColorU32(foreground); - bgc = ImGui::GetColorU32(background); - - /* columns 0 and width-1 are off screen and not drawn. */ - for (y = 1; y < f->height - 1; y++) { - fg_count = 0; - bg_count = 0; - - /* rows 0 and height-1 are off screen and not drawn. */ - for (x = 1; x < f->width - 1; x++) { - rx = xoshiro256plus(); - ry = rx >> f->cell_size; - rx &= mask; - ry &= mask; - - uint8_t age = *cell_at(x, y); - if (age) { - drawdot(p, - (short) x *size - rx - 1, - (short) y *size - ry - 1, - get_color_age(pal, age)); - } else { - drawdot(p, (short) x *size - rx - 1, - (short) y *size - ry - 1, - bgc); - } - } - } -} - -static inline unsigned int -cell_value(unsigned char c, unsigned int age) -{ - if (!c) { - return 0; - } else if (c > age) { - return (3); - } else { - return (1); - } -} - -static inline unsigned int -is_alive(unsigned int x, unsigned int y) -{ - unsigned int count; - unsigned int i, j; - unsigned char *p; - - count = 0; - - for (i = x - 1; i <= x + 1; i++) { - for (j = y - 1; j <= y + 1; j++) { - if (y != j || x != i) { - count += cell_value(*cell_at(i, j), f->max_age); - } - } - } - - p = cell_at(x, y); - if (*p) { - if (count == 2 || count == 3) { - return ((*p) + 1); - } else { - return (0); - } - } else { - if (count == 3) { - return (1); - } else { - return (0); - } - } -} - -static unsigned int -do_tick() -{ - unsigned int x, y; - unsigned int count = 0; - for (x = 1; x < f->width - 1; x++) { - for (y = 1; y < f->height - 1; y++) { - count += *new_cell_at(x, y) = is_alive(x, y); - } - } - memcpy(f->cells, f->new_cells, f->width * f->height * - sizeof(unsigned char)); - return count; -} - -static void -draw_life(uint32_t *p) { - unsigned int count = 0; - - for (int i=0; iticks_per_frame; ++i) - count = do_tick(); - - if (count < (f->height + f->width) / 4) { - populate_field(density); - } - - if (cycles % (f->max_age / 2) == 0) { - populate_edges(density); - do_tick(); - populate_edges(0); - } - - draw_field(f->image); - - memcpy(p, f->image, DATA_SIZE); - - cycles++; -} - - - static void glfw_error_callback(int error, const char* description) { @@ -475,9 +149,11 @@ int main(int argc, char *argv[]) glfwSwapInterval(vsync); make_pbos(); - init_field(); - refield(); + art.reset(new Cloudlife); + + get_size(0,0); + art->resize(sw, sh); IMGUI_CHECKVERSION(); ImGui::CreateContext(); @@ -503,7 +179,7 @@ int main(int argc, char *argv[]) ImGui::Begin("Cloudlife from xscreensaver"); - draw_gui(); + art->render_gui(); ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, @@ -514,7 +190,7 @@ int main(int argc, char *argv[]) if (get_size(0,0)) { destroy_pbos(); make_pbos(); - refield(); + art->resize(sw, sh); } int nexti = pbo_index; @@ -527,7 +203,7 @@ int main(int argc, char *argv[]) glBufferData(GL_PIXEL_UNPACK_BUFFER, DATA_SIZE, 0, GL_STREAM_DRAW); uint32_t* ptr = (uint32_t*)glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY); assert(ptr); - draw_life(ptr); + art->render(ptr); glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); @@ -550,9 +226,6 @@ int main(int argc, char *argv[]) destroy_pbos(); free(image_data); - free(f->cells); - free(f->new_cells); - free(f->image); glfwDestroyWindow(window); glfwTerminate(); diff --git a/settings.cpp b/settings.cpp new file mode 100644 index 0000000..a36f8d3 --- /dev/null +++ b/settings.cpp @@ -0,0 +1,38 @@ + +#include "settings.hpp" + +#include "imgui.h" + +#include "imgui_elements.h" + + +bool PaletteSetting::RenderGui() { + bool ret = false; + auto pb = colormap::palettes.begin(); + std::advance(pb, item_current_idx); + + int size = colormap::palettes.size(); + const char* combo_preview_value = pb->first.c_str(); + if (ImGui::BeginCombo("Palette", combo_preview_value, 0)) + { + auto pb = colormap::palettes.begin(); + for (int n = 0; n < size; n++) + { + const bool is_selected = (item_current_idx == n); + const char * name = pb->first.c_str(); + if (ImGui::Selectable(name, is_selected)) + { + item_current_idx = n; + value = pb->second; + ret = true; + } + + // Set the initial focus when opening the combo (scrolling + keyboard navigation focus) + if (is_selected) + ImGui::SetItemDefaultFocus(); + std::advance(pb, 1); + } + ImGui::EndCombo(); + } + return ret; +} diff --git a/settings.hpp b/settings.hpp new file mode 100644 index 0000000..22e5ca2 --- /dev/null +++ b/settings.hpp @@ -0,0 +1,33 @@ +#pragma once +#include + +class Setting { +public: + virtual bool RenderGui() = 0; + virtual void Load(const std::string & json) {}; + virtual void Save(std::string &json) {}; +private: +}; + + +#include + +//typedef std::map> pal_t; +//typedef decltype(colormap::palettes.at("inferno"))) pal_t; + +class PaletteSetting : public Setting { +public: + typedef colormap::map> pal_t; + + PaletteSetting() + { } + bool RenderGui() override; + + pal_t & operator*() {return value;} + + pal_t value = colormap::palettes.at("inferno"); +private: + int item_current_idx = 0; + +}; +