commit
6c01a1200c
55 changed files with 2988 additions and 0 deletions
-
3.gitmodules
-
55config.scad
-
25constant.scad
-
1extern/KeyV2
-
20keycap_gen.scad
-
80keycap_stl.scad
-
682keycaps.scad
-
135kinessis.scad
-
86main.scad
-
11pcb/amoeba-royale.scad
-
6pcb/rp2040.scad
-
BINstl/keycaps/mt3/mt3_r1_u1.stl
-
BINstl/keycaps/mt3/mt3_r2_u1.stl
-
BINstl/keycaps/mt3/mt3_r3_u1.stl
-
BINstl/keycaps/mt3/mt3_r4_u1.stl
-
BINstl/keycaps/mt3/mt3_r5_u1.stl
-
BINstl/keycaps/sa/sa_r1_u1.stl
-
BINstl/keycaps/sa/sa_r2_u1.stl
-
BINstl/keycaps/sa/sa_r3_u1.stl
-
BINstl/keycaps/sa/sa_r4_u1.stl
-
BINstl/keycaps/sa/sa_r5_u1.stl
-
BINstl/pcb/rp2040.stl
-
160switch.scad
-
92switch_cutout.scad
-
121switch_holder.scad
-
220switch_holder_plate.scad
-
6util/__angy_angz.scad
-
4util/__frags.scad
-
1util/__to3d.scad
-
3util/_face_normal.scad
-
31util/bezier/_bezier_curve_impl.scad
-
15util/bezier/bezier_curve.scad
-
29util/bezier/bezier_surface.scad
-
13util/flat.scad
-
41util/hull_polyline3d.scad
-
62util/matrix/_m_rotation_impl.scad
-
16util/matrix/_m_scaling_impl.scad
-
15util/matrix/_m_translation_impl.scad
-
13util/matrix/m_rotation.scad
-
13util/matrix/m_scaling.scad
-
13util/matrix/m_translation.scad
-
22util/matrix/m_transpose.scad
-
177util/path_extrude.scad
-
11util/reverse.scad
-
22util/shape_circle.scad
-
20util/sum.scad
-
234util/surf/function_grapher.scad
-
157util/surf/sf_solidify.scad
-
26util/surf/sf_spline.scad
-
86util/surf/sf_thicken.scad
-
209util/sweep.scad
-
3util/vector/face_normal.scad
-
6util/vector/to_ang_vec.scad
-
1util/vector/unit_vector.scad
-
42util/vector/v_rot.scad
@ -0,0 +1,3 @@ |
|||
[submodule "KeyV2"] |
|||
path = extern/KeyV2 |
|||
url = https://github.com/rsheldiii/KeyV2.git |
@ -0,0 +1,55 @@ |
|||
switch_t=[ |
|||
// colum 3 |
|||
[ [0,70,50], [37,60,20] ], |
|||
// colum 2 |
|||
[ [0,40,40], [32,40,10] ], |
|||
// colum 2 |
|||
[ [0,20,25], [28.5, 20, 5] ], |
|||
// colum 1 |
|||
[ [0, 0,15], [25,0,0] ] |
|||
]; |
|||
|
|||
switch_r=[ |
|||
// row 3 |
|||
[ [0,0,0], [0,0,-10] ], |
|||
// row 2 |
|||
[ [0,0,0], [20,0,-10] ], |
|||
// row 2 |
|||
[ [0,0,0], [10,0,-10] ], |
|||
// row 1 |
|||
[ [0,0,0], [0,0,-10] ] |
|||
]; |
|||
switch_keycap=[ |
|||
[["mt3", 1], ["SA", 1], ], |
|||
[["mt3", 1], ["SA", 1], ], |
|||
[["mt3", 1], ["SA", 1], ], |
|||
[["mt3", 1], ["SA", 1], ], |
|||
]; |
|||
|
|||
$switch_with_pcb=true; |
|||
$show_switches=true; |
|||
$show_keycaps=true; |
|||
$show_switch_pcb=true; |
|||
|
|||
// |
|||
// cutout types |
|||
// |
|||
// C H |
|||
// _________ _________ |
|||
// | | _| |_ |
|||
// | | |_ _| |
|||
// | | _| |_ |
|||
// | | |_ _| |
|||
// |_________| |_________| |
|||
// |
|||
// V _ _ B _ _ |
|||
// _| |_| |_ _| |_| |_ |
|||
// | | _| |_ |
|||
// | | |_ _| |
|||
// | | _| |_ |
|||
// | | |_ _| |
|||
// |_ _ _| |_ _ _| |
|||
// |_| |_| |_| |_| |
|||
// |
|||
switch_cutout_type="V"; |
|||
switch_pcb_with_screws=true; |
@ -0,0 +1,25 @@ |
|||
$c=0.1; |
|||
$fn=60; |
|||
$e=0.01; |
|||
|
|||
switch_cutout_width = 14; |
|||
switch_dim_x=13.98; |
|||
switch_dim_y=13.98; |
|||
|
|||
switch_travel_max = 4; |
|||
|
|||
switch_clamp_height =1.1; |
|||
switch_extra_keycap_width = 2.5; |
|||
switch_holder_width = switch_cutout_width + switch_extra_keycap_width*2; |
|||
switch_holder_height = 4.8; |
|||
|
|||
switch_cutout_side_offset = 1; |
|||
switch_cutout_side_width = 3.5; |
|||
switch_cutout_side_dept = 1.2; |
|||
|
|||
|
|||
amoeba_royale_pcb_h = 1.6; |
|||
m2_head_d=4; |
|||
m2_head_h=0.75; |
|||
m2_screw_d=2; |
|||
amoeba_royale_pcb_screw_h=5; |
@ -0,0 +1,20 @@ |
|||
// the point of this file is to be a sort of DSL for constructing keycaps. |
|||
// when you create a method chain you are just changing the parameters |
|||
// key.scad uses, it doesn't generate anything itself until the end. This |
|||
// lets it remain easy to use key.scad like before (except without key profiles) |
|||
// without having to rely on this file. Unfortunately that means setting tons of |
|||
// special variables, but that's a limitation of SCAD we have to work around |
|||
|
|||
include <extern/KeyV2/includes.scad> |
|||
|
|||
|
|||
// example key |
|||
|
|||
flat_support() no_stem_support() rounded_cherry() sa_row(3) key(); |
|||
// example row |
|||
/* for (x = [0:1:4]) { |
|||
translate_u(0,-x) dcs_row(x) key(); |
|||
} */ |
|||
|
|||
// example layout |
|||
/* preonic_default("dcs") key(); */ |
@ -0,0 +1,80 @@ |
|||
module keycap(type="sa",row=1) |
|||
{ |
|||
if(type == "sa" || type == "SA" ) { |
|||
keycap_sa(row); |
|||
} |
|||
if(type == "mt3" || type == "MT3") { |
|||
keycap_mt3(row); |
|||
} |
|||
} |
|||
|
|||
module keycap_sa(row=1) |
|||
{ |
|||
// sa is taken from https://github.com/getclacking/SA-profile-keys-3D-models |
|||
$fn=400; |
|||
if(row == 1) { |
|||
import("stl/keycaps/sa/sa_r1_u1.stl"); |
|||
} |
|||
if(row == 2) { |
|||
import("stl/keycaps/sa/sa_r2_u1.stl"); |
|||
} |
|||
if(row == 3) { |
|||
import("stl/keycaps/sa/sa_r3_u1.stl"); |
|||
} |
|||
if(row == 4) { |
|||
import("stl/keycaps/sa/sa_r4_u1.stl"); |
|||
} |
|||
if(row == 5) { |
|||
import("stl/keycaps/sa/sa_r5_u1.stl"); |
|||
} |
|||
} |
|||
module keycap_mt3(row=1) |
|||
{ |
|||
// sa is taken from https://github.com/getclacking/SA-profile-keys-3D-models |
|||
$fn=400; |
|||
if(row == 1) { |
|||
import("stl/keycaps/mt3/mt3_r1_u1.stl"); |
|||
} |
|||
if(row == 2) { |
|||
import("stl/keycaps/mt3/mt3_r2_u1.stl"); |
|||
} |
|||
if(row == 3) { |
|||
import("stl/keycaps/mt3/mt3_r3_u1.stl"); |
|||
} |
|||
if(row == 4) { |
|||
import("stl/keycaps/mt3/mt3_r5_u1.stl"); |
|||
} |
|||
if(row == 5) { |
|||
import("stl/keycaps/mt3/mt3_r5_u1.stl"); |
|||
} |
|||
} |
|||
o=[10,30,50,70]; |
|||
|
|||
translate([0,-5,0]) |
|||
text("sa 1u",size=5); |
|||
|
|||
translate([30,-5,0]) |
|||
text("mt3 1u",size=5); |
|||
|
|||
translate([-10,o[0],0]) |
|||
text("R1",size=5); |
|||
|
|||
translate([-10,o[1],0]) |
|||
text("R2",size=5); |
|||
|
|||
translate([-10,o[2],0]) |
|||
text("R3",size=5); |
|||
|
|||
translate([-10,o[3],0]) |
|||
text("R4",size=5); |
|||
|
|||
for(i=[1:4]) { |
|||
translate([10,o[i-1],0]) |
|||
keycap("sa",5-i); |
|||
} |
|||
for(i=[1:4]) { |
|||
translate([40,o[i-1],0]) |
|||
keycap("mt3",5-i); |
|||
} |
|||
|
|||
|
@ -0,0 +1,682 @@ |
|||
|
|||
/* [Key] */ |
|||
//length in units of key |
|||
key_length = 1; |
|||
//height in units of key. should remain 1 for most uses |
|||
key_height = 1; |
|||
//keycap type, [0:DCS Row 5, 1:DCS Row 1, 2:DCS Row 2, 3:DCS Row 3, 4:DCS Row 4, 5:DSA Row 3, 6:SA Row 1, 7:SA Row 2, 8:SA Row 3, 9:SA Row 4, 10:DCS Row 4 Spacebar, 11: g20 key (faked)] |
|||
key_profile_index = 8; |
|||
|
|||
// keytop thickness, aka how many millimeters between the inside and outside of the top surface of the key |
|||
keytop_thickness = 1; |
|||
keybot_height=4.5; |
|||
|
|||
// wall thickness, aka the thickness of the sides of the keycap. note this is the total thickness, aka 3 = 1.5mm walls |
|||
wall_thickness = 3; |
|||
|
|||
/* [Brim] */ |
|||
//enable brim for connector |
|||
has_brim = 0; |
|||
//brim radius. 11 ensconces normal keycap stem in normal keycap |
|||
brim_radius = 11; |
|||
//brim depth |
|||
brim_depth = .3; |
|||
|
|||
/* [Stabilizers] */ |
|||
//whether stabilizer connectors are enabled |
|||
stabilizers = 0; |
|||
//stabilizer distance in mm |
|||
stabilizer_distance = 50; |
|||
|
|||
/* [Dish] */ |
|||
// invert dishing. mostly for spacebar |
|||
inverted_dish = 0; |
|||
|
|||
/* [Stem] */ |
|||
// cherry MX or Alps stem, or totally broken circular cherry stem [0..2] |
|||
stem_profile = 2; |
|||
// how inset the stem is from the bottom of the key. experimental. requires support |
|||
stem_inset = 0; |
|||
// stem offset in units NOT MM. for stepped caps lock |
|||
stem_offset = 0; |
|||
|
|||
/* [Hidden] */ |
|||
//change to round things better |
|||
$fn = 64; |
|||
//beginning to use unit instead of baked in 19.05 |
|||
unit = 19.05; |
|||
|
|||
//minkowski radius. radius of sphere used in minkowski sum for minkowski_key function. 1.75 default for faux G20 |
|||
minkowski_radius = 1.75; |
|||
|
|||
//profile specific stuff |
|||
|
|||
/* |
|||
Here we have, for lack of a better implementation, an array |
|||
that defines the more intimate aspects of a key. |
|||
order is thus: |
|||
1. Bottom Key Width: width of the immediate bottom of the key |
|||
2. Bottom Key Height: height of the immediate bottom of the key |
|||
3. Top Key Width Difference: mm to subtract from bottom key width to create top key width |
|||
4. Top Key Height Difference: mm to subtract from bottom key height to create top key height |
|||
5. total Depth: how tall the total in the switch is before dishing |
|||
6. Top Tilt: X rotation of the top. Top and dish obj are rotated |
|||
7. Top Skew: Y skew of the top of the key relative to the bottom. DCS has some, DSA has none (its centered) |
|||
8. Dish Type: type of dishing. check out dish function for the options |
|||
9. Dish Depth: how many mm to cut into the key with |
|||
10. Dish Radius: radius of dish obj, the Sphere or Cylinder that cuts into the keycap |
|||
*/ |
|||
|
|||
key_profiles = [ |
|||
|
|||
//DCS Profile |
|||
|
|||
[ //DCS ROW 5 |
|||
18.16, // Bottom Key Width |
|||
18.16, // Bottom Key Height |
|||
6, // Top Key Width Difference |
|||
4, // Top Key Height Difference |
|||
11.5, // total Depth |
|||
-6, // Top Tilt |
|||
1.75,// Top Skew |
|||
|
|||
//Dish Profile |
|||
|
|||
0, // Dish Type |
|||
1, // Dish Depth |
|||
0, // Dish Skew X |
|||
0 // DIsh Skew Y |
|||
], |
|||
[ //DCS ROW 1 |
|||
18.16, // Bottom Key Width |
|||
18.16, // Bottom Key Height |
|||
6, // Top Key Width Difference |
|||
4, // Top Key Height Difference |
|||
9.5, // total Depth |
|||
-1, // Top Tilt |
|||
1.75,// Top Skew |
|||
|
|||
//Dish Profile |
|||
|
|||
0, // Dish Type |
|||
1, // Dish Depth |
|||
0, // Dish Skew X |
|||
0 // DIsh Skew Y |
|||
], |
|||
[ //DCS ROW 2 |
|||
18.16, // Bottom Key Width |
|||
18.16, // Bottom Key Height |
|||
6.2, // Top Key Width Difference |
|||
4, // Top Key Height Difference |
|||
7.5, // total Depth |
|||
3, // Top Tilt |
|||
1.75,// Top Skew |
|||
|
|||
//Dish Profile |
|||
|
|||
0, // Dish Type |
|||
1, // Dish Depth |
|||
0, // Dish Skew X |
|||
0 // DIsh Skew Y |
|||
], |
|||
[ //DCS ROW 3 |
|||
18.16, // Bottom Key Width |
|||
18.16, // Bottom Key Height |
|||
6, // Top Key Width Difference |
|||
4, // Top Key Height Difference |
|||
6.2, // total Depth |
|||
7, // Top Tilt |
|||
1.75,// Top Skew |
|||
|
|||
//Dish Profile |
|||
|
|||
0, // Dish Type |
|||
1, // Dish Depth |
|||
0, // Dish Skew X |
|||
0 // DIsh Skew Y |
|||
], |
|||
[ //DCS ROW 4 |
|||
18.16, // Bottom Key Width |
|||
18.16, // Bottom Key Height |
|||
6, // Top Key Width Difference |
|||
4, // Top Key Height Difference |
|||
6.2, // total Depth |
|||
16, // Top Tilt |
|||
1.75,// Top Skew |
|||
|
|||
//Dish Profile |
|||
|
|||
0, // Dish Type |
|||
1, // Dish Depth |
|||
0, // Dish Skew X |
|||
0 // DIsh Skew Y |
|||
], |
|||
|
|||
//DSA Profile |
|||
|
|||
[ //DSA ROW 3 |
|||
18.4, // Bottom Key Width |
|||
18.4, // Bottom Key Height |
|||
5.7, // Top Key Width Difference |
|||
5.7, // Top Key Height Difference |
|||
7.4, // total Depth |
|||
0, // Top Tilt |
|||
0, // Top Skew |
|||
|
|||
//Dish Profile |
|||
|
|||
1, // Dish Type |
|||
1.2, // Dish Depth |
|||
0, // Dish Skew X |
|||
0 // DIsh Skew Y |
|||
], |
|||
|
|||
//SA Proile |
|||
|
|||
[ //SA ROW 1 |
|||
18.4, // Bottom Key Width |
|||
18.4, // Bottom Key Height |
|||
5.7, // Top Key Width Difference |
|||
5.7, // Top Key Height Difference |
|||
13.73, // total Depth, fudged |
|||
-14, // Top Tilt |
|||
0, // Top Skew |
|||
|
|||
//Dish Profile |
|||
|
|||
1, // Dish Type |
|||
1.2, // Dish Depth |
|||
0, // Dish Skew X |
|||
0 // DIsh Skew Y |
|||
], |
|||
[ //SA ROW 2 |
|||
18.4, // Bottom Key Width |
|||
18.4, // Bottom Key Height |
|||
5.7, // Top Key Width Difference |
|||
5.7, // Top Key Height Difference |
|||
11.73, // total Depth |
|||
-7, // Top Tilt |
|||
0, // Top Skew |
|||
|
|||
//Dish Profile |
|||
|
|||
1, // Dish Type |
|||
1.2, // Dish Depth |
|||
0, // Dish Skew X |
|||
0 // DIsh Skew Y |
|||
], |
|||
[ //SA ROW 3 |
|||
18.4, // Bottom Key Width |
|||
18.4, // Bottom Key Height |
|||
5.7, // Top Key Width Difference |
|||
5.7, // Top Key Height Difference |
|||
11.73, // total Depth |
|||
0, // Top Tilt |
|||
0, // Top Skew |
|||
|
|||
//Dish Profile |
|||
|
|||
1, // Dish Type |
|||
1.2, // Dish Depth |
|||
0, // Dish Skew X |
|||
0 // DIsh Skew Y |
|||
], |
|||
[ //SA ROW 4 |
|||
18.4, // Bottom Key Width |
|||
18.4, // Bottom Key Height |
|||
5.7, // Top Key Width Difference |
|||
5.7, // Top Key Height Difference |
|||
11.73, // total Depth |
|||
7, // Top Tilt |
|||
0, // Top Skew |
|||
|
|||
//Dish Profile |
|||
|
|||
1, // Dish Type |
|||
1.2, // Dish Depth |
|||
0, // Dish Skew X |
|||
0 // DIsh Skew Y |
|||
], |
|||
[ //DCS ROW 4 SPACEBAR |
|||
18.16, // Bottom Key Width |
|||
18.16, // Bottom Key Height |
|||
6, // Top Key Width Difference |
|||
4, // Top Key Height Difference |
|||
6.2, // total Depth |
|||
16, // Top Tilt |
|||
1.75,// Top Skew |
|||
|
|||
//Dish Profile |
|||
|
|||
2, // Dish Type |
|||
1, // Dish Depth |
|||
0, // Dish Skew X |
|||
0 // DIsh Skew Y |
|||
], |
|||
[ //G20 AKA DCS Row 2 with no dish and shorter |
|||
18.16, // Bottom Key Width |
|||
18.16, // Bottom Key Height |
|||
2, // Top Key Width Difference |
|||
2, // Top Key Height Difference |
|||
6, // total Depth |
|||
2.5, // Top Tilt |
|||
0.75,// Top Skew |
|||
|
|||
//Dish Profile |
|||
|
|||
3, // Dish Type |
|||
0, // Dish Depth |
|||
0, // Dish Skew X |
|||
0 // DIsh Skew Y |
|||
], |
|||
[ //NONWORKING fake ISO enter |
|||
18.16 * 1.5, // Bottom Key Width |
|||
18.16 * 2, // Bottom Key Height |
|||
4, // Top Key Width Difference |
|||
4, // Top Key Height Difference |
|||
7, // total Depth |
|||
0, // Top Tilt |
|||
1.75,// Top Skew |
|||
|
|||
//Dish Profile |
|||
|
|||
0, // Dish Type |
|||
1, // Dish Depth |
|||
0, // Dish Skew X |
|||
0 // DIsh Skew Y |
|||
], |
|||
]; |
|||
|
|||
// derived variables |
|||
//key profile selected |
|||
key_profile = key_profiles[key_profile_index]; |
|||
|
|||
// names, so I don't go crazy |
|||
bottom_key_width = key_profile[0]; |
|||
bottom_key_height = key_profile[1]; |
|||
width_difference = key_profile[2]; |
|||
height_difference = key_profile[3]; |
|||
total_depth = key_profile[4]; |
|||
top_tilt = key_profile[5] / key_height; |
|||
top_skew = key_profile[6]; |
|||
dish_type = key_profile[7]; |
|||
dish_depth = key_profile[8]; |
|||
dish_skew_x = key_profile[9]; |
|||
dish_skew_y = key_profile[10]; |
|||
|
|||
// actual mm key width and height |
|||
total_key_width = bottom_key_width + (unit * (key_length - 1)); |
|||
total_key_height = bottom_key_height + (unit * (key_height - 1)); |
|||
|
|||
// actual mm key width and height at the top |
|||
top_total_key_width = bottom_key_width + (unit * (key_length - 1)) - width_difference; |
|||
top_total_key_height = bottom_key_height + (unit * (key_height - 1)) - height_difference; |
|||
|
|||
//centered |
|||
module roundedRect(size, radius) { |
|||
x = size[0]; |
|||
y = size[1]; |
|||
z = size[2]; |
|||
|
|||
translate([-x/2,-y/2,0]) |
|||
linear_extrude(height=z) |
|||
hull() { |
|||
translate([radius, radius, 0]) |
|||
circle(r=radius); |
|||
|
|||
translate([x - radius, radius, 0]) |
|||
circle(r=radius); |
|||
|
|||
translate([x - radius, y - radius, 0]) |
|||
circle(r=radius); |
|||
|
|||
translate([radius, y - radius, 0]) |
|||
circle(r=radius); |
|||
} |
|||
} |
|||
|
|||
// stem related stuff |
|||
|
|||
// bottom we can use to anchor the stem, just a big ol cube with the inside of |
|||
// the keycap hollowed out |
|||
module inside(){ |
|||
difference(){ |
|||
translate([0,0,50]) cube([100000,100000,100000],center=true); |
|||
// NOTE: you're saying hey, if this is the inside why aren't we doing |
|||
// wall_thickness, keytop_thickness? well first off congratulations for |
|||
// figuring that out cuz it's a rat's nest in here. second off |
|||
// due to how the minkowski_key function works that isn't working out right |
|||
// now. it's a simple change if is_minkowski is implemented though |
|||
shape(0, 0); |
|||
} |
|||
} |
|||
|
|||
module cherry_stem(){ |
|||
// cross length |
|||
cross_length = 4.4; |
|||
//extra vertical cross length - the extra length of the up/down bar of the cross |
|||
extra_vertical_cross_length = 1.1; |
|||
//dimensions of connector |
|||
// outer cross extra length in x |
|||
extra_outer_cross_width = 2.10; |
|||
// outer cross extra length in y |
|||
extra_outer_cross_height = 1.0; |
|||
// dimensions of cross |
|||
// horizontal cross bar width |
|||
horizontal_cross_width = 1.3; |
|||
// vertical cross bar width |
|||
vertical_cross_width = 1.1; |
|||
// cross depth, stem height is 3.4mm |
|||
cross_depth = 4; |
|||
|
|||
difference(){ |
|||
union(){ |
|||
if (stem_profile != 2){ |
|||
translate([ |
|||
-(cross_length+extra_outer_cross_width)/2, |
|||
-(cross_length+extra_outer_cross_height)/2, |
|||
stem_inset |
|||
]) |
|||
cube([ // the base of the stem, the part the cruciform digs into |
|||
cross_length+extra_outer_cross_width, |
|||
cross_length+extra_outer_cross_height, |
|||
50 |
|||
]); |
|||
} else { |
|||
cylinder( |
|||
d = cross_length+extra_outer_cross_height, |
|||
h = 50 |
|||
); |
|||
} |
|||
if (has_brim == 1){ cylinder(r=brim_radius,h=brim_depth); } |
|||
} |
|||
//the cross part of the steam |
|||
translate([0,0,(cross_depth)/2 + stem_inset]){ |
|||
cube([vertical_cross_width,cross_length+extra_vertical_cross_length,cross_depth], center=true ); |
|||
cube([cross_length,horizontal_cross_width,cross_depth], center=true ); |
|||
} |
|||
} |
|||
} |
|||
|
|||
module alps_stem(){ |
|||
cross_depth = 40; |
|||
width = 4.45; |
|||
height = 2.25; |
|||
|
|||
base_width = 12; |
|||
base_height = 15; |
|||
|
|||
translate([0,0,cross_depth/2 + stem_inset]){ |
|||
cube([width,height,cross_depth], center = true); |
|||
} |
|||
} |
|||
|
|||
//whole connector, alps or cherry, trimmed to fit |
|||
module connector(has_brim){ |
|||
difference(){ |
|||
//TODO can I really not do an array index here? |
|||
translate([-unit * stem_offset, 0, 0]) |
|||
union(){ |
|||
if(stem_profile == 0 || stem_profile == 2) cherry_stem(); |
|||
if(stem_profile == 1) alps_stem(); |
|||
} |
|||
inside(); |
|||
} |
|||
} |
|||
|
|||
//stabilizer connectors |
|||
module stabilizer_connectors(has_brim){ |
|||
translate([stabilizer_distance,0,0]) connector(has_brim); |
|||
translate([-stabilizer_distance,0,0]) connector(has_brim); |
|||
} |
|||
|
|||
|
|||
|
|||
//shape related stuff |
|||
|
|||
|
|||
|
|||
|
|||
//general shape of key. used for inside and outside |
|||
module shape(thickness_difference, depth_difference){ |
|||
if (inverted_dish == 1){ |
|||
difference(){ |
|||
union(){ |
|||
shape_hull(thickness_difference, depth_difference, 1); |
|||
dish(depth_difference); |
|||
} |
|||
outside(thickness_difference); |
|||
} |
|||
} else{ |
|||
difference(){ |
|||
shape_hull(thickness_difference, depth_difference, 1); |
|||
dish(depth_difference); |
|||
} |
|||
} |
|||
} |
|||
|
|||
// conicalish clipping shape to trim things off the outside of the keycap |
|||
// literally just a key with height of 2 to make sure nothing goes awry with dishing etc |
|||
module outside(thickness_difference){ |
|||
difference(){ |
|||
cube([100000,100000,100000],center = true); |
|||
shape_hull(thickness_difference, 0, 2); |
|||
} |
|||
} |
|||
|
|||
// super basic hull shape without dish |
|||
// modifier multiplies the height and top differences of the shape, |
|||
// which is only used for dishing to cut the dish off correctly |
|||
// height_difference used for keytop thickness |
|||
module shape_hull(thickness_difference, depth_difference, modifier){ |
|||
hull(){ |
|||
// bottom_key_width + (key_length -1) * unit is the correct length of the |
|||
// key. only 1u of the key should be bottom_key_width long; all others |
|||
// should be 1u |
|||
roundedRect([total_key_width - thickness_difference, total_key_height - thickness_difference, .001],1.5); |
|||
|
|||
//height_difference outside of modifier because that doesnt make sense |
|||
translate([0,top_skew,total_depth * modifier - depth_difference]) |
|||
rotate([-top_tilt,0,0]) |
|||
roundedRect([total_key_width - thickness_difference - width_difference * modifier, total_key_height - thickness_difference - height_difference * modifier, .001],1.5); |
|||
} |
|||
} |
|||
|
|||
|
|||
|
|||
//dish related stuff |
|||
|
|||
|
|||
|
|||
//dish selector |
|||
module dish(depth_difference){ |
|||
if(dish_type == 0){ // cylindrical dish |
|||
cylindrical_dish(depth_difference); |
|||
} |
|||
else if (dish_type == 1) { // spherical dish |
|||
spherical_dish(depth_difference); |
|||
} |
|||
else if (dish_type == 2){ // SIDEWAYS cylindrical dish - used for spacebar |
|||
sideways_cylindrical_dish(depth_difference); |
|||
} |
|||
else if (dish_type == 3){ |
|||
// no dish |
|||
} |
|||
} |
|||
|
|||
module cylindrical_dish(depth_difference){ |
|||
/* we do some funky math here |
|||
* basically you want to have the dish "dig in" to the keycap x millimeters |
|||
* in order to do that you have to solve a small (2d) system of equations |
|||
* where the chord of the spherical cross section of the dish is |
|||
* the width of the keycap. |
|||
*/ |
|||
// the distance you have to move the dish up so it digs in dish_depth millimeters |
|||
chord_length = (pow(top_total_key_width, 2) - 4 * pow(dish_depth, 2)) / (8 * dish_depth); |
|||
//the radius of the dish |
|||
rad = (pow(top_total_key_width, 2) + 4 * pow(dish_depth, 2)) / (8 * dish_depth); |
|||
|
|||
if (inverted_dish == 1){ |
|||
translate([dish_skew_x, top_skew + dish_skew_y, total_depth - depth_difference]) |
|||
rotate([90-top_tilt,0,0]) |
|||
translate([0,-chord_length,0]) |
|||
cylinder(h=100,r=rad, $fn=$fn*32, center=true); |
|||
} |
|||
else{ |
|||
translate([dish_skew_x, top_skew + dish_skew_y, total_depth - depth_difference]) |
|||
rotate([90-top_tilt,0,0]) |
|||
translate([0,chord_length,0]) |
|||
cylinder(h=100,r=rad, $fn=$fn*32, center=true); |
|||
} |
|||
|
|||
} |
|||
|
|||
module spherical_dish(depth_difference){ |
|||
//same thing as the cylindrical dish here, but we need the corners to just touch - so we have to find the hypotenuse of the top |
|||
chord = pow((pow(top_total_key_width,2) + pow(top_total_key_height, 2)),0.5); //getting diagonal of the top |
|||
|
|||
// the distance you have to move the dish up so it digs in dish_depth millimeters |
|||
chord_length = (pow(chord, 2) - 4 * pow(dish_depth, 2)) / (8 * dish_depth); |
|||
//the radius of the dish |
|||
rad = (pow(chord, 2) + 4 * pow(dish_depth, 2)) / (8 * dish_depth); |
|||
|
|||
if (inverted_dish == 1){ |
|||
translate([dish_skew_x, top_skew + dish_skew_y, total_depth - depth_difference]) |
|||
rotate([-top_tilt,0,0]) |
|||
translate([0,0,-chord_length]) |
|||
//NOTE: if your dish is long at all you might need to increase this number |
|||
sphere(r=rad, $fn=$fn*16); |
|||
} |
|||
else{ |
|||
translate([dish_skew_x, top_skew + dish_skew_y, total_depth - depth_difference]) |
|||
rotate([-top_tilt,0,0]) |
|||
translate([0,0,chord_length]) |
|||
sphere(r=rad, $fn=$fn*8); |
|||
} |
|||
} |
|||
|
|||
module sideways_cylindrical_dish(depth_difference){ |
|||
chord_length = (pow(top_total_key_height, 2) - 4 * pow(dish_depth, 2)) / (8 * dish_depth); |
|||
rad = (pow(top_total_key_height, 2) + 4 * pow(dish_depth, 2)) / (8 * dish_depth); |
|||
|
|||
if (inverted_dish == 1){ |
|||
translate([dish_skew_x, top_skew + dish_skew_y, total_depth - depth_difference]) |
|||
rotate([90,top_tilt,90]) |
|||
translate([0,-chord_length,0]) |
|||
cylinder(h=total_key_width + 20,r=rad, $fn=$fn*32, center=true); // +20 just cuz |
|||
} |
|||
else{ |
|||
translate([dish_skew_x, top_skew + dish_skew_y, total_depth - depth_difference]) |
|||
rotate([90,top_tilt,90]) |
|||
translate([0,chord_length,0]) |
|||
cylinder(h=total_key_width + 20,r=rad, $fn=$fn*32, center=true); |
|||
} |
|||
} |
|||
|
|||
|
|||
|
|||
//actual full key with space carved out and keystem/stabilizer connectors |
|||
module key(){ |
|||
union(){ |
|||
difference(){ |
|||
shape(0, 0); |
|||
intersection() { |
|||
shape(wall_thickness, keytop_thickness); |
|||
translate([-50,-50,0]) |
|||
cube([100,100,keybot_height]); |
|||
} |
|||
} |
|||
} |
|||
|
|||
connector(has_brim); |
|||
|
|||
if (stabilizers == 1){ |
|||
stabilizer_connectors(has_brim); |
|||
} |
|||
} |
|||
|
|||
// ACTUAL OUTPUT |
|||
difference(){ |
|||
key(); |
|||
// preview cube, for seeing inside the keycap |
|||
//cube([100,100,100]); |
|||
} |
|||
|
|||
//minkowski_key(); |
|||
|
|||
|
|||
|
|||
|
|||
// Experimental stuff |
|||
|
|||
|
|||
// key rounded with minkowski sum. still supports wall and keytop thickness. |
|||
// use in actual output section. takes a long time to render with dishes. |
|||
// required for keycap 11, G20 keycap. |
|||
module minkowski_key(){ |
|||
union(){ |
|||
difference(){ |
|||
minkowski(){ |
|||
shape(minkowski_radius*2, minkowski_radius); |
|||
difference(){ |
|||
sphere(r=minkowski_radius, $fn=24); |
|||
translate([0,0,-minkowski_radius]) |
|||
cube([2*minkowski_radius,2*minkowski_radius,2*minkowski_radius], center=true); |
|||
} |
|||
} |
|||
shape(wall_thickness, keytop_thickness); |
|||
} |
|||
} |
|||
|
|||
connector(has_brim); |
|||
|
|||
if (stabilizers == 1){ |
|||
stabilizer_connectors(has_brim); |
|||
} |
|||
} |
|||
|
|||
|
|||
// NOT 3D, NOT CENTERED |
|||
// corollary is roundedRect |
|||
module fakeISOEnter(thickness_difference){ |
|||
z = 0.001; |
|||
radius = 2; |
|||
/*TODO I figured it out. 18.16 is the actual keycap width / height, |
|||
whereas 19.01 is the unit. ISO enter obeys that just like everything else, |
|||
which means that it's height is 18.16 * 2 + (19.01 - 18.16) or, two |
|||
keycap heights plus the space between them, also known as 18.16 + |
|||
(19.01 * (key_height - 1)). this is followed by the width too. should fix |
|||
to make this finally work*/ |
|||
unit = 18.16; // TODO probably not |
|||
|
|||
// t is all modifications to the polygon array |
|||
t = radius + thickness_difference/2; |
|||
|
|||
pointArray = [ |
|||
[0 + t,0 + t], |
|||
[unit*1.25 - t, 0 + t], |
|||
[unit*1.25 - t, unit*2 - t], |
|||
[unit*-.25 + t, unit*2 - t], |
|||
[unit*-.25 + t, unit*1 + t], |
|||
[0 + t, unit*1 + t] |
|||
]; |
|||
|
|||
minkowski(){ |
|||
circle(r=radius, $fn=24); |
|||
polygon(points=pointArray); |
|||
} |
|||
} |
|||
|
|||
//corollary is shape_hull |
|||
module ISOEnterShapeHull(thickness_difference, depth_difference, modifier){ |
|||
unit = 18.16; // TODO probably not |
|||
height = 8 - depth_difference; |
|||
length = 1.5 * unit; // TODO not used. need for dish |
|||
|
|||
translate([-0.125 * unit, unit*.5]) linear_extrude(height=height*modifier, scale=[.8, .9]){ |
|||
translate([-unit*.5, -unit*1.5]) minkowski(){ |
|||
fakeISOEnter(thickness_difference); |
|||
} |
|||
} |
|||
} |
|||
|
@ -0,0 +1,135 @@ |
|||
|
|||
|
|||
|
|||
// tip dip pip mcp |
|||
finger_h = 0.1*[ |
|||
[ 10, 15, 18, 28 ], [ 10, 15, 18, 28 ], [ 10, 15, 18, 28 ], [ 10, 15, 18, 28 ], [ 10, 15, 18, 28 ]]; |
|||
finger_o = [ |
|||
[ 10, 15, 18, 28 ], [ 10, 15, 18, 28 ], [ 10, 15, 18, 28 ], [ 10, 15, 18, 28 ], [ 10, 15, 18, 28 ]]; |
|||
finger_translate = [ |
|||
[ 20, 40, 40 ], [ 23, 20, 40 ], [ 20, 40, 35 ], [ 20, 40, 35 ], [ 20, 40, 35 ]]; |
|||
finger_max_angles = [[ [-5,85], [-5,90], [-5,70] ],[ [-5,85], [-5,90], [-5,70] ],[ [-5,85], [-5,90], [-5,70] ],[ [-5,85], [-5,90], [-5,70] ],[ [-5,85], [-5,90], [-5,70] ]]; |
|||
finger_offset_trans_from_index=[[-20,0,-40],[0,0,0],[-23,3,3],[-43,2,1],[-64,0,0]]; |
|||
finger_offset_rot_from_index=[[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]]; |
|||
|
|||
finger_curr_angles = [[0,10,0],[0,10,0],[0,10,0],[0,10,0],[0,10,0]]; |
|||
|
|||
// uncoment to do sync movement |
|||
//finger_curr_angles = [ calc_angles(0,abs($t-0.5)*2),calc_angles(0,abs($t-0.5)*2),calc_angles(0,abs($t-0.5)*2),calc_angles(0,abs($t-0.5)*2),calc_angles(0,abs($t-0.5)*2)]; |
|||
// sanity check |
|||
|
|||
|
|||
|
|||
module finger(idx,finger_curr_angles,show_blob_only) { |
|||
|
|||
if(len(finger_h[idx]) != len(finger_translate[idx]) +1) |
|||
echo("finger_h does not match to finger_translate"); |
|||
if(len(finger_h[idx]) != len(finger_max_angles[idx]) +1) |
|||
echo("finger_h does not match to finger_max_angles"); |
|||
if(len(finger_max_angles[idx][0]) != 2) |
|||
echo("finger_max_angles does not have 2 entries"); |
|||
|
|||
translate(finger_offset_trans_from_index[idx]) |
|||
rotate(finger_offset_rot_from_index[idx]) |
|||
{ |
|||
for(r_idx = [0:len(finger_translate[idx])-1]) |
|||
{ |
|||
rotate([r_idx<=2 ? finger_curr_angles[2] : 0,0,0]) |
|||
translate([0,0,r_idx<2 ? finger_translate[idx][2] : 0]) |
|||
rotate([r_idx<=1 ? finger_curr_angles[1] : 0,0,0]) |
|||
translate([0,0,r_idx<1 ? finger_translate[idx][1] : 0]) |
|||
rotate([r_idx<=0 ? finger_curr_angles[0] : 0,0,0]) |
|||
//translate([0,0,r_idx<0 ? finger_translate[idx][0] : 0]) |
|||
if (show_blob_only == true) { |
|||
if (r_idx == 0) |
|||
translate([0,0,finger_translate[idx][0]]) |
|||
sphere(d=2); |
|||
}else { |
|||
cylinder(d2=finger_h[idx][r_idx],d1=finger_h[idx][r_idx+1] , h=finger_translate[idx][r_idx]); |
|||
} |
|||
} |
|||
//translate([-20,-15,-20]) cube([40,30,20]); |
|||
sphere(d=10); |
|||
} |
|||
} |
|||
|
|||
|
|||
translate([140,125,120]) |
|||
rotate([-25,17+6,05]) |
|||
rotate([-90,0,0]) |
|||
rotate([0,0,180]) |
|||
{ |
|||
translate([20-100,0,-50]) |
|||
cube([100,5,50]); |
|||
for(idx=[1:4]) { |
|||
finger(idx,finger_curr_angles[idx]); |
|||
|
|||
for (i=[0:0.01:1]) |
|||
finger(idx,calc_angles(idx,i),show_blob_only=true); |
|||
} |
|||
} |
|||
|
|||
function calc_angles(idx,t)=[ |
|||
finger_max_angles[idx][0][1] + t * (finger_max_angles[idx][0][0] - finger_max_angles[idx][0][1]), |
|||
finger_max_angles[idx][1][1] + t * (finger_max_angles[idx][1][0] - finger_max_angles[idx][1][1]), |
|||
finger_max_angles[idx][2][1] + t * (finger_max_angles[idx][2][0] - finger_max_angles[idx][2][1]), |
|||
]; |
|||
|
|||
function pi()=3.14159265358979323846; |
|||
|
|||
|
|||
// scale since patent is in inch |
|||
scale([25.4,25.4,25.4]) |
|||
{ |
|||
d_t=0.01; |
|||
d_1a=0.01; |
|||
d_1b=0.1; |
|||
d_2=0.01; |
|||
d_3=0.01; |
|||
d_4a=0.01; |
|||
d_4b=0.01; |
|||
// finger thumb |
|||
color("magenta")hull() { translate([2.660100,3.769870,2.090850]) sphere(d_t); translate([2.666560,3.808380,1.894690]) sphere(d_t); } |
|||
color("magenta")hull() { translate([1.973320,3.510470,2.010680]) sphere(d_t); translate([1.980350,3.553940,1.815560]) sphere(d_t); } |
|||
color("magenta")hull() { translate([2.245370,2.853450,1.888880]) sphere(d_t); translate([2.261190,2.888580,1.692630]) sphere(d_t); } |
|||
color("magenta")hull() { translate([2.534610,2.192950,1.774750]) sphere(d_t); translate([2.534420,2.232090,1.578620]) sphere(d_t); } |
|||
color("magenta")hull() { translate([3.121180,2.762650,1.907540]) sphere(d_t); translate([3.118430,2.801320,1.711340]) sphere(d_t); } |
|||
color("magenta")hull() { translate([3.798070,3.053630,1.922090]) sphere(d_t); translate([3.787220,3.098700,1.727540]) sphere(d_t); } |
|||
// finger 1 |
|||
color("red")hull() { translate([5.626412,4.890490,4.921143]) sphere(d_1a); translate([3.603139,5.951178,1.756010]) sphere(d_1a); } |
|||
color("red")hull() { translate([3.601304,5.185127,1.597157]) sphere(d_1a); translate([5.626412,4.890490,4.921143]) sphere(d_1a); } |
|||
color("red") hull() { translate([5.626412,4.859049,4.921143]) sphere(d_1a); translate([3.680820,4.407240,1.571830]) sphere(d_1a); } |
|||
color("red") hull() { translate([3.686250,6.574620,2.042010]) sphere(d_1a); translate([5.626412,4.890490,4.921143]) sphere(d_1a); } |
|||
|
|||
color("green")hull() { translate([4.324486,6.084067,1.400235]) sphere(d_1b); translate([5.476960,5.040864,4.524379]) sphere(d_1b); } |
|||
color("green")hull() { translate([5.476960,5.040864,4.524379]) sphere(d_1b); translate([4.328402,5.307730,1.240122]) sphere(d_1b); } |
|||
color("green")hull() { translate([4.391585,4.517622,1.249485]) sphere(d_1b); translate([5.476960,5.040864,4.524379]) sphere(d_1b); } |
|||
color("green")hull() { translate([4.510779,3.754514,1.427841]) sphere(d_1b); translate([5.476960,5.040864,4.524379]) sphere(d_1b); } |
|||
color("green")hull() { translate([5.476960,5.040864,4.524379]) sphere(d_1b); translate([4.380041,6.806573,1.721562]) sphere(d_1b); } |
|||
|
|||
|
|||
// finger 2 |
|||
color("blue")hull() { translate([5.614666,5.194831,3.515830]) sphere(d_2); translate([5.065645,6.200934,1.067919]) sphere(d_2); } |
|||
color("blue")hull() { translate([5.080251,5.408403,0.874860]) sphere(d_2); translate([5.614666,5.194831,3.515830]) sphere(d_2); } |
|||
color("blue")hull() { translate([5.614666,5.194831,3.515830]) sphere(d_2); translate([5.143544,4.896416,0.922400]) sphere(d_2); } |
|||
color("blue")hull() { translate([5.249757,3.838946,1.206208]) sphere(d_2); translate([5.614666,5.194831,3.515830]) sphere(d_2); } |
|||
color("blue")hull() { translate([5.101057,6.901806,1.483988]) sphere(d_2); translate([5.614666,5.194831,3.515830]) sphere(d_2); } |
|||
|
|||
// finger 3 |
|||
color("brown")hull() { translate([6.306084,5.091255,4.240877]) sphere(d_3); translate([5.889712,6.192233,1.070680]) sphere(d_3); } |
|||
color("brown")hull() { translate([5.915799,5.415294,0.897474]) sphere(d_3); translate([6.306084,5.091255,4.240877]) sphere(d_3); } |
|||
color("brown")hull() { translate([6.306084,5.091255,4.240877]) sphere(d_3); translate([5.963534,4.620381,0.909720]) sphere(d_3); } |
|||
color("brown")hull() { translate([6.030270,3.851587,1.106739]) sphere(d_3); translate([6.306084,5.091255,4.240877]) sphere(d_3); } |
|||
color("brown")hull() { translate([5.886720,6.908103,1.419731]) sphere(d_3); translate([6.306084,5.091255,4.240877]) sphere(d_3); } |
|||
|
|||
// finger 4 |
|||
color("gray")hull() { translate([6.736145,5.156350,0.989659]) sphere(d_4a); translate([7.085053,4.987385,4.453756]) sphere(d_4a); } |
|||
color("gray")hull() { translate([7.085053,4.987385,4.453756]) sphere(d_4a); translate([6.777004,4.371971,1.036648]) sphere(d_4a); } |
|||
color("gray")hull() { translate([6.708940,6.659750,1.418630]) sphere(d_4a); translate([7.085053,4.987385,4.453756]) sphere(d_4a); } |
|||
color("gray")hull() { translate([6.833560,3.618950,1.257760]) sphere(d_4a); translate([7.085053,4.987385,4.453756]) sphere(d_4a); } |
|||
|
|||
color("cyan")hull() { translate([7.569335,6.085933,1.084243]) sphere(d_4b); translate([7.736480,5.132073,3.684937]) sphere(d_4b); } |
|||
color("cyan")hull() { translate([7.736480,5.132073,3.684937]) sphere(d_4b); translate([7.606606,5.316667,0.918996]) sphere(d_4b); } |
|||
color("cyan")hull() { translate([7.654340,4.532530,0.976580]) sphere(d_4b); translate([7.736480,5.132073,3.684937]) sphere(d_4b); } |
|||
color("cyan")hull() { translate([7.545530,6.778350,1.459020]) sphere(d_4b); translate([7.736480,5.132073,3.684937]) sphere(d_4b); } |
|||
} |
@ -0,0 +1,86 @@ |
|||
use <switch.scad>; |
|||
|
|||
|
|||
// rows |
|||
|
|||
|
|||
finger_colums=[-1,0,1,2,3,4]; |
|||
finger_rows = [ |
|||
4, // -1 |
|||
4, // 0 |
|||
4, // 1 |
|||
4, // 2 |
|||
4, // 3 |
|||
4, // 3 |
|||
]; |
|||
angle_switch_x=[ |
|||
[-10,0,10,20,30], // -1 |
|||
[-10,0,10,20,30], // 0 |
|||
[-10,0,10,20,30], // 1 |
|||
[-10,0,10,20,30], // 2 |
|||
[-10,0,10,20,30], // 3 |
|||
[-10,0,10,20,30], // 4 |
|||
]; |
|||
|
|||
offset_switch_y=[ |
|||
[0,0,0,0,0], // -1 |
|||
[0,0,0,0,0], // 0 |
|||
[0,0,0,0,0], // 1 |
|||
[0,0,0,0,0], // 2 |
|||
[0,0,0,0,0], // 3 |
|||
[0,0,0,0,0], // 4 |
|||
]; |
|||
offset_switch_z=[ |
|||
[0,0,0,0,0], // -1 |
|||
[0,0,0,0,0], // 0 |
|||
[0,0,0,0,0], // 1 |
|||
[0,0,0,0,0], // 2 |
|||
[0,0,0,0,0], // 3 |
|||
[0,0,0,0,0], // 4 |
|||
]; |
|||
angle_switch_y=[ |
|||
[0,0,0,0,0], // -1 |
|||
[0,0,0,0,0], // 0 |
|||
[0,0,0,0,0], // 1 |
|||
[0,0,0,0,0], // 2 |
|||
[0,0,0,0,0], // 3 |
|||
[0,0,0,0,0], // 4 |
|||
]; |
|||
keycaps_row=[ |
|||
[3,4,3,2,1,1], // -1 |
|||
[3,4,3,2,1,1], // 0 |
|||
[3,4,3,2,1,1], // 1 |
|||
[3,4,3,2,1,1], // 2 |
|||
[3,4,3,2,1,1], // 3 |
|||
[3,4,3,2,1,1], // 4 |
|||
]; |
|||
row_offset=[ |
|||
-15.1, -13.1, -13.15, -13.1 |
|||
]; |
|||
|
|||
|
|||
|
|||
//for(idx_x=[1:len(finger_colums)]) { |
|||
for(idx_x=[0:len(finger_colums)-1]) { |
|||
for(idx_y=[0:finger_rows[idx_x]-1]) { |
|||
// first try |
|||
finger_offset=19.0; |
|||
//translate([finger_offset*finger_colums[idx_x],finger_offset*idx_y,0]) |
|||
//rotate([angle_switch_x[idx_x][idx_y],angle_switch_y[idx_x][idx_y],0]) |
|||
// switch($t=$t, travel_max = 4, kap_type="sa", kap_row = keycaps_row[idx_x][idx_y]); |
|||
|
|||
// second try |
|||
// translate([0,0,42]) // lower by radius of keycaps |
|||
// rotate([20*(idx_y-2),0,0]) |
|||
// translate([0,0,-42]) // lower by radius of keycaps |
|||
// translate([0,0,row_offset[keycaps_row[idx_x][idx_y]-1]]) // bring keycaps in heigth |
|||
// switch($t=$t, travel_max = 4, kap_type="sa", kap_row = keycaps_row[idx_x][idx_y]); |
|||
|
|||
// third try |
|||
// cos alpha = b/c |
|||
translate([0,offset_switch_y[idx_x][idx_y],offset_switch_z[idx_x][idx_y]]) |
|||
rotate([angle_switch_x[idx_x][idx_y],angle_switch_y[idx_x][idx_y],0]) |
|||
translate([0,0,row_offset[keycaps_row[idx_x][idx_y]-1]]) // bring keycaps in heigth |
|||
switch($t=$t, travel_max = 4, kap_type="sa", kap_row = keycaps_row[idx_x][idx_y]); |
|||
} |
|||
} |
11
pcb/amoeba-royale.scad
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,6 @@ |
|||
module rp2040_pcb() { |
|||
rotate([90,0,0]) |
|||
import("../stl/pcb/rp2040.stl"); |
|||
} |
|||
|
|||
rp2040_pcb(); |
@ -0,0 +1,160 @@ |
|||
|
|||
include <constant.scad> |
|||
include <config.scad> |
|||
|
|||
use <keycap_stl.scad>; |
|||
|
|||
include <util/bezier/bezier_surface.scad>; |
|||
include <util/bezier/bezier_curve.scad>; |
|||
|
|||
include <util/surf/function_grapher.scad>; |
|||
include <util/surf/sf_thicken.scad>; |
|||
include <util/surf/sf_spline.scad>; |
|||
|
|||
include <util/vector/v_rot.scad>; |
|||
include <util/matrix/m_rotation.scad>; |
|||
include <util/matrix/m_translation.scad>; |
|||
|
|||
// show a switch |
|||
if($show_switches == true) |
|||
switch(0, "SA", 4); |
|||
|
|||
|
|||
|
|||
|
|||
module switch(t=0, kap_type="none", kap_row = 1) { |
|||
/** |
|||
a cherry mx switch. |
|||
most of the measurements done with a caliper. some taken from |
|||
http://geekhack.org/index.php?topic=47744.0 |
|||
*/ |
|||
|
|||
// move the whole thing 3mm to give the empty space in usual keycaps. |
|||
// that is, the extra space inside a keycap female connector. |
|||
// i do that since i create all my keycaps with 0,0,0 being the internal base of the keycap |
|||
|
|||
translate([0,0,4 +5.3 +0.82]) |
|||
{ |
|||
|
|||
if (kap_type != "none") |
|||
translate([0,0,-3.5 - abs(t-0.5) * 2*switch_travel_max]) |
|||
keycap(kap_type,kap_row); |
|||
|
|||
//1. steam |
|||
color("brown") |
|||
translate([0,0,-abs(t-0.5)*2*switch_travel_max]) |
|||
{ |
|||
//1.1. l-r tab is 2.35mm |
|||
translate([0,0,-3.62/2]) |
|||
cube([1.35,4.5,3.62], center=true); |
|||
//1.2. f-b tab is 1.15mm. it has a smal notch that i will ignore. |
|||
translate([0,0,-3.62/2]) |
|||
cube([4.5,1.15,3.62], center=true); |
|||
//1.3. base. it has a chamfered top that i will ignore. |
|||
translate([0,0,-5.62]) |
|||
cube([7.2,5.56,4], center=true); |
|||
} |
|||
// 2. top |
|||
color("lightgray"){ |
|||
difference(){ |
|||
// make a trapezoid with the general shape (volume?) of the top |
|||
hull(){ |
|||
translate([0,0,-4]) //distance from top of switch... some i measured 3.9 others 4.2... so leaving at 4 |
|||
cube([9.87,10.62,0.1], center=true); |
|||
translate([0,0,-4 -5.2]) // bottom has a measured 5.3... so move 5.2 and use the 0.1 bellow |
|||
cube([14.58,14.58,0.1], center=true); |
|||
} |
|||
|
|||
|
|||
// and subtract: |
|||
// the front led. NOTE: totally off... measured by eye. just for astetics |
|||
// adding just so there is a visual cue of the direction |
|||
translate([0,-4.7,-6]) |
|||
cylinder(r=3/2, h=6, center=true); |
|||
translate([0,-5.5,-6]) |
|||
cube([8,4,5], center=true); |
|||
|
|||
// stem cutout |
|||
|
|||
translate([0,0,-5.6]) |
|||
cube([7.3,5.6,8], center=true); |
|||
// the four corners |
|||
// TODO waste of time? this is all for looks, you shouldn't invade any of that space anyway... |
|||
} |
|||
} |
|||
|
|||
// 3. bottom |
|||
color("gray") |
|||
// 3.1 main body volume |
|||
hull(){ |
|||
translate([0,0,-4 -5.3]) //steam + top |
|||
cube([13.98,13.98,0.1], center=true); |
|||
translate([0,0,-4 -5.3 -2.2]) //steam + top + straigth part |
|||
cube([13.98,13.98,0.1], center=true); |
|||
translate([0,0,-4 -5.3 -5.5]) //steam + top + bottom (measured 5.5) |
|||
cube([12.74,13.6,0.1], center=true); |
|||
} |
|||
// 3.2 tabs |
|||
// note: only measured the lenght, if they are slightly off-center, this will be all wrong :) |
|||
color("black") |
|||
difference(){ |
|||
translate([0,0,-4 -5.3 -0.82/2]) //steam + top |
|||
cube([15.64,15.64,0.82], center=true); |
|||
translate([0,0,-4 -5.3 -0.82/2 ]) // front-back cut |
|||
cube([5.64,20,0.82 +2], center=true); |
|||
translate([0,0,-4 -5.3 -0.82/2 ]) //side cut |
|||
cube([20,11.64,0.82 +2], center=true); |
|||
} |
|||
|
|||
// 3.3 tab (plate snap on). to use this mechanically, you have to take into account the bending (as it will move the bottom part slightly up...) just for gross reference here for now |
|||
color("white"){ |
|||
// 3.3.1 top |
|||
translate([0,0,-4 -5.3 -0.82/2 ]) // front-back cut |
|||
cube([1.82,16.33,0.82], center=true); |
|||
// 3.3.2 bottom |
|||
difference(){ |
|||
hull(){ |
|||
translate([0,0,-4 -5.3 -0.82/2 -1.76 ]) // front-back cut |
|||
cube([3.65,14,0.1], center=true); |
|||
translate([0,0,-4 -5.3 -0.82/2 -2.2 ]) // front-back cut |
|||
cube([3.65,14.74,0.1], center=true); |
|||
translate([0,0,-4 -5.3 -0.82/2 -2.89 ]) // front-back cut |
|||
cube([3.65,14,0.1], center=true); |
|||
} |
|||
translate([0,0,-4 -5.3 -0.82/2 -1.76 ]) // front-back cut |
|||
cube([2.2,20,4], center=true); |
|||
|
|||
} |
|||
} |
|||
|
|||
// 4. bottom guides |
|||
// again, i'm assuming everything is centered... |
|||
color("darkGreen"){ |
|||
// 4.1 cylinder |
|||
translate([0,0,-4 -5.3 -5.5 -2/2]) //steam + top + bottom (measured 5.5) |
|||
cylinder(r=3.85/2, h=2, center=true); |
|||
translate([0,0,-4 -5.3 -5.5 -2 -1/2]) //steam + top + bottom (measured 5.5) |
|||
cylinder(r2=3.85/2, r1=2.8/2, h=1, center=true); |
|||
// 4.2 PCB pins |
|||
translate([4.95,0,-4 -5.3 -5.5 -2/2]) //steam + top + bottom (measured 5.5) |
|||
cylinder(r=1.6/2, h=2, center=true); |
|||
translate([4.95,0,-4 -5.3 -5.5 -2 -1/2]) //steam + top + bottom (measured 5.5) |
|||
cylinder(r2=1.6/2, r1=1/2, h=1, center=true); |
|||
translate([-4.95,0,-4 -5.3 -5.5 -2/2]) //steam + top + bottom (measured 5.5) |
|||
cylinder(r=1.6/2, h=2, center=true); |
|||
translate([-4.95,0,-4 -5.3 -5.5 -2 -1/2]) //steam + top + bottom (measured 5.5) |
|||
cylinder(r2=1.6/2, r1=1/2, h=1, center=true); |
|||
} |
|||
|
|||
// 5. pins |
|||
color("orange"){ |
|||
translate([-3.77,2.7,-4 -5.3 -5.5 -3.1/2]) //steam + top + bottom (measured 5.5) |
|||
cube([.86, 0.2,3.1], center=true); |
|||
translate([2.7,5.2,-4 -5.3 -5.5 -3.1/2]) //steam + top + bottom (measured 5.5) |
|||
cube([.86, 0.2,3.1], center=true); |
|||
} |
|||
|
|||
} |
|||
} |
|||
|
|||
|
@ -0,0 +1,92 @@ |
|||
include<constant.scad>; |
|||
include<config.scad>; |
|||
use<pcb/amoeba-royale.scad>; |
|||
|
|||
|
|||
// |
|||
// cutout types |
|||
// |
|||
// C H |
|||
// _________ _________ |
|||
// | | _| |_ |
|||
// | | |_ _| |
|||
// | | _| |_ |
|||
// | | |_ _| |
|||
// |_________| |_________| |
|||
// |
|||
// V _ _ B _ _ |
|||
// _| |_| |_ _| |_| |_ |
|||
// | | _| |_ |
|||
// | | |_ _| |
|||
// | | _| |_ |
|||
// | | |_ _| |
|||
// |_ _ _| |_ _ _| |
|||
// |_| |_| |_| |_| |
|||
// |
|||
switch_cutout(,extra=0.01); |
|||
translate([0,0,-switch_holder_height-amoeba_royale_pcb_h-$c]) |
|||
%amoeba_royale_pcb(); |
|||
|
|||
module switch_cutout(extra=0.01) { |
|||
h = switch_holder_height; |
|||
s_x=switch_dim_x+extra*2; |
|||
s_y=switch_dim_y+extra*2; |
|||
w=switch_cutout_width; |
|||
w2=w + switch_extra_keycap_width*2; |
|||
|
|||
translate([-s_x/2 , -s_y/2,-h]) |
|||
cube([s_x,s_y,h]); |
|||
|
|||
for(x=[-1,1]) |
|||
for(y=[-1,1]) |
|||
translate([x/2 * s_x , y/2 * s_y,-h]) |
|||
cylinder(d=0.5,h=h); |
|||
|
|||
switch_holder_cuts(switch_cutout_type); |
|||
|
|||
|
|||
} |
|||
|
|||
module switch_holder_cuts(type="C",borders=[1,1,1,1]) { |
|||
w=switch_cutout_width; |
|||
w2=w+switch_extra_keycap_width*2; |
|||
if (type=="C" || type=="c") { |
|||
// nothing |
|||
} |
|||
if (type=="H" || type=="h") { |
|||
// cutout for opening the switch |
|||
for(x=[1,0], r=[0,180]) |
|||
rotate([0,0,r]) |
|||
mirror([x,0,0]) |
|||
translate([w/2-$e,w/2-switch_cutout_side_width- switch_cutout_side_offset,-switch_holder_height-$e]) |
|||
cube([switch_cutout_side_dept+$e, |
|||
switch_cutout_side_width, |
|||
switch_holder_height +$e*2 ]); |
|||
|
|||
// cut out to reduce place for |
|||
// clamp on front/back of switch |
|||
for(r=[0,180]) |
|||
rotate([0,0,r]) { |
|||
translate([-6/2,w/2-$e,-switch_holder_height-$e]) |
|||
cube([6,1.5,switch_holder_height - switch_clamp_height+$e]); |
|||
} |
|||
|
|||
// screw cutout for pcb |
|||
if(switch_pcb_with_screws==true) |
|||
for(y=[1,0]) |
|||
mirror([0,y,0]) { |
|||
translate([-1,9.5,-switch_holder_height-amoeba_royale_pcb_h-$e-m2_head_h]) { |
|||
cylinder(d=m2_screw_d,h=amoeba_royale_pcb_screw_h); |
|||
cylinder(d=m2_head_d,h=m2_head_h); |
|||
} |
|||
} |
|||
} |
|||
if (type=="V" || type =="v") { |
|||
rotate([0,0,90]) |
|||
switch_holder_cuts("H",borders); |
|||
} |
|||
if (type=="B" || type =="b") { |
|||
switch_holder_cuts("V",borders); |
|||
switch_holder_cuts("H",borders); |
|||
} |
|||
} |
@ -0,0 +1,121 @@ |
|||
use <switch.scad>; |
|||
use <switch_cutout.scad>; |
|||
use <pcb/amoeba-royale.scad>; |
|||
|
|||
include<constant.scad>; |
|||
include<config.scad>; |
|||
|
|||
switch($t=0, "none", 4); |
|||
switch_holder_with_cutouts("V"); |
|||
|
|||
if($show_switch_pcb == true) |
|||
translate([0,0,-switch_holder_height-amoeba_royale_pcb_h-$c]) |
|||
%amoeba_royale_pcb(); |
|||
|
|||
|
|||
|
|||
|
|||
module switch_holder_with_cutouts(type="C") |
|||
{ |
|||
|
|||
difference() { |
|||
switch_holder(borders=[1,1,1,1]); |
|||
color("red") |
|||
switch_cutout(); |
|||
} |
|||
|
|||
} |
|||
|
|||
module switch_holder(borders=[0,1,0,0,0],thickness=2.5,type="C") { |
|||
|
|||
w=switch_cutout_width; |
|||
w2=w+thickness*2; |
|||
//north |
|||
echo(borders[1]); |
|||
if(borders[0]==1) |
|||
translate([-w2/2,w/2,-switch_holder_height]) |
|||
cube([w2,(w2-w)/2,switch_holder_height]); |
|||
|
|||
//south |
|||
if(borders[1]==1) |
|||
translate([-w2/2,-(w2-w)/2-w/2,-switch_holder_height]) |
|||
cube([w2,(w2-w)/2,switch_holder_height]); |
|||
|
|||
//east |
|||
if(borders[2]==1) |
|||
translate([-(w2-w)/2-w/2,-w2/2,-switch_holder_height]) |
|||
cube([(w2-w)/2,w2,switch_holder_height]); |
|||
|
|||
//west |
|||
if(borders[3]==1) |
|||
translate([w/2,-w2/2,-switch_holder_height]) |
|||
cube([(w2-w)/2,w2,switch_holder_height]); |
|||
|
|||
// b |
|||
if(borders[4]==1) |
|||
if($switch_with_pcb == false) |
|||
difference() { |
|||
translate([-w2/2,-w2/2,-switch_holder_height-1]) |
|||
cube([w2,w2,1]); |
|||
|
|||
|
|||
// 5. pins |
|||
color("orange"){ |
|||
translate([-3.77,2.7,0]) |
|||
cylinder(d=3,h=100, center=true); |
|||
|
|||
translate([2.7,5.2,0]) //steam + top + bottom (measured 5.5) |
|||
cylinder(d=3,h=100, center=true); |
|||
|
|||
translate([0,0,0]) //steam + top + bottom (measured 5.5) |
|||
cylinder(d=4.1, h=100, center=true); |
|||
|
|||
translate([4.95,0,0]) //steam + top + bottom (measured 5.5) |
|||
cylinder(d=1.8, h=100, center=true); |
|||
translate([-4.95,0,0]) //steam + top + bottom (measured 5.5) |
|||
cylinder(d=1.8, h=100, center=true); |
|||
} |
|||
} |
|||
} |
|||
|
|||
// returns the coordinates of the switch holder |
|||
function switch_point(a, h) = ( |
|||
(a == "NW" && h == "top") ? [-switch_holder_width/2, switch_holder_width/2,0] : |
|||
(a == "SW" && h == "top") ? [-switch_holder_width/2,-switch_holder_width/2,0] : |
|||
(a == "NO" && h == "top") ? [ switch_holder_width/2, switch_holder_width/2,0] : |
|||
(a == "SO" && h == "top") ? [ switch_holder_width/2,-switch_holder_width/2,0] : |
|||
(a == "NW" && h == "bot") ? [-switch_holder_width/2, switch_holder_width/2,-switch_holder_height] : |
|||
(a == "SW" && h == "bot") ? [-switch_holder_width/2,-switch_holder_width/2,-switch_holder_height] : |
|||
(a == "NO" && h == "bot") ? [ switch_holder_width/2, switch_holder_width/2,-switch_holder_height] : |
|||
(a == "SO" && h == "bot") ? [ switch_holder_width/2,-switch_holder_width/2,-switch_holder_height] : |
|||
|
|||
(a == "N" && h == "top") ? [0, switch_holder_width/2,0] : |
|||
(a == "S" && h == "top") ? [0,-switch_holder_width/2,0] : |
|||
(a == "O" && h == "top") ? [ switch_holder_width/2,0,0] : |
|||
(a == "w" && h == "top") ? [-switch_holder_width/2,0,0] : |
|||
undef |
|||
); |
|||
|
|||
module switch_connector_brim(borders=[1,1]) { |
|||
w=switch_holder_width; |
|||
w2=w+switch_extra_keycap_width*2; |
|||
|
|||
//north |
|||
if(borders[0]==1) |
|||
translate([-w2/2,w/2+(w2-w)/2,-switch_holder_height]) |
|||
cube([w2,$e,switch_holder_height]); |
|||
|
|||
//south |
|||
if(borders[0]==-1) |
|||
translate([-w2/2,-(w2-w)/2-w/2,-switch_holder_height]) |
|||
cube([w2,$e,switch_holder_height]); |
|||
|
|||
//east |
|||
if(borders[1]==1) |
|||
translate([-(w2-w)/2-w/2,-w2/2,-switch_holder_height]) |
|||
cube([$e,w2,switch_holder_height]); |
|||
//west |
|||
if(borders[1]==-1) |
|||
translate([w/2+(w2-w)/2,-w2/2,-switch_holder_height]) |
|||
cube([$e,w2,switch_holder_height]); |
|||
} |
@ -0,0 +1,220 @@ |
|||
include <config.scad>; |
|||
include <constant.scad>; |
|||
|
|||
use <switch_cutout.scad>; |
|||
use <switch_holder.scad>; |
|||
use <switch.scad>; |
|||
use <pcb/amoeba-royale.scad>; |
|||
|
|||
for(c=[0:len(switch_r[0])-1],r=[0:len(switch_r)-1]) { |
|||
|
|||
echo("switches c",c,"d",r); |
|||
translate(switch_t[r][c]) |
|||
rotate(switch_r[r][c]) { |
|||
if($show_switches == true) |
|||
switch(0, $show_keycaps ? switch_keycap[r][c][0]: "none", switch_keycap[r][c][1] ); |
|||
|
|||
if($show_switch_pcb == true) |
|||
translate([0,0,-switch_holder_height-amoeba_royale_pcb_h-$c]) |
|||
amoeba_royale_pcb(); |
|||
|
|||
switch_holder_with_cutouts(type="B"); |
|||
} |
|||
} |
|||
|
|||
function switch_rc_point(row,colum,a,h,offset=[0,0,0]) = v_rot(switch_r[row][colum]) * (switch_point(a,h) + offset) + switch_t[row][colum]; |
|||
|
|||
function switch_bz(row,colum,a,h,offset=[0,-10,0]) = [ |
|||
[ |
|||
switch_rc_point(row,colum,a[0],h,[0,0,0]), |
|||
switch_rc_point(row,colum,a[1],h,[0,0,0]), |
|||
], |
|||
[ |
|||
switch_rc_point(row,colum,a[0],h,offset), |
|||
switch_rc_point(row,colum,a[1],h,offset), |
|||
], |
|||
[ |
|||
zero_axis("z", switch_rc_point(row,colum,a[0],h,offset), -20), |
|||
zero_axis("z", switch_rc_point(row,colum,a[1],h,offset), -20) |
|||
], |
|||
]; |
|||
function switch_bz_edge(row,colum,a,h,offset1=[0,-10,0],offset2=[0,-10,0]) = [ |
|||
[ |
|||
switch_rc_point(row,colum,a,h,$e*offset1), |
|||
switch_rc_point(row,colum,a,h, 0*offset1), |
|||
switch_rc_point(row,colum,a,h,$e*offset2), |
|||
], |
|||
[ |
|||
switch_rc_point(row,colum,a,h,offset1), |
|||
switch_rc_point(row,colum,a,h,offset1+offset2), |
|||
switch_rc_point(row,colum,a,h,offset2), |
|||
], |
|||
[ |
|||
zero_axis("z", switch_rc_point(row,colum,a,h,offset1), -20), |
|||
zero_axis("z", switch_rc_point(row,colum,a,h,offset1+offset2), -20), |
|||
zero_axis("z", switch_rc_point(row,colum,a,h,offset2), -20) |
|||
], |
|||
]; |
|||
function switch_bz_offset(a)= ( |
|||
(a == "N" ) ? [ 10, 0, 0] : |
|||
(a == "S" ) ? [-10, 0, 0] : |
|||
(a == "O" ) ? [ 0, 10, 0] : |
|||
(a == "W" ) ? [ 0,-10, 0] : |
|||
undef |
|||
); |
|||
|
|||
|
|||
function switch_bz_between(rows,colums,a,h,offset_bz=[2,0,0],offset_out=[0,-10,0]) = [ |
|||
[ |
|||
switch_rc_point(rows[0],colums[0],a[0],h,[0,0,0]), |
|||
switch_rc_point(rows[0],colums[0],a[0],h,[0,0,0]+offset_bz), |
|||
switch_rc_point(rows[len(rows) == 1 ? 0 : 1],colums[len(colums) == 1 ? 0 : 1],a[1],h,[0,0,0]-offset_bz), |
|||
switch_rc_point(rows[len(rows) == 1 ? 0 : 1],colums[len(colums) == 1 ? 0 : 1],a[1],h,[0,0,0]), |
|||
], |
|||
[ |
|||
switch_rc_point(rows[0],colums[0],a[0],h,[0,0,0]+offset_out), |
|||
switch_rc_point(rows[0],colums[0],a[0],h,[0,0,0]+offset_bz+offset_out), |
|||
switch_rc_point(rows[len(rows) == 1 ? 0 : 1],colums[len(colums) == 1 ? 0 : 1],a[1],h,[0,0,0]-offset_bz+offset_out), |
|||
switch_rc_point(rows[len(rows) == 1 ? 0 : 1],colums[len(colums) == 1 ? 0 : 1],a[1],h,[0,0,0]+offset_out), |
|||
], |
|||
[ |
|||
zero_axis("z", switch_rc_point(rows[0],colums[0],a[0],h,[0,0,0]+offset_out), -20), |
|||
zero_axis("z", switch_rc_point(rows[0],colums[0],a[0],h,[0,0,0]+offset_bz+offset_out), -20), |
|||
zero_axis("z", switch_rc_point(rows[len(rows) == 1 ? 0 : 1],colums[len(colums) == 1 ? 0 : 1],a[1],h,[0,0,0]-offset_bz+offset_out), -20), |
|||
zero_axis("z", switch_rc_point(rows[len(rows) == 1 ? 0 : 1],colums[len(colums) == 1 ? 0 : 1],a[1],h,[0,0,0]+offset_out), -20), |
|||
], |
|||
]; |
|||
|
|||
function switch_bz_connection(rows,colums,a,h,offset_bz=[2,0,0]) = [ |
|||
[ |
|||
switch_rc_point(rows[0],colums[0],a[0][0],h,[0,0,0]-$e*offset_bz), |
|||
switch_rc_point(rows[0],colums[0],a[0][1],h,[0,0,0]-$e*offset_bz), |
|||
], |
|||
[ |
|||
switch_rc_point(rows[0],colums[0],a[0][0],h,[0,0,0]), |
|||
switch_rc_point(rows[0],colums[0],a[0][1],h,[0,0,0]), |
|||
], |
|||
[ |
|||
switch_rc_point(rows[0],colums[0],a[0][0],h,[0,0,0]+offset_bz), |
|||
switch_rc_point(rows[0],colums[0],a[0][1],h,[0,0,0]+offset_bz), |
|||
], |
|||
[ |
|||
switch_rc_point(rows[len(rows) == 1 ? 0 : 1],colums[len(colums) == 1 ? 0 : 1],a[1][0],h,[0,0,0]-offset_bz), |
|||
switch_rc_point(rows[len(rows) == 1 ? 0 : 1],colums[len(colums) == 1 ? 0 : 1],a[1][1],h,[0,0,0]-offset_bz), |
|||
], |
|||
[ |
|||
switch_rc_point(rows[len(rows) == 1 ? 0 : 1],colums[len(colums) == 1 ? 0 : 1],a[1][0],h,[0,0,0]), |
|||
switch_rc_point(rows[len(rows) == 1 ? 0 : 1],colums[len(colums) == 1 ? 0 : 1],a[1][1],h,[0,0,0]), |
|||
], |
|||
[ |
|||
switch_rc_point(rows[len(rows) == 1 ? 0 : 1],colums[len(colums) == 1 ? 0 : 1],a[1][0],h,[0,0,0]+$e*offset_bz), |
|||
switch_rc_point(rows[len(rows) == 1 ? 0 : 1],colums[len(colums) == 1 ? 0 : 1],a[1][1],h,[0,0,0]+$e*offset_bz), |
|||
], |
|||
]; |
|||
|
|||
function switch_bz_connection_cross(rows,colums,a,h,offset_bz=[2,0,0]) = [ |
|||
[ |
|||
switch_rc_point(rows[0],colums[0],a[0][0],h,[0,0,0]-$e*offset_bz), |
|||
switch_rc_point(rows[1],colums[0],a[0][1],h,[0,0,0]-$e*offset_bz), |
|||
], |
|||
[ |
|||
switch_rc_point(rows[0],colums[0],a[0][0],h,[0,0,0]), |
|||
switch_rc_point(rows[1],colums[0],a[0][1],h,[0,0,0]), |
|||
], |
|||
[ |
|||
switch_rc_point(rows[0],colums[0],a[0][0],h,[0,0,0]+offset_bz), |
|||
switch_rc_point(rows[1],colums[0],a[0][1],h,[0,0,0]+offset_bz), |
|||
], |
|||
[ |
|||
switch_rc_point(rows[0],colums[1],a[1][0],h,[0,0,0]-offset_bz), |
|||
switch_rc_point(rows[1],colums[1],a[1][1],h,[0,0,0]-offset_bz), |
|||
], |
|||
[ |
|||
switch_rc_point(rows[0],colums[1],a[1][0],h,[0,0,0]), |
|||
switch_rc_point(rows[1],colums[1],a[1][1],h,[0,0,0]), |
|||
], |
|||
[ |
|||
switch_rc_point(rows[0],colums[1],a[1][0],h,[0,0,0]+$e*offset_bz), |
|||
switch_rc_point(rows[1],colums[1],a[1][1],h,[0,0,0]+$e*offset_bz), |
|||
], |
|||
]; |
|||
function reverse_bz(points) = [for(i = [len(points)-1:-1:0]) points[i], ]; |
|||
|
|||
function switch_bz_all_w(colum,a,h,offset=[0,-10,0]) = [for (r=[0:3]) switch_bz(row, colum, a, h, offset),]; |
|||
|
|||
t_step = 0.1; |
|||
thickness = switch_holder_height+1; |
|||
|
|||
|
|||
ctrl_pts_S = [ |
|||
// S curve |
|||
switch_bz(3,0,["SW","SO"],"top",[0,-10,0]), |
|||
switch_bz(3,1,["SW","SO"],"top",[0,-10,0]), |
|||
switch_bz_between([3], [0,1],["SO","SW"],"top",[1,0,0],[0,-10,0]), |
|||
|
|||
// N curve |
|||
switch_bz(0,0,["NO","NW"],"top",[0,10,0]), |
|||
switch_bz(0,1,["NO","NW"],"top",[0,10,0]), |
|||
switch_bz_between([0], [1,0],["NW","NO"],"top",[-1,0,0],[0,+10,0]), |
|||
|
|||
// within OW |
|||
switch_bz_connection([0], [1,0],[ ["NW", "SW"],["NO", "SO"] ],"top",[-1,0,0]), |
|||
switch_bz_connection([1], [1,0],[ ["NW", "SW"],["NO", "SO"] ],"top",[-1,0,0]), |
|||
switch_bz_connection([2], [1,0],[ ["NW", "SW"],["NO", "SO"] ],"top",[-1,0,0]), |
|||
switch_bz_connection([3], [1,0],[ ["NW", "SW"],["NO", "SO"] ],"top",[-1,0,0]), |
|||
|
|||
// within NS |
|||
switch_bz_connection([0,1], [0],[ ["SW", "SO"],["NW", "NO"] ],"top",[0,-1,0]), |
|||
switch_bz_connection([1,2], [0],[ ["SW", "SO"],["NW", "NO"] ],"top",[0,-1,0]), |
|||
switch_bz_connection([2,3], [0],[ ["SW", "SO"],["NW", "NO"] ],"top",[0,-1,0]), |
|||
switch_bz_connection([0,1], [1],[ ["SW", "SO"],["NW", "NO"] ],"top",[0,-1,0]), |
|||
switch_bz_connection([1,2], [1],[ ["SW", "SO"],["NW", "NO"] ],"top",[0,-1,0]), |
|||
switch_bz_connection([2,3], [1],[ ["SW", "SO"],["NW", "NO"] ],"top",[0,-1,0]), |
|||
|
|||
switch_bz_connection_cross([1,0], [0,1],[ [ "NO", "SO"],["NW", "SW"] ],"top",[1,0,0]), |
|||
switch_bz_connection_cross([2,1], [0,1],[ [ "NO", "SO"],["NW", "SW"] ],"top",[1,0,0]), |
|||
switch_bz_connection_cross([3,2], [0,1],[ [ "NO", "SO"],["NW", "SW"] ],"top",[1,0,0]), |
|||
|
|||
// W curve |
|||
switch_bz(0,0,["NW","SW"],"top",[-10,0,0]), |
|||
switch_bz(1,0,["NW","SW"],"top",[-10,0,0]), |
|||
switch_bz(2,0,["NW","SW"],"top",[-10,0,0]), |
|||
switch_bz(3,0,["NW","SW"],"top",[-10,0,0]), |
|||
switch_bz_between([0,1], [0],["SW","NW"],"top",[1,0,0],[-10,0,0]), |
|||
switch_bz_between([1,2], [0],["SW","NW"],"top",[1,0,0],[-10,0,0]), |
|||
switch_bz_between([2,3], [0],["SW","NW"],"top",[1,0,0],[-10,0,0]), |
|||
|
|||
switch_bz_between([1,0], [1],["NO","SO"],"top",[1,0,0],[10,0,0]), |
|||
switch_bz_between([2,1], [1],["NO","SO"],"top",[1,0,0],[10,0,0]), |
|||
switch_bz_between([3,2], [1],["NO","SO"],"top",[1,0,0],[10,0,0]), |
|||
|
|||
// O curve |
|||
switch_bz(0,1,["SO","NO"],"top",[+10,0,0]), |
|||
switch_bz(1,1,["SO","NO"],"top",[+10,0,0]), |
|||
switch_bz(2,1,["SO","NO"],"top",[+10,0,0]), |
|||
switch_bz(3,1,["SO","NO"],"top",[+10,0,0]), |
|||
|
|||
// edge |
|||
switch_bz_edge(3,0,"SW","top",[-10,0,0],[0,-10,0]), |
|||
reverse_bz(switch_bz_edge(3,1,"SO","top",[ 10,0,0],[0,-10,0])), |
|||
reverse_bz(switch_bz_edge(0,0,"NW","top",[-10,0,0],[0, 10,0])), |
|||
switch_bz_edge(0,1,"NO","top",[ 10,0,0],[0, 10,0]), |
|||
]; |
|||
|
|||
for(k=[0:len(ctrl_pts_S)-1]) { |
|||
g_S = bezier_surface(t_step, ctrl_pts_S[k]); |
|||
// g_S = sf_splines(t_step, ctrl_pts_S); |
|||
sf_thicken(g_S, thickness,"FORWARD"); |
|||
//function_grapher(g_S,thickness,"FORWARD"); |
|||
} |
|||
|
|||
|
|||
// draw bezier_points |
|||
color("black") |
|||
for(k=[0:len(ctrl_pts_S)-1]) |
|||
for(i=[0:len(ctrl_pts_S[k])-1]) |
|||
for(j=[0:len(ctrl_pts_S[k][i])-1]) |
|||
translate(ctrl_pts_S[k][i][j]) |
|||
{ |
|||
%sphere(d=0.5); |
|||
} |
@ -0,0 +1,6 @@ |
|||
function __angy_angz(p1, p2) = |
|||
let(v = p2 - p1) |
|||
[ |
|||
atan2(v.z, norm([v.x, v.y])), |
|||
atan2(v.y, v.x) |
|||
]; |
@ -0,0 +1,4 @@ |
|||
function __frags(radius) = |
|||
$fn == 0 ? |
|||
max(min(360 / $fa, radius * 6.283185307179586 / $fs), 5) : |
|||
max($fn, 3); |
@ -0,0 +1 @@ |
|||
function __to3d(p) = [each p, 0]; |
@ -0,0 +1,3 @@ |
|||
use <vector/unit_vector.scad> |
|||
|
|||
function _face_normal(points) = unit_vector(cross(points[2] - points[0], points[1] - points[0])); |
@ -0,0 +1,31 @@ |
|||
use <../sum.scad> |
|||
|
|||
function _combi(n, k) = |
|||
let( |
|||
bi_coef = [ |
|||
[1], // n = 0: for padding |
|||
[1,1], // n = 1: for Linear curves, how about drawing a line directly? |
|||
[1,2,1], // n = 2: for Quadratic curves |
|||
[1,3,3,1] // n = 3: for Cubic Bézier curves |
|||
] |
|||
) |
|||
n < 4 ? bi_coef[n][k] : |
|||
k == 0 ? 1 : (_combi(n, k - 1) * (n - k + 1) / k); |
|||
|
|||
function _component(t, points, n, i) = |
|||
let(one_t = 1 - t) |
|||
sum([for(j = [0:n]) _combi(n, j) * points[j][i] * one_t ^ (n - j) * t ^ j]); |
|||
|
|||
function _coordinate(range, t, points, n) = |
|||
[for(i = range) _component(t, points, n, i)]; |
|||
|
|||
function _bezier_curve_impl(t_step, points) = |
|||
let( |
|||
t_end = ceil(1 / t_step), |
|||
n = len(points) - 1, |
|||
range = [0:len(points[0]) - 1] |
|||
) |
|||
[ |
|||
each [for(t = 0; t < t_end; t = t + 1) _coordinate(range, t * t_step, points, n)], |
|||
_coordinate(range, 1, points, n) |
|||
]; |
@ -0,0 +1,15 @@ |
|||
/** |
|||
* bezier_curve.scad |
|||
* |
|||
* @copyright Justin Lin, 2017 |
|||
* @license https://opensource.org/licenses/lgpl-3.0.html |
|||
* |
|||
* @see https://openhome.cc/eGossip/OpenSCAD/lib3x-bezier_curve.html |
|||
* |
|||
**/ |
|||
|
|||
use <_bezier_curve_impl.scad> |
|||
|
|||
|
|||
|
|||
function bezier_curve(t_step, points) = _bezier_curve_impl(t_step, points); |
@ -0,0 +1,29 @@ |
|||
/** |
|||
* bezier_surface.scad |
|||
* |
|||
* @copyright Justin Lin, 2017 |
|||
* @license https://opensource.org/licenses/lgpl-3.0.html |
|||
* |
|||
* @see https://openhome.cc/eGossip/OpenSCAD/lib3x-bezier_surface.html |
|||
* |
|||
**/ |
|||
|
|||
use <bezier_curve.scad> |
|||
use <../matrix/m_transpose.scad> |
|||
|
|||
function bezier_surface(t_step, ctrl_pts) = |
|||
let( |
|||
leng_ctrl_pts = len(ctrl_pts), |
|||
pts = [ |
|||
for(i = 0; i < leng_ctrl_pts; i = i + 1) |
|||
bezier_curve(t_step, ctrl_pts[i]) |
|||
], |
|||
leng_pts0 = len(pts[0]), |
|||
leng_pts = len(pts) |
|||
) |
|||
m_transpose([for(x = 0; x < leng_pts0; x = x + 1) |
|||
bezier_curve( |
|||
t_step, |
|||
[for(y = 0; y < leng_pts; y = y + 1) pts[y][x]] |
|||
) |
|||
]); |
@ -0,0 +1,13 @@ |
|||
/** |
|||
* flat.scad |
|||
* |
|||
* @copyright Justin Lin, 2020 |
|||
* @license https://opensource.org/licenses/lgpl-3.0.html |
|||
* |
|||
* @see https://openhome.cc/eGossip/OpenSCAD/lib3x-flat.html |
|||
* |
|||
**/ |
|||
|
|||
function flat(lt, depth = 1) = |
|||
depth == 1 ? [for(row = lt) each row] : |
|||
[for(row = lt) each flat(row, depth - 1)]; |
@ -0,0 +1,41 @@ |
|||
/** |
|||
* hull_polyline3d.scad |
|||
* |
|||
* @copyright Justin Lin, 2017 |
|||
* @license https://opensource.org/licenses/lgpl-3.0.html |
|||
* |
|||
* @see https://openhome.cc/eGossip/OpenSCAD/lib3x-hull_polyline3d.html |
|||
* |
|||
**/ |
|||
|
|||
module hull_polyline3d(points, diameter = 1) { |
|||
echo("`hull_polyline3d` is deprecated since 3.2. Use `polyline_join` instead."); |
|||
|
|||
radius = diameter / 2; |
|||
leng = len(points); |
|||
|
|||
module hull_line3d(index) { |
|||
point1 = points[index - 1]; |
|||
point2 = points[index]; |
|||
|
|||
hull() { |
|||
translate(point1) |
|||
children(); |
|||
translate(point2) |
|||
children(); |
|||
} |
|||
|
|||
// hook for testing |
|||
test_hull_polyline3d_line_segment(index, point1, point2, radius); |
|||
} |
|||
|
|||
for(i = [1:leng - 1]) { |
|||
hull_line3d(i) |
|||
sphere(radius); |
|||
} |
|||
} |
|||
|
|||
// override it to test |
|||
module test_hull_polyline3d_line_segment(index, point1, point2, radius) { |
|||
|
|||
} |
@ -0,0 +1,62 @@ |
|||
use <../vector/to_ang_vec.scad> |
|||
|
|||
FINAL_ROW = [0, 0, 0, 1]; |
|||
function __m_rotation_q_rotation(a, v) = |
|||
let( |
|||
uv = v / norm(v), |
|||
s = sin(a / 2) * uv, |
|||
w = sin(a) * uv, |
|||
|
|||
xx = 2 * s.x ^ 2, |
|||
yy = 2 * s.y ^ 2, |
|||
zz = 2 * s.z ^ 2, |
|||
|
|||
xy = 2 * s.x * s.y, |
|||
xz = 2 * s.x * s.z, |
|||
yz = 2 * s.y * s.z |
|||
) |
|||
[ |
|||
[1 - yy - zz, xy - w.z, xz + w.y, 0], |
|||
[xy + w.z, 1 - xx - zz, yz - w.x, 0], |
|||
[xz - w.y, yz + w.x, 1 - xx - yy, 0], |
|||
FINAL_ROW |
|||
]; |
|||
|
|||
function __m_rotation_xRotation(a) = |
|||
let(c = cos(a), s = sin(a)) |
|||
[ |
|||
[1, 0, 0, 0], |
|||
[0, c, -s, 0], |
|||
[0, s, c, 0], |
|||
FINAL_ROW |
|||
]; |
|||
|
|||
function __m_rotation_yRotation(a) = |
|||
let(c = cos(a), s = sin(a)) |
|||
[ |
|||
[c, 0, s, 0], |
|||
[0, 1, 0, 0], |
|||
[-s, 0, c, 0], |
|||
FINAL_ROW |
|||
]; |
|||
|
|||
function __m_rotation_zRotation(a) = |
|||
let(c = cos(a), s = sin(a)) |
|||
[ |
|||
[c, -s, 0, 0], |
|||
[s, c, 0, 0], |
|||
[0, 0, 1, 0], |
|||
FINAL_ROW |
|||
]; |
|||
|
|||
function __m_rotation_xyz_rotation(a) = |
|||
let(ang = to_ang_vect(a)) |
|||
__m_rotation_zRotation(ang[2]) * __m_rotation_yRotation(ang[1]) * __m_rotation_xRotation(ang[0]); |
|||
|
|||
function _m_rotation_impl(a, v) = |
|||
(a == 0 || a == [0, 0, 0] || a == [0] || a == [0, 0]) ? [ |
|||
[1, 0, 0, 0], |
|||
[0, 1, 0, 0], |
|||
[0, 0, 1, 0], |
|||
FINAL_ROW |
|||
] : (is_undef(v) ? __m_rotation_xyz_rotation(a) : __m_rotation_q_rotation(a, v)); |
@ -0,0 +1,16 @@ |
|||
function __m_scaling_to_3_elems_scaling_vect(s) = |
|||
let(leng = len(s)) |
|||
leng == 3 ? s : |
|||
leng == 2 ? [each s, 1] : [s.x, 1, 1]; |
|||
|
|||
function __m_scaling_to_scaling_vect(s) = is_num(s) ? [s, s, s] : __m_scaling_to_3_elems_scaling_vect(s); |
|||
|
|||
FINAL_ROW = [0, 0, 0, 1]; |
|||
function _m_scaling_impl(s) = |
|||
let(v = __m_scaling_to_scaling_vect(s)) |
|||
[ |
|||
[v.x, 0, 0, 0], |
|||
[0, v.y, 0, 0], |
|||
[0, 0, v.z, 0], |
|||
FINAL_ROW |
|||
]; |
@ -0,0 +1,15 @@ |
|||
function _to_3_elems_translation_vect(v) = |
|||
let(leng = len(v)) |
|||
leng == 3 ? v : |
|||
leng == 2 ? [each v, 0] : [v.x, 0, 0]; |
|||
|
|||
function _to_translation_vect(v) = is_num(v) ? [v, 0, 0] : _to_3_elems_translation_vect(v); |
|||
|
|||
function _m_translation_impl(v) = |
|||
let(vt = _to_translation_vect(v)) |
|||
[ |
|||
[1, 0, 0, vt.x], |
|||
[0, 1, 0, vt.y], |
|||
[0, 0, 1, vt.z], |
|||
[0, 0, 0, 1] |
|||
]; |
@ -0,0 +1,13 @@ |
|||
/** |
|||
* m_rotation.scad |
|||
* |
|||
* @copyright Justin Lin, 2019 |
|||
* @license https://opensource.org/licenses/lgpl-3.0.html |
|||
* |
|||
* @see https://openhome.cc/eGossip/OpenSCAD/lib3x-m_rotation.html |
|||
* |
|||
**/ |
|||
|
|||
use <../matrix/_m_rotation_impl.scad> |
|||
|
|||
function m_rotation(a, v) = _m_rotation_impl(a, v); |
@ -0,0 +1,13 @@ |
|||
/** |
|||
* m_scaling.scad |
|||
* |
|||
* @copyright Justin Lin, 2019 |
|||
* @license https://opensource.org/licenses/lgpl-3.0.html |
|||
* |
|||
* @see https://openhome.cc/eGossip/OpenSCAD/lib3x-m_scaling.html |
|||
* |
|||
**/ |
|||
|
|||
use <_m_scaling_impl.scad> |
|||
|
|||
function m_scaling(s) = _m_scaling_impl(s); |
@ -0,0 +1,13 @@ |
|||
/** |
|||
* m_translation.scad |
|||
* |
|||
* @copyright Justin Lin, 2019 |
|||
* @license https://opensource.org/licenses/lgpl-3.0.html |
|||
* |
|||
* @see https://openhome.cc/eGossip/OpenSCAD/lib3x-m_translation.html |
|||
* |
|||
**/ |
|||
|
|||
use <_m_translation_impl.scad>; |
|||
|
|||
function m_translation(v) = _m_translation_impl(v); |
@ -0,0 +1,22 @@ |
|||
/** |
|||
* m_transpose.scad |
|||
* |
|||
* @copyright Justin Lin, 2021 |
|||
* @license https://opensource.org/licenses/lgpl-3.0.html |
|||
* |
|||
* @see https://openhome.cc/eGossip/OpenSCAD/lib3x-m_transpose.html |
|||
* |
|||
**/ |
|||
|
|||
function m_transpose(m) = |
|||
let( |
|||
column = len(m[0]), |
|||
row = len(m) |
|||
) |
|||
[ |
|||
for(y = 0; y < column; y = y + 1) |
|||
[ |
|||
for(x = 0; x < row; x = x + 1) |
|||
m[x][y] |
|||
] |
|||
]; |
@ -0,0 +1,177 @@ |
|||
/** |
|||
* path_extrude.scad |
|||
* |
|||
* @copyright Justin Lin, 2017 |
|||
* @license https://opensource.org/licenses/lgpl-3.0.html |
|||
* |
|||
* @see https://openhome.cc/eGossip/OpenSCAD/lib3x-path_extrude.html |
|||
* |
|||
**/ |
|||
|
|||
use <__to3d.scad> |
|||
use <__angy_angz.scad> |
|||
use <sweep.scad> |
|||
use <matrix/m_scaling.scad> |
|||
use <matrix/m_translation.scad> |
|||
use <matrix/m_rotation.scad> |
|||
|
|||
module path_extrude(shape_pts, path_pts, triangles = "SOLID", twist = 0, scale = 1.0, closed = false, method = "AXIS_ANGLE") { |
|||
sh_pts = len(shape_pts[0]) == 3 ? [for(p = shape_pts) [each p, 1]] : [for(p = shape_pts) [p.x, p.y, 0, 1]]; |
|||
pth_pts = len(path_pts[0]) == 3 ? path_pts : [for(p = path_pts) __to3d(p)]; |
|||
|
|||
len_path_pts = len(pth_pts); |
|||
len_path_pts_minus_one = len_path_pts - 1; |
|||
|
|||
m_rot_90_0_n90 = m_rotation([90, 0, -90]); |
|||
one = [1, 1, 1]; |
|||
|
|||
module axis_angle_path_extrude() { |
|||
twist_step_a = twist / len_path_pts; |
|||
|
|||
function translate_pts(pts, t) = [for(p = pts) p + t]; |
|||
|
|||
scale_step_vt = ( |
|||
is_num(scale) ? |
|||
let(s = scale - 1) [s, s, s] : |
|||
len(scale) == 2 ? [each (scale - [1, 1]), 0]: |
|||
scale - one |
|||
) / len_path_pts_minus_one; |
|||
|
|||
identity_matrix = [ |
|||
[1, 0, 0, 0], |
|||
[0, 1, 0, 0], |
|||
[0, 0, 1, 0], |
|||
[0, 0, 0, 1] |
|||
]; |
|||
|
|||
// get rotation matrice for sections |
|||
rot_matrice = [ |
|||
for(i = len_path_pts - 2; i > 0; i = i - 1) |
|||
let( |
|||
vt0 = pth_pts[i] - pth_pts[i - 1], |
|||
vt1 = pth_pts[i + 1] - pth_pts[i], |
|||
a = acos((vt0 * vt1) / sqrt(vt0 * vt0 * vt1 * vt1)), |
|||
v = cross(vt0, vt1) |
|||
) |
|||
m_rotation(a, v) |
|||
]; |
|||
|
|||
leng_rot_matrice = len(rot_matrice); |
|||
cumu_rot_matrice = leng_rot_matrice <= 1 ? |
|||
[each rot_matrice, identity_matrix] : |
|||
[ |
|||
for( |
|||
i = leng_rot_matrice - 1, m = rot_matrice[i]; |
|||
i > -1; |
|||
i = i - 1, m = i == -1 ? undef : rot_matrice[i] * m |
|||
) |
|||
m |
|||
]; |
|||
|
|||
// get all sections |
|||
angleyz_pts01 = __angy_angz(pth_pts[0], pth_pts[1]); |
|||
function init_section(a, s) = |
|||
let(transform_m = m_rotation([0, -angleyz_pts01[0], angleyz_pts01[1]]) * m_rot_90_0_n90 * m_rotation(a) * m_scaling(s)) |
|||
[ |
|||
for(p = sh_pts) |
|||
let(transformed = transform_m * p) |
|||
[transformed.x, transformed.y, transformed.z] |
|||
]; |
|||
|
|||
function local_rotate_section(j, init_a, init_s) = |
|||
let( |
|||
ms = cumu_rot_matrice[j - 1], |
|||
ms0 = ms[0], |
|||
ms1 = ms[1], |
|||
ms2 = ms[2], |
|||
ms0p = [ms0[0], ms0[1], ms0[2]], |
|||
ms1p = [ms1[0], ms1[1], ms1[2]], |
|||
ms2p = [ms2[0], ms2[1], ms2[2]] |
|||
) |
|||
[ |
|||
for(p = init_section(init_a, init_s)) |
|||
[ms0p * p, ms1p * p, ms2p * p] |
|||
]; |
|||
|
|||
sections = |
|||
let( |
|||
fst_section = translate_pts(init_section(0, one), pth_pts[0]), |
|||
snd_section = translate_pts(init_section(0, one + scale_step_vt), pth_pts[1]), |
|||
end_i = len_path_pts - 1, |
|||
remain_sections = [ |
|||
for(i = 1; i < end_i; i = i + 1) |
|||
translate_pts( |
|||
local_rotate_section(i, i * twist_step_a, one + scale_step_vt * (i + 1)), |
|||
pth_pts[i + 1] |
|||
) |
|||
] |
|||
) [fst_section, snd_section, each remain_sections]; |
|||
|
|||
calculated_sections = |
|||
closed && pth_pts[0] == pth_pts[len_path_pts_minus_one] ? |
|||
[each sections, sections[0]] : // round-robin |
|||
sections; |
|||
|
|||
sweep( |
|||
calculated_sections, |
|||
triangles |
|||
); |
|||
|
|||
// hook for testing |
|||
test_path_extrude(sections, method); |
|||
} |
|||
|
|||
module euler_angle_path_extrude() { |
|||
scale_step_vt = ((is_num(scale) ? [scale, scale] : scale) - [1, 1]) / len_path_pts_minus_one; |
|||
|
|||
twist_step = twist / len_path_pts_minus_one; |
|||
|
|||
function section(p1, p2, i) = |
|||
let( |
|||
angy_angz = __angy_angz(p1, p2), |
|||
ay = -angy_angz[0], |
|||
az = angy_angz[1], |
|||
transform_m = m_translation(p1) * |
|||
m_rotation([0, ay, az]) * |
|||
m_translation([i == 0 ? 0 : norm(p1 - p2), 0, 0]) * |
|||
m_rot_90_0_n90 * |
|||
m_rotation(twist_step * i) * |
|||
m_scaling([1 + scale_step_vt.x * i, 1 + scale_step_vt.y * i, 1]) |
|||
) |
|||
[ |
|||
for(p = sh_pts) |
|||
let(transformed = transform_m * p) |
|||
[transformed.x, transformed.y, transformed.z] |
|||
]; |
|||
|
|||
path_extrude_inner = [ |
|||
for(i = 1; i < len_path_pts; i = i + 1) |
|||
section(pth_pts[i - 1], pth_pts[i], i) |
|||
]; |
|||
|
|||
calculated_sections = |
|||
closed && pth_pts[0] == pth_pts[len_path_pts_minus_one] ? |
|||
[each path_extrude_inner, path_extrude_inner[0]] : // round-robin |
|||
[section(pth_pts[0], pth_pts[1], 0), each path_extrude_inner]; |
|||
|
|||
sweep( |
|||
calculated_sections, |
|||
triangles |
|||
); |
|||
|
|||
// hook for testing |
|||
test_path_extrude(calculated_sections, method); |
|||
} |
|||
|
|||
if(method == "AXIS_ANGLE") { |
|||
axis_angle_path_extrude(); |
|||
} |
|||
else if(method == "EULER_ANGLE") { |
|||
euler_angle_path_extrude(); |
|||
} |
|||
} |
|||
|
|||
// override to test |
|||
module test_path_extrude(sections, method) { |
|||
|
|||
} |
@ -0,0 +1,11 @@ |
|||
/** |
|||
* reverse.scad |
|||
* |
|||
* @copyright Justin Lin, 2019 |
|||
* @license https://opensource.org/licenses/lgpl-3.0.html |
|||
* |
|||
* @see https://openhome.cc/eGossip/OpenSCAD/lib3x-reverse.html |
|||
* |
|||
**/ |
|||
|
|||
function reverse(lt) = [for(i = len(lt) - 1; i > -1; i = i - 1) lt[i]]; |
@ -0,0 +1,22 @@ |
|||
/** |
|||
* shape_circle.scad |
|||
* |
|||
* @copyright Justin Lin, 2020 |
|||
* @license https://opensource.org/licenses/lgpl-3.0.html |
|||
* |
|||
* @see https://openhome.cc/eGossip/OpenSCAD/lib3x-shape_circle.html |
|||
* |
|||
**/ |
|||
|
|||
use <__frags.scad> |
|||
|
|||
function shape_circle(radius, n) = |
|||
let( |
|||
_frags = __frags(radius), |
|||
step_a = 360 / _frags, |
|||
to = (is_undef(n) || n > _frags) ? _frags - 1: n - 1 |
|||
) |
|||
[ |
|||
for(a = [each [0:to]] * step_a) |
|||
[radius * cos(a), radius * sin(a)] |
|||
]; |
@ -0,0 +1,20 @@ |
|||
/** |
|||
* sum.scad |
|||
* |
|||
* @copyright Justin Lin, 2020 |
|||
* @license https://opensource.org/licenses/lgpl-3.0.html |
|||
* |
|||
* @see https://openhome.cc/eGossip/OpenSCAD/lib3x-sum.html |
|||
* |
|||
**/ |
|||
|
|||
function sum(lt) = |
|||
let(end = len(lt) - 1) |
|||
end == 0 ? lt[0] : |
|||
let( |
|||
cum_total = [ |
|||
for(i = 0, s = lt[0], is_continue = i < end; |
|||
is_continue; |
|||
i = i + 1, is_continue = i < end, s = is_continue ? s + lt[i] : undef) s] |
|||
) |
|||
cum_total[end - 1] + lt[end]; |
@ -0,0 +1,234 @@ |
|||
/** |
|||
* function_grapher.scad |
|||
* |
|||
* @copyright Justin Lin, 2017 |
|||
* @license https://opensource.org/licenses/lgpl-3.0.html |
|||
* |
|||
* @see https://openhome.cc/eGossip/OpenSCAD/lib3x-function_grapher.html |
|||
* |
|||
**/ |
|||
|
|||
use <../reverse.scad> |
|||
use <../hull_polyline3d.scad> |
|||
use <../path_extrude.scad> |
|||
use <../shape_circle.scad> |
|||
|
|||
module function_grapher(points, thickness = 1, style = "FACES") { |
|||
echo("`function_grapher` is deprecated since 3.1. Use `sf_thicken` instead."); |
|||
|
|||
rows = len(points); |
|||
columns = len(points[0]); |
|||
|
|||
yi_range = [0:rows - 2]; |
|||
xi_range = [0:columns - 2]; |
|||
|
|||
half_thickness = thickness / 2; |
|||
|
|||
// Increasing $fn will be slow when you use "LINES", "HULL_FACES" or "HULL_LINES". |
|||
|
|||
module faces() { |
|||
function xy_to_index(x, y, columns) = y * columns + x; |
|||
|
|||
top_pts = [ |
|||
for(row_pts = points) |
|||
for(pt = row_pts) |
|||
pt |
|||
]; |
|||
|
|||
base_pts = [ |
|||
for(pt = top_pts) |
|||
[pt[0], pt[1], pt[2] - thickness] |
|||
]; |
|||
|
|||
leng_pts = len(top_pts); |
|||
|
|||
top_tri_faces1 = [ |
|||
for(yi = yi_range) |
|||
for(xi = xi_range) |
|||
[ |
|||
xy_to_index(xi, yi, columns), |
|||
xy_to_index(xi + 1, yi + 1, columns), |
|||
xy_to_index(xi + 1, yi, columns) |
|||
] |
|||
]; |
|||
|
|||
top_tri_faces2 = [ |
|||
for(yi = yi_range) |
|||
for(xi = xi_range) |
|||
[ |
|||
xy_to_index(xi, yi, columns), |
|||
xy_to_index(xi, yi + 1, columns), |
|||
xy_to_index(xi + 1, yi + 1, columns) |
|||
] |
|||
]; |
|||
|
|||
offset_v = [leng_pts, leng_pts, leng_pts]; |
|||
base_tri_faces1 = [ |
|||
for(face = top_tri_faces1) |
|||
reverse(face) + offset_v |
|||
]; |
|||
|
|||
base_tri_faces2 = [ |
|||
for(face = top_tri_faces2) |
|||
reverse(face) + offset_v |
|||
]; |
|||
|
|||
side_faces1 = [ |
|||
for(xi = xi_range) |
|||
let( |
|||
idx1 = xy_to_index(xi, 0, columns), |
|||
idx2 = xy_to_index(xi + 1, 0, columns) |
|||
) |
|||
[ |
|||
idx1, |
|||
idx2, |
|||
idx2 + leng_pts, |
|||
idx1 + leng_pts |
|||
] |
|||
]; |
|||
|
|||
side_faces2 = [ |
|||
for(yi = yi_range) |
|||
let( |
|||
xi = columns - 1, |
|||
idx1 = xy_to_index(xi, yi, columns), |
|||
idx2 = xy_to_index(xi, yi + 1, columns) |
|||
) |
|||
[ |
|||
idx1, |
|||
idx2, |
|||
idx2 + leng_pts, |
|||
idx1 + leng_pts |
|||
] |
|||
]; |
|||
|
|||
side_faces3 = [ |
|||
for(xi = xi_range) |
|||
let( |
|||
yi = rows - 1, |
|||
idx1 = xy_to_index(xi, yi, columns), |
|||
idx2 = xy_to_index(xi + 1, yi, columns) |
|||
) |
|||
[ |
|||
idx2, |
|||
idx1, |
|||
idx1 + leng_pts, |
|||
idx2 + leng_pts |
|||
] |
|||
]; |
|||
|
|||
side_faces4 = [ |
|||
for(yi = yi_range) |
|||
let( |
|||
idx1 = xy_to_index(0, yi, columns), |
|||
idx2 = xy_to_index(0, yi + 1, columns) |
|||
) |
|||
[ |
|||
idx2, |
|||
idx1, |
|||
idx1 + leng_pts, |
|||
idx2 + leng_pts |
|||
] |
|||
]; |
|||
|
|||
pts = concat(top_pts, base_pts); |
|||
face_idxs = concat( |
|||
top_tri_faces1, top_tri_faces2, |
|||
base_tri_faces1, base_tri_faces2, |
|||
side_faces1, |
|||
side_faces2, |
|||
side_faces3, |
|||
side_faces4 |
|||
); |
|||
|
|||
polyhedron( |
|||
points = pts, |
|||
faces = face_idxs |
|||
); |
|||
|
|||
// hook for testing |
|||
test_function_grapher_faces(pts, face_idxs); |
|||
} |
|||
|
|||
module hull_pts(tri) { |
|||
hull() { |
|||
translate(tri[0]) sphere(half_thickness); |
|||
translate(tri[1]) sphere(half_thickness); |
|||
translate(tri[2]) sphere(half_thickness); |
|||
} |
|||
} |
|||
|
|||
module tri_to_hull_faces(tri1, tri2) { |
|||
hull_pts(tri1); |
|||
hull_pts(tri2); |
|||
} |
|||
|
|||
if(style == "FACES") { |
|||
faces(); |
|||
} |
|||
else if(style == "HULL_FACES") { |
|||
twintri_lt = |
|||
[ |
|||
for(yi = yi_range) |
|||
for(xi = xi_range) |
|||
[ |
|||
[ |
|||
points[yi][xi], |
|||
points[yi][xi + 1], |
|||
points[yi + 1][xi] |
|||
], |
|||
[ |
|||
points[yi + 1][xi], |
|||
points[yi][xi + 1], |
|||
points[yi + 1][xi + 1] |
|||
] |
|||
] |
|||
]; |
|||
|
|||
for(twintri = twintri_lt) { |
|||
tri_to_hull_faces(twintri[0], twintri[1]); |
|||
} |
|||
} |
|||
else { |
|||
if(style == "LINES") { |
|||
section = shape_circle(radius = half_thickness); |
|||
for(row = points) { |
|||
path_extrude(section, row); |
|||
} |
|||
|
|||
for(x = [0:columns - 1]) { |
|||
path_extrude(section, [for(y = [0:rows - 1]) points[y][x]]); |
|||
} |
|||
|
|||
for(c = [0:columns - 2]) { |
|||
path_extrude(section, [for(r = [0:rows - 1 - c]) points[r + c][r]]); |
|||
} |
|||
|
|||
for(c = [0:columns - 2]) { |
|||
path_extrude(section, [for(r = [0:rows - 1 - c]) points[r][r + c]]); |
|||
} |
|||
} |
|||
else { |
|||
for(row = points) { |
|||
hull_polyline3d(row, thickness); |
|||
} |
|||
|
|||
for(x = [0:columns - 1]) { |
|||
hull_polyline3d([for(y = [0:rows - 1]) points[y][x]], thickness); |
|||
} |
|||
|
|||
for(c = [0:columns - 2]) { |
|||
hull_polyline3d([for(r = [0:rows - 1 - c]) points[r + c][r]], thickness); |
|||
} |
|||
|
|||
for(c = [0:columns - 2]) { |
|||
hull_polyline3d([for(r = [0:rows - 1 - c]) points[r][r + c]], thickness); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// override it to test |
|||
module test_function_grapher_faces(points, faces) { |
|||
|
|||
} |
@ -0,0 +1,157 @@ |
|||
/** |
|||
* sf_solidify.scad |
|||
* |
|||
* @copyright Justin Lin, 2020 |
|||
* @license https://opensource.org/licenses/lgpl-3.0.html |
|||
* |
|||
* @see https://openhome.cc/eGossip/OpenSCAD/lib3x-sf_solidify.html |
|||
* |
|||
**/ |
|||
|
|||
use <../reverse.scad> |
|||
use <../flat.scad> |
|||
|
|||
module sf_solidify(surface1, surface2, slicing = "SLASH", convexity = 1) { |
|||
rows = len(surface1); |
|||
columns = len(surface1[0]); |
|||
|
|||
// dimensionality reduction |
|||
indices = [ |
|||
for(y = [0:rows - 1]) |
|||
let(y_cols = y * columns) |
|||
[for(x = [0:columns - 1]) y_cols + x] |
|||
]; |
|||
|
|||
flatted_sf1 = flat(surface1); |
|||
flatted_sf2 = flat(surface2); |
|||
|
|||
leng_pts = len(flatted_sf1); |
|||
|
|||
yi_range = [0:rows - 2]; |
|||
xi_range = [0:columns - 2]; |
|||
|
|||
sf1_tri_faces1 = slicing == "SLASH" ? [ |
|||
for(yi = yi_range) |
|||
let(indices_yi = indices[yi]) |
|||
for(xi = xi_range) |
|||
let(xi_1 = xi + 1) |
|||
[ |
|||
indices_yi[xi], |
|||
indices[yi + 1][xi_1], |
|||
indices_yi[xi_1] |
|||
] |
|||
] : [ |
|||
for(yi = yi_range) |
|||
let(indices_yi = indices[yi]) |
|||
for(xi = xi_range) |
|||
[ |
|||
indices_yi[xi], |
|||
indices[yi + 1][xi], |
|||
indices_yi[xi + 1] |
|||
] |
|||
]; |
|||
|
|||
sf1_tri_faces2 = slicing == "SLASH" ? [ |
|||
for(yi = yi_range) |
|||
let(indices_yi = indices[yi], indices_yi_1 = indices[yi + 1]) |
|||
for(xi = xi_range) |
|||
[ |
|||
indices_yi[xi], |
|||
indices_yi_1[xi], |
|||
indices_yi_1[xi + 1] |
|||
] |
|||
] : [ |
|||
for(yi = yi_range) |
|||
let(indices_yi = indices[yi], indices_yi_1 = indices[yi + 1]) |
|||
for(xi = xi_range) |
|||
let(xi_1 = xi + 1) |
|||
[ |
|||
indices_yi_1[xi], |
|||
indices_yi_1[xi_1], |
|||
indices_yi[xi_1] |
|||
] |
|||
]; |
|||
|
|||
offset_v = [leng_pts, leng_pts, leng_pts]; |
|||
sf2_tri_faces1 = [ |
|||
for(face = sf1_tri_faces1) |
|||
reverse(face) + offset_v |
|||
]; |
|||
|
|||
sf2_tri_faces2 = [ |
|||
for(face = sf1_tri_faces2) |
|||
reverse(face) + offset_v |
|||
]; |
|||
|
|||
indices_0 = indices[0]; |
|||
side_faces1 = [ |
|||
for(xi = xi_range) |
|||
let( |
|||
idx1 = indices_0[xi], |
|||
idx2 = indices_0[xi + 1] |
|||
) |
|||
[ |
|||
idx1, |
|||
idx2, |
|||
idx2 + leng_pts, |
|||
idx1 + leng_pts |
|||
] |
|||
]; |
|||
|
|||
last_xi = columns - 1; |
|||
side_faces2 = [ |
|||
for(yi = yi_range) |
|||
let( |
|||
idx1 = indices[yi][last_xi], |
|||
idx2 = indices[yi + 1][last_xi] |
|||
) |
|||
[ |
|||
idx1, |
|||
idx2, |
|||
idx2 + leng_pts, |
|||
idx1 + leng_pts |
|||
] |
|||
]; |
|||
|
|||
indices_last = indices[rows - 1]; |
|||
side_faces3 = [ |
|||
for(xi = xi_range) |
|||
let( |
|||
idx1 = indices_last[xi], |
|||
idx2 = indices_last[xi + 1] |
|||
) |
|||
[ |
|||
idx2, |
|||
idx1, |
|||
idx1 + leng_pts, |
|||
idx2 + leng_pts |
|||
] |
|||
]; |
|||
|
|||
side_faces4 = [ |
|||
for(yi = yi_range) |
|||
let( |
|||
idx1 = indices[yi][0], |
|||
idx2 = indices[yi + 1][0] |
|||
) |
|||
[ |
|||
idx2, |
|||
idx1, |
|||
idx1 + leng_pts, |
|||
idx2 + leng_pts |
|||
] |
|||
]; |
|||
|
|||
polyhedron( |
|||
points = concat(flatted_sf1, flatted_sf2), |
|||
faces = concat( |
|||
sf1_tri_faces1, sf1_tri_faces2, |
|||
sf2_tri_faces1, sf2_tri_faces2, |
|||
side_faces1, |
|||
side_faces2, |
|||
side_faces3, |
|||
side_faces4 |
|||
), |
|||
convexity = convexity |
|||
); |
|||
} |
@ -0,0 +1,26 @@ |
|||
/** |
|||
* sf_splines.scad |
|||
* |
|||
* @copyright Justin Lin, 2021 |
|||
* @license https://opensource.org/licenses/lgpl-3.0.html |
|||
* |
|||
* @see https://openhome.cc/eGossip/OpenSCAD/lib3x-sf_splines.html |
|||
* |
|||
**/ |
|||
|
|||
use <../matrix/m_transpose.scad> |
|||
|
|||
function sf_splines(ctrl_pts, row_spline, column_spline) = |
|||
let( |
|||
leng_ctrl_pts = len(ctrl_pts), |
|||
cspline = is_undef(column_spline) ? row_spline : column_spline, |
|||
r_pts = [ |
|||
for(r = 0; r < leng_ctrl_pts; r = r + 1) |
|||
row_spline(ctrl_pts[r]) |
|||
], |
|||
leng_r_pts0 = len(r_pts[0]) |
|||
) |
|||
m_transpose([ |
|||
for(c = 0; c < leng_r_pts0; c = c + 1) |
|||
cspline([for(pts = r_pts) pts[c]]) |
|||
]); |
@ -0,0 +1,86 @@ |
|||
/** |
|||
* sf_thicken.scad |
|||
* |
|||
* @copyright Justin Lin, 2021 |
|||
* @license https://opensource.org/licenses/lgpl-3.0.html |
|||
* |
|||
* @see https://openhome.cc/eGossip/OpenSCAD/lib3x-sf_thicken.html |
|||
* |
|||
**/ |
|||
|
|||
use <../_face_normal.scad> |
|||
use <../sum.scad> |
|||
use <../vector/unit_vector.scad> |
|||
use <../surf/sf_solidify.scad> |
|||
|
|||
module sf_thicken(points, thickness, direction = "BOTH", convexity = 1) { |
|||
// clockwise |
|||
dirs = [ |
|||
[[ 1, 0], [ 0, -1]], |
|||
[[ 0, -1], [-1, 0]], |
|||
[[-1, 0], [ 0, 1]], |
|||
[[ 0, 1], [ 1, 0]] |
|||
]; |
|||
function vertex_normal_xs(sf, x_range, yi) = |
|||
let(sfyi = sf[yi]) |
|||
[ |
|||
for(xi = x_range) |
|||
let(v0 = sfyi[xi]) |
|||
if(!is_undef(v0)) |
|||
let( |
|||
xyi = [xi, yi], |
|||
normals = [ |
|||
for(dir = dirs) |
|||
let( |
|||
p1 = dir[0] + xyi, |
|||
v1 = sf[p1.y][p1.x], |
|||
p2 = dir[1] + xyi, |
|||
v2 = sf[p2.y][p2.x] |
|||
) |
|||
if(!(is_undef(v1) || is_undef(v2))) |
|||
_face_normal([v0, v1, v2]) |
|||
] |
|||
) |
|||
sum(normals) / len(normals) |
|||
]; |
|||
|
|||
leng_points = len(points); |
|||
leng_point0 = len(points[0]); |
|||
x_range = [0:leng_point0 - 1]; |
|||
if(is_list(direction)) { |
|||
dir_v = unit_vector(direction); |
|||
dir_vs = [for(x = x_range) dir_v]; |
|||
surface_another = points + thickness * [ |
|||
for(y = [0:leng_points - 1]) |
|||
dir_vs |
|||
]; |
|||
|
|||
midy = leng_points / 2; |
|||
midx = leng_point0 / 2; |
|||
nv = _face_normal([points[midy][midx], points[midy + 1][midx], points[midy][midx + 1]]); |
|||
|
|||
if(nv * dir_v > 0) { |
|||
sf_solidify(surface_another, points, convexity = convexity); |
|||
} |
|||
else { |
|||
sf_solidify(points, surface_another, convexity = convexity); |
|||
} |
|||
} |
|||
else { |
|||
vertex_normals = [ |
|||
for(y = [0:leng_points - 1]) |
|||
vertex_normal_xs(points, x_range, y) |
|||
]; |
|||
|
|||
if(direction == "BOTH") { |
|||
off = vertex_normals * (thickness / 2); |
|||
sf_solidify(points + off, points - off, convexity = convexity); |
|||
} |
|||
else if(direction == "FORWARD") { |
|||
sf_solidify(points + thickness * vertex_normals, points, convexity = convexity); |
|||
} |
|||
else { |
|||
sf_solidify(points, points - thickness * vertex_normals, convexity = convexity); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,209 @@ |
|||
/** |
|||
* sweep.scad |
|||
* |
|||
* @copyright Justin Lin, 2020 |
|||
* @license https://opensource.org/licenses/lgpl-3.0.html |
|||
* |
|||
* @see https://openhome.cc/eGossip/OpenSCAD/lib3x-sweep.html |
|||
* |
|||
**/ |
|||
|
|||
use <reverse.scad> |
|||
|
|||
module sweep(sections, triangles = "SOLID") { |
|||
|
|||
function side_indexes(sects, begin_idx = 0) = |
|||
let( |
|||
leng_sects = len(sects), |
|||
leng_pts_sect = len(sects[0]), |
|||
range_j = [begin_idx:leng_pts_sect:begin_idx + (leng_sects - 2) * leng_pts_sect], |
|||
range_i = [0:leng_pts_sect - 1] |
|||
) |
|||
concat( |
|||
[ |
|||
for(j = range_j, i = range_i) |
|||
let(i2 = j + (i + 1) % leng_pts_sect) |
|||
[ |
|||
j + i, |
|||
i2, |
|||
i2 + leng_pts_sect |
|||
] |
|||
], |
|||
[ |
|||
for(j = range_j, i = range_i) |
|||
let(ji = j + i) |
|||
[ |
|||
ji, |
|||
j + (i + 1) % leng_pts_sect + leng_pts_sect , |
|||
ji + leng_pts_sect |
|||
] |
|||
] |
|||
); |
|||
|
|||
|
|||
function the_same_after_twisting(f_sect, l_sect) = |
|||
let(found = search([l_sect[0]], f_sect)[0], leng = len(l_sect)) |
|||
found != [] && |
|||
len([for(i = 0; l_sect[i] == f_sect[(found + i) % leng]; i = i + 1) undef]) == leng; |
|||
|
|||
function to_v_pts(sects) = [for(sect = sects) each sect]; |
|||
|
|||
module solid_sections(sects) { |
|||
leng_sects = len(sects); |
|||
leng_pts_sect = len(sects[0]); |
|||
first_sect = sects[0]; |
|||
last_sect = sects[leng_sects - 1]; |
|||
|
|||
v_pts = to_v_pts(sects); |
|||
|
|||
begin_end_the_same = |
|||
first_sect == last_sect || the_same_after_twisting(first_sect, last_sect); |
|||
|
|||
if(begin_end_the_same) { |
|||
f_idxes = side_indexes(sects); |
|||
|
|||
polyhedron(v_pts, f_idxes); |
|||
|
|||
// hook for testing |
|||
test_sweep_solid(v_pts, f_idxes, triangles); |
|||
} else { |
|||
from = leng_pts_sect * (leng_sects - 1); |
|||
f_idxes = [ |
|||
[each [leng_pts_sect - 1:-1:0]], |
|||
each side_indexes(sects), |
|||
[each [from:from + leng_pts_sect - 1]] |
|||
]; |
|||
|
|||
polyhedron(v_pts, f_idxes); |
|||
|
|||
// hook for testing |
|||
test_sweep_solid(v_pts, f_idxes, triangles); |
|||
} |
|||
} |
|||
|
|||
module hollow_sections(sects) { |
|||
leng_sects = len(sects); |
|||
leng_sect = len(sects[0]); |
|||
half_leng_sect = leng_sect / 2; |
|||
half_leng_v_pts = leng_sects * half_leng_sect; |
|||
|
|||
function strip_sects(begin_idx, end_idx) = |
|||
let(range = [begin_idx:end_idx]) |
|||
[for(sect = sects) [for(j = range) sect[j]]]; |
|||
|
|||
range = [0:half_leng_sect - 1]; |
|||
function first_idxes() = |
|||
[ |
|||
for(i = range) |
|||
let(i3 = (i + 1) % half_leng_sect) |
|||
[ |
|||
i, |
|||
i + half_leng_v_pts, |
|||
i3 + half_leng_v_pts, |
|||
i3 |
|||
] |
|||
]; |
|||
|
|||
function last_idxes(begin_idx) = |
|||
[ |
|||
for(i = range) |
|||
let(bi = begin_idx + i, i2 = begin_idx + (i + 1) % half_leng_sect) |
|||
[ |
|||
bi, |
|||
i2, |
|||
i2 + half_leng_v_pts, |
|||
bi + half_leng_v_pts |
|||
] |
|||
]; |
|||
|
|||
outer_sects = strip_sects(0, half_leng_sect - 1); |
|||
inner_sects = strip_sects(half_leng_sect, leng_sect - 1); |
|||
|
|||
outer_idxes = side_indexes(outer_sects); |
|||
inner_idxes = [for(idxes = side_indexes(inner_sects, half_leng_v_pts)) reverse(idxes)]; |
|||
|
|||
first_outer_sect = outer_sects[0]; |
|||
last_outer_sect = outer_sects[leng_sects - 1]; |
|||
first_inner_sect = inner_sects[0]; |
|||
last_inner_sect = inner_sects[leng_sects - 1]; |
|||
|
|||
leng_pts_sect = len(first_outer_sect); |
|||
|
|||
begin_end_the_same = |
|||
(first_outer_sect == last_outer_sect && first_inner_sect == last_inner_sect) || |
|||
( |
|||
the_same_after_twisting(first_outer_sect, last_outer_sect) && |
|||
the_same_after_twisting(first_inner_sect, last_inner_sect) |
|||
); |
|||
|
|||
v_pts = concat(to_v_pts(outer_sects), to_v_pts(inner_sects)); |
|||
|
|||
if(begin_end_the_same) { |
|||
f_idxes = concat(outer_idxes, inner_idxes); |
|||
|
|||
polyhedron( |
|||
v_pts, |
|||
f_idxes |
|||
); |
|||
|
|||
// hook for testing |
|||
test_sweep_solid(v_pts, f_idxes, triangles); |
|||
} else { |
|||
f_idxes = concat( |
|||
first_idxes(), |
|||
outer_idxes, |
|||
inner_idxes, |
|||
last_idxes(half_leng_v_pts - half_leng_sect) |
|||
); |
|||
|
|||
polyhedron( |
|||
v_pts, |
|||
f_idxes |
|||
); |
|||
|
|||
// hook for testing |
|||
test_sweep_solid(v_pts, f_idxes, triangles); |
|||
} |
|||
} |
|||
|
|||
module triangles_defined_sections() { |
|||
faces = [ |
|||
[0, 1, 2], [3, 5, 4], |
|||
[1, 3, 4], [2, 1, 4], [2, 3, 0], |
|||
[0, 3, 1], [2, 4, 5], [2, 5, 3] |
|||
]; |
|||
module two_sections(section1, section2) { |
|||
for(idx = triangles) { |
|||
polyhedron( |
|||
concat( |
|||
[for(i = idx) section1[i]], |
|||
[for(i = idx) section2[i]] |
|||
), |
|||
faces |
|||
); |
|||
} |
|||
} |
|||
|
|||
for(i = [0:len(sections) - 2]) { |
|||
two_sections( |
|||
sections[i], |
|||
sections[i + 1] |
|||
); |
|||
} |
|||
} |
|||
|
|||
if(triangles == "SOLID") { |
|||
solid_sections(sections); |
|||
} else if(triangles == "HOLLOW") { |
|||
hollow_sections(sections); |
|||
} |
|||
else { |
|||
triangles_defined_sections(); |
|||
} |
|||
} |
|||
|
|||
// override it to test |
|||
|
|||
module test_sweep_solid(points, faces, triangles) { |
|||
|
|||
} |
@ -0,0 +1,3 @@ |
|||
use <unit_vector.scad> |
|||
|
|||
function _face_normal(points) = unit_vector(cross(points[2] - points[0], points[1] - points[0])); |
@ -0,0 +1,6 @@ |
|||
function __to_3_elems_ang_vect(a) = |
|||
let(leng = len(a)) |
|||
leng == 3 ? a : |
|||
leng == 2 ? [each a, 0] : [a.x, 0, 0]; |
|||
|
|||
function to_ang_vec(a) = is_num(a) ? [0, 0, a] : __to_3_elems_ang_vect(a); |
@ -0,0 +1 @@ |
|||
function unit_vector(v) = v / norm(v); |
@ -0,0 +1,42 @@ |
|||
include <../vector/to_ang_vec.scad> |
|||
function __v_rotation_xRotation(a) = |
|||
let(c = cos(a), s = sin(a)) |
|||
[ |
|||
[1, 0, 0], |
|||
[0, c, -s], |
|||
[0, s, c], |
|||
]; |
|||
|
|||
function __v_rotation_yRotation(a) = |
|||
let(c = cos(a), s = sin(a)) |
|||
[ |
|||
[ c, 0, s], |
|||
[ 0, 1, 0], |
|||
[-s, 0, c], |
|||
]; |
|||
|
|||
function __v_rotation_zRotation(a) = |
|||
let(c = cos(a), s = sin(a)) |
|||
[ |
|||
[c, -s, 0], |
|||
[s, c, 0], |
|||
[0, 0, 1], |
|||
]; |
|||
|
|||
function __v_rotation_xyz_rotation(a) = |
|||
let(ang = to_ang_vec(a)) |
|||
__v_rotation_zRotation(ang[2]) * __v_rotation_yRotation(ang[1]) * __v_rotation_xRotation(ang[0]); |
|||
|
|||
function _v_rotation_impl(a) = |
|||
(a == 0 || a == [0, 0, 0] || a == [0] || a == [0, 0]) ? [ |
|||
[1, 0, 0], |
|||
[0, 1, 0], |
|||
[0, 0, 1], |
|||
] : __v_rotation_xyz_rotation(a); |
|||
|
|||
function v_rotation(a) = _v_rotation_impl(a); |
|||
function v_rot(a) = _v_rotation_impl(a); |
|||
|
|||
|
|||
|
|||
function zero_axis(x,p,v=0) = [x!="x"?p[0]:v, x!="y"?p[1]:v,x!="z"?p[2]:v]; |
Write
Preview
Loading…
Cancel
Save
Reference in new issue