From 3a17d7380b5f024992b032dcdcad2c2aa16df99e Mon Sep 17 00:00:00 2001
From: Philip J Freeman <elektron@halo.nu>
Date: Wed, 1 Jan 2020 15:04:34 -0800
Subject: spiro: spirograph emulator

This commit adds a module that emulates a spirograph
---
 src/Makefile.am               |   2 +-
 src/modules/Makefile.am       |   2 +-
 src/modules/spiro/Makefile.am |   3 +
 src/modules/spiro/draw.h      |  16 +++++
 src/modules/spiro/spiro.c     | 155 ++++++++++++++++++++++++++++++++++++++++++
 src/rototiller.c              |   2 +
 6 files changed, 178 insertions(+), 2 deletions(-)
 create mode 100644 src/modules/spiro/Makefile.am
 create mode 100644 src/modules/spiro/draw.h
 create mode 100644 src/modules/spiro/spiro.c

(limited to 'src')

diff --git a/src/Makefile.am b/src/Makefile.am
index 5446acd..43c79e7 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -4,4 +4,4 @@ rototiller_SOURCES = fb.c fb.h fps.c fps.h rototiller.c rototiller.h sdl_fb.c se
 if ENABLE_DRM
 rototiller_SOURCES += drm_fb.c
 endif
-rototiller_LDADD = modules/flui2d/libflui2d.a modules/julia/libjulia.a modules/meta2d/libmeta2d.a modules/montage/libmontage.a modules/pixbounce/libpixbounce.a modules/plasma/libplasma.a modules/ray/libray.a modules/roto/libroto.a modules/rtv/librtv.a modules/snow/libsnow.a modules/sparkler/libsparkler.a modules/stars/libstars.a modules/submit/libsubmit.a modules/swab/libswab.a libs/grid/libgrid.a libs/ray/libray.a libs/txt/libtxt.a libs/ascii/libascii.a libs/din/libdin.a -lm
+rototiller_LDADD = modules/flui2d/libflui2d.a modules/julia/libjulia.a modules/meta2d/libmeta2d.a modules/montage/libmontage.a modules/pixbounce/libpixbounce.a modules/plasma/libplasma.a modules/ray/libray.a modules/roto/libroto.a modules/rtv/librtv.a modules/snow/libsnow.a modules/sparkler/libsparkler.a modules/spiro/libspiro.a modules/stars/libstars.a modules/submit/libsubmit.a modules/swab/libswab.a libs/grid/libgrid.a libs/ray/libray.a libs/txt/libtxt.a libs/ascii/libascii.a libs/din/libdin.a -lm
diff --git a/src/modules/Makefile.am b/src/modules/Makefile.am
index a4ff9e7..958d835 100644
--- a/src/modules/Makefile.am
+++ b/src/modules/Makefile.am
@@ -1 +1 @@
-SUBDIRS = flui2d julia meta2d montage pixbounce plasma ray roto rtv snow sparkler stars submit swab
+SUBDIRS = flui2d julia meta2d montage pixbounce plasma ray roto rtv snow sparkler spiro stars submit swab
diff --git a/src/modules/spiro/Makefile.am b/src/modules/spiro/Makefile.am
new file mode 100644
index 0000000..f03d8cd
--- /dev/null
+++ b/src/modules/spiro/Makefile.am
@@ -0,0 +1,3 @@
+noinst_LIBRARIES = libspiro.a
+libspiro_a_SOURCES = draw.h spiro.c
+libspiro_a_CPPFLAGS = -I@top_srcdir@/src
diff --git a/src/modules/spiro/draw.h b/src/modules/spiro/draw.h
new file mode 100644
index 0000000..0b68c00
--- /dev/null
+++ b/src/modules/spiro/draw.h
@@ -0,0 +1,16 @@
+#ifndef _DRAW_H
+#define _DRAW_H
+
+#include <stdint.h>
+
+/* helper for scaling rgb colors and packing them into an pixel */
+static inline uint32_t makergb(uint32_t r, uint32_t g, uint32_t b, float intensity)
+{
+	r = (((float)intensity) * r);
+	g = (((float)intensity) * g);
+	b = (((float)intensity) * b);
+
+	return (((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff));
+}
+
+#endif
diff --git a/src/modules/spiro/spiro.c b/src/modules/spiro/spiro.c
new file mode 100644
index 0000000..f6aeddc
--- /dev/null
+++ b/src/modules/spiro/spiro.c
@@ -0,0 +1,155 @@
+#include <math.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "draw.h"
+#include "fb.h"
+#include "rototiller.h"
+
+/* Copyright (C) 2020 Philip J. Freeman <elektron@halo.nu> */
+
+/*
+
+Spirograph Emulator
+
+  refs:
+
+    - https://en.wikipedia.org/wiki/Spirograph#Mathematical_basis
+    - https://en.wikipedia.org/wiki/Unit_circle#Trigonometric_functions_on_the_unit_circle
+*/
+
+typedef struct spiro_context_t {
+	float r;
+	int r_dir;
+	float p;
+	int p_dir;
+} spiro_context_t;
+
+
+static void * spiro_create_context(unsigned num_cpus)
+{
+	spiro_context_t *ctxt;
+	float		z;
+
+	ctxt = malloc(sizeof(spiro_context_t));
+	if (!ctxt)
+		return NULL;
+
+	srand(time(NULL) + getpid());
+
+	ctxt->r=.25f+(rand()/(float)RAND_MAX)*.5f;
+	if(ctxt->r>.5f)
+		ctxt->r_dir=-1;
+	else
+		ctxt->r_dir=1;
+	ctxt->p=(rand()/(float)RAND_MAX)*ctxt->r;
+	ctxt->p_dir=ctxt->r_dir*-1;
+#ifdef DEBUG
+	printf("spiro: initial context: r=%f, dir=%i, p=%f, dir=%i\n", ctxt->r, ctxt->r_dir, ctxt->p, ctxt->p_dir);
+#endif
+	return ctxt;
+}
+
+static void spiro_destroy_context(void *context)
+{
+	spiro_context_t *ctxt = context;
+
+	free(context);
+}
+
+
+static void spiro_render_fragment(void *context, unsigned cpu, fb_fragment_t *fragment)
+{
+	spiro_context_t	*ctxt = context;
+
+	int		width = fragment->width, height = fragment->height;
+
+	int display_R, display_origin_x, display_origin_y;
+
+	/* Based on the fragment's dimensions, calculate the origin and radius of the fixed outer
+	circle, C0. */
+
+	if(width>=height) {			 // landscape or square aspect ratio
+		display_R=(height-1)*0.5f;
+		display_origin_x=((width-height)*.5f)+display_R;
+		display_origin_y=display_R;
+	} else {				// portrait
+		display_R=(width-1)*.5f;
+		display_origin_x=display_R;
+		display_origin_y=((height-width)*.5f)+display_R;
+	}
+
+	/* blank the fragment */
+	fb_fragment_zero(fragment);
+
+	/* plot one spirograph run */
+	float l=ctxt->p/ctxt->r;
+	float k=ctxt->r;
+	for(float t=0.f; t<128*2*M_PI; t+= M_PI/display_R) {
+		float my_x=((1.f-k)*cosf(t))+(l*k*cosf(((1.f-k)/k)*t));
+		float my_y=((1.f-k)*sinf(t))-(l*k*sinf(((1.f-k)/k)*t));
+		int pos_x=display_origin_x+(my_x*display_R);
+		int pos_y=display_origin_y+(my_y*display_R);
+		fb_fragment_put_pixel_unchecked(fragment, pos_x, pos_y,
+			makergb(sinf(M_1_PI*t)*127+128,
+				sinf(M_1_PI*t+(2*M_PI*.333333333333f))*127+128,
+				sinf(M_1_PI*t+(4*M_PI*.333333333333f))*127+128,
+				0.76));
+	}
+
+#ifdef DEBUG
+	/* plot the origin point */
+	fb_fragment_put_pixel_unchecked(fragment, display_origin_x, display_origin_y,
+		makergb(0xFF, 0xFF, 0x00, 1));
+
+	/* plot the fixed outer circle C0 */
+	for(float a=0.f; a<2*M_PI; a+= M_PI_2/display_R) {
+		int pos_x=display_origin_x+(cosf(a)*display_R);
+		int pos_y=display_origin_y+(sinf(a)*display_R);
+		fb_fragment_put_pixel_unchecked(fragment, pos_x, pos_y,
+			makergb(0xFF, 0xFF, 0x00, 1));
+	}
+
+	/* plot inner circle Ci */
+	fb_fragment_put_pixel_unchecked(fragment, display_origin_x+display_R-(ctxt->r*display_R),
+		display_origin_y, makergb(0xFF, 0xFF, 0x00, 1));
+
+	for(float a=0.f; a<2*M_PI; a+= M_PI_2/display_R) {
+		int pos_x=display_origin_x+display_R-(ctxt->r*display_R)+
+			(cosf(a)*ctxt->r*display_R);
+		int pos_y=display_origin_y+(sinf(a)*ctxt->r*display_R);
+		fb_fragment_put_pixel_unchecked(fragment, pos_x, pos_y,
+			makergb(0xFF, 0xFF, 0x00, 1));
+	}
+
+	/* plot p */
+	fb_fragment_put_pixel_unchecked(fragment, display_origin_x+display_R-(ctxt->r*display_R)+
+		(ctxt->p*display_R), display_origin_y, makergb(0xFF, 0xFF, 0x00, 1));
+#endif
+
+	/* check bounds and increment r & p */
+	float next_r=ctxt->r+(.00001f*ctxt->r_dir);
+	if(next_r >= 1.f || next_r <= 0.f || next_r <= ctxt->p)
+		ctxt->r_dir=ctxt->r_dir*-1;
+	else
+		ctxt->r=ctxt->r+(.00001f*ctxt->r_dir);
+
+	float next_p=ctxt->p+(.0003f*ctxt->p_dir);
+	if(next_p >= ctxt->r || next_p <= 0)
+		ctxt->p_dir=ctxt->p_dir*-1;
+	else
+		ctxt->p=ctxt->p+(.0003f*ctxt->p_dir);
+
+}
+
+rototiller_module_t	spiro_module = {
+	.create_context  = spiro_create_context,
+	.destroy_context = spiro_destroy_context,
+	.render_fragment = spiro_render_fragment,
+	.name = "spiro",
+	.description = "Spirograph Emulator",
+	.author = "Philip J Freeman <elektron@halo.nu>",
+	.license = "GPLv2",
+};
diff --git a/src/rototiller.c b/src/rototiller.c
index d8fa665..644013b 100644
--- a/src/rototiller.c
+++ b/src/rototiller.c
@@ -43,6 +43,7 @@ extern rototiller_module_t	roto_module;
 extern rototiller_module_t	rtv_module;
 extern rototiller_module_t	snow_module;
 extern rototiller_module_t	sparkler_module;
+extern rototiller_module_t	spiro_module;
 extern rototiller_module_t	stars_module;
 extern rototiller_module_t	submit_module;
 extern rototiller_module_t	swab_module;
@@ -59,6 +60,7 @@ static const rototiller_module_t	*modules[] = {
 	&rtv_module,
 	&snow_module,
 	&sparkler_module,
+	&spiro_module,
 	&stars_module,
 	&submit_module,
 	&swab_module,
-- 
cgit v1.2.3