
include <BOSL2/std.scad>;
include <BOSL2/beziers.scad>;
include <BOSL2/rounding.scad>;
include <BOSL2/skin.scad>;
include <BOSL2/threading.scad>;

include <DotScad/src/bend.scad>;
include <DotScad/src/bend_extrude.scad>;
//include <DotScad/src/bezier_curve>;
include <DotScad/src/ellipse_extrude.scad>;
include <DotScad/src/loft.scad>;

include <scad-utils/morphology.scad>;
include <common.scad>;

include <./lib/chinook.scad> // US Box
include <./lib/finbox_us.scad> // US Box


// https://github.com/BelfrySCAD/BOSL2/wiki/Tutorial-Beziers_for_Beginners
// ideas  offset_stroke 

/**
 * Kitesurf Fins mold
 *
 *
 *
 * Requirements : 
 * - The plunger normaly should be twice the height of the part 
 *
 */

/************************************************/
/*  Parameters                                  */
/************************************************/

/* [Fin Specs] */

// Height
fin_height       = 254;      // 10 inches in mm
// Top Width
fin_width        = 270;     // Width at the top in mm
// Base Width
fin_base         = 130;     // Width at the base in mm


// Define top point
fin_top_withdraw    = 35;      
fin_edge_withdraw   = 20;

fin_start_angle  = 70; // Angle [Point 0 ]
fin_sweep        = 25; // Sweep Angle [Point 1] 

fin_edge_angle  = 30;
fin_edge_strength  = 30;

fin_counter_angle = 30;     // counter angle [Point 2] 
fin_counter_strength = 10;  // Length [Point 2] 

// Length at the base in mm



fin_width_tip   = 5; // Width at the tip in mm
fin_thickness   = 9; // Thickness of the fin in mm

fin_end_angle   = 110;
fin_end_strength   = 30;

// Back
fin_back_height = 30; // Percent height
fin_back_widthdraw = 20; // Percent height
fin_back_angle = 20; // back angle
fin_back_strength=30;

/* [Base Specs] */

base_tickness=8; 
base_extra_thickness=20;


// ---------------------*/
/*        [Mold]        */
// ---------------------*/

// Mold base height
mold_base_height        = 10;
mold_top_height         = 5;
mold_extra_width        = 15;

mold_piston_height      = 10;

resin_escape_diameter   = 4; 

// Diameter for screw holes in mm
merge_holes_diameter = 6;
piston_depth = 5;

skirt_angle=80;

// ---------------------*/
/*        [US Box]        */
// ---------------------*/
/* [US Box] */
// Dimensions in mm
//length = 180;
//length=fin_base;

box_thickness = 9.6;
height = 25;
thick  = 9.2;
mirror_vec = [1,1,0];

//screw_pos = 0.0096*1000;
screw_pos = 9.6;
//screw_dia = 0.0045*1000;
screw_dia = 4.5;

//tab_height = 0.013*1000;
tab_height = 13;
tab_length = screw_pos * 2;
tab_round  = tab_height;

// real pin: d=0.0048 -> drill to fit
//pin_dia   = 0.003*1000;
pin_dia   = 3;
//pin_depth = 0.0164*1000;
pin_depth = 16.4;
//pin_back  = 0.009*1000;
pin_back  = 9;

// Thickness to cut for easier sliding (0 for no cut)
//thick_cut = 0.001*1000;
thick_cut = 1;

// Offset the box to fit the fin
box_offset = 80;


// ---------------------*/
/*     [Debugging]      */
// ---------------------*/

// Showing all layers
show_debug_layers = false;

show_curve_points = false;

// Draw master profile
draw_profile = false;

// ---------------------*/
/*        [Rendering]   */
// ---------------------*/

// Rendering parts
build_fin       = false;
build_box       = false;
build_mold      = true;
partition       = "none";    // [none, low, high]

parts           = "all";      // [all, top, bottom]


mold_part       = "all";   // [all, top, bottom]

version         ="0.9b";

// Scaling
scale_factor    = 1.0; // [0.1:0.1:2] 

// Printable rendering
printable       = true;

$fn=64;

/*************/
/* Constants */
/*************/
/* [Hidden] */ 
// Define the offset value as a constant
OFFSET = 0.01;
CLEARING = 0.1;
INCH=2.54;
X=0;
Y=1;
Z=2;

/************************************************/
/* Derived variable */

mold_width  = fin_height +2 * mold_extra_width;
mold_length = fin_width  +2 * mold_extra_width;
mold_height = mold_base_height;

drilling_length = mold_base_height+mold_piston_height+2*OFFSET;

start               = [ 0                ,0                              ];
top_point           = [ fin_width-fin_top_withdraw,fin_height            ];
edge_point          = [ fin_width        ,fin_height-fin_edge_withdraw   ];
counter_edge_point  = [ fin_width -70    ,fin_height-100                 ];
tail_point          = [ fin_width -95    ,fin_height-175                 ];
end_point           = [ fin_base         ,0                              ];



/******************/
/* Profile points */
/******************/
points = flatten([
    // *****************
    //   INITIAL POINT
    // *****************    
    bez_begin(start,70,80),
    // *****************
    //   TOP POINT
    // *****************    
    bez_tang(top_point,0,80,15),
    // *****************
    //   EDGE POINT
    // *****************    
    bez_tang(edge_point,-90,15),
    // **********************
    //   COUNTER EDGE POINT
    // **********************
    bez_tang(counter_edge_point,-120,30),
    // *****************
    //   TAIL Point
    // ***************** 
    bez_tang(tail_point,-120,40),
    // *****************
    //   END POINT                                                               
    // *****************   
    bez_end(end_point,75,40),    
]);

profile_curve_with_base  = translate_path( addBase(asCurve(points,32)),-fin_base/2,0 );
profile  = translate_path( asCurve(points,32),-fin_base/2,0 );

assert(is_path_simple(profile_curve_with_base));

// *****************
// *  Fin Drawing  *
// *****************
if ( build_fin ) 
    scale([scale_factor, scale_factor, scale_factor]) {
        union() {
            buildCompletFin();
            left(box_offset) usBox(fin_base+30);
        }
    }        
        
/**
 * Render Mold
 */
if ( build_mold )            
    difference(){
        buildMold();
        if (partition=="low")
             partitionMask(false);
        if (partition=="high")
             partitionMask(true);
             
    }                

/**
 * Build complete fin
 */
module buildCompletFin() {
    difference(){
        buildFin( fin_thickness );  // Fin 
        baseMask();                 // Remove extra base
    }
}

module baseMask() {
    fwd(OFFSET) cube([fin_base*1.1,base_extra_thickness*1.1,fin_thickness*1.1],anchor=BACK);
}


module usBoxMask() {
    left(box_offset) 
        color("Blue") 
            usBox(fin_base+30,thickness = box_thickness+2*OFFSET,drill=false);
}


/**
 * Build mold
 * 
 */
module buildMold() {
    // Bottom Mold
    if (mold_part != "top") right( printable ? 120 : 0 ) union() {
        // Fin mold
        difference() {
            support(part="bottom");
            usBoxMask();
            buildCompletFin();
        }
        // Box mold
        difference() {
            left(box_offset)  
                finBoxMold( fin_base+30,part="bottom",skirt=false );
            // Remove fin
            buildCompletFin(); 
        }
        // Mold skirt
        moldSkirt( part="bottom" );
    }
    // Top
    if (mold_part != "bottom") /*up(1* 150)*/ left( printable ? 120 : 0 ) yrot(printable ? 180 : 0 )   union() {
        difference(){
            support(part="top");
            usBoxMask();
            buildCompletFin();
            
        }    
        // Box mold
        difference() {
            left(box_offset)  
                finBoxMold( fin_base+30,part="top",skirt=false );
            // Remove fin
            buildCompletFin(); 
        }
        // Mold skirt
        difference() 
        {
            down( fin_thickness * 3 ) moldSkirt( part="top" ); // Inclinded Skirt
            back(0.65)
                cube([250,40,9.6],anchor=BACK); // Remove skirt on us box side
        }    
        
    }
     
}


module support( part ) {
    moldHeight = 10;
    moldContour =  expandPath(profile,mold_extra_width);
    if (part == "bottom") {
        down( moldHeight ) 
            linear_extrude (moldHeight) polygon(moldContour);  // bottom support
    } else if (part == "top") {
        //linear_extrude (moldHeight) polygon(moldContour);  // top support
        linear_extrude (moldHeight) polygon(profile);  // top support
    }
}


/**
 * Build the piston skirt 
 * 
 * @param part   -  Part could be 'top' or 'bottom'
 */
module moldSkirt( part ){
    assert( part , "'part' parameter should be defined in mold skirt");
    
    box_profile         = move([-80,0],profileUS (fin_base+30));
    finAndBoxProfile    = path_merge_collinear(union([box_profile,profile]));    
    moldHeight          = fin_thickness * 3;
    
    up (part=="top" ? moldHeight : 0 )
        skirt( finAndBoxProfile, side = part ,moldThickness = 3 , angle = skirt_angle, moldDeep = fin_thickness );
}

module moldJunctionMask(moldThickness = 3,height,angle){

    deviation = opp_ang_to_adj (height,angle);
    right(moldThickness)
    left(moldThickness/2)
    down( OFFSET ) back( moldThickness*1.2 - OFFSET )
        color("Red") 
            prismoid(
                size1=[fin_width/2-1.5*moldThickness, moldThickness*3+2*OFFSET], 
                size2=[fin_width/2+1*deviation+0*moldThickness, /*moldThickness/2*/moldThickness * 3+deviation], 
                h=height+OFFSET*3 
            );
}

module buildMoldSkirt( height,offset,thickness ) {
    outside = expandPath(profile,offset);
    inside  = expandPath(profile,offset-thickness);
    down(height) {
        // Skirt
        linear_extrude (height-thick/2) difference() {
            polygon(outside);
            polygon(inside);
        };
        difference() {
        //union() {
            intersection() {
                ycopies(30,10,sp=[-100,0,0]) 
                    cube([fin_width*1.3,thickness,height]);
                linear_extrude (height) polygon(outside);
            };
            up(height) buildCompletFin();    
        }
        
        
    }
}


// *****************
// * Bottom insert *
// *****************
module bottomInsert() {
    debug=true;
    layer_profile       = profile_curve_with_base; 
    skin( [ layer_profile,expandPath(layer_profile,3) ], z=[0,piston_depth], slices=0 )
       up(OFFSET) 
        buildFinSide(true)
        ;
}

// *****************
// * Top piston    *
// *****************
module topPiston() {
    debug=true;
    layer_profile       = profile_curve_with_base; 
    difference() {
        skin( [ layer_profile,expandPath(layer_profile,3) ], z=[0,piston_depth], slices=0 );
        down(OFFSET)
        buildFinSide(false);
    }
}

/**
 * Build fin
 *
 */ 
module buildFin( thickness ) {
    union() {
        if (parts != "bottom") buildFinSide( thickness );
        if (parts != "top")    buildFinSide( thickness , true );
    }
}

/**
 * Build fin side
 * 
 * @param flip - Define it it is top or bottom fin side
 */
module buildFinSide(thickness,flip=false) {

    //profile_curve_with_base1 = subdivide_path (profile_curve_with_base,500);
    
    //if (false)
    mirror([0,0,flip ? 1 : 0])
        color(flip ? "Gray" : "LightGray")
        ellipse_extrude( thickness / 2 /*,height=2*/,center=false,twist=-7) 
            //zrot(30)
            polygon( profile_curve_with_base);
} 

module resin_escape(length) { 
    color ("Blue") cylinder(h=length, r=resin_escape_diameter/2);
}


function addBase(path) = concat(path,[[fin_base,-base_extra_thickness],[0,-base_extra_thickness]]);


module partitionMask(inverse = false) {
    up(10)
    partition_mask(
                    w=fin_height/2*1.5, 
                    h=80,           // height
                    l=fin_width*3, 
                    cutpath="sinewave",
                    //spread=0.1,
                    inverse=inverse,
                    anchor=FRONT
    );
}





/**
 * Draws a profile based on Bezier path points with optional debug visualization.
 * 
 * @param points - Array of points defining the Bezier path.
 * @param debug - Boolean flag to enable/disable debug visualization. Defaults to true.
 * 
 * This module:
 * - Calculates the closest point on the Bezier path to a fixed point.
 * - Draws the Bezier path with debug information if debug is true.
 * - Optionally shows spheres at specific points for debugging (currently commented out).
 */
module drawProfile( points,debug = true ){


    debugPoint (start,      "Start",    "Red"   );
    debugPoint (top_point,  "Top",      "Blue"  );
    debugPoint (edge_point, "Edge",     "Brown" );
    debugPoint (counter_edge_point,     "Counter Edge", "Brown" );
    debugPoint (tail_point, "Tail",     "Yellow" );
    debugPoint (end_point,  "End",      "Yellow" );
    
    //pt  = [100,0];
    //pos = bezpath_closest_point(points, pt);
    //xy  = bezpath_points(points,pos[0],pos[1]);
    
    debug_bezier(points, N=3,width=1.2);
}

echo ("****************************");
echo ("** Build              **");
echo ("****************************");
echo (str("filename :","    surf-fins-10inch-",mold_part,"-",partition,"_",version,".stl","       "));

// Draw fin profile
if (draw_profile)  
    back(10) right(350) showDebugPath(profile_curve_with_base);

// Show curve points    
if (show_curve_points)
    color("Blue")move_copies(profile_curve_with_base) circle($fn=16);


    
    