$fa = $preview ? 10 : 10; // 1; $fs = $preview ? 0.2 : 0.2; // 0.01; colors = [ "#df0000", "#fb8721", "#f1e100", "#158f25", "#2c3ee5", "#7706af", "#333333", "#88502b", "#5dccfb", "#f0a67b", "#ffffff" ]; wideR = 65; braidScale = 5; braidR = 12; strands = len(colors); strandGap = 3; wideRes = $preview ? 180 : 360; strandRes = $preview ? 36 : 180; braidCircumference = 2 * PI * braidR; strandR = (braidCircumference / strands - strandGap) / 2; outermostCircumference = 2 * PI * (wideR + braidR); strandH = outermostCircumference * wideRes / 360 + 0.1; function rodriguesRotation(vector, axis, angle) = vector * cos(angle) + cross(axis, vector) * sin(angle) + axis * (axis * vector) * (1 - cos(angle)); /* these cross sections were always parallel to the Z axis, but that’s not really correct actual d/dwideT of center of braid is (-wideR sin wideT, wideR cos wideT, 0) magnitude of that is wideR absolute d/dwideT of center of strand is ( -wideR sin wideT - braidR (braidScale cos wideT sin braidT + sin wideT cos braidT), wideR cos wideT + braidR (-braidScale sin wideT sin braidT + cos wideT cos braidT), braidR braidScale cos braidT ) the Maxima CAS says that the magnitude of that is sqrt(wideR² + 2wideRbraidR cos braidT + braidR²cos²braidT + braidR²braidScale²) but actually OpenSCAD has a norm() function to get magnitudes anyway oops strand plane basis is strand plane normal x braid center normal, since we know those two vectors are never parallel with braidScale ≠ 0 */ function point(wideT, strandI, strandT) = let ( braidO = [wideR * cos(wideT), wideR * sin(wideT), 0], braidPlaneNormal = [-wideR * sin(wideT), wideR * cos(wideT), 0], braidPlaneNormalUnit = braidPlaneNormal / wideR, braidT = wideT * braidScale - strandI * 360 / strands, braidPlaneRel = [braidR * cos(braidT), braidR * sin(braidT)], strandO = [braidO.x + braidPlaneRel.x * cos(wideT), braidO.y + braidPlaneRel.x * sin(wideT), braidPlaneRel.y], strandPlaneNormal = [braidPlaneNormal.x - braidR * (braidScale * cos(wideT) * sin(braidT) + sin(wideT) * cos(braidT)), braidPlaneNormal.y + braidR * (-braidScale * sin(wideT) * sin(braidT) + cos(wideT) * cos(braidT)), braidR * braidScale * cos(braidT)], strandPlaneNormalUnit = strandPlaneNormal / norm(strandPlaneNormal), strandPlaneBasis = cross(strandPlaneNormalUnit, braidPlaneNormalUnit), strandRelDir = rodriguesRotation(strandPlaneBasis, strandPlaneNormalUnit, strandT), strandRel = strandRelDir * strandR / norm(strandRelDir) ) strandO + strandRel; pointCount = (wideRes + 1) * (strandRes + 1); module theStrand(strandI) { color(colors[strandI]) polyhedron( [ for (wideT = [0 : 360/wideRes : 360.01]) each [ for (strandT = [0 : 360/strandRes : 360.01]) point(wideT, strandI, strandT) ] ], [ for (wideTI = [0 : (strandRes + 1) : pointCount - 1]) each [ for (strandTI = [wideTI : wideTI + strandRes + 1]) each [ [(strandTI + 1) % pointCount, strandTI % pointCount, (strandTI + strandRes + 1) % pointCount], [(strandTI + strandRes + 1) % pointCount, strandTI % pointCount, (strandTI + strandRes) % pointCount], ] ] ] ); } for (strandI = [0 : strands - 1]) { theStrand(strandI); }