314 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			OpenSCAD
		
	
	
	
	
	
			
		
		
	
	
			314 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			OpenSCAD
		
	
	
	
	
	
| 
 | |
| include <BOSL2/std.scad>;
 | |
| include <BOSL2/beziers.scad>;
 | |
| include <BOSL2/rounding.scad>;
 | |
| include <BOSL2/skin.scad>;
 | |
| include <./lib/chinook.scad>
 | |
| 
 | |
| include <common.scad>
 | |
| 
 | |
| 
 | |
| /************************************************/
 | |
| /*  Parameters                                  */
 | |
| /************************************************/
 | |
| 
 | |
| /* [Fin Specs] */
 | |
| 
 | |
| fin_height       = 250; // 10 inches in mm
 | |
| fin_width        = 240; // Width at the base in mm
 | |
| fin_top_withdraw = 30;
 | |
| fin_back_withraw = 20;
 | |
| 
 | |
| fin_start_angle  = 70; // Angle [Point 0 ]
 | |
| fin_sweep        = 25; // Sweep Angle [Point 1] 
 | |
| 
 | |
| fin_counter_angle = 30;     // counter angle [Point 2] 
 | |
| fin_counter_strength = 20;  // Length [Point 2] 
 | |
| 
 | |
| // Length at the base in mm
 | |
| fin_base  = 200; 
 | |
| 
 | |
| 
 | |
| fin_width_tip   = 5; // Width at the tip in mm
 | |
| fin_thickness   = 5; // 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=9.2; 
 | |
| base_extra_thickness=80;
 | |
| 
 | |
| 
 | |
| /* [US Box] */
 | |
| // Dimensions in mm
 | |
| //length = 180;
 | |
| //length=fin_base;
 | |
| 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;
 | |
| 
 | |
| 
 | |
| 
 | |
| /* [Debugging] */
 | |
| 
 | |
| //Showing all layers
 | |
| show_debug_layers = false;
 | |
| 
 | |
| // Draw master profile
 | |
| draw_profile = false;
 | |
| 
 | |
| draw_fin = true;
 | |
| draw_box = true;
 | |
| 
 | |
| /* [Rendering] */
 | |
| 
 | |
| // Rendering parts
 | |
| parts="all"; // [all, top, bottom]
 | |
| 
 | |
| // Scaling
 | |
| scale_factor = 1.0; // [0.1:0.1:2] 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| $fn=32;
 | |
| /************************************************/
 | |
| 
 | |
| 
 | |
| pt1_x = adj_ang_to_opp(fin_height,fin_sweep)+fin_base/2;
 | |
| pt1_y = fin_height;
 | |
| 
 | |
| 
 | |
| control_points = [
 | |
|     // *****************
 | |
|     //   INITIAL POINT
 | |
|     // *****************    
 | |
|     [0,0],                                                          // Point 0 
 | |
|     [opp_ang_to_adj(fin_height/3,fin_start_angle),fin_height/3],    // Handle 0
 | |
|     // *****************
 | |
|     //   TOP POINT
 | |
|     // *****************    
 | |
|     [pt1_x-100,pt1_y],                                              // Handle 1 (Start)    
 | |
|     [pt1_x,pt1_y],                                                  // Point 1
 | |
|     [pt1_x+30,pt1_y],                                               // Handle 1 (End)
 | |
|     // *****************
 | |
|     //   COUNTER POINT
 | |
|     // *****************
 | |
|     handleStart(fin_width,fin_top_withdraw,fin_counter_angle,fin_counter_strength),
 | |
|     counterTop(fin_width,fin_top_withdraw), // [250,220],   // Point 2
 | |
|     handleEnd(fin_width,fin_top_withdraw,fin_counter_angle,fin_counter_strength),
 | |
|     // *****************
 | |
|     //   BACK POINT
 | |
|     // *****************
 | |
|     handle(backPoint(),fin_back_angle,fin_back_strength),
 | |
|     backPoint(),
 | |
|     handle(backPoint(),-fin_back_angle,fin_back_strength),
 | |
|     // *****************
 | |
|     //   END POINT
 | |
|     // *****************
 | |
|     endHandle(fin_end_angle),                                       // End Handle
 | |
|     [fin_base,0]                                                    // End point
 | |
| ];
 | |
| 
 | |
| 
 | |
| function counterTop(x,withdraw)                 = [x, x-withdraw ];
 | |
| function handleStart(x,withdraw,angle,strength) = [x+adj_ang_to_opp(strength,angle), x-withdraw +strength];
 | |
| function handleEnd(x,withdraw,angle,strength)   = [x-adj_ang_to_opp(strength,angle), x-withdraw -strength];
 | |
| function endHandle(angle)= 
 | |
|     angle < 90 ? 
 | |
|         [fin_base + adj_ang_to_opp(fin_end_strength,90-angle),fin_end_strength] 
 | |
|         : 
 | |
|         [fin_base - adj_ang_to_opp(fin_end_strength,angle-90),fin_end_strength] 
 | |
|         ;
 | |
| function backPoint() = [fin_base-(fin_back_widthdraw/100*fin_base),fin_back_height/100*fin_height];
 | |
| function handle(point,angle,strength) = [point[0] + adj_ang_to_opp(strength,angle),point[1] + (angle>0 ? strength :-strength)]; 
 | |
| function decrease_y(points, percentage) = [ for (p = points) [p[0], p[1] * (1 - percentage/100)]];
 | |
| 
 | |
| function pathProfile(points) = bezpath_curve(points,N=3);
 | |
| 
 | |
| function remove_last(arr) = select(arr, 0, len(arr) - 2);
 | |
| 
 | |
| 
 | |
| // Surf Fin profile
 | |
| fin_profile = pathProfile( control_points );
 | |
| 
 | |
| // ****************
 | |
| // *  Slicing     *
 | |
| // ****************
 | |
| 
 | |
| layer_0 = addBase(fin_profile);                     // Master layer with base
 | |
| layer_1 = offset(layer_0,delta=-10,chamfer=true);
 | |
| layer_2 = offset(layer_1,delta=-15,chamfer=false);
 | |
| layer_3 = offset(layer_2,delta=-10,chamfer=false);
 | |
| layer_4 = offset(layer_3,delta=-10,chamfer=false);
 | |
| 
 | |
| 
 | |
| echo ("layer_2",layer_2);
 | |
| echo ("layer_3",layer_3);
 | |
| 
 | |
| 
 | |
| layers = [layer_0,layer_1,layer_2,layer_3];
 | |
| echo ("**********************");
 | |
| echo ("*  Configuration     *");
 | |
| echo ("**********************");
 | |
| echo ("Layers count"        ,len(layers));
 | |
| echo ("Base thickness"      ,str(base_tickness," mm"));
 | |
| echo ("Layers heights"      ,layerHeights(len(layers),base_tickness/2));
 | |
| 
 | |
| 
 | |
| 
 | |
| // ****************
 | |
| // *  Fin Drawing     *
 | |
| // ****************
 | |
| if (draw_fin) {
 | |
| 
 | |
|     scale([scale_factor, scale_factor, scale_factor]) build();
 | |
| }
 | |
| 
 | |
| module build() {
 | |
|     union(){
 | |
|         //cube([50,50,90]);
 | |
|         difference(){
 | |
|             union() {
 | |
|                 if (parts !=  "bottom") buildFinSide([layer_0,layer_1,layer_2],base_tickness/2);
 | |
|                 if (parts !=  "top") zflip() buildFinSide([layer_0,layer_1,layer_2],base_tickness/2);    
 | |
|             }
 | |
|             color("Red") cube([fin_base+20,base_extra_thickness+20,base_tickness+20],anchor=LEFT+BACK);
 | |
|         }
 | |
|         // ****************
 | |
|         // *  Box Drawing *
 | |
|         // ****************
 | |
|         if (draw_box) {
 | |
|             difference() {
 | |
|                 xflip() finfit(fin_base, mirror_vec);
 | |
|                 if (parts == "top")
 | |
|                     subtracted(TOP+LEFT+FRONT);
 | |
|                 if (parts == "bottom")
 | |
|                     subtracted(BOTTOM+LEFT+FRONT);                
 | |
|             }
 | |
|         }
 | |
|     };
 | |
| };
 | |
| 
 | |
| 
 | |
| module subtracted(anchor) {
 | |
|     color("Red") fwd(40) left(40) cube([fin_width+100,fin_height+100,20],anchor=anchor);
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * Build fin side
 | |
|  * 
 | |
|  * @param layers - Layers path as array
 | |
|  * @param thickness - thickness of half fin
 | |
|  */
 | |
| module buildFinSide(layers,thickness,flip=false) {
 | |
|     color("Grey") 
 | |
|         skin(layers,slices=0,z=layerHeights(len(layers),thickness));
 | |
| } 
 | |
| 
 | |
| echo ("layerHeights(3,thickness):",layerHeights(3,base_tickness/2));
 | |
| 
 | |
| if (show_debug_layers) {
 | |
|     left(300) {
 | |
|         showDebugPath(layer_0);
 | |
|         showDebugPath(layer_1);
 | |
|         showDebugPath(layer_2);
 | |
|         
 | |
|         assert(is_path(layer_3),"Layer 3 is not a path");
 | |
|         showDebugPath(layer_3);
 | |
|         showDebugPath(layer_4);        
 | |
|     }
 | |
|     //showDebugPath(layer_3);
 | |
|     
 | |
| }
 | |
| if (draw_profile) {
 | |
|     // Draw fin profile
 | |
|     drawProfile( control_points, true  );
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * Displays debug information for a path by visualizing its self-crossings.
 | |
|  * 
 | |
|  * @param path - The input path to analyze for self-crossings.
 | |
|  * 
 | |
|  * This module:
 | |
|  * - Splits the path at points where it intersects itself.
 | |
|  * - Renders each segment of the split path with different colors for easy identification.
 | |
|  */
 | |
| module showDebugPath(path) {
 | |
|     assert(is_path(path),"Path to show is not a path");
 | |
|     assert(is_path_simple(path),"Path is not simple");
 | |
|     rainbow(split_path_at_self_crossings(path)) 
 | |
|         stroke($item, closed=false, width=1);
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * 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 ){
 | |
|     pt = [100,0];
 | |
|     pos = bezpath_closest_point(points, pt);
 | |
|     xy = bezpath_points(points,pos[0],pos[1]);
 | |
|     debug_bezier(points, N=3);
 | |
|     //color("red") translate(pt) sphere(r=6);
 | |
|     //color("blue") translate(xy) sphere(r=6);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Adds a base to the given path by extending it with additional points.
 | |
|  * 
 | |
|  * @param path - The original path to which the base will be added.
 | |
|  * @return A new path with an added base.
 | |
|  * 
 | |
|  * This function concatenates:
 | |
|  * - The original path (`pathProfile`).
 | |
|  * - A point `fin_base` which might represent the start or end of the base.
 | |
|  * - Points for additional thickness (`-base_extra_thickness`) at both ends of the base.
 | |
|  */
 | |
| function addBase(path) = concat(path,[[fin_base,-base_extra_thickness],[0,-base_extra_thickness]]);
 | |
| 
 | |
| 
 | |
| 
 |