convert all tabs to spaces

This commit is contained in:
Robert Sheldon 2018-06-04 21:13:09 -04:00
parent bc70ca109b
commit 67a830c489
33 changed files with 1172 additions and 1177 deletions

View File

@ -236,19 +236,19 @@ unit = 19.05;
// corollary is rounded_square // corollary is rounded_square
// NOT 3D // NOT 3D
module ISO_enter_shape(size, delta, progress){ module ISO_enter_shape(size, delta, progress){
width = size[0]; width = size[0];
height = size[1]; height = size[1];
function unit_length(length) = unit * (length - 1) + 18.16; function unit_length(length) = unit * (length - 1) + 18.16;
// in order to make the ISO keycap shape generic, we are going to express the // in order to make the ISO keycap shape generic, we are going to express the
// 'elbow point' in terms of ratios. an ISO enter is just a 1.5u key stuck on // 'elbow point' in terms of ratios. an ISO enter is just a 1.5u key stuck on
// top of a 1.25u key, but since our key_shape function doesnt understand that // top of a 1.25u key, but since our key_shape function doesnt understand that
// and wants to pass just width and height, we make these ratios to know where // and wants to pass just width and height, we make these ratios to know where
// to put the elbow joint // to put the elbow joint
width_ratio = unit_length(1.25) / unit_length(1.5); width_ratio = unit_length(1.25) / unit_length(1.5);
height_ratio = unit_length(1) / unit_length(2); height_ratio = unit_length(1) / unit_length(2);
pointArray = [ pointArray = [
[ 0, 0], // top right [ 0, 0], // top right
@ -259,13 +259,13 @@ module ISO_enter_shape(size, delta, progress){
[ -width, 0] // top left [ -width, 0] // top left
]; ];
minkowski(){ minkowski(){
circle(r=corner_size); circle(r=corner_size);
// gives us rounded inner corner // gives us rounded inner corner
offset(r=-corner_size*2) { offset(r=-corner_size*2) {
translate([(width * width_ratio)/2, height/2]) polygon(points=pointArray); translate([(width * width_ratio)/2, height/2]) polygon(points=pointArray);
} }
} }
} }
// side sculpting functions // side sculpting functions
@ -275,30 +275,30 @@ function side_sculpting(progress) = (1 - progress) * 2.5;
function corner_sculpting(progress) = pow(progress, 2); function corner_sculpting(progress) = pow(progress, 2);
module rounded_square_shape(size, delta, progress, center = true) { module rounded_square_shape(size, delta, progress, center = true) {
width = size[0]; width = size[0];
height = size[1]; height = size[1];
width_difference = delta[0]; width_difference = delta[0];
height_difference = delta[1]; height_difference = delta[1];
// makes the sides bow // makes the sides bow
extra_side_size = $enable_side_sculpting ? side_sculpting(progress) : 0; extra_side_size = $enable_side_sculpting ? side_sculpting(progress) : 0;
// makes the rounded corners of the keycap grow larger as they move upwards // makes the rounded corners of the keycap grow larger as they move upwards
extra_corner_size = $enable_side_sculpting ? corner_sculpting(progress) : 0; extra_corner_size = $enable_side_sculpting ? corner_sculpting(progress) : 0;
// computed values for this slice // computed values for this slice
extra_width_this_slice = (width_difference - extra_side_size) * progress; extra_width_this_slice = (width_difference - extra_side_size) * progress;
extra_height_this_slice = (height_difference - extra_side_size) * progress; extra_height_this_slice = (height_difference - extra_side_size) * progress;
extra_corner_radius_this_slice = ($corner_radius + extra_corner_size); extra_corner_radius_this_slice = ($corner_radius + extra_corner_size);
offset(r=extra_corner_radius_this_slice){ offset(r=extra_corner_radius_this_slice){
square( square(
[ [
width - extra_width_this_slice - extra_corner_radius_this_slice * 2, width - extra_width_this_slice - extra_corner_radius_this_slice * 2,
height - extra_height_this_slice - extra_corner_radius_this_slice * 2 height - extra_height_this_slice - extra_corner_radius_this_slice * 2
], ],
center=center center=center
); );
} }
} }
@ -308,30 +308,30 @@ module square_shape(size, delta, progress){
module oblong_shape(size, delta, progress) { module oblong_shape(size, delta, progress) {
// .05 is because of offset. if we set offset to be half the height of the shape, and then subtract height from the shape, the height of the shape will be zero (because the shape would be [width - height, height - height]). that doesn't play well with openSCAD (understandably), so we add this tiny fudge factor to make sure the shape we offset has a positive width // .05 is because of offset. if we set offset to be half the height of the shape, and then subtract height from the shape, the height of the shape will be zero (because the shape would be [width - height, height - height]). that doesn't play well with openSCAD (understandably), so we add this tiny fudge factor to make sure the shape we offset has a positive width
height = size[1] - delta[1] * progress - .05; height = size[1] - delta[1] * progress - .05;
if (progress < 0.5) { if (progress < 0.5) {
} else { } else {
offset(r=height / 2) { offset(r=height / 2) {
square(size - [height, height] - delta * progress, center=true); square(size - [height, height] - delta * progress, center=true);
} }
} }
} }
module key_shape(size, delta, progress = 0) { module key_shape(size, delta, progress = 0) {
if ($key_shape_type == "iso_enter") { if ($key_shape_type == "iso_enter") {
ISO_enter_shape(size, delta, progress); ISO_enter_shape(size, delta, progress);
} else if ($key_shape_type == "rounded_square") { } else if ($key_shape_type == "rounded_square") {
rounded_square_shape(size, delta, progress); rounded_square_shape(size, delta, progress);
} else if ($key_shape_type == "square") { } else if ($key_shape_type == "square") {
square_shape(size, delta, progress); square_shape(size, delta, progress);
} else if ($key_shape_type == "oblong") { } else if ($key_shape_type == "oblong") {
oblong_shape(size, delta, progress); oblong_shape(size, delta, progress);
} else { } else {
echo("Warning: unsupported $key_shape_type"); echo("Warning: unsupported $key_shape_type");
} }
} }
module cherry_stem(depth, has_brim) { module cherry_stem(depth, has_brim) {
@ -406,13 +406,13 @@ module filled_stem() {
//whole stem, alps or cherry, trimmed to fit //whole stem, alps or cherry, trimmed to fit
module stem(stem_type, depth, has_brim){ module stem(stem_type, depth, has_brim){
if (stem_type == "alps") { if (stem_type == "alps") {
alps_stem(depth, has_brim); alps_stem(depth, has_brim);
} else if (stem_type == "cherry_rounded") { } else if (stem_type == "cherry_rounded") {
rounded_cherry_stem(depth, has_brim); rounded_cherry_stem(depth, has_brim);
} else if (stem_type == "cherry") { } else if (stem_type == "cherry") {
cherry_stem(depth, has_brim); cherry_stem(depth, has_brim);
} else if (stem_type == "filled") { } else if (stem_type == "filled") {
filled_stem(); filled_stem();
} else { } else {
echo("Warning: unsupported $stem_type"); echo("Warning: unsupported $stem_type");
@ -420,18 +420,18 @@ module stem(stem_type, depth, has_brim){
} }
module cylindrical_dish(width, height, depth, inverted){ module cylindrical_dish(width, height, depth, inverted){
// .5 has problems starting around 3u // .5 has problems starting around 3u
$fa=.25; $fa=.25;
/* we do some funky math here /* we do some funky math here
* basically you want to have the dish "dig in" to the keycap x millimeters * 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 * 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 * where the chord of the spherical cross section of the dish is
* the width of the keycap. * the width of the keycap.
*/ */
// the distance you have to move the dish so it digs in depth millimeters // the distance you have to move the dish so it digs in depth millimeters
chord_length = (pow(width, 2) - 4 * pow(depth, 2)) / (8 * depth); chord_length = (pow(width, 2) - 4 * pow(depth, 2)) / (8 * depth);
//the radius of the dish //the radius of the dish
rad = (pow(width, 2) + 4 * pow(depth, 2)) / (8 * depth); rad = (pow(width, 2) + 4 * pow(depth, 2)) / (8 * depth);
direction = inverted ? -1 : 1; direction = inverted ? -1 : 1;
translate([0,0, chord_length * direction]){ translate([0,0, chord_length * direction]){
@ -444,64 +444,64 @@ module cylindrical_dish(width, height, depth, inverted){
// much more graduated curvature at an immense cost // much more graduated curvature at an immense cost
module old_spherical_dish(width, height, depth, inverted){ module old_spherical_dish(width, height, depth, inverted){
//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 //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(width,2) + pow(height, 2)),0.5); //getting diagonal of the top chord = pow((pow(width,2) + pow(height, 2)),0.5); //getting diagonal of the top
// the distance you have to move the dish up so it digs in depth millimeters // the distance you have to move the dish up so it digs in depth millimeters
chord_length = (pow(chord, 2) - 4 * pow(depth, 2)) / (8 * depth); chord_length = (pow(chord, 2) - 4 * pow(depth, 2)) / (8 * depth);
//the radius of the dish //the radius of the dish
rad = (pow(chord, 2) + 4 * pow(depth, 2)) / (8 * depth); rad = (pow(chord, 2) + 4 * pow(depth, 2)) / (8 * depth);
direction = inverted ? -1 : 1; direction = inverted ? -1 : 1;
translate([0,0,chord_length * direction]){ translate([0,0,chord_length * direction]){
if (geodesic){ if (geodesic){
$fa=7; $fa=7;
geodesic_sphere(r=rad); geodesic_sphere(r=rad);
} else { } else {
$fa=1; $fa=1;
// rotate 1 because the bottom of the sphere looks like trash // rotate 1 because the bottom of the sphere looks like trash
sphere(r=rad); sphere(r=rad);
} }
} }
} }
module sideways_cylindrical_dish(width, height, depth, inverted){ module sideways_cylindrical_dish(width, height, depth, inverted){
$fa=1; $fa=1;
chord_length = (pow(height, 2) - 4 * pow(depth, 2)) / (8 * depth); chord_length = (pow(height, 2) - 4 * pow(depth, 2)) / (8 * depth);
rad = (pow(height, 2) + 4 * pow(depth, 2)) / (8 * depth); rad = (pow(height, 2) + 4 * pow(depth, 2)) / (8 * depth);
direction = inverted ? -1 : 1; direction = inverted ? -1 : 1;
translate([0,0, chord_length * direction]){ translate([0,0, chord_length * direction]){
// cylinder is rendered facing up, so we rotate it on the y axis first // cylinder is rendered facing up, so we rotate it on the y axis first
rotate([0,90,0]) cylinder(h = width + 20,r=rad, center=true); // +20 for fudge factor rotate([0,90,0]) cylinder(h = width + 20,r=rad, center=true); // +20 for fudge factor
} }
} }
module spherical_dish(width, height, depth, inverted){ module spherical_dish(width, height, depth, inverted){
//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 //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(width,2) + pow(height, 2)),0.5); //getting diagonal of the top chord = pow((pow(width,2) + pow(height, 2)),0.5); //getting diagonal of the top
// the distance you have to move the dish up so it digs in depth millimeters // the distance you have to move the dish up so it digs in depth millimeters
chord_length = (pow(chord, 2) - 4 * pow(depth, 2)) / (8 * depth); chord_length = (pow(chord, 2) - 4 * pow(depth, 2)) / (8 * depth);
//the radius of the dish //the radius of the dish
rad = (pow(chord, 2) + 4 * pow(depth, 2)) / (8 * depth); rad = (pow(chord, 2) + 4 * pow(depth, 2)) / (8 * depth);
direction = inverted ? -1 : 1; direction = inverted ? -1 : 1;
translate([0,0,0 * direction]){ translate([0,0,0 * direction]){
if (geodesic){ if (geodesic){
$fa=20; $fa=20;
scale([chord/2/depth, chord/2/depth]) { scale([chord/2/depth, chord/2/depth]) {
geodesic_sphere(r=depth); geodesic_sphere(r=depth);
} }
} else { } else {
$fa=7; $fa=7;
// rotate 1 because the bottom of the sphere looks like trash. // rotate 1 because the bottom of the sphere looks like trash.
scale([chord/2/depth, chord/2/depth]) { scale([chord/2/depth, chord/2/depth]) {
geodesic_sphere(r=depth); geodesic_sphere(r=depth);
} }
} }
} }
} }
@ -510,22 +510,22 @@ geodesic=false;
//dish selector //dish selector
module dish(width, height, depth, inverted) { module dish(width, height, depth, inverted) {
if($dish_type == "cylindrical"){ if($dish_type == "cylindrical"){
cylindrical_dish(width, height, depth, inverted); cylindrical_dish(width, height, depth, inverted);
} }
else if ($dish_type == "spherical") { else if ($dish_type == "spherical") {
spherical_dish(width, height, depth, inverted); spherical_dish(width, height, depth, inverted);
} }
else if ($dish_type == "sideways cylindrical"){ else if ($dish_type == "sideways cylindrical"){
sideways_cylindrical_dish(width, height, depth, inverted); sideways_cylindrical_dish(width, height, depth, inverted);
} }
else if ($dish_type == "old spherical") { else if ($dish_type == "old spherical") {
old_spherical_dish(width, height, depth, inverted); old_spherical_dish(width, height, depth, inverted);
} else { } else {
// else no dish, "no dish" is the value // else no dish, "no dish" is the value
// switchted to actually diffing a cube here due to changes to stems being differenced from the dish instead of the inside // switchted to actually diffing a cube here due to changes to stems being differenced from the dish instead of the inside
translate([0,0,500]) cube([width, height, 1000], center=true); translate([0,0,500]) cube([width, height, 1000], center=true);
} }
} }
// figures out the scale factor needed to make a 45 degree wall // figures out the scale factor needed to make a 45 degree wall
@ -601,25 +601,25 @@ function top_total_key_height() = $bottom_key_height + (unit * ($key_height - 1)
// key shape including dish. used as the ouside and inside shape in keytop(). allows for itself to be shrunk in depth and width / height // key shape including dish. used as the ouside and inside shape in keytop(). allows for itself to be shrunk in depth and width / height
module shape(thickness_difference, depth_difference){ module shape(thickness_difference, depth_difference){
dished(depth_difference, $inverted_dish) { dished(depth_difference, $inverted_dish) {
color(color1) shape_hull(thickness_difference, depth_difference, 1); color(color1) shape_hull(thickness_difference, depth_difference, 1);
} }
} }
// shape of the key but with soft, rounded edges. much more realistic, MUCH more complex. orders of magnitude more complex // shape of the key but with soft, rounded edges. much more realistic, MUCH more complex. orders of magnitude more complex
module rounded_shape() { module rounded_shape() {
render(){ render(){
color(color1) minkowski(){ color(color1) minkowski(){
// half minkowski. that means the shape is neither circumscribed nor inscribed. // half minkowski. that means the shape is neither circumscribed nor inscribed.
shape($minkowski_radius * 2, $minkowski_radius/2); shape($minkowski_radius * 2, $minkowski_radius/2);
difference(){ difference(){
sphere(r=$minkowski_radius, $fn=24); sphere(r=$minkowski_radius, $fn=24);
translate([0,0,-$minkowski_radius]){ translate([0,0,-$minkowski_radius]){
cube($minkowski_radius * 2, center=true); cube($minkowski_radius * 2, center=true);
} }
} }
} }
} }
} }
@ -628,227 +628,227 @@ module rounded_shape() {
// $height_difference used for keytop thickness // $height_difference used for keytop thickness
// extra_slices is a hack to make inverted dishes still work // extra_slices is a hack to make inverted dishes still work
module shape_hull(thickness_difference, depth_difference, extra_slices = 0){ module shape_hull(thickness_difference, depth_difference, extra_slices = 0){
render() { render() {
if ($linear_extrude_shape) { if ($linear_extrude_shape) {
linear_extrude_shape_hull(thickness_difference, depth_difference, extra_slices); linear_extrude_shape_hull(thickness_difference, depth_difference, extra_slices);
} else { } else {
hull_shape_hull(thickness_difference, depth_difference, extra_slices); hull_shape_hull(thickness_difference, depth_difference, extra_slices);
} }
} }
} }
// corollary is hull_shape_hull // corollary is hull_shape_hull
// extra_slices unused, only to match argument signatures // extra_slices unused, only to match argument signatures
module linear_extrude_shape_hull(thickness_difference, depth_difference, extra_slices = 0){ module linear_extrude_shape_hull(thickness_difference, depth_difference, extra_slices = 0){
height = $total_depth - depth_difference; height = $total_depth - depth_difference;
width_scale = top_total_key_width() / total_key_width(); width_scale = top_total_key_width() / total_key_width();
height_scale = top_total_key_height() / total_key_height(); height_scale = top_total_key_height() / total_key_height();
translate([0,$linear_extrude_height_adjustment,0]){ translate([0,$linear_extrude_height_adjustment,0]){
linear_extrude(height = height, scale = [width_scale, height_scale]) { linear_extrude(height = height, scale = [width_scale, height_scale]) {
translate([0,-$linear_extrude_height_adjustment,0]){ translate([0,-$linear_extrude_height_adjustment,0]){
key_shape(total_key_width(thickness_difference), total_key_height(thickness_difference)); key_shape(total_key_width(thickness_difference), total_key_height(thickness_difference));
} }
} }
} }
} }
module hull_shape_hull(thickness_difference, depth_difference, extra_slices = 0) { module hull_shape_hull(thickness_difference, depth_difference, extra_slices = 0) {
for (index = [0:$height_slices - 1 + extra_slices]) { for (index = [0:$height_slices - 1 + extra_slices]) {
hull() { hull() {
shape_slice(index / $height_slices, thickness_difference, depth_difference); shape_slice(index / $height_slices, thickness_difference, depth_difference);
shape_slice((index + 1) / $height_slices, thickness_difference, depth_difference); shape_slice((index + 1) / $height_slices, thickness_difference, depth_difference);
} }
} }
} }
module shape_slice(progress, thickness_difference, depth_difference) { module shape_slice(progress, thickness_difference, depth_difference) {
skew_this_slice = $top_skew * progress; skew_this_slice = $top_skew * progress;
depth_this_slice = ($total_depth - depth_difference) * progress; depth_this_slice = ($total_depth - depth_difference) * progress;
tilt_this_slice = -$top_tilt / $key_height * progress; tilt_this_slice = -$top_tilt / $key_height * progress;
translate([0, skew_this_slice, depth_this_slice]) { translate([0, skew_this_slice, depth_this_slice]) {
rotate([tilt_this_slice,0,0]){ rotate([tilt_this_slice,0,0]){
linear_extrude(height = 0.001){ linear_extrude(height = 0.001){
key_shape( key_shape(
[ [
total_key_width(thickness_difference), total_key_width(thickness_difference),
total_key_height(thickness_difference) total_key_height(thickness_difference)
], ],
[$width_difference, $height_difference], [$width_difference, $height_difference],
progress progress
); );
} }
} }
} }
} }
// for when you want something to only exist inside the keycap. // for when you want something to only exist inside the keycap.
// used for the support structure // used for the support structure
module inside() { module inside() {
intersection() { intersection() {
shape($wall_thickness, $keytop_thickness); shape($wall_thickness, $keytop_thickness);
children(); children();
} }
} }
// put something at the top of the key, with no adjustments for dishing // put something at the top of the key, with no adjustments for dishing
module top_placement(depth_difference) { module top_placement(depth_difference) {
translate([$dish_skew_x, $top_skew + $dish_skew_y, $total_depth - depth_difference]){ translate([$dish_skew_x, $top_skew + $dish_skew_y, $total_depth - depth_difference]){
rotate([-$top_tilt / top_total_key_height(),0,0]){ rotate([-$top_tilt / top_total_key_height(),0,0]){
children(); children();
} }
} }
} }
// just to DRY up the code // just to DRY up the code
module _dish() { module _dish() {
color(color3) dish(top_total_key_width() + $dish_overdraw_width, top_total_key_height() + $dish_overdraw_height, $dish_depth, $inverted_dish); color(color3) dish(top_total_key_width() + $dish_overdraw_width, top_total_key_height() + $dish_overdraw_height, $dish_depth, $inverted_dish);
} }
// for when you want to take the dish out of things // for when you want to take the dish out of things
// used for adding the dish to the key shape and making sure stems don't stick out the top // used for adding the dish to the key shape and making sure stems don't stick out the top
module dished(depth_difference, inverted = false) { module dished(depth_difference, inverted = false) {
difference() { difference() {
children(); children();
top_placement(depth_difference){ top_placement(depth_difference){
difference(){ difference(){
union() { union() {
translate([-500, -500]) cube(1000); translate([-500, -500]) cube(1000);
if (!inverted) _dish(); if (!inverted) _dish();
} }
if (inverted) _dish(); if (inverted) _dish();
} }
} }
} }
} }
// puts it's children at the center of the dishing on the key, including dish height // puts it's children at the center of the dishing on the key, including dish height
// more user-friendly than top_placement // more user-friendly than top_placement
module top_of_key(){ module top_of_key(){
// if there is a dish, we need to account for how much it digs into the top // if there is a dish, we need to account for how much it digs into the top
dish_depth = ($dish_type == "no dish") ? 0 : $dish_depth; dish_depth = ($dish_type == "no dish") ? 0 : $dish_depth;
// if the dish is inverted, we need to account for that too. in this case we do half, otherwise the children would be floating on top of the dish // if the dish is inverted, we need to account for that too. in this case we do half, otherwise the children would be floating on top of the dish
corrected_dish_depth = ($inverted_dish) ? -dish_depth / 2 : dish_depth; corrected_dish_depth = ($inverted_dish) ? -dish_depth / 2 : dish_depth;
top_placement(corrected_dish_depth) { top_placement(corrected_dish_depth) {
children(); children();
} }
} }
module keytext(text, depth = 0) { module keytext(text, depth = 0) {
translate([0, 0, -depth]){ translate([0, 0, -depth]){
linear_extrude(height=$dish_depth){ linear_extrude(height=$dish_depth){
text(text=text, font=$font, size=$font_size, halign="center", valign="center"); text(text=text, font=$font, size=$font_size, halign="center", valign="center");
} }
} }
} }
module keystem_positions() { module keystem_positions() {
for (connector_pos = $connectors) { for (connector_pos = $connectors) {
translate(connector_pos) { translate(connector_pos) {
rotate([0, 0, $stem_rotation]){ rotate([0, 0, $stem_rotation]){
children(); children();
} }
} }
} }
} }
module keystems() { module keystems() {
keystem_positions() { keystem_positions() {
color(color4) stem($stem_type, $total_depth, $has_brim); color(color4) stem($stem_type, $total_depth, $has_brim);
} }
} }
module keystem_supports() { module keystem_supports() {
keystem_positions() { keystem_positions() {
color(color4) supports($support_type, $stem_type, $stem_throw, $total_depth - $stem_throw); color(color4) supports($support_type, $stem_type, $stem_throw, $total_depth - $stem_throw);
} }
} }
// a fake cherry keyswitch, abstracted out to maybe replace with a better one later // a fake cherry keyswitch, abstracted out to maybe replace with a better one later
module cherry_keyswitch() { module cherry_keyswitch() {
union() { union() {
hull() { hull() {
cube([15.6, 15.6, 0.01], center=true); cube([15.6, 15.6, 0.01], center=true);
translate([0,1,5 - 0.01]) cube([10.5,9.5, 0.01], center=true); translate([0,1,5 - 0.01]) cube([10.5,9.5, 0.01], center=true);
} }
hull() { hull() {
cube([15.6, 15.6, 0.01], center=true); cube([15.6, 15.6, 0.01], center=true);
translate([0,0,-5.5]) cube([13.5,13.5,0.01], center=true); translate([0,0,-5.5]) cube([13.5,13.5,0.01], center=true);
} }
} }
} }
//approximate (fully depressed) cherry key to check clearances //approximate (fully depressed) cherry key to check clearances
module clearance_check() { module clearance_check() {
if($stem_type == "cherry" || $stem_type == "cherry_rounded"){ if($stem_type == "cherry" || $stem_type == "cherry_rounded"){
color(transparent_red){ color(transparent_red){
translate([0,0,3.6 + $stem_inset - 5]) { translate([0,0,3.6 + $stem_inset - 5]) {
cherry_keyswitch(); cherry_keyswitch();
} }
} }
} }
} }
// legends / artisan support // legends / artisan support
module artisan(legend, depth) { module artisan(legend, depth) {
top_of_key() { top_of_key() {
// outset legend // outset legend
if (legend != "") keytext(legend, depth); if (legend != "") keytext(legend, depth);
// artisan objects / outset shape legends // artisan objects / outset shape legends
children(); children();
} }
} }
// key with hollowed inside but no stem // key with hollowed inside but no stem
module keytop() { module keytop() {
difference(){ difference(){
if ($rounded_key) { if ($rounded_key) {
rounded_shape(); rounded_shape();
} else { } else {
shape(0, 0); shape(0, 0);
} }
// translation purely for aesthetic purposes, to get rid of that awful lattice // translation purely for aesthetic purposes, to get rid of that awful lattice
translate([0,0,-0.005]) { translate([0,0,-0.005]) {
shape($wall_thickness, $keytop_thickness); shape($wall_thickness, $keytop_thickness);
} }
} }
} }
// The final, penultimate key generation function. // The final, penultimate key generation function.
// takes all the bits and glues them together. requires configuration with special variables. // takes all the bits and glues them together. requires configuration with special variables.
module key(legend = "", inset = false) { module key(legend = "", inset = false) {
difference() { difference() {
union(){ union(){
// the shape of the key, inside and out // the shape of the key, inside and out
keytop(); keytop();
// additive objects at the top of the key // additive objects at the top of the key
if(!inset) artisan(legend) children(); if(!inset) artisan(legend) children();
// render the clearance check if it's enabled, but don't have it intersect with anything // render the clearance check if it's enabled, but don't have it intersect with anything
if ($clearance_check) %clearance_check(); if ($clearance_check) %clearance_check();
} }
// subtractive objects at the top of the key // subtractive objects at the top of the key
if (inset) artisan(legend, 0.3) children(); if (inset) artisan(legend, 0.3) children();
// subtract the clearance check if it's enabled, letting the user see the // subtract the clearance check if it's enabled, letting the user see the
// parts of the keycap that will hit the cherry switch // parts of the keycap that will hit the cherry switch
if ($clearance_check) clearance_check(); if ($clearance_check) clearance_check();
} }
// both stem and support are optional // both stem and support are optional
if ($stem_type){ if ($stem_type){
dished($keytop_thickness, $inverted_dish) { dished($keytop_thickness, $inverted_dish) {
translate([0, 0, $stem_inset]) keystems(); translate([0, 0, $stem_inset]) keystems();
} }
} }
if ($support_type){ if ($support_type){
inside() { inside() {
translate([0, 0, $stem_inset]) keystem_supports(); translate([0, 0, $stem_inset]) keystem_supports();
} }
} }
} }
// key width functions // key width functions
@ -925,11 +925,11 @@ module 6_25uh() {
// unlike the other files with their own dedicated folders, this one doesn't need a selector. it just collects all the functions // unlike the other files with their own dedicated folders, this one doesn't need a selector. it just collects all the functions
module dcs_row(n=1) { module dcs_row(n=1) {
// names, so I don't go crazy // names, so I don't go crazy
$bottom_key_width = 18.16; $bottom_key_width = 18.16;
$bottom_key_height = 18.16; $bottom_key_height = 18.16;
$width_difference = 6; $width_difference = 6;
$height_difference = 4; $height_difference = 4;
$dish_type = "cylindrical"; $dish_type = "cylindrical";
$dish_depth = 1; $dish_depth = 1;
$dish_skew_x = 0; $dish_skew_x = 0;
@ -960,16 +960,16 @@ module dcs_row(n=1) {
} }
module oem_row(n=1) { module oem_row(n=1) {
$bottom_key_width = 18.05; $bottom_key_width = 18.05;
$bottom_key_height = 18.05; $bottom_key_height = 18.05;
$width_difference = 5.8; $width_difference = 5.8;
$height_difference = 4; $height_difference = 4;
$dish_type = "cylindrical"; $dish_type = "cylindrical";
$dish_depth = 1; $dish_depth = 1;
$dish_skew_x = 0; $dish_skew_x = 0;
$dish_skew_y = 0; $dish_skew_y = 0;
$top_skew = 1.75; $top_skew = 1.75;
$stem_inset = 1.2; $stem_inset = 1.2;
if (n == 5) { if (n == 5) {
$total_depth = 11.2; $total_depth = 11.2;
@ -995,41 +995,41 @@ module oem_row(n=1) {
} }
module dsa_row(n=3) { module dsa_row(n=3) {
$bottom_key_width = 18.24; // 18.4; $bottom_key_width = 18.24; // 18.4;
$bottom_key_height = 18.24; // 18.4; $bottom_key_height = 18.24; // 18.4;
$width_difference = 6; // 5.7; $width_difference = 6; // 5.7;
$height_difference = 6; // 5.7; $height_difference = 6; // 5.7;
$total_depth = 8.1 + abs((n-3) * 1); $total_depth = 8.1 + abs((n-3) * 1);
$top_tilt = (n-3) * -7; $top_tilt = (n-3) * -7;
$top_skew = 0; $top_skew = 0;
$dish_type = "spherical"; $dish_type = "spherical";
$dish_depth = 1.2; $dish_depth = 1.2;
$dish_skew_x = 0; $dish_skew_x = 0;
$dish_skew_y = 0; $dish_skew_y = 0;
$height_slices = 10; $height_slices = 10;
$enable_side_sculpting = true; $enable_side_sculpting = true;
// might wanna change this if you don't minkowski // might wanna change this if you don't minkowski
// do you even minkowski bro // do you even minkowski bro
$corner_radius = 0.25; $corner_radius = 0.25;
children(); children();
} }
module sa_row(n=1) { module sa_row(n=1) {
$bottom_key_width = 18.4; $bottom_key_width = 18.4;
$bottom_key_height = 18.4; $bottom_key_height = 18.4;
$width_difference = 5.7; $width_difference = 5.7;
$height_difference = 5.7; $height_difference = 5.7;
$dish_type = "spherical"; $dish_type = "spherical";
$dish_depth = 0.85; $dish_depth = 0.85;
$dish_skew_x = 0; $dish_skew_x = 0;
$dish_skew_y = 0; $dish_skew_y = 0;
$top_skew = 0; $top_skew = 0;
$height_slices = 10; $height_slices = 10;
$enable_side_sculpting = true; $enable_side_sculpting = true;
// might wanna change this if you don't minkowski // might wanna change this if you don't minkowski
// do you even minkowski bro // do you even minkowski bro
$corner_radius = 0.25; $corner_radius = 0.25;
if (n == 1){ if (n == 1){
$total_depth = 14.89; $total_depth = 14.89;
@ -1051,24 +1051,24 @@ module sa_row(n=1) {
} }
module g20_row(n=3) { module g20_row(n=3) {
$bottom_key_width = 18.16; $bottom_key_width = 18.16;
$bottom_key_height = 18.16; $bottom_key_height = 18.16;
$width_difference = 2; $width_difference = 2;
$height_difference = 2; $height_difference = 2;
$total_depth = 6; $total_depth = 6;
$top_tilt = 2.5; $top_tilt = 2.5;
$top_tilt = (n-3) * -7 + 2.5; $top_tilt = (n-3) * -7 + 2.5;
$top_skew = 0.75; $top_skew = 0.75;
$dish_type = "no dish"; $dish_type = "no dish";
$dish_depth = 0; $dish_depth = 0;
$dish_skew_x = 0; $dish_skew_x = 0;
$dish_skew_y = 0; $dish_skew_y = 0;
$minkowski_radius = 1.75; $minkowski_radius = 1.75;
//also, //also,
/*$rounded_key = true;*/ /*$rounded_key = true;*/
children(); children();
} }
// man, wouldn't it be so cool if functions were first order // man, wouldn't it be so cool if functions were first order
@ -1089,7 +1089,7 @@ module key_profile(key_profile_type, row) {
module spacebar() { module spacebar() {
$inverted_dish = true; $inverted_dish = true;
$dish_type = "sideways cylindrical"; $dish_type = "sideways cylindrical";
6_25u() stabilized(mm=50) children(); 6_25u() stabilized(mm=50) children();
} }
module lshift() { module lshift() {
@ -1130,20 +1130,20 @@ module stepped_caps_lock() {
} }
module iso_enter() { module iso_enter() {
$key_length = 1.5; $key_length = 1.5;
$key_height = 2; $key_height = 2;
$top_tilt = 0; $top_tilt = 0;
$key_shape_type = "iso_enter"; $key_shape_type = "iso_enter";
$linear_extrude_shape = true; $linear_extrude_shape = true;
$linear_extrude_height_adjustment = 19.05 * 0.5; $linear_extrude_height_adjustment = 19.05 * 0.5;
// (unit_length(1.5) - unit_length(1.25)) / 2 // (unit_length(1.5) - unit_length(1.25)) / 2
$dish_overdraw_width = 2.38125; $dish_overdraw_width = 2.38125;
stabilized(vertical=true) { stabilized(vertical=true) {
children(); children();
} }
} }

View File

@ -10,20 +10,20 @@ geodesic=false;
//dish selector //dish selector
module dish(width, height, depth, inverted) { module dish(width, height, depth, inverted) {
if($dish_type == "cylindrical"){ if($dish_type == "cylindrical"){
cylindrical_dish(width, height, depth, inverted); cylindrical_dish(width, height, depth, inverted);
} }
else if ($dish_type == "spherical") { else if ($dish_type == "spherical") {
spherical_dish(width, height, depth, inverted); spherical_dish(width, height, depth, inverted);
} }
else if ($dish_type == "sideways cylindrical"){ else if ($dish_type == "sideways cylindrical"){
sideways_cylindrical_dish(width, height, depth, inverted); sideways_cylindrical_dish(width, height, depth, inverted);
} }
else if ($dish_type == "old spherical") { else if ($dish_type == "old spherical") {
old_spherical_dish(width, height, depth, inverted); old_spherical_dish(width, height, depth, inverted);
} else { } else {
// else no dish, "no dish" is the value // else no dish, "no dish" is the value
// switchted to actually diffing a cube here due to changes to stems being differenced from the dish instead of the inside // switchted to actually diffing a cube here due to changes to stems being differenced from the dish instead of the inside
translate([0,0,500]) cube([width, height, 1000], center=true); translate([0,0,500]) cube([width, height, 1000], center=true);
} }
} }

View File

@ -1,16 +1,16 @@
module cylindrical_dish(width, height, depth, inverted){ module cylindrical_dish(width, height, depth, inverted){
// .5 has problems starting around 3u // .5 has problems starting around 3u
$fa=.25; $fa=.25;
/* we do some funky math here /* we do some funky math here
* basically you want to have the dish "dig in" to the keycap x millimeters * 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 * 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 * where the chord of the spherical cross section of the dish is
* the width of the keycap. * the width of the keycap.
*/ */
// the distance you have to move the dish so it digs in depth millimeters // the distance you have to move the dish so it digs in depth millimeters
chord_length = (pow(width, 2) - 4 * pow(depth, 2)) / (8 * depth); chord_length = (pow(width, 2) - 4 * pow(depth, 2)) / (8 * depth);
//the radius of the dish //the radius of the dish
rad = (pow(width, 2) + 4 * pow(depth, 2)) / (8 * depth); rad = (pow(width, 2) + 4 * pow(depth, 2)) / (8 * depth);
direction = inverted ? -1 : 1; direction = inverted ? -1 : 1;
translate([0,0, chord_length * direction]){ translate([0,0, chord_length * direction]){

View File

@ -3,23 +3,23 @@
// much more graduated curvature at an immense cost // much more graduated curvature at an immense cost
module old_spherical_dish(width, height, depth, inverted){ module old_spherical_dish(width, height, depth, inverted){
//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 //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(width,2) + pow(height, 2)),0.5); //getting diagonal of the top chord = pow((pow(width,2) + pow(height, 2)),0.5); //getting diagonal of the top
// the distance you have to move the dish up so it digs in depth millimeters // the distance you have to move the dish up so it digs in depth millimeters
chord_length = (pow(chord, 2) - 4 * pow(depth, 2)) / (8 * depth); chord_length = (pow(chord, 2) - 4 * pow(depth, 2)) / (8 * depth);
//the radius of the dish //the radius of the dish
rad = (pow(chord, 2) + 4 * pow(depth, 2)) / (8 * depth); rad = (pow(chord, 2) + 4 * pow(depth, 2)) / (8 * depth);
direction = inverted ? -1 : 1; direction = inverted ? -1 : 1;
translate([0,0,chord_length * direction]){ translate([0,0,chord_length * direction]){
if (geodesic){ if (geodesic){
$fa=7; $fa=7;
geodesic_sphere(r=rad); geodesic_sphere(r=rad);
} else { } else {
$fa=1; $fa=1;
// rotate 1 because the bottom of the sphere looks like trash // rotate 1 because the bottom of the sphere looks like trash
sphere(r=rad); sphere(r=rad);
} }
} }
} }

View File

@ -1,12 +1,12 @@
module sideways_cylindrical_dish(width, height, depth, inverted){ module sideways_cylindrical_dish(width, height, depth, inverted){
$fa=1; $fa=1;
chord_length = (pow(height, 2) - 4 * pow(depth, 2)) / (8 * depth); chord_length = (pow(height, 2) - 4 * pow(depth, 2)) / (8 * depth);
rad = (pow(height, 2) + 4 * pow(depth, 2)) / (8 * depth); rad = (pow(height, 2) + 4 * pow(depth, 2)) / (8 * depth);
direction = inverted ? -1 : 1; direction = inverted ? -1 : 1;
translate([0,0, chord_length * direction]){ translate([0,0, chord_length * direction]){
// cylinder is rendered facing up, so we rotate it on the y axis first // cylinder is rendered facing up, so we rotate it on the y axis first
rotate([0,90,0]) cylinder(h = width + 20,r=rad, center=true); // +20 for fudge factor rotate([0,90,0]) cylinder(h = width + 20,r=rad, center=true); // +20 for fudge factor
} }
} }

View File

@ -1,26 +1,26 @@
module spherical_dish(width, height, depth, inverted){ module spherical_dish(width, height, depth, inverted){
//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 //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(width,2) + pow(height, 2)),0.5); //getting diagonal of the top chord = pow((pow(width,2) + pow(height, 2)),0.5); //getting diagonal of the top
// the distance you have to move the dish up so it digs in depth millimeters // the distance you have to move the dish up so it digs in depth millimeters
chord_length = (pow(chord, 2) - 4 * pow(depth, 2)) / (8 * depth); chord_length = (pow(chord, 2) - 4 * pow(depth, 2)) / (8 * depth);
//the radius of the dish //the radius of the dish
rad = (pow(chord, 2) + 4 * pow(depth, 2)) / (8 * depth); rad = (pow(chord, 2) + 4 * pow(depth, 2)) / (8 * depth);
direction = inverted ? -1 : 1; direction = inverted ? -1 : 1;
translate([0,0,0 * direction]){ translate([0,0,0 * direction]){
if (geodesic){ if (geodesic){
$fa=20; $fa=20;
scale([chord/2/depth, chord/2/depth]) { scale([chord/2/depth, chord/2/depth]) {
geodesic_sphere(r=depth); geodesic_sphere(r=depth);
} }
} else { } else {
$fa=6.5; $fa=6.5;
// rotate 1 because the bottom of the sphere looks like trash. // rotate 1 because the bottom of the sphere looks like trash.
scale([chord/2/depth, chord/2/depth]) { scale([chord/2/depth, chord/2/depth]) {
sphere(r=depth); sphere(r=depth);
} }
} }
} }
} }

View File

@ -1,7 +1,7 @@
module keybump(depth = 0, edge_inset=0.4) { module keybump(depth = 0, edge_inset=0.4) {
radius = 0.5; radius = 0.5;
translate([0, -top_total_key_height()/2 + edge_inset, depth]){ translate([0, -top_total_key_height()/2 + edge_inset, depth]){
rotate([90,0,90]) cylinder($font_size, radius, radius, true); rotate([90,0,90]) cylinder($font_size, radius, radius, true);
translate([0,0,-radius]) cube([$font_size, radius*2, radius*2], true); translate([0,0,-radius]) cube([$font_size, radius*2, radius*2], true);
} }
} }

View File

@ -30,23 +30,23 @@ function top_total_key_height() = $bottom_key_height + (unit * ($key_height - 1)
// key shape including dish. used as the ouside and inside shape in keytop(). allows for itself to be shrunk in depth and width / height // key shape including dish. used as the ouside and inside shape in keytop(). allows for itself to be shrunk in depth and width / height
module shape(thickness_difference, depth_difference){ module shape(thickness_difference, depth_difference){
dished(depth_difference, $inverted_dish) { dished(depth_difference, $inverted_dish) {
color(color1) shape_hull(thickness_difference, depth_difference, 2); color(color1) shape_hull(thickness_difference, depth_difference, 2);
} }
} }
// shape of the key but with soft, rounded edges. much more realistic, MUCH more complex. orders of magnitude more complex // shape of the key but with soft, rounded edges. much more realistic, MUCH more complex. orders of magnitude more complex
module rounded_shape() { module rounded_shape() {
color(color1) minkowski(){ color(color1) minkowski(){
// half minkowski in the z direction // half minkowski in the z direction
shape($minkowski_radius * 2, $minkowski_radius/2); shape($minkowski_radius * 2, $minkowski_radius/2);
difference(){ difference(){
sphere(r=$minkowski_radius, $fn=20); sphere(r=$minkowski_radius, $fn=20);
translate([0,0,-$minkowski_radius]){ translate([0,0,-$minkowski_radius]){
cube($minkowski_radius * 2, center=true); cube($minkowski_radius * 2, center=true);
} }
} }
} }
} }
@ -55,239 +55,239 @@ module rounded_shape() {
// $height_difference used for keytop thickness // $height_difference used for keytop thickness
// extra_slices is a hack to make inverted dishes still work // extra_slices is a hack to make inverted dishes still work
module shape_hull(thickness_difference, depth_difference, extra_slices = 0){ module shape_hull(thickness_difference, depth_difference, extra_slices = 0){
render() { render() {
if ($linear_extrude_shape) { if ($linear_extrude_shape) {
linear_extrude_shape_hull(thickness_difference, depth_difference, extra_slices); linear_extrude_shape_hull(thickness_difference, depth_difference, extra_slices);
} else { } else {
hull_shape_hull(thickness_difference, depth_difference, extra_slices); hull_shape_hull(thickness_difference, depth_difference, extra_slices);
} }
} }
} }
// corollary is hull_shape_hull // corollary is hull_shape_hull
// extra_slices unused, only to match argument signatures // extra_slices unused, only to match argument signatures
module linear_extrude_shape_hull(thickness_difference, depth_difference, extra_slices = 0){ module linear_extrude_shape_hull(thickness_difference, depth_difference, extra_slices = 0){
height = $total_depth - depth_difference; height = $total_depth - depth_difference;
width_scale = top_total_key_width() / total_key_width(); width_scale = top_total_key_width() / total_key_width();
height_scale = top_total_key_height() / total_key_height(); height_scale = top_total_key_height() / total_key_height();
translate([0,$linear_extrude_height_adjustment,0]){ translate([0,$linear_extrude_height_adjustment,0]){
linear_extrude(height = height, scale = [width_scale, height_scale]) { linear_extrude(height = height, scale = [width_scale, height_scale]) {
translate([0,-$linear_extrude_height_adjustment,0]){ translate([0,-$linear_extrude_height_adjustment,0]){
key_shape(total_key_width(thickness_difference), total_key_height(thickness_difference)); key_shape(total_key_width(thickness_difference), total_key_height(thickness_difference));
} }
} }
} }
} }
module hull_shape_hull(thickness_difference, depth_difference, extra_slices = 0) { module hull_shape_hull(thickness_difference, depth_difference, extra_slices = 0) {
for (index = [0:$height_slices - 1 + extra_slices]) { for (index = [0:$height_slices - 1 + extra_slices]) {
hull() { hull() {
shape_slice(index / $height_slices, thickness_difference, depth_difference); shape_slice(index / $height_slices, thickness_difference, depth_difference);
shape_slice((index + 1) / $height_slices, thickness_difference, depth_difference); shape_slice((index + 1) / $height_slices, thickness_difference, depth_difference);
} }
} }
} }
module shape_slice(progress, thickness_difference, depth_difference) { module shape_slice(progress, thickness_difference, depth_difference) {
skew_this_slice = $top_skew * progress; skew_this_slice = $top_skew * progress;
depth_this_slice = ($total_depth - depth_difference) * progress; depth_this_slice = ($total_depth - depth_difference) * progress;
tilt_this_slice = -$top_tilt / $key_height * progress; tilt_this_slice = -$top_tilt / $key_height * progress;
translate([0, skew_this_slice, depth_this_slice]) { translate([0, skew_this_slice, depth_this_slice]) {
rotate([tilt_this_slice,0,0]){ rotate([tilt_this_slice,0,0]){
linear_extrude(height = 0.001){ linear_extrude(height = 0.001){
key_shape( key_shape(
[ [
total_key_width(thickness_difference), total_key_width(thickness_difference),
total_key_height(thickness_difference) total_key_height(thickness_difference)
], ],
[$width_difference, $height_difference], [$width_difference, $height_difference],
progress progress
); );
} }
} }
} }
} }
// for when you want something to only exist inside the keycap. // for when you want something to only exist inside the keycap.
// used for the support structure // used for the support structure
module inside() { module inside() {
intersection() { intersection() {
shape($wall_thickness, $keytop_thickness); shape($wall_thickness, $keytop_thickness);
children(); children();
} }
} }
// put something at the top of the key, with no adjustments for dishing // put something at the top of the key, with no adjustments for dishing
module top_placement(depth_difference) { module top_placement(depth_difference) {
translate([$dish_skew_x, $top_skew + $dish_skew_y, $total_depth - depth_difference]){ translate([$dish_skew_x, $top_skew + $dish_skew_y, $total_depth - depth_difference]){
rotate([-$top_tilt / $key_height,0,0]){ rotate([-$top_tilt / $key_height,0,0]){
children(); children();
} }
} }
} }
// just to DRY up the code // just to DRY up the code
module _dish() { module _dish() {
color(color3) dish(top_total_key_width() + $dish_overdraw_width, top_total_key_height() + $dish_overdraw_height, $dish_depth, $inverted_dish); color(color3) dish(top_total_key_width() + $dish_overdraw_width, top_total_key_height() + $dish_overdraw_height, $dish_depth, $inverted_dish);
} }
// for when you want to take the dish out of things // for when you want to take the dish out of things
// used for adding the dish to the key shape and making sure stems don't stick out the top // used for adding the dish to the key shape and making sure stems don't stick out the top
module dished(depth_difference, inverted = false) { module dished(depth_difference, inverted = false) {
difference() { difference() {
children(); children();
top_placement(depth_difference){ top_placement(depth_difference){
difference(){ difference(){
union() { union() {
translate([-500, -500]) cube(1000); translate([-500, -500]) cube(1000);
if (!inverted) _dish(); if (!inverted) _dish();
} }
if (inverted) _dish(); if (inverted) _dish();
} }
} }
} }
} }
// puts it's children at the center of the dishing on the key, including dish height // puts it's children at the center of the dishing on the key, including dish height
// more user-friendly than top_placement // more user-friendly than top_placement
module top_of_key(){ module top_of_key(){
// if there is a dish, we need to account for how much it digs into the top // if there is a dish, we need to account for how much it digs into the top
dish_depth = ($dish_type == "no dish") ? 0 : $dish_depth; dish_depth = ($dish_type == "no dish") ? 0 : $dish_depth;
// if the dish is inverted, we need to account for that too. in this case we do half, otherwise the children would be floating on top of the dish // if the dish is inverted, we need to account for that too. in this case we do half, otherwise the children would be floating on top of the dish
corrected_dish_depth = ($inverted_dish) ? -dish_depth / 2 : dish_depth; corrected_dish_depth = ($inverted_dish) ? -dish_depth / 2 : dish_depth;
top_placement(corrected_dish_depth) { top_placement(corrected_dish_depth) {
children(); children();
} }
} }
module keytext(text, position, font_size, depth) { module keytext(text, position, font_size, depth) {
woffset = (top_total_key_width()/3.5) * position[0]; woffset = (top_total_key_width()/3.5) * position[0];
hoffset = (top_total_key_height()/3.5) * -position[1]; hoffset = (top_total_key_height()/3.5) * -position[1];
translate([woffset, hoffset, -depth]){ translate([woffset, hoffset, -depth]){
linear_extrude(height=$dish_depth){ linear_extrude(height=$dish_depth){
text(text=text, font=$font, size=font_size, halign="center", valign="center"); text(text=text, font=$font, size=font_size, halign="center", valign="center");
} }
} }
} }
module keystem_positions() { module keystem_positions() {
for (connector_pos = $connectors) { for (connector_pos = $connectors) {
translate(connector_pos) { translate(connector_pos) {
rotate([0, 0, $stem_rotation]){ rotate([0, 0, $stem_rotation]){
children(); children();
} }
} }
} }
} }
module keystems() { module keystems() {
keystem_positions() { keystem_positions() {
color(color4) stem($stem_type, $total_depth, $has_brim); color(color4) stem($stem_type, $total_depth, $has_brim);
} }
} }
module keystem_supports() { module keystem_supports() {
keystem_positions() { keystem_positions() {
color(color4) supports($support_type, $stem_type, $stem_throw, $total_depth - $stem_throw); color(color4) supports($support_type, $stem_type, $stem_throw, $total_depth - $stem_throw);
} }
} }
// a fake cherry keyswitch, abstracted out to maybe replace with a better one later // a fake cherry keyswitch, abstracted out to maybe replace with a better one later
module cherry_keyswitch() { module cherry_keyswitch() {
union() { union() {
hull() { hull() {
cube([15.6, 15.6, 0.01], center=true); cube([15.6, 15.6, 0.01], center=true);
translate([0,1,5 - 0.01]) cube([10.5,9.5, 0.01], center=true); translate([0,1,5 - 0.01]) cube([10.5,9.5, 0.01], center=true);
} }
hull() { hull() {
cube([15.6, 15.6, 0.01], center=true); cube([15.6, 15.6, 0.01], center=true);
translate([0,0,-5.5]) cube([13.5,13.5,0.01], center=true); translate([0,0,-5.5]) cube([13.5,13.5,0.01], center=true);
} }
} }
} }
//approximate (fully depressed) cherry key to check clearances //approximate (fully depressed) cherry key to check clearances
module clearance_check() { module clearance_check() {
if($stem_type == "cherry" || $stem_type == "cherry_rounded"){ if($stem_type == "cherry" || $stem_type == "cherry_rounded"){
color(transparent_red){ color(transparent_red){
translate([0,0,3.6 + $stem_inset - 5]) { translate([0,0,3.6 + $stem_inset - 5]) {
cherry_keyswitch(); cherry_keyswitch();
} }
} }
} }
} }
// legends / artisan support // legends / artisan support
module artisan(depth) { module artisan(depth) {
top_of_key() { top_of_key() {
// outset legend // outset legend
for (i=[0:len($legends)-1]) { for (i=[0:len($legends)-1]) {
keytext($legends[i][0], $legends[i][1], $legends[i][2], depth); keytext($legends[i][0], $legends[i][1], $legends[i][2], depth);
} }
// artisan objects / outset shape legends // artisan objects / outset shape legends
children(); children();
} }
} }
// key with hollowed inside but no stem // key with hollowed inside but no stem
module keytop() { module keytop() {
difference(){ difference(){
if ($rounded_key) { if ($rounded_key) {
rounded_shape(); rounded_shape();
} else { } else {
shape(0, 0); shape(0, 0);
} }
// translation purely for aesthetic purposes, to get rid of that awful lattice // translation purely for aesthetic purposes, to get rid of that awful lattice
translate([0,0,-0.005]) { translate([0,0,-0.005]) {
shape($wall_thickness, $keytop_thickness); shape($wall_thickness, $keytop_thickness);
} }
} }
} }
// The final, penultimate key generation function. // The final, penultimate key generation function.
// takes all the bits and glues them together. requires configuration with special variables. // takes all the bits and glues them together. requires configuration with special variables.
module key(inset = false) { module key(inset = false) {
difference() { difference() {
union(){ union(){
// the shape of the key, inside and out // the shape of the key, inside and out
keytop(); keytop();
if($key_bump) top_of_key() keybump($key_bump_depth, $key_bump_edge); if($key_bump) top_of_key() keybump($key_bump_depth, $key_bump_edge);
// additive objects at the top of the key // additive objects at the top of the key
if(!inset) artisan() children(); if(!inset) artisan() children();
// render the clearance check if it's enabled, but don't have it intersect with anything // render the clearance check if it's enabled, but don't have it intersect with anything
if ($clearance_check) %clearance_check(); if ($clearance_check) %clearance_check();
} }
// subtractive objects at the top of the key // subtractive objects at the top of the key
if (inset) artisan(0.3) children(); if (inset) artisan(0.3) children();
// subtract the clearance check if it's enabled, letting the user see the // subtract the clearance check if it's enabled, letting the user see the
// parts of the keycap that will hit the cherry switch // parts of the keycap that will hit the cherry switch
if ($clearance_check) clearance_check(); if ($clearance_check) clearance_check();
} }
// both stem and support are optional // both stem and support are optional
if ($stem_type){ if ($stem_type){
dished($keytop_thickness, $inverted_dish) { dished($keytop_thickness, $inverted_dish) {
translate([0, 0, $stem_inset]) keystems(); translate([0, 0, $stem_inset]) keystems();
} }
} }
if ($support_type){ if ($support_type){
inside() { inside() {
translate([0, 0, $stem_inset]) keystem_supports(); translate([0, 0, $stem_inset]) keystem_supports();
} }
} }
} }
// actual full key with space carved out and keystem/stabilizer connectors // actual full key with space carved out and keystem/stabilizer connectors
// this is an example key with all the fixins from settings.scad // this is an example key with all the fixins from settings.scad
module example_key(){ module example_key(){
include <settings.scad> include <settings.scad>
key(); key();
} }
example_key(); example_key();

View File

@ -1,9 +1,9 @@
module dcs_row(n=1) { module dcs_row(n=1) {
// names, so I don't go crazy // names, so I don't go crazy
$bottom_key_width = 18.16; $bottom_key_width = 18.16;
$bottom_key_height = 18.16; $bottom_key_height = 18.16;
$width_difference = 6; $width_difference = 6;
$height_difference = 4; $height_difference = 4;
$dish_type = "cylindrical"; $dish_type = "cylindrical";
$dish_depth = 1; $dish_depth = 1;
$dish_skew_x = 0; $dish_skew_x = 0;

View File

@ -1,21 +1,21 @@
module dsa_row(n=3) { module dsa_row(n=3) {
depth_raisers = [0, 3.5, 1, 0, 1, 3]; depth_raisers = [0, 3.5, 1, 0, 1, 3];
$bottom_key_width = 18.24; // 18.4; $bottom_key_width = 18.24; // 18.4;
$bottom_key_height = 18.24; // 18.4; $bottom_key_height = 18.24; // 18.4;
$width_difference = 6; // 5.7; $width_difference = 6; // 5.7;
$height_difference = 6; // 5.7; $height_difference = 6; // 5.7;
$total_depth = 8.1 + depth_raisers[n]; $total_depth = 8.1 + depth_raisers[n];
$top_tilt = n == 5 ? -21 : (n-3) * 7; $top_tilt = n == 5 ? -21 : (n-3) * 7;
$top_skew = 0; $top_skew = 0;
$dish_type = "spherical"; $dish_type = "spherical";
$dish_depth = 1.2; $dish_depth = 1.2;
$dish_skew_x = 0; $dish_skew_x = 0;
$dish_skew_y = 0; $dish_skew_y = 0;
$height_slices = 10; $height_slices = 10;
$enable_side_sculpting = true; $enable_side_sculpting = true;
// might wanna change this if you don't minkowski // might wanna change this if you don't minkowski
// do you even minkowski bro // do you even minkowski bro
$corner_radius = 0.25; $corner_radius = 0.25;
children(); children();
} }

View File

@ -1,22 +1,22 @@
module g20_row(n=3) { module g20_row(n=3) {
$bottom_key_width = 18.16; $bottom_key_width = 18.16;
$bottom_key_height = 18.16; $bottom_key_height = 18.16;
$width_difference = 2; $width_difference = 2;
$height_difference = 2; $height_difference = 2;
$total_depth = 6 + abs((n-3) * 0.5); $total_depth = 6 + abs((n-3) * 0.5);
$top_tilt = 2.5; $top_tilt = 2.5;
$top_tilt = n == 5 ? -18.5 : (n-3) * 7 + 2.5; $top_tilt = n == 5 ? -18.5 : (n-3) * 7 + 2.5;
$top_skew = 0.75; $top_skew = 0.75;
$dish_type = "no dish"; $dish_type = "no dish";
$dish_depth = 0; $dish_depth = 0;
$dish_skew_x = 0; $dish_skew_x = 0;
$dish_skew_y = 0; $dish_skew_y = 0;
$minkowski_radius = 1.75; $minkowski_radius = 1.75;
$key_bump_depth = 0.6; $key_bump_depth = 0.6;
$key_bump_edge = 2; $key_bump_edge = 2;
//also, //also,
$rounded_key = true; $rounded_key = true;
children(); children();
} }

View File

@ -1,14 +1,14 @@
module oem_row(n=1) { module oem_row(n=1) {
$bottom_key_width = 18.05; $bottom_key_width = 18.05;
$bottom_key_height = 18.05; $bottom_key_height = 18.05;
$width_difference = 5.8; $width_difference = 5.8;
$height_difference = 4; $height_difference = 4;
$dish_type = "cylindrical"; $dish_type = "cylindrical";
$dish_depth = 1; $dish_depth = 1;
$dish_skew_x = 0; $dish_skew_x = 0;
$dish_skew_y = 0; $dish_skew_y = 0;
$top_skew = 1.75; $top_skew = 1.75;
$stem_inset = 1.2; $stem_inset = 1.2;
if (n == 5) { if (n == 5) {
$total_depth = 11.2; $total_depth = 11.2;

View File

@ -1,22 +1,22 @@
module sa_row(n=1) { module sa_row(n=1) {
$bottom_key_width = 18.4; $bottom_key_width = 18.4;
$bottom_key_height = 18.4; $bottom_key_height = 18.4;
$width_difference = 5.7; $width_difference = 5.7;
$height_difference = 5.7; $height_difference = 5.7;
$dish_type = "spherical"; $dish_type = "spherical";
$dish_depth = 0.85; $dish_depth = 0.85;
$dish_skew_x = 0; $dish_skew_x = 0;
$dish_skew_y = 0; $dish_skew_y = 0;
$top_skew = 0; $top_skew = 0;
$height_slices = 10; $height_slices = 10;
$enable_side_sculpting = true; $enable_side_sculpting = true;
// might wanna change this if you don't minkowski // might wanna change this if you don't minkowski
// do you even minkowski bro // do you even minkowski bro
$corner_radius = 0.25; $corner_radius = 0.25;
// 5th row is usually unsculpted or the same as the row below it // 5th row is usually unsculpted or the same as the row below it
// making a super-sculpted top row (or bottom row!) would be real easy // making a super-sculpted top row (or bottom row!) would be real easy
// bottom row would just be 13 tilt and 14.89 total depth // bottom row would just be 13 tilt and 14.89 total depth
// top row would be something new entirely - 18 tilt maybe? // top row would be something new entirely - 18 tilt maybe?
if (n == 1 || n == 5){ if (n == 1 || n == 5){
$total_depth = 14.89; $total_depth = 14.89;
$top_tilt = -13; $top_tilt = -13;

View File

@ -24,8 +24,8 @@ module inverted() {
} }
module rotated() { module rotated() {
$stem_rotation = 90; $stem_rotation = 90;
children(); children();
} }
module stabilized(mm=12, vertical = false) { module stabilized(mm=12, vertical = false) {
@ -54,13 +54,13 @@ module dishless() {
} }
module inset(val=1) { module inset(val=1) {
$stem_inset = val; $stem_inset = val;
children(); children();
} }
module filled() { module filled() {
$stem_type = "filled"; $stem_type = "filled";
children(); children();
} }
module blank() { module blank() {
@ -69,19 +69,19 @@ module blank() {
} }
module cherry(slop = 0.3) { module cherry(slop = 0.3) {
$stem_slop = slop; $stem_slop = slop;
$stem_type = "cherry"; $stem_type = "cherry";
children(); children();
} }
module alps(slop = 0.3) { module alps(slop = 0.3) {
$stem_slop = slop; $stem_slop = slop;
$stem_type = "alps"; $stem_type = "alps";
children(); children();
} }
module rounded_cherry(slop = 0.3) { module rounded_cherry(slop = 0.3) {
$stem_slop = slop; $stem_slop = slop;
$stem_type = "cherry_rounded"; $stem_type = "cherry_rounded";
children(); children();
} }

View File

@ -1,7 +1,7 @@
module spacebar() { module spacebar() {
$inverted_dish = true; $inverted_dish = true;
$dish_type = "sideways cylindrical"; $dish_type = "sideways cylindrical";
6_25u() stabilized(mm=50) children(); 6_25u() stabilized(mm=50) children();
} }
module lshift() { module lshift() {
@ -42,18 +42,18 @@ module stepped_caps_lock() {
} }
module iso_enter() { module iso_enter() {
$key_length = 1.5; $key_length = 1.5;
$key_height = 2; $key_height = 2;
$top_tilt = 0; $top_tilt = 0;
$key_shape_type = "iso_enter"; $key_shape_type = "iso_enter";
$linear_extrude_shape = true; $linear_extrude_shape = true;
$linear_extrude_height_adjustment = 19.05 * 0.5; $linear_extrude_height_adjustment = 19.05 * 0.5;
// (unit_length(1.5) - unit_length(1.25)) / 2 // (unit_length(1.5) - unit_length(1.25)) / 2
$dish_overdraw_width = 2.38125; $dish_overdraw_width = 2.38125;
stabilized(vertical=true) { stabilized(vertical=true) {
children(); children();
} }
} }

View File

@ -16,8 +16,8 @@ contains basic 2D morphology operations
- positive values of d places the shell on the outside - positive values of d places the shell on the outside
- negative values of d places the shell on the inside - negative values of d places the shell on the inside
- center=true and positive d places the shell centered on the edge - center=true and positive d places the shell centered on the edge
### Examples ### Examples
With a basic sample polygon shape, With a basic sample polygon shape,
@ -72,10 +72,9 @@ contains simple mirroring functions
mirror_x() mirror_x()
mirror_y() mirror_y()
mirror_z() mirror_z()
example: example:
module arrow(l=1,w=.6,t=0.15) { module arrow(l=1,w=.6,t=0.15) {
mirror_y() polygon([[0,0],[l,0],[l-w/2,w/2],[l-w/2-sqrt(2)*t,w/2],[l-t/2-sqrt(2)*t,t/2],[0,t/2]]); mirror_y() polygon([[0,0],[l,0],[l-w/2,w/2],[l-w/2-sqrt(2)*t,w/2],[l-t/2-sqrt(2)*t,t/2],[0,t/2]]);
} }

View File

@ -1,204 +1,204 @@
// NOTE: this code uses // NOTE: this code uses
// * experimental let() syntax // * experimental let() syntax
// * experimental list comprehension syntax // * experimental list comprehension syntax
// * search() bugfix and feature addition // * search() bugfix and feature addition
// * vector min()/max() // * vector min()/max()
// Calculates the convex hull of a set of points. // Calculates the convex hull of a set of points.
// The result is expressed in point indices. // The result is expressed in point indices.
// If the points are collinear (or 2d), the result is a convex // If the points are collinear (or 2d), the result is a convex
// polygon [i1,i2,i3,...], otherwise a triangular // polygon [i1,i2,i3,...], otherwise a triangular
// polyhedron [[i1,i2,i3],[i2,i3,i4],...] // polyhedron [[i1,i2,i3],[i2,i3,i4],...]
function hull(points) = function hull(points) =
!(len(points) > 0) ? [] : !(len(points) > 0) ? [] :
len(points[0]) == 2 ? convexhull2d(points) : len(points[0]) == 2 ? convexhull2d(points) :
len(points[0]) == 3 ? convexhull3d(points) : []; len(points[0]) == 3 ? convexhull3d(points) : [];
epsilon = 1e-9; epsilon = 1e-9;
// 2d version // 2d version
function convexhull2d(points) = function convexhull2d(points) =
len(points) < 3 ? [] : let( len(points) < 3 ? [] : let(
a=0, b=1, a=0, b=1,
c = find_first_noncollinear([a,b], points, 2) c = find_first_noncollinear([a,b], points, 2)
) c == len(points) ? convexhull_collinear(points) : let( ) c == len(points) ? convexhull_collinear(points) : let(
remaining = [ for (i = [2:len(points)-1]) if (i != c) i ], remaining = [ for (i = [2:len(points)-1]) if (i != c) i ],
polygon = area_2d(points[a], points[b], points[c]) > 0 ? [a,b,c] : [b,a,c] polygon = area_2d(points[a], points[b], points[c]) > 0 ? [a,b,c] : [b,a,c]
) convex_hull_iterative_2d(points, polygon, remaining); ) convex_hull_iterative_2d(points, polygon, remaining);
// Adds the remaining points one by one to the convex hull // Adds the remaining points one by one to the convex hull
function convex_hull_iterative_2d(points, polygon, remaining, i_=0) = i_ >= len(remaining) ? polygon : function convex_hull_iterative_2d(points, polygon, remaining, i_=0) = i_ >= len(remaining) ? polygon :
let ( let (
// pick a point // pick a point
i = remaining[i_], i = remaining[i_],
// find the segments that are in conflict with the point (point not inside) // find the segments that are in conflict with the point (point not inside)
conflicts = find_conflicting_segments(points, polygon, points[i]) conflicts = find_conflicting_segments(points, polygon, points[i])
// no conflicts, skip point and move on // no conflicts, skip point and move on
) len(conflicts) == 0 ? convex_hull_iterative_2d(points, polygon, remaining, i_+1) : let( ) len(conflicts) == 0 ? convex_hull_iterative_2d(points, polygon, remaining, i_+1) : let(
// find the first conflicting segment and the first not conflicting // find the first conflicting segment and the first not conflicting
// conflict will be sorted, if not wrapping around, do it the easy way // conflict will be sorted, if not wrapping around, do it the easy way
polygon = remove_conflicts_and_insert_point(polygon, conflicts, i) polygon = remove_conflicts_and_insert_point(polygon, conflicts, i)
) convex_hull_iterative_2d( ) convex_hull_iterative_2d(
points, points,
polygon, polygon,
remaining, remaining,
i_+1 i_+1
); );
function find_conflicting_segments(points, polygon, point) = [ function find_conflicting_segments(points, polygon, point) = [
for (i = [0:len(polygon)-1]) let(j = (i+1) % len(polygon)) for (i = [0:len(polygon)-1]) let(j = (i+1) % len(polygon))
if (area_2d(points[polygon[i]], points[polygon[j]], point) < 0) if (area_2d(points[polygon[i]], points[polygon[j]], point) < 0)
i i
]; ];
// remove the conflicting segments from the polygon // remove the conflicting segments from the polygon
function remove_conflicts_and_insert_point(polygon, conflicts, point) = function remove_conflicts_and_insert_point(polygon, conflicts, point) =
conflicts[0] == 0 ? let( conflicts[0] == 0 ? let(
nonconflicting = [ for(i = [0:len(polygon)-1]) if (!contains(conflicts, i)) i ], nonconflicting = [ for(i = [0:len(polygon)-1]) if (!contains(conflicts, i)) i ],
new_indices = concat(nonconflicting, (nonconflicting[len(nonconflicting)-1]+1) % len(polygon)), new_indices = concat(nonconflicting, (nonconflicting[len(nonconflicting)-1]+1) % len(polygon)),
polygon = concat([ for (i = new_indices) polygon[i] ], point) polygon = concat([ for (i = new_indices) polygon[i] ], point)
) polygon : let( ) polygon : let(
prior_to_first_conflict = [ for(i = [0:1:min(conflicts)]) polygon[i] ], prior_to_first_conflict = [ for(i = [0:1:min(conflicts)]) polygon[i] ],
after_last_conflict = [ for(i = [max(conflicts)+1:1:len(polygon)-1]) polygon[i] ], after_last_conflict = [ for(i = [max(conflicts)+1:1:len(polygon)-1]) polygon[i] ],
polygon = concat(prior_to_first_conflict, point, after_last_conflict) polygon = concat(prior_to_first_conflict, point, after_last_conflict)
) polygon; ) polygon;
// 3d version // 3d version
function convexhull3d(points) = function convexhull3d(points) =
len(points) < 3 ? [ for(i = [0:1:len(points)-1]) i ] : let ( len(points) < 3 ? [ for(i = [0:1:len(points)-1]) i ] : let (
// start with a single triangle // start with a single triangle
a=0, b=1, c=2, a=0, b=1, c=2,
plane = plane(points,a,b,c), plane = plane(points,a,b,c),
d = find_first_noncoplanar(plane, points, 3) d = find_first_noncoplanar(plane, points, 3)
) d == len(points) ? /* all coplanar*/ let ( ) d == len(points) ? /* all coplanar*/ let (
pts2d = [ for (p = points) plane_project(p, points[a], points[b], points[c]) ], pts2d = [ for (p = points) plane_project(p, points[a], points[b], points[c]) ],
hull2d = convexhull2d(pts2d) hull2d = convexhull2d(pts2d)
) hull2d : let( ) hull2d : let(
remaining = [for (i = [3:len(points)-1]) if (i != d) i], remaining = [for (i = [3:len(points)-1]) if (i != d) i],
// Build an initial tetrahedron // Build an initial tetrahedron
// swap b,c if d is in front of triangle t // swap b,c if d is in front of triangle t
bc = in_front(plane, points[d]) ? [c,b] : [b,c], bc = in_front(plane, points[d]) ? [c,b] : [b,c],
b = bc[0], c = bc[1], b = bc[0], c = bc[1],
triangles = [
[a,b,c],
[d,b,a],
[c,d,a],
[b,d,c],
],
// calculate the plane equations triangles = [
planes = [ for (t = triangles) plane(points, t[0], t[1], t[2]) ] [a,b,c],
[d,b,a],
[c,d,a],
[b,d,c],
],
// calculate the plane equations
planes = [ for (t = triangles) plane(points, t[0], t[1], t[2]) ]
) convex_hull_iterative(points, triangles, planes, remaining); ) convex_hull_iterative(points, triangles, planes, remaining);
// A plane equation (normal, offset) // A plane equation (normal, offset)
function plane(points, a, b, c) = let( function plane(points, a, b, c) = let(
normal = unit(cross(points[c]-points[a], points[b]-points[a])) normal = unit(cross(points[c]-points[a], points[b]-points[a]))
) [ ) [
normal, normal,
normal * points[a] normal * points[a]
]; ];
// Adds the remaining points one by one to the convex hull // Adds the remaining points one by one to the convex hull
function convex_hull_iterative(points, triangles, planes, remaining, i_=0) = i_ >= len(remaining) ? triangles : function convex_hull_iterative(points, triangles, planes, remaining, i_=0) = i_ >= len(remaining) ? triangles :
let ( let (
// pick a point // pick a point
i = remaining[i_], i = remaining[i_],
// find the triangles that are in conflict with the point (point not inside) // find the triangles that are in conflict with the point (point not inside)
conflicts = find_conflicts(points[i], planes), conflicts = find_conflicts(points[i], planes),
// for all triangles that are in conflict, collect their halfedges // for all triangles that are in conflict, collect their halfedges
halfedges = [ halfedges = [
for(c = conflicts) for(c = conflicts)
for(i = [0:2]) let(j = (i+1)%3) for(i = [0:2]) let(j = (i+1)%3)
[triangles[c][i], triangles[c][j]] [triangles[c][i], triangles[c][j]]
], ],
// find the outer perimeter of the set of conflicting triangles // find the outer perimeter of the set of conflicting triangles
horizon = remove_internal_edges(halfedges), horizon = remove_internal_edges(halfedges),
// generate a new triangle for each horizon halfedge together with the picked point i // generate a new triangle for each horizon halfedge together with the picked point i
new_triangles = [ for (h = horizon) concat(h,i) ], new_triangles = [ for (h = horizon) concat(h,i) ],
// calculate the corresponding plane equations // calculate the corresponding plane equations
new_planes = [ for (t = new_triangles) plane(points, t[0], t[1], t[2]) ] new_planes = [ for (t = new_triangles) plane(points, t[0], t[1], t[2]) ]
) convex_hull_iterative( ) convex_hull_iterative(
points, points,
// remove the conflicting triangles and add the new ones // remove the conflicting triangles and add the new ones
concat(remove_elements(triangles, conflicts), new_triangles), concat(remove_elements(triangles, conflicts), new_triangles),
concat(remove_elements(planes, conflicts), new_planes), concat(remove_elements(planes, conflicts), new_planes),
remaining, remaining,
i_+1 i_+1
); );
function convexhull_collinear(points) = let( function convexhull_collinear(points) = let(
n = points[1] - points[0], n = points[1] - points[0],
a = points[0], a = points[0],
points1d = [ for(p = points) (p-a)*n ], points1d = [ for(p = points) (p-a)*n ],
min_i = min_index(points1d), min_i = min_index(points1d),
max_i = max_index(points1d) max_i = max_index(points1d)
) [ min_i, max_i ]; ) [ min_i, max_i ];
function min_index(values,min_,min_i_,i_) = function min_index(values,min_,min_i_,i_) =
i_ == undef ? min_index(values,values[0],0,1) : i_ == undef ? min_index(values,values[0],0,1) :
i_ >= len(values) ? min_i_ : i_ >= len(values) ? min_i_ :
values[i_] < min_ ? min_index(values,values[i_],i_,i_+1) values[i_] < min_ ? min_index(values,values[i_],i_,i_+1)
: min_index(values,min_,min_i_,i_+1); : min_index(values,min_,min_i_,i_+1);
function max_index(values,max_,max_i_,i_) = function max_index(values,max_,max_i_,i_) =
i_ == undef ? max_index(values,values[0],0,1) : i_ == undef ? max_index(values,values[0],0,1) :
i_ >= len(values) ? max_i_ : i_ >= len(values) ? max_i_ :
values[i_] > max_ ? max_index(values,values[i_],i_,i_+1) values[i_] > max_ ? max_index(values,values[i_],i_,i_+1)
: max_index(values,max_,max_i_,i_+1); : max_index(values,max_,max_i_,i_+1);
function remove_elements(array, elements) = [ function remove_elements(array, elements) = [
for (i = [0:len(array)-1]) for (i = [0:len(array)-1])
if (!search(i, elements)) if (!search(i, elements))
array[i] array[i]
]; ];
function remove_internal_edges(halfedges) = [ function remove_internal_edges(halfedges) = [
for (h = halfedges) for (h = halfedges)
if (!contains(halfedges, reverse(h))) if (!contains(halfedges, reverse(h)))
h h
]; ];
function plane_project(point, a, b, c) = let( function plane_project(point, a, b, c) = let(
u = b-a, u = b-a,
v = c-a, v = c-a,
n = cross(u,v), n = cross(u,v),
w = cross(n,u), w = cross(n,u),
relpoint = point-a relpoint = point-a
) [relpoint * u, relpoint * w]; ) [relpoint * u, relpoint * w];
function plane_unproject(point, a, b, c) = let( function plane_unproject(point, a, b, c) = let(
u = b-a, u = b-a,
v = c-a, v = c-a,
n = cross(u,v), n = cross(u,v),
w = cross(n,u) w = cross(n,u)
) a + point[0] * u + point[1] * w; ) a + point[0] * u + point[1] * w;
function reverse(arr) = [ for (i = [len(arr)-1:-1:0]) arr[i] ]; function reverse(arr) = [ for (i = [len(arr)-1:-1:0]) arr[i] ];
@ -206,22 +206,22 @@ function reverse(arr) = [ for (i = [len(arr)-1:-1:0]) arr[i] ];
function contains(arr, element) = search([element],arr)[0] != [] ? true : false; function contains(arr, element) = search([element],arr)[0] != [] ? true : false;
function find_conflicts(point, planes) = [ function find_conflicts(point, planes) = [
for (i = [0:len(planes)-1]) for (i = [0:len(planes)-1])
if (in_front(planes[i], point)) if (in_front(planes[i], point))
i i
]; ];
function find_first_noncollinear(line, points, i) = function find_first_noncollinear(line, points, i) =
i >= len(points) ? len(points) : i >= len(points) ? len(points) :
collinear(points[line[0]], collinear(points[line[0]],
points[line[1]], points[line[1]],
points[i]) ? find_first_noncollinear(line, points, i+1) points[i]) ? find_first_noncollinear(line, points, i+1)
: i; : i;
function find_first_noncoplanar(plane, points, i) = function find_first_noncoplanar(plane, points, i) =
i >= len(points) ? len(points) : i >= len(points) ? len(points) :
coplanar(plane, points[i]) ? find_first_noncoplanar(plane, points, i+1) coplanar(plane, points[i]) ? find_first_noncoplanar(plane, points, i+1)
: i; : i;
function distance(plane, point) = plane[0] * point - plane[1]; function distance(plane, point) = plane[0] * point - plane[1];
@ -232,9 +232,9 @@ function coplanar(plane, point) = abs(distance(plane,point)) <= epsilon;
function unit(v) = v/norm(v); function unit(v) = v/norm(v);
function area_2d(a,b,c) = ( function area_2d(a,b,c) = (
a[0] * (b[1] - c[1]) + a[0] * (b[1] - c[1]) +
b[0] * (c[1] - a[1]) + b[0] * (c[1] - a[1]) +
c[0] * (a[1] - b[1])) / 2; c[0] * (a[1] - b[1])) / 2;
function collinear(a,b,c) = abs(area_2d(a,b,c)) < epsilon; function collinear(a,b,c) = abs(area_2d(a,b,c)) < epsilon;
@ -244,9 +244,9 @@ function spherical(cartesian) = [
]; ];
function cartesian(spherical) = [ function cartesian(spherical) = [
cos(spherical[1]) * cos(spherical[0]), cos(spherical[1]) * cos(spherical[0]),
cos(spherical[1]) * sin(spherical[0]), cos(spherical[1]) * sin(spherical[0]),
sin(spherical[1]) sin(spherical[1])
]; ];
@ -255,13 +255,13 @@ function cartesian(spherical) = [
phi = 1.618033988749895; phi = 1.618033988749895;
testpoints_on_sphere = [ for(p = testpoints_on_sphere = [ for(p =
[ [
[1,phi,0], [-1,phi,0], [1,-phi,0], [-1,-phi,0], [1,phi,0], [-1,phi,0], [1,-phi,0], [-1,-phi,0],
[0,1,phi], [0,-1,phi], [0,1,-phi], [0,-1,-phi], [0,1,phi], [0,-1,phi], [0,1,-phi], [0,-1,-phi],
[phi,0,1], [-phi,0,1], [phi,0,-1], [-phi,0,-1] [phi,0,1], [-phi,0,1], [phi,0,-1], [-phi,0,-1]
]) ])
unit(p) unit(p)
]; ];
testpoints_spherical = [ for(p = testpoints_on_sphere) spherical(p) ]; testpoints_spherical = [ for(p = testpoints_on_sphere) spherical(p) ];
@ -299,26 +299,26 @@ visualize_hull(testpoints3d);
module visualize_hull(points) { module visualize_hull(points) {
hull = hull(points); hull = hull(points);
%if (len(hull) > 0 && len(hull[0]) > 0) %if (len(hull) > 0 && len(hull[0]) > 0)
polyhedron(points=points, faces = hull); polyhedron(points=points, faces = hull);
else else
polyhedron(points=points, faces = [hull]); polyhedron(points=points, faces = [hull]);
for (i = [0:len(points)-1]) assign(p = points[i], $fn = 16) { for (i = [0:len(points)-1]) assign(p = points[i], $fn = 16) {
translate(p) { translate(p) {
if (hull_contains_index(hull,i)) { if (hull_contains_index(hull,i)) {
color("blue") sphere(1); color("blue") sphere(1);
} else { } else {
color("red") sphere(1); color("red") sphere(1);
} }
} }
} }
function hull_contains_index(hull, index) = function hull_contains_index(hull, index) =
search(index,hull,1,0) || search(index,hull,1,0) ||
search(index,hull,1,1) || search(index,hull,1,1) ||
search(index,hull,1,2); search(index,hull,1,2);
} }

View File

@ -8,7 +8,7 @@ function vec3(p) = len(p) < 3 ? concat(p,0) : p;
function vec4(p) = let (v3=vec3(p)) len(v3) < 4 ? concat(v3,1) : v3; function vec4(p) = let (v3=vec3(p)) len(v3) < 4 ? concat(v3,1) : v3;
function unit(v) = v/norm(v); function unit(v) = v/norm(v);
function identity3()=[[1,0,0],[0,1,0],[0,0,1]]; function identity3()=[[1,0,0],[0,1,0],[0,0,1]];
function identity4()=[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]; function identity4()=[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]];
@ -24,7 +24,7 @@ function transpose_3(m) = [[m[0][0],m[1][0],m[2][0]],[m[0][1],m[1][1],m[2][1]],[
function transpose_4(m) = [[m[0][0],m[1][0],m[2][0],m[3][0]], function transpose_4(m) = [[m[0][0],m[1][0],m[2][0],m[3][0]],
[m[0][1],m[1][1],m[2][1],m[3][1]], [m[0][1],m[1][1],m[2][1],m[3][1]],
[m[0][2],m[1][2],m[2][2],m[3][2]], [m[0][2],m[1][2],m[2][2],m[3][2]],
[m[0][3],m[1][3],m[2][3],m[3][3]]]; [m[0][3],m[1][3],m[2][3],m[3][3]]];
function invert_rt(m) = construct_Rt(transpose_3(rotation_part(m)), -(transpose_3(rotation_part(m)) * translation_part(m))); function invert_rt(m) = construct_Rt(transpose_3(rotation_part(m)), -(transpose_3(rotation_part(m)) * translation_part(m)));
function construct_Rt(R,t) = [concat(R[0],t[0]),concat(R[1],t[1]),concat(R[2],t[2]),[0,0,0,1]]; function construct_Rt(R,t) = [concat(R[0],t[0]),concat(R[1],t[1]),concat(R[2],t[2]),[0,0,0,1]];

View File

@ -9,22 +9,22 @@
module mirror_x() { module mirror_x() {
union() { union() {
child(); child();
scale([-1,1,1]) child(); scale([-1,1,1]) child();
} }
} }
module mirror_y() { module mirror_y() {
union() { union() {
child(); child();
scale([1,-1,1]) child(); scale([1,-1,1]) child();
} }
} }
module mirror_z() { module mirror_z() {
union() { union() {
child(); child();
scale([1,1,-1]) child(); scale([1,1,-1]) child();
} }
} }

View File

@ -13,12 +13,12 @@
// - center=true and positive d places the shell centered on the edge // - center=true and positive d places the shell centered on the edge
module outset(d=1) { module outset(d=1) {
// Bug workaround for older OpenSCAD versions // Bug workaround for older OpenSCAD versions
if (version_num() < 20130424) render() outset_extruded(d) child(); if (version_num() < 20130424) render() outset_extruded(d) child();
else minkowski() { else minkowski() {
circle(r=d); circle(r=d);
child(); child();
} }
} }
module outset_extruded(d=1) { module outset_extruded(d=1) {
@ -29,47 +29,47 @@ module outset_extruded(d=1) {
} }
module inset(d=1) { module inset(d=1) {
render() inverse() outset(d=d) inverse() child(); render() inverse() outset(d=d) inverse() child();
} }
module fillet(r=1) { module fillet(r=1) {
inset(d=r) render() outset(d=r) child(); inset(d=r) render() outset(d=r) child();
} }
module rounding(r=1) { module rounding(r=1) {
outset(d=r) inset(d=r) child(); outset(d=r) inset(d=r) child();
} }
module shell(d,center=false) { module shell(d,center=false) {
if (center && d > 0) { if (center && d > 0) {
difference() { difference() {
outset(d=d/2) child(); outset(d=d/2) child();
inset(d=d/2) child(); inset(d=d/2) child();
} }
} }
if (!center && d > 0) { if (!center && d > 0) {
difference() { difference() {
outset(d=d) child(); outset(d=d) child();
child(); child();
} }
} }
if (!center && d < 0) { if (!center && d < 0) {
difference() { difference() {
child(); child();
inset(d=-d) child(); inset(d=-d) child();
} }
} }
if (d == 0) child(); if (d == 0) child();
} }
// Below are for internal use only // Below are for internal use only
module inverse() { module inverse() {
difference() { difference() {
square(1e5,center=true); square(1e5,center=true);
child(); child();
} }
} }
@ -78,32 +78,32 @@ module inverse() {
use <mirror.scad> use <mirror.scad>
module arrow(l=1,w=.6,t=0.15) { module arrow(l=1,w=.6,t=0.15) {
mirror_y() polygon([[0,0],[l,0],[l-w/2,w/2],[l-w/2-sqrt(2)*t,w/2],[l-t/2-sqrt(2)*t,t/2],[0,t/2]]); mirror_y() polygon([[0,0],[l,0],[l-w/2,w/2],[l-w/2-sqrt(2)*t,w/2],[l-t/2-sqrt(2)*t,t/2],[0,t/2]]);
} }
module shape() { module shape() {
polygon([[0,0],[1,0],[1.5,1],[2.5,1],[2,-1],[0,-1]]); polygon([[0,0],[1,0],[1.5,1],[2.5,1],[2,-1],[0,-1]]);
} }
if(0) assign($fn=32) { if(0) assign($fn=32) {
for (p = [0:10*3-1]) assign(o=floor(p/3)) { for (p = [0:10*3-1]) assign(o=floor(p/3)) {
translate([(p%3)*2.5,-o*3]) { translate([(p%3)*2.5,-o*3]) {
//%if (p % 3 == 1) translate([0,0,1]) shape(); //%if (p % 3 == 1) translate([0,0,1]) shape();
if (p % 3 == 0) shape(); if (p % 3 == 0) shape();
if (p % 3 == 1) translate([0.6,0]) arrow(); if (p % 3 == 1) translate([0.6,0]) arrow();
if (p % 3 == 2) { if (p % 3 == 2) {
if (o == 0) inset(d=0.3) shape(); if (o == 0) inset(d=0.3) shape();
if (o == 1) outset(d=0.3) shape(); if (o == 1) outset(d=0.3) shape();
if (o == 2) rounding(r=0.3) shape(); if (o == 2) rounding(r=0.3) shape();
if (o == 3) fillet(r=0.3) shape(); if (o == 3) fillet(r=0.3) shape();
if (o == 4) shell(d=0.3) shape(); if (o == 4) shell(d=0.3) shape();
if (o == 5) shell(d=-0.3) shape(); if (o == 5) shell(d=-0.3) shape();
if (o == 6) shell(d=0.3,center=true) shape(); if (o == 6) shell(d=0.3,center=true) shape();
if (o == 7) rounding(r=0.3) fillet(r=0.3) shape(); if (o == 7) rounding(r=0.3) fillet(r=0.3) shape();
if (o == 8) shell(d=0.3,center=true) fillet(r=0.3) rounding(r=0.3) shape(); if (o == 8) shell(d=0.3,center=true) fillet(r=0.3) rounding(r=0.3) shape();
if (o == 9) shell(d=-0.3) fillet(r=0.3) rounding(r=0.3) shape(); if (o == 9) shell(d=-0.3) fillet(r=0.3) rounding(r=0.3) shape();
} }
} }
} }
} }

View File

@ -10,21 +10,21 @@ function se3_exp_1(t,w) = concat(
); );
function se3_exp_2(t,w) = se3_exp_2_0(t,w,w*w); function se3_exp_2(t,w) = se3_exp_2_0(t,w,w*w);
function se3_exp_2_0(t,w,theta_sq) = function se3_exp_2_0(t,w,theta_sq) =
se3_exp_23( se3_exp_23(
so3_exp_2(theta_sq), so3_exp_2(theta_sq),
C = (1.0 - theta_sq/20) / 6, C = (1.0 - theta_sq/20) / 6,
t=t,w=w); t=t,w=w);
function se3_exp_3(t,w) = se3_exp_3_0(t,w,sqrt(w*w)*180/PI,1/sqrt(w*w)); function se3_exp_3(t,w) = se3_exp_3_0(t,w,sqrt(w*w)*180/PI,1/sqrt(w*w));
function se3_exp_3_0(t,w,theta_deg,inv_theta) = function se3_exp_3_0(t,w,theta_deg,inv_theta) =
se3_exp_23( se3_exp_23(
so3_exp_3_0(theta_deg = theta_deg, inv_theta = inv_theta), so3_exp_3_0(theta_deg = theta_deg, inv_theta = inv_theta),
C = (1 - sin(theta_deg) * inv_theta) * (inv_theta * inv_theta), C = (1 - sin(theta_deg) * inv_theta) * (inv_theta * inv_theta),
t=t,w=w); t=t,w=w);
function se3_exp_23(AB,C,t,w) = function se3_exp_23(AB,C,t,w) =
[AB[0], AB[1], t + AB[1] * cross(w,t) + C * cross(w,cross(w,t)) ]; [AB[0], AB[1], t + AB[1] * cross(w,t) + C * cross(w,cross(w,t)) ];
function se3_exp(mu) = se3_exp_0(t=take3(mu),w=tail3(mu)/180*PI); function se3_exp(mu) = se3_exp_0(t=take3(mu),w=tail3(mu)/180*PI);
@ -32,7 +32,7 @@ function se3_exp(mu) = se3_exp_0(t=take3(mu),w=tail3(mu)/180*PI);
function se3_exp_0(t,w) = function se3_exp_0(t,w) =
combine_se3_exp(w, combine_se3_exp(w,
// Evaluate by Taylor expansion when near 0 // Evaluate by Taylor expansion when near 0
w*w < 1e-8 w*w < 1e-8
? se3_exp_1(t,w) ? se3_exp_1(t,w)
: w*w < 1e-6 : w*w < 1e-6
? se3_exp_2(t,w) ? se3_exp_2(t,w)
@ -42,7 +42,7 @@ combine_se3_exp(w,
function se3_ln(m) = se3_ln_to_deg(se3_ln_rad(m)); function se3_ln(m) = se3_ln_to_deg(se3_ln_rad(m));
function se3_ln_to_deg(v) = concat(take3(v),tail3(v)*180/PI); function se3_ln_to_deg(v) = concat(take3(v),tail3(v)*180/PI);
function se3_ln_rad(m) = se3_ln_0(m, function se3_ln_rad(m) = se3_ln_0(m,
rot = so3_ln_rad(rotation_part(m))); rot = so3_ln_rad(rotation_part(m)));
function se3_ln_0(m,rot) = se3_ln_1(m,rot, function se3_ln_0(m,rot) = se3_ln_1(m,rot,
theta = sqrt(rot*rot)); theta = sqrt(rot*rot));
@ -50,8 +50,8 @@ function se3_ln_1(m,rot,theta) = se3_ln_2(m,rot,theta,
shtot = theta > 0.00001 ? sin(theta/2*180/PI)/theta : 0.5, shtot = theta > 0.00001 ? sin(theta/2*180/PI)/theta : 0.5,
halfrotator = so3_exp_rad(rot * -.5)); halfrotator = so3_exp_rad(rot * -.5));
function se3_ln_2(m,rot,theta,shtot,halfrotator) = function se3_ln_2(m,rot,theta,shtot,halfrotator) =
concat( (halfrotator * translation_part(m) - concat( (halfrotator * translation_part(m) -
(theta > 0.001 (theta > 0.001
? rot * ((translation_part(m) * rot) * (1-2*shtot) / (rot*rot)) ? rot * ((translation_part(m) * rot) * (1-2*shtot) / (rot*rot))
: rot * ((translation_part(m) * rot)/24) : rot * ((translation_part(m) * rot)/24)
)) / (2 * shtot), rot); )) / (2 * shtot), rot);

View File

@ -4,13 +4,13 @@ function circle(r) = [for (i=[0:$fn-1]) let (a=i*360/$fn) r * [cos(a), sin(a)]];
function regular(r, n) = circle(r, $fn=n); function regular(r, n) = circle(r, $fn=n);
function rectangle_profile(size=[1,1]) = [ function rectangle_profile(size=[1,1]) = [
// The first point is the anchor point, put it on the point corresponding to [cos(0),sin(0)] // The first point is the anchor point, put it on the point corresponding to [cos(0),sin(0)]
[ size[0]/2, 0], [ size[0]/2, 0],
[ size[0]/2, size[1]/2], [ size[0]/2, size[1]/2],
[-size[0]/2, size[1]/2], [-size[0]/2, size[1]/2],
[-size[0]/2, -size[1]/2], [-size[0]/2, -size[1]/2],
[ size[0]/2, -size[1]/2], [ size[0]/2, -size[1]/2],
]; ];
// FIXME: Move rectangle and rounded rectangle from extrusion // FIXME: Move rectangle and rounded rectangle from extrusion

View File

@ -11,28 +11,28 @@ function rodrigues_so3_exp(w, A, B) = [
function so3_exp(w) = so3_exp_rad(w/180*PI); function so3_exp(w) = so3_exp_rad(w/180*PI);
function so3_exp_rad(w) = function so3_exp_rad(w) =
combine_so3_exp(w, combine_so3_exp(w,
w*w < 1e-8 w*w < 1e-8
? so3_exp_1(w*w) ? so3_exp_1(w*w)
: w*w < 1e-6 : w*w < 1e-6
? so3_exp_2(w*w) ? so3_exp_2(w*w)
: so3_exp_3(w*w)); : so3_exp_3(w*w));
function combine_so3_exp(w,AB) = rodrigues_so3_exp(w,AB[0],AB[1]); function combine_so3_exp(w,AB) = rodrigues_so3_exp(w,AB[0],AB[1]);
// Taylor series expansions close to 0 // Taylor series expansions close to 0
function so3_exp_1(theta_sq) = [ function so3_exp_1(theta_sq) = [
1 - 1/6*theta_sq, 1 - 1/6*theta_sq,
0.5 0.5
]; ];
function so3_exp_2(theta_sq) = [ function so3_exp_2(theta_sq) = [
1.0 - theta_sq * (1.0 - theta_sq/20) / 6, 1.0 - theta_sq * (1.0 - theta_sq/20) / 6,
0.5 - 0.25/6 * theta_sq 0.5 - 0.25/6 * theta_sq
]; ];
function so3_exp_3_0(theta_deg, inv_theta) = [ function so3_exp_3_0(theta_deg, inv_theta) = [
sin(theta_deg) * inv_theta, sin(theta_deg) * inv_theta,
(1 - cos(theta_deg)) * (inv_theta * inv_theta) (1 - cos(theta_deg)) * (inv_theta * inv_theta)
]; ];
function so3_exp_3(theta_sq) = so3_exp_3_0(sqrt(theta_sq)*180/PI, 1/sqrt(theta_sq)); function so3_exp_3(theta_sq) = so3_exp_3_0(sqrt(theta_sq)*180/PI, 1/sqrt(theta_sq));
@ -42,41 +42,41 @@ function rot_axis_part(m) = [m[2][1] - m[1][2], m[0][2] - m[2][0], m[1][0] - m[0
function so3_ln(m) = 180/PI*so3_ln_rad(m); function so3_ln(m) = 180/PI*so3_ln_rad(m);
function so3_ln_rad(m) = so3_ln_0(m, function so3_ln_rad(m) = so3_ln_0(m,
cos_angle = rot_cos_angle(m), cos_angle = rot_cos_angle(m),
preliminary_result = rot_axis_part(m)); preliminary_result = rot_axis_part(m));
function so3_ln_0(m, cos_angle, preliminary_result) = function so3_ln_0(m, cos_angle, preliminary_result) =
so3_ln_1(m, cos_angle, preliminary_result, so3_ln_1(m, cos_angle, preliminary_result,
sin_angle_abs = sqrt(preliminary_result*preliminary_result)); sin_angle_abs = sqrt(preliminary_result*preliminary_result));
function so3_ln_1(m, cos_angle, preliminary_result, sin_angle_abs) = function so3_ln_1(m, cos_angle, preliminary_result, sin_angle_abs) =
cos_angle > sqrt(1/2) cos_angle > sqrt(1/2)
? sin_angle_abs > 0 ? sin_angle_abs > 0
? preliminary_result * asin(sin_angle_abs)*PI/180 / sin_angle_abs ? preliminary_result * asin(sin_angle_abs)*PI/180 / sin_angle_abs
: preliminary_result : preliminary_result
: cos_angle > -sqrt(1/2) : cos_angle > -sqrt(1/2)
? preliminary_result * acos(cos_angle)*PI/180 / sin_angle_abs ? preliminary_result * acos(cos_angle)*PI/180 / sin_angle_abs
: so3_get_symmetric_part_rotation( : so3_get_symmetric_part_rotation(
preliminary_result, preliminary_result,
m, m,
angle = PI - asin(sin_angle_abs)*PI/180, angle = PI - asin(sin_angle_abs)*PI/180,
d0 = m[0][0] - cos_angle, d0 = m[0][0] - cos_angle,
d1 = m[1][1] - cos_angle, d1 = m[1][1] - cos_angle,
d2 = m[2][2] - cos_angle d2 = m[2][2] - cos_angle
); );
function so3_get_symmetric_part_rotation(preliminary_result, m, angle, d0, d1, d2) = function so3_get_symmetric_part_rotation(preliminary_result, m, angle, d0, d1, d2) =
so3_get_symmetric_part_rotation_0(preliminary_result,angle,so3_largest_column(m, d0, d1, d2)); so3_get_symmetric_part_rotation_0(preliminary_result,angle,so3_largest_column(m, d0, d1, d2));
function so3_get_symmetric_part_rotation_0(preliminary_result, angle, c_max) = function so3_get_symmetric_part_rotation_0(preliminary_result, angle, c_max) =
angle * unit(c_max * preliminary_result < 0 ? -c_max : c_max); angle * unit(c_max * preliminary_result < 0 ? -c_max : c_max);
function so3_largest_column(m, d0, d1, d2) = function so3_largest_column(m, d0, d1, d2) =
d0*d0 > d1*d1 && d0*d0 > d2*d2 d0*d0 > d1*d1 && d0*d0 > d2*d2
? [d0, (m[1][0]+m[0][1])/2, (m[0][2]+m[2][0])/2] ? [d0, (m[1][0]+m[0][1])/2, (m[0][2]+m[2][0])/2]
: d1*d1 > d2*d2 : d1*d1 > d2*d2
? [(m[1][0]+m[0][1])/2, d1, (m[2][1]+m[1][2])/2] ? [(m[1][0]+m[0][1])/2, d1, (m[2][1]+m[1][2])/2]
: [(m[0][2]+m[2][0])/2, (m[2][1]+m[1][2])/2, d2]; : [(m[0][2]+m[2][0])/2, (m[2][1]+m[1][2])/2, d2];
__so3_test = [12,-125,110]; __so3_test = [12,-125,110];
echo(UNITTEST_so3=norm(__so3_test-so3_ln(so3_exp(__so3_test))) < 1e-8); echo(UNITTEST_so3=norm(__so3_test-so3_ln(so3_exp(__so3_test))) < 1e-8);

View File

@ -20,11 +20,11 @@ z4=[0,0,0,0];
function matrix_power(m,n)= n==0? (len(m)==3?identity3():identity4()) : function matrix_power(m,n)= n==0? (len(m)==3?identity3():identity4()) :
n==1 ? m : (n%2==1) ? matrix_power(m*m,floor(n/2))*m : matrix_power(m*m,n/2); n==1 ? m : (n%2==1) ? matrix_power(m*m,floor(n/2))*m : matrix_power(m*m,n/2);
function det(m) = let(r=[for(i=[0:1:len(m)-1]) i]) det_help(m, 0, r); function det(m) = let(r=[for(i=[0:1:len(m)-1]) i]) det_help(m, 0, r);
// Construction indices list is inefficient, but currently there is no way to imperatively // Construction indices list is inefficient, but currently there is no way to imperatively
// assign to a list element // assign to a list element
function det_help(m, i, r) = len(r) == 0 ? 1 : function det_help(m, i, r) = len(r) == 0 ? 1 :
m[len(m)-len(r)][r[i]]*det_help(m,0,remove(r,i)) - (i+1<len(r)? det_help(m, i+1, r) : 0); m[len(m)-len(r)][r[i]]*det_help(m,0,remove(r,i)) - (i+1<len(r)? det_help(m, i+1, r) : 0);
function matrix_invert(m) = let(r=[for(i=[0:len(m)-1]) i]) [for(i=r) [for(j=r) function matrix_invert(m) = let(r=[for(i=[0:len(m)-1]) i]) [for(i=r) [for(j=r)
@ -40,7 +40,7 @@ function spline_args(p, closed=false, v1=undef, v2=undef)=len(p)<2 ? []:
pcnt=closed? len(p) + 1 : len(p), pcnt=closed? len(p) + 1 : len(p),
un=[p[pcnt-2],p[closed?0:pcnt-1],v1==undef?z4:v1, v2==undef?z4:v2], un=[p[pcnt-2],p[closed?0:pcnt-1],v1==undef?z4:v1, v2==undef?z4:v2],
sn=matrix_invert(q4+q3*matrix_power(qn1i2,pcnt-2))*(un-q3*q1inv*spline_helper(0, pcnt, p))) sn=matrix_invert(q4+q3*matrix_power(qn1i2,pcnt-2))*(un-q3*q1inv*spline_helper(0, pcnt, p)))
// result[i+1] recurrently defines result[i]. This is O(n) runtime with imperative language and // result[i+1] recurrently defines result[i]. This is O(n) runtime with imperative language and
// may be O(n^2) if OpenSCAD doesn't cache spline_si(i+1). // may be O(n^2) if OpenSCAD doesn't cache spline_si(i+1).
[for(i=[0:pcnt-2]) spline_si(i, pcnt-2, p, sn)]; [for(i=[0:pcnt-2]) spline_si(i, pcnt-2, p, sn)];
@ -57,24 +57,24 @@ function spline_si(i,n, p, sn) = i == n ? sn : q1inv*(spline_u(i,p)-q2*spline_si
// In the second case second tangent is constructed from the next tangent by symmetric map. // In the second case second tangent is constructed from the next tangent by symmetric map.
// I.e. if current points are p0,p1,p2 then anchor points are p0 and p2, first tangent defined by p1-p0, // I.e. if current points are p0,p1,p2 then anchor points are p0 and p2, first tangent defined by p1-p0,
// second tangent defined by p3-p2. // second tangent defined by p3-p2.
// Return array of coefficients accepted by spline(), spline_tan() and similar // Return array of coefficients accepted by spline(), spline_tan() and similar
function bezier3_args(p, symmetric=false) = let(step=symmetric?2:3) function bezier3_args(p, symmetric=false) = let(step=symmetric?2:3)
[for(i=[0:step:len(p)-3]) [[1,0,0,0],[-3,3,0,0],[3,-6,3,0],[-1,3,-3,1]]* [for(i=[0:step:len(p)-3]) [[1,0,0,0],[-3,3,0,0],[3,-6,3,0],[-1,3,-3,1]]*
(symmetric?[p[i],p[i]+p[i+1],p[i+2]-p[i+3],p[i+2]] : [p[i], p[i]+p[i+1], p[i+3]+p[i+2], p[i+3]])]; (symmetric?[p[i],p[i]+p[i+1],p[i+2]-p[i+3],p[i+2]] : [p[i], p[i]+p[i+1], p[i+3]+p[i+2], p[i+3]])];
// s - spline arguments calculated by spline_args // s - spline arguments calculated by spline_args
// t - defines point on curve. each segment length is 1. I.e. t= 0..1 is first segment, t=1..2 - second. // t - defines point on curve. each segment length is 1. I.e. t= 0..1 is first segment, t=1..2 - second.
function spline(s, t)= let(i=t>=len(s)?len(s)-1: floor(t), t2=t-i) [1,t2,t2*t2,t2*t2*t2]*s[i]; function spline(s, t)= let(i=t>=len(s)?len(s)-1: floor(t), t2=t-i) [1,t2,t2*t2,t2*t2*t2]*s[i];
function spline_tan(s, t)= let(i=t>=len(s)?len(s)-1: floor(t), t2=t-i) [0,1,2*t2,3*t2*t2]*s[i]; function spline_tan(s, t)= let(i=t>=len(s)?len(s)-1: floor(t), t2=t-i) [0,1,2*t2,3*t2*t2]*s[i];
function spline_tan_unit(s, t)= unit(spline_tan(s,t)); function spline_tan_unit(s, t)= unit(spline_tan(s,t));
function spline_d2(s,t)= let(i=t>=len(s)?len(s)-1: floor(t), t2=t-i) [0,0,2,6*t2]*s[i]; function spline_d2(s,t)= let(i=t>=len(s)?len(s)-1: floor(t), t2=t-i) [0,0,2,6*t2]*s[i];
function spline_binormal_unit(s,t)= unit(cross(spline_tan(s, t), spline_d2(s,t))); function spline_binormal_unit(s,t)= unit(cross(spline_tan(s, t), spline_d2(s,t)));
function spline_normal_unit(s,t)= unit(cross(spline_tan(s, t), spline_binormal_unit(s,t))); function spline_normal_unit(s,t)= unit(cross(spline_tan(s, t), spline_binormal_unit(s,t)));
function spline_transform(s, t)= function spline_transform(s, t)=
construct_Rt(transpose_3([spline_normal_unit(s,t), spline_binormal_unit(s,t), spline_tan_unit(s,t)]), spline(s,t)); construct_Rt(transpose_3([spline_normal_unit(s,t), spline_binormal_unit(s,t), spline_tan_unit(s,t)]), spline(s,t));
// Unit tests // Unit tests
__s = spline_args([[0,10,0], [10,0,0],[0,-5,2]], v1=[0,1,0], v2=[-1,0,0], closed=true); __s = spline_args([[0,10,0], [10,0,0],[0,-5,2]], v1=[0,1,0], v2=[-1,0,0], closed=true);
for(t=[0:0.01:len(__s)]) translate(spline(__s, t)) for(t=[0:0.01:len(__s)]) translate(spline(__s, t))
@ -107,7 +107,4 @@ for(t=[0:0.05:len(__s3)]) translate(spline(__s3, t)) {
} }
translate([0,0,9]) for(t=[0:0.025:len(__s3)]) translate([0,0,9]) for(t=[0:0.025:len(__s3)])
multmatrix(spline_transform(__s3,t)) cube([1,1,0.1],center=true); multmatrix(spline_transform(__s3,t)) cube([1,1,0.1],center=true);

View File

@ -8,36 +8,36 @@ function is_undef(x) = len(x) > 0 ? vec_is_undef(x) : x == undef;
// Either a or b, but not both // Either a or b, but not both
function either(a,b,default=undef) = is_undef(a) ? (is_undef(b) ? default : b) : is_undef(b) ? a : undef; function either(a,b,default=undef) = is_undef(a) ? (is_undef(b) ? default : b) : is_undef(b) ? a : undef;
function translationv(left=undef,right=undef,up=undef,down=undef,forward=undef,backward=undef,translation=undef) = function translationv(left=undef,right=undef,up=undef,down=undef,forward=undef,backward=undef,translation=undef) =
translationv_2( translationv_2(
x = either(up,-down), x = either(up,-down),
y = either(right,-left), y = either(right,-left),
z = either(forward,-backward), z = either(forward,-backward),
translation = translation); translation = translation);
function translationv_2(x,y,z,translation) = function translationv_2(x,y,z,translation) =
x == undef && y == undef && z == undef ? translation : x == undef && y == undef && z == undef ? translation :
is_undef(translation) ? [val(x,0),val(y,0),val(z,0)] is_undef(translation) ? [val(x,0),val(y,0),val(z,0)]
: undef; : undef;
function rotationv(pitch=undef,yaw=undef,roll=undef,rotation=undef) = function rotationv(pitch=undef,yaw=undef,roll=undef,rotation=undef) =
rotation == undef ? [val(yaw,0),val(pitch,0),val(roll,0)] : rotation == undef ? [val(yaw,0),val(pitch,0),val(roll,0)] :
pitch == undef && yaw == undef && roll == undef ? rotation : pitch == undef && yaw == undef && roll == undef ? rotation :
undef; undef;
function trajectory( function trajectory(
left=undef, right=undef, left=undef, right=undef,
up=undef, down=undef, up=undef, down=undef,
forward=undef, backward=undef, forward=undef, backward=undef,
translation=undef, translation=undef,
pitch=undef, pitch=undef,
yaw=undef, yaw=undef,
roll=undef, roll=undef,
rotation=undef rotation=undef
) = concat( ) = concat(
translationv(left=left,right=right,up=up,down=down,forward=forward,backward=backward,translation=translation), translationv(left=left,right=right,up=up,down=down,forward=forward,backward=backward,translation=translation),
rotationv(pitch=pitch,yaw=yaw,roll=roll,rotation=rotation) rotationv(pitch=pitch,yaw=yaw,roll=roll,rotation=rotation)
); );
function rotationm(rotation=undef,pitch=undef,yaw=undef,roll=undef) = so3_exp(rotationv(rotation=rotation,pitch=pitch,yaw=yaw,roll=roll)); function rotationm(rotation=undef,pitch=undef,yaw=undef,roll=undef) = so3_exp(rotationv(rotation=rotation,pitch=pitch,yaw=yaw,roll=roll));

View File

@ -2,88 +2,87 @@ use <linalg.scad>
use <se3.scad> use <se3.scad>
function left_multiply(a,bs,i_=0) = i_ >= len(bs) ? [] : function left_multiply(a,bs,i_=0) = i_ >= len(bs) ? [] :
concat([ concat([
a * bs[i_] a * bs[i_]
], left_multiply(a,bs,i_+1)); ], left_multiply(a,bs,i_+1));
function right_multiply(as,b,i_=0) = i_ >= len(as) ? [] : function right_multiply(as,b,i_=0) = i_ >= len(as) ? [] :
concat([ concat([
as[i_] * b as[i_] * b
], right_multiply(as,b,i_+1)); ], right_multiply(as,b,i_+1));
function quantize_trajectory(trajectory,step=undef,start_position=0,steps=undef,i_=0,length_=undef) = function quantize_trajectory(trajectory,step=undef,start_position=0,steps=undef,i_=0,length_=undef) =
length_ == undef ? quantize_trajectory( length_ == undef ? quantize_trajectory(
trajectory=trajectory, trajectory=trajectory,
start_position=(step==undef?norm(take3(trajectory))/steps*start_position:start_position), start_position=(step==undef?norm(take3(trajectory))/steps*start_position:start_position),
length_=norm(take3(trajectory)), length_=norm(take3(trajectory)),
step=step,steps=steps,i_=i_) : step=step,steps=steps,i_=i_) :
(steps==undef?start_position > length_:i_>=steps) ? [] : (steps==undef?start_position > length_:i_>=steps) ? [] :
concat([ concat([
// if steps is defined, ignore start_position // if steps is defined, ignore start_position
se3_exp(trajectory*(steps==undef ? start_position/length_ se3_exp(trajectory*(steps==undef ? start_position/length_
: i_/(steps>1?steps-1:1))) : i_/(steps>1?steps-1:1)))
], quantize_trajectory(trajectory=trajectory,step=step,start_position=(steps==undef?start_position+step:start_position),steps=steps,i_=i_+1,length_=length_)); ], quantize_trajectory(trajectory=trajectory,step=step,start_position=(steps==undef?start_position+step:start_position),steps=steps,i_=i_+1,length_=length_));
function close_trajectory_loop(trajectories) = concat(trajectories,[se3_ln(invert_rt(trajectories_end_position(trajectories)))]); function close_trajectory_loop(trajectories) = concat(trajectories,[se3_ln(invert_rt(trajectories_end_position(trajectories)))]);
function quantize_trajectories(trajectories,step=undef,start_position=0,steps=undef,loop=false,last_=identity4(),i_=0,current_length_=undef,j_=0) = function quantize_trajectories(trajectories,step=undef,start_position=0,steps=undef,loop=false,last_=identity4(),i_=0,current_length_=undef,j_=0) =
// due to quantization differences, the last step may be missed. In that case, add it: // due to quantization differences, the last step may be missed. In that case, add it:
loop==true ? quantize_trajectories( loop==true ? quantize_trajectories(
trajectories=close_trajectory_loop(trajectories), trajectories=close_trajectory_loop(trajectories),
step=step, step=step,
start_position = start_position, start_position = start_position,
steps=steps, steps=steps,
loop=false, loop=false,
last_=last_, last_=last_,
i_=i_, i_=i_,
current_length_=current_length_, current_length_=current_length_,
j_=j_) : j_=j_) :
i_ >= len(trajectories) ? (j_ < steps ? [last_] : []) : i_ >= len(trajectories) ? (j_ < steps ? [last_] : []) :
current_length_ == undef ? current_length_ == undef ?
quantize_trajectories( quantize_trajectories(
trajectories=trajectories, trajectories=trajectories,
step = (step == undef ? trajectories_length(trajectories) / steps : step), step = (step == undef ? trajectories_length(trajectories) / steps : step),
start_position = (step == undef ? start_position * trajectories_length(trajectories) / steps : start_position), start_position = (step == undef ? start_position * trajectories_length(trajectories) / steps : start_position),
steps=steps, steps=steps,
loop=loop, loop=loop,
last_=last_, last_=last_,
i_=i_, i_=i_,
current_length_=norm(take3(trajectories[i_])), current_length_=norm(take3(trajectories[i_])),
j_=j_) : j_=j_) :
concat( concat(
left_multiply(last_,quantize_trajectory( left_multiply(last_,quantize_trajectory(
trajectory=trajectories[i_], trajectory=trajectories[i_],
start_position=start_position, start_position=start_position,
step=step)), step=step)),
quantize_trajectories( quantize_trajectories(
trajectories=trajectories, trajectories=trajectories,
step=step, step=step,
start_position = start_position > current_length_ start_position = start_position > current_length_
? start_position - current_length_ ? start_position - current_length_
: step - ((current_length_-start_position) % step), : step - ((current_length_-start_position) % step),
steps=steps, steps=steps,
loop=loop, loop=loop,
last_=last_ * se3_exp(trajectories[i_]), last_=last_ * se3_exp(trajectories[i_]),
i_=i_+1, i_=i_+1,
current_length_ = undef, current_length_ = undef,
j_=j_+len( j_=j_+len(
quantize_trajectory( quantize_trajectory(
trajectory=trajectories[i_], trajectory=trajectories[i_],
start_position=start_position, start_position=start_position,
step=step step=step
)) ))
)) ))
; ;
function trajectories_length(trajectories, i_=0) = i_ >= len(trajectories) ? 0 function trajectories_length(trajectories, i_=0) = i_ >= len(trajectories) ? 0
: norm(take3(trajectories[i_])) + trajectories_length(trajectories,i_+1); : norm(take3(trajectories[i_])) + trajectories_length(trajectories,i_+1);
function trajectories_end_position(rt,i_=0,last_=identity4()) = function trajectories_end_position(rt,i_=0,last_=identity4()) =
i_ >= len(rt) ? last_ : i_ >= len(rt) ? last_ :
trajectories_end_position(rt, i_+1, last_ * se3_exp(rt[i_])); trajectories_end_position(rt, i_+1, last_ * se3_exp(rt[i_]));

View File

@ -8,32 +8,32 @@ use <lists.scad>
xyz = euler angles = rz * ry * rx xyz = euler angles = rz * ry * rx
axis = rotation_axis * rotation_angle axis = rotation_axis * rotation_angle
*/ */
function rotation(xyz=undef, axis=undef) = function rotation(xyz=undef, axis=undef) =
xyz != undef && axis != undef ? undef : xyz != undef && axis != undef ? undef :
xyz == undef ? se3_exp([0,0,0,axis[0],axis[1],axis[2]]) : xyz == undef ? se3_exp([0,0,0,axis[0],axis[1],axis[2]]) :
len(xyz) == undef ? rotation(axis=[0,0,xyz]) : len(xyz) == undef ? rotation(axis=[0,0,xyz]) :
(len(xyz) >= 3 ? rotation(axis=[0,0,xyz[2]]) : identity4()) * (len(xyz) >= 3 ? rotation(axis=[0,0,xyz[2]]) : identity4()) *
(len(xyz) >= 2 ? rotation(axis=[0,xyz[1],0]) : identity4()) * (len(xyz) >= 2 ? rotation(axis=[0,xyz[1],0]) : identity4()) *
(len(xyz) >= 1 ? rotation(axis=[xyz[0],0,0]) : identity4()); (len(xyz) >= 1 ? rotation(axis=[xyz[0],0,0]) : identity4());
/*! /*!
Creates a scaling matrix Creates a scaling matrix
*/ */
function scaling(v) = [ function scaling(v) = [
[v[0],0,0,0], [v[0],0,0,0],
[0,v[1],0,0], [0,v[1],0,0],
[0,0,v[2],0], [0,0,v[2],0],
[0,0,0,1], [0,0,0,1],
]; ];
/*! /*!
Creates a translation matrix Creates a translation matrix
*/ */
function translation(v) = [ function translation(v) = [
[1,0,0,v[0]], [1,0,0,v[0]],
[0,1,0,v[1]], [0,1,0,v[1]],
[0,0,1,v[2]], [0,0,1,v[2]],
[0,0,0,1], [0,0,0,1],
]; ];
// Convert between cartesian and homogenous coordinates // Convert between cartesian and homogenous coordinates

View File

@ -3,69 +3,69 @@ use <scad-utils/lists.scad>
// Skin a set of profiles with a polyhedral mesh // Skin a set of profiles with a polyhedral mesh
module skin(profiles, loop=false /* unimplemented */) { module skin(profiles, loop=false /* unimplemented */) {
P = max_len(profiles); P = max_len(profiles);
N = len(profiles); N = len(profiles);
profiles = [ profiles = [
for (p = profiles) for (p = profiles)
for (pp = augment_profile(to_3d(p),P)) for (pp = augment_profile(to_3d(p),P))
pp pp
]; ];
function quad(i,P,o) = [[o+i, o+i+P, o+i%P+P+1], [o+i, o+i%P+P+1, o+i%P+1]]; function quad(i,P,o) = [[o+i, o+i+P, o+i%P+P+1], [o+i, o+i%P+P+1, o+i%P+1]];
function profile_triangles(tindex) = [ function profile_triangles(tindex) = [
for (index = [0:P-1]) for (index = [0:P-1])
let (qs = quad(index+1, P, P*(tindex-1)-1)) let (qs = quad(index+1, P, P*(tindex-1)-1))
for (q = qs) q for (q = qs) q
]; ];
triangles = [ triangles = [
for(index = [1:N-1]) for(index = [1:N-1])
for(t = profile_triangles(index)) for(t = profile_triangles(index))
t t
]; ];
start_cap = [range([0:P-1])]; start_cap = [range([0:P-1])];
end_cap = [range([P*N-1 : -1 : P*(N-1)])]; end_cap = [range([P*N-1 : -1 : P*(N-1)])];
polyhedron(convexity=2, points=profiles, faces=concat(start_cap, triangles, end_cap)); polyhedron(convexity=2, points=profiles, faces=concat(start_cap, triangles, end_cap));
} }
// Augments the profile with steiner points making the total number of vertices n // Augments the profile with steiner points making the total number of vertices n
function augment_profile(profile, n) = function augment_profile(profile, n) =
subdivide(profile,insert_extra_vertices_0([profile_lengths(profile),dup(0,len(profile))],n-len(profile))[1]); subdivide(profile,insert_extra_vertices_0([profile_lengths(profile),dup(0,len(profile))],n-len(profile))[1]);
function subdivide(profile,subdivisions) = let (N=len(profile)) [ function subdivide(profile,subdivisions) = let (N=len(profile)) [
for (i = [0:N-1]) for (i = [0:N-1])
let(n = len(subdivisions)>0 ? subdivisions[i] : subdivisions) let(n = len(subdivisions)>0 ? subdivisions[i] : subdivisions)
for (p = interpolate(profile[i],profile[(i+1)%N],n+1)) for (p = interpolate(profile[i],profile[(i+1)%N],n+1))
p p
]; ];
function interpolate(a,b,subdivisions) = [ function interpolate(a,b,subdivisions) = [
for (index = [0:subdivisions-1]) for (index = [0:subdivisions-1])
let(t = index/subdivisions) let(t = index/subdivisions)
a*(1-t)+b*t a*(1-t)+b*t
]; ];
function distribute_extra_vertex(lengths_count,ma_=-1) = ma_<0 ? distribute_extra_vertex(lengths_count, max_element(lengths_count[0])) : function distribute_extra_vertex(lengths_count,ma_=-1) = ma_<0 ? distribute_extra_vertex(lengths_count, max_element(lengths_count[0])) :
concat([set(lengths_count[0],ma_,lengths_count[0][ma_] * (lengths_count[1][ma_]+1) / (lengths_count[1][ma_]+2))], [increment(lengths_count[1],max_element(lengths_count[0]),1)]); concat([set(lengths_count[0],ma_,lengths_count[0][ma_] * (lengths_count[1][ma_]+1) / (lengths_count[1][ma_]+2))], [increment(lengths_count[1],max_element(lengths_count[0]),1)]);
function insert_extra_vertices_0(lengths_count,n_extra) = n_extra <= 0 ? lengths_count : function insert_extra_vertices_0(lengths_count,n_extra) = n_extra <= 0 ? lengths_count :
insert_extra_vertices_0(distribute_extra_vertex(lengths_count),n_extra-1); insert_extra_vertices_0(distribute_extra_vertex(lengths_count),n_extra-1);
// Find the index of the maximum element of arr // Find the index of the maximum element of arr
function max_element(arr,ma_,ma_i_=-1,i_=0) = i_ >= len(arr) ? ma_i_ : function max_element(arr,ma_,ma_i_=-1,i_=0) = i_ >= len(arr) ? ma_i_ :
i_ == 0 || arr[i_] > ma_ ? max_element(arr,arr[i_],i_,i_+1) : max_element(arr,ma_,ma_i_,i_+1); i_ == 0 || arr[i_] > ma_ ? max_element(arr,arr[i_],i_,i_+1) : max_element(arr,ma_,ma_i_,i_+1);
function max_len(arr) = max([for (i=arr) len(i)]); function max_len(arr) = max([for (i=arr) len(i)]);
function increment(arr,i,x=1) = set(arr,i,arr[i]+x); function increment(arr,i,x=1) = set(arr,i,arr[i]+x);
function profile_lengths(profile) = [ function profile_lengths(profile) = [
for (i = [0:len(profile)-1]) for (i = [0:len(profile)-1])
profile_segment_length(profile,i) profile_segment_length(profile,i)
]; ];
function profile_segment_length(profile,i) = norm(profile[(i+1)%len(profile)] - profile[i]); function profile_segment_length(profile,i) = norm(profile[(i+1)%len(profile)] - profile[i]);
@ -85,14 +85,14 @@ use <scad-utils/trajectory.scad>
use <scad-utils/shapes.scad> use <scad-utils/shapes.scad>
module fakeISOEnter(thickness_difference = 0){ module fakeISOEnter(thickness_difference = 0){
// 1u is the space taken upy by a 1u keycap. // 1u is the space taken upy by a 1u keycap.
// unit is the space taken up by a unit space for a keycap. // unit is the space taken up by a unit space for a keycap.
// formula is 1u + unit *(length - 1) // formula is 1u + unit *(length - 1)
// t is all modifications to the polygon array // t is all modifications to the polygon array
t = thickness_difference/2 - (19.02 - 18.16); t = thickness_difference/2 - (19.02 - 18.16);
function unit(length) = 19.02 * length; function unit(length) = 19.02 * length;
pointArray = [ pointArray = [
[19.05 * (-.5) + t, 19.05 * (-1) + t], [19.05 * (-.5) + t, 19.05 * (-1) + t],
@ -104,12 +104,12 @@ module fakeISOEnter(thickness_difference = 0){
]; ];
/*translate([unit(-.5), unit(-1) + 0.86]){*/ /*translate([unit(-.5), unit(-1) + 0.86]){*/
minkowski() { minkowski() {
circle($corner_radius, $fn=20); circle($corner_radius, $fn=20);
offset(r=-$corner_radius * 2, $fn=20) polygon(points=pointArray); offset(r=-$corner_radius * 2, $fn=20) polygon(points=pointArray);
} }
/*}*/ /*}*/
} }
function isoEnter() = [ function isoEnter() = [

View File

@ -7,15 +7,15 @@ include <shapes/square.scad>
include <shapes/oblong.scad> include <shapes/oblong.scad>
module key_shape(size, delta, progress = 0) { module key_shape(size, delta, progress = 0) {
if ($key_shape_type == "iso_enter") { if ($key_shape_type == "iso_enter") {
ISO_enter_shape(size, delta, progress); ISO_enter_shape(size, delta, progress);
} else if ($key_shape_type == "rounded_square") { } else if ($key_shape_type == "rounded_square") {
rounded_square_shape(size, delta, progress); rounded_square_shape(size, delta, progress);
} else if ($key_shape_type == "square") { } else if ($key_shape_type == "square") {
square_shape(size, delta, progress); square_shape(size, delta, progress);
} else if ($key_shape_type == "oblong") { } else if ($key_shape_type == "oblong") {
oblong_shape(size, delta, progress); oblong_shape(size, delta, progress);
} else { } else {
echo("Warning: unsupported $key_shape_type"); echo("Warning: unsupported $key_shape_type");
} }
} }

View File

@ -1,19 +1,19 @@
// corollary is rounded_square // corollary is rounded_square
// NOT 3D // NOT 3D
module ISO_enter_shape(size, delta, progress){ module ISO_enter_shape(size, delta, progress){
width = size[0]; width = size[0];
height = size[1]; height = size[1];
function unit_length(length) = unit * (length - 1) + 18.16; function unit_length(length) = unit * (length - 1) + 18.16;
// in order to make the ISO keycap shape generic, we are going to express the // in order to make the ISO keycap shape generic, we are going to express the
// 'elbow point' in terms of ratios. an ISO enter is just a 1.5u key stuck on // 'elbow point' in terms of ratios. an ISO enter is just a 1.5u key stuck on
// top of a 1.25u key, but since our key_shape function doesnt understand that // top of a 1.25u key, but since our key_shape function doesnt understand that
// and wants to pass just width and height, we make these ratios to know where // and wants to pass just width and height, we make these ratios to know where
// to put the elbow joint // to put the elbow joint
width_ratio = unit_length(1.25) / unit_length(1.5); width_ratio = unit_length(1.25) / unit_length(1.5);
height_ratio = unit_length(1) / unit_length(2); height_ratio = unit_length(1) / unit_length(2);
pointArray = [ pointArray = [
[ 0, 0], // top right [ 0, 0], // top right
@ -24,11 +24,11 @@ module ISO_enter_shape(size, delta, progress){
[ -width, 0] // top left [ -width, 0] // top left
]; ];
minkowski(){ minkowski(){
circle(r=corner_size); circle(r=corner_size);
// gives us rounded inner corner // gives us rounded inner corner
offset(r=-corner_size*2) { offset(r=-corner_size*2) {
translate([(width * width_ratio)/2, height/2]) polygon(points=pointArray); translate([(width * width_ratio)/2, height/2]) polygon(points=pointArray);
} }
} }
} }

View File

@ -1,11 +1,11 @@
module oblong_shape(size, delta, progress) { module oblong_shape(size, delta, progress) {
// .05 is because of offset. if we set offset to be half the height of the shape, and then subtract height from the shape, the height of the shape will be zero (because the shape would be [width - height, height - height]). that doesn't play well with openSCAD (understandably), so we add this tiny fudge factor to make sure the shape we offset has a positive width // .05 is because of offset. if we set offset to be half the height of the shape, and then subtract height from the shape, the height of the shape will be zero (because the shape would be [width - height, height - height]). that doesn't play well with openSCAD (understandably), so we add this tiny fudge factor to make sure the shape we offset has a positive width
height = size[1] - delta[1] * progress - .05; height = size[1] - delta[1] * progress - .05;
if (progress < 0.5) { if (progress < 0.5) {
} else { } else {
offset(r=height / 2) { offset(r=height / 2) {
square(size - [height, height] - delta * progress, center=true); square(size - [height, height] - delta * progress, center=true);
} }
} }
} }

View File

@ -5,28 +5,28 @@ function side_sculpting(progress) = (1 - progress) * 2.5;
function corner_sculpting(progress) = pow(progress, 2); function corner_sculpting(progress) = pow(progress, 2);
module rounded_square_shape(size, delta, progress, center = true) { module rounded_square_shape(size, delta, progress, center = true) {
width = size[0]; width = size[0];
height = size[1]; height = size[1];
width_difference = delta[0]; width_difference = delta[0];
height_difference = delta[1]; height_difference = delta[1];
// makes the sides bow // makes the sides bow
extra_side_size = $enable_side_sculpting ? side_sculpting(progress) : 0; extra_side_size = $enable_side_sculpting ? side_sculpting(progress) : 0;
// makes the rounded corners of the keycap grow larger as they move upwards // makes the rounded corners of the keycap grow larger as they move upwards
extra_corner_size = $enable_side_sculpting ? corner_sculpting(progress) : 0; extra_corner_size = $enable_side_sculpting ? corner_sculpting(progress) : 0;
// computed values for this slice // computed values for this slice
extra_width_this_slice = (width_difference - extra_side_size) * progress; extra_width_this_slice = (width_difference - extra_side_size) * progress;
extra_height_this_slice = (height_difference - extra_side_size) * progress; extra_height_this_slice = (height_difference - extra_side_size) * progress;
extra_corner_radius_this_slice = ($corner_radius + extra_corner_size); extra_corner_radius_this_slice = ($corner_radius + extra_corner_size);
offset(r=extra_corner_radius_this_slice){ offset(r=extra_corner_radius_this_slice){
square( square(
[ [
width - extra_width_this_slice - extra_corner_radius_this_slice * 2, width - extra_width_this_slice - extra_corner_radius_this_slice * 2,
height - extra_height_this_slice - extra_corner_radius_this_slice * 2 height - extra_height_this_slice - extra_corner_radius_this_slice * 2
], ],
center=center center=center
); );
} }
} }

View File

@ -6,13 +6,13 @@ include <stems/filled.scad>
//whole stem, alps or cherry, trimmed to fit //whole stem, alps or cherry, trimmed to fit
module stem(stem_type, depth, has_brim){ module stem(stem_type, depth, has_brim){
if (stem_type == "alps") { if (stem_type == "alps") {
alps_stem(depth, has_brim); alps_stem(depth, has_brim);
} else if (stem_type == "cherry_rounded") { } else if (stem_type == "cherry_rounded") {
rounded_cherry_stem(depth, has_brim); rounded_cherry_stem(depth, has_brim);
} else if (stem_type == "cherry") { } else if (stem_type == "cherry") {
cherry_stem(depth, has_brim); cherry_stem(depth, has_brim);
} else if (stem_type == "filled") { } else if (stem_type == "filled") {
filled_stem(); filled_stem();
} else { } else {
echo("Warning: unsupported $stem_type"); echo("Warning: unsupported $stem_type");