On 1/5/2025 3:14 PM, Chris M. Thomasson wrote:
[...]
Ooops, I don't think that is my animation slice code, it just creates a
single frame. This one create multiple slices, or frames of the
volumetric if you will:
ct_anime is the function of interest.
Sorry about that:
____________________________________
/*
Experimental Power Stack Mandelbulb for Owen
By: Chris M. Thomasson
*_____________________________________________________________*/
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <complex.h>
#include <tgmath.h>
#include <stdbool.h>
#define CT_WIDTH 256 // width of plane
#define CT_HEIGHT 256 // height of plane
#define CT_FRAMES 256 // slices of the mbulb
#define CT_ITERS 64 // iterations per pixel
/* The Canvas
___________________________________*/
struct ct_rgb
{
unsigned char r;
unsigned char g;
unsigned char b;
};
struct ct_canvas
{
unsigned long width;
unsigned long height;
struct ct_rgb* buf;
};
bool
ct_canvas_create(
struct ct_canvas* const self,
unsigned long width,
unsigned long height
){
size_t size = width * height * sizeof(*self->buf);
self->buf = calloc(1, size);
if (self->buf)
{
self->width = width;
self->height = height;
return true;
}
return false;
}
void
ct_canvas_destroy(
struct ct_canvas const* const self
){
free(self->buf);
}
bool
ct_canvas_save_ppm(
struct ct_canvas const* const self,
char const* fname
){
FILE* fout = fopen(fname, "wb");
if (fout)
{
char const ppm_head[] =
"P6\n"
"# Chris M. Thomasson Simple 2d Plane ver:0.0.0.0 (pre-alpha)";
fprintf(fout, "%s\n%lu %lu\n%u\n",
ppm_head,
self->width, self->height,
255U);
size_t size = self->width * self->height;
for (size_t i = 0; i < size; ++i)
{
//unsigned int c = self->buf[i];
struct ct_rgb* c = self->buf + i;
fprintf(fout, "%c%c%c", c->r, c->g, c->b);
}
if (! fclose(fout))
{
return true;
}
}
return false;
}
/* The Axes
___________________________________*/
struct ct_axes
{
double xmin;
double xmax;
double ymin;
double ymax;
};
struct ct_axes
ct_axes_from_point(
double complex z,
double radius
){
struct ct_axes axes = {
creal(z) - radius, creal(z) + radius,
cimag(z) - radius, cimag(z) + radius
};
return axes;
}
/* The Plane
___________________________________*/
struct ct_plane
{
struct ct_axes axes;
double xstep;
double ystep;
};
void
ct_plane_init(
struct ct_plane* const self,
struct ct_axes const* axes,
unsigned long width,
unsigned long height
){
self->axes = *axes;
double awidth = self->axes.xmax - self->axes.xmin;
double aheight = self->axes.ymax - self->axes.ymin;
assert(width > 0 && height > 0 && awidth > 0.0);
double daspect = fabs((double)height / width);
double waspect = fabs(aheight / awidth);
if (daspect > waspect)
{
double excess = aheight * (daspect / waspect - 1.0);
self->axes.ymax += excess / 2.0;
self->axes.ymin -= excess / 2.0;
}
else if (daspect < waspect)
{
double excess = awidth * (waspect / daspect - 1.0);
self->axes.xmax += excess / 2.0;
self->axes.xmin -= excess / 2.0;
}
self->xstep = (self->axes.xmax - self->axes.xmin) / width;
self->ystep = (self->axes.ymax - self->axes.ymin) / height;
}
/* The Plot
___________________________________*/
struct ct_plot
{
struct ct_plane plane;
struct ct_canvas* canvas;
};
void
ct_plot_init(
struct ct_plot* const self,
struct ct_axes const* axes,
struct ct_canvas* canvas
){
ct_plane_init(&self->plane, axes, canvas->width - 1, canvas->height
- 1);
self->canvas = canvas;
}
bool
ct_plot_add(
struct ct_plot* const self,
double complex z,
struct ct_rgb const* color
){
long x = (creal(z) - self->plane.axes.xmin) / self->plane.xstep;
long y = (self->plane.axes.ymax - cimag(z)) / self->plane.ystep;
if (x > -1 && x < (long)self->canvas->width &&
y > -1 && y < (long)self->canvas->height)
{
// Now, we can convert to index.
size_t i = x + y * self->canvas->width;
assert(i < self->canvas->height * self->canvas->width);
struct ct_rgb exist = self->canvas->buf[i];
exist.r += 1;
self->canvas->buf[i] = exist;
return true;
}
return true;
}
bool
ct_plot_pixel(
struct ct_plot* const self,
long x,
long y,
struct ct_rgb const* color
){
if (x > -1 && x < (long)self->canvas->width &&
y > -1 && y < (long)self->canvas->height)
{
// Now, we can convert to index.
size_t i = x + y * self->canvas->width;
assert(i < self->canvas->height * self->canvas->width);
self->canvas->buf[i] = *color;
return true;
}
return false;
}
void
ct_plot_clear(
struct ct_plot* const self,
struct ct_rgb const* color
){
size_t size = self->canvas->height * self->canvas->width;
for (size_t i = 0; i < size; ++i)
{
self->canvas->buf[i] = *color;
}
}
double complex
ct_project_to_xy(
struct ct_plot* const self,
long x,
long y
){
double complex p =
(self->plane.axes.xmin + x * self->plane.xstep) +
(self->plane.axes.ymax - y * self->plane.ystep) * I;
return p;
}
bool
ct_plot_point(
struct ct_plot* const self,
double complex z,
struct ct_rgb const* color
){
long x = (creal(z) - self->plane.axes.xmin) / self->plane.xstep;
long y = (self->plane.axes.ymax - cimag(z)) / self->plane.ystep;
if (x > -1 && x < (long)self->canvas->width &&
y > -1 && y < (long)self->canvas->height)
{
// Now, we can convert to index.
size_t i = x + y * self->canvas->width;
assert(i < self->canvas->height * self->canvas->width);
self->canvas->buf[i] = *color;
return true;
}
return false;
}
// slow, so what for now... ;^)
void ct_circle(
struct ct_plot* const plot,
double complex c,
double radius,
unsigned int n
){
double abase = 6.2831853071 / n;
for (unsigned int i = 0; i < n; ++i)
{
double angle = abase * i;
double complex z =
(creal(c) + cos(angle) * radius) +
(cimag(c) + sin(angle) * radius) * I;
struct ct_rgb rgb = { 255, 255, 255 };
ct_plot_point(plot, z, &rgb);
}
}
/* Simple vec3 for just what I need
___________________________________*/
struct ct_vec3
{
double x;
double y;
double z;
};
double ct_vev3_length(
struct ct_vec3 const p
){
double sum = p.x * p.x + p.y * p.y + p.z * p.z;
return sqrt(sum);
}
struct ct_vec3
ct_vec3_add(
struct ct_vec3 const p,
struct ct_vec3 const addend
){
struct ct_vec3 sum = {
p.x + addend.x,
p.y + addend.y,
p.z + addend.z
};
return sum;
}
/* The Power Stack Mandelbulb
___________________________________*/
void
ct_iterate_mbulb_pixel(
struct ct_plot* const plot,
struct ct_vec3 const z0,
struct ct_vec3 const c0,
double power,
long x,
long y,
unsigned int n
){
struct ct_vec3 zs = z0;
struct ct_vec3 cs = c0;
for (unsigned long i = 0; i < n; ++i)
{
double r = ct_vev3_length(zs);
double rpower = pow(r, power);
double angle0 = atan2(zs.z, sqrt(zs.x * zs.x + zs.y * zs.y));
double angle1 = atan2(zs.y, zs.x);
struct ct_vec3 znew;
znew.x = rpower * cos(power * angle1) * cos(power * angle0);
znew.y = rpower * sin(power * angle1) * cos(power * angle0);
znew.z = rpower * sin(power * angle0);
zs = ct_vec3_add(znew, cs);
if (r > 2.0)
{
return;
}
}
struct ct_rgb rgb = { 0, 255, 0 };
ct_plot_pixel(plot, x, y, &rgb);
}
// Gain the field...
void
ct_iterate_mbulb_plane(
struct ct_plot* const plot,
double zaxis,
double power,
unsigned int n
){
for (long y = 0; y < plot->canvas->height; ++y)
{
for (long x = 0; x < plot->canvas->width; ++x)
{
double complex cz = ct_project_to_xy(plot, x, y);
// Well, our per slice coords...
struct ct_vec3 z;
z.x = creal(cz);
z.y = cimag(cz);
z.z = zaxis;
// Iterate the slice
ct_iterate_mbulb_pixel(plot, z, z, power, x, y, n);
}
if (! (y % (plot->canvas->height / 10)))
{
printf("ct_iterate_mbulb_plane: %lu of %lu\r",
y + 1, plot->canvas->height);
fflush(stdout);
}
}
printf("ct_iterate_mbulb_plane: %lu of %lu\n\n",
plot->canvas->height, plot->canvas->height);
fflush(stdout);
}
/* The Animation Driver
___________________________________*/
void ct_anime(
struct ct_plot* const plot,
unsigned long frame_n,
unsigned long n
){
assert(frame_n > 0);
double zmin = 0.;
double zmax = 1.;
double zdif = zmax - zmin;
double zbase = zdif / (frame_n - 1.);
double pmin = 2.;
double pmax = 10.;
double pdif = pmax - pmin;
double pbase = pdif / (frame_n - 1.);
char fname[256] = { '\0' };
for (unsigned long frame_i = 0; frame_i < frame_n; ++frame_i)
{
double zaxis = zmin + zbase * frame_i;
double power = pmin + pbase * frame_i;
sprintf(fname, "ct_anime_%lu.ppm", frame_i);
printf("ct_anime: %lu of %lu, zaxis(%lf), power(%lf), (%s)\n",
frame_i, frame_n, zaxis, power, fname);
fflush(stdout);
struct ct_rgb black = { 0, 0, 0 };
ct_plot_clear(plot, &black);
ct_iterate_mbulb_plane(plot, zaxis, power, n);
ct_canvas_save_ppm(plot->canvas, fname);
}
printf("\n\nct_anime complete! %lu frames: \n", frame_n);
fflush(stdout);
}
void ct_test_plane(
struct ct_plot* const plot
){
printf("Generating...\n\n");
fflush(stdout);
ct_anime(plot, CT_FRAMES, CT_ITERS);
}
int main(void)
{
struct ct_canvas canvas;
if (ct_canvas_create(&canvas, CT_WIDTH, CT_HEIGHT))
{
double complex plane_origin = 0+0*I;
double plane_radius = 1.5;
struct ct_axes axes = ct_axes_from_point(plane_origin,
plane_radius);
struct ct_plot plot;
ct_plot_init(&plot, &axes, &canvas);
ct_test_plane(&plot);
// Our unit circle
ct_circle(&plot, 0+0*I, 1.0, 2048);
ct_circle(&plot, 2+0*I, 2.0, 2048);
ct_circle(&plot, -2+0*I, 2.0, 2048);
ct_circle(&plot, 0+2*I, 2.0, 2048);
ct_circle(&plot, 0-2*I, 2.0, 2048);
ct_canvas_save_ppm(&canvas, "ct_plane.ppm");
ct_canvas_destroy(&canvas);
return EXIT_SUCCESS;
}
return EXIT_FAILURE;
}
____________________________________