1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
|
#include <stdint.h>
#include <inttypes.h>
#include <math.h>
#include <stdlib.h>
#include "fb.h"
#include "rototiller.h"
/* Copyright (C) 2017 Vito Caputo <vcaputo@pengaru.com> */
/* Julia set renderer - see https://en.wikipedia.org/wiki/Julia_set, morphing just means to vary C. */
/* TODO: explore using C99 complex.h and its types? */
typedef struct julia_context_t {
float rr;
float realscale;
float imagscale;
float creal;
float cimag;
float threshold;
unsigned n_cpus;
} julia_context_t;
static uint32_t colors[] = {
/* this palette is just something I slapped together, definitely needs improvement. TODO */
0x000000,
0x000044,
0x000088,
0x0000aa,
0x0000ff,
0x0044ff,
0x0088ff,
0x00aaff,
0x00ffff,
0x44ffaa,
0x88ff88,
0xaaff44,
0xffff00,
0xffaa00,
0xff8800,
0xff4400,
0xff0000,
0xaa0000,
0x880000,
0x440000,
0x440044,
0x880088,
0xaa00aa,
0xff00ff,
0xff4400,
0xff8800,
0xffaa00,
0xffff00,
0xaaff44,
0x88ff88,
0x44ffaa,
0x00ffff,
0x00aaff,
0x0088ff,
0xff4400,
0xff00ff,
0xaa00aa,
0x880088,
0x440044,
};
static void * julia_create_context(unsigned ticks, unsigned num_cpus)
{
return calloc(1, sizeof(julia_context_t));
}
static void julia_destroy_context(void *context)
{
free(context);
}
static inline unsigned julia_iter(float real, float imag, float creal, float cimag, unsigned max_iters, float threshold)
{
unsigned i;
float newr, newi;
for (i = 1; i < max_iters; i++) {
newr = real * real - imag * imag;
newi = imag * real;
newi += newi;
newr += creal;
newi += cimag;
if ((newr * newr + newi * newi) > threshold)
return i;
real = newr;
imag = newi;
}
return 0;
}
static int julia_fragmenter(void *context, const fb_fragment_t *fragment, unsigned number, fb_fragment_t *res_fragment)
{
julia_context_t *ctxt = context;
return fb_fragment_slice_single(fragment, ctxt->n_cpus, number, res_fragment);
}
/* Prepare a frame for concurrent drawing of fragment using multiple fragments */
static void julia_prepare_frame(void *context, unsigned ticks, unsigned n_cpus, fb_fragment_t *fragment, rototiller_fragmenter_t *res_fragmenter)
{
julia_context_t *ctxt = context;
*res_fragmenter = julia_fragmenter;
ctxt->n_cpus = n_cpus;
ctxt->rr += .01;
/* Rather than just sweeping creal,cimag from -2.0-+2.0, I try to keep things confined
* to an interesting (visually) range. TODO: could certainly use refinement.
*/
ctxt->realscale = 0.01f * cosf(ctxt->rr) + 0.01f;
ctxt->imagscale = 0.01f * sinf(ctxt->rr * 3.0f) + 0.01f;
ctxt->creal = (1.01f + (ctxt->realscale * cosf(1.5f * M_PI + ctxt->rr) + ctxt->realscale)) * cosf(ctxt->rr * .3f);
ctxt->cimag = (1.01f + (ctxt->imagscale * sinf(ctxt->rr * 3.0f) + ctxt->imagscale)) * sinf(ctxt->rr);
/* Vary the divergent threshold, this has been tuned to dwell around 1 a bit since it's
* quite different looking, then shoot up to a very huge value approaching FLT_MAX which
* is also interesting.
*/
ctxt->threshold = cosf(M_PI + ctxt->rr * .1f) * .5f + .5f;
ctxt->threshold *= ctxt->threshold * ctxt->threshold;
ctxt->threshold *= 35.f;
ctxt->threshold = powf(10.f, ctxt->threshold);
}
/* Draw a morphing Julia set */
static void julia_render_fragment(void *context, unsigned ticks, unsigned cpu, fb_fragment_t *fragment)
{
julia_context_t *ctxt = context;
unsigned x, y;
unsigned width = fragment->width, height = fragment->height;
uint32_t *buf = fragment->buf;
float real, imag;
float realstep = 3.6f / (float)fragment->frame_width, imagstep = 3.6f / (float)fragment->frame_height;
/* Complex plane confined to {-1.8 - 1.8} on both axis (slightly zoomed), no dynamic zooming is performed. */
for (imag = 1.8 + -(imagstep * (float)fragment->y), y = fragment->y; y < fragment->y + height; y++, imag += -imagstep) {
for (real = -1.8 + realstep * (float)fragment->x, x = fragment->x; x < fragment->x + width; x++, buf++, real += realstep) {
*buf = colors[julia_iter(real, imag, ctxt->creal, ctxt->cimag, sizeof(colors) / sizeof(*colors), ctxt->threshold)];
}
buf = ((void *)buf) + fragment->stride;
}
}
rototiller_module_t julia_module = {
.create_context = julia_create_context,
.destroy_context = julia_destroy_context,
.prepare_frame = julia_prepare_frame,
.render_fragment = julia_render_fragment,
.name = "julia",
.description = "Julia set fractal morpher (threaded)",
.author = "Vito Caputo <vcaputo@pengaru.com>",
.license = "GPLv2",
};
|