summaryrefslogtreecommitdiff
path: root/src/modules/spiro/spiro.c
diff options
context:
space:
mode:
authorPhilip J Freeman <elektron@halo.nu>2020-01-01 15:04:34 -0800
committerVito Caputo <vcaputo@pengaru.com>2020-01-06 11:45:03 -0800
commit3a17d7380b5f024992b032dcdcad2c2aa16df99e (patch)
treea147783354fb36f8864a8d23fa6dde703ee0362c /src/modules/spiro/spiro.c
parent3ae1e53ac08887a0e8209499aee4daade9c5ea8e (diff)
spiro: spirograph emulator
This commit adds a module that emulates a spirograph
Diffstat (limited to 'src/modules/spiro/spiro.c')
-rw-r--r--src/modules/spiro/spiro.c155
1 files changed, 155 insertions, 0 deletions
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",
+};
© All Rights Reserved