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.
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.
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.
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.
And the CAD file: Download Hinge
3D Scanning Link to heading
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);
}