screws/screw_nut: fix nut thread hole cutout

include <system.scad>
include <thread-data.scad>
include <nut-data.scad>
include <materials.scad>

use <shapes.scad>
use <transforms.scad>
use <misc.scad>

// naive, assume head height is same as thread size (generally true for cap heads)
function get_screw_head_h(head, thread) = get(ThreadSize, thread);
function get_screw_head_d(head, thread) = 2 * get(ThreadSize, thread);

module screw(nut, thread, head="socket", h=10, tolerance=1.05, head_embed=false, with_nut=true, with_head=true, nut_offset=0, orient=Z, align=N)
{
    nut_thread = get(NutThread, nut);
    thread_ = fallback(thread, nut_thread);
    assert(thread_ != U, "screw: No nut or thread given");
    assert(head != U, "screw: head == U");
    /*assert(nut!=U && thread!=U && nut_thread != thread_, "screw: Mismatched nut and thread");*/

    head_h = get_screw_head_h(head=head, thread=thread_);
    nut_h = get(NutThickness,nut)*tolerance;
    threadsize = get(ThreadSize, thread_);

    s = threadsize*tolerance;
    total_h = h;
    size_align(size=[s, s, total_h], orient=-orient, orient_ref=Z, align=align)
    {
        translate([0,0,head_embed?-head_h:0])
        {
            if(with_head)
            {
                translate([0,0,h/2+.01])
                screw_head(head=head, thread=thread_, orient=Z, align=Z);
            }

            screw_thread(thread=thread_, h=h+.1, tolerance=tolerance, orient=Z, align=N);

            if(with_nut && nut != U)
            {
                translate([0,0,-h/2+nut_h+nut_offset+(head_embed?head_h:0)])
                screw_nut(nut=nut, tolerance=tolerance, orient=Z, align=-Z);
            }
        }

        if($show_vit)
        {
            if(with_nut && nut != U)
            {
                translate([0,0,-h/2+nut_h+nut_offset+(head_embed?head_h:0)])
                %screw_nut(nut=nut, align=-Z);
            }
        }
    }
}

module screw_cut(nut, thread, head="socket", h=10, tolerance=1.05, head_embed=false, with_nut=true, nut_cut_h=1000, with_head=true, head_cut_h=1000, nut_offset=0, cut_access=true, orient=Z, align=N)
{
    nut_thread = get(NutThread, nut);
    thread_ = fallback(thread, nut_thread);
    assert(thread_ != U, "screw_cut: No nut or thread given");
    /*assert(nut!=U && thread!=U && nut_thread != thread_, "screw_cut: Mismatched nut and thread");*/

    nut_h = get(NutThickness,nut)*tolerance;
    assert(head != U, "screw_cut: head == U");

    head_h = get_screw_head_h(head=head, thread=thread_);
    assert(head_h != U, "screw_cut: head_h is U!");

    threadsize = get(ThreadSize, thread_);
    assert(threadsize != U, "screw_cut: threadsize is U!");

    s = threadsize*tolerance;
    total_h = h;
    assert(s != U, "screw_cut: s is U!");
    size_align(size=[s, s, total_h], orient=-orient, orient_ref=Z, align=align)
    {
        translate([0,0,head_embed?-head_h:0])
        {
            if(with_head)
            {
                translate([0,0,h/2+.01])
                screw_head_cut(head=head, thread=thread_, tolerance=tolerance, override_h=head_cut_h, align=Z);
            }

            screw_thread_cut(thread=thread_, h=h+.1, tolerance=tolerance, orient=Z, align=N);

            if(with_nut && nut != U)
            {
                translate([0,0,-h/2+nut_h+nut_offset+(head_embed?head_h:0)])
                screw_nut_cut(nut=nut, tolerance=tolerance, h=nut_cut_h, align=-Z);
            }

            if($show_vit)
            {
                %screw(nut=nut, thread=thread, head=head, h=h, tolerance=tolerance, with_nut=with_nut, with_head=with_head, nut_offset=nut_offset, orient=-Z, align=N);
            }
        }
    }
}

module screw_thread(thread, h=10, tolerance=1.00, orient=Z, align=N)
{
    assert(thread!=U, "screw_thread: thread is undef");
    // TODO render thread
    threadsize = get(ThreadSize, thread);

    material(Mat_Steel)
    cylindera(d=threadsize, h=h, orient=orient, align=align);
}

module screw_thread_cut(thread, h=10, tolerance=1.05, orient=Z, align=N)
{
    assert(thread!=U, "screw_thread_cut: thread is undef");
    threadsize = get(ThreadSize, thread);
    cylindera(d=threadsize*tolerance, h=h, orient=orient, align=align);
}

module screw_head(head="socket", drive="hex", thread, tolerance=1.00, override_h=U, orient=Z, align=N)
{
    assert(head != U, "screw_head: head == U");
    assert(thread!=U, "screw_head: thread == undef");

    threadsize = get(ThreadSize, thread);
    head_h = get_screw_head_h(head=head, thread=thread);
    head_d = get_screw_head_d(head=head, thread=thread);
    material(Mat_Steel)
    size_align(size=[head_d, head_d, head_h], orient=orient, align=align)
    {
        if(head=="socket")
        {
            difference()
            {
                tz(-head_h/2)
                cylindera(d=head_d, h = fallback(override_h,head_h), align=Z);

                if(drive == "hex")
                {
                    cylindera(d = fn_radius(threadsize, 6), h = 1000, $fn = 6, align=Z);
                }
            }
        }
        else if(head=="button")
        {
            c = head_d;
            f = head_h*1.25;
            r = ( pow(c,2)/4 + pow(f,2) )/(2*f);

            d = r - f; // displacement to move sphere

            difference()
            {
                intersection()
                {
                    tz(-head_h/2)
                    scale([1,1,.5])
                    spherea(r = head_h, align=N);

                    tz(-head_h/2)
                    cylindera(d=head_d, h = fallback(override_h,head_h)/2, align=Z);
                }

                if(drive == "hex")
                {
                    // button head uses -0.5*mm smaller hex
                    tz(-head_h/2)
                    cylindera(d = fn_radius(threadsize-.5, 6), h = 1000, $fn = 6, align=Z);
                }
            }

        }
    }
}

module screw_head_cut(head="socket", thread, tolerance=1.05, override_h=U, orient=Z, align=N)
{
    assert(head != U, "screw_head: head == U");
    assert(thread!=U, "screw_head_cut: thread is undef");

    threadsize = get(ThreadSize, thread);
    head_h = get_screw_head_h(head=head, thread=thread);
    head_d = get_screw_head_d(head=head, thread=thread)*tolerance;
    size_align(size=[head_d, head_d, head_h], orient=orient, align=align)
    translate([0,0,-head_h/2])
    cylindera(d=head_d, h=fallback(override_h, head_h), align=Z);
}

module screw_nut(nut, thread, tolerance=1.00, override_h=U, orient=Z, align=N)
{
    assert(nut!=U, "screw_nut: nut is undef");

    nut_thick = get(NutThickness,nut)*tolerance;
    nut_facets = get(NutFacets, nut);
    nut_dia = nut_dia(nut);
    nut_thread = fallback(thread, get(NutThread, nut));
    h_ = fallback(override_h, nut_thick);
    material(Mat_Steel)
    size_align([nut_dia, nut_dia, h_], orient=orient, align=align)
    {
        difference()
        {
            cylindera($fn=nut_facets, d=nut_dia, h=h_);
            screw_thread(thread=nut_thread, h=h_+.2);
        }
    }
}

module screw_nut_cut(nut, tolerance=1.05, h=1000, orient=Z, align=N)
{
    assert(nut!=U, "screw_nut_cut: nut is undef");

    nut_thick = get(NutThickness,nut)*tolerance;
    nut_dia = nut_dia(nut);
    size_align(size=[nut_dia, nut_dia, nut_thick], orient=orient, align=align)
    {
        translate([0,0,nut_thick/2])
        {
            screw_nut(nut, tolerance, orient=Z, align=-Z);
            translate([0,0,-nut_thick+.01])
            {
                cylindera(d=nut_dia+2*mm, h=h, orient=Z, align=-Z);
            }
        }
    }
}

function nut_radius(nut) = outradius(get(NutWidthMin, nut)/2, get(NutFacets, nut));
function nut_dia(nut) = 2*nut_radius(nut);

module nut_trap_cut(nut, thread, head, trap_offset=10, screw_l=10*mm, screw_offset=0*mm, cut_screw=false, trap_h=10, trap_axis=-Y, orient=Z, align=N)
{
    nut_thread = get(NutThread, nut);
    thread_ = fallback(thread, nut_thread);
    assert(thread_ != U, "nut_trap_cut: No nut or thread given");
    /*assert(nut!=U && thread!=U && nut_thread != thread_, "nut_trap_cut: Mismatched nut and thread");*/

    threadsize = get(ThreadSize, thread_);
    head_h = get_screw_head_h(head=head, thread=thread);
    nut_h = get(NutThickness, nut) +.5*mm;

    nut_width_min = get(NutWidthMin, nut);
    nut_facets = get(NutFacets, nut);
    nut_dia = nut_dia(nut);
    total_h = nut_h;
    size_align(size=[nut_dia, nut_dia, total_h], orient=orient, orient_ref=Z, align=align)
    {
        if(cut_screw)
        {
            translate(-nut_h/2*Z)
            translate(screw_offset*Z)
            screw_cut($show_vit=false, nut=nut, thread=thread_, with_nut=false, tolerance=1.1, h=screw_l, orient=-Z, align=Z);
        }
        else
        {
            translate([0,0,nut_h/2+screw_offset])
            screw_thread_cut($show_vit=false, thread=thread_, tolerance=1.1, h=screw_l, orient=-Z, align=Z);
        }

        hull()
        {
            orient(axis=Z, axis_ref=orient)
            translate(-.15*mm*trap_axis)
            stack(dist=trap_h, axis=trap_axis)
            {
                orient(axis=orient, axis_ref=Z)
                {
                    rotate([0,0,30])
                    cylindera($fn=nut_facets, d=nut_dia, h=nut_h, align=N);
                }

                orient(axis=orient, axis_ref=Z)
                cubea(size=[nut_width_min,nut_width_min,nut_h], align=N);
            }
        }

        if($show_vit)
        {
            if(cut_screw)
            {
                translate(-nut_h/2*Z)
                translate(screw_offset*Z)
                %screw(nut=nut, thread=thread_, with_nut=false, tolerance=1, h=screw_l, orient=-Z, align=Z);
            }

            rotate([0,0,30])
            %screw_nut(nut=nut, thread=thread, tolerance=1, orient=-Z, align=N);
        }

    }
}

if(false)
{
    all_axes()
    translate($axis*5*mm)
    color($color)
    screw(nut=NutHexM3, h=10*mm, orient=-$axis, align=$axis);
}

// all nuts
if(false)
{
    for(nuti=[0:1:len(AllNut)-1])
    {
        nut = AllNut[nuti];
        v_threadsize = v_get(AllNut,ThreadSize);
        v_nutwidthmin = v_get(AllNut,NutWidthMin);
        dist = v_cumsum(v_nutwidthmin, 0, nuti)[nuti]*1.2;
        translate(X*dist)
        rotate([0,0,30])
        screw(nut=nut, h=get(NutWidthMax, nut)*5, head_embed=false, orient=-Z, align=Z);
    }
}

if(false)
{
    nut1 = NutHexM3;
    stack(axis=X, dist = 10)
    {
        screw(nut=nut1, head="button", h=10, orient=-Z);
        screw(nut=nut1, head="socket", h=10, orient=-Z);
    }
}

if(false)
{
    box_w = 150*mm;
    box_d = 10*mm;
    box_h = 10*mm;
    o = 10*mm;

    $show_vit = true;
    /*$fs= 0.2;*/
    /*$fa = 4;*/

    nut1 = NutHexM3;
    nut2 = NutHexM5;
    nut3 = NutKnurlM3_3_42;
    nut4 = NutKnurlM3_5_42;

    /*all_axes()*/
    /*for($axis=[X])*/
    /*color($color)*/
    /*translate($axis*15)*/
    /*echo($axis)*/

    /*orient(axis=$axis, axis_ref=-X)*/
    for(y=[-1,1])
    translate(-y*Y*20*mm)
    difference()
    {
        cubea([box_w,box_d,box_h], align=X+Z);
        test_cuts(orient=y*Z);
        /*%test_cuts(orient=y, axis_ref=-Z);*/
    }

}

module test_cuts(orient)
{
    union()
    {
        /*offset = 0;*/
        offset = -5;
        /*translate([0,offset,0])*/
        /*translate([5,0,-5])*/
        translate([5,0,0])
        stack(dist=10,axis=X)
        {
            screw_cut(nut=nut1, head="button", h=box_h, head_embed=false, orient=orient, align=Z);
            screw_cut(nut=nut1, head="socket", h=box_h, head_embed=false, orient=orient, align=Z);
            screw_cut(thread=ThreadM5, h=box_h, head_embed=false, orient=orient, align=Z);
            screw_cut(nut=nut1, h=box_h, head_embed=false, orient=orient, align=Z);

            screw_cut(nut=nut1, h=box_h, nut_offset=0*mm, head_embed=true, orient=orient, align=Z);
            screw_cut(nut=nut2, h=box_h, nut_offset=3*mm, head_embed=false, orient=orient, align=Z);
            screw_cut(nut=nut3, h=box_h, head_embed=false, orient=orient, align=Z);
            screw_cut(nut=nut4, h=box_h, head_embed=false, orient=orient, align=Z);

            translate([0,0,-5*mm])
            screw_cut(nut=nut3, h=box_h, head_embed=false, orient=orient, align=Z);

            translate([0,0,box_h/2])
            nut_trap_cut(nut=nut1, h=5, head_embed=false, trap_h=10, trap_axis=-Y, orient=orient, align=N);

            translate([0,0,box_h/2])
            nut_trap_cut(nut=nut1, h=5, head_embed=false, trap_h=10, trap_axis=Y, orient=orient, align=N);

            translate([0,0,box_h/2])
            nut_trap_cut(nut=nut1, h=box_d, head_embed=false, trap_h=10, screw_offset=2*mm, trap_axis=Z, orient=orient, align=N);
            translate([0,0,box_h/2])
            nut_trap_cut(nut=nut1, h=box_d, head_embed=false, trap_h=10, screw_offset=2*mm, trap_axis=-Z, orient=orient, align=N);
        }
    }
}

include <system.scad>
include <thread-data.scad>
include <nut-data.scad>
include <materials.scad>

use <shapes.scad>
use <transforms.scad>
use <misc.scad>

// naive, assume head height is same as thread size (generally true for cap heads)
function get_screw_head_h(head, thread) = get(ThreadSize, thread);
function get_screw_head_d(head, thread) = 2 * get(ThreadSize, thread);

module screw(nut, thread, head="socket", h=10, tolerance=1.05, head_embed=false, with_nut=true, with_head=true, nut_offset=0, orient=Z, align=N)
{
    nut_thread = get(NutThread, nut);
    thread_ = fallback(thread, nut_thread);
    assert(thread_ != U, "screw: No nut or thread given");
    assert(head != U, "screw: head == U");
    /*assert(nut!=U && thread!=U && nut_thread != thread_, "screw: Mismatched nut and thread");*/

    head_h = get_screw_head_h(head=head, thread=thread_);
    nut_h = get(NutThickness,nut)*tolerance;
    threadsize = get(ThreadSize, thread_);

    s = threadsize*tolerance;
    total_h = h;
    size_align(size=[s, s, total_h], orient=-orient, orient_ref=Z, align=align)
    {
        translate([0,0,head_embed?-head_h:0])
        {
            if(with_head)
            {
                translate([0,0,h/2+.01])
                screw_head(head=head, thread=thread_, orient=Z, align=Z);
            }

            screw_thread(thread=thread_, h=h+.1, tolerance=tolerance, orient=Z, align=N);

            if(with_nut && nut != U)
            {
                translate([0,0,-h/2+nut_h+nut_offset+(head_embed?head_h:0)])
                screw_nut(nut=nut, tolerance=tolerance, orient=Z, align=-Z);
            }
        }

        if($show_vit)
        {
            if(with_nut && nut != U)
            {
                translate([0,0,-h/2+nut_h+nut_offset+(head_embed?head_h:0)])
                %screw_nut(nut=nut, align=-Z);
            }
        }
    }
}

module screw_cut(nut, thread, head="socket", h=10, tolerance=1.05, head_embed=false, with_nut=true, nut_cut_h=1000, with_head=true, head_cut_h=1000, nut_offset=0, cut_access=true, orient=Z, align=N)
{
    nut_thread = get(NutThread, nut);
    thread_ = fallback(thread, nut_thread);
    assert(thread_ != U, "screw_cut: No nut or thread given");
    /*assert(nut!=U && thread!=U && nut_thread != thread_, "screw_cut: Mismatched nut and thread");*/

    nut_h = get(NutThickness,nut)*tolerance;
    assert(head != U, "screw_cut: head == U");

    head_h = get_screw_head_h(head=head, thread=thread_);
    assert(head_h != U, "screw_cut: head_h is U!");

    threadsize = get(ThreadSize, thread_);
    assert(threadsize != U, "screw_cut: threadsize is U!");

    s = threadsize*tolerance;
    total_h = h;
    assert(s != U, "screw_cut: s is U!");
    size_align(size=[s, s, total_h], orient=-orient, orient_ref=Z, align=align)
    {
        translate([0,0,head_embed?-head_h:0])
        {
            if(with_head)
            {
                translate([0,0,h/2+.01])
                screw_head_cut(head=head, thread=thread_, tolerance=tolerance, override_h=head_cut_h, align=Z);
            }

            screw_thread_cut(thread=thread_, h=h+.1, tolerance=tolerance, orient=Z, align=N);

            if(with_nut && nut != U)
            {
                translate([0,0,-h/2+nut_h+nut_offset+(head_embed?head_h:0)])
                screw_nut_cut(nut=nut, tolerance=tolerance, h=nut_cut_h, align=-Z);
            }

            if($show_vit)
            {
                %screw(nut=nut, thread=thread, head=head, h=h, tolerance=tolerance, with_nut=with_nut, with_head=with_head, nut_offset=nut_offset, orient=-Z, align=N);
            }
        }
    }
}

module screw_thread(thread, h=10, tolerance=1.00, orient=Z, align=N)
{
    assert(thread!=U, "screw_thread: thread is undef");
    // TODO render thread
    threadsize = get(ThreadSize, thread);

    material(Mat_Steel)
    cylindera(d=threadsize, h=h, orient=orient, align=align);
}

module screw_thread_cut(thread, h=10, tolerance=1.05, orient=Z, align=N)
{
    assert(thread!=U, "screw_thread_cut: thread is undef");
    threadsize = get(ThreadSize, thread);
    cylindera(d=threadsize*tolerance, h=h, orient=orient, align=align);
}

module screw_head(head="socket", drive="hex", thread, tolerance=1.00, override_h=U, orient=Z, align=N)
{
    assert(head != U, "screw_head: head == U");
    assert(thread!=U, "screw_head: thread == undef");

    threadsize = get(ThreadSize, thread);
    head_h = get_screw_head_h(head=head, thread=thread);
    head_d = get_screw_head_d(head=head, thread=thread);
    material(Mat_Steel)
    size_align(size=[head_d, head_d, head_h], orient=orient, align=align)
    {
        if(head=="socket")
        {
            difference()
            {
                tz(-head_h/2)
                cylindera(d=head_d, h = fallback(override_h,head_h), align=Z);

                if(drive == "hex")
                {
                    cylindera(d = fn_radius(threadsize, 6), h = 1000, $fn = 6, align=Z);
                }
            }
        }
        else if(head=="button")
        {
            c = head_d;
            f = head_h*1.25;
            r = ( pow(c,2)/4 + pow(f,2) )/(2*f);

            d = r - f; // displacement to move sphere

            difference()
            {
                intersection()
                {
                    tz(-head_h/2)
                    scale([1,1,.5])
                    spherea(r = head_h, align=N);

                    tz(-head_h/2)
                    cylindera(d=head_d, h = fallback(override_h,head_h)/2, align=Z);
                }

                if(drive == "hex")
                {
                    // button head uses -0.5*mm smaller hex
                    tz(-head_h/2)
                    cylindera(d = fn_radius(threadsize-.5, 6), h = 1000, $fn = 6, align=Z);
                }
            }

        }
    }
}

module screw_head_cut(head="socket", thread, tolerance=1.05, override_h=U, orient=Z, align=N)
{
    assert(head != U, "screw_head: head == U");
    assert(thread!=U, "screw_head_cut: thread is undef");

    threadsize = get(ThreadSize, thread);
    head_h = get_screw_head_h(head=head, thread=thread);
    head_d = get_screw_head_d(head=head, thread=thread)*tolerance;
    size_align(size=[head_d, head_d, head_h], orient=orient, align=align)
    translate([0,0,-head_h/2])
    cylindera(d=head_d, h=fallback(override_h, head_h), align=Z);
}

module screw_nut(nut, tolerance=1.00, override_h=U, orient=Z, align=N)
{
    assert(nut!=U, "screw_nut: nut is undef");

    nut_thick = get(NutThickness,nut)*tolerance;
    nut_facets = get(NutFacets, nut);
    nut_dia = nut_dia(nut);
    material(Mat_Steel)
    cylindera($fn=nut_facets, d=nut_dia, h=fallback(override_h, nut_thick), orient=orient, align=align);
}

module screw_nut_cut(nut, tolerance=1.05, h=1000, orient=Z, align=N)
{
    assert(nut!=U, "screw_nut_cut: nut is undef");

    nut_thick = get(NutThickness,nut)*tolerance;
    nut_dia = nut_dia(nut);
    size_align(size=[nut_dia, nut_dia, nut_thick], orient=orient, align=align)
    {
        translate([0,0,nut_thick/2])
        {
            screw_nut(nut, tolerance, orient=Z, align=-Z);
            translate([0,0,-nut_thick+.01])
            {
                cylindera(d=nut_dia+2*mm, h=h, orient=Z, align=-Z);
            }
        }
    }
}

function nut_radius(nut) = outradius(get(NutWidthMin, nut)/2, get(NutFacets, nut));
function nut_dia(nut) = 2*nut_radius(nut);

module nut_trap_cut(nut, thread, head, trap_offset=10, screw_l=10*mm, screw_offset=0*mm, cut_screw=false, trap_h=10, trap_axis=-Y, orient=Z, align=N)
{
    nut_thread = get(NutThread, nut);
    thread_ = fallback(thread, nut_thread);
    assert(thread_ != U, "nut_trap_cut: No nut or thread given");
    /*assert(nut!=U && thread!=U && nut_thread != thread_, "nut_trap_cut: Mismatched nut and thread");*/

    threadsize = get(ThreadSize, thread_);
    head_h = get_screw_head_h(head=head, thread=thread);
    nut_h = get(NutThickness, nut) +.5*mm;

    nut_width_min = get(NutWidthMin, nut);
    nut_facets = get(NutFacets, nut);
    nut_dia = nut_dia(nut);
    total_h = nut_h;
    size_align(size=[nut_dia, nut_dia, total_h], orient=orient, orient_ref=Z, align=align)
    {
        if(cut_screw)
        {
            translate(-nut_h/2*Z)
            translate(screw_offset*Z)
            screw_cut($show_vit=false, nut=nut, thread=thread_, with_nut=false, tolerance=1.1, h=screw_l, orient=-Z, align=Z);
        }
        else
        {
            translate([0,0,nut_h/2+screw_offset])
            screw_thread_cut($show_vit=false, thread=thread_, tolerance=1.1, h=screw_l, orient=-Z, align=Z);
        }

        hull()
        {
            orient(axis=Z, axis_ref=orient)
            translate(-.15*mm*trap_axis)
            stack(dist=trap_h, axis=trap_axis)
            {
                orient(axis=orient, axis_ref=Z)
                {
                    rotate([0,0,30])
                    cylindera($fn=nut_facets, d=nut_dia, h=nut_h, align=N);
                }

                orient(axis=orient, axis_ref=Z)
                cubea(size=[nut_width_min,nut_width_min,nut_h], align=N);
            }
        }

        if($show_vit)
        {
            if(cut_screw)
            {
                translate(-nut_h/2*Z)
                translate(screw_offset*Z)
                %screw(nut=nut, thread=thread_, with_nut=false, tolerance=1, h=screw_l, orient=-Z, align=Z);
            }

            rotate([0,0,30])
            %screw_nut(nut=nut, thread=thread, tolerance=1, orient=-Z, align=N);
        }

    }
}

if(false)
{
    all_axes()
    translate($axis*5*mm)
    color($color)
    screw(nut=NutHexM3, h=10*mm, orient=-$axis, align=$axis);
}

// all nuts
if(false)
{
    for(nuti=[0:1:len(AllNut)-1])
    {
        nut = AllNut[nuti];
        v_threadsize = v_get(AllNut,ThreadSize);
        v_nutwidthmin = v_get(AllNut,NutWidthMin);
        dist = v_cumsum(v_nutwidthmin, 0, nuti)[nuti]*1.2;
        translate(X*dist)
        rotate([0,0,30])
        screw(nut=nut, h=get(NutWidthMax, nut)*5, head_embed=false, orient=-Z, align=Z);
    }
}

if(false)
{
    nut1 = NutHexM3;
    stack(axis=X, dist = 10)
    {
        screw(nut=nut1, head="button", h=10, orient=-Z);
        screw(nut=nut1, head="socket", h=10, orient=-Z);
    }
}

if(false)
{
    box_w = 150*mm;
    box_d = 10*mm;
    box_h = 10*mm;
    o = 10*mm;

    $show_vit = true;
    /*$fs= 0.2;*/
    /*$fa = 4;*/

    nut1 = NutHexM3;
    nut2 = NutHexM5;
    nut3 = NutKnurlM3_3_42;
    nut4 = NutKnurlM3_5_42;

    /*all_axes()*/
    /*for($axis=[X])*/
    /*color($color)*/
    /*translate($axis*15)*/
    /*echo($axis)*/

    /*orient(axis=$axis, axis_ref=-X)*/
    for(y=[-1,1])
    translate(-y*Y*20*mm)
    difference()
    {
        cubea([box_w,box_d,box_h], align=X+Z);
        test_cuts(orient=y*Z);
        /*%test_cuts(orient=y, axis_ref=-Z);*/
    }

}

module test_cuts(orient)
{
    union()
    {
        /*offset = 0;*/
        offset = -5;
        /*translate([0,offset,0])*/
        /*translate([5,0,-5])*/
        translate([5,0,0])
        stack(dist=10,axis=X)
        {
            screw_cut(nut=nut1, head="button", h=box_h, head_embed=false, orient=orient, align=Z);
            screw_cut(nut=nut1, head="socket", h=box_h, head_embed=false, orient=orient, align=Z);
            screw_cut(thread=ThreadM5, h=box_h, head_embed=false, orient=orient, align=Z);
            screw_cut(nut=nut1, h=box_h, head_embed=false, orient=orient, align=Z);

            screw_cut(nut=nut1, h=box_h, nut_offset=0*mm, head_embed=true, orient=orient, align=Z);
            screw_cut(nut=nut2, h=box_h, nut_offset=3*mm, head_embed=false, orient=orient, align=Z);
            screw_cut(nut=nut3, h=box_h, head_embed=false, orient=orient, align=Z);
            screw_cut(nut=nut4, h=box_h, head_embed=false, orient=orient, align=Z);

            translate([0,0,-5*mm])
            screw_cut(nut=nut3, h=box_h, head_embed=false, orient=orient, align=Z);

            translate([0,0,box_h/2])
            nut_trap_cut(nut=nut1, h=5, head_embed=false, trap_h=10, trap_axis=-Y, orient=orient, align=N);

            translate([0,0,box_h/2])
            nut_trap_cut(nut=nut1, h=5, head_embed=false, trap_h=10, trap_axis=Y, orient=orient, align=N);

            translate([0,0,box_h/2])
            nut_trap_cut(nut=nut1, h=box_d, head_embed=false, trap_h=10, screw_offset=2*mm, trap_axis=Z, orient=orient, align=N);
            translate([0,0,box_h/2])
            nut_trap_cut(nut=nut1, h=box_d, head_embed=false, trap_h=10, screw_offset=2*mm, trap_axis=-Z, orient=orient, align=N);
        }
    }
}


screws: fix screw trap offset

screws: fix screw trap offset

include <system.scad>
include <thread-data.scad>
include <nut-data.scad>
include <materials.scad>

use <shapes.scad>
use <transforms.scad>
use <misc.scad>

// for proper threads
use <naca_sweep.scad>

// naive, assume head height is same as thread size (generally true for cap heads)
function get_screw_head_h(head, thread) =
(head=="socket" || head == "button") ?
    get(ThreadSize, thread)
:
head == "set" ? 0
:
0;

function get_screw_head_d(head, thread) =
(head=="socket" || head == "button") ?
    2 * get(ThreadSize, thread)
:
head == "set" ? 0
:
0;

module screw(part, nut, thread, head="socket", h=10, tolerance=1.05, head_embed=false, with_nut=true, with_head=true, nut_offset=0, orient=Z, align=N)
{
    nut_thread = get(NutThread, nut);
    thread_ = fallback(thread, nut_thread);
    assert(thread_ != U, "screw: No nut or thread given");
    assert(head != U, "screw: head == U");
    /*assert(nut!=U && thread!=U && nut_thread != thread_, "screw: Mismatched nut and thread");*/

    head_h = get_screw_head_h(head=head, thread=thread_);
    nut_h = get(NutThickness,nut)*tolerance;
    threadsize = get(ThreadSize, thread_);

    s = threadsize*tolerance;
    total_h = h;

    if(part==U)
    {
        difference()
        {
            screw(part="pos", nut=nut, thread=thread, head=head, h=h, tolerance=tolerance, head_embed=head_embed, with_nut=with_nut, with_head=with_head, nut_offset=nut_offset, orient=orient, align=align);
            screw(part="neg", nut=nut, thread=thread, head=head, h=h, tolerance=tolerance, head_embed=head_embed, with_nut=with_nut, with_head=with_head, nut_offset=nut_offset, orient=orient, align=align);
        }
        if($show_vit)
        %screw(part="vit", nut=nut, thread=thread, head=head, h=h, tolerance=tolerance, head_embed=head_embed, with_nut=with_nut, with_head=with_head, nut_offset=nut_offset, orient=orient, align=align);
    }
    else if(part=="pos")
    size_align(size=[s, s, total_h], orient=-orient, orient_ref=Z, align=align)
    tz(head_embed?-head_h:0)
    {
        if(with_head)
        {
            tz(h/2+.01)
            screw_head(part=part, head=head, thread=thread_, orient=Z, align=Z);
        }

        tz(-h/2+.01)
        screw_thread(thread=thread_, head=head, h=h+.1, tolerance=tolerance, orient=Z, align=N);

        if(with_nut && nut != U)
        {
            tz(-h/2+nut_h+nut_offset+(head_embed?head_h:0))
            screw_nut(nut=nut, tolerance=tolerance, orient=Z, align=-Z);
        }
    }
    else if(part=="neg")
    size_align(size=[s, s, total_h], orient=-orient, orient_ref=Z, align=align)
    tz(head_embed?-head_h:0)
    {
        if(with_head)
        tz(h/2+.01)
        screw_head(part=part, head=head, thread=thread_, orient=Z, align=Z);
    }
    else if(part=="vit")
    size_align(size=[s, s, total_h], orient=-orient, orient_ref=Z, align=align)
    {
        if(with_nut && nut != U)
        {
            tz(-h/2+nut_h+nut_offset+(head_embed?head_h:0))
            %screw_nut(nut=nut, align=-Z);
        }
    }
}

module screw_cut(nut, thread, head="socket", h=10, tolerance=1.05, head_embed=false, with_nut=true, with_nut_cut=true, with_nut_access=true, nut_cut_h=1000, with_head=true, head_cut_h=1000, nut_offset=0, cut_access=true, orient=Z, align=N)
{
    nut_thread = get(NutThread, nut);
    thread_ = fallback(thread, nut_thread);
    assert(thread_ != U, "screw_cut: No nut or thread given");
    /*assert(nut!=U && thread!=U && nut_thread != thread_, "screw_cut: Mismatched nut and thread");*/

    nut_h = get(NutThickness,nut)*tolerance;
    assert(head != U, "screw_cut: head == U");

    head_h = get_screw_head_h(head=head, thread=thread_);
    assert(head_h != U, "screw_cut: head_h is U!");

    threadsize = get(ThreadSize, thread_);
    assert(threadsize != U, "screw_cut: threadsize is U!");

    s = threadsize*tolerance;
    total_h = h;
    assert(s != U, "screw_cut: s is U!");
    size_align(size=[s, s, total_h], orient=-orient, orient_ref=Z, align=align)
    {
        tz(head_embed?-head_h:0)
        {
            if(with_head)
            {
                tz(h/2+.01)
                screw_head_cut(head=head, thread=thread_, tolerance=tolerance, override_h=head_cut_h, align=Z);
            }

            screw_thread_cut(thread=thread_, h=h+.1, tolerance=tolerance, orient=Z, align=N);

            if(with_nut && nut != U && with_nut_cut)
            {
                tz(-h/2+nut_h+nut_offset+(head_embed?head_h:0))
                screw_nut_cut(nut=nut, tolerance=tolerance, h=nut_cut_h, with_access=with_nut_access, align=-Z);
            }

            if($show_vit)
            {
                %screw(nut=nut, thread=thread, head=head, h=h, tolerance=tolerance, with_nut=with_nut, with_head=with_head, nut_offset=nut_offset, orient=-Z, align=N);
            }
        }
    }
}

module screw_thread(thread, h=10, tolerance=1.00, orient=Z, align=N)
{
    assert(thread!=U, "screw_thread: thread is undef");
    threadsize = get(ThreadSize, thread);
    d = get(ThreadSize, thread);
    pitch = get(ThreadPitchMm, thread);
    windings = ceil(h/pitch);

    size_align(size=[d,d,h], orient=orient, align=align)
    material(Mat_Steel)
    {
        if($preview_mode)
        {
            cylindera(d=d, h=h, align=Z);
        }
        else
        {
            intersection()
            {
                cylindera(d=d, h=h, align=Z);
                tz(-pitch)
                threading(angle=60, pitch=pitch, d=d, windings=windings, full=true);
            }
            /*inner_d = (get(ThreadInternalMinorDiaMin, thread) + get(ThreadInternalMinorDiaMin, thread))/2;*/
            /*cylindera(d=inner_d, h=h, align=Z);*/
        }
    }
}

module screw_thread_cut(thread, h=10, tolerance=1.05, orient=Z, align=N)
{
    assert(thread!=U, "screw_thread_cut: thread is undef");
    threadsize = get(ThreadSize, thread);
    cylindera(d=threadsize*tolerance, h=h, orient=orient, align=align);
}

module screw_head(part, head="socket", drive="hex", thread, tolerance=1.00, override_h=U, orient=Z, align=N)
{
    assert(head != U, "screw_head: head == U");
    assert(thread!=U, "screw_head: thread == undef");

    threadsize = get(ThreadSize, thread);
    head_h = fallback(override_h, get_screw_head_h(head=head, thread=thread));
    head_d = get_screw_head_d(head=head, thread=thread);

    if(part==U)
    {
        difference()
        {
            screw_head(part="pos", head=head, drive=drive, thread=thread, tolerance=tolerance, override_h=override_h, orient=orient, align=align);
            screw_head(part="neg", head=head, drive=drive, thread=thread, tolerance=tolerance, override_h=override_h, orient=orient, align=align);
        }
    }
    else if(part=="pos")
    material(Mat_Steel)
    size_align(size=[head_d, head_d, head_h], orient=orient, align=align)
    {
        if(head=="socket")
        {
            tz(-head_h/2)
            cylindera(d=head_d, h = head_h, align=Z);
        }
        else if(head=="button")
        {
            c = head_d;
            f = head_h*1.25;
            r = ( pow(c,2)/4 + pow(f,2) )/(2*f);

            d = r - f; // displacement to move sphere

            intersection()
            {
                tz(-head_h/2)
                scale([1,1,.5])
                spherea(r = head_h, align=N);

                tz(-head_h/2)
                cylindera(d=head_d, h = head_h/2, align=Z);
            }
        }
    }
    else if(part=="neg")
    material(Mat_Steel)
    size_align(size=[head_d, head_d, head_h], orient=orient, align=align)
    {
        tz(-head_h/2)
        if(drive=="hex")
        {
            threadsize_cut = threadsize-head_drive_threadsize_minus(head);
            inset = head_drive_inset(head, head_h);
            tz(inset)
            cylindera(d = fn_radius(threadsize_cut, 6), h = head_h, $fn = 6, align=Z);
        }
        else if(part=="neg")
        {
            if(head=="socket")
            {
                difference()
                {
                    tz(-head_h/2)
                    cylindera(d=head_d, h = fallback(override_h,head_h), align=Z);

                    if(drive == "hex")
                    {
                        cylindera(d = fn_radius(threadsize, 6), h = 1000, $fn = 6, align=Z);
                    }
                }
            }
            else if(head=="button")
            {
                c = head_d;
                f = head_h*1.25;
                r = ( pow(c,2)/4 + pow(f,2) )/(2*f);

                d = r - f; // displacement to move sphere

                difference()
                {
                    intersection()
                    {
                        tz(-head_h/2)
                        scale([1,1,.5])
                        spherea(r = head_h, align=N);

                        tz(-head_h/2)
                        cylindera(d=head_d, h = fallback(override_h,head_h)/2, align=Z);
                    }

                    if(drive == "hex")
                    {
                        // button head uses -0.5*mm smaller hex
                        tz(-head_h/2)
                        cylindera(d = fn_radius(threadsize-.5, 6), h = 1000, $fn = 6, align=Z);
                    }
                }
            }
            else if(head=="set")
            {
            }
        }
    }
}

function head_drive_threadsize_minus(head) =
head=="socket"?  0:
head=="button"?  0.5:
head=="set"?     1.5:
0;

function head_drive_inset(head, head_h) =
head=="socket"?  1*mm:
head=="button"?  0.5*mm:
head=="set"?     -head_h/2:
0;

module screw_head_cut(head="socket", thread, tolerance=1.05, override_h=U, orient=Z, align=N)
{
    assert(head != U, "screw_head: head == U");
    assert(thread!=U, "screw_head_cut: thread is undef");

    threadsize = get(ThreadSize, thread);
    head_h = get_screw_head_h(head=head, thread=thread);
    head_d = get_screw_head_d(head=head, thread=thread)*tolerance;
    size_align(size=[head_d, head_d, head_h], orient=orient, align=align)
    {
        if(head=="socket" || head=="button")
        {
            tz(-head_h/2)
            cylindera(d=head_d, h=fallback(override_h, head_h), align=Z);
        }
        else if(head=="set")
        {
            tz(-head_h/2)
            cylindera(d=threadsize, h=fallback(override_h, head_h), align=Z);
        }
        else
        {
            assert("head: not implemented");
        }
    }
}

module screw_nut(nut, thread, tolerance=1.00, override_h=U, orient=Z, align=N)
{
    assert(nut!=U, "screw_nut: nut is undef");

    nut_thick = get(NutThickness,nut)*tolerance;
    nut_facets = get(NutFacets, nut);
    nut_dia = nut_dia(nut);
    nut_thread = fallback(thread, get(NutThread, nut));
    h_ = fallback(override_h, nut_thick);
    material(Mat_Steel)
    size_align([nut_dia, nut_dia, h_], orient=orient, align=align)
    {
        difference()
        {
            cylindera($fn=nut_facets, d=nut_dia, h=h_);
            tz(-h_/2-.2)
            screw_thread(thread=nut_thread, h=h_+.1);
        }
    }
}

module screw_nut_cut(nut, tolerance=1.05, h=1000, with_access=true, orient=Z, align=N)
{
    assert(nut!=U, "screw_nut_cut: nut is undef");

    nut_thick = get(NutThickness,nut)*tolerance;
    nut_dia = nut_dia(nut);
    size_align(size=[nut_dia, nut_dia, nut_thick], orient=orient, align=align)
    {
        tz(nut_thick/2)
        {
            tz(-.1)
            screw_nut(nut=nut, tolerance=tolerance, orient=Z, align=-Z);

            if(with_access)
            tz(-nut_thick-.01)
            {
                cylindera(d=nut_dia+2*mm, h=h, orient=Z, align=-Z);
            }
        }
    }
}

function nut_radius(nut) = outradius(get(NutWidthMin, nut)/2, get(NutFacets, nut));
function nut_dia(nut) = 2*nut_radius(nut);

module nut_trap_cut(nut, thread, head="socket", trap_offset=10, screw_l=10*mm, screw_offset=0*mm, cut_screw=false, trap_h=10, trap_axis=-Y, orient=Z, align=N)
{
    nut_thread = get(NutThread, nut);
    thread_ = fallback(thread, nut_thread);
    assert(thread_ != U, "nut_trap_cut: No nut or thread given");
    /*assert(nut!=U && thread!=U && nut_thread != thread_, "nut_trap_cut: Mismatched nut and thread");*/

    threadsize = get(ThreadSize, thread_);
    head_h = get_screw_head_h(head=head, thread=thread);
    nut_h = get(NutThickness, nut) +.5*mm;

    tolerance = -.2*mm;
    nut_width_min = get(NutWidthMin, nut)+tolerance;
    nut_facets = get(NutFacets, nut);
    nut_dia = nut_dia(nut)+tolerance;
    total_h = nut_h;
    size_align(size=[nut_dia, nut_dia, total_h], orient=orient, orient_ref=Z, align=align)
    {
        if(cut_screw)
        {
            tz(-nut_h/2)
            tz(screw_offset)
            screw_cut($show_vit=false, nut=nut, thread=thread_, head=head, with_nut=false, tolerance=1.1, h=screw_l, orient=-Z, align=Z);
        }
        else
        {
            tz(nut_h/2+screw_offset)
            screw_thread_cut($show_vit=false, thread=thread_, tolerance=1.1, h=screw_l, orient=-Z, align=Z);
        }

        tz(trap_offset)
        hull()
        {
            orient(axis=Z, axis_ref=orient)
            translate(-.15*mm*trap_axis)
            stack(dist=trap_h, axis=trap_axis)
            {
                orient(axis=orient, axis_ref=Z)
                {
                    rotate([0,0,30])
                    cylindera($fn=nut_facets, d=nut_dia, h=nut_h, align=N);
                }

                orient(axis=orient, axis_ref=Z)
                cubea(size=[nut_width_min,nut_width_min,nut_h], align=N);
            }
        }

        tz(trap_offset)
        if($show_vit)
        {
            if(cut_screw)
            {
                tz(-nut_h/2)
                tz(screw_offset)
                %screw(nut=nut, thread=thread_, head=head, with_nut=false, tolerance=1, h=screw_l, orient=-Z, align=Z);
            }

            rotate([0,0,30])
            %screw_nut(nut=nut, thread=thread, tolerance=1, orient=-Z, align=N);
        }

    }
}

if(false)
{
    all_axes()
    translate($axis*5*mm)
    color($color)
    screw(nut=NutHexM3, h=10*mm, orient=-$axis, align=$axis);
}

// all nuts
if(false)
{
    for(nuti=[0:1:len(AllNut)-1])
    {
        nut = AllNut[nuti];
        v_threadsize = v_get(AllNut,ThreadSize);
        v_nutwidthmin = v_get(AllNut,NutWidthMin);
        dist = v_cumsum(v_nutwidthmin, 0, nuti)[nuti]*1.2;
        translate(X*dist)
        rotate([0,0,30])
        screw(nut=nut, h=get(NutWidthMax, nut)*5, head_embed=false, orient=-Z, align=Z);
    }
}

// From ParkinBots threading library
// https://www.thingiverse.com/thing:1659079
/*module Threading(D = 0, pitch = 1, d = 12, windings = 10, helices = 1, angle = 60, steps=40)*/
/*{*/
    /*R = D==0?d/2+2*pitch/PI:D/2; */
    /*translate([0,0,-pitch])*/
    /*difference()*/
    /*{*/
        /*translate([0,0,pitch])*/
        /*cylinder (r=R, h =pitch*(windings-helices));*/
        /*threading(pitch, d, windings, helices, angle, steps, true); */
    /*}*/
/*}*/

// From ParkinBots threading library
// https://www.thingiverse.com/thing:1659079
module threading(pitch = 1, d = 12, windings = 10, helices = 1, angle = 60, steps=40, full = false)
{
    // tricky: glue two 180° pieces together to get a proper manifold
    r = d/2;
    Steps = steps/2;
    Pitch = pitch*helices;
    if(full)
    {
        cylinder(r = r-.5-pitch/PI, h=pitch*(windings+helices), $fn=steps);
    }

    sweep(gen_dat(), planar_caps=true);   // half screw
    rz(180)
    tz(Pitch/2)
    sweep(gen_dat(), planar_caps=true);   // half screw

    function gen_dat() =
        let(ang = 180, bar = R_(180, -90, 0, Ty_(-r+.5, vec3D(pitch/PI*Rack(windings, angle)))))
        [for (i=[0:Steps]) Tz_(i/2/Steps*Pitch, -Rz_(i*ang/Steps, bar))];

    function Rack(w, angle) =
        concat([[0, 2]],
            [for (i=[0:w-1], j=[0:3])
            let(t = [ [0, 1], [2*tan(angle/2), -1], [PI/2, -1], [2*tan(angle/2)+PI/2, 1]])
            [t[j][0]+i*PI, t[j][1]]], [[w*PI, 1], [w*PI, 2]
            ]);
}

if(false)
{
    $fn=32;
    all_heads=
    [
        "socket",
        "button",
        "set"
    ];
    for(i=[0:len(all_heads)-1])
    {
        head=all_heads[i];
        tx(i*8*mm)
        screw(nut=NutHexM3, head=head, h=25, orient=-Z);
    }

}

if(false)
{
    $fn=128;
    nut1 = NutHexM3;
    stack(axis=X, dist = 20)
    {
        screw(nut=NutHexM8, head="socket", h=25, orient=-Z);
        screw(nut=NutHexM8, head="button", h=25, orient=-Z);
        screw(nut=NutHexM8, head="button", h=25, with_head=1, with_nut=0, orient=-Z);
        screw(nut=NutHexM8, head="button", h=25, with_head=0, with_nut=0, orient=-Z);
        screw(nut=NutHexM8, head="set", h=25, with_nut=0, orient=-Z);

        threading(pitch = 1.25, d=8, windings=10, full=true);
    }
}

if(false)
{
    box_w = 150*mm;
    box_d = 10*mm;
    box_h = 10*mm;
    o = 10*mm;

    $show_vit = true;
    /*$fs= 0.2;*/
    /*$fa = 4;*/

    nut1 = NutHexM3;
    nut2 = NutHexM5;
    nut3 = NutKnurlM3_3_42;
    nut4 = NutKnurlM3_5_42;

    /*all_axes()*/
    /*for($axis=[X])*/
    /*color($color)*/
    /*translate($axis*15)*/
    /*echo($axis)*/

    /*orient(axis=$axis, axis_ref=-X)*/
    for(y=[-1,1])
    translate(-y*Y*20*mm)
    difference()
    {
        cubea([box_w,box_d,box_h], align=X+Z);
        test_cuts(orient=y*Z);
        /*%test_cuts(orient=y, axis_ref=-Z);*/
    }

}

module test_cuts(orient)
{
    union()
    {
        /*offset = 0;*/
        offset = -5;
        /*translate([0,offset,0])*/
        /*translate([5,0,-5])*/
        translate([5,0,0])
        stack(dist=10,axis=X)
        {
            screw_cut(nut=nut1, head="button", h=box_h, head_embed=false, orient=orient, align=Z);
            screw_cut(nut=nut1, head="socket", h=box_h, head_embed=false, orient=orient, align=Z);
            screw_cut(thread=ThreadM5, h=box_h, head_embed=false, orient=orient, align=Z);
            screw_cut(nut=nut1, h=box_h, head_embed=false, orient=orient, align=Z);

            screw_cut(nut=nut1, h=box_h, nut_offset=0*mm, head_embed=true, orient=orient, align=Z);
            screw_cut(nut=nut2, h=box_h, nut_offset=3*mm, head_embed=false, orient=orient, align=Z);
            screw_cut(nut=nut3, h=box_h, head_embed=false, orient=orient, align=Z);
            screw_cut(nut=nut4, h=box_h, head_embed=false, orient=orient, align=Z);

            translate([0,0,-5*mm])
            screw_cut(nut=nut3, h=box_h, head_embed=false, orient=orient, align=Z);

            translate([0,0,box_h/2])
            nut_trap_cut(nut=nut1, h=5, head_embed=false, trap_h=10, trap_axis=-Y, orient=orient, align=N);

            translate([0,0,box_h/2])
            nut_trap_cut(nut=nut1, h=5, head_embed=false, trap_h=10, trap_axis=Y, orient=orient, align=N);

            translate([0,0,box_h/2])
            nut_trap_cut(nut=nut1, h=box_d, head_embed=false, trap_h=10, screw_offset=2*mm, trap_axis=Z, orient=orient, align=N);
            translate([0,0,box_h/2])
            nut_trap_cut(nut=nut1, h=box_d, head_embed=false, trap_h=10, screw_offset=2*mm, trap_axis=-Z, orient=orient, align=N);
        }
    }
}

include <system.scad>
include <thread-data.scad>
include <nut-data.scad>
include <materials.scad>

use <shapes.scad>
use <transforms.scad>
use <misc.scad>

// for proper threads
use <naca_sweep.scad>

// naive, assume head height is same as thread size (generally true for cap heads)
function get_screw_head_h(head, thread) =
(head=="socket" || head == "button") ?
    get(ThreadSize, thread)
:
head == "set" ? 0
:
0;

function get_screw_head_d(head, thread) =
(head=="socket" || head == "button") ?
    2 * get(ThreadSize, thread)
:
head == "set" ? 0
:
0;

module screw(part, nut, thread, head="socket", h=10, tolerance=1.05, head_embed=false, with_nut=true, with_head=true, nut_offset=0, orient=Z, align=N)
{
    nut_thread = get(NutThread, nut);
    thread_ = fallback(thread, nut_thread);
    assert(thread_ != U, "screw: No nut or thread given");
    assert(head != U, "screw: head == U");
    /*assert(nut!=U && thread!=U && nut_thread != thread_, "screw: Mismatched nut and thread");*/

    head_h = get_screw_head_h(head=head, thread=thread_);
    nut_h = get(NutThickness,nut)*tolerance;
    threadsize = get(ThreadSize, thread_);

    s = threadsize*tolerance;
    total_h = h;

    if(part==U)
    {
        difference()
        {
            screw(part="pos", nut=nut, thread=thread, head=head, h=h, tolerance=tolerance, head_embed=head_embed, with_nut=with_nut, with_head=with_head, nut_offset=nut_offset, orient=orient, align=align);
            screw(part="neg", nut=nut, thread=thread, head=head, h=h, tolerance=tolerance, head_embed=head_embed, with_nut=with_nut, with_head=with_head, nut_offset=nut_offset, orient=orient, align=align);
        }
        if($show_vit)
        %screw(part="vit", nut=nut, thread=thread, head=head, h=h, tolerance=tolerance, head_embed=head_embed, with_nut=with_nut, with_head=with_head, nut_offset=nut_offset, orient=orient, align=align);
    }
    else if(part=="pos")
    size_align(size=[s, s, total_h], orient=-orient, orient_ref=Z, align=align)
    tz(head_embed?-head_h:0)
    {
        if(with_head)
        {
            tz(h/2+.01)
            screw_head(part=part, head=head, thread=thread_, orient=Z, align=Z);
        }

        tz(-h/2+.01)
        screw_thread(thread=thread_, head=head, h=h+.1, tolerance=tolerance, orient=Z, align=N);

        if(with_nut && nut != U)
        {
            tz(-h/2+nut_h+nut_offset+(head_embed?head_h:0))
            screw_nut(nut=nut, tolerance=tolerance, orient=Z, align=-Z);
        }
    }
    else if(part=="neg")
    size_align(size=[s, s, total_h], orient=-orient, orient_ref=Z, align=align)
    tz(head_embed?-head_h:0)
    {
        if(with_head)
        tz(h/2+.01)
        screw_head(part=part, head=head, thread=thread_, orient=Z, align=Z);
    }
    else if(part=="vit")
    size_align(size=[s, s, total_h], orient=-orient, orient_ref=Z, align=align)
    {
        if(with_nut && nut != U)
        {
            tz(-h/2+nut_h+nut_offset+(head_embed?head_h:0))
            %screw_nut(nut=nut, align=-Z);
        }
    }
}

module screw_cut(nut, thread, head="socket", h=10, tolerance=1.05, head_embed=false, with_nut=true, with_nut_cut=true, with_nut_access=true, nut_cut_h=1000, with_head=true, head_cut_h=1000, nut_offset=0, cut_access=true, orient=Z, align=N)
{
    nut_thread = get(NutThread, nut);
    thread_ = fallback(thread, nut_thread);
    assert(thread_ != U, "screw_cut: No nut or thread given");
    /*assert(nut!=U && thread!=U && nut_thread != thread_, "screw_cut: Mismatched nut and thread");*/

    nut_h = get(NutThickness,nut)*tolerance;
    assert(head != U, "screw_cut: head == U");

    head_h = get_screw_head_h(head=head, thread=thread_);
    assert(head_h != U, "screw_cut: head_h is U!");

    threadsize = get(ThreadSize, thread_);
    assert(threadsize != U, "screw_cut: threadsize is U!");

    s = threadsize*tolerance;
    total_h = h;
    assert(s != U, "screw_cut: s is U!");
    size_align(size=[s, s, total_h], orient=-orient, orient_ref=Z, align=align)
    {
        tz(head_embed?-head_h:0)
        {
            if(with_head)
            {
                tz(h/2+.01)
                screw_head_cut(head=head, thread=thread_, tolerance=tolerance, override_h=head_cut_h, align=Z);
            }

            screw_thread_cut(thread=thread_, h=h+.1, tolerance=tolerance, orient=Z, align=N);

            if(with_nut && nut != U && with_nut_cut)
            {
                tz(-h/2+nut_h+nut_offset+(head_embed?head_h:0))
                screw_nut_cut(nut=nut, tolerance=tolerance, h=nut_cut_h, with_access=with_nut_access, align=-Z);
            }

            if($show_vit)
            {
                %screw(nut=nut, thread=thread, head=head, h=h, tolerance=tolerance, with_nut=with_nut, with_head=with_head, nut_offset=nut_offset, orient=-Z, align=N);
            }
        }
    }
}

module screw_thread(thread, h=10, tolerance=1.00, orient=Z, align=N)
{
    assert(thread!=U, "screw_thread: thread is undef");
    threadsize = get(ThreadSize, thread);
    d = get(ThreadSize, thread);
    pitch = get(ThreadPitchMm, thread);
    windings = ceil(h/pitch);

    size_align(size=[d,d,h], orient=orient, align=align)
    material(Mat_Steel)
    {
        if($preview_mode)
        {
            cylindera(d=d, h=h, align=Z);
        }
        else
        {
            intersection()
            {
                cylindera(d=d, h=h, align=Z);
                tz(-pitch)
                threading(angle=60, pitch=pitch, d=d, windings=windings, full=true);
            }
            /*inner_d = (get(ThreadInternalMinorDiaMin, thread) + get(ThreadInternalMinorDiaMin, thread))/2;*/
            /*cylindera(d=inner_d, h=h, align=Z);*/
        }
    }
}

module screw_thread_cut(thread, h=10, tolerance=1.05, orient=Z, align=N)
{
    assert(thread!=U, "screw_thread_cut: thread is undef");
    threadsize = get(ThreadSize, thread);
    cylindera(d=threadsize*tolerance, h=h, orient=orient, align=align);
}

module screw_head(part, head="socket", drive="hex", thread, tolerance=1.00, override_h=U, orient=Z, align=N)
{
    assert(head != U, "screw_head: head == U");
    assert(thread!=U, "screw_head: thread == undef");

    threadsize = get(ThreadSize, thread);
    head_h = fallback(override_h, get_screw_head_h(head=head, thread=thread));
    head_d = get_screw_head_d(head=head, thread=thread);

    if(part==U)
    {
        difference()
        {
            screw_head(part="pos", head=head, drive=drive, thread=thread, tolerance=tolerance, override_h=override_h, orient=orient, align=align);
            screw_head(part="neg", head=head, drive=drive, thread=thread, tolerance=tolerance, override_h=override_h, orient=orient, align=align);
        }
    }
    else if(part=="pos")
    material(Mat_Steel)
    size_align(size=[head_d, head_d, head_h], orient=orient, align=align)
    {
        if(head=="socket")
        {
            tz(-head_h/2)
            cylindera(d=head_d, h = head_h, align=Z);
        }
        else if(head=="button")
        {
            c = head_d;
            f = head_h*1.25;
            r = ( pow(c,2)/4 + pow(f,2) )/(2*f);

            d = r - f; // displacement to move sphere

            intersection()
            {
                tz(-head_h/2)
                scale([1,1,.5])
                spherea(r = head_h, align=N);

                tz(-head_h/2)
                cylindera(d=head_d, h = head_h/2, align=Z);
            }
        }
    }
    else if(part=="neg")
    material(Mat_Steel)
    size_align(size=[head_d, head_d, head_h], orient=orient, align=align)
    {
        tz(-head_h/2)
        if(drive=="hex")
        {
            threadsize_cut = threadsize-head_drive_threadsize_minus(head);
            inset = head_drive_inset(head, head_h);
            tz(inset)
            cylindera(d = fn_radius(threadsize_cut, 6), h = head_h, $fn = 6, align=Z);
        }
        else if(part=="neg")
        {
            if(head=="socket")
            {
                difference()
                {
                    tz(-head_h/2)
                    cylindera(d=head_d, h = fallback(override_h,head_h), align=Z);

                    if(drive == "hex")
                    {
                        cylindera(d = fn_radius(threadsize, 6), h = 1000, $fn = 6, align=Z);
                    }
                }
            }
            else if(head=="button")
            {
                c = head_d;
                f = head_h*1.25;
                r = ( pow(c,2)/4 + pow(f,2) )/(2*f);

                d = r - f; // displacement to move sphere

                difference()
                {
                    intersection()
                    {
                        tz(-head_h/2)
                        scale([1,1,.5])
                        spherea(r = head_h, align=N);

                        tz(-head_h/2)
                        cylindera(d=head_d, h = fallback(override_h,head_h)/2, align=Z);
                    }

                    if(drive == "hex")
                    {
                        // button head uses -0.5*mm smaller hex
                        tz(-head_h/2)
                        cylindera(d = fn_radius(threadsize-.5, 6), h = 1000, $fn = 6, align=Z);
                    }
                }
            }
            else if(head=="set")
            {
            }
        }
    }
}

function head_drive_threadsize_minus(head) =
head=="socket"?  0:
head=="button"?  0.5:
head=="set"?     1.5:
0;

function head_drive_inset(head, head_h) =
head=="socket"?  1*mm:
head=="button"?  0.5*mm:
head=="set"?     -head_h/2:
0;

module screw_head_cut(head="socket", thread, tolerance=1.05, override_h=U, orient=Z, align=N)
{
    assert(head != U, "screw_head: head == U");
    assert(thread!=U, "screw_head_cut: thread is undef");

    threadsize = get(ThreadSize, thread);
    head_h = get_screw_head_h(head=head, thread=thread);
    head_d = get_screw_head_d(head=head, thread=thread)*tolerance;
    size_align(size=[head_d, head_d, head_h], orient=orient, align=align)
    {
        if(head=="socket" || head=="button")
        {
            tz(-head_h/2)
            cylindera(d=head_d, h=fallback(override_h, head_h), align=Z);
        }
        else if(head=="set")
        {
            tz(-head_h/2)
            cylindera(d=threadsize, h=fallback(override_h, head_h), align=Z);
        }
        else
        {
            assert("head: not implemented");
        }
    }
}

module screw_nut(nut, thread, tolerance=1.00, override_h=U, orient=Z, align=N)
{
    assert(nut!=U, "screw_nut: nut is undef");

    nut_thick = get(NutThickness,nut)*tolerance;
    nut_facets = get(NutFacets, nut);
    nut_dia = nut_dia(nut);
    nut_thread = fallback(thread, get(NutThread, nut));
    h_ = fallback(override_h, nut_thick);
    material(Mat_Steel)
    size_align([nut_dia, nut_dia, h_], orient=orient, align=align)
    {
        difference()
        {
            cylindera($fn=nut_facets, d=nut_dia, h=h_);
            tz(-h_/2-.2)
            screw_thread(thread=nut_thread, h=h_+.1);
        }
    }
}

module screw_nut_cut(nut, tolerance=1.05, h=1000, with_access=true, orient=Z, align=N)
{
    assert(nut!=U, "screw_nut_cut: nut is undef");

    nut_thick = get(NutThickness,nut)*tolerance;
    nut_dia = nut_dia(nut);
    size_align(size=[nut_dia, nut_dia, nut_thick], orient=orient, align=align)
    {
        tz(nut_thick/2)
        {
            tz(-.1)
            screw_nut(nut=nut, tolerance=tolerance, orient=Z, align=-Z);

            if(with_access)
            tz(-nut_thick-.01)
            {
                cylindera(d=nut_dia+2*mm, h=h, orient=Z, align=-Z);
            }
        }
    }
}

function nut_radius(nut) = outradius(get(NutWidthMin, nut)/2, get(NutFacets, nut));
function nut_dia(nut) = 2*nut_radius(nut);

module nut_trap_cut(nut, thread, head="socket", trap_offset=10, screw_l=10*mm, screw_offset=0*mm, cut_screw=false, trap_h=10, trap_axis=-Y, orient=Z, align=N)
{
    nut_thread = get(NutThread, nut);
    thread_ = fallback(thread, nut_thread);
    assert(thread_ != U, "nut_trap_cut: No nut or thread given");
    /*assert(nut!=U && thread!=U && nut_thread != thread_, "nut_trap_cut: Mismatched nut and thread");*/

    threadsize = get(ThreadSize, thread_);
    head_h = get_screw_head_h(head=head, thread=thread);
    nut_h = get(NutThickness, nut) +.5*mm;

    tolerance = -.2*mm;
    nut_width_min = get(NutWidthMin, nut)+tolerance;
    nut_facets = get(NutFacets, nut);
    nut_dia = nut_dia(nut)+tolerance;
    total_h = nut_h;
    size_align(size=[nut_dia, nut_dia, total_h], orient=orient, orient_ref=Z, align=align)
    {
        if(cut_screw)
        {
            tz(-nut_h/2)
            tz(screw_offset)
            screw_cut($show_vit=false, nut=nut, thread=thread_, head=head, with_nut=false, tolerance=1.1, h=screw_l, orient=-Z, align=Z);
        }
        else
        {
            tz(nut_h/2+screw_offset)
            screw_thread_cut($show_vit=false, thread=thread_, tolerance=1.1, h=screw_l, orient=-Z, align=Z);
        }

        hull()
        {
            orient(axis=Z, axis_ref=orient)
            translate(-.15*mm*trap_axis)
            stack(dist=trap_h, axis=trap_axis)
            {
                orient(axis=orient, axis_ref=Z)
                {
                    rotate([0,0,30])
                    cylindera($fn=nut_facets, d=nut_dia, h=nut_h, align=N);
                }

                orient(axis=orient, axis_ref=Z)
                cubea(size=[nut_width_min,nut_width_min,nut_h], align=N);
            }
        }

        if($show_vit)
        {
            if(cut_screw)
            {
                tz(-nut_h/2)
                tz(screw_offset)
                %screw(nut=nut, thread=thread_, head=head, with_nut=false, tolerance=1, h=screw_l, orient=-Z, align=Z);
            }

            rotate([0,0,30])
            %screw_nut(nut=nut, thread=thread, tolerance=1, orient=-Z, align=N);
        }

    }
}

if(false)
{
    all_axes()
    translate($axis*5*mm)
    color($color)
    screw(nut=NutHexM3, h=10*mm, orient=-$axis, align=$axis);
}

// all nuts
if(false)
{
    for(nuti=[0:1:len(AllNut)-1])
    {
        nut = AllNut[nuti];
        v_threadsize = v_get(AllNut,ThreadSize);
        v_nutwidthmin = v_get(AllNut,NutWidthMin);
        dist = v_cumsum(v_nutwidthmin, 0, nuti)[nuti]*1.2;
        translate(X*dist)
        rotate([0,0,30])
        screw(nut=nut, h=get(NutWidthMax, nut)*5, head_embed=false, orient=-Z, align=Z);
    }
}

// From ParkinBots threading library
// https://www.thingiverse.com/thing:1659079
/*module Threading(D = 0, pitch = 1, d = 12, windings = 10, helices = 1, angle = 60, steps=40)*/
/*{*/
    /*R = D==0?d/2+2*pitch/PI:D/2; */
    /*translate([0,0,-pitch])*/
    /*difference()*/
    /*{*/
        /*translate([0,0,pitch])*/
        /*cylinder (r=R, h =pitch*(windings-helices));*/
        /*threading(pitch, d, windings, helices, angle, steps, true); */
    /*}*/
/*}*/

// From ParkinBots threading library
// https://www.thingiverse.com/thing:1659079
module threading(pitch = 1, d = 12, windings = 10, helices = 1, angle = 60, steps=40, full = false)
{
    // tricky: glue two 180° pieces together to get a proper manifold
    r = d/2;
    Steps = steps/2;
    Pitch = pitch*helices;
    if(full)
    {
        cylinder(r = r-.5-pitch/PI, h=pitch*(windings+helices), $fn=steps);
    }

    sweep(gen_dat(), planar_caps=true);   // half screw
    rz(180)
    tz(Pitch/2)
    sweep(gen_dat(), planar_caps=true);   // half screw

    function gen_dat() =
        let(ang = 180, bar = R_(180, -90, 0, Ty_(-r+.5, vec3D(pitch/PI*Rack(windings, angle)))))
        [for (i=[0:Steps]) Tz_(i/2/Steps*Pitch, -Rz_(i*ang/Steps, bar))];

    function Rack(w, angle) =
        concat([[0, 2]],
            [for (i=[0:w-1], j=[0:3])
            let(t = [ [0, 1], [2*tan(angle/2), -1], [PI/2, -1], [2*tan(angle/2)+PI/2, 1]])
            [t[j][0]+i*PI, t[j][1]]], [[w*PI, 1], [w*PI, 2]
            ]);
}

if(false)
{
    $fn=32;
    all_heads=
    [
        "socket",
        "button",
        "set"
    ];
    for(i=[0:len(all_heads)-1])
    {
        head=all_heads[i];
        tx(i*8*mm)
        screw(nut=NutHexM3, head=head, h=25, orient=-Z);
    }

}

if(false)
{
    $fn=128;
    nut1 = NutHexM3;
    stack(axis=X, dist = 20)
    {
        screw(nut=NutHexM8, head="socket", h=25, orient=-Z);
        screw(nut=NutHexM8, head="button", h=25, orient=-Z);
        screw(nut=NutHexM8, head="button", h=25, with_head=1, with_nut=0, orient=-Z);
        screw(nut=NutHexM8, head="button", h=25, with_head=0, with_nut=0, orient=-Z);
        screw(nut=NutHexM8, head="set", h=25, with_nut=0, orient=-Z);

        threading(pitch = 1.25, d=8, windings=10, full=true);
    }
}

if(false)
{
    box_w = 150*mm;
    box_d = 10*mm;
    box_h = 10*mm;
    o = 10*mm;

    $show_vit = true;
    /*$fs= 0.2;*/
    /*$fa = 4;*/

    nut1 = NutHexM3;
    nut2 = NutHexM5;
    nut3 = NutKnurlM3_3_42;
    nut4 = NutKnurlM3_5_42;

    /*all_axes()*/
    /*for($axis=[X])*/
    /*color($color)*/
    /*translate($axis*15)*/
    /*echo($axis)*/

    /*orient(axis=$axis, axis_ref=-X)*/
    for(y=[-1,1])
    translate(-y*Y*20*mm)
    difference()
    {
        cubea([box_w,box_d,box_h], align=X+Z);
        test_cuts(orient=y*Z);
        /*%test_cuts(orient=y, axis_ref=-Z);*/
    }

}

module test_cuts(orient)
{
    union()
    {
        /*offset = 0;*/
        offset = -5;
        /*translate([0,offset,0])*/
        /*translate([5,0,-5])*/
        translate([5,0,0])
        stack(dist=10,axis=X)
        {
            screw_cut(nut=nut1, head="button", h=box_h, head_embed=false, orient=orient, align=Z);
            screw_cut(nut=nut1, head="socket", h=box_h, head_embed=false, orient=orient, align=Z);
            screw_cut(thread=ThreadM5, h=box_h, head_embed=false, orient=orient, align=Z);
            screw_cut(nut=nut1, h=box_h, head_embed=false, orient=orient, align=Z);

            screw_cut(nut=nut1, h=box_h, nut_offset=0*mm, head_embed=true, orient=orient, align=Z);
            screw_cut(nut=nut2, h=box_h, nut_offset=3*mm, head_embed=false, orient=orient, align=Z);
            screw_cut(nut=nut3, h=box_h, head_embed=false, orient=orient, align=Z);
            screw_cut(nut=nut4, h=box_h, head_embed=false, orient=orient, align=Z);

            translate([0,0,-5*mm])
            screw_cut(nut=nut3, h=box_h, head_embed=false, orient=orient, align=Z);

            translate([0,0,box_h/2])
            nut_trap_cut(nut=nut1, h=5, head_embed=false, trap_h=10, trap_axis=-Y, orient=orient, align=N);

            translate([0,0,box_h/2])
            nut_trap_cut(nut=nut1, h=5, head_embed=false, trap_h=10, trap_axis=Y, orient=orient, align=N);

            translate([0,0,box_h/2])
            nut_trap_cut(nut=nut1, h=box_d, head_embed=false, trap_h=10, screw_offset=2*mm, trap_axis=Z, orient=orient, align=N);
            translate([0,0,box_h/2])
            nut_trap_cut(nut=nut1, h=box_d, head_embed=false, trap_h=10, screw_offset=2*mm, trap_axis=-Z, orient=orient, align=N);
        }
    }
}


stepper: fixes

stepper: fixes

 // Some code from MCAD/stepper.scad
 // Originally by Hans Häggström, 2010.
 // Dual licenced under Creative Commons Attribution-Share Alike 3.0 and LGPL2 or later

include <system.scad>
include <units.scad>
include <materials.scad>
include <stepper-data.scad>
include <misc.scad>

use <transforms.scad>
use <shapes.scad>

/*test2();*/

module test()
{
    for (size = AllNemaSizes)
    {
        ty(size*100)
        stack(axis=X, dist=100)
        {
            motor(model=Nema34, size=size, dual_axis=true);
            motor(model=Nema23, size=size, dual_axis=true);
            motor(model=Nema17, size=size, dual_axis=true);
            motor(model=Nema14, size=size, dual_axis=true);
            motor(model=Nema11, size=size, dual_axis=true);
            motor(model=Nema08, size=size, dual_axis=true);
        }
    }
}

module test2()
{
    $show_vit=true;
    model = Nema17;
    size = NemaMedium;
    all_axes()
    color($color)
    translate(100*$axis)
    motor_mount(model=model, size=size, thickness=6*mm, dual_axis=true, orient=$axis);
    /*motor(model=model, size=size, thickness=6*mm, dual_axis=true, orient=$axis);*/
}

function motorWidth(model) = get(NemaSideSize, model);
function motorLength(model, size=NemaMedium) = get(size, model);

module motor_mount(part=U, model, thickness, size=NemaMedium, extra_size=U, dual_axis=false, orient=Z, align=N)
{
    assert(model!=U, "motor_mount(): model==U");
    assert(thickness!=U, "motor_mount(): thickness==U");
    assert(size!=U, "motor_mount(): size==U");

    length = get(size, model);
    side = get(NemaSideSize, model);
    extrSize = get(NemaRoundExtrusionHeight, model);
    extrRad = get(NemaRoundExtrusionDiameter, model) * 0.5;
    holeDist = get(NemaDistanceBetweenMountingHoles, model) * 0.5;
    holeRadius = get(NemaMountingHoleDiameter, model) * 0.5;

    s=[side+fallback(extra_size.x,0), side+fallback(extra_size.y,0), length];
    size_align(size=s, orient=orient, align=align)
    if(part==U)
    {
        material(Mat_Plastic)
        render()
        difference()
        {
            motor_mount(part="pos", model=model, thickness=thickness, size=size, dual_axis=dual_axis);
            motor_mount(part="neg", model=model, thickness=thickness, size=size, dual_axis=dual_axis);
        }

        if($show_vit)
        motor_mount(part="vit", model=model, thickness=thickness, size=size, dual_axis=dual_axis);
    }
    else if(part=="pos")
    {
        rcubea([s.x,s.y,thickness], align=Z);
    }
    else if(part=="neg")
    {
        cylindera(h=thickness+.1, r=extrRad, align=Z, extra_h=.1, extra_align=-Z);

        // main motor
        tz(-s.z)
        cubea(size=[side+.3*mm,side+.3*mm,length], align=Z);

        // Bolt holes
        material(Mat_Aluminium)
        for(x=[-1,1])
        for(y=[-1,1])
        tx(x*holeDist)
        ty(y*holeDist)
        cylindera(h=thickness+.1, r=holeRadius, align=Z, extra_h=.1, extra_align=-Z);

    }
    else if(part=="vit")
    {
        motor(model=model, size=size, dualAxis=dual_axis);
    }
}

module motor(part=U, model, size=NemaMedium, dual_axis=false, orient=Z, align=N)
{
    assert(model!=U, "model == U");
    assert(size!=U, "size == U");
    length = get(size, model);

    side = get(NemaSideSize, model);
    cutR = get(NemaMountingHoleCutoutRadius, model);
    lip = get(NemaMountingHoleLip, model);
    holeDepth = get(NemaMountingHoleDepth, model);

    axleRadius = get(NemaAxleDiameter, model) * 0.5;

    extrSize = get(NemaRoundExtrusionHeight, model);
    extrRad = get(NemaRoundExtrusionDiameter, model) * 0.5;

    holeDist = get(NemaDistanceBetweenMountingHoles, model) * 0.5;
    holeRadius = get(NemaMountingHoleDiameter, model) * 0.5;

    mid = side / 2;

    roundR = get(NemaEdgeRoundingRadius, model);

    s=[side, side, length];
    size_align(size=s, orient=orient, orient_ref=Z, align=align)
    if(part==U)
    {
        difference()
        {
            motor(part="pos", model=model, size=size, dual_axis=dual_axis);
            motor(part="neg", model=model, size=size, dual_axis=dual_axis);
        }
        motor(part="vit", model=model, size=size, dual_axis=dual_axis);

        motor_axle(model=model, size=size, dual_axis=dual_axis);
    }
    if(part=="echo")
    {
        echo(str("  Motor: Nema",get(NemaModel, model),", length= ",length,"mm, dual axis=",dual_axis));
    }
    else if(part=="pos")
    {
        material(Mat_BlackPaint)
        tz(-s.z)
        cubea(s, align=Z, extra_size=Z*extrSize, extra_align=Z);
    }
    else if(part=="neg")
    {
        // Axle hole
        material(Mat_Aluminium)
        cylindera(r=axleRadius+.2*mm, h=1000);

        // Corner cutouts
        if (lip > 0)
        material(Mat_BlackPaint)
        tz(-s.z)
        for(x=[-1,1])
        for(y=[-1,1])
        tx(x*side/2)
        ty(y*side/2)
        tz(-lip)
        cylindera(h=length, r=cutR, align=Z);

        // Rounded edges
        if (roundR > 0)
        material(Mat_BlackPaint)
        tz(-s.z)
        for(x=[-1,1])
        for(y=[-1,1])
        tx(x*side/2)
        ty(y*side/2)
        mirror([x>0?0:1,y>0?0:1,0])
        rz(45)
        cubea(size=[roundR, roundR*2, 4+length + extrSize+2], align=Z, extra_size=.1*Z, extra_align=-Z);

        // Bolt holes
        material(Mat_Aluminium)
        for(x=[-1,1])
        for(y=[-1,1])
        tx(x*holeDist)
        ty(y*holeDist)
        cylindera(h=holeDepth+.1*mm, r=holeRadius, align=-Z, extra_h=.1, extra_align=Z);

        // Grinded flat
        material(Mat_BlackPaint)
        difference()
        {
            cubea(size=[side+.1, side+.1, extrSize+.1], align=Z);
            cylindera(h=extrSize, r=extrRad, align=Z);
        }
    }
}

module motor_axle(model=Nema23, size=NemaMedium, dual_axis=false, orient=Z, align=U)
{
    assert(model!=U, "model == U");
    assert(size!=U, "size == U");
    length = get(size, model);

    axleLengthFront = get(NemaFrontAxleLength, model);
    axleLengthBack = get(NemaBackAxleLength, model);
    axleRadius = get(NemaAxleDiameter, model) * 0.5;

    extrSize = get(NemaRoundExtrusionHeight, model);

    roundR = get(NemaEdgeRoundingRadius, model);

    axleFlatDepth = get(NemaAxleFlatDepth, model);
    axleFlatLengthFront = get(NemaAxleFlatLengthFront, model);
    axleFlatLengthBack = get(NemaAxleFlatLengthBack, model);

    material(Mat_Steel)
    render()
    intersection()
    {
        material(Mat_Aluminium)
        tz(-length)
        cylindera(r=axleRadius, h=length+extrSize+axleLengthFront, align=Z, extra_h=dual_axis?axleLengthBack:0, extra_align=-Z);

        material(Mat_Aluminium)
        union()
        {
            tx(axleFlatDepth>0?-axleFlatDepth:0)
            cubea(size=[2*axleRadius, 2*axleRadius, axleLengthFront], align=Z);

            cubea(size=[2*axleRadius, 2*axleRadius, length+.4*mm], align=-Z, extra_size=Z*(extrSize+.4*mm), extra_align=Z);

            tx(axleFlatDepth>0?-axleFlatDepth:0)
            tz(-length)
            cubea(size=[2*axleRadius, 2*axleRadius, axleLengthBack], align=-Z);
        }
    }
}

 // Some code from MCAD/stepper.scad
 // Originally by Hans Häggström, 2010.
 // Dual licenced under Creative Commons Attribution-Share Alike 3.0 and LGPL2 or later

include <system.scad>
include <units.scad>
include <materials.scad>
include <stepper-data.scad>
include <misc.scad>

use <transforms.scad>
use <shapes.scad>

/*test2();*/

module test()
{
    for (size = AllNemaSizes)
    {
        ty(size*100)
        stack(axis=X, dist=100)
        {
            motor(model=Nema34, size=size, dual_axis=true);
            motor(model=Nema23, size=size, dual_axis=true);
            motor(model=Nema17, size=size, dual_axis=true);
            motor(model=Nema14, size=size, dual_axis=true);
            motor(model=Nema11, size=size, dual_axis=true);
            motor(model=Nema08, size=size, dual_axis=true);
        }
    }
}

module test2()
{
    model = Nema17;
    size = NemaMedium;
    motor_mount(model=model, size=size, dual_axis=true);
}

function motorWidth(model) = get(NemaSideSize, model);
function motorLength(model, size=NemaMedium) = get(size, model);

module motor_mount(part=U, model, thickness, size=NemaMedium, extra_size=U, dual_axis=false, orient=Z, align=U)
{
    assert(model!=U, "motor_mount(): model==U");
    assert(thickness!=U, "motor_mount(): thickness==U");
    assert(size!=U, "motor_mount(): size==U");

    length = get(size, model);
    side = get(NemaSideSize, model);
    extrSize = get(NemaRoundExtrusionHeight, model);
    extrRad = get(NemaRoundExtrusionDiameter, model) * 0.5;
    holeDist = get(NemaDistanceBetweenMountingHoles, model) * 0.5;
    holeRadius = get(NemaMountingHoleDiameter, model) * 0.5;

    s=[side+fallback(extra_size.x,0), side+fallback(extra_size.y,0), length];
    size_align(size=s, orient=orient, align=align)
    if(part==U)
    {
        material(Mat_Plastic)
        render()
        difference()
        {
            motor_mount(part="pos", model=model, thickness=thickness, size=size, dual_axis=dual_axis);
            motor_mount(part="neg", model=model, thickness=thickness, size=size, dual_axis=dual_axis);
        }

        if($show_vit)
        motor_mount(part="vit", model=model, thickness=thickness, size=size, dual_axis=dual_axis);
    }
    else if(part=="pos")
    {
        rcubea([s.x,s.y,thickness], align=Z);
    }
    else if(part=="neg")
    {
        cylindera(h=thickness+.1, r=extrRad, align=Z, extra_h=.1, extra_align=-Z);

        // main motor
        tz(-s.z)
        cubea(size=[side+.3*mm,side+.3*mm,length], align=Z);

        // Bolt holes
        material(Mat_Aluminium)
        for(x=[-1,1])
        for(y=[-1,1])
        tx(x*holeDist)
        ty(y*holeDist)
        cylindera(h=thickness+.1, r=holeRadius, align=Z, extra_h=.1, extra_align=-Z);

    }
    else if(part=="vit")
    {
        motor(model=model, size=size, dualAxis=dual_axis, orient=Z);
    }
}

module motor(part=U, model, size=NemaMedium, dual_axis=false, orient=Z, align=U)
{
    assert(model!=U, "model == U");
    assert(size!=U, "size == U");
    length = get(size, model);

    side = get(NemaSideSize, model);
    cutR = get(NemaMountingHoleCutoutRadius, model);
    lip = get(NemaMountingHoleLip, model);
    holeDepth = get(NemaMountingHoleDepth, model);

    axleRadius = get(NemaAxleDiameter, model) * 0.5;

    extrSize = get(NemaRoundExtrusionHeight, model);
    extrRad = get(NemaRoundExtrusionDiameter, model) * 0.5;

    holeDist = get(NemaDistanceBetweenMountingHoles, model) * 0.5;
    holeRadius = get(NemaMountingHoleDiameter, model) * 0.5;

    mid = side / 2;

    roundR = get(NemaEdgeRoundingRadius, model);

    s=[side, side, length];
    size_align(size=s, orient=orient, align=align)
    if(part==U)
    {
        difference()
        {
            motor(part="pos", model=model, size=size, dual_axis=dual_axis);
            motor(part="neg", model=model, size=size, dual_axis=dual_axis);
        }
        motor(part="vit", model=model, size=size, dual_axis=dual_axis);

        motor_axle(model=model, size=size, dual_axis=dual_axis);
    }
    if(part=="echo")
    {
        echo(str("  Motor: Nema",get(NemaModel, model),", length= ",length,"mm, dual axis=",dual_axis));
    }
    else if(part=="pos")
    {
        material(Mat_BlackPaint)
        tz(-s.z)
        cubea(s, align=Z, extra_size=Z*extrSize, extra_align=Z);
    }
    else if(part=="neg")
    {
        // Axle hole
        material(Mat_Aluminium)
        cylindera(r=axleRadius+.2*mm, h=1000);

        // Corner cutouts
        if (lip > 0)
        material(Mat_BlackPaint)
        tz(-s.z)
        for(x=[-1,1])
        for(y=[-1,1])
        tx(x*side/2)
        ty(y*side/2)
        tz(-lip)
        cylindera(h=length, r=cutR, align=Z);

        // Rounded edges
        if (roundR > 0)
        material(Mat_BlackPaint)
        tz(-s.z)
        for(x=[-1,1])
        for(y=[-1,1])
        tx(x*side/2)
        ty(y*side/2)
        mirror([x>0?0:1,y>0?0:1,0])
        rz(45)
        cubea(size=[roundR, roundR*2, 4+length + extrSize+2], align=Z, extra_size=.1*Z, extra_align=-Z);

        // Bolt holes
        material(Mat_Aluminium)
        for(x=[-1,1])
        for(y=[-1,1])
        tx(x*holeDist)
        ty(y*holeDist)
        cylindera(h=holeDepth+.1*mm, r=holeRadius, align=-Z, extra_h=.1, extra_align=Z);

        // Grinded flat
        material(Mat_BlackPaint)
        difference()
        {
            cubea(size=[side+.1, side+.1, extrSize+.1], align=Z);
            cylindera(h=extrSize, r=extrRad, align=Z);
        }
    }
}

module motor_axle(model=Nema23, size=NemaMedium, dual_axis=false, orient=Z, align=U)
{
    assert(model!=U, "model == U");
    assert(size!=U, "size == U");
    length = get(size, model);

    axleLengthFront = get(NemaFrontAxleLength, model);
    axleLengthBack = get(NemaBackAxleLength, model);
    axleRadius = get(NemaAxleDiameter, model) * 0.5;

    extrSize = get(NemaRoundExtrusionHeight, model);

    roundR = get(NemaEdgeRoundingRadius, model);

    axleFlatDepth = get(NemaAxleFlatDepth, model);
    axleFlatLengthFront = get(NemaAxleFlatLengthFront, model);
    axleFlatLengthBack = get(NemaAxleFlatLengthBack, model);

    material(Mat_Steel)
    render()
    intersection()
    {
        material(Mat_Aluminium)
        tz(-length)
        cylindera(r=axleRadius, h=length+extrSize+axleLengthFront, align=Z, extra_h=dual_axis?axleLengthBack:0, extra_align=-Z);

        material(Mat_Aluminium)
        union()
        {
            tx(axleFlatDepth>0?-axleFlatDepth:0)
            cubea(size=[2*axleRadius, 2*axleRadius, axleLengthFront], align=Z);

            cubea(size=[2*axleRadius, 2*axleRadius, length+.4*mm], align=-Z, extra_size=Z*(extrSize+.4*mm), extra_align=Z);

            tx(axleFlatDepth>0?-axleFlatDepth:0)
            tz(-length)
            cubea(size=[2*axleRadius, 2*axleRadius, axleLengthBack], align=-Z);
        }
    }
}


Add an operator module that apply a render mode

/**
 * @license
 * MIT License
 *
 * Copyright (c) 2017 Jean-Sebastien CONAN
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/**
 * Part of the camelSCAD library.
 *
 * Rendering mode.
 *
 * @package core/mode
 * @author jsconan
 */

/**
 * Specifications for each rendering modes.
 * Each mode is defined this way: ["name", value for $fa, value for $fs]
 * @type Vector
 */
MODES = [
    ["dirty", 6, 2],
    ["dev", 1, 1.5],
    ["prod", .5, .5]
];

/**
 * The name of the default rendering mode.
 * @type String
 */
DEFAULT_MODE = "dev";

/**
 * Gets the specifications of a particular rendering mode.
 *
 * @param String [mode] - The mode for which get the specifications.
 * @returns Vector
 */
function renderMode(mode) = fetch(MODES, or(mode, DEFAULT_MODE));

/**
 * Gets the minimum facet angle for a particular rendering mode.
 *
 * @param String [mode] - The mode for which get the specification.
 * @returns Number
 */
function facetAngle(mode) = renderMode(mode)[1];

/**
 * Gets the minimum facet size for a particular rendering mode.
 *
 * @param String [mode] - The mode for which get the specification.
 * @returns Number
 */
function facetSize(mode) = renderMode(mode)[2];

/**
 * Applies a render mode onto the children modules.
 *
 * @param String [mode] - The mode to apply on the children modules.
 */
module applyMode(mode) {
    $fa = facetAngle(mode);
    $fs = facetSize(mode);

    children();
}


=Deckelnut verkleinert und Kuehlrippen angepasst.

/*
 * UW Video Leuchte
 *
 * Breiter Fokus mit viel Lichtleistung
 * Möglichst modular aufgebaut.
 *
 */

//ACHTUNG: Das Glas hat eine Toleranz von ± 0.5mm, was eine Berechnung des
// O-Rings und der dazugehörigen Nut erst ermöglicht,
// wenn das Teil genau ausgemessen ist.

// Aktueller Raum für Elektronik: 48mm Durchmesser, 40mm Länge...

//Rendering information
$fn=100;

// Variablenblock:

// max. Gesamtradius des Gehäuses
gehaeuseRadius = 44;
auflageRadiusGlas = 30;
glasRadius = 35;
gesamtLaenge = 90;

//M3 DIN 912 Innensechskantschrauben
kernlochM3 = 1.25;
durchfuehrungM3 = 1.6;
schraubenkopfM3 = 2.85;
schraubenkopfM3hoehe = 3.0;

//Axiale O-Ringe fuer Scheibe (ID = 60mm, Dicke = 2.5mm)
oRingRadiusInnen = 30.0;
oRingRadiusAussen = oRingRadiusInnen + 2.5;
oRingNutbreite = 3.4;
oRingNutTiefe = 1.9; //(± 0,06)
oRingNutAussenRadius = gehaeuseRadius - 4 -durchfuehrungM3 - schraubenkopfM3 - kernlochM3;
oRingNutInnenRadius = oRingNutAussenRadius - oRingNutbreite ;

kernlochKabelverschraubung = 7;

echo ("O-Ring Nut Maße: ID= ", oRingNutInnenRadius * 2, " AD=", oRingNutAussenRadius *2);

//Konstruktion der Hauptkomponenten:

//Die Deckelscheibe hält das Glas in der Fassung.
translate ([0, 0, 230]){
  deckelscheibe(auflageRadiusGlas, gehaeuseRadius, 6);
}

//Glasscheibe mit Dichtringen
translate ([0, 0, 200]){
  frontGlas (oRingRadiusInnen, oRingRadiusAussen);
}

//Linse vor LED
translate ([0, 0, 160]){
  rotate ([0,0,25]){
    linse ();
  }
}

//LED
translate ([0,0,140]){
  rotate ([0,0,25]){
    led ();
  }
}

//LED Trägerplatte mit Kernlochbohrungen für LED Modul
difference (){
  //LED carrier
  translate ([0,0,130]){
    ledCarrier(30, 5);
  }

  // Bohrungen für LED
  rotate ([0,0,25]){
    for (i=  [[17,17,130-1],
             [-17,17,130-1],
             [17,-17,130-1],
             [-17,-17, 130-1]]){
      translate (i){
        cylinder (h = 11, r = 1.25);
      }
    }
  }
}

korpus (gehaeuseRadius, glasRadius, 30);

//Konstruktion der Deckelscheibe.
// Diese klemmt die Borsilika Scheibe mit zwei O-Ringen.
// Sie wird mit 8 Schrauben auf dem Gehäuse befestigt.
module deckelscheibe (innenRadius, aussenRadius, hoehe){
  difference(){
    cylinder (h = hoehe, r = aussenRadius);
    translate ([0, 0, -0.1]){
      cylinder (h = hoehe + 0.2, r = innenRadius);
    }

    // M3 Durchführung, 1mm vom Rand entfernt
    translate ([0, 0, -0.1]){
      bohrungenAufRadius (aussenRadius - 2 - schraubenkopfM3, durchfuehrungM3, 8 , hoehe + 0.2);
    }
    // Senkung für M3, DIN 912, Innensechskant Schraubenköpfe
    translate ([0, 0, hoehe-schraubenkopfM3hoehe]){
      bohrungenAufRadius (aussenRadius - 2 - schraubenkopfM3,
                          schraubenkopfM3, 8 ,
                          schraubenkopfM3hoehe + 0.1);
    }

    //Nut für O-Ring
    translate ([0, 0, -1.0]){
      // 2 mm vom Kernloch der Verschraubung entfernt:
      ring (oRingNutAussenRadius,
            oRingNutInnenRadius,
            oRingNutTiefe);  //  + 1.0 O-Ring klemmt nur und dichtet hier nicht.
    }
  }
}

//Konstruktion der Borsilikascheibe mit Dichtringen
module frontGlas (dichtringID, dichtringAD){

  // Borsilika Glas
  // ø: 70 (± 0.5) x 12 (± 0.5)

  color("LightBlue"){
    cylinder (h = 12, r = glasRadius);
  }

  //O-Ringe
  color("Black"){
    //axial oben (nicht dichtend)
    translate ([0,0, 20]){
      ring ( dichtringAD, dichtringID, dichtringAD-dichtringID);
    }

    //axial unten
    translate ([0,0,-10]){
      ring ( dichtringAD, dichtringID,  dichtringAD-dichtringID);
    }
  }
}

// Linse für LED
module linse (){
  color("LightSteelBlue"){
    difference (){
      sphere (22.0);
      translate ([-25,-25,-23.0]){
       cube ([50,50,23]);
      }
    }
    translate ([-0,-0, -8]){
      cylinder(r=25,h=8);
    }
  }
}

// Die LED die aufgeschraubt wird
module led(){

  color("SlateGray"){
    difference (){
      cylinder (h = 1.42, r = 28);
      translate ([-20,20,-1.0]){
        cube ( [40, 20, 2.43]);
      }
      translate ([-20,-40,-1]){
        cube ( [40, 20, 3.43]);
      }

      for (i=[[17,17,-1],
             [-17,17,-1],
             [17,-17,-1],
             [ -17,-17, -1]]){
        translate (i){
          cylinder (h = 2.430, r= 1.75);
        }
      }

    }
  }

  color("Gray"){
    difference (){
      translate ([-20, -20, 1.42]){
        cube ( [40, 40, 2.86]);
      }
      translate ([-20, -20, 1.41]){
        for (i=[[0,40,-1],
                [40,0,-1],
                [0,0,-1],
                [40  ,40 ,-1]]){
          translate (i){
            cylinder (h = 3.895, r= 10);
          }
        }
      }
    }
  }

  color ("Yellow"){
    translate ([-10.5, -10.5, 1.43]){
      cube ( [21, 21, 2.88]);
    }
  }

}

//Auf diesen Träger wird die LED geschraubt
module ledCarrier(aussenRadius, hoehe){

  difference (){

    cylinder (h = hoehe, r = aussenRadius);

     // M3 Durchführung, 1mm vom Rand entfernt
    translate ([0, 0, -0.1]){
      bohrungenAufRadius (aussenRadius - 1 - schraubenkopfM3, durchfuehrungM3 , 8 , hoehe + 0.2);
    }
    // Senkung für M3, DIN 912, Innensechskant Schraubenköpfe
    translate ([0, 0, hoehe-schraubenkopfM3hoehe]){
      bohrungenAufRadius (aussenRadius - 1 - schraubenkopfM3,
                          schraubenkopfM3, 8 ,
                          schraubenkopfM3hoehe + 0.1);
    }
  }
}

//Der Hauptlampenkörper
module korpus(aussenRadius, glasAufnahmeRadius, ledAufnahmeRadius){

  difference(){
    cylinder (h = gesamtLaenge, r = aussenRadius);

    // === Innen

    //Glasaufnahme
    translate ([0, 0, gesamtLaenge - 12]){
      cylinder (h = gesamtLaenge, r =  glasAufnahmeRadius + 0.25);
    }

    //Bohrungen für Glasplattenhalter
    translate ([0, 0, gesamtLaenge - 20]){
      bohrungenAufRadius (aussenRadius - 2 - schraubenkopfM3, kernlochM3, 8 ,gesamtLaenge );
    }

    //Nut für O-Ring
    translate ([0, 0, gesamtLaenge - 12 - oRingNutTiefe]){
      // 2 mm vom Kernloch der Verschraubung entfernt:
      ring (oRingNutAussenRadius,
            oRingNutInnenRadius,
            oRingNutTiefe + 1.0);
    }

    //LED Träger Aufnahme
    translate ([0, 0, gesamtLaenge - 12 - 35]){ //= -glas - ledcarrier
      cylinder (h = gesamtLaenge, r = ledAufnahmeRadius + 0.25);
    }

    //Kernlochbohrungen für Trägeraufnahme M3
    translate ([0, 0, gesamtLaenge - 12 - 35 - 20]){
      bohrungenAufRadius (ledAufnahmeRadius - 1 - schraubenkopfM3, kernlochM3, 8 , 150);
    }

    //Raum elektronik
    translate ([0, 0, 3]){
      cylinder (h = gesamtLaenge, r = 24);
    }

    //Kabelverschraubung
    translate ([0,0,-.05]){
       cylinder (h = 6.1, r = kernlochKabelverschraubung);
    }

    // === Außen

    // Aufnahme für Goodman Befestigung
    translate ([0, 0, gesamtLaenge - 30]){
      ring (aussenRadius + 3.00,
            aussenRadius - 1.0,
            10.0);
    }

    // Kühlrippen
    for (i=[22:4:gesamtLaenge - 34]){
      translate ([0, 0, i]){
        ring (aussenRadius + 3.00,
              ledAufnahmeRadius + 3.0,
              2.5);
      }
    }
    translate ([0, 0, 10]){
      ring (aussenRadius + 3.00,
            ledAufnahmeRadius + 11, //rippenlänge = 8mm
            45);
    }

    translate ([0, 0, -1]){
      ring (aussenRadius + 3.0,  27, 20);
    }
  }

  //Leichte erhöhung für Gewinde der Kabelverschraubung
  difference(){

    cylinder (h = 6, r = kernlochKabelverschraubung *1.6);

    //Gewinde für M16 x 1,5 Verschraubung
    translate ([0,0,-.05]){
      cylinder (h = 6.1, r = kernlochKabelverschraubung);
    }

  }

}

// Zeichnet eine Reihe von Zylindern auf einem festgelegtem Radius.
// Gut geeignet um auf einem Ring Bohrungen anzubringen.
module bohrungenAufRadius (radius, lochRadius, anzahl, tiefe){
  for ( i = [0 : anzahl] ) {
      rotate( i * 360 / anzahl + 1, [0, 0, 1]){
        translate ([radius ,0 , -0.1]){
          cylinder (h = tiefe + 0.1, r = lochRadius);
        }
      }
    }
}

// Ein einfacher Ring
module ring(aussenRadius, innenRadius, hoehe) {
    difference() {
        cylinder(r = aussenRadius, h = hoehe);
        translate([ 0, 0, -1 ]) cylinder(r = innenRadius, h = hoehe+2);
    }
}

/*
 * UW Video Leuchte
 *
 * Breiter Fokus mit viel Lichtleistung
 * Möglichst modular aufgebaut.
 *
 */

//ACHTUNG: Das Glas hat eine Toleranz von ± 0.5mm, was eine Berechnung des
// O-Rings und der dazugehörigen Nut erst ermöglicht,
// wenn das Teil genau ausgemessen ist.

// Aktueller Raum für Elektronik: 48mm Durchmesser, 40mm Länge...

//Rendering information
$fn=200;

// Variablenblock:

// max. Gesamtradius des Gehäuses
gehaeuseRadius = 44;
auflageRadiusGlas = 30;
glasRadius = 35;
gesamtLaenge = 90;

//M3 DIN 912 Innensechskantschrauben
kernlochM3 = 1.25;
durchfuehrungM3 = 1.6;
schraubenkopfM3 = 2.85;
schraubenkopfM3hoehe = 3.0;

//Axiale O-Ringe fuer Scheibe (ID = 60mm, Dicke = 2.5mm)
oRingRadiusInnen = 30.0;
oRingRadiusAussen = oRingRadiusInnen + 2.5;
oRingNutbreite = 3.4;
oRingNutTiefe = 1.9; //(± 0,06)
oRingNutAussenRadius = gehaeuseRadius - 4 -durchfuehrungM3 - schraubenkopfM3 - kernlochM3;
oRingNutInnenRadius = oRingNutAussenRadius - oRingNutbreite ;

kernlochKabelverschraubung = 7;

echo ("O-Ring Nut Maße: ID= ", oRingNutInnenRadius * 2, " AD=", oRingNutAussenRadius *2);

//Konstruktion der Hauptkomponenten:

//Die Deckelscheibe hält das Glas in der Fassung.
translate ([0, 0, 230]){
  deckelscheibe(auflageRadiusGlas, gehaeuseRadius, 6);
}

//Glasscheibe mit Dichtringen
translate ([0, 0, 200]){
  frontGlas (oRingRadiusInnen, oRingRadiusAussen);
}

//Linse vor LED
translate ([0, 0, 160]){
  rotate ([0,0,25]){
    linse ();
  }
}

//LED
translate ([0,0,140]){
  rotate ([0,0,25]){
    led ();
  }
}

//LED Trägerplatte mit Kernlochbohrungen für LED Modul
difference (){
  //LED carrier
  translate ([0,0,130]){
    ledCarrier(30, 5);
  }

  // Bohrungen für LED
  rotate ([0,0,25]){
    for (i=  [[17,17,130-1],
             [-17,17,130-1],
             [17,-17,130-1],
             [-17,-17, 130-1]]){
      translate (i){
        cylinder (h = 11, r = 1.25);
      }
    }
  }
}

korpus (gehaeuseRadius, glasRadius, 30);

//Konstruktion der Deckelscheibe.
// Diese klemmt die Borsilika Scheibe mit zwei O-Ringen.
// Sie wird mit 8 Schrauben auf dem Gehäuse befestigt.
module deckelscheibe (innenRadius, aussenRadius, hoehe){
  difference(){
    cylinder (h = hoehe, r = aussenRadius);
    translate ([0, 0, -0.1]){
      cylinder (h = hoehe + 0.2, r = innenRadius);
    }

    // M3 Durchführung, 1mm vom Rand entfernt
    translate ([0, 0, -0.1]){
      bohrungenAufRadius (aussenRadius - 2 - schraubenkopfM3, durchfuehrungM3, 8 , hoehe + 0.2);
    }
    // Senkung für M3, DIN 912, Innensechskant Schraubenköpfe
    translate ([0, 0, hoehe-schraubenkopfM3hoehe]){
      bohrungenAufRadius (aussenRadius - 2 - schraubenkopfM3,
                          schraubenkopfM3, 8 ,
                          schraubenkopfM3hoehe + 0.1);
    }

    //Nut für O-Ring
    translate ([0, 0, -1.0]){
      // 2 mm vom Kernloch der Verschraubung entfernt:
      ring (oRingNutAussenRadius,
            oRingNutInnenRadius,
            oRingNutTiefe + 1.0);
    }
  }
}

//Konstruktion der Borsilikascheibe mit Dichtringen
module frontGlas (dichtringID, dichtringAD){

  // Borsilika Glas
  // ø: 70 (± 0.5) x 12 (± 0.5)

  color("LightBlue"){
    cylinder (h = 12, r = glasRadius);
  }

  //O-Ringe
  color("Black"){
    //axial oben (nicht dichtend)
    translate ([0,0, 20]){
      ring ( dichtringAD, dichtringID, dichtringAD-dichtringID);
    }

    //axial unten
    translate ([0,0,-10]){
      ring ( dichtringAD, dichtringID,  dichtringAD-dichtringID);
    }
  }
}

// Linse für LED
module linse (){
  color("LightSteelBlue"){
    difference (){
      sphere (22.0);
      translate ([-25,-25,-23.0]){
       cube ([50,50,23]);
      }
    }
    translate ([-0,-0, -8]){
      cylinder(r=25,h=8);
    }
  }
}

// Die LED die aufgeschraubt wird
module led(){

  color("SlateGray"){
    difference (){
      cylinder (h = 1.42, r = 28);
      translate ([-20,20,-1.0]){
        cube ( [40, 20, 2.43]);
      }
      translate ([-20,-40,-1]){
        cube ( [40, 20, 3.43]);
      }

      for (i=[[17,17,-1],
             [-17,17,-1],
             [17,-17,-1],
             [ -17,-17, -1]]){
        translate (i){
          cylinder (h = 2.430, r= 1.75);
        }
      }

    }
  }

  color("Gray"){
    difference (){
      translate ([-20, -20, 1.42]){
        cube ( [40, 40, 2.86]);
      }
      translate ([-20, -20, 1.41]){
        for (i=[[0,40,-1],
                [40,0,-1],
                [0,0,-1],
                [40  ,40 ,-1]]){
          translate (i){
            cylinder (h = 3.895, r= 10);
          }
        }
      }
    }
  }

  color ("Yellow"){
    translate ([-10.5, -10.5, 1.43]){
      cube ( [21, 21, 2.88]);
    }
  }

}

//Auf diesen Träger wird die LED geschraubt
module ledCarrier(aussenRadius, hoehe){

  difference (){

    cylinder (h = hoehe, r = aussenRadius);

     // M3 Durchführung, 1mm vom Rand entfernt
    translate ([0, 0, -0.1]){
      bohrungenAufRadius (aussenRadius - 1 - schraubenkopfM3, durchfuehrungM3 , 8 , hoehe + 0.2);
    }
    // Senkung für M3, DIN 912, Innensechskant Schraubenköpfe
    translate ([0, 0, hoehe-schraubenkopfM3hoehe]){
      bohrungenAufRadius (aussenRadius - 1 - schraubenkopfM3,
                          schraubenkopfM3, 8 ,
                          schraubenkopfM3hoehe + 0.1);
    }
  }
}

//Der Hauptlampenkörper
module korpus(aussenRadius, glasAufnahmeRadius, ledAufnahmeRadius){

  difference(){
    cylinder (h = gesamtLaenge, r = aussenRadius);

    // === Innen

    //Glasaufnahme
    translate ([0, 0, gesamtLaenge - 12]){
      cylinder (h = gesamtLaenge, r =  glasAufnahmeRadius + 0.25);
    }

    //Bohrungen für Glasplattenhalter
    translate ([0, 0, gesamtLaenge - 20]){
      bohrungenAufRadius (aussenRadius - 2 - schraubenkopfM3, kernlochM3, 8 ,gesamtLaenge );
    }

    //Nut für O-Ring
    translate ([0, 0, gesamtLaenge - 12 - oRingNutTiefe]){
      // 2 mm vom Kernloch der Verschraubung entfernt:
      ring (oRingNutAussenRadius,
            oRingNutInnenRadius,
            oRingNutTiefe + 1.0);
    }

    //LED Träger Aufnahme
    translate ([0, 0, gesamtLaenge - 12 - 35]){ //= -glas - ledcarrier
      cylinder (h = gesamtLaenge, r = ledAufnahmeRadius + 0.25);
    }

    //Kernlochbohrungen für Trägeraufnahme M3
    translate ([0, 0, gesamtLaenge - 12 - 35 - 20]){
      bohrungenAufRadius (ledAufnahmeRadius - 1 - schraubenkopfM3, kernlochM3, 8 , 150);
    }

    //Raum elektronik
    translate ([0, 0, 3]){
      cylinder (h = gesamtLaenge, r = 24);
    }

    //Kabelverschraubung
    translate ([0,0,-.05]){
       cylinder (h = 6.1, r = kernlochKabelverschraubung);
    }

    // === Außen

    // Aufnahme für Goodman Befestigung
    translate ([0, 0, gesamtLaenge - 30]){
      ring (aussenRadius + 3.00,
            aussenRadius - 1.0,
            10.0);
    }

    // Kühlrippen
    for (i=[25:4:gesamtLaenge - 34]){
      translate ([0, 0, i]){
        ring (aussenRadius + 3.00,
              ledAufnahmeRadius + 3.0,
              2.5);
      }
    }
    translate ([0, 0, 20]){
      ring (aussenRadius + 3.00,
            ledAufnahmeRadius + 11, //rippenlänge = 8mm
            35);
    }

    translate ([0, 0, -1]){
      ring (aussenRadius + 3.0,  27, 25);
    }
  }

  //Leichte erhöhung für Gewinde der Kabelverschraubung
  difference(){

    cylinder (h = 6, r = kernlochKabelverschraubung *1.6);

    //Gewinde für M16 x 1,5 Verschraubung
    translate ([0,0,-.05]){
      cylinder (h = 6.1, r = kernlochKabelverschraubung);
    }

  }

}

// Zeichnet eine Reihe von Zylindern auf einem festgelegtem Radius.
// Gut geeignet um auf einem Ring Bohrungen anzubringen.
module bohrungenAufRadius (radius, lochRadius, anzahl, tiefe){
  for ( i = [0 : anzahl] ) {
      rotate( i * 360 / anzahl + 1, [0, 0, 1]){
        translate ([radius ,0 , -0.1]){
          cylinder (h = tiefe + 0.1, r = lochRadius);
        }
      }
    }
}

// Ein einfacher Ring
module ring(aussenRadius, innenRadius, hoehe) {
    difference() {
        cylinder(r = aussenRadius, h = hoehe);
        translate([ 0, 0, -1 ]) cylinder(r = innenRadius, h = hoehe+2);
    }
}