From de7cc127dab0b56d0f26b5d39e143c441fea9dcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Dante=20Ursini?= Date: Sun, 19 Jan 2025 21:11:49 -0300 Subject: [PATCH] 10 inch fin modeling --- .gitignore | 3 + Readme.md | 4 +- Surf-Fins-10inch.json | 47 ++++ Surf-Fins-10inch.scad | 510 +++++++++++++++++++++++++----------------- SurfFins.scad | 17 +- common.scad | 40 +++- 6 files changed, 392 insertions(+), 229 deletions(-) create mode 100644 Surf-Fins-10inch.json diff --git a/.gitignore b/.gitignore index f717f0f..62d9a2c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ .DS_Store various + + +test.scad diff --git a/Readme.md b/Readme.md index 60c793e..1aface4 100644 --- a/Readme.md +++ b/Readme.md @@ -39,8 +39,10 @@ Tissus de fibre de verre : Différents grammages, comme le roving (tissé) pour - Fibre de carbone : Tissus de fibre de carbone : Pour des pièces plus légères et très résistantes. - Résine époxy : Choisissez selon les propriétés mécaniques et la température de travail. Il est préférable d'utiliser une résine époxy de bonne qualité pour des pièces finies robustes. -- Wax (Vonixx Carnauba Plus) R$ 30 +- Wax (Vonixx Carnauba Plus) R$30 ![](./res/products/vonixx_carnauba_plus.webp) +- Pinceau Tigre Simples R$3.20 + diff --git a/Surf-Fins-10inch.json b/Surf-Fins-10inch.json new file mode 100644 index 0000000..df4d03c --- /dev/null +++ b/Surf-Fins-10inch.json @@ -0,0 +1,47 @@ +{ + "fileFormatVersion": "1", + "parameterSets": { + "New set 1": { + "$fn": "64", + "base_extra_thickness": "20", + "base_tickness": "8", + "build_box": "true", + "build_fin": "true", + "draw_profile": "false", + "fin_back_angle": "20", + "fin_back_height": "30", + "fin_back_strength": "30", + "fin_back_widthdraw": "20", + "fin_base": "130", + "fin_counter_angle": "30", + "fin_counter_strength": "10", + "fin_edge_angle": "30", + "fin_edge_strength": "30", + "fin_end_angle": "110", + "fin_end_strength": "30", + "fin_height": "254", + "fin_start_angle": "70", + "fin_sweep": "25", + "fin_thickness": "9", + "fin_top_withdraw": "35", + "fin_width": "250", + "fin_width_tip": "5", + "merge_holes_diameter": "6", + "mold_base_height": "10", + "mold_extra_width": "15", + "mold_part": "all", + "mold_piston_height": "10", + "mold_top_height": "5", + "parts": "all", + "piston_depth": "5", + "printable": "false", + "render_drill_template": "false", + "render_mold": "false", + "resin_escape_diameter": "4", + "scale_factor": "1", + "show_curve_points": "false", + "show_debug_layers": "false", + "start": "[0, 0]" + } + } +} diff --git a/Surf-Fins-10inch.scad b/Surf-Fins-10inch.scad index 8e210ec..9d6394c 100644 --- a/Surf-Fins-10inch.scad +++ b/Surf-Fins-10inch.scad @@ -7,10 +7,18 @@ include ; include ; include ; +//include ; include ; include ; + +include ; include +include <./lib/chinook.scad> // US Box + + +// https://github.com/BelfrySCAD/BOSL2/wiki/Tutorial-Beziers_for_Beginners +// ideas offset_stroke /** * Kitesurf Fins mold @@ -38,7 +46,7 @@ fin_base = 130; // Width at the base in mm // Define top point fin_top_withdraw = 35; -fin_edge_withdraw = 15; +fin_edge_withdraw = 20; fin_start_angle = 70; // Angle [Point 0 ] fin_sweep = 25; // Sweep Angle [Point 1] @@ -54,7 +62,7 @@ fin_counter_strength = 10; // Length [Point 2] fin_width_tip = 5; // Width at the tip in mm -fin_thickness = 8; // Thickness of the fin in mm +fin_thickness = 10; // Thickness of the fin in mm fin_end_angle = 110; fin_end_strength = 30; @@ -68,38 +76,78 @@ fin_back_strength=30; /* [Base Specs] */ base_tickness=8; -base_extra_thickness=20; +base_extra_thickness=40; -/* [Debugging] */ -// Showing all layers -show_debug_layers = false; - -// Draw master profile -draw_profile = false; - -// Draw Fin -build_fin = false; - -/* [Mold] */ +// ---------------------*/ +/* [Mold] */ +// ---------------------*/ // Mold base height -mold_base_height = 10; -mold_top_height = 5; -mold_extra_width = 15; +mold_base_height = 10; +mold_top_height = 5; +mold_extra_width = 15; -mold_piston_height = 10; +mold_piston_height = 10; -resin_escape_diameter=4; +resin_escape_diameter = 4; // Diameter for screw holes in mm merge_holes_diameter = 6; piston_depth = 5; -/* [Rendering] */ +// ---------------------*/ +/* [US Box] */ +// ---------------------*/ +/* [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; + +show_curve_points = false; + +// Draw master profile +draw_profile = false; + +// ---------------------*/ +/* [Rendering] */ +// ---------------------*/ // Rendering parts -render_fin = false; +build_fin = false; +build_box = false; render_mold = true; render_drill_template = false; @@ -136,68 +184,134 @@ mold_height = mold_base_height; drilling_length = mold_base_height+mold_piston_height+2*OFFSET; -start = [0,0]; - -pt1 = [ - adj_ang_to_opp(fin_height,fin_sweep)+fin_base/2, - fin_height -]; // Top - -top_point = [ fin_width-fin_top_withdraw,fin_height ]; - -//pt1_x = adj_ang_to_opp(fin_height,fin_sweep)+fin_base/2; -//pt1_y = fin_height; +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 ]; -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 */ /******************/ - profile_points = [ +points = flatten([ // ***************** // INITIAL POINT // ***************** - start, // Point 0 - //[opp_ang_to_adj(fin_height/3,fin_start_angle),fin_height/3], // Handle 0 - handle(start,70,80), + bez_begin(start,70,80), // ***************** // TOP POINT // ***************** - top_point + [-fin_width/3,2], // Handle 1 (Start) - top_point, // Top Point - top_point + [+fin_base/7 ,0], // Handle Top (end) + bez_tang(top_point,0,80,15), // ***************** // EDGE POINT - // ***************** - edge_point + [0,15], - edge_point, - edge_point + [0,-15], // Point Edge Point - // ***************** + // ***************** + bez_tang(edge_point,-90,15), + // ********************** // COUNTER EDGE POINT - // ***************** - handle(counter_edge_point,60,30), - counter_edge_point, // Point Counter Edge - handle(counter_edge_point,240,30), + // ********************** + bez_tang(counter_edge_point,-120,30), // ***************** // TAIL Point + // ***************** + bez_tang(tail_point,-120,40), // ***************** - handle(tail_point,60 ,40), - tail_point, - handle(tail_point,60+180,40), - // ***************** - // END POINT - // ***************** - handle([fin_base,0],75,40), - [fin_base,0] // End point -]; + // END POINT + // ***************** + bez_end(end_point,75,40), +]); +profile_curve = translate_path( addBase(asCurve(points,32)),-fin_base/2,0 ); +//profile_curve = translate_path( asCurve(points,32),-fin_base/2,0 ); +//profile_curve = resample_path(translate_path( asCurve(points,32),-fin_base/2,0 ),64); + +assert(is_path_simple(profile_curve)); -profile_path = translate_path( asCurve(profile_points),-fin_width/2 ); +if (show_curve_points) + color("Blue")move_copies(profile_curve) circle($fn=16); + + +// Draw fin profile +if (draw_profile) { + //left(425) drawProfile( points, true ); + + + layer0 = expandPath(profile_curve,-2); + layer1 = expandPath(profile_curve,-8); + layer2 = expandPath(profile_curve,-15); + layer3 = expandPath(profile_curve,-30); + layer4 = expandPath(profile_curve,-40); + + back(10) showDebugPath(layer0); + + + left(500) { + color("Red") polygon(profile_curve); + up(10) polygon(layer0); + up(20) color("Yellow") polygon(layer1); + up(30) color("Brown") polygon(layer2); + } + + layers = [profile_curve,layer0,layer1,layer2,layer3,layer4]; + + + /* + echo ("profile_curve"); + print_path_points (profile_curve); + echo ("layer0"); + print_path_points (layer0); + */ + + + /* + left(200) { + // Turning a VNF into geometry + vnf = skin(layers,slices=0,z=layerHeights(len(layers),fin_thickness),method="fast_distance"); + + //vnf1 = up(50, p=vnf); + //bent1 = vnf_bend(vnf1, axis="X",r=10); + + vnf2 = vnf_hull(vnf); + vnf_polyhedron(vnf2); + + + // Expanded + + //expanded_vnf = vnf_small_offset(vnf,20); + //back(80) vnf_polyhedron(expanded_vnf); + + + back(380) vnf_polyhedron(vnf_sheet(vnf,-30)); + + + //vnf_polyhedron(vnf); + //vnf_wireframe(vnf); + } + */ + //left(365) polygon( profile_curve ); + + //test = round_corners(polygon(profile_curve), radius=20, closed=false, $fn=72); + + left (800) color ("Green") polygon(profile_curve); + + left (1000) color ("Green") shell(10) polygon(profile_curve); + + /* + convex_offset_extrude( + bottom = os_circle(r=5), + top=os_circle(r=5), + height=10, + offset="delta", + angle=70 + ) + */ + + + +} + /** @@ -213,31 +327,32 @@ profile_path = translate_path( asCurve(profile_points),-fin_width/2 ); */ 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=0.2); - //color("red") translate(pt) sphere(r=6); - //color("blue") translate(xy) sphere(r=6); + 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); } - - + // ***************** // * Fin Drawing * // ***************** -if ( render_fin ) { +if ( build_fin ) scale([scale_factor, scale_factor, scale_factor]) buildFin( fin_thickness ); -} +if ( build_box ) { + left(80) xflip() finfit(fin_base+20, [1,1,0]); +} + /** * Render Mold */ @@ -245,7 +360,6 @@ if ( render_mold ) buildMold(); if ( render_drill_template ) buildDrillTemplate(); - /** * Build mold * @@ -253,7 +367,6 @@ if ( render_drill_template ) buildDrillTemplate(); module buildMold() { // Bottom mold if (mold_part != "top") - difference() { case(mold_base_height,true); // Piston @@ -351,25 +464,13 @@ module buildDrillTemplate() { } - - -module rounded_triangle(radius, height) { - triangle_points = [[0, 0], [75, 0], [0, -35]]; // Equilateral triangle with side length 100 - rounded_shape = round_corners( triangle_points, r=radius ); - mirror([0,0,1]) linear_extrude(height = height) polygon(rounded_shape); -} - - - // ***************** // * Bottom insert * // ***************** module bottomInsert() { debug=true; - //layer_profile = pathProcess(profile_path); - layer_profile = profile_path; + layer_profile = profile_curve; skin( [ layer_profile,expandPath(layer_profile,3) ], z=[0,piston_depth], slices=0 ) - //up(debug ? 2 : 0) up(OFFSET) buildFinSide(true) ; @@ -380,7 +481,7 @@ module bottomInsert() { // ***************** module topPiston() { debug=true; - layer_profile = profile_path; + layer_profile = profile_curve; difference() { skin( [ layer_profile,expandPath(layer_profile,3) ], z=[0,piston_depth], slices=0 ); down(OFFSET) @@ -405,13 +506,18 @@ module buildFin( thickness ) { * @param flip - Define it it is top or bottom fin side */ module buildFinSide(thickness,flip=false) { + + //profile_curve1 = subdivide_path (profile_curve,500); + + //if (false) mirror([0,0,flip ? 1 : 0]) - color(flip ? "Gray" : "LightGray") /*up(fin_thickness/4)*/ /*down(fin_thickness) */ - ellipse_extrude( thickness / 2 ) - polygon(profile_path); + color(flip ? "Gray" : "LightGray") + ellipse_extrude( thickness / 2 /*,height=2*/,center=false,twist=-7) + //zrot(30) + polygon( profile_curve); } -module resin_escape(length) { +module resin_escape(length) { color ("Blue") cylinder(h=length, r=resin_escape_diameter/2); } @@ -430,127 +536,111 @@ module drilling(){ ); } +/* +echo ("bez_begin([-50,0],[0,-20])",bez_begin([-50,0],[0,-20])); +echo ("bez_begin([-50,0],[0,-20])", -module subtracted(anchor) { - color("Red") fwd(40) left(40) cube([fin_width+100,fin_height+100,20],anchor=anchor); -} + flatten([ + bez_begin([-50,0],[0,-20]), + bez_end([-50,0],[0,-20]) + ]) + +); +*/ +echo ("bez_tang([0,0],70,80)",bez_tang(counter_edge_point,60+180,30)); +echo ("point 1 ",points[8]); +echo ("point 2",points[9]); +echo ("point 3",points[10]); - -// Draw fin profile -if (draw_profile) drawProfile( profile_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=0.2); -} - - - - -/** - * 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]]); -/** - * Function to translate a path manually along X and Y - */ -function translate_path(path, dx=0, dy=0) = - [for (p = path) [p[0] + dx, p[1] + dy]]; + +// Function to create a parallel bezier curve offset by a given amount +function offset_bezier_points(points, offset_distance, normal=[0,0,1]) = + [for (pt = points) pt + offset_distance * normal]; + +// Function to create multiple parallel bezier curves +function create_patch_points(base_points, width, steps=4) = + [for (i = [0:1:steps]) + let( + t = i/steps, + offset = width * t + ) + offset_bezier_points(base_points, offset) + ]; + +// Create the bezier patch VNF +function make_bezier_patch_from_curve(base_points, width, length_steps=32, width_steps=8) = + let( + // Generate parallel curves to form patch + patch_points = create_patch_points(base_points, width, width_steps), + // Generate points for each curve + curves_points = [for (curve_pts = patch_points) + bezier_points(curve_pts, length_steps) + ], + // Create vertices for VNF + vertices = flatten(curves_points), + // Calculate indices for faces + indices = [ + for (i=[0:1:width_steps-1], j=[0:1:length_steps-1]) + let( + idx00 = i*(length_steps+1) + j, + idx10 = (i+1)*(length_steps+1) + j, + idx11 = (i+1)*(length_steps+1) + (j+1), + idx01 = i*(length_steps+1) + (j+1) + ) + each [[idx00, idx10, idx11, idx01]] + ] + ) + [vertices, indices]; +if (false) { + + //vnf = bezier_vnf(profile_curve, splinesteps=10); -// Function to print points of a path -module print_path_points(path) { - for (i = [0:len(path)-1]) { - echo(str("\t Point ", i, ": [", path[i][0], ", ", path[i][1], "]")); - } -} + /* + vnf = make_bezier_patch_from_curve(points, width=10); + back (100) { + cube([20,20,20]); + vnf_polyhedron(vnf); + } + */ + /* + polygon(profile_curve); + vnf2 =vnf_from_region(polygon(profile_curve)); + //vnf2 = vnf_from_polygons(profile_curve, fast=true); + vnf_polyhedron(vnf2); + */ -echo ("**********************"); -echo ("* Configuration *"); -echo ("**********************"); -echo ("Base thickness" ,str(base_tickness," mm")); - - - -function asCurve(points) = bezier_curve(points/*,N=3*/,splinesteps=128); - -function xAngleFactor(angle) = angle > 0 && angle < 180 ? 1 : -1; -function yAngleFactor(angle) = angle > -90 && angle < 90 ? 1 : -1; - -//function counterTop(x,withdraw,height) = [x-withdraw, height ]; -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 decrease_y(points, percentage) = [ for (p = points) [p[0], p[1] * (1 - percentage/100)]]; - -function pathProfile(points) = bezpath_curve(points,N=3); - -// Surf Fin profile -fin_profile = pathProfile( profile_points ); - -function expandPath(path,delta) = - offset( - deduplicate(path), - delta=delta, - chamfer=false, - same_length=true - ) - ; - - -function inches_to_cm(inches) = inches * 2.54; - - -echo ("pt1",pt1); -echo ("pt1[X]",pt1[X]); -echo ("pt1[Y]",pt1[Y]); -echo ("X",X); -echo ("Y",Y); - - - - - - - - - - - - + //curve_points = bezier_curve(points,N=3, splinesteps=32); + curve_points = bezpath_curve(points,N=3, splinesteps=32); + curve_points2 = [ for(p = curve_points) [p.x, p.y, 0]]; // Offset by 1 in Z + //echo ("curve_points",curve_points); + curve_points_offset = [ for(p = curve_points) [p.x, p.y, (p.z != undef ? p.z : 0) + 1 ]]; // Offset by 1 in Z +// Combine original and offset points to form a 2D array for a simple patch + patch_points = [ + curve_points, + curve_points_offset + ]; + //echo ("patch_points",patch_points); + //echo ("curve_points_offset",curve_points_offset); + echo ("curve_points2",curve_points2); + echo ("length curve_points2",len(curve_points2)); + + //vnf_5 = bezier_vnf(patch_points, caps=false, splinesteps=10); + //vnf_5 = bezier_vnf(curve_points2, splinesteps=10); + echo ("is_bezier_patch",is_bezier_patch(curve_points)); + echo ("is_bezier_patch",is_bezier_patch(curve_points2)); + + //vnf_5 = bezier_vnf(curve_points_offset, splinesteps=10); + +} + + \ No newline at end of file diff --git a/SurfFins.scad b/SurfFins.scad index bd6afb9..ac91805 100644 --- a/SurfFins.scad +++ b/SurfFins.scad @@ -5,6 +5,8 @@ include ; include ; include <./lib/chinook.scad> +include + /************************************************/ /* Parameters */ @@ -205,7 +207,7 @@ module build() { color("Red") cube([fin_base+20,base_extra_thickness+20,base_tickness+20],anchor=LEFT+BACK); } // **************** - // * Box Drawing * + // * Box Drawing * // **************** if (draw_box) { difference() { @@ -308,15 +310,4 @@ module drawProfile( points,debug = true ){ function addBase(path) = concat(path,[[fin_base,-base_extra_thickness],[0,-base_extra_thickness]]); -/** - * Calculates heights for n layers where the first layer starts at 0 and the last at fin_thickness. - * - * @param n - Number of layers. - * @param fin_thickness - The total height to be divided. - * @return An array where each element represents the height of the top of each layer. - */ -function layerHeights(n, fin_thickness) = - let( - layer_height = fin_thickness / (n - 1) - ) - [ for (i = [0 : n-1]) i * layer_height ]; + diff --git a/common.scad b/common.scad index 18c630f..d31c510 100644 --- a/common.scad +++ b/common.scad @@ -30,19 +30,33 @@ function handle(point,angle,strength) = [ /** * Show Debug point */ -module debugPoint(point, id, color) { +module debugPoint(point, id, color,textSize=20,r=1.5) { translate(point) { - color(color) sphere(r = 1.5); + color(color) sphere(r = r); - translate([3, 3, 0.6]) { + //translate([3, 3, 0.6]) { + translate([textSize/7, textSize/7, textSize/10]) { color("white") scale([0.2, 0.2, 1]) linear_extrude(height = 0.1) - text(str(id), size = 20, halign = "center", valign = "center"); + text(str(id), size = textSize, halign = "center", valign = "center"); } } } +/** + * Show vnf points + */ +module vnfPoints(points,textSize=1,r=0.1) { + for(i = [0:1:len(points)-1]) + for(j = [0:1:len(points[i])-1]) { + pos = points[i][j]; + label = str(i,",",j); + debugPoint(pos, label, "Red", textSize=textSize, r=r); + } +} + + /** * Displays debug information for a path by visualizing its self-crossings. * @@ -84,8 +98,10 @@ function translate_path(path, dx=0, dy=0) = [for (p = path) [p[0] + dx, p[1] + d * Convert points to bezier curve * * @param points - Points + * + * @return a path */ -function asCurve( points ) = bezpath_curve(points,N=3,splinesteps=64); +function asCurve( points,steps=128 ) = bezpath_curve(points,N=3,splinesteps=steps); @@ -123,3 +139,17 @@ function expandPath(path,delta) = */ function inches_to_cm(inches) = inches * 2.54; + + +/** + * Calculates heights for n layers where the first layer starts at 0 and the last at fin_thickness. + * + * @param n - Number of layers. + * @param fin_thickness - The total height to be divided. + * @return An array where each element represents the height of the top of each layer. + */ +function layerHeights(n, fin_thickness) = + let( + layer_height = fin_thickness / (n - 1) + ) + [ for (i = [0 : n-1]) i * layer_height ];