OpenFlexure Microscope OpenSCAD docs

libs/lighttrap.scad

function squircle_points_simple(r=1, flat=0, z=0, n_points=32)
[Source]
function squircle_points_simple(r=1, flat=0, z=0, n_points=32) = [
    for(j=[0:n_points-1]) let(
        angle = 360 * j / n_points,
        unit_vector = [cos(angle), sin(angle), 0],
        corner = [sign(cos(angle)), sign(sin(angle)), 0] * flat/2
    ) corner + unit_vector * r + [0, 0, z]
];
function squircle_points(r=1, flat=0, z=0, n_points=32, min_fragment=tiny())
[Source]
function squircle_points(r=1, flat=0, z=0, n_points=32, min_fragment=tiny()) = let(
    circumference = 2*PI*r + 4*flat,
    dc = min(min_fragment, circumference/n_points), // distance between points
    corner_points = min(PI*r/4/dc, n_points/8)      // number of points from corner to flat, i.e. 1/8 of a turn
    // if corner_points == n_points/8, this should give the same behaviour as squircle.
) assert(
    circumference > n_points * min_fragment, "Shape is too small!!"
) assert(
    r >= 0 && flat >= 0, "r and f must be non-negative!"
) [
    // We follow the same convention as the simple version: points start on the
    // x axis and work around anticlockwise.
    for(j=[0:n_points-1]) let(
        i=j+0.5, // This means that if n_points divides by four, we have points either side of the
        // flat sections, rather than having points on the x/y axes.
        // We keep the index of the corners the same, and expand points so they overflow onto the flats.
        nearest_corner = floor(4*i/n_points)/4 + 1/8, // which corner is closest? this will be (2j+1)/8 for j=0...3
        corner_i = nearest_corner * n_points, // what point would be on that corner?
        corner_angle = nearest_corner * 360,
        corner_centre = [sign(cos(corner_angle)), sign(sin(corner_angle))] * flat/2,
        // Having established the nearest corner, now we use our position relative to that corner.
        rel_i = i - corner_i, // index, relative to the nearest corner
        // If we weren't overflowing from the corners, rel_a would simply be rel_i * 360/n_points
        // We cap the magnitude of rel_a at +/- 45, so we don't go past the curved section.
        rel_a = min(abs(rel_i)/corner_points * 45, 45) * sign(rel_i), // The angle around the corner we are
        position_on_curve = corner_centre + [cos(rel_a + corner_angle), sin(rel_a + corner_angle)] * r,
        // If rel_i is large enough, we must also displace the point along the flat:
        flat_angle = (rel_a + corner_angle) + 90 * sign(rel_a), // The angle along the flat
        flat_distance = max(abs(rel_i) - corner_points, 0) * min_fragment,
        position_incl_flat = position_on_curve + [cos(flat_angle), sin(flat_angle)] * flat_distance
    ) [each position_incl_flat, z]
];
module lighttrap_cylinder(r1, r2, h, ridge=1.5)
[Source]
module lighttrap_cylinder(r1, r2, h, ridge=1.5){
    lighttrap_sqylinder(r1, 0, r2, 0, h, ridge=ridge);
}
module lighttrap_sqylinder(r1, f1, r2, f2, h, ridge=1.5)
[Source]
module lighttrap_sqylinder(r1, f1, r2, f2, h, ridge=1.5){
    // Set the number of ridges so that the height of each ridge is at least
    // `ridge`.  There is a minimum of 1, if h<ridge we effectively set ridge=h.
    n_cones = max(floor(h/ridge), 1);
    assert(n_cones > 1, "Error: light traps will not render correctly with <=1 ridge!");
    assert(r1>=ridge, "r1 is less than ridge, this will cause the light trap to fail");
    // The structure is actually h+2*tiny() tall, to match the old structure.
    // It starts at z=-tiny() and ends at z=h+tiny().
    cone_h = (h + 2*tiny())/n_cones;
    n_points = determine_number_of_fragments(max(r1, r2));  // honour $fn, $fa, $fs
    echo("n_points", n_points);
    function join_rings_with_quads(start1, start2, N) = [
        for(j=[0:N - 1]) [
                start1 + j,
                start2 + j,
                start2 + ((j + 1) % N),
                start1 + ((j + 1) % N)
            ],
    ];
    function circular_face(start, N, centre, direction=1) = let(
        d = (direction==1) ? 1 : 0
    ) [
        for(j=[0:N - 1]) [
            start + ((j + (1-d)) % N),
            start + ((j + d) % N),
            centre
        ]
    ];
    polyhedron(
        points=[
            for(i=[0:n_cones-1]) let(
                p=i/(n_cones-1),             // p=0 on bottom cone, 1 on top cone
                section_r1=r1*(1-p) + (r2+ridge)*p,  // radius of lower/outer ring
                section_r2=(r1-ridge)*(1-p) + r2*p,  // radius of upper/inner ring
                section_f=f1*(1-p) + f2*p,            // length of the flat section
                z=i*cone_h - tiny()          // NB these are the *bottoms* of the cones
            ) each concat(                   // Alternate between small and large rings
                squircle_points(section_r1, section_f, z=z, n_points=n_points),        // lower/outer ring
                squircle_points(section_r2, section_f, z=z+cone_h, n_points=n_points)  // upper/inner ring
            ),
            // Extra points at the top and bottom, so we can make nicely-triangulated circles
            [0,0,-tiny()],  //bottom
            [0,0,h+tiny()]  //top
        ],
        faces=[
            // Sloping faces
            for(i = [0:(2*n_cones - 2)])
                each join_rings_with_quads(i*n_points, (i+1)*n_points, n_points),
            // Bottom and top faces
            each circular_face(0, n_points, 2*n_points*n_cones, direction=1),
            each circular_face((2*n_cones - 1)*n_points, n_points, 2*n_points*n_cones + 1, direction=-1),
        ]
    );
}
module old_lighttrap_cylinder(r1, r2, h, ridge=1.5)
[Source]
module old_lighttrap_cylinder(r1,r2,h,ridge=1.5){
    assert(false, "This module exists for documentation only, do not use.");
    //there must be at least one cone or we divide by zero
    n_cones = max(floor(h/ridge),1);
    cone_h = h/n_cones;

    for(i = [0 : n_cones - 1]){
        p = i/(n_cones - 1);
        section_r1 = (1-p)*r1 + p*(r2+ridge);
        section_r2 = (1-p)*(r1-ridge) + p*r2;
        translate_z(i * cone_h - tiny()){
            cylinder(r1=section_r1, r2=section_r2, h=cone_h+2*tiny());
        }
    }
}
module old_lighttrap_sqylinder(r1, f1, r2, f2, h, ridge=1.5)
[Source]
module old_lighttrap_sqylinder(r1,f1,r2,f2,h,ridge=1.5){
    // This is an older version of lighttrap_sqylinder, kept in case it helps
    // make it clearer what is happening - it's more readable than the replacement,
    // though not as performant.
    //A shape made up of rounded truncated pyramids to form a
    //square christmas-tree-like shape.
    //Similar to lighttrap_cylinder each section has flat sides
    //It can be subtracted from and object to create a square shaft that is
    //good for trapping stray light in an optical path. The shaft rounded
    //corners
    //r1 is radius of cuvature of the bottom of the bottom pyramid
    //f1 is the flat section of the bottom of the bottom pyramid
    //r2 is radius of cuvature of the top of the top pyramid
    //f2 is the flat section of the to of the top pyramid
    //NOTE: to make a uniform width shaft set r2==r1-ridge and f1=f2
    //ALSO NOTE: Each truncated pyramid is made by varying r, not f. As such
    //    r1 must be greater than or equal to ridge
    assert(false, "This module exists for documentation only, do not use.");

    assert(r1>=ridge, "r1 is less than ridge this will cause the light trap to fail");
    //there must be at least one cone or we divide by zero
    n_cones = max(floor(h/ridge),1);
    cone_h = h/n_cones;

    for(i = [0 : n_cones - 1]){
        p = i/(n_cones - 1);
        section_r1 = (1-p)*r1 + p*(r2+ridge);
        section_r2 = (1-p)*(r1-ridge) + p*r2;
        section_flat_l = ((1-p)*f1 + p*f2);
        translate_z(i * cone_h - tiny()){
            minkowski(){
                cylinder(r1=section_r1, r2=section_r2, h=cone_h);
                cube([section_flat_l, section_flat_l, 2*tiny()], center=true);
            }
        }
    }
}