The vision Link to heading

At first, I attempted to leverage the power of programmatic modeling in order to build something with a high degree of detail. The natural progression of things lead me to start researching fractals.

fractalflames

Fractal Flames example

The fractals Link to heading

At first I investigated the fractal flame algorithm due to its visual appeal. However, whileentranched into its beauty, I failed to realize that an Iterated Function system was incompatible with what 3d printing was able to offer me. I needed a 3d design that was focused on already formed primitives, not on simple points.

After this failure, I pivoted towards flowers which can be viewed as nature’s attempt at beauty through regular structures. After investigating several patterns for the leaves and the flower itself and trying to reproduce them in OpenScad. The main issue is that flowers are comprised of curved primitives, while OpenScad specializes in polygons.

After finally pivoting to something as polygonal as possible, I finally stumbled upon the dragon curve.

dragoncurve

Dragon Curve

The main issue with this fractal is the fact that it is 2-dimensional, however by applying small changes to the algorithm I was able to bring it to 3 dimensions.

The final design Link to heading

In order to make my project a bit more exciting than a simple fractal, I decided to choose a nice shape and overlay dragon curves over it.

sphere

Dragon Curves on Sphere

spiral

Dragon Curves on Spiral

3D Printing Link to heading

As for the 3D print itself, I had to add plenty of supports because I couldn’t not postprocess the build in order to remove the parts without any support.

I decided that in order to make up for my mistake to come up with another simple build in the form of a hinge. Incidentally, the hinge would actually properly fullfill the requirement of making something that can’t be made subtractively.

spiral

Hinge print

And the CAD file: Download Hinge

3D Scanning Link to heading

spiral

Cat scan

For the scan I only have a photo of the display of the computer since I arrived at a busy time and it would’ve taken a while to generate a proper object file. Overall, it seems like the scan worked moderately well since I choose an object that would be easy to scan (small and white).

The code used to generate the fractal Link to heading

$fn = 100 ;

// Parameters
radius = 2;         // Radius of the helix
pitch = 4;          // Pitch (vertical distance between turns)
turns = 2;          // Number of turns of the helix
helix_height = turns * pitch;  // Total height of the helix
seed_value = 42;

num_curves = 450;            // Number of dragon curves to generate
sphere_radius = 10;         // Radius of the sphere
dragon_curve_iterations = 5;
dragon_curve_length = 0.8;
internal_cube_size = 0.8;

// Dragon curve module
module dragon_curve_3d(iter, length) {
    if (iter == 0) {
        // Base case: draw a straight line segment in the current direction
        random_rotation_noise = [
            rands(-180, 180, 1)[0],  // Random noise for X-axis
            rands(-180, 180, 1)[0],  // Random noise for Y-axis
            rands(-180, 180, 1)[0]   // Random noise for Z-axis
        ];
        translate([length / 2, 0, 0])
        rotate(random_rotation_noise)
        cube([length, internal_cube_size, internal_cube_size], center=true); // Thicker lines
    } else {
        // Recursive case: generate dragon curve in 3D
        dragon_curve_3d(iter - 1, length / sqrt(2));

        // Apply random noise to the rotation
        random_rotation_noise = [
            rands(-5, 5, 1)[0],  // Random noise for X-axis
            rands(-5, 5, 1)[0],  // Random noise for Y-axis
            rands(-5, 5, 1)[0]   // Random noise for Z-axis
        ];

        // Determine if we should change direction randomly
        if (rands(1, 0, 1)[0] < 0.5) {
            // Change direction randomly
            translate([length / sqrt(2), 0, 0]) {
                rotate([random_rotation_noise[0], random_rotation_noise[1], 90 + rands(1, -45, 45)[0]])  // Apply random rotation noise
                    dragon_curve_3d(iter - 1, length / sqrt(2));
            }
        } else {
            // Normal direction with added noise
            translate([length / sqrt(2), 0, 0]) {
                rotate([random_rotation_noise[0], 90 + random_rotation_noise[1], random_rotation_noise[2]])  // Apply random noise in rotation
                    dragon_curve_3d(iter - 1, length / sqrt(2));
            }
        }
    }
}


// Function to convert spherical coordinates to Cartesian coordinates
function spherical_to_cartesian(r, theta, phi) = [
    r * sin(phi) * cos(theta),
    r * sin(phi) * sin(theta),
    r * cos(phi)
];

// Draw random points on the double helix
module draw_random_curves(sphere_radius) {
    for (i = [1:num_curves]) {
        // Generate random angles using rands
        theta = rands(0, 360, 1)[0];  // Azimuthal angle
        phi = rands(0, 180, 1)[0];     // Polar angle

        // Get Cartesian coordinates
        position = spherical_to_cartesian(sphere_radius, theta, phi);

        // Calculate inward direction
        inward_direction = [-position[0], -position[1], -position[2]]; // Negate position for inward

        // Random orientation for each dragon curve
        random_rotation = [
            rands(1, 0, 360)[0],
            rands(1, 0, 360)[0],
            rands(1, 0, 360)[0]
        ];

        // Random color for each dragon curve
        random_color = [
            rands(1, 0, 1)[0],
            rands(1, 0, 1)[0],
            rands(1, 0, 1)[0]
        ];

        // Place the dragon curve at the random position with random rotation and color
        translate(position) {
            rotate(random_rotation)
                color(random_color)
                    rotate([0, 0, 180]) // Invert the direction to point inward
                    dragon_curve_3d(dragon_curve_iterations, dragon_curve_length);
        }
    }
}


// Function to convert radians to degrees
function rad_to_deg(radians) = radians * 180 / PI;

// Function to generate random points on the first helix
function helix_1(t, r, p) = [
    r * cos(rad_to_deg(t)),          // x-coordinate, convert t from radians to degrees
    r * sin(rad_to_deg(t)),          // y-coordinate, convert t from radians to degrees
    p * t / (2 * PI)                 // z-coordinate (scaled by pitch)
];

// Function to generate random points on the second helix
function helix_2(t, r, p) = [
    r * cos(rad_to_deg(2 * PI - (t + PI))),     // x-coordinate (shifted by 180 degrees), converted to degrees
    r * sin(rad_to_deg(2 * PI - (t + PI))),     // y-coordinate (shifted by 180 degrees), converted to degrees
    p * t / (2 * PI)                 // z-coordinate (same height as helix 1)
];

// Draw the helices for visualization, with each helix in a different color
module draw_double_helix(radius, pitch, turns) {
    // Color the first helix red
    color("red") {
        for (t = [0:0.5:2 * PI * turns]) {
            translate(helix_1(t, radius, pitch)) {
                dragon_curve_3d(dragon_curve_iterations, dragon_curve_length);
            }
        }
    }
    // Color the second helix blue
    color("blue") {
        for (t = [0:0.5:2 * PI * turns]) {
            translate(helix_2(t, radius, pitch)) {
                dragon_curve_3d(dragon_curve_iterations, dragon_curve_length);
            }
        }
    }
}

module draw_double_helix_2(radius, pitch, turns) {
    // Color the first helix green
    color("green") {
        for (t = [0:0.5:2 * PI * turns]) {
            translate(helix_1(t, radius, pitch)) {
                dragon_curve_3d(dragon_curve_iterations, dragon_curve_length);
            }
        }
    }
    // Color the second helix purple
    color("yellow") {
        for (t = [0:0.5:2 * PI * turns]) {
            translate(helix_2(t, radius, pitch)) {
                dragon_curve_3d(dragon_curve_iterations, dragon_curve_length);
            }
        }
    }
}

// Main code: Draw the helices with different colors
draw_double_helix(radius, pitch, turns);

// Optional: Rotate and draw the helix
rotate([0, 0, 180]) {
    draw_double_helix_2(radius, pitch, turns);
}