From 192a89a7f9f396fff4eb6b0836f597c376e98932 Mon Sep 17 00:00:00 2001 From: Vito Caputo Date: Fri, 20 May 2022 21:25:39 -0700 Subject: modules/shapes: add procedural 2D shapes module Mostly for compositing purposes, here will be a corpus of 2D shapes, parameterized/procedurally generated and able to rotate and perhaps have other dynamics added. What shapes are there presently I had started implementing in checkers as "styles", before realizing they really should just be a separate module checkers can call into. Not terribly interesting by itself, but as blinds and checkers demonstrated, these things deliver a lot of value in compositional situations. They're creating the palette to draw from. --- src/Makefile.am | 2 +- src/modules/Makefile.am | 2 +- src/modules/shapes/Makefile.am | 3 + src/modules/shapes/shapes.c | 269 +++++++++++++++++++++++++++++++++++++++++ src/til.c | 2 + 5 files changed, 276 insertions(+), 2 deletions(-) create mode 100644 src/modules/shapes/Makefile.am create mode 100644 src/modules/shapes/shapes.c (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 10553fe..0313a1a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -3,7 +3,7 @@ SUBDIRS = libs modules noinst_LTLIBRARIES = libtil.la libtil_la_SOURCES = til_args.c til_args.h til_fb.c til_fb.h til_knobs.h til.c til.h til_settings.h til_settings.c til_setup.c til_setup.h til_threads.c til_threads.h til_util.c til_util.h libtil_la_CPPFLAGS = -I@top_srcdir@/src -libtil_la_LIBADD = modules/blinds/libblinds.la modules/checkers/libcheckers.la modules/compose/libcompose.la modules/drizzle/libdrizzle.la modules/flui2d/libflui2d.la modules/julia/libjulia.la modules/meta2d/libmeta2d.la modules/montage/libmontage.la modules/pixbounce/libpixbounce.la modules/plasma/libplasma.la modules/plato/libplato.la modules/ray/libray.la modules/roto/libroto.la modules/rtv/librtv.la modules/snow/libsnow.la modules/sparkler/libsparkler.la modules/spiro/libspiro.la modules/stars/libstars.la modules/submit/libsubmit.la modules/swab/libswab.la modules/swarm/libswarm.la modules/voronoi/libvoronoi.la libs/grid/libgrid.la libs/puddle/libpuddle.la libs/ray/libray.la libs/sig/libsig.la libs/txt/libtxt.la libs/ascii/libascii.la libs/din/libdin.la +libtil_la_LIBADD = modules/blinds/libblinds.la modules/checkers/libcheckers.la modules/compose/libcompose.la modules/drizzle/libdrizzle.la modules/flui2d/libflui2d.la modules/julia/libjulia.la modules/meta2d/libmeta2d.la modules/montage/libmontage.la modules/pixbounce/libpixbounce.la modules/plasma/libplasma.la modules/plato/libplato.la modules/ray/libray.la modules/roto/libroto.la modules/rtv/librtv.la modules/shapes/libshapes.la modules/snow/libsnow.la modules/sparkler/libsparkler.la modules/spiro/libspiro.la modules/stars/libstars.la modules/submit/libsubmit.la modules/swab/libswab.la modules/swarm/libswarm.la modules/voronoi/libvoronoi.la libs/grid/libgrid.la libs/puddle/libpuddle.la libs/ray/libray.la libs/sig/libsig.la libs/txt/libtxt.la libs/ascii/libascii.la libs/din/libdin.la if ENABLE_ROTOTILLER bin_PROGRAMS = rototiller diff --git a/src/modules/Makefile.am b/src/modules/Makefile.am index e5c9971..d00ac7b 100644 --- a/src/modules/Makefile.am +++ b/src/modules/Makefile.am @@ -1 +1 @@ -SUBDIRS = blinds checkers compose drizzle flui2d julia meta2d montage pixbounce plasma plato ray roto rtv snow sparkler spiro stars submit swab swarm voronoi +SUBDIRS = blinds checkers compose drizzle flui2d julia meta2d montage pixbounce plasma plato ray roto rtv shapes snow sparkler spiro stars submit swab swarm voronoi diff --git a/src/modules/shapes/Makefile.am b/src/modules/shapes/Makefile.am new file mode 100644 index 0000000..84f25fa --- /dev/null +++ b/src/modules/shapes/Makefile.am @@ -0,0 +1,3 @@ +noinst_LTLIBRARIES = libshapes.la +libshapes_la_SOURCES = shapes.c +libshapes_la_CPPFLAGS = -I@top_srcdir@/src diff --git a/src/modules/shapes/shapes.c b/src/modules/shapes/shapes.c new file mode 100644 index 0000000..6c912ca --- /dev/null +++ b/src/modules/shapes/shapes.c @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2022 - Vito Caputo - + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* The impetus for adding this is a desire for adding a variety of shapes + * to modueles/checkers. I had started open-coding shapes like circle, + * rhombus, pinwheel, and star, directly into checkers with a new style= + * setting for choosing which to use instead of the plain filled square. + * + * But it seemed silly to bury this directly in checkers, when checkers + * could trivially call into another module for rendering the filled + * fragment. And as the shapes became more interesting like star/pinwheel, + * it also became clear that parameterizing them to really take advantage + * of their procedural implementation would be a lot of fun. Exposing + * those parameters only as checkers settings, and knobs once available, + * only within checkers, seemed like it'd be really selling things short. + * + * So here we are, shapes is its own module, it's kind of boring ATM. Its + * addition will likely be followed by checkers getting a filler module + * setting, which could then invoke shapes - or any other module. + * + * TODO: + * + * - Add more interesting shapes + * + * - Expose parameters as settings + * + * - Parameterize more things, stuff like twist for the radial shapes + * comes to mind. The simplistic CCW rotation of star/pinwheel should be + * more variable and exposed as settings, etc. + * + * - Go threaded, for ease of implementation this is currently simple + * non-threaded code. In the checkers use case, the individual checkers + * are already being rendered concurrently, so as-is this still becomes + * threaded there. It's just full-frame shapes situations where it + * hurts. + * + * - The presently static shapes like circle and rhombus could be simply + * rendered once @ context_create() into a dense buffer then copied at + * render_fragment() time. The current implementation is very naive and + * slow procedurally redrawing even these constant shapes. But the + * assumption is as more parameterizing is added, all the shapes will + * become dynamic. So there's no sense adding a cache. + */ + + + +#include +#include +#include +#include + +#include "til.h" +#include "til_fb.h" + +#define SHAPES_DEFAULT_TYPE SHAPES_TYPE_PINWHEEL + +typedef enum shapes_type_t { + SHAPES_TYPE_CIRCLE, + SHAPES_TYPE_PINWHEEL, + SHAPES_TYPE_RHOMBUS, + SHAPES_TYPE_STAR, +} shapes_type_t; + +typedef struct shapes_setup_t { + til_setup_t til_setup; + shapes_type_t type; +} shapes_setup_t; + +typedef struct shapes_context_t { + shapes_setup_t setup; +} shapes_context_t; + + +static shapes_setup_t shapes_default_setup = { + .type = SHAPES_DEFAULT_TYPE, +}; + + +static void * shapes_create_context(unsigned ticks, unsigned n_cpus, til_setup_t *setup) +{ + shapes_context_t *ctxt; + + if (!setup) + setup = &shapes_default_setup.til_setup; + + ctxt = calloc(1, sizeof(shapes_context_t)); + if (!ctxt) + return NULL; + + ctxt->setup = *(shapes_setup_t *)setup; + + return ctxt; +} + + +static void shapes_destroy_context(void *context) +{ + shapes_context_t *ctxt = context; + + free(ctxt); +} + + +static void shapes_render_fragment(void *context, unsigned ticks, unsigned cpu, til_fb_fragment_t *fragment) +{ + shapes_context_t *ctxt = context; + unsigned size = MIN(fragment->width, fragment->height); + unsigned xoff = (fragment->width - size) >> 1; + unsigned yoff = (fragment->height - size) >> 1; + + /* eventually these should probably get broken out into functions, + * but it's not too unwieldy for now. + */ + switch (ctxt->setup.type) { + case SHAPES_TYPE_CIRCLE: { + int r_sq = (size >> 1); + + r_sq *= r_sq; + + for (int y = yoff, Y = -(size >> 1); y < fragment->height; y++, Y++) { + for (int x = xoff, X = -(size >> 1); x < fragment->width; x++, X++) { + if (Y*Y+X*X <= r_sq) + til_fb_fragment_put_pixel_unchecked(fragment, TIL_FB_DRAW_FLAG_TEXTURABLE, fragment->x + x, fragment->y + y, 0xffffffff); + else if (!fragment->cleared) + til_fb_fragment_put_pixel_unchecked(fragment, 0, fragment->x + x, fragment->y + y, 0x0); + + } + } + break; + } + + case SHAPES_TYPE_PINWHEEL: { + float s = 2.f / (float)size; + float X, Y; + + Y = -1.f; + for (unsigned y = yoff; y < fragment->height; y++, Y += s) { + X = -1.f; + for (unsigned x = xoff; x < fragment->width; x++, X += s) { + float rad = atan2f(Y, X) + (float)ticks * .001f; + float r = cosf(5.f * rad) * .5f + .5f; + + if (X * X + Y * Y < r * r) + til_fb_fragment_put_pixel_unchecked(fragment, TIL_FB_DRAW_FLAG_TEXTURABLE, fragment->x + x, fragment->y + y, 0xffffffff); + else if (!fragment->cleared) + til_fb_fragment_put_pixel_unchecked(fragment, 0, fragment->x + x, fragment->y + y, 0x0); + + } + } + break; + } + + case SHAPES_TYPE_RHOMBUS: { + int r = (size >> 1); + + for (unsigned y = yoff, Y = -(size >> 1); y < fragment->height; y++, Y++) { + for (unsigned x = xoff, X = -(size >> 1); x < fragment->width; x++, X++) { + if (abs(Y)+abs(X) <= r) + til_fb_fragment_put_pixel_unchecked(fragment, TIL_FB_DRAW_FLAG_TEXTURABLE, fragment->x + x, fragment->y + y, 0xffffffff); + else if (!fragment->cleared) + til_fb_fragment_put_pixel_unchecked(fragment, 0, fragment->x + x, fragment->y + y, 0x0); + + } + } + break; + } + + case SHAPES_TYPE_STAR: { + float s = 2.f / (float)size; + float X, Y; + + Y = -1.f; + for (unsigned y = yoff; y < fragment->height; y++, Y += s) { + X = -1.f; + for (unsigned x = xoff; x < fragment->width; x++, X += s) { + float rad = atan2f(Y, X) + (float)ticks * .001f; + float r = (M_2_PI * asinf(sinf(5 * rad) * .5f + .5f)) * .5f + .5f; + /* ^^^^^^^^^^^^^^^^^^^ approximates a triangle wave */ + + if (X * X + Y * Y < r * r) + til_fb_fragment_put_pixel_unchecked(fragment, TIL_FB_DRAW_FLAG_TEXTURABLE, fragment->x + x, fragment->y + y, 0xffffffff); + else if (!fragment->cleared) + til_fb_fragment_put_pixel_unchecked(fragment, 0, fragment->x + x, fragment->y + y, 0x0); + } + } + break; + } + } +} + + +static int shapes_setup(const til_settings_t *settings, til_setting_t **res_setting, const til_setting_desc_t **res_desc, til_setup_t **res_setup) +{ + const char *type; + const char *type_values[] = { + "circle", + "pinwheel", + "rhombus", + "star", + NULL + }; + int r; + + r = til_settings_get_and_describe_value(settings, + &(til_setting_desc_t){ + .name = "Shape type", + .key = "type", + .regex = "[a-zA-Z]+", + .preferred = type_values[SHAPES_DEFAULT_TYPE], + .values = type_values, + .annotations = NULL + }, + &type, + res_setting, + res_desc); + if (r) + return r; + + if (res_setup) { + int i; + + shapes_setup_t *setup; + + setup = til_setup_new(sizeof(*setup), (void(*)(til_setup_t *))free); + if (!setup) + return -ENOMEM; + + for (i = 0; type_values[i]; i++) { + if (!strcasecmp(type, type_values[i])) { + setup->type = i; + break; + } + } + + if (!type_values[i]) { + til_setup_free(&setup->til_setup); + return -EINVAL; + } + + *res_setup = &setup->til_setup; + } + + return 0; +} + + +til_module_t shapes_module = { + .create_context = shapes_create_context, + .destroy_context = shapes_destroy_context, + .render_fragment = shapes_render_fragment, + .setup = shapes_setup, + .name = "shapes", + .description = "Procedural 2D shapes", + .author = "Vito Caputo ", + .flags = TIL_MODULE_OVERLAYABLE, +}; diff --git a/src/til.c b/src/til.c index 08a574c..8451113 100644 --- a/src/til.c +++ b/src/til.c @@ -37,6 +37,7 @@ extern til_module_t plato_module; extern til_module_t ray_module; extern til_module_t roto_module; extern til_module_t rtv_module; +extern til_module_t shapes_module; extern til_module_t snow_module; extern til_module_t sparkler_module; extern til_module_t spiro_module; @@ -61,6 +62,7 @@ static const til_module_t *modules[] = { &ray_module, &roto_module, &rtv_module, + &shapes_module, &snow_module, &sparkler_module, &spiro_module, -- cgit v1.2.1