Browse Source

inital add

main
phschoen 2 years ago
commit
6c01a1200c
  1. 3
      .gitmodules
  2. 55
      config.scad
  3. 25
      constant.scad
  4. 1
      extern/KeyV2
  5. 20
      keycap_gen.scad
  6. 80
      keycap_stl.scad
  7. 682
      keycaps.scad
  8. 135
      kinessis.scad
  9. 86
      main.scad
  10. 11
      pcb/amoeba-royale.scad
  11. 6
      pcb/rp2040.scad
  12. BIN
      stl/keycaps/mt3/mt3_r1_u1.stl
  13. BIN
      stl/keycaps/mt3/mt3_r2_u1.stl
  14. BIN
      stl/keycaps/mt3/mt3_r3_u1.stl
  15. BIN
      stl/keycaps/mt3/mt3_r4_u1.stl
  16. BIN
      stl/keycaps/mt3/mt3_r5_u1.stl
  17. BIN
      stl/keycaps/sa/sa_r1_u1.stl
  18. BIN
      stl/keycaps/sa/sa_r2_u1.stl
  19. BIN
      stl/keycaps/sa/sa_r3_u1.stl
  20. BIN
      stl/keycaps/sa/sa_r4_u1.stl
  21. BIN
      stl/keycaps/sa/sa_r5_u1.stl
  22. BIN
      stl/pcb/rp2040.stl
  23. 160
      switch.scad
  24. 92
      switch_cutout.scad
  25. 121
      switch_holder.scad
  26. 220
      switch_holder_plate.scad
  27. 6
      util/__angy_angz.scad
  28. 4
      util/__frags.scad
  29. 1
      util/__to3d.scad
  30. 3
      util/_face_normal.scad
  31. 31
      util/bezier/_bezier_curve_impl.scad
  32. 15
      util/bezier/bezier_curve.scad
  33. 29
      util/bezier/bezier_surface.scad
  34. 13
      util/flat.scad
  35. 41
      util/hull_polyline3d.scad
  36. 62
      util/matrix/_m_rotation_impl.scad
  37. 16
      util/matrix/_m_scaling_impl.scad
  38. 15
      util/matrix/_m_translation_impl.scad
  39. 13
      util/matrix/m_rotation.scad
  40. 13
      util/matrix/m_scaling.scad
  41. 13
      util/matrix/m_translation.scad
  42. 22
      util/matrix/m_transpose.scad
  43. 177
      util/path_extrude.scad
  44. 11
      util/reverse.scad
  45. 22
      util/shape_circle.scad
  46. 20
      util/sum.scad
  47. 234
      util/surf/function_grapher.scad
  48. 157
      util/surf/sf_solidify.scad
  49. 26
      util/surf/sf_spline.scad
  50. 86
      util/surf/sf_thicken.scad
  51. 209
      util/sweep.scad
  52. 3
      util/vector/face_normal.scad
  53. 6
      util/vector/to_ang_vec.scad
  54. 1
      util/vector/unit_vector.scad
  55. 42
      util/vector/v_rot.scad

3
.gitmodules

@ -0,0 +1,3 @@
[submodule "KeyV2"]
path = extern/KeyV2
url = https://github.com/rsheldiii/KeyV2.git

55
config.scad

@ -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;

25
constant.scad

@ -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;

1
extern/KeyV2

@ -0,0 +1 @@
Subproject commit 19de89dc6ec9eff87b4683c869dc3aeaa9a5f1b1

20
keycap_gen.scad

@ -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(); */

80
keycap_stl.scad

@ -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);
}

682
keycaps.scad

@ -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);
}
}
}

135
kinessis.scad

@ -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); }
}

86
main.scad

@ -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

6
pcb/rp2040.scad

@ -0,0 +1,6 @@
module rp2040_pcb() {
rotate([90,0,0])
import("../stl/pcb/rp2040.stl");
}
rp2040_pcb();

BIN
stl/keycaps/mt3/mt3_r1_u1.stl

BIN
stl/keycaps/mt3/mt3_r2_u1.stl

BIN
stl/keycaps/mt3/mt3_r3_u1.stl

BIN
stl/keycaps/mt3/mt3_r4_u1.stl

BIN
stl/keycaps/mt3/mt3_r5_u1.stl

BIN
stl/keycaps/sa/sa_r1_u1.stl

BIN
stl/keycaps/sa/sa_r2_u1.stl

BIN
stl/keycaps/sa/sa_r3_u1.stl

BIN
stl/keycaps/sa/sa_r4_u1.stl

BIN
stl/keycaps/sa/sa_r5_u1.stl

BIN
stl/pcb/rp2040.stl

160
switch.scad

@ -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);
}
}
}

92
switch_cutout.scad

@ -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);
}
}

121
switch_holder.scad

@ -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]);
}

220
switch_holder_plate.scad

@ -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);
}

6
util/__angy_angz.scad

@ -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)
];

4
util/__frags.scad

@ -0,0 +1,4 @@
function __frags(radius) =
$fn == 0 ?
max(min(360 / $fa, radius * 6.283185307179586 / $fs), 5) :
max($fn, 3);

1
util/__to3d.scad

@ -0,0 +1 @@
function __to3d(p) = [each p, 0];

3
util/_face_normal.scad

@ -0,0 +1,3 @@
use <vector/unit_vector.scad>
function _face_normal(points) = unit_vector(cross(points[2] - points[0], points[1] - points[0]));

31
util/bezier/_bezier_curve_impl.scad

@ -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)
];

15
util/bezier/bezier_curve.scad

@ -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);

29
util/bezier/bezier_surface.scad

@ -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]]
)
]);

13
util/flat.scad

@ -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)];

41
util/hull_polyline3d.scad

@ -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) {
}

62
util/matrix/_m_rotation_impl.scad

@ -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));

16
util/matrix/_m_scaling_impl.scad

@ -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
];

15
util/matrix/_m_translation_impl.scad

@ -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]
];

13
util/matrix/m_rotation.scad

@ -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);

13
util/matrix/m_scaling.scad

@ -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);

13
util/matrix/m_translation.scad

@ -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);

22
util/matrix/m_transpose.scad

@ -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]
]
];

177
util/path_extrude.scad

@ -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) {
}

11
util/reverse.scad

@ -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]];

22
util/shape_circle.scad

@ -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)]
];

20
util/sum.scad

@ -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];

234
util/surf/function_grapher.scad

@ -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) {
}

157
util/surf/sf_solidify.scad

@ -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
);
}

26
util/surf/sf_spline.scad

@ -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]])
]);

86
util/surf/sf_thicken.scad

@ -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);
}
}
}

209
util/sweep.scad

@ -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) {
}

3
util/vector/face_normal.scad

@ -0,0 +1,3 @@
use <unit_vector.scad>
function _face_normal(points) = unit_vector(cross(points[2] - points[0], points[1] - points[0]));

6
util/vector/to_ang_vec.scad

@ -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);

1
util/vector/unit_vector.scad

@ -0,0 +1 @@
function unit_vector(v) = v / norm(v);

42
util/vector/v_rot.scad

@ -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];
Loading…
Cancel
Save