diff --git a/openGrid/openGrid.scad b/openGrid/openGrid.scad index 6f22d38..3a16a84 100644 --- a/openGrid/openGrid.scad +++ b/openGrid/openGrid.scad @@ -50,8 +50,8 @@ Board_Width = 2; Board_Height = 2; /*[Chamfer and Connector Options]*/ -//Cosmetic Chamfers - If screw holes turned on, Chamfers will only be on the outside corners. -Chamfers = "Corners"; //[Everywhere, Corners, None] +//Cosmetic Chamfers - places with screw holes and connector holes will not generate chamfers +Chamfers = "Corners"; //[Everywhere, Border Only, Non-Border Only, Corners, None] Chamfer_Top_Left = true; Chamfer_Top_Right = true; Chamfer_Bottom_Left = true; @@ -64,7 +64,7 @@ Connector_Holes_Left = true; Connector_Holes_Top = true; /*[Screw Options]*/ -//Screw holes for mounting - +//Screw holes for mounting Screw_Mounting = "Corners"; //[Everywhere, Corners, By Row and Column, Custom, None] Screw_Every_X_Rows = 1; Screw_Every_X_Columns = 2; @@ -77,6 +77,13 @@ Screw_Head_Inset = 1; Screw_Head_Is_CounterSunk = true; Screw_Head_CounterSunk_Degree = 90; +// Optional backside screw head cut +Backside_Screw_Hole = false; +Backside_Screw_Head_Diameter_Shrink = 0.0; // amount subtracted from Screw_Head_Diameter on the back side +Backside_Screw_Head_Inset = Screw_Head_Inset; +Backside_Screw_Head_Is_CounterSunk = Screw_Head_Is_CounterSunk; +Backside_Screw_Head_CounterSunk_Degree = Screw_Head_CounterSunk_Degree; + /*[Adhesive Base Options]*/ //[Lite only] Adds a backing which allows you to adhere with double sided tape Add_Adhesive_Base = false; @@ -90,6 +97,7 @@ Tile_Thickness = 6.8; Lite_Tile_Thickness = 4; //0.1 Heavy_Tile_Thickness = 13.8; Heavy_Tile_Gap = 0.2; // Space between the two grid sides (prevents snaps from snagging) + /*[Tile Stacking]*/ //Stacking more than 6 tiles may time out. Desktop version recommended for larger stacks. Stack_Count = 1; @@ -112,6 +120,40 @@ adjustedStackCount = Add_Adhesive_Base ? 1 : Stack_Count; adjustedInterfaceThickness = Stacking_Method == "Interface Layer" ? Interface_Thickness : 0; +function og_node_pos(ix, iy, bw, bh, tileSize) = [ + -tileSize * bw / 2 + ix * tileSize, + tileSize * bh / 2 - iy * tileSize, + 0 +]; + +function og_point_in_list(p, pts) = + len([for (q = pts) if (q[0] == p[0] && q[1] == p[1]) 1]) > 0; + +function og_copy_coords(size, spacing, offset = 0) = + size < 0 ? [] : + let( + n = floor(size / spacing) + 1, + start = -((n - 1) * spacing) / 2 + offset + ) + [for (i = [0:n - 1]) start + i * spacing]; + +function og_chamfer_allowed_at(ix, iy, bw, bh) = + (ix == 0 && iy == 0 ) ? Chamfer_Top_Left : + (ix == bw && iy == 0 ) ? Chamfer_Top_Right : + (ix == 0 && iy == bh) ? Chamfer_Bottom_Left : + (ix == bw && iy == bh) ? Chamfer_Bottom_Right : + true; + +function og_connector_blocks_chamfer(ix, iy, bw, bh) = + (ix == bw && iy > 0 && iy < bh && Connector_Holes_Right) || + (ix == 0 && iy > 0 && iy < bh && Connector_Holes_Left) || + (iy == 0 && ix > 0 && ix < bw && Connector_Holes_Top) || + (iy == bh && ix > 0 && ix < bw && Connector_Holes_Bottom); + +function og_chamfer_allowed_by_options(ix, iy, bw, bh) = + og_chamfer_allowed_at(ix, iy, bw, bh) && + !og_connector_blocks_chamfer(ix, iy, bw, bh); + if (Fill_Space_Mode == "Complete Tiles Only") FillSpaceFullTiles(); if (Fill_Space_Mode == "Fill Available Space") @@ -119,9 +161,14 @@ if (Fill_Space_Mode == "Fill Available Space") //GENERATE SINGLE TILES if (Fill_Space_Mode == "None") { - if (Full_or_Lite == "Full" && adjustedStackCount == 1) openGrid(Board_Width=Board_Width, Board_Height=Board_Height, tileSize=Tile_Size, Tile_Thickness=Tile_Thickness, Screw_Mounting=Screw_Mounting, Chamfers=Chamfers, Add_Adhesive_Base=Add_Adhesive_Base, anchor=BOT, Connector_Holes=Connector_Holes); - if (Full_or_Lite == "Lite" && adjustedStackCount == 1) openGridLite(Board_Width=Board_Width, Board_Height=Board_Height, tileSize=Tile_Size, Screw_Mounting=Screw_Mounting, Chamfers=Chamfers, Add_Adhesive_Base=Add_Adhesive_Base, anchor=BOT, Connector_Holes=Connector_Holes); - if (Full_or_Lite == "Heavy" && adjustedStackCount == 1) openGridHeavy(Board_Width=Board_Width, Board_Height=Board_Height, tileSize=Tile_Size, Screw_Mounting=Screw_Mounting, Chamfers=Chamfers, anchor=BOT, Connector_Holes=Connector_Holes); + if (Full_or_Lite == "Full" && adjustedStackCount == 1) + openGrid(Board_Width=Board_Width, Board_Height=Board_Height, tileSize=Tile_Size, Tile_Thickness=Tile_Thickness, Screw_Mounting=Screw_Mounting, Chamfers=Chamfers, anchor=BOT, Connector_Holes=Connector_Holes); + + if (Full_or_Lite == "Lite" && adjustedStackCount == 1) + openGridLite(Board_Width=Board_Width, Board_Height=Board_Height, tileSize=Tile_Size, Screw_Mounting=Screw_Mounting, Chamfers=Chamfers, Add_Adhesive_Base=Add_Adhesive_Base, anchor=BOT, Connector_Holes=Connector_Holes); + + if (Full_or_Lite == "Heavy" && adjustedStackCount == 1) + openGridHeavy(Board_Width=Board_Width, Board_Height=Board_Height, tileSize=Tile_Size, Screw_Mounting=Screw_Mounting, Chamfers=Chamfers, anchor=BOT, Connector_Holes=Connector_Holes); //GENERATE STACKED TILES if (Full_or_Lite == "Full" && adjustedStackCount > 1) { @@ -185,6 +232,7 @@ module interfaceLayer2D(Board_Width, Board_Height, tileSize = 28, Tile_Thickness ); } } + module openGridLite(Board_Width, Board_Height, tileSize = 28, Screw_Mounting = "None", Chamfers = "None", Add_Adhesive_Base = false, anchor = CENTER, spin = 0, orient = UP, Connector_Holes = false) { // Screw_Mounting options: [Everywhere, Corners, None] // Bevel options: [Everywhere, Corners, None] @@ -220,7 +268,7 @@ module openGridLite(Board_Width, Board_Height, tileSize = 28, Screw_Mounting = " diff() { cube([tileSize * Board_Width, tileSize * Board_Height, Adhesive_Base_Thickness], anchor=BOT, orient=DOWN); down(Adhesive_Base_Thickness) - applyTileCornerModifications(Board_Width=Board_Width, Board_Height=Board_Height, Tile_Thickness=Adhesive_Base_Thickness, Screw_Mounting=Screw_Mounting, Chamfers=Chamfers, anchor=BOT); + applyTileCornerModifications(Board_Width=Board_Width, Board_Height=Board_Height, tileSize=tileSize, Tile_Thickness=Adhesive_Base_Thickness, Screw_Mounting=Screw_Mounting, Chamfers=Chamfers, anchor=BOT); } children(); @@ -295,7 +343,6 @@ module openGrid(Board_Width, Board_Height, tileSize = 28, Tile_Thickness = 6.8, wonderboardTileAp2(); } - //TODO: Modularize positioning (Outside Corners, inside corners, inside all) and holes (chamfer and screw holes) applyTileCornerModifications(Board_Width, Board_Height, tileSize, Tile_Thickness, Screw_Mounting, Chamfers, anchor); if (Connector_Holes) { @@ -309,13 +356,14 @@ module openGrid(Board_Width, Board_Height, tileSize = 28, Tile_Thickness = 6.8, zrot(180) ycopies(spacing=tileSize, l=Board_Height > 2 ? Board_Height * tileSize - tileSize * 2 : Board_Height * tileSize - tileSize - 1) connector_cutout_delete_tool(anchor=LEFT); - //xflip_copy(offset = -tileSize*Board_Width/2-0.005) + //top connector holes if (Connector_Holes_Left) right(-tileSize * Board_Width / 2 - 0.005) ycopies(spacing=tileSize, l=Board_Height > 2 ? Board_Height * tileSize - tileSize * 2 : Board_Height * tileSize - tileSize - 1) connector_cutout_delete_tool(anchor=LEFT); } + //right and left connector holes if (Board_Width > 1) tag("remove") @@ -326,7 +374,7 @@ module openGrid(Board_Width, Board_Height, tileSize = 28, Tile_Thickness = 6.8, xcopies(spacing=tileSize, l=Board_Width > 2 ? Board_Width * tileSize - tileSize * 2 : Board_Width * tileSize - tileSize - 1) zrot(-90) connector_cutout_delete_tool(anchor=LEFT); - //yflip_copy(offset = -tileSize*Board_Height/2-0.005) + //left connector holes if (Connector_Holes_Bottom) back(-tileSize * Board_Height / 2 - 0.005) @@ -369,11 +417,6 @@ module openGrid(Board_Width, Board_Height, tileSize = 28, Tile_Thickness = 6.8, right(connector_cutout_radius - connector_cutout_separation) ycopies(spacing=(connector_cutout_radius + connector_cutout_separation) * 2) circle(r=connector_cutout_dimple_radius); - //dimple (ass) to force seam. Only needed for positive connector piece (not delete tool) - //tag("remove") - //right(connector_cutout_radius*2 + 0.45 )//move dimple in or out - // yflip_copy(offset=(dimple_radius+connector_cutout_radius)/2)//both sides of the dimpme - // rect([1,dimple_radius+connector_cutout_radius], rounding=[0,-connector_cutout_radius,-dimple_radius,0], $fn=32); //rect with rounding of inner flare and outer smoothing } //outward flare fillet for easier insertion rect([1, connector_cutout_separation * 2 - (connector_cutout_dimple_radius - connector_cutout_separation)], rounding=[0, -.25, -.25, 0], $fn=32, corner_flip=true, anchor=LEFT); @@ -406,7 +449,6 @@ module openGrid(Board_Width, Board_Height, tileSize = 28, Tile_Thickness = 6.8, CorderSquareWidth = sqrt(Corner_Square_Thickness ^ 2 + Corner_Square_Thickness ^ 2) + Intersection_Distance; - full_tile_profile = Full_or_Lite == "Heavy" ? [ [0, 0], [Outside_Extrusion, 0], @@ -462,74 +504,170 @@ module applyTileCornerModifications(Board_Width, Board_Height, tileSize = 28, Ti Intersection_Distance = 4.2; tileChamfer = sqrt(Intersection_Distance ^ 2 * 2); - cornerChamfers = concat( - Chamfer_Bottom_Right ? [[tileSize * Board_Width / 2, -tileSize * Board_Height / 2, 0]] : [], - Chamfer_Top_Right ? [[tileSize * Board_Width / 2, tileSize * Board_Height / 2, 0]] : [], - Chamfer_Bottom_Left ? [[-tileSize * Board_Width / 2, -tileSize * Board_Height / 2, 0]] : [], - Chamfer_Top_Left ? [[-tileSize * Board_Width / 2, tileSize * Board_Height / 2, 0]] : [] + cornerChamferPositions = concat( + Chamfer_Bottom_Right ? [og_node_pos(Board_Width, Board_Height, Board_Width, Board_Height, tileSize)] : [], + Chamfer_Top_Right ? [og_node_pos(Board_Width, 0, Board_Width, Board_Height, tileSize)] : [], + Chamfer_Bottom_Left ? [og_node_pos(0, Board_Height, Board_Width, Board_Height, tileSize)] : [], + Chamfer_Top_Left ? [og_node_pos(0, 0, Board_Width, Board_Height, tileSize)] : [] ); - //TODO: Modularize positioning (Outside Corners, inside corners, inside all) and holes (chamfer and screw holes) - //Bevel Everywhere - if (Chamfers == "Everywhere" && Screw_Mounting != "Everywhere" && Screw_Mounting != "Corners") + step_cols = max(1, Screw_Every_X_Columns); + step_rows = max(1, Screw_Every_X_Rows); + + byrow_xs = og_copy_coords( + (Board_Width - 2) * tileSize, + tileSize * step_cols, + ((Board_Width - 2) % step_cols) % 2 == 0 ? 0 : -tileSize / 2 + ); + + byrow_ys = og_copy_coords( + (Board_Height - 2) * tileSize, + tileSize * step_rows, + ((Board_Height - 2) % step_rows) % 2 == 0 ? 0 : tileSize / 2 + ); + + screwPositions = + Screw_Mounting == "Corners" + ? ( + Board_Width > 1 && Board_Height > 1 + ? [ + og_node_pos(1, 1, Board_Width, Board_Height, tileSize), + og_node_pos(Board_Width - 1, 1, Board_Width, Board_Height, tileSize), + og_node_pos(1, Board_Height - 1, Board_Width, Board_Height, tileSize), + og_node_pos(Board_Width - 1, Board_Height - 1, Board_Width, Board_Height, tileSize) + ] + : [] + ) + : Screw_Mounting == "Everywhere" + ? [for (ix = [1:Board_Width - 1], iy = [1:Board_Height - 1]) og_node_pos(ix, iy, Board_Width, Board_Height, tileSize)] + : Screw_Mounting == "By Row and Column" + ? [for (x = byrow_xs, y = byrow_ys) [x, y, 0]] + : Screw_Mounting == "Custom" + ? ( + Board_Width > 1 && Board_Height > 1 + ? [ + for (i = [0:min(len(Screw_Custom_Positions), (Board_Width - 1) * (Board_Height - 1)) - 1]) + if (Screw_Custom_Positions[i] == "1") + og_node_pos( + 1 + (i % (Board_Width - 1)), + 1 + floor(i / (Board_Width - 1)), + Board_Width, + Board_Height, + tileSize + ) + ] + : [] + ) + : []; + + everywhereChamferPositions = + Chamfers == "Everywhere" + ? [ + for (ix = [0:Board_Width], iy = [0:Board_Height]) + let( + p = og_node_pos(ix, iy, Board_Width, Board_Height, tileSize), + chamfer_allowed = og_chamfer_allowed_by_options(ix, iy, Board_Width, Board_Height) + ) + if (chamfer_allowed && !og_point_in_list(p, screwPositions)) + p + ] + : []; + + borderChamferPositions = + Chamfers == "Border Only" + ? [ + for (ix = [0:Board_Width], iy = [0:Board_Height]) + let( + p = og_node_pos(ix, iy, Board_Width, Board_Height, tileSize), + is_border = ix == 0 || ix == Board_Width || iy == 0 || iy == Board_Height, + chamfer_allowed = og_chamfer_allowed_by_options(ix, iy, Board_Width, Board_Height) + ) + if (is_border && chamfer_allowed && !og_point_in_list(p, screwPositions)) + p + ] + : []; + + nonBorderChamferPositions = + Chamfers == "Non-Border Only" + ? [ + for (ix = [0:Board_Width], iy = [0:Board_Height]) + let( + p = og_node_pos(ix, iy, Board_Width, Board_Height, tileSize), + is_border = ix == 0 || ix == Board_Width || iy == 0 || iy == Board_Height, + chamfer_allowed = og_chamfer_allowed_by_options(ix, iy, Board_Width, Board_Height) + ) + if (!is_border && chamfer_allowed && !og_point_in_list(p, screwPositions)) + p + ] + : []; + + module screw_hole_cut(Tile_Thickness) { + front_head_d = Screw_Head_Diameter; + back_head_d = max(Screw_Diameter, Screw_Head_Diameter - Backside_Screw_Head_Diameter_Shrink); + + front_cs_h = + Screw_Head_Is_CounterSunk + ? tan((180 - Screw_Head_CounterSunk_Degree) / 2) * (front_head_d / 2 - Screw_Diameter / 2) - 0.01 + : 0.01; + + back_cs_h = + Backside_Screw_Head_Is_CounterSunk + ? tan((180 - Backside_Screw_Head_CounterSunk_Degree) / 2) * (back_head_d / 2 - Screw_Diameter / 2) - 0.01 + : 0.01; + + enable_backside_screw_hole = Backside_Screw_Hole && Full_or_Lite != "Heavy"; + + union() { + up(Tile_Thickness + 0.01) + cyl(d=front_head_d, h=Screw_Head_Inset > 0 ? Screw_Head_Inset : 0.01, anchor=TOP) + attach(BOT, TOP) + cyl(d2=front_head_d, d1=Screw_Diameter, h=front_cs_h) + attach(BOT, TOP) + cyl(d=Screw_Diameter, h=Tile_Thickness + 0.02); + + if (enable_backside_screw_hole) + down(0.01) + cyl(d=back_head_d, h=Backside_Screw_Head_Inset > 0 ? Backside_Screw_Head_Inset : 0.01, anchor=BOT) + attach(TOP, BOT) + cyl(d1=back_head_d, d2=Screw_Diameter, h=back_cs_h); + } + } + + if (Chamfers == "Corners") tag("remove") - grid_copies(spacing=tileSize, size=[Board_Width * tileSize, Board_Height * tileSize]) + move_copies(cornerChamferPositions) down(0.01) zrot(45) cuboid([tileChamfer, tileChamfer, Tile_Thickness + 0.02], anchor=BOT); - //Bevel Corners - if (Chamfers == "Corners" || (Chamfers == "Everywhere" && (Screw_Mounting == "Everywhere" || Screw_Mounting == "Corners"))) + + if (Chamfers == "Everywhere") tag("remove") - move_copies( - cornerChamfers - //[ - //[tileSize*Board_Width/2,tileSize*Board_Height/2,0], - //[-tileSize*Board_Width/2,tileSize*Board_Height/2,0], - //[tileSize*Board_Width/2,-tileSize*Board_Height/2,0], - //[-tileSize*Board_Width/2,-tileSize*Board_Height/2,0] - //] - ) + move_copies(everywhereChamferPositions) down(0.01) zrot(45) cuboid([tileChamfer, tileChamfer, Tile_Thickness + 0.02], anchor=BOT); - //Screw Mount Corners - if (Screw_Mounting == "Corners") + + if (Chamfers == "Border Only") tag("remove") - move_copies([[tileSize * Board_Width / 2 - tileSize, tileSize * Board_Height / 2 - tileSize, 0], [-tileSize * Board_Width / 2 + tileSize, tileSize * Board_Height / 2 - tileSize, 0], [tileSize * Board_Width / 2 - tileSize, -tileSize * Board_Height / 2 + tileSize, 0], [-tileSize * Board_Width / 2 + tileSize, -tileSize * Board_Height / 2 + tileSize, 0]]) - up(Tile_Thickness + 0.01) - cyl(d=Screw_Head_Diameter, h=Screw_Head_Inset > 0 ? Screw_Head_Inset : 0.01, anchor=TOP) - attach(BOT, TOP) cyl(d2=Screw_Head_Diameter, d1=Screw_Diameter, h=Screw_Head_Is_CounterSunk ? tan((180 - Screw_Head_CounterSunk_Degree) / 2) * (Screw_Head_Diameter / 2 - Screw_Diameter / 2) - 0.01 : 0.01) - attach(BOT, TOP) cyl(d=Screw_Diameter, h=Tile_Thickness + 0.02); - //Screw Mount Everywhere - if (Screw_Mounting == "Everywhere") + move_copies(borderChamferPositions) + down(0.01) + zrot(45) + cuboid([tileChamfer, tileChamfer, Tile_Thickness + 0.02], anchor=BOT); + + if (Chamfers == "Non-Border Only") tag("remove") - grid_copies(spacing=tileSize, size=[(Board_Width - 2) * tileSize, (Board_Height - 2) * tileSize]) up(Tile_Thickness + 0.01) - cyl(d=Screw_Head_Diameter, h=Screw_Head_Inset > 0 ? Screw_Head_Inset : 0.01, anchor=TOP) - attach(BOT, TOP) cyl(d2=Screw_Head_Diameter, d1=Screw_Diameter, h=Screw_Head_Is_CounterSunk ? tan((180 - Screw_Head_CounterSunk_Degree) / 2) * (Screw_Head_Diameter / 2 - Screw_Diameter / 2) - 0.01 : 0.01) - attach(BOT, TOP) cyl(d=Screw_Diameter, h=Tile_Thickness + 0.02); - if (Screw_Mounting == "By Row and Column") - translate([(Board_Width - 2) % max(1, Screw_Every_X_Columns) % 2 == 0 ? 0 : -tileSize / 2, (Board_Height - 2) % max(1, Screw_Every_X_Rows) % 2 == 0 ? 0 : tileSize / 2]) - tag("remove") grid_copies(spacing=[tileSize * max(1, Screw_Every_X_Columns), tileSize * max(1, Screw_Every_X_Rows)], size=[(Board_Width - 2) * tileSize, (Board_Height - 2) * tileSize]) - up(Tile_Thickness + 0.01) cyl(d=Screw_Head_Diameter, h=Screw_Head_Inset > 0 ? Screw_Head_Inset : 0.01, anchor=TOP) - attach(BOT, TOP) cyl(d2=Screw_Head_Diameter, d1=Screw_Diameter, h=Screw_Head_Is_CounterSunk ? tan((180 - Screw_Head_CounterSunk_Degree) / 2) * (Screw_Head_Diameter / 2 - Screw_Diameter / 2) - 0.01 : 0.01) - attach(BOT, TOP) cyl(d=Screw_Diameter, h=Tile_Thickness + 0.02); - if (Screw_Mounting == "Custom") { - start_point_x = -(Board_Width - 2) / 2 * tileSize; - start_point_y = (Board_Height - 2) / 2 * tileSize; - for (i = [0:min(len(Screw_Custom_Positions), (Board_Width - 1) * (Board_Height - 1)) - 1]) { - if (Screw_Custom_Positions[i] == "1") { - tag("remove") - move_copies([[start_point_x + tileSize * (i % (Board_Width - 1)), start_point_y - tileSize * floor(i / (Board_Width - 1)), 0]]) - up(Tile_Thickness + 0.01) cyl(d=Screw_Head_Diameter, h=Screw_Head_Inset > 0 ? Screw_Head_Inset : 0.01, anchor=TOP) - attach(BOT, TOP) cyl(d2=Screw_Head_Diameter, d1=Screw_Diameter, h=Screw_Head_Is_CounterSunk ? tan((180 - Screw_Head_CounterSunk_Degree) / 2) * (Screw_Head_Diameter / 2 - Screw_Diameter / 2) - 0.01 : 0.01) - attach(BOT, TOP) cyl(d=Screw_Diameter, h=Tile_Thickness + 0.02); - } - } - } + move_copies(nonBorderChamferPositions) + down(0.01) + zrot(45) + cuboid([tileChamfer, tileChamfer, Tile_Thickness + 0.02], anchor=BOT); - children(); -} + if (Screw_Mounting != "None") + tag("remove") + move_copies(screwPositions) + screw_hole_cut(Tile_Thickness); + children(); +} module FillSpaceFullTiles() { // === Derived tile space === @@ -708,4 +846,4 @@ module FillSpaceClipOneSide() { place_centered_and_clipped(x_base, y_base); } } -} +} \ No newline at end of file