Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Crude code for a new fractal...

59 views
Skip to first unread message

Chris M. Thomasson

unread,
Jul 6, 2022, 3:33:17 PM7/6/22
to
I asked around to some very knowledgeable fractal geniuses, and so far,
nobody has seen it before. To make this more on topic, here is my quick
and dirty code that generates a PPM. It uses the GLM library that can be
found here:

https://github.com/g-truc/glm

For those that are using vcpkg for MSVC just install it using:

.\vcpkg install glm

https://vcpkg.io/en/index.html

Actually, this tool makes installing packages rather easy... Ahh that is
another topic altogether. Anyway, here is my code. I only compiled it
with MSVC so far in C++14 mode. I have to try some other compilers. Can
you get it to compile and generate a ppm called "ct_p0.ppm" on your end?
Thanks everybody:
_______________________________
#include <iostream>
#include <vector>
#include <cstdio>

#include <glm/glm.hpp>


#define CT_WIDTH 1920
#define CT_HEIGHT 1080
#define CT_PI 3.14159f
#define CT_PI2 (CT_PI * 2)


// The PPM Canvas
namespace ct
{
namespace ppm
{
struct canvas
{
unsigned long m_width;
unsigned long m_height;
std::vector<glm::ivec3> m_buf;

canvas(
unsigned long width,
unsigned long height
): m_width(width),
m_height(height),
m_buf(width * height)
{
std::cout << "(" << this << ")->canvas::canvas(" <<
width << ", " << height << ")\n";
}


bool
save(
char const* fname
) {
std::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)";

std::fprintf(fout, "%s\n%lu %lu\n%u\n",
ppm_head,
m_width, m_height,
255U
);

std::size_t size = m_width * m_height;

for (std::size_t i = 0; i < size; ++i)
{
glm::ivec3& pixel = m_buf[i];

std::fprintf(fout, "%c%c%c", pixel.r, pixel.g,
pixel.b);
}

if (! std::fclose(fout))
{
std::cout << "(" << this << ")->canvas::save("
<< fname << ")\n";
return true;
}
}

return false;
}
};
}
}



// The Plane
namespace ct
{
namespace plot2d
{
glm::vec4
axes_from_point(
glm::vec2 z,
float radius
) {
return {
z.x - radius, z.x + radius,
z.y - radius, z.y + radius
};
}

struct plane
{
glm::vec4 m_axes;
glm::vec4 m_step;

plane(
glm::vec4 const& axes,
unsigned long width,
unsigned long height
) : m_axes(axes)
{
float awidth = m_axes.y - m_axes.x;
float aheight = m_axes.w - m_axes.z;

float daspect = glm::abs((float)height / width);
float waspect = glm::abs(aheight / awidth);

if (daspect > waspect)
{
float excess = aheight * (daspect / waspect - 1.0f);
m_axes.w += excess / 2.0f;
m_axes.z -= excess / 2.0f;
}

else if (daspect < waspect)
{
float excess = awidth * (waspect / daspect - 1.0f);
m_axes.y += excess / 2.0f;
m_axes.x -= excess / 2.0f;
}

m_step.x = (m_axes.y - m_axes.x) / width;
m_step.y = (m_axes.w - m_axes.z) / height;
}
};
}
}


// The Plotter
namespace ct
{
namespace plot2d
{
struct plot
{
plane m_plane;
ppm::canvas& m_canvas;

plot(
plane const& plane,
ppm::canvas& canvas
) : m_plane(plane),
m_canvas(canvas)
{

}

bool
plot_pixel(
long x,
long y,
glm::vec3 const& color
) {
if (x > -1 && x < (long)m_canvas.m_width &&
y > -1 && y < (long)m_canvas.m_height)
{
// Now, we can convert to index.
std::size_t i = x + y * m_canvas.m_width;

glm::ivec3 rgb_color = { color.x * 255, color.y *
255, color.z * 255 };

m_canvas.m_buf[i] = rgb_color;
return true;
}

return false;
}

bool
plot_point(
glm::vec2 z,
glm::vec3 const& color
) {
long x = (long)((z.x - m_plane.m_axes.x) /
m_plane.m_step.x);
long y = (long)((m_plane.m_axes.w - z.y) /
m_plane.m_step.y);

if (x > -1 && x < (long)m_canvas.m_width &&
y > -1 && y < (long)m_canvas.m_height)
{
// Now, we can convert to index.
std::size_t i = x + y * m_canvas.m_width;

glm::ivec3 rgb_color = { color.x * 255, color.y *
255, color.z * 255 };

m_canvas.m_buf[i] = rgb_color;

return true;
}

return false;
}
};
}
}



// TTR
namespace ct
{
namespace ttr
{
struct circle
{
glm::vec3 p;
float r;
};

#define SQ(a) ((a) * (a))

bool
intersect(
circle const& c0,
circle const& c1,
circle& i0,
circle& i1
) {
// load from the two input circles c0 and c1
float A = c0.p.x;
float B = c0.p.y;
float C = c0.r;

float O = c1.p.x;
float P = c1.p.y;
float F = c1.r;


// gather delta's between c0 and c1
float G = A - O;
float H = B - P;


// evaluate the formulas conditions
if ((SQ(O - A) + SQ(P - B)) == 0 ||
(4 * SQ(O - A) + 4 * SQ(P - B)) == 0 ||
(((-SQ(SQ(C) - SQ(F) + SQ(O - A) + SQ(H))) /
(4 * SQ(O - A) + 4 * SQ(P - B)) +
SQ(C)) / (SQ(O - A) + SQ(P - B))) < 0.f)
{
return false;
}

float Q = -2 * SQ(A) + 4 * A * O - 2 * SQ(B) + 4 * B * P -
2 * SQ(O) - 2 * SQ(P);

float J = glm::sqrt(((-SQ(SQ(C) - SQ(F) + SQ(G) + SQ(H))) /
(4 * SQ(G) + 4 * SQ(H)) + SQ(C)) / (SQ(G) + SQ(H)));

float K = (-G) * (SQ(F) - SQ(C)) / Q + A / 2 + O / 2;
float L = (-H) * (SQ(F) - SQ(C)) / Q + B / 2 + P / 2;

float M = K - H * J; // x i0
float R = G * J + L; // y i0
float S = H * J + K; // x i1
float T = L - G * J; // y i1

i0.p.x = M;
i0.p.y = R;
i0.p.z = 0;
i0.r = 0;

i1.p.x = S;
i1.p.y = T;
i1.p.z = 0;
i1.r = 0;

return true;
}
}
}




// Test Fractal
namespace ct
{
namespace test_fractal
{
bool
find_tangent(
ct::ttr::circle c0,
ct::ttr::circle c1,
ct::ttr::circle& t0,
ct::ttr::circle& t1,
float radius
) {
// add the radius to the circles
c0.r += radius;
c1.r += radius;

// Find the intersections
if (!ct::ttr::intersect(c0, c1, t0, t1)) return false;

// Set the radius of the intersections
t0.r = radius;
t1.r = radius;

return true;
}



void
recur(
ct::plot2d::plot& scene,
unsigned long ri,
unsigned long rn,
ct::ttr::circle c0,
ct::ttr::circle c1,
float radius,
float scale
) {
if (ri >= rn) return;

// Find the tangent circles
ct::ttr::circle t0;
ct::ttr::circle t1;

if (! find_tangent(c0, c1, t0, t1, radius))
{
return;
}

// Plot points
scene.plot_point(t0.p, { 1, 1, 1 });
scene.plot_point(t1.p, { 1, 1, 1 });

float radius_next = radius * scale;

recur(scene, ri + 1, rn, c0, t0, radius_next, scale);
recur(scene, ri + 1, rn, c1, t0, radius_next, scale);
recur(scene, ri + 1, rn, c0, t1, radius_next, scale);
recur(scene, ri + 1, rn, c1, t1, radius_next, scale);
}
}
}




// Circle Test
void
ct_plot_circle(
ct::plot2d::plot& scene,
glm::vec3 origin,
float origin_radius
) {
unsigned long n = 512;

float angle_base = CT_PI2 / n;

for (unsigned long i = 0; i < n; ++i)
{
float angle = angle_base * i;

glm::vec3 p0_normal = {
glm::cos(angle),
glm::sin(angle),
0
};

glm::vec3 p0 = origin + p0_normal * origin_radius;

scene.plot_point(p0, { 1, 0, 1 });
}
}



int
main(void)
{
std::cout << "ct_ppm_base\n";
std::cout << "________________________________\n" << std::endl;

{
// Setup plotter...
ct::ppm::canvas canvas(CT_WIDTH, CT_HEIGHT);
glm::vec4 axes = ct::plot2d::axes_from_point({ 0, 0 }, 2.f);
ct::plot2d::plane plane(axes, canvas.m_width, canvas.m_height);
ct::plot2d::plot scene(plane, canvas);

// Plot test fractal
{
ct::ttr::circle c0 = { { -.5, 0, 0 }, .5 };
ct::ttr::circle c1 = { { .5, 0, 0 }, .5 };

ct::test_fractal::recur(scene, 0, 10, c0, c1, .8f, .45f);
}

// Plot unit circle
ct_plot_circle(scene, { 0, 0, 0 }, 1.f);

scene.m_canvas.save("ct_p0.ppm");
}

return 0;
}
_______________________________

Take a look at the code and tear it apart. Tell me about any issues.
What am I doing wrong? ;^)

Thanks again.

Chris M. Thomasson

unread,
Jul 6, 2022, 3:40:32 PM7/6/22
to
On 7/6/2022 12:32 PM, Chris M. Thomasson wrote:
> I asked around to some very knowledgeable fractal geniuses, and so far,
> nobody has seen it before. To make this more on topic, here is my quick
> and dirty code that generates a PPM. It uses the GLM library that can be
> found here:
[...]

> I only compiled it
> with MSVC so far in C++14 mode. I have to try some other compilers. Can
> you get it to compile and generate a ppm called "ct_p0.ppm" on your end?
> Thanks everybody:

One nitpick on my own crude work:
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Humm. I should really cast pixel.r/g/b components into 8-bit quantity's
here... Any thoughts?

>                     }
>
>                     if (! std::fclose(fout))
>                     {
>                         std::cout << "(" << this << ")->canvas::save("
> << fname << ")\n";
>                         return true;
>                     }
>                 }
[...]

Ben Bacarisse

unread,
Jul 6, 2022, 6:26:56 PM7/6/22
to
"Chris M. Thomasson" <chris.m.t...@gmail.com> writes:

> Can you get it to compile and generate a ppm called "ct_p0.ppm" on
> your end?

Compiles and runs on Ubuntu with gcc.

--
Ben.

Chris M. Thomasson

unread,
Jul 6, 2022, 9:42:25 PM7/6/22
to
Ahhh, perfect. Thanks, Ben. Funny thing is I was just about to install
GCC on the computer before I read this. Can you view the resulting PPM?
It should open right up. :^)

Ben Bacarisse

unread,
Jul 7, 2022, 6:22:39 AM7/7/22
to
Yes.

--
Ben.

Chris M. Thomasson

unread,
Jul 7, 2022, 5:22:48 PM7/7/22
to
On 7/7/2022 3:22 AM, Ben Bacarisse wrote:
> "Chris M. Thomasson" <chris.m.t...@gmail.com> writes:
>
>> On 7/6/2022 3:26 PM, Ben Bacarisse wrote:
>>> "Chris M. Thomasson" <chris.m.t...@gmail.com> writes:
>>>
>>>> Can you get it to compile and generate a ppm called "ct_p0.ppm" on
>>>> your end?
>>> Compiles and runs on Ubuntu with gcc.
>>
>> Ahhh, perfect. Thanks, Ben. Funny thing is I was just about to install
>> GCC on the computer before I read this. Can you view the resulting
>> PPM? It should open right up. :^)
>
> Yes.
>

Great news, Ben. I should really put this up on GitHub. I am still
porting my original code that used the Cairo graphics lib. You will be
able to get renderings like:

https://i.ibb.co/Vqr253K/ct-p0.png

https://i.ibb.co/f1D0grq/ct-p2.png

Actually, there is a decent amount of work to port. A lot of different
coloring algorithms, ect... I know I can just post my code using Cario,
but who wants to install Cario just to plot one of my fractals? GLM is
pretty easy, header only, nice lib. Cairo has a decent number of
prerequisite libs that need to be installed as well. vcpkg finally makes
it "easy" for the MSVC users, just run:

.\vcpkg install cairo

and, it compiles everything needed, and BAM... It's there. :^)

Chris M. Thomasson

unread,
Jul 7, 2022, 5:55:35 PM7/7/22
to
On 7/6/2022 12:32 PM, Chris M. Thomasson wrote:
> I asked around to some very knowledgeable fractal geniuses, and so far,
> nobody has seen it before. To make this more on topic, here is my quick
> and dirty code that generates a PPM. It uses the GLM library that can be
> found here:
>
> https://github.com/g-truc/glm
>
> For those that are using vcpkg for MSVC just install it using:
>
> .\vcpkg install glm
>
> https://vcpkg.io/en/index.html
>
> Actually, this tool makes installing packages rather easy... Ahh that is
> another topic altogether. Anyway, here is my code. I only compiled it
> with MSVC so far in C++14 mode. I have to try some other compilers. Can
> you get it to compile and generate a ppm called "ct_p0.ppm" on your end?
> Thanks everybody:
[...]

Fwiw, here is another thread about my potentially new fractal:

https://fractalforums.org/fractal-mathematics-and-new-theories/28/have-you-seen-this-fractal-before/4804

https://fractalforums.org/fractal-mathematics-and-new-theories/28/experimental-tangent-fractal/4817


Chris M. Thomasson

unread,
Jul 11, 2022, 3:20:44 AM7/11/22
to
On 7/6/2022 12:32 PM, Chris M. Thomasson wrote:
> [....]

Hummm.. I am missing a damn std:: for fopen! damn it!

0 new messages