| // Brachistochrone Curve Marble Run Track Section
|
| // Generates the first half of the arch (0-180 deg)
|
| // Creates a channel suitable for a 20mm marble.
|
| // Assumes units are in mm.
|
|
|
| // --- Parameters ---
|
|
|
| // Marble Properties
|
| marble_diameter = 20; // mm
|
| marble_radius = marble_diameter / 2;
|
|
|
| // Track Design Properties
|
| channel_clearance = 1; // Extra space around marble in channel (mm) on each side
|
| wall_thickness = 3; // Thickness of the track walls (mm)
|
| base_thickness = 3; // Thickness of the track base below the channel lowest point (mm)
|
|
|
| // Curve Definition
|
| // Radius 'r' of the generating circle (mm).
|
| // This determines the overall scale and "depth" of the curve arch.
|
| // Adjust this to control the curve steepness and overall size for your run.
|
| // A larger radius makes the curve shallower relative to its length.
|
| cycloid_radius = 50;
|
| theta_max_deg = 180; // Use 180 degrees for the first half of the cycloid arch (starts steep, ends flat)
|
|
|
| // Resolution and Rendering
|
| steps = 100; // Number of points along the curve (more = smoother but slower)
|
| $fa = 1;
|
| $fs = 0.4;
|
|
|
| // --- Calculated Track Dimensions ---
|
| // Radius of the channel to be cut out (marble + clearance)
|
| channel_radius = marble_radius + channel_clearance;
|
| // Radius of the solid tube needed before cutting the base flat and carving the channel
|
| // This needs to accommodate the channel radius plus the wall thickness.
|
| solid_path_radius = channel_radius + wall_thickness;
|
|
|
| // --- Curve Generation ---
|
|
|
| // Function to calculate a point (x, y, z) on the inverted cycloid
|
| // Input: theta_deg (degrees), r (cycloid generating radius)
|
| // Output: [x, y, z] coordinates
|
| function brachistochrone_point(theta_deg, r) =
|
| let(
|
| // Convert degrees to radians ONLY for the r*theta term in the formula
|
| theta_rad = theta_deg * PI / 180
|
| )
|
| [
|
| r * (theta_rad - sin(theta_deg)), // x = r*(theta_rad - sin(theta_deg))
|
| r * (cos(theta_deg) - 1), // y = r*(cos(theta_deg) - 1) [Inverted: y goes from 0 downwards]
|
| 0 // z = 0 (Path lies in XY plane initially)
|
| ];
|
|
|
| // Generate a list of points along the curve path
|
| points = [ for (i = [0:steps])
|
| let(current_theta_deg = i/steps * theta_max_deg)
|
| brachistochrone_point(current_theta_deg, cycloid_radius)
|
| ];
|
|
|
| // --- Helper Module for Creating Path Tube ---
|
|
|
| // Module to draw a tube-like path by creating hulls between consecutive spheres
|
| // This is used for both the solid part and the channel cutout.
|
| module draw_curve_cutout(pts, tube_r) {
|
| hull() {
|
| for (i = [0 : len(pts) - 1]) {
|
| translate(pts[i]) sphere(r=tube_r);
|
| }
|
| // Add a sphere at the very last point for cleaner ends, especially for the subtraction
|
| translate(pts[len(pts)-1]) sphere(r=tube_r);
|
| // Add a sphere at the very first point too for symmetry
|
| translate(pts[0]) sphere(r=tube_r);
|
| translate([pts[len(pts)-1][0], pts[0][1], pts[0][2]]) sphere(r=tube_r);
|
| }
|
| }
|
|
|
| // --- Track Construction ---
|
|
|
| // Calculate approximate bounding box of the path for efficient intersection cube size
|
| min_x = min([for (p=points) p.x]);
|
| max_x = max([for (p=points) p.x]);
|
| min_y = min([for (p=points) p.y]); // This will be negative
|
| max_y = max([for (p=points) p.y]); // This will be 0 if starting at theta=0
|
|
|
| // Add padding to bounds based on the solid path radius to ensure cube covers everything
|
| bound_padding = solid_path_radius * 1.1; // A little extra margin
|
| bound_min = [min_x - bound_padding, min_y - bound_padding];
|
| bound_max = [max_x + bound_padding, max_y + bound_padding]; // max_y will be ~0
|
| bound_size = [bound_max[0] - bound_min[0], bound_max[1] - bound_min[1]];
|
|
|
| // Echo dimensions for information (check Console output after preview/render)
|
| echo("Track X Extent:", max_x);
|
| echo("Track Y Extent (Depth):", min_y); // This is how far down it goes
|
| echo("Solid Path Radius:", solid_path_radius);
|
| echo("Channel Radius:", channel_radius);
|
|
|
| // Create the final track shape using difference and intersection
|
| //difference() {
|
|
|
| // Part 1: Create a solid object following the path, with a flat bottom surface.
|
| // intersection() {
|
|
|
| rotate([90, 0, 0])
|
| difference() {
|
| translate([-marble_diameter, points[len(points)-1][1] - marble_diameter, -1.2*marble_diameter/2])
|
| cube([points[len(points)-1][0] + marble_diameter, abs(points[len(points)-1][1]) + marble_diameter, 1.2*marble_diameter]);
|
|
|
| // 1a. Create a thick tube following the path centerline.
|
| // The radius includes wall thickness.
|
| draw_curve_cutout(points, solid_path_radius);
|
| }
|