// 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); }