Compare commits
1 commit
master
...
github-des
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
15d87a8f95 |
32 changed files with 482 additions and 1981 deletions
|
|
@ -17,7 +17,7 @@ If you are technically inclined at all, this is definitely the best way to run t
|
|||
|
||||
First, you'll need OpenSCAD: http://www.openscad.org/downloads.html. I highly recommend installing the development snapshot, as they generally support more features and are relatively stable. Development snapshots are listed in their own section on the downloads page.
|
||||
|
||||
After you have openSCAD installed, you need to download the code and run it. running `git clone https://github.com/rsheldiii/KeyV2.git` if you have git, or downloading [this zip](https://github.com/rsheldiii/KeyV2/archive/master.zip) and extracting the directory should do it. Then all you need to do is open `keys.scad` with openSCAD and you are set! It is possible to edit this project with an external editor by checking off Design => 'Automatic Reload and Preview' in OpenSCAD.
|
||||
After you have openSCAD installed, you need to download the code and run it. running `git clone https://github.com/rsheldiii/openSCAD-projects.git` if you have git, or downloading [this zip](https://github.com/rsheldiii/openSCAD-projects/archive/master.zip) and extracting the directory should do it. Then all you need to do is open `keys.scad` with openSCAD and you are set! It is possible to edit this project with an external editor by checking off Design => 'Automatic Reload and Preview' in OpenSCAD.
|
||||
|
||||
All examples below assume you are running the library on your computer with OpenSCAD.
|
||||
|
||||
|
|
@ -60,7 +60,7 @@ You can chain as many modifier functions as you like!
|
|||
|
||||
## Modifier functions
|
||||
|
||||
There is a bevy of supporting functions to customize your keycaps. You can add a brim to more easily print the stem with `brimmed_stem_support`, make 2x2 keycaps with `2u() 2uh()`, add legends, rotate stems, and more. All these functions manipulate the settings available to you in [`settings.scad`](https://github.com/rsheldiii/KeyV2/blob/master/src/settings.scad), though [some of them](https://github.com/rsheldiii/KeyV2/blob/851ececdb297c77bfbcd0a7cb4cdbc5e21970396/src/key_transformations.scad#L128) are quite complex.
|
||||
There is a bevy of supporting functions to customize your keycaps. You can add a brim to more easily print the stem with `brimmed_stem_support`, make 2x2 keycaps with `2u() 2uh()`, add legends, rotate stems, and more. All these functions manipulate the settings available to you in [`settings.scad`](https://github.com/rsheldiii/KeyV2/blob/master/src/settings.scad), though [some of them](https://github.com/rsheldiii/KeyV2/blob/master/src/key_transformations.scad#L128) are quite complex.
|
||||
|
||||
These modifier functions can be found in [`key_profiles/`](https://github.com/rsheldiii/KeyV2/blob/master/src/key_profiles) for different keycap profiles, [`key_types.scad`](https://github.com/rsheldiii/KeyV2/blob/master/src/key_types.scad) for predefined settings for common keys (spacebar, left shift, etc), [`key_sizes.scad`](https://github.com/rsheldiii/KeyV2/blob/master/src/key_sizes.scad) for common unit sizes, and [`key_transformations.scad`](https://github.com/rsheldiii/KeyV2/blob/master/src/key_transformations.scad) for everything else. I encourage you to do some sleuthing but for a list of (most) helper functions with explanations, [Check out the wiki!](https://github.com/rsheldiii/KeyV2/wiki/KeyV2-Helper-Documentation)
|
||||
|
||||
|
|
|
|||
774
customizer.scad
774
customizer.scad
File diff suppressed because it is too large
Load diff
83
keys.scad
83
keys.scad
|
|
@ -5,11 +5,88 @@
|
|||
// without having to rely on this file. Unfortunately that means setting tons of
|
||||
// special variables, but that's a limitation of SCAD we have to work around
|
||||
|
||||
|
||||
echo($fa);
|
||||
echo("WHERES THE BEEF");
|
||||
|
||||
|
||||
include <./includes.scad>
|
||||
dsa_row(3) {
|
||||
union() {
|
||||
$key_shape_type="sculpted_square";
|
||||
$dish_type="disabled";
|
||||
$skin_extrude_shape = true;
|
||||
key();
|
||||
}
|
||||
}
|
||||
|
||||
/* $inverted_dish = true; */
|
||||
/* $rounded_key = true; */
|
||||
|
||||
/* $linear_extrude_shape=true; */
|
||||
/* $rounded_key=true; */
|
||||
/* $outset_legends = true; */
|
||||
/* $inverted_dish =true; */
|
||||
/* $dish_type = "pyramid"; */
|
||||
/* legend("q") key(); */
|
||||
/* $outset_legends = true; */
|
||||
dcs_row(3) {
|
||||
/* iso_enter() */
|
||||
union() {
|
||||
union() {
|
||||
/* $minkowski_radius = 10; */
|
||||
/* $minkowski_radius = 0.12; */
|
||||
|
||||
/* $key_shape_type = "iso_enter"; */
|
||||
$key_shape_type="sculpted_square";
|
||||
/* $dish_type ="disable"; */
|
||||
/* $inverted_dish = true; */
|
||||
/* $stem_type = "disable"; */
|
||||
/* $dish_type="3d_surface"; */
|
||||
/* $support_type = "disable"; */
|
||||
/* $stabilizer_type = "disable"; */
|
||||
/* rounded_shape(); */
|
||||
/* minkowski() { */
|
||||
|
||||
/* rounded_key(); */
|
||||
/* translate([0,0,-200]) cube(10); */
|
||||
|
||||
|
||||
// example key
|
||||
dcs_row(5) legend("⇪", size=9) key();
|
||||
/* difference(){ */
|
||||
/* key(); */
|
||||
/* cube(100); */
|
||||
/* } */
|
||||
|
||||
/* minkowski_shape(); */
|
||||
/* } */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* translate_u(1,0) {
|
||||
oem_row(3) {
|
||||
cherry() legend("q")
|
||||
union() {
|
||||
key();
|
||||
}
|
||||
}
|
||||
} */
|
||||
|
||||
/* difference() {
|
||||
translate([0,0,-.1]) cube(1.1);
|
||||
translate([0.5,0.5,-0.5]) polar_3d_surface(step=0.1);
|
||||
} */
|
||||
|
||||
|
||||
// Written in 2015 by Torsten Paul <Torsten.Paul@gmx.de>
|
||||
//
|
||||
// To the extent possible under law, the author(s) have dedicated all
|
||||
// copyright and related and neighboring rights to this software to the
|
||||
// public domain worldwide. This software is distributed without any
|
||||
// warranty.
|
||||
//
|
||||
// For details of the CC0 Public Domain Dedication see
|
||||
// <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
|
||||
// example row
|
||||
/* for (x = [0:1:4]) {
|
||||
|
|
@ -17,4 +94,4 @@ dcs_row(5) legend("⇪", size=9) key();
|
|||
} */
|
||||
|
||||
// example layout
|
||||
/* preonic_default("dcs"); */
|
||||
/* preonic_default("dcs"); */
|
||||
|
|
|
|||
|
|
@ -1,3 +1 @@
|
|||
SMALLEST_POSSIBLE = 1/128;
|
||||
$fs = .1;
|
||||
$unit = 19.05;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ include <dishes/old_spherical.scad>
|
|||
include <dishes/sideways_cylindrical.scad>
|
||||
include <dishes/spherical.scad>
|
||||
include <dishes/flat.scad>
|
||||
include <dishes/3d_surface.scad>
|
||||
|
||||
//geodesic looks much better, but runs very slow for anything above a 2u
|
||||
geodesic=false;
|
||||
|
|
@ -20,10 +19,9 @@ module dish(width, height, depth, inverted) {
|
|||
}
|
||||
else if ($dish_type == "sideways cylindrical"){
|
||||
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);
|
||||
} else if ($dish_type == "3d_surface") {
|
||||
3d_surface_dish(width, height, depth, inverted);
|
||||
} else if ($dish_type == "flat") {
|
||||
flat_dish(width, height, depth, inverted);
|
||||
} else if ($dish_type == "disable") {
|
||||
|
|
|
|||
|
|
@ -1,14 +0,0 @@
|
|||
include <../libraries/3d_surface.scad>
|
||||
|
||||
module 3d_surface_dish(width, height, depth, inverted) {
|
||||
echo(inverted ? "inverted" : "not inverted");
|
||||
// scale_factor is dead reckoning
|
||||
// it doesn't have to be dead reckoning for anything but sculpted sides
|
||||
// we know the angle of the sides from the width difference, height difference,
|
||||
// skew and tilt of the top. it's a pain to calculate though
|
||||
scale_factor = 1.1;
|
||||
// the edges on this behave differently than with the previous dish implementations
|
||||
scale([width*scale_factor/$3d_surface_size/2,height*scale_factor/$3d_surface_size/2,depth]) rotate([inverted ? 0:180,0,90]) polar_3d_surface(bottom=-10);
|
||||
/* %scale([width*scale_factor/$3d_surface_size/2,height*scale_factor/$3d_surface_size/2,depth]) rotate([180,0,0]) polar_3d_surface(bottom=-10); */
|
||||
|
||||
}
|
||||
|
|
@ -40,26 +40,3 @@ function vertical_inclination_due_to_top_tilt() = sin($top_tilt) * (top_total_ke
|
|||
// of the keycap a flat plane. 1 = front, -1 = back
|
||||
// I derived this through a bunch of trig reductions I don't really understand.
|
||||
function extra_keytop_length_for_flat_sides() = ($width_difference * vertical_inclination_due_to_top_tilt()) / ($total_depth);
|
||||
|
||||
// 3d surface functions (still in beta)
|
||||
|
||||
// monotonically increasing function that distributes the points of the surface mesh
|
||||
// only for polar_3d_surface right now
|
||||
// if it's linear it's a grid. sin(dim) * size concentrates detail around the edges
|
||||
function surface_distribution_function(dim, size) = sin(dim) * size;
|
||||
|
||||
// the function that actually determines what the surface is.
|
||||
// feel free to override, the last one wins
|
||||
|
||||
// debug
|
||||
function surface_function(x,y) = 1;
|
||||
// cylindrical
|
||||
function surface_function(x,y) = (sin(acos(x/$3d_surface_size)));
|
||||
// spherical
|
||||
function surface_function(x,y) = (sin(acos(x/$3d_surface_size))) * sin(acos(y/$3d_surface_size));
|
||||
// (statically) random!
|
||||
/* function surface_function(x,y) = sin(rands(0,90,1,x+y)[0]); */
|
||||
// adds uniform rounding radius for round-anything polyRound
|
||||
function add_rounding(p, radius)=[for(i=[0:len(p)-1])[p[i].x,p[i].y, radius]];
|
||||
// computes millimeter length from unit length
|
||||
function unit_length(length) = unit * (length - 1) + 18.16;
|
||||
|
|
|
|||
432
src/key.scad
432
src/key.scad
|
|
@ -1,12 +1,12 @@
|
|||
// files
|
||||
include <constants.scad>
|
||||
include <functions.scad>
|
||||
include <shapes.scad>
|
||||
include <stems.scad>
|
||||
include <stem_supports.scad>
|
||||
include <dishes.scad>
|
||||
include <supports.scad>
|
||||
include <key_features.scad>
|
||||
include <features.scad>
|
||||
include <hulls.scad>
|
||||
|
||||
include <libraries/geodesic_sphere.scad>
|
||||
|
||||
|
|
@ -16,205 +16,44 @@ use <libraries/scad-utils/lists.scad>
|
|||
use <libraries/scad-utils/shapes.scad>
|
||||
use <libraries/skin.scad>
|
||||
|
||||
/* [Hidden] */
|
||||
SMALLEST_POSSIBLE = 1/128;
|
||||
// basically disable $fs - though it might be useful for these CGAL problems
|
||||
$fs = .01;
|
||||
$unit = 19.05;
|
||||
|
||||
// key shape including dish. used as the ouside and inside shape in hollow_key(). allows for itself to be shrunk in depth and width / height
|
||||
module shape(thickness_difference, depth_difference=0){
|
||||
dished(depth_difference, $inverted_dish) {
|
||||
/* %shape_hull(thickness_difference, depth_difference, $inverted_dish ? 2 : 0); */
|
||||
color($primary_color) shape_hull(thickness_difference, depth_difference, $inverted_dish ? 2 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
// shape of the key but with soft, rounded edges. no longer includes dish
|
||||
// randomly doesnt work sometimes
|
||||
// the dish doesn't _quite_ reach as far as it should
|
||||
module rounded_shape() {
|
||||
dished(-$minkowski_radius, $inverted_dish) {
|
||||
color($primary_color) minkowski(){
|
||||
// half minkowski in the z direction
|
||||
color($primary_color) shape_hull($minkowski_radius * 2, $minkowski_radius/2, $inverted_dish ? 2 : 0);
|
||||
/* cube($minkowski_radius); */
|
||||
sphere(r=$minkowski_radius, $fn=$minkowski_facets);
|
||||
}
|
||||
}
|
||||
/* %envelope(); */
|
||||
}
|
||||
|
||||
// this function is more correct, but takes _forever_
|
||||
// the main difference is minkowski happens after dishing, meaning the dish is
|
||||
// also minkowski'd
|
||||
/* module rounded_shape() {
|
||||
module rounded_shape() {
|
||||
color($primary_color) minkowski(){
|
||||
// half minkowski in the z direction
|
||||
shape($minkowski_radius * 2, $minkowski_radius/2);
|
||||
difference(){
|
||||
sphere(r=$minkowski_radius, $fn=20);
|
||||
translate([0,0,-$minkowski_radius]){
|
||||
cube($minkowski_radius * 2, center=true);
|
||||
}
|
||||
}
|
||||
minkowski_object();
|
||||
}
|
||||
} */
|
||||
}
|
||||
|
||||
// minkowski places this object at every vertex of the other object then mashes
|
||||
// it all together
|
||||
module minkowski_object() {
|
||||
// alternative minkowski shape that needs the bottom of the keycap to be trimmed
|
||||
/* sphere(1); */
|
||||
|
||||
|
||||
// basic key shape, no dish, no inside
|
||||
// which is only used for dishing to cut the dish off correctly
|
||||
// $height_difference used for keytop thickness
|
||||
// extra_slices is a hack to make inverted dishes still work
|
||||
module shape_hull(thickness_difference, depth_difference, extra_slices = 0){
|
||||
render() {
|
||||
if ($skin_extrude_shape) {
|
||||
skin_extrude_shape_hull(thickness_difference, depth_difference, extra_slices);
|
||||
} else if ($linear_extrude_shape) {
|
||||
linear_extrude_shape_hull(thickness_difference, depth_difference, extra_slices);
|
||||
} else {
|
||||
hull_shape_hull(thickness_difference, depth_difference, extra_slices);
|
||||
difference(){
|
||||
sphere(r=$minkowski_radius, $fa=360/$minkowski_facets);
|
||||
translate([0,0,-$minkowski_radius]){
|
||||
cube($minkowski_radius * 2, center=true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// use skin() instead of successive hulls. much more correct, and looks faster
|
||||
// too, in most cases. successive hull relies on overlapping faces which are
|
||||
// not good. But, skin works on vertex sets instead of shapes, which makes it
|
||||
// a lot more difficult to use
|
||||
module skin_extrude_shape_hull(thickness_difference, depth_difference, extra_slices = 0 ) {
|
||||
skin([
|
||||
for (index = [0:$height_slices + extra_slices])
|
||||
let(
|
||||
progress = (index / $height_slices),
|
||||
skew_this_slice = $top_skew * progress,
|
||||
x_skew_this_slice = $top_skew_x * progress,
|
||||
depth_this_slice = ($total_depth - depth_difference) * progress,
|
||||
tilt_this_slice = -$top_tilt / $key_height * progress,
|
||||
y_tilt_this_slice = $double_sculpted ? (-$top_tilt_y / $key_length * progress) : 0
|
||||
)
|
||||
skin_shape_slice(progress, thickness_difference, skew_this_slice, x_skew_this_slice, depth_this_slice, tilt_this_slice, y_tilt_this_slice)
|
||||
]);
|
||||
}
|
||||
|
||||
function skin_shape_slice(progress, thickness_difference, skew_this_slice, x_skew_this_slice, depth_this_slice, tilt_this_slice, y_tilt_this_slice) =
|
||||
transform(
|
||||
translation([x_skew_this_slice,skew_this_slice,depth_this_slice]),
|
||||
transform(
|
||||
rotation([tilt_this_slice,y_tilt_this_slice,0]),
|
||||
skin_key_shape([
|
||||
total_key_width(0),
|
||||
total_key_height(0),
|
||||
],
|
||||
[$width_difference, $height_difference],
|
||||
progress,
|
||||
thickness_difference
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
// corollary is hull_shape_hull
|
||||
// extra_slices unused, only to match argument signatures
|
||||
module linear_extrude_shape_hull(thickness_difference, depth_difference, extra_slices = 0){
|
||||
height = $total_depth - depth_difference;
|
||||
width_scale = top_total_key_width() / total_key_width();
|
||||
height_scale = top_total_key_height() / total_key_height();
|
||||
|
||||
translate([0,$linear_extrude_height_adjustment,0]){
|
||||
linear_extrude(height = height, scale = [width_scale, height_scale]) {
|
||||
translate([0,-$linear_extrude_height_adjustment,0]){
|
||||
key_shape(
|
||||
[total_key_width(thickness_difference), total_key_height(thickness_difference)],
|
||||
[$width_difference, $height_difference]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module hull_shape_hull(thickness_difference, depth_difference, extra_slices = 0) {
|
||||
for (index = [0:$height_slices - 1 + extra_slices]) {
|
||||
hull() {
|
||||
shape_slice(index / $height_slices, thickness_difference, depth_difference);
|
||||
shape_slice((index + 1) / $height_slices, thickness_difference, depth_difference);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module shape_slice(progress, thickness_difference, depth_difference) {
|
||||
skew_this_slice = $top_skew * progress;
|
||||
x_skew_this_slice = $top_skew_x * progress;
|
||||
|
||||
depth_this_slice = ($total_depth - depth_difference) * progress;
|
||||
|
||||
tilt_this_slice = -$top_tilt / $key_height * progress;
|
||||
y_tilt_this_slice = $double_sculpted ? (-$top_tilt_y / $key_length * progress) : 0;
|
||||
|
||||
translate([x_skew_this_slice, skew_this_slice, depth_this_slice]) {
|
||||
rotate([tilt_this_slice,y_tilt_this_slice,0]){
|
||||
linear_extrude(height = SMALLEST_POSSIBLE){
|
||||
key_shape(
|
||||
[
|
||||
total_key_width(thickness_difference),
|
||||
total_key_height(thickness_difference)
|
||||
],
|
||||
[$width_difference, $height_difference],
|
||||
progress
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// for when you want something to only exist inside the keycap.
|
||||
// used for the support structure
|
||||
module inside() {
|
||||
intersection() {
|
||||
shape($wall_thickness, $keytop_thickness);
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
// for when you want something to only exist outside the keycap
|
||||
module outside() {
|
||||
difference() {
|
||||
children();
|
||||
shape($wall_thickness, $keytop_thickness);
|
||||
}
|
||||
}
|
||||
|
||||
// put something at the top of the key, with no adjustments for dishing
|
||||
module top_placement(depth_difference=0) {
|
||||
top_tilt_by_height = -$top_tilt / $key_height;
|
||||
top_tilt_y_by_length = $double_sculpted ? (-$top_tilt_y / $key_length) : 0;
|
||||
|
||||
minkowski_height = $rounded_key ? $minkowski_radius : 0;
|
||||
|
||||
translate([$top_skew_x + $dish_skew_x, $top_skew + $dish_skew_y, $total_depth - depth_difference + minkowski_height/2]){
|
||||
rotate([top_tilt_by_height, top_tilt_y_by_length,0]){
|
||||
children();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module front_placement() {
|
||||
// all this math is to take top skew and tilt into account
|
||||
// we need to find the new effective height and depth of the top, front lip
|
||||
// of the keycap to find the angle so we can rotate things correctly into place
|
||||
total_depth_difference = sin(-$top_tilt) * (top_total_key_height()/2);
|
||||
total_height_difference = $top_skew + (1 - cos(-$top_tilt)) * (top_total_key_height()/2);
|
||||
|
||||
angle = atan2(($total_depth - total_depth_difference), ($height_difference/2 + total_height_difference));
|
||||
hypotenuse = ($total_depth -total_depth_difference) / sin(angle);
|
||||
|
||||
translate([0,-total_key_height()/2,0]) {
|
||||
rotate([-(90-angle), 0, 0]) {
|
||||
translate([0,0,hypotenuse/2]){
|
||||
children();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// just to DRY up the code
|
||||
module _dish() {
|
||||
translate([$dish_offset_x,0,0]) dish(top_total_key_width() + $dish_overdraw_width, top_total_key_height() + $dish_overdraw_height, $dish_depth, $inverted_dish);
|
||||
}
|
||||
|
||||
module envelope(depth_difference=0) {
|
||||
s = 1.5;
|
||||
|
|
@ -226,18 +65,6 @@ module envelope(depth_difference=0) {
|
|||
}
|
||||
}
|
||||
|
||||
// I think this is unused
|
||||
module dished_for_show() {
|
||||
difference(){
|
||||
union() {
|
||||
envelope();
|
||||
if ($inverted_dish) top_placement(0) color($secondary_color) _dish();
|
||||
}
|
||||
if (!$inverted_dish) top_placement(0) color($secondary_color) _dish();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 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
|
||||
// creates a bounding box 1.5 times larger in width and height than the keycap.
|
||||
|
|
@ -246,16 +73,21 @@ module dished(depth_difference = 0, inverted = false) {
|
|||
children();
|
||||
difference(){
|
||||
union() {
|
||||
// envelope is needed to "fill in" the rest of the keycap
|
||||
envelope(depth_difference);
|
||||
if (inverted) top_placement(depth_difference) color($secondary_color) _dish();
|
||||
if (inverted) top_placement(depth_difference) _dish(inverted);
|
||||
}
|
||||
if (!inverted) top_placement(depth_difference) color($secondary_color) _dish();
|
||||
/* %top_placement(depth_difference) _dish(); */
|
||||
if (!inverted) top_placement(depth_difference) _dish(inverted);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// puts it's children at the center of the dishing on the key, including dish height
|
||||
// just to DRY up the code
|
||||
module _dish(inverted=$inverted_dish) {
|
||||
color($secondary_color) dish(top_total_key_width() + $dish_overdraw_width, top_total_key_height() + $dish_overdraw_height, $dish_depth, inverted);
|
||||
}
|
||||
|
||||
// puts its children at the center of the dishing on the key, including dish height
|
||||
// more user-friendly than top_placement
|
||||
module top_of_key(){
|
||||
// if there is a dish, we need to account for how much it digs into the top
|
||||
|
|
@ -268,16 +100,7 @@ module top_of_key(){
|
|||
}
|
||||
}
|
||||
|
||||
module keytext(text, position, font_size, depth) {
|
||||
woffset = (top_total_key_width()/3.5) * position[0];
|
||||
hoffset = (top_total_key_height()/3.5) * -position[1];
|
||||
translate([woffset, hoffset, -depth]){
|
||||
color($tertiary_color) linear_extrude(height=$dish_depth){
|
||||
text(text=text, font=$font, size=font_size, halign="center", valign="center");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// puts its children at each keystem position provided
|
||||
module keystem_positions(positions) {
|
||||
for (connector_pos = positions) {
|
||||
translate(connector_pos) {
|
||||
|
|
@ -296,132 +119,139 @@ module support_for(positions, stem_type) {
|
|||
|
||||
module stems_for(positions, stem_type) {
|
||||
keystem_positions(positions) {
|
||||
color($tertiary_color) stem(stem_type, $total_depth, $stem_slop, $stem_throw);
|
||||
color($tertiary_color) stem(stem_type, $total_depth, $stem_slop);
|
||||
if ($stem_support_type != "disable") {
|
||||
color($quaternary_color) stem_support($stem_support_type, stem_type, $stem_support_height, $stem_slop);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// a fake cherry keyswitch, abstracted out to maybe replace with a better one later
|
||||
module cherry_keyswitch() {
|
||||
union() {
|
||||
hull() {
|
||||
cube([15.6, 15.6, 0.01], center=true);
|
||||
translate([0,1,5 - 0.01]) cube([10.5,9.5, 0.01], center=true);
|
||||
}
|
||||
hull() {
|
||||
cube([15.6, 15.6, 0.01], center=true);
|
||||
translate([0,0,-5.5]) cube([13.5,13.5,0.01], center=true);
|
||||
// put something at the top of the key, with no adjustments for dishing
|
||||
module top_placement(depth_difference=0) {
|
||||
top_tilt_by_height = -$top_tilt / $key_height;
|
||||
top_tilt_y_by_length = $double_sculpted ? (-$top_tilt_y / $key_length) : 0;
|
||||
|
||||
minkowski_height = $rounded_key ? $minkowski_radius : 0;
|
||||
|
||||
translate([$top_skew_x + $dish_skew_x, $top_skew + $dish_skew_y, $total_depth - depth_difference + minkowski_height/2]){
|
||||
rotate([top_tilt_by_height, top_tilt_y_by_length,0]){
|
||||
children();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//approximate (fully depressed) cherry key to check clearances
|
||||
module clearance_check() {
|
||||
if($stem_type == "cherry" || $stem_type == "cherry_rounded"){
|
||||
color($warning_color){
|
||||
translate([0,0,3.6 + $stem_inset - 5]) {
|
||||
cherry_keyswitch();
|
||||
module front_of_key() {
|
||||
// all this math is to take top skew and tilt into account
|
||||
// we need to find the new effective height and depth of the top, front lip
|
||||
// of the keycap to find the angle so we can rotate things correctly into place
|
||||
total_depth_difference = sin(-$top_tilt) * (top_total_key_height()/2);
|
||||
total_height_difference = $top_skew + (1 - cos(-$top_tilt)) * (top_total_key_height()/2);
|
||||
|
||||
angle = atan2(($total_depth - total_depth_difference), ($height_difference/2 + total_height_difference));
|
||||
hypotenuse = ($total_depth -total_depth_difference) / sin(angle);
|
||||
|
||||
translate([0,-total_key_height()/2,0]) {
|
||||
rotate([-(90-angle), 0, 0]) {
|
||||
translate([0,0,hypotenuse/2]){
|
||||
children();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module legends(depth=0) {
|
||||
if (len($front_legends) > 0) {
|
||||
front_placement() {
|
||||
if (len($front_legends) > 0) {
|
||||
for (i=[0:len($front_legends)-1]) {
|
||||
rotate([90,0,0]) keytext($front_legends[i][0], $front_legends[i][1], $front_legends[i][2], depth);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (len($legends) > 0) {
|
||||
top_of_key() {
|
||||
// outset legend
|
||||
if (len($legends) > 0) {
|
||||
for (i=[0:len($legends)-1]) {
|
||||
keytext($legends[i][0], $legends[i][1], $legends[i][2], depth);
|
||||
}
|
||||
}
|
||||
}
|
||||
module outer_shape() {
|
||||
if ($rounded_key) {
|
||||
rounded_shape();
|
||||
} else {
|
||||
shape(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// legends / artisan support
|
||||
module artisan(depth) {
|
||||
top_of_key() {
|
||||
// artisan objects / outset shape legends
|
||||
color($secondary_color) children();
|
||||
}
|
||||
}
|
||||
|
||||
// key with hollowed inside but no stem
|
||||
module hollow_key() {
|
||||
difference(){
|
||||
if ($rounded_key) {
|
||||
rounded_shape();
|
||||
module inner_shape(extra_wall_thickness = 0, extra_keytop_thickness = 0) {
|
||||
translate([0,0,-SMALLEST_POSSIBLE]) {
|
||||
if ($flat_keytop_bottom) {
|
||||
/* $key_shape_type="square"; */
|
||||
$height_slices = 1;
|
||||
color($primary_color) shape_hull($wall_thickness + extra_wall_thickness, $keytop_thickness + extra_keytop_thickness, 0);
|
||||
} else {
|
||||
shape(0, 0);
|
||||
}
|
||||
// translation purely for aesthetic purposes, to get rid of that awful lattice
|
||||
translate([0,0,-SMALLEST_POSSIBLE]) {
|
||||
shape($wall_thickness, $keytop_thickness);
|
||||
shape($wall_thickness + extra_wall_thickness, $keytop_thickness + extra_keytop_thickness);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// additive objects at the top of the key
|
||||
module additive_features(inset) {
|
||||
top_of_key() {
|
||||
if($key_bump) keybump($key_bump_depth, $key_bump_edge);
|
||||
if(!inset && $children > 0) color($secondary_color) children();
|
||||
}
|
||||
if($outset_legends) legends(0);
|
||||
// render the clearance check if it's enabled, but don't have it intersect with anything
|
||||
if ($clearance_check) %clearance_check();
|
||||
}
|
||||
|
||||
// subtractive objects at the top of the key
|
||||
module subtractive_features(inset) {
|
||||
top_of_key() {
|
||||
if (inset && $children > 0) color($secondary_color) children();
|
||||
}
|
||||
if(!$outset_legends) legends($inset_legend_depth);
|
||||
// subtract the clearance check if it's enabled, letting the user see the
|
||||
// parts of the keycap that will hit the cherry switch
|
||||
if ($clearance_check) %clearance_check();
|
||||
}
|
||||
|
||||
module inside_features() {
|
||||
translate([0, 0, $stem_inset]) {
|
||||
// both stem and support are optional
|
||||
if ($stabilizer_type != "disable") stems_for($stabilizers, $stabilizer_type);
|
||||
if ($stem_type != "disable") stems_for($stem_positions, $stem_type);
|
||||
if ($stabilizer_type != "disable") support_for($stabilizers, $stabilizer_type);
|
||||
// always render stem support even if there isn't a stem.
|
||||
// rendering flat support w/no stem is much more common than a hollow keycap
|
||||
// so if you want a hollow keycap you'll have to turn support off entirely
|
||||
support_for($stem_positions, $stem_type);
|
||||
}
|
||||
}
|
||||
|
||||
// The final, penultimate key generation function.
|
||||
// takes all the bits and glues them together. requires configuration with special variables.
|
||||
module key(inset = false) {
|
||||
module key(inset=false) {
|
||||
difference(){
|
||||
union() {
|
||||
outer_shape();
|
||||
additive_features(inset);
|
||||
}
|
||||
|
||||
difference() {
|
||||
inner_shape();
|
||||
inside_features();
|
||||
}
|
||||
|
||||
subtractive_features(inset);
|
||||
}
|
||||
}
|
||||
|
||||
module rounded_key(inset=false) {
|
||||
difference() {
|
||||
union(){
|
||||
// the shape of the key, inside and out
|
||||
hollow_key();
|
||||
if($key_bump) top_of_key() keybump($key_bump_depth, $key_bump_edge);
|
||||
// additive objects at the top of the key
|
||||
// outside() makes them stay out of the inside. it's a bad name
|
||||
if(!inset && $children > 0) outside() artisan(0) children();
|
||||
if($outset_legends) legends(0);
|
||||
// render the clearance check if it's enabled, but don't have it intersect with anything
|
||||
if ($clearance_check) %clearance_check();
|
||||
}
|
||||
|
||||
// subtractive objects at the top of the key
|
||||
// no outside() - I can't think of a use for it. will save render time
|
||||
if (inset && $children > 0) artisan($inset_legend_depth) children();
|
||||
if(!$outset_legends) legends($inset_legend_depth);
|
||||
// subtract the clearance check if it's enabled, letting the user see the
|
||||
// parts of the keycap that will hit the cherry switch
|
||||
if ($clearance_check) %clearance_check();
|
||||
}
|
||||
|
||||
// both stem and support are optional
|
||||
if ($stem_type != "disable" || ($stabilizers != [] && $stabilizer_type != "disable")) {
|
||||
dished($keytop_thickness, $inverted_dish) {
|
||||
translate([0, 0, $stem_inset]) {
|
||||
if ($stabilizer_type != "disable") stems_for($stabilizers, $stabilizer_type);
|
||||
|
||||
if ($stem_type != "disable") stems_for($stem_positions, $stem_type);
|
||||
minkowski() {
|
||||
difference() {
|
||||
outer_shape();
|
||||
inner_shape();
|
||||
}
|
||||
|
||||
minkowski_object();
|
||||
}
|
||||
|
||||
subtractive_features(inset);
|
||||
}
|
||||
|
||||
if ($support_type != "disable"){
|
||||
inside() {
|
||||
translate([0, 0, $stem_inset]) {
|
||||
if ($stabilizer_type != "disable") support_for($stabilizers, $stabilizer_type);
|
||||
/* additive_features(inset); */
|
||||
|
||||
// always render stem support even if there isn't a stem.
|
||||
// rendering flat support w/no stem is much more common than a hollow keycap
|
||||
// so if you want a hollow keycap you'll have to turn support off entirely
|
||||
support_for($stem_positions, $stem_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* intersection() {
|
||||
inner_shape($wall_thickness, $keytop_thickness);
|
||||
inside_features();
|
||||
} */
|
||||
}
|
||||
|
||||
// actual full key with space carved out and keystem/stabilizer connectors
|
||||
|
|
|
|||
|
|
@ -9,8 +9,6 @@ include <key_profiles/sa.scad>
|
|||
include <key_profiles/g20.scad>
|
||||
include <key_profiles/hipro.scad>
|
||||
include <key_profiles/grid.scad>
|
||||
include <key_profiles/cherry.scad>
|
||||
include <key_profiles/dss.scad>
|
||||
|
||||
// man, wouldn't it be so cool if functions were first order
|
||||
module key_profile(key_profile_type, row, column=0) {
|
||||
|
|
@ -20,8 +18,6 @@ module key_profile(key_profile_type, row, column=0) {
|
|||
oem_row(row, column) children();
|
||||
} else if (key_profile_type == "dsa") {
|
||||
dsa_row(row, column) children();
|
||||
} else if (key_profile_type == "dss") {
|
||||
dss_row(row, column) children();
|
||||
} else if (key_profile_type == "sa") {
|
||||
sa_row(row, column) children();
|
||||
} else if (key_profile_type == "g20") {
|
||||
|
|
@ -30,8 +26,6 @@ module key_profile(key_profile_type, row, column=0) {
|
|||
hipro_row(row, column) children();
|
||||
} else if (key_profile_type == "grid") {
|
||||
grid_row(row, column) children();
|
||||
} else if (key_profile_type == "cherry") {
|
||||
cherry_row(row, column) children();
|
||||
} else if (key_profile_type == "disable") {
|
||||
children();
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1,48 +0,0 @@
|
|||
// based off GMK keycap set
|
||||
|
||||
module cherry_row(row=3, column=0) {
|
||||
$bottom_key_width = 18.16;
|
||||
$bottom_key_height = 18.16;
|
||||
$width_difference = $bottom_key_width - 11.85;
|
||||
$height_difference = $bottom_key_height - 14.64;
|
||||
$dish_type = "cylindrical";
|
||||
$dish_depth = 0.65;
|
||||
$dish_skew_x = 0;
|
||||
$dish_skew_y = 0;
|
||||
$top_skew = 2;
|
||||
|
||||
$top_tilt_y = side_tilt(column);
|
||||
extra_height = $double_sculpted ? extra_side_tilt_height(column) : 0;
|
||||
|
||||
// NOTE: cherry keycaps have this stem inset, but I'm reticent to turn it on
|
||||
// since it'll be surprising to folks. the height has been adjusted accordingly
|
||||
// $stem_inset = 0.6;
|
||||
extra_stem_inset_height = max(0.6 - $stem_inset, 0);
|
||||
|
||||
// <= is a hack so you can do these in a for loop. function row = 0
|
||||
if (row <= 1) {
|
||||
$total_depth = 9.8 - extra_stem_inset_height + extra_height;
|
||||
$top_tilt = 0;
|
||||
|
||||
children();
|
||||
} else if (row == 2) {
|
||||
$total_depth = 7.45 - extra_stem_inset_height + extra_height;
|
||||
$top_tilt = 2.5;
|
||||
|
||||
children();
|
||||
} else if (row == 3) {
|
||||
$total_depth = 6.55 - extra_stem_inset_height + extra_height;
|
||||
$top_tilt = 5;
|
||||
children();
|
||||
} else if (row == 3) {
|
||||
$total_depth = 6.7 + 0.65 - extra_stem_inset_height + extra_height;
|
||||
$top_tilt = 11.5;
|
||||
children();
|
||||
} else if (row >= 4) {
|
||||
$total_depth = 6.7 + 0.65 - extra_stem_inset_height + extra_height;
|
||||
$top_tilt = 11.5;
|
||||
children();
|
||||
} else {
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
|
@ -12,7 +12,7 @@ module dsa_row(row=3, column = 0) {
|
|||
$dish_skew_y = 0;
|
||||
$height_slices = 10;
|
||||
$enable_side_sculpting = true;
|
||||
$corner_radius = 1;
|
||||
$corner_radius = 0.25;
|
||||
|
||||
$top_tilt_y = side_tilt(column);
|
||||
extra_height = $double_sculpted ? extra_side_tilt_height(column) : 0;
|
||||
|
|
|
|||
|
|
@ -1,49 +0,0 @@
|
|||
module dss_row(n=3, column=0) {
|
||||
$key_shape_type = "sculpted_square";
|
||||
$bottom_key_width = 18.24;
|
||||
$bottom_key_height = 18.24;
|
||||
$width_difference = 6;
|
||||
$height_difference = 6;
|
||||
$dish_type = "spherical";
|
||||
$dish_depth = 1.2;
|
||||
$dish_skew_x = 0;
|
||||
$dish_skew_y = 0;
|
||||
$top_skew = 0;
|
||||
$height_slices = 10;
|
||||
$enable_side_sculpting = true;
|
||||
// might wanna change this if you don't minkowski
|
||||
// do you even minkowski bro
|
||||
$corner_radius = 1;
|
||||
|
||||
// this is _incredibly_ intensive
|
||||
/* $rounded_key = true; */
|
||||
|
||||
$top_tilt_y = side_tilt(column);
|
||||
extra_height = $double_sculpted ? extra_side_tilt_height(column) : 0;
|
||||
|
||||
// 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
|
||||
// bottom row would just be 13 tilt and 14.89 total depth
|
||||
// top row would be something new entirely - 18 tilt maybe?
|
||||
if (n <= 1){
|
||||
$total_depth = 10.5 + extra_height;
|
||||
$top_tilt = -1;
|
||||
children();
|
||||
} else if (n == 2) {
|
||||
$total_depth = 8.6 + extra_height;
|
||||
$top_tilt = 3;
|
||||
children();
|
||||
} else if (n == 3) {
|
||||
$total_depth = 7.9 + extra_height;
|
||||
$top_tilt = 8;
|
||||
children();
|
||||
} else if (n == 4){
|
||||
$total_depth = 9.1 + extra_height;
|
||||
$top_tilt = 16;
|
||||
children();
|
||||
} else {
|
||||
$total_depth = 7.9 + extra_height;
|
||||
$top_tilt = 8;
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
module grid_row(row=3, column = 0) {
|
||||
$bottom_key_width = 18.16;
|
||||
$bottom_key_height = 18.16;
|
||||
$width_difference = 1;
|
||||
$height_difference = 1;
|
||||
$width_difference = 0.2;
|
||||
$height_difference = 0.2;
|
||||
$top_tilt = 0;
|
||||
$top_skew = 0;
|
||||
$dish_type = "old spherical";
|
||||
|
|
@ -14,8 +14,8 @@ module grid_row(row=3, column = 0) {
|
|||
$linear_extrude_shape = true;
|
||||
|
||||
|
||||
$dish_overdraw_width = -6.5;
|
||||
$dish_overdraw_height = -6.5;
|
||||
$dish_overdraw_width = -8;
|
||||
$dish_overdraw_height = -8;
|
||||
|
||||
$minkowski_radius = 0.5;
|
||||
//also,
|
||||
|
|
@ -24,7 +24,7 @@ module grid_row(row=3, column = 0) {
|
|||
$top_tilt_y = side_tilt(column);
|
||||
extra_height = $double_sculpted ? extra_side_tilt_height(column) : 0;
|
||||
|
||||
$total_depth = 7 + abs((row-3) * 0.5) + extra_height;
|
||||
$total_depth = 6 + abs((row-3) * 0.5) + extra_height;
|
||||
|
||||
if (row == 5 || row == 0) {
|
||||
/* $top_tilt = -18.55; */
|
||||
|
|
|
|||
|
|
@ -13,7 +13,9 @@ module hipro_row(row=3, column=0) {
|
|||
$dish_skew_y = 0;
|
||||
$top_skew = 0;
|
||||
$height_slices = 10;
|
||||
$corner_radius = 1;
|
||||
// might wanna change this if you don't minkowski
|
||||
// do you even minkowski bro
|
||||
$corner_radius = 0.25;
|
||||
|
||||
$top_tilt_y = side_tilt(column);
|
||||
extra_height = $double_sculpted ? extra_side_tilt_height(column) : 0;
|
||||
|
|
|
|||
|
|
@ -10,7 +10,9 @@ module sa_row(n=3, column=0) {
|
|||
$dish_skew_y = 0;
|
||||
$top_skew = 0;
|
||||
$height_slices = 10;
|
||||
$corner_radius = 1;
|
||||
// might wanna change this if you don't minkowski
|
||||
// do you even minkowski bro
|
||||
$corner_radius = 0.25;
|
||||
|
||||
// this is _incredibly_ intensive
|
||||
/* $rounded_key = true; */
|
||||
|
|
|
|||
|
|
@ -44,12 +44,6 @@ module rotated() {
|
|||
children();
|
||||
}
|
||||
|
||||
module vertically_stabilized(mm=12, vertical=true, type=undef) {
|
||||
stabilized(mm,vertical,type) {
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
module stabilized(mm=12, vertical = false, type=undef) {
|
||||
if (vertical) {
|
||||
$stabilizer_type = (type ? type : ($stabilizer_type ? $stabilizer_type : "costar_stabilizer"));
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
include <functions.scad>
|
||||
|
||||
module spacebar() {
|
||||
$inverted_dish = true;
|
||||
$dish_type = "sideways cylindrical";
|
||||
|
|
@ -45,15 +43,13 @@ module iso_enter() {
|
|||
$key_length = 1.5;
|
||||
$key_height = 2;
|
||||
|
||||
$dish_offset_x = -(unit_length(1.5) - unit_length(1.25))/2;
|
||||
|
||||
/* $top_tilt = 0; */
|
||||
$top_tilt = 0;
|
||||
$stem_support_type = "disable";
|
||||
$key_shape_type = "iso_enter";
|
||||
/* $linear_extrude_shape = true; */
|
||||
$linear_extrude_height_adjustment = 19.05 * 0.5;
|
||||
// this equals (unit_length(1.5) - unit_length(1.25)) / 2
|
||||
/* $dish_overdraw_width = 2.38125; */
|
||||
$dish_overdraw_width = 2.38125;
|
||||
|
||||
|
||||
stabilized(vertical=true) {
|
||||
|
|
|
|||
|
|
@ -1,79 +0,0 @@
|
|||
// thanks Paul https://github.com/openscad/list-comprehension-demos/
|
||||
|
||||
include <../functions.scad>
|
||||
|
||||
module 3d_surface(size=$3d_surface_size, step=$3d_surface_step, bottom=-SMALLEST_POSSIBLE){
|
||||
function p(x, y) = [ x, y, max(0,surface_function(x, y)) ];
|
||||
function p0(x, y) = [ x, y, bottom ];
|
||||
function rev(b, v) = b ? v : [ v[3], v[2], v[1], v[0] ];
|
||||
function face(x, y) = [ p(x, y + step), p(x + step, y + step), p(x + step, y), p(x + step, y), p(x, y), p(x, y + step) ];
|
||||
function fan(a, i) =
|
||||
a == 0 ? [ [ 0, 0, bottom ], [ i, -size, bottom ], [ i + step, -size, bottom ] ]
|
||||
: a == 1 ? [ [ 0, 0, bottom ], [ i + step, size, bottom ], [ i, size, bottom ] ]
|
||||
: a == 2 ? [ [ 0, 0, bottom ], [ -size, i + step, bottom ], [ -size, i, bottom ] ]
|
||||
: [ [ 0, 0, bottom ], [ size, i, bottom ], [ size, i + step, bottom ] ];
|
||||
function sidex(x, y) = [ p0(x, y), p(x, y), p(x + step, y), p0(x + step, y) ];
|
||||
function sidey(x, y) = [ p0(x, y), p(x, y), p(x, y + step), p0(x, y + step) ];
|
||||
|
||||
points = flatten(concat(
|
||||
// top surface
|
||||
[ for (x = [ -size : step : size - step ], y = [ -size : step : size - step ]) face(x, y) ],
|
||||
// bottom surface as triangle fan
|
||||
[ for (a = [ 0 : 3 ], i = [ -size : step : size - step ]) fan(a, i) ],
|
||||
// sides
|
||||
[ for (x = [ -size : step : size - step ], y = [ -size, size ]) rev(y < 0, sidex(x, y)) ],
|
||||
[ for (y = [ -size : step : size - step ], x = [ -size, size ]) rev(x > 0, sidey(x, y)) ]
|
||||
));
|
||||
|
||||
tcount = 2 * pow(2 * size / step, 2) + 8 * size / step;
|
||||
scount = 8 * size / step;
|
||||
|
||||
tfaces = [ for (a = [ 0 : 3 : 3 * (tcount - 1) ] ) [ a, a + 1, a + 2 ] ];
|
||||
sfaces = [ for (a = [ 3 * tcount : 4 : 3 * tcount + 4 * scount ] ) [ a, a + 1, a + 2, a + 3 ] ];
|
||||
faces = concat(tfaces, sfaces);
|
||||
|
||||
polyhedron(points, faces, convexity = 8);
|
||||
}
|
||||
|
||||
module polar_3d_surface(size=$3d_surface_size, step=$3d_surface_step, bottom=-SMALLEST_POSSIBLE){
|
||||
function to_polar(q, size) = q * (90 / size);
|
||||
|
||||
function p(x, y) = [
|
||||
surface_distribution_function(to_polar(x, size), size),
|
||||
surface_distribution_function(to_polar(y, size), size),
|
||||
max(0,surface_function(surface_distribution_function(to_polar(x, size), size), surface_distribution_function(to_polar(y, size), size)))
|
||||
];
|
||||
function p0(x, y) = [ x, y, bottom ];
|
||||
function rev(b, v) = b ? v : [ v[3], v[2], v[1], v[0] ];
|
||||
function face(x, y) = [ p(x, y + step), p(x + step, y + step), p(x + step, y), p(x + step, y), p(x, y), p(x, y + step) ];
|
||||
function fan(a, i) =
|
||||
a == 0 ? [ [ 0, 0, bottom ], [ i, -size, bottom ], [ i + step, -size, bottom ] ]
|
||||
: a == 1 ? [ [ 0, 0, bottom ], [ i + step, size, bottom ], [ i, size, bottom ] ]
|
||||
: a == 2 ? [ [ 0, 0, bottom ], [ -size, i + step, bottom ], [ -size, i, bottom ] ]
|
||||
: [ [ 0, 0, bottom ], [ size, i, bottom ], [ size, i + step, bottom ] ];
|
||||
function sidex(x, y) = [ p0(x, y), p(x, y), p(x + step, y), p0(x + step, y) ];
|
||||
function sidey(x, y) = [ p0(x, y), p(x, y), p(x, y + step), p0(x, y + step) ];
|
||||
|
||||
points = flatten(concat(
|
||||
// top surface
|
||||
[ for (x = [ -size : step : size - step ], y = [ -size : step : size - step ]) face(x, y) ],
|
||||
// bottom surface as triangle fan
|
||||
[ for (a = [ 0 : 3 ], i = [ -size : step : size - step ]) fan(a, i) ],
|
||||
// sides
|
||||
[ for (x = [ -size : step : size - step ], y = [ -size, size ]) rev(y < 0, sidex(x, y)) ],
|
||||
[ for (y = [ -size : step : size - step ], x = [ -size, size ]) rev(x > 0, sidey(x, y)) ]
|
||||
));
|
||||
|
||||
tcount = 2 * pow(2 * size / step, 2) + 8 * size / step;
|
||||
scount = 8 * size / step;
|
||||
|
||||
tfaces = [ for (a = [ 0 : 3 : 3 * (tcount - 1) ] ) [ a, a + 1, a + 2 ] ];
|
||||
sfaces = [ for (a = [ 3 * tcount : 4 : 3 * tcount + 4 * scount ] ) [ a, a + 1, a + 2, a + 3 ] ];
|
||||
faces = concat(tfaces, sfaces);
|
||||
|
||||
polyhedron(points, faces, convexity = 8);
|
||||
}
|
||||
|
||||
// defaults, overridden in functions.scad
|
||||
function surface_distribution_function(dim, size) = sin(dim) * size;
|
||||
function surface_function(x,y) = (sin(acos(x/$3d_surface_size))) * sin(acos(y/$3d_surface_size));
|
||||
|
|
@ -1,687 +0,0 @@
|
|||
// Library: round-anything
|
||||
// Version: 1.0
|
||||
// Author: IrevDev
|
||||
// Contributors: TLC123
|
||||
// Copyright: 2020
|
||||
// License: MIT
|
||||
|
||||
|
||||
function addZcoord(points,displacement)=[for(i=[0:len(points)-1])[points[i].x,points[i].y, displacement]];
|
||||
function translate3Dcoords(points,tran=[0,0,0],mult=[1,1,1])=[for(i=[0:len(points)-1])[
|
||||
(points[i].x*mult.x)+tran.x,
|
||||
(points[i].y*mult.y)+tran.y,
|
||||
(points[i].z*mult.z)+tran.z
|
||||
]];
|
||||
function offsetPolygonPoints(points, offset=0)=
|
||||
// Work sthe same as the offset does, except for the fact that instead of a 2d shape
|
||||
// It works directly on polygon points
|
||||
// It returns the same number of points just offset into or, away from the original shape.
|
||||
// points= a series of x,y points[[x1,y1],[x2,y2],...]
|
||||
// offset= amount to offset by, negative numbers go inwards into the shape, positive numbers go out
|
||||
// return= a series of x,y points[[x1,y1],[x2,y2],...]
|
||||
let(
|
||||
isCWorCCW=sign(offset)*CWorCCW(points)*-1,
|
||||
lp=len(points)
|
||||
)
|
||||
[for(i=[0:lp-1]) parallelFollow([
|
||||
points[listWrap(i-1,lp)],
|
||||
points[i],
|
||||
points[listWrap(i+1,lp)],
|
||||
],thick=offset,mode=isCWorCCW)];
|
||||
|
||||
function makeCurvedPartOfPolyHedron(radiiPoints,r,fn,minR=0.01)=
|
||||
// this is a private function that I'm not expecting library users to use directly
|
||||
// radiiPoints= serise of x, y, r points
|
||||
// r= radius of curve that will be put on the end of the extrusion
|
||||
// fn= amount of subdivisions
|
||||
// minR= if one of the points in radiiPoints is less than r, it's likely to converge and form a sharp edge,
|
||||
// the min radius on these converged edges can be controled with minR, though because of legacy reasons it can't be 0, but can be a very small number.
|
||||
// return= array of [polyhedronPoints, Polyhedronfaces, theLength of a singe layer in the curve]
|
||||
let(
|
||||
lp=len(radiiPoints),
|
||||
radii=[for(i=[0:lp-1])radiiPoints[i].z],
|
||||
isCWorCCWOverall=CWorCCW(radiiPoints),
|
||||
dir=sign(r),
|
||||
absR=abs(r),
|
||||
fractionOffLp=1-1/fn,
|
||||
allPoints=[for(fraction=[0:1/fn:1])
|
||||
let(
|
||||
iterationOffset=dir*sqrt(sq(absR)-sq(fraction*absR))-dir*absR,
|
||||
theOffsetPoints=offsetPolygonPoints(radiiPoints,iterationOffset),
|
||||
polyRoundOffsetPoints=[for(i=[0:lp-1])
|
||||
let(
|
||||
pointsAboutCurrent=[
|
||||
theOffsetPoints[listWrap(i-1,lp)],
|
||||
theOffsetPoints[i],
|
||||
theOffsetPoints[listWrap(i+1,lp)]
|
||||
],
|
||||
isCWorCCWLocal=CWorCCW(pointsAboutCurrent),
|
||||
isInternalRadius=(isCWorCCWLocal*isCWorCCWOverall)==-1,
|
||||
// the radius names are only true for positive r,
|
||||
// when are r is negative increasingRadius is actually decreasing and vice-vs
|
||||
// increasingRadiusWithPositiveR is just to verbose of a variable name for my liking
|
||||
increasingRadius=max(radii[i]-iterationOffset, minR),
|
||||
decreasingRadius=max(radii[i]+iterationOffset, minR)
|
||||
)
|
||||
[theOffsetPoints[i].x, theOffsetPoints[i].y, isInternalRadius? increasingRadius: decreasingRadius]
|
||||
],
|
||||
pointsForThisLayer=polyRound(polyRoundOffsetPoints,fn)
|
||||
)
|
||||
addZcoord(pointsForThisLayer,fraction*absR)
|
||||
],
|
||||
polyhedronPoints=flatternArray(allPoints),
|
||||
allLp=len(allPoints),
|
||||
layerLength=len(allPoints[0]),
|
||||
loopToSecondLastLayer=allLp-2,
|
||||
sideFaces=[for(layerIndex=[0:loopToSecondLastLayer])let(
|
||||
currentLayeroffset=layerIndex*layerLength,
|
||||
nextLayeroffset=(layerIndex+1)*layerLength,
|
||||
layerFaces=[for(subLayerIndex=[0:layerLength-1])
|
||||
[
|
||||
currentLayeroffset+subLayerIndex, currentLayeroffset + listWrap(subLayerIndex+1,layerLength), nextLayeroffset+listWrap(subLayerIndex+1,layerLength), nextLayeroffset+subLayerIndex]
|
||||
]
|
||||
)layerFaces],
|
||||
polyhedronFaces=flatternArray(sideFaces)
|
||||
)
|
||||
[polyhedronPoints, polyhedronFaces, layerLength];
|
||||
|
||||
function flatternRecursion(array, init=[], currentIndex)=
|
||||
// this is a private function, init and currentIndex are for the function's use
|
||||
// only for when it's calling itself, which is why there is a simplified version flatternArray that just calls this one
|
||||
// array= array to flattern by one level of nesting
|
||||
// init= the array used to cancat with the next call, only for when the function calls itself
|
||||
// currentIndex= so the function can keep track of how far it's progressed through the array, only for when it's calling itself
|
||||
// returns= flatterned array, by one level of nesting
|
||||
let(
|
||||
shouldKickOffRecursion=currentIndex==undef?1:0,
|
||||
isLastIndex=currentIndex+1==len(array)?1:0,
|
||||
flatArray=shouldKickOffRecursion?flatternRecursion(array,[],0):
|
||||
isLastIndex?concat(init,array[currentIndex]):
|
||||
flatternRecursion(array,concat(init,array[currentIndex]),currentIndex+1)
|
||||
)
|
||||
flatArray;
|
||||
|
||||
function flatternArray(array)=
|
||||
// public version of flatternRecursion, has simplified params to avoid confusion
|
||||
// array= array to be flatterned
|
||||
// return= array that been flatterend by one level of nesting
|
||||
flatternRecursion(array);
|
||||
|
||||
function offsetAllFacesBy(array,offset)=[
|
||||
// polyhedron faces are simply a list of indices to points, if your concat points together than you probably need to offset
|
||||
// your faces array to points to the right place in the new list
|
||||
// array= array of point indicies
|
||||
// offset= number to offset all indecies by
|
||||
// return= array of point indices (i.e. faces) with offset applied
|
||||
for(faceIndex=[0:len(array)-1])[
|
||||
for(pointIndex=[0:len(array[faceIndex])-1])array[faceIndex][pointIndex]+offset
|
||||
]
|
||||
];
|
||||
|
||||
function extrudePolygonWithRadius(radiiPoints,h=5,r1=1,r2=1,fn=4)=
|
||||
// this basically calls makeCurvedPartOfPolyHedron twice to get the curved section of the final polyhedron
|
||||
// and then goes about assmbling them, as the side faces and the top and bottom face caps are missing
|
||||
// radiiPoints= series of [x,y,r] points,
|
||||
// h= height of the extrude (total including radius sections)
|
||||
// r1,r2= define the radius at the top and bottom of the extrud respectively, negative number flange out the extrude
|
||||
// fn= number of subdivisions
|
||||
// returns= [polyhedronPoints, polyhedronFaces]
|
||||
let(
|
||||
// top is the top curved part of the extrude
|
||||
top=makeCurvedPartOfPolyHedron(radiiPoints,r1,fn),
|
||||
topRadiusPoints=translate3Dcoords(top[0],[0,0,h-r1]),
|
||||
singeLayerLength=top[2],
|
||||
topRadiusFaces=top[1],
|
||||
radiusPointsLength=len(topRadiusPoints), // is the same length as bottomRadiusPoints
|
||||
// bottom is the bottom curved part of the extrude
|
||||
bottom=makeCurvedPartOfPolyHedron(radiiPoints,r2,fn),
|
||||
// Z axis needs to be multiplied by -1 to flip it so the radius is going in the right direction [1,1,-1]
|
||||
bottomRadiusPoints=translate3Dcoords(bottom[0],[0,0,abs(r2)],[1,1,-1]),
|
||||
// becaues the points will be all concatenated into the same array, and the bottom points come second, than
|
||||
// the original indices the faces are points towards are wrong and need to have an offset applied to them
|
||||
bottomRadiusFaces=offsetAllFacesBy(bottom[1],radiusPointsLength),
|
||||
// all of the side panel of the extrusion, connecting points from the inner layers of each
|
||||
// of the curved sections
|
||||
sideFaces=[for(i=[0:singeLayerLength-1])[
|
||||
i,
|
||||
listWrap(i+1,singeLayerLength),
|
||||
radiusPointsLength + listWrap(i+1,singeLayerLength),
|
||||
radiusPointsLength + i
|
||||
]],
|
||||
// both of these caps are simple every point from the last layer of the radius points
|
||||
topCapFace=[for(i=[0:singeLayerLength-1])radiusPointsLength-singeLayerLength+i],
|
||||
bottomCapFace=[for(i=[0:singeLayerLength-1])radiusPointsLength*2-singeLayerLength+i],
|
||||
finalPolyhedronPoints=concat(topRadiusPoints,bottomRadiusPoints),
|
||||
finalPolyhedronFaces=concat(topRadiusFaces,bottomRadiusFaces, sideFaces, [topCapFace], [bottomCapFace])
|
||||
)
|
||||
[
|
||||
finalPolyhedronPoints,
|
||||
finalPolyhedronFaces
|
||||
];
|
||||
|
||||
module polyRoundExtrude(radiiPoints,length=5,r1=1,r2=1,fn=10,convexity=10) {
|
||||
polyhedronPointsNFaces=extrudePolygonWithRadius(radiiPoints,length,r1,r2,fn);
|
||||
polyhedron(points=polyhedronPointsNFaces[0], faces=polyhedronPointsNFaces[1], convexity=convexity);
|
||||
}
|
||||
|
||||
|
||||
// testingInternals();
|
||||
module testingInternals(){
|
||||
//example of rounding random points, this has no current use but is a good demonstration
|
||||
random=[for(i=[0:20])[rnd(0,50),rnd(0,50),/*rnd(0,30)*/1000]];
|
||||
R =polyRound(random,7);
|
||||
translate([-25,25,0]){
|
||||
polyline(R);
|
||||
}
|
||||
|
||||
//example of different modes of the CentreN2PointsArc() function 0=shortest arc, 1=longest arc, 2=CW, 3=CCW
|
||||
p1=[0,5];p2=[10,5];centre=[5,0];
|
||||
translate([60,0,0]){
|
||||
color("green"){
|
||||
polygon(CentreN2PointsArc(p1,p2,centre,0,20));//draws the shortest arc
|
||||
}
|
||||
color("cyan"){
|
||||
polygon(CentreN2PointsArc(p1,p2,centre,1,20));//draws the longest arc
|
||||
}
|
||||
}
|
||||
translate([75,0,0]){
|
||||
color("purple"){
|
||||
polygon(CentreN2PointsArc(p1,p2,centre,2,20));//draws the arc CW (which happens to be the short arc)
|
||||
}
|
||||
color("red"){
|
||||
polygon(CentreN2PointsArc(p2,p1,centre,2,20));//draws the arc CW but p1 and p2 swapped order resulting in the long arc being drawn
|
||||
}
|
||||
}
|
||||
|
||||
radius=6;
|
||||
radiipoints=[[0,0,0],[10,20,radius],[20,0,0]];
|
||||
tangentsNcen=round3points(radiipoints);
|
||||
translate([10,0,0]){
|
||||
for(i=[0:2]){
|
||||
color("red")translate(getpoints(radiipoints)[i])circle(1);//plots the 3 input points
|
||||
color("cyan")translate(tangentsNcen[i])circle(1);//plots the two tangent poins and the circle centre
|
||||
}
|
||||
translate([tangentsNcen[2][0],tangentsNcen[2][1],-0.2])circle(r=radius,$fn=25);//draws the cirle
|
||||
%polygon(getpoints(radiipoints));//draws a polygon
|
||||
}
|
||||
}
|
||||
|
||||
function polyRound(radiipoints,fn=5,mode=0)=
|
||||
/*Takes a list of radii points of the format [x,y,radius] and rounds each point
|
||||
with fn resolution
|
||||
mode=0 - automatic radius limiting - DEFAULT
|
||||
mode=1 - Debug, output radius reduction for automatic radius limiting
|
||||
mode=2 - No radius limiting*/
|
||||
let(
|
||||
p=getpoints(radiipoints), //make list of coordinates without radii
|
||||
Lp=len(p),
|
||||
//remove the middle point of any three colinear points, otherwise adding a radius to the middle of a straigh line causes problems
|
||||
radiiPointsWithoutTrippleColinear=[
|
||||
for(i=[0:len(p)-1]) if(
|
||||
// keep point if it isn't colinear or if the radius is 0
|
||||
!isColinear(
|
||||
p[listWrap(i-1,Lp)],
|
||||
p[listWrap(i+0,Lp)],
|
||||
p[listWrap(i+1,Lp)]
|
||||
)||
|
||||
p[listWrap(i+0,Lp)].z!=0
|
||||
) radiipoints[listWrap(i+0,Lp)]
|
||||
],
|
||||
newrp2=processRadiiPoints(radiiPointsWithoutTrippleColinear),
|
||||
plusMinusPointRange=mode==2?1:2,
|
||||
temp=[
|
||||
for(i=[0:len(newrp2)-1]) //for each point in the radii array
|
||||
let(
|
||||
thepoints=[for(j=[-plusMinusPointRange:plusMinusPointRange])newrp2[listWrap(i+j,len(newrp2))]],//collect 5 radii points
|
||||
temp2=mode==2?round3points(thepoints,fn):round5points(thepoints,fn,mode)
|
||||
)
|
||||
mode==1?temp2:newrp2[i][2]==0?
|
||||
[[newrp2[i][0],newrp2[i][1]]]: //return the original point if the radius is 0
|
||||
CentreN2PointsArc(temp2[0],temp2[1],temp2[2],0,fn) //return the arc if everything is normal
|
||||
]
|
||||
)
|
||||
[for (a = temp) for (b = a) b];//flattern and return the array
|
||||
|
||||
function round5points(rp,fn,debug=0)=
|
||||
rp[2][2]==0&&debug==0?[[rp[2][0],rp[2][1]]]://return the middle point if the radius is 0
|
||||
rp[2][2]==0&&debug==1?0://if debug is enabled and the radius is 0 return 0
|
||||
let(
|
||||
p=getpoints(rp), //get list of points
|
||||
r=[for(i=[1:3]) abs(rp[i][2])],//get the centre 3 radii
|
||||
//start by determining what the radius should be at point 3
|
||||
//find angles at points 2 , 3 and 4
|
||||
a2=cosineRuleAngle(p[0],p[1],p[2]),
|
||||
a3=cosineRuleAngle(p[1],p[2],p[3]),
|
||||
a4=cosineRuleAngle(p[2],p[3],p[4]),
|
||||
//find the distance between points 2&3 and between points 3&4
|
||||
d23=pointDist(p[1],p[2]),
|
||||
d34=pointDist(p[2],p[3]),
|
||||
//find the radius factors
|
||||
F23=(d23*tan(a2/2)*tan(a3/2))/(r[0]*tan(a3/2)+r[1]*tan(a2/2)),
|
||||
F34=(d34*tan(a3/2)*tan(a4/2))/(r[1]*tan(a4/2)+r[2]*tan(a3/2)),
|
||||
newR=min(r[1],F23*r[1],F34*r[1]),//use the smallest radius
|
||||
//now that the radius has been determined, find tangent points and circle centre
|
||||
tangD=newR/tan(a3/2),//distance to the tangent point from p3
|
||||
circD=newR/sin(a3/2),//distance to the circle centre from p3
|
||||
//find the angle from the p3
|
||||
an23=getAngle(p[1],p[2]),//angle from point 3 to 2
|
||||
an34=getAngle(p[3],p[2]),//angle from point 3 to 4
|
||||
//find tangent points
|
||||
t23=[p[2][0]-cos(an23)*tangD,p[2][1]-sin(an23)*tangD],//tangent point between points 2&3
|
||||
t34=[p[2][0]-cos(an34)*tangD,p[2][1]-sin(an34)*tangD],//tangent point between points 3&4
|
||||
//find circle centre
|
||||
tmid=getMidpoint(t23,t34),//midpoint between the two tangent points
|
||||
anCen=getAngle(tmid,p[2]),//angle from point 3 to circle centre
|
||||
cen=[p[2][0]-cos(anCen)*circD,p[2][1]-sin(anCen)*circD]
|
||||
)
|
||||
//circle center by offseting from point 3
|
||||
//determine the direction of rotation
|
||||
debug==1?//if debug in disabled return arc (default)
|
||||
(newR-r[1]):
|
||||
[t23,t34,cen];
|
||||
|
||||
function round3points(rp,fn)=
|
||||
rp[1][2]==0?[[rp[1][0],rp[1][1]]]://return the middle point if the radius is 0
|
||||
let(
|
||||
p=getpoints(rp), //get list of points
|
||||
r=rp[1][2],//get the centre 3 radii
|
||||
ang=cosineRuleAngle(p[0],p[1],p[2]),//angle between the lines
|
||||
//now that the radius has been determined, find tangent points and circle centre
|
||||
tangD=r/tan(ang/2),//distance to the tangent point from p2
|
||||
circD=r/sin(ang/2),//distance to the circle centre from p2
|
||||
//find the angles from the p2 with respect to the postitive x axis
|
||||
angleFromPoint1ToPoint2=getAngle(p[0],p[1]),
|
||||
angleFromPoint2ToPoint3=getAngle(p[2],p[1]),
|
||||
//find tangent points
|
||||
t12=[p[1][0]-cos(angleFromPoint1ToPoint2)*tangD,p[1][1]-sin(angleFromPoint1ToPoint2)*tangD],//tangent point between points 1&2
|
||||
t23=[p[1][0]-cos(angleFromPoint2ToPoint3)*tangD,p[1][1]-sin(angleFromPoint2ToPoint3)*tangD],//tangent point between points 2&3
|
||||
//find circle centre
|
||||
tmid=getMidpoint(t12,t23),//midpoint between the two tangent points
|
||||
angCen=getAngle(tmid,p[1]),//angle from point 2 to circle centre
|
||||
cen=[p[1][0]-cos(angCen)*circD,p[1][1]-sin(angCen)*circD] //circle center by offseting from point 2
|
||||
)
|
||||
[t12,t23,cen];
|
||||
|
||||
function parallelFollow(rp,thick=4,minR=1,mode=1)=
|
||||
//rp[1][2]==0?[rp[1][0],rp[1][1],0]://return the middle point if the radius is 0
|
||||
thick==0?[rp[1][0],rp[1][1],0]://return the middle point if the radius is 0
|
||||
let(
|
||||
p=getpoints(rp), //get list of points
|
||||
r=thick,//get the centre 3 radii
|
||||
ang=cosineRuleAngle(p[0],p[1],p[2]),//angle between the lines
|
||||
//now that the radius has been determined, find tangent points and circle centre
|
||||
tangD=r/tan(ang/2),//distance to the tangent point from p2
|
||||
sgn=CWorCCW(rp),//rotation of the three points cw or ccw?let(sgn=mode==0?1:-1)
|
||||
circD=mode*sgn*r/sin(ang/2),//distance to the circle centre from p2
|
||||
//find the angles from the p2 with respect to the postitive x axis
|
||||
angleFromPoint1ToPoint2=getAngle(p[0],p[1]),
|
||||
angleFromPoint2ToPoint3=getAngle(p[2],p[1]),
|
||||
//find tangent points
|
||||
t12=[p[1][0]-cos(angleFromPoint1ToPoint2)*tangD,p[1][1]-sin(angleFromPoint1ToPoint2)*tangD],//tangent point between points 1&2
|
||||
t23=[p[1][0]-cos(angleFromPoint2ToPoint3)*tangD,p[1][1]-sin(angleFromPoint2ToPoint3)*tangD],//tangent point between points 2&3
|
||||
//find circle centre
|
||||
tmid=getMidpoint(t12,t23),//midpoint between the two tangent points
|
||||
angCen=getAngle(tmid,p[1]),//angle from point 2 to circle centre
|
||||
cen=[p[1][0]-cos(angCen)*circD,p[1][1]-sin(angCen)*circD],//circle center by offseting from point 2
|
||||
outR=max(minR,rp[1][2]-thick*sgn*mode) //ensures radii are never too small.
|
||||
)
|
||||
concat(cen,outR);
|
||||
|
||||
function findPoint(ang1,refpoint1,ang2,refpoint2,r=0)=
|
||||
let(
|
||||
m1=tan(ang1),
|
||||
c1=refpoint1.y-m1*refpoint1.x,
|
||||
m2=tan(ang2),
|
||||
c2=refpoint2.y-m2*refpoint2.x,
|
||||
outputX=(c2-c1)/(m1-m2),
|
||||
outputY=m1*outputX+c1
|
||||
)
|
||||
[outputX,outputY,r];
|
||||
|
||||
function beamChain(radiiPoints,offset1=0,offset2,mode=0,minR=0,startAngle,endAngle)=
|
||||
/*This function takes a series of radii points and plots points to run along side at a consistant distance, think of it as offset but for line instead of a polygon
|
||||
radiiPoints=radii points,
|
||||
offset1 & offset2= The two offsets that give the beam it's thickness. When using with mode=2 only offset1 is needed as there is no return path for the polygon
|
||||
minR=min radius, if all of your radii are set properly within the radii points this value can be ignored
|
||||
startAngle & endAngle= Angle at each end of the beam, different mode determine if this angle is relative to the ending legs of the beam or absolute.
|
||||
mode=1 - include endpoints startAngle&2 are relative to the angle of the last two points and equal 90deg if not defined
|
||||
mode=2 - Only the forward path is defined, useful for combining the beam with other radii points, see examples for a use-case.
|
||||
mode=3 - include endpoints startAngle&2 are absolute from the x axis and are 0 if not defined
|
||||
negative radiuses only allowed for the first and last radii points
|
||||
|
||||
As it stands this function could probably be tidied a lot, but it works, I'll tidy later*/
|
||||
let(
|
||||
offset2undef=offset2==undef?1:0,
|
||||
offset2=offset2undef==1?0:offset2,
|
||||
CWorCCW1=sign(offset1)*CWorCCW(radiiPoints),
|
||||
CWorCCW2=sign(offset2)*CWorCCW(radiiPoints),
|
||||
offset1=abs(offset1),
|
||||
offset2b=abs(offset2),
|
||||
Lrp3=len(radiiPoints)-3,
|
||||
Lrp=len(radiiPoints),
|
||||
startAngle=mode==0&&startAngle==undef?
|
||||
getAngle(radiiPoints[0],radiiPoints[1])+90:
|
||||
mode==2&&startAngle==undef?
|
||||
0:
|
||||
mode==0?
|
||||
getAngle(radiiPoints[0],radiiPoints[1])+startAngle:
|
||||
startAngle,
|
||||
endAngle=mode==0&&endAngle==undef?
|
||||
getAngle(radiiPoints[Lrp-1],radiiPoints[Lrp-2])+90:
|
||||
mode==2&&endAngle==undef?
|
||||
0:
|
||||
mode==0?
|
||||
getAngle(radiiPoints[Lrp-1],radiiPoints[Lrp-2])+endAngle:
|
||||
endAngle,
|
||||
OffLn1=[for(i=[0:Lrp3]) offset1==0?radiiPoints[i+1]:parallelFollow([radiiPoints[i],radiiPoints[i+1],radiiPoints[i+2]],offset1,minR,mode=CWorCCW1)],
|
||||
OffLn2=[for(i=[0:Lrp3]) offset2==0?radiiPoints[i+1]:parallelFollow([radiiPoints[i],radiiPoints[i+1],radiiPoints[i+2]],offset2b,minR,mode=CWorCCW2)],
|
||||
Rp1=abs(radiiPoints[0].z),
|
||||
Rp2=abs(radiiPoints[Lrp-1].z),
|
||||
endP1a=findPoint(getAngle(radiiPoints[0],radiiPoints[1]), OffLn1[0], startAngle,radiiPoints[0], Rp1),
|
||||
endP1b=findPoint(getAngle(radiiPoints[Lrp-1],radiiPoints[Lrp-2]), OffLn1[len(OffLn1)-1], endAngle,radiiPoints[Lrp-1], Rp2),
|
||||
endP2a=findPoint(getAngle(radiiPoints[0],radiiPoints[1]), OffLn2[0], startAngle,radiiPoints[0], Rp1),
|
||||
endP2b=findPoint(getAngle(radiiPoints[Lrp-1],radiiPoints[Lrp-2]), OffLn2[len(OffLn1)-1], endAngle,radiiPoints[Lrp-1], Rp2),
|
||||
absEnda=getAngle(endP1a,endP2a),
|
||||
absEndb=getAngle(endP1b,endP2b),
|
||||
negRP1a=[cos(absEnda)*radiiPoints[0].z*10+endP1a.x, sin(absEnda)*radiiPoints[0].z*10+endP1a.y, 0.0],
|
||||
negRP2a=[cos(absEnda)*-radiiPoints[0].z*10+endP2a.x, sin(absEnda)*-radiiPoints[0].z*10+endP2a.y, 0.0],
|
||||
negRP1b=[cos(absEndb)*radiiPoints[Lrp-1].z*10+endP1b.x, sin(absEndb)*radiiPoints[Lrp-1].z*10+endP1b.y, 0.0],
|
||||
negRP2b=[cos(absEndb)*-radiiPoints[Lrp-1].z*10+endP2b.x, sin(absEndb)*-radiiPoints[Lrp-1].z*10+endP2b.y, 0.0],
|
||||
OffLn1b=(mode==0||mode==2)&&radiiPoints[0].z<0&&radiiPoints[Lrp-1].z<0?
|
||||
concat([negRP1a],[endP1a],OffLn1,[endP1b],[negRP1b])
|
||||
:(mode==0||mode==2)&&radiiPoints[0].z<0?
|
||||
concat([negRP1a],[endP1a],OffLn1,[endP1b])
|
||||
:(mode==0||mode==2)&&radiiPoints[Lrp-1].z<0?
|
||||
concat([endP1a],OffLn1,[endP1b],[negRP1b])
|
||||
:mode==0||mode==2?
|
||||
concat([endP1a],OffLn1,[endP1b])
|
||||
:
|
||||
OffLn1,
|
||||
OffLn2b=(mode==0||mode==2)&&radiiPoints[0].z<0&&radiiPoints[Lrp-1].z<0?
|
||||
concat([negRP2a],[endP2a],OffLn2,[endP2b],[negRP2b])
|
||||
:(mode==0||mode==2)&&radiiPoints[0].z<0?
|
||||
concat([negRP2a],[endP2a],OffLn2,[endP2b])
|
||||
:(mode==0||mode==2)&&radiiPoints[Lrp-1].z<0?
|
||||
concat([endP2a],OffLn2,[endP2b],[negRP2b])
|
||||
:mode==0||mode==2?
|
||||
concat([endP2a],OffLn2,[endP2b])
|
||||
:
|
||||
OffLn2
|
||||
)//end of let()
|
||||
offset2undef==1?OffLn1b:concat(OffLn2b,revList(OffLn1b));
|
||||
|
||||
function revList(list)=//reverse list
|
||||
let(Llist=len(list)-1)
|
||||
[for(i=[0:Llist]) list[Llist-i]];
|
||||
|
||||
function CWorCCW(p)=
|
||||
let(
|
||||
Lp=len(p),
|
||||
e=[for(i=[0:Lp-1])
|
||||
(p[listWrap(i+0,Lp)].x-p[listWrap(i+1,Lp)].x)*(p[listWrap(i+0,Lp)].y+p[listWrap(i+1,Lp)].y)
|
||||
]
|
||||
)
|
||||
sign(sum(e));
|
||||
|
||||
function CentreN2PointsArc(p1,p2,cen,mode=0,fn)=
|
||||
/* This function plots an arc from p1 to p2 with fn increments using the cen as the centre of the arc.
|
||||
the mode determines how the arc is plotted
|
||||
mode==0, shortest arc possible
|
||||
mode==1, longest arc possible
|
||||
mode==2, plotted clockwise
|
||||
mode==3, plotted counter clockwise
|
||||
*/
|
||||
let(
|
||||
isCWorCCW=CWorCCW([cen,p1,p2]),//determine the direction of rotation
|
||||
//determine the arc angle depending on the mode
|
||||
p1p2Angle=cosineRuleAngle(p2,cen,p1),
|
||||
arcAngle=
|
||||
mode==0?p1p2Angle:
|
||||
mode==1?p1p2Angle-360:
|
||||
mode==2&&isCWorCCW==-1?p1p2Angle:
|
||||
mode==2&&isCWorCCW== 1?p1p2Angle-360:
|
||||
mode==3&&isCWorCCW== 1?p1p2Angle:
|
||||
mode==3&&isCWorCCW==-1?p1p2Angle-360:
|
||||
cosineRuleAngle(p2,cen,p1),
|
||||
r=pointDist(p1,cen),//determine the radius
|
||||
p1Angle=getAngle(cen,p1) //angle of line 1
|
||||
)
|
||||
[for(i=[0:fn])
|
||||
let(angleIncrement=(arcAngle/fn)*i*isCWorCCW)
|
||||
[cos(p1Angle+angleIncrement)*r+cen.x,sin(p1Angle+angleIncrement)*r+cen.y]];
|
||||
|
||||
function translateRadiiPoints(radiiPoints,tran=[0,0],rot=0)=
|
||||
[for(i=radiiPoints)
|
||||
let(
|
||||
a=getAngle([0,0],[i.x,i.y]),//get the angle of the this point
|
||||
h=pointDist([0,0],[i.x,i.y]) //get the hypotenuse/radius
|
||||
)
|
||||
[h*cos(a+rot)+tran.x,h*sin(a+rot)+tran.y,i.z]//calculate the point's new position
|
||||
];
|
||||
|
||||
module round2d(OR=3,IR=1){
|
||||
offset(OR,$fn=100){
|
||||
offset(-IR-OR,$fn=100){
|
||||
offset(IR,$fn=100){
|
||||
children();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module shell2d(offset1,offset2=0,minOR=0,minIR=0){
|
||||
difference(){
|
||||
round2d(minOR,minIR){
|
||||
offset(max(offset1,offset2)){
|
||||
children(0);//original 1st child forms the outside of the shell
|
||||
}
|
||||
}
|
||||
round2d(minIR,minOR){
|
||||
difference(){//round the inside cutout
|
||||
offset(min(offset1,offset2)){
|
||||
children(0);//shrink the 1st child to form the inside of the shell
|
||||
}
|
||||
if($children>1){
|
||||
for(i=[1:$children-1]){
|
||||
children(i);//second child and onwards is used to add material to inside of the shell
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module internalSq(size,r,center=0){
|
||||
tran=center==1?[0,0]:size/2;
|
||||
translate(tran){
|
||||
square(size,true);
|
||||
offs=sin(45)*r;
|
||||
for(i=[-1,1],j=[-1,1]){
|
||||
translate([(size.x/2-offs)*i,(size.y/2-offs)*j])circle(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module extrudeWithRadius(length,r1=0,r2=0,fn=30){
|
||||
n1=sign(r1);n2=sign(r2);
|
||||
r1=abs(r1);r2=abs(r2);
|
||||
translate([0,0,r1]){
|
||||
linear_extrude(length-r1-r2){
|
||||
children();
|
||||
}
|
||||
}
|
||||
for(i=[0:fn-1]){
|
||||
translate([0,0,i/fn*r1]){
|
||||
linear_extrude(r1/fn+0.01){
|
||||
offset(n1*sqrt(sq(r1)-sq(r1-i/fn*r1))-n1*r1){
|
||||
children();
|
||||
}
|
||||
}
|
||||
}
|
||||
translate([0,0,length-r2+i/fn*r2]){
|
||||
linear_extrude(r2/fn+0.01){
|
||||
offset(n2*sqrt(sq(r2)-sq(i/fn*r2))-n2*r2){
|
||||
children();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function mirrorPoints(radiiPoints,rot=0,endAttenuation=[0,0])= //mirrors a list of points about Y, ignoring the first and last points and returning them in reverse order for use with polygon or polyRound
|
||||
let(
|
||||
a=translateRadiiPoints(radiiPoints,[0,0],-rot),
|
||||
temp3=[for(i=[0+endAttenuation[0]:len(a)-1-endAttenuation[1]])
|
||||
[a[i][0],-a[i][1],a[i][2]]
|
||||
],
|
||||
temp=translateRadiiPoints(temp3,[0,0],rot),
|
||||
temp2=revList(temp3)
|
||||
)
|
||||
concat(radiiPoints,temp2);
|
||||
|
||||
function processRadiiPoints(rp)=
|
||||
[for(i=[0:len(rp)-1])
|
||||
processRadiiPoints2(rp,i)
|
||||
];
|
||||
|
||||
function processRadiiPoints2(list,end=0,idx=0,result=0)=
|
||||
idx>=end+1?result:
|
||||
processRadiiPoints2(list,end,idx+1,relationalRadiiPoints(result,list[idx]));
|
||||
|
||||
function cosineRuleBside(a,c,C)=c*cos(C)-sqrt(sq(a)+sq(c)+sq(cos(C))-sq(c));
|
||||
|
||||
function absArelR(po,pn)=
|
||||
let(
|
||||
th2=atan(po[1]/po[0]),
|
||||
r2=sqrt(sq(po[0])+sq(po[1])),
|
||||
r3=cosineRuleBside(r2,pn[1],th2-pn[0])
|
||||
)
|
||||
[cos(pn[0])*r3,sin(pn[0])*r3,pn[2]];
|
||||
|
||||
function relationalRadiiPoints(po,pi)=
|
||||
let(
|
||||
p0=pi[0],
|
||||
p1=pi[1],
|
||||
p2=pi[2],
|
||||
pv0=pi[3][0],
|
||||
pv1=pi[3][1],
|
||||
pt0=pi[3][2],
|
||||
pt1=pi[3][3],
|
||||
pn=
|
||||
(pv0=="y"&&pv1=="x")||(pv0=="r"&&pv1=="a")||(pv0=="y"&&pv1=="a")||(pv0=="x"&&pv1=="a")||(pv0=="y"&&pv1=="r")||(pv0=="x"&&pv1=="r")?
|
||||
[p1,p0,p2,concat(pv1,pv0,pt1,pt0)]:
|
||||
[p0,p1,p2,concat(pv0,pv1,pt0,pt1)],
|
||||
n0=pn[0],
|
||||
n1=pn[1],
|
||||
n2=pn[2],
|
||||
nv0=pn[3][0],
|
||||
nv1=pn[3][1],
|
||||
nt0=pn[3][2],
|
||||
nt1=pn[3][3],
|
||||
temp=
|
||||
pn[0]=="l"?
|
||||
[po[0],pn[1],pn[2]]
|
||||
:pn[1]=="l"?
|
||||
[pn[0],po[1],pn[2]]
|
||||
:nv0==undef?
|
||||
[pn[0],pn[1],pn[2]]//abs x, abs y as default when undefined
|
||||
:nv0=="a"?
|
||||
nv1=="r"?
|
||||
nt0=="a"?
|
||||
nt1=="a"||nt1==undef?
|
||||
[cos(n0)*n1,sin(n0)*n1,n2]//abs angle, abs radius
|
||||
:absArelR(po,pn)//abs angle rel radius
|
||||
:nt1=="r"||nt1==undef?
|
||||
[po[0]+cos(pn[0])*pn[1],po[1]+sin(pn[0])*pn[1],pn[2]]//rel angle, rel radius
|
||||
:[pn[0],pn[1],pn[2]]//rel angle, abs radius
|
||||
:nv1=="x"?
|
||||
nt0=="a"?
|
||||
nt1=="a"||nt1==undef?
|
||||
[pn[1],pn[1]*tan(pn[0]),pn[2]]//abs angle, abs x
|
||||
:[po[0]+pn[1],(po[0]+pn[1])*tan(pn[0]),pn[2]]//abs angle rel x
|
||||
:nt1=="r"||nt1==undef?
|
||||
[po[0]+pn[1],po[1]+pn[1]*tan(pn[0]),pn[2]]//rel angle, rel x
|
||||
:[pn[1],po[1]+(pn[1]-po[0])*tan(pn[0]),pn[2]]//rel angle, abs x
|
||||
:nt0=="a"?
|
||||
nt1=="a"||nt1==undef?
|
||||
[pn[1]/tan(pn[0]),pn[1],pn[2]]//abs angle, abs y
|
||||
:[(po[1]+pn[1])/tan(pn[0]),po[1]+pn[1],pn[2]]//abs angle rel y
|
||||
:nt1=="r"||nt1==undef?
|
||||
[po[0]+(pn[1]-po[0])/tan(90-pn[0]),po[1]+pn[1],pn[2]]//rel angle, rel y
|
||||
:[po[0]+(pn[1]-po[1])/tan(pn[0]),pn[1],pn[2]]//rel angle, abs y
|
||||
:nv0=="r"?
|
||||
nv1=="x"?
|
||||
nt0=="a"?
|
||||
nt1=="a"||nt1==undef?
|
||||
[pn[1],sign(pn[0])*sqrt(sq(pn[0])-sq(pn[1])),pn[2]]//abs radius, abs x
|
||||
:[po[0]+pn[1],sign(pn[0])*sqrt(sq(pn[0])-sq(po[0]+pn[1])),pn[2]]//abs radius rel x
|
||||
:nt1=="r"||nt1==undef?
|
||||
[po[0]+pn[1],po[1]+sign(pn[0])*sqrt(sq(pn[0])-sq(pn[1])),pn[2]]//rel radius, rel x
|
||||
:[pn[1],po[1]+sign(pn[0])*sqrt(sq(pn[0])-sq(pn[1]-po[0])),pn[2]]//rel radius, abs x
|
||||
:nt0=="a"?
|
||||
nt1=="a"||nt1==undef?
|
||||
[sign(pn[0])*sqrt(sq(pn[0])-sq(pn[1])),pn[1],pn[2]]//abs radius, abs y
|
||||
:[sign(pn[0])*sqrt(sq(pn[0])-sq(po[1]+pn[1])),po[1]+pn[1],pn[2]]//abs radius rel y
|
||||
:nt1=="r"||nt1==undef?
|
||||
[po[0]+sign(pn[0])*sqrt(sq(pn[0])-sq(pn[1])),po[1]+pn[1],pn[2]]//rel radius, rel y
|
||||
:[po[0]+sign(pn[0])*sqrt(sq(pn[0])-sq(pn[1]-po[1])),pn[1],pn[2]]//rel radius, abs y
|
||||
:nt0=="a"?
|
||||
nt1=="a"||nt1==undef?
|
||||
[pn[0],pn[1],pn[2]]//abs x, abs y
|
||||
:[pn[0],po[1]+pn[1],pn[2]]//abs x rel y
|
||||
:nt1=="r"||nt1==undef?
|
||||
[po[0]+pn[0],po[1]+pn[1],pn[2]]//rel x, rel y
|
||||
:[po[0]+pn[0],pn[1],pn[2]]//rel x, abs y
|
||||
)
|
||||
temp;
|
||||
|
||||
function invtan(run,rise)=
|
||||
let(a=abs(atan(rise/run)))
|
||||
rise==0&&run>0?
|
||||
0:rise>0&&run>0?
|
||||
a:rise>0&&run==0?
|
||||
90:rise>0&&run<0?
|
||||
180-a:rise==0&&run<0?
|
||||
180:rise<0&&run<0?
|
||||
a+180:rise<0&&run==0?
|
||||
270:rise<0&&run>0?
|
||||
360-a:"error";
|
||||
|
||||
function cosineRuleAngle(p1,p2,p3)=
|
||||
let(
|
||||
p12=abs(pointDist(p1,p2)),
|
||||
p13=abs(pointDist(p1,p3)),
|
||||
p23=abs(pointDist(p2,p3))
|
||||
)
|
||||
acos((sq(p23)+sq(p12)-sq(p13))/(2*p23*p12));
|
||||
|
||||
function sum(list, idx = 0, result = 0) =
|
||||
idx >= len(list) ? result : sum(list, idx + 1, result + list[idx]);
|
||||
|
||||
function sq(x)=x*x;
|
||||
function getGradient(p1,p2)=(p2.y-p1.y)/(p2.x-p1.x);
|
||||
function getAngle(p1,p2)=p1==p2?0:invtan(p2[0]-p1[0],p2[1]-p1[1]);
|
||||
function getMidpoint(p1,p2)=[(p1[0]+p2[0])/2,(p1[1]+p2[1])/2]; //returns the midpoint of two points
|
||||
function pointDist(p1,p2)=sqrt(abs(sq(p1[0]-p2[0])+sq(p1[1]-p2[1]))); //returns the distance between two points
|
||||
function isColinear(p1,p2,p3)=getGradient(p1,p2)==getGradient(p2,p3)?1:0;//return 1 if 3 points are colinear
|
||||
module polyline(p, width=0.3) {
|
||||
for(i=[0:max(0,len(p)-1)]){
|
||||
color([i*1/len(p),1-i*1/len(p),0,0.5])line(p[i],p[listWrap(i+1,len(p) )],width);
|
||||
}
|
||||
} // polyline plotter
|
||||
module line(p1, p2 ,width=0.3) { // single line plotter
|
||||
hull() {
|
||||
translate(p1){
|
||||
circle(width);
|
||||
}
|
||||
translate(p2){
|
||||
circle(width);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getpoints(p)=[for(i=[0:len(p)-1])[p[i].x,p[i].y]];// gets [x,y]list of[x,y,r]list
|
||||
function listWrap(x,x_max=1,x_min=0) = (((x - x_min) % (x_max - x_min)) + (x_max - x_min)) % (x_max - x_min) + x_min; // wraps numbers inside boundaries
|
||||
function rnd(a = 1, b = 0, s = []) =
|
||||
s == [] ?
|
||||
(rands(min(a, b), max( a, b), 1)[0]):(rands(min(a, b), max(a, b), 1, s)[0]); // nice rands wrapper
|
||||
33
src/libraries/rounded_rectangle_profile.scad
Normal file
33
src/libraries/rounded_rectangle_profile.scad
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
function sign_x(i,n) =
|
||||
i < n/4 || i > n*3/4 ? 1 :
|
||||
i > n/4 && i < n*3/4 ? -1 :
|
||||
0;
|
||||
|
||||
function sign_y(i,n) =
|
||||
i > 0 && i < n/2 ? 1 :
|
||||
i > n/2 ? -1 :
|
||||
0;
|
||||
|
||||
|
||||
function rectangle_profile(size=[1,1],fn=32) = [
|
||||
for (index = [0:fn-1])
|
||||
let(a = index/fn*360)
|
||||
sign_x(index, fn) * [size[0]/2,0]
|
||||
+ sign_y(index, fn) * [0,size[1]/2]
|
||||
];
|
||||
|
||||
function rounded_rectangle_profile(size=[1,1],r=1,fn=32) = [
|
||||
for (index = [0:fn-1])
|
||||
let(a = index/fn*360)
|
||||
r * [cos(a), sin(a)]
|
||||
+ sign_x(index, fn) * [size[0]/2-r,0]
|
||||
+ sign_y(index, fn) * [0,size[1]/2-r]
|
||||
];
|
||||
|
||||
function double_rounded_rectangle_profile(size=[1,1], r=1, fn=32) = [
|
||||
for (index = [0:fn-1])
|
||||
let(a = index/fn*360)
|
||||
r * [cos(a), sin(a)]
|
||||
+ sign_x(index, fn) * [size[0]/2-r,0]
|
||||
+ sign_y(index, fn) * [0,size[1]/2-r]
|
||||
];
|
||||
|
|
@ -37,7 +37,7 @@ $outset_legends = false;
|
|||
// Height in units of key. should remain 1 for most uses
|
||||
$key_height = 1.0;
|
||||
// Keytop thickness, aka how many millimeters between the inside and outside of the top surface of the key
|
||||
$keytop_thickness = 1;
|
||||
$keytop_thickness = 2;
|
||||
// Wall thickness, aka the thickness of the sides of the keycap. note this is the total thickness, aka 3 = 1.5mm walls
|
||||
$wall_thickness = 3;
|
||||
// Radius of corners of keycap
|
||||
|
|
@ -102,11 +102,7 @@ $dish_depth = 1;
|
|||
$dish_skew_x = 0;
|
||||
// How skewed in the y direction (height) the dish is
|
||||
$dish_skew_y = 0;
|
||||
|
||||
|
||||
$dish_offset_x = 0;
|
||||
|
||||
// If you need the dish to extend further, you can 'overdraw' the rectangle it will hit. this was mostly for iso enter and should be deprecated
|
||||
// If you need the dish to extend further, you can 'overdraw' the rectangle it will hit
|
||||
$dish_overdraw_width = 0;
|
||||
// Same as width but for height
|
||||
$dish_overdraw_height = 0;
|
||||
|
|
@ -188,12 +184,15 @@ $tertiary_color = [1, .6941, .2];
|
|||
$quaternary_color = [.4078, .3569, .749];
|
||||
$warning_color = [1,0,0, 0.15];
|
||||
|
||||
// 3d surface variables
|
||||
// see functions.scad for the surface function
|
||||
$3d_surface_size = 10;
|
||||
$3d_surface_step = 1;
|
||||
// normally the bottom of the keytop looks like the top - curved, at least
|
||||
// underneath the support structure. This ensures there's a minimum thickness for the
|
||||
// underside of the keycap, but it's a fair bit of geometry
|
||||
$flat_keytop_bottom = true;
|
||||
|
||||
// how many facets circles will have when used in these features
|
||||
$minkowski_facets = 30;
|
||||
$shape_facets =30;
|
||||
|
||||
// 3d surface settings
|
||||
// unused for now
|
||||
$3d_surface_size = 100;
|
||||
// resolution in each axis. 10 = 10 divisions per x/y = 100 points total
|
||||
$3d_surface_step = 10;
|
||||
$shape_facets = 30;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,6 @@
|
|||
include <../functions.scad>
|
||||
include <../libraries/round-anything/polyround.scad>
|
||||
|
||||
width_ratio = unit_length(1.25) / unit_length(1.5);
|
||||
height_ratio = unit_length(1) / unit_length(2);
|
||||
|
||||
// corollary is rounded_square
|
||||
// NOT 3D
|
||||
function unit_length(length) = unit * (length - 1) + 18.16;
|
||||
|
||||
module ISO_enter_shape(size, delta, progress){
|
||||
width = size[0];
|
||||
|
|
@ -16,53 +13,43 @@ module ISO_enter_shape(size, delta, progress){
|
|||
// and wants to pass just width and height, we make these ratios to know where
|
||||
// to put the elbow joint
|
||||
|
||||
delta = delta / 2;
|
||||
width_ratio = unit_length(1.25) / unit_length(1.5);
|
||||
height_ratio = unit_length(1) / unit_length(2);
|
||||
|
||||
pointArray = [
|
||||
[ 0-delta.x, 0-delta.y], // top right
|
||||
[ 0-delta.x, -height+delta.y], // bottom right
|
||||
[-width * width_ratio+delta.x, -height+delta.y], // bottom left
|
||||
[-width * width_ratio + delta.x,-height * height_ratio+delta.y], // inner middle point
|
||||
[ -width + delta.x,-height * height_ratio + delta.y], // outer middle point
|
||||
[ -width + delta.x, 0-delta.y] // top left
|
||||
[ 0, 0], // top right
|
||||
[ 0, -height], // bottom right
|
||||
[-width * width_ratio, -height], // bottom left
|
||||
[-width * width_ratio,-height * height_ratio], // inner middle point
|
||||
[ -width,-height * height_ratio], // outer middle point
|
||||
[ -width, 0] // top left
|
||||
];
|
||||
|
||||
minkowski(){
|
||||
circle(r=$corner_radius);
|
||||
circle(r=corner_size);
|
||||
// gives us rounded inner corner
|
||||
offset(r=-$corner_radius*2) {
|
||||
offset(r=-corner_size*2) {
|
||||
translate([(width * width_ratio)/2, height/2]) polygon(points=pointArray);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function iso_enter_vertices(size, delta, progress, thickness_difference) = [
|
||||
[ 0-delta.x/2 * progress - thickness_difference/2, 0 - delta.y / 2 * progress - thickness_difference/2], // top right
|
||||
[ 0-delta.x/2 * progress - thickness_difference/2, -size[1] + delta.y / 2 * progress + thickness_difference/2], // bottom right
|
||||
[-size[0] * width_ratio + delta.x/2 * progress + thickness_difference/2, -size[1] + delta.y / 2 * progress + thickness_difference/2], // bottom left
|
||||
[-size[0] * width_ratio + delta.x/2 * progress + thickness_difference/2,-size[1] * height_ratio + delta.y / 2 * progress + thickness_difference/2], // inner middle point
|
||||
[ -size[0] + delta.x/2 * progress + thickness_difference/2,-size[1] * height_ratio + delta.y / 2 * progress + thickness_difference/2], // outer middle point
|
||||
[ -size[0] + delta.x/2 * progress + thickness_difference/2, 0 - delta.y / 2 * progress - thickness_difference/2] // top left
|
||||
function iso_enter_vertices(width, height, width_ratio, height_ratio, wd, hd) = [
|
||||
[ 0-wd, 0-hd], // top right
|
||||
[ 0-wd, -height+hd], // bottom right
|
||||
[-width * width_ratio+wd, -height+hd], // bottom left
|
||||
[-width * width_ratio+wd,-height * height_ratio+hd], // inner middle point
|
||||
[ -width+wd,-height * height_ratio+hd], // outer middle point
|
||||
[ -width+wd, 0-hd] // top left
|
||||
] + [
|
||||
[(size[0] * width_ratio)/2, size[1]/2 ],
|
||||
[(size[0] * width_ratio)/2, size[1]/2 ],
|
||||
[(size[0] * width_ratio)/2, size[1]/2 ],
|
||||
[(size[0] * width_ratio)/2, size[1]/2 ],
|
||||
[(size[0] * width_ratio)/2, size[1]/2 ],
|
||||
[(size[0] * width_ratio)/2, size[1]/2 ]
|
||||
[(width * width_ratio)/2, height/2 ],
|
||||
[(width * width_ratio)/2, height/2 ],
|
||||
[(width * width_ratio)/2, height/2 ],
|
||||
[(width * width_ratio)/2, height/2 ],
|
||||
[(width * width_ratio)/2, height/2 ],
|
||||
[(width * width_ratio)/2, height/2 ]
|
||||
];
|
||||
|
||||
// no rounding on the corners at all
|
||||
function skin_iso_enter_shape(size, delta, progress, thickness_difference) =
|
||||
polyRound(
|
||||
add_rounding(
|
||||
iso_enter_vertices(
|
||||
size,
|
||||
delta,
|
||||
progress,
|
||||
thickness_difference
|
||||
),
|
||||
$corner_radius
|
||||
),
|
||||
$shape_facets
|
||||
);
|
||||
iso_enter_vertices(size.x, size.y, unit_length(1.25) / unit_length(1.5), unit_length(1) / unit_length(2), thickness_difference/2 + delta.x * progress/2, thickness_difference/2 + delta.y * progress/2);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
include <square.scad>
|
||||
include <../libraries/round-anything/polyround.scad>
|
||||
include <../libraries/rounded_rectangle_profile.scad>
|
||||
|
||||
module rounded_square_shape(size, delta, progress, center = true) {
|
||||
offset(r=$corner_radius, $fa=360/$shape_facets){
|
||||
|
|
@ -8,5 +7,6 @@ module rounded_square_shape(size, delta, progress, center = true) {
|
|||
}
|
||||
|
||||
// for skin
|
||||
|
||||
function skin_rounded_square(size, delta, progress, thickness_difference) =
|
||||
polyRound(add_rounding(rectangle_profile(size - (delta * progress)), $corner_radius), $shape_facets/4);
|
||||
rounded_rectangle_profile(size - (delta * progress) - [thickness_difference, thickness_difference]*2, fn=$shape_facets, r=$corner_radius);
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
include <../libraries/rounded_rectangle_profile.scad>
|
||||
|
||||
// rounded square shape with additional sculpting functions to better approximate
|
||||
|
||||
// When sculpting sides, how much in should the tops come
|
||||
|
|
@ -42,35 +44,8 @@ module sculpted_square_shape(size, delta, progress) {
|
|||
}
|
||||
}
|
||||
|
||||
function new_side_rounded_square(size, r, cornerRadius=0) =
|
||||
let(
|
||||
width = (size.x - r)/2,
|
||||
height = (size.y - r)/2,
|
||||
|
||||
// fudge numbers! the radius conflict resolution in polyround smooths out
|
||||
// the entire shape based on the ratios between conflicting radii. bumping
|
||||
// these up makes the whole shape more fluid
|
||||
widthRadius = r ? width*8 : 0,
|
||||
heightRadius = r ? height*8 : 0,
|
||||
|
||||
bow = r/2,
|
||||
|
||||
// close enough :/
|
||||
facets = 360 / $shape_facets/2,
|
||||
|
||||
points = [
|
||||
[-width,-height,cornerRadius],
|
||||
[0,-height-bow,widthRadius],
|
||||
[width,-height,cornerRadius],
|
||||
[width + bow,0,heightRadius],
|
||||
[width,height,cornerRadius],
|
||||
[0,height + bow,widthRadius],
|
||||
[-width,height,cornerRadius],
|
||||
[-width-bow,0,heightRadius]
|
||||
]
|
||||
) polyRound(points,facets);
|
||||
|
||||
|
||||
// fudging the hell out of this, I don't remember what the negative-offset-positive-offset was doing in the module above
|
||||
// also no 'bowed' square shape for now
|
||||
function skin_sculpted_square_shape(size, delta, progress, thickness_difference) =
|
||||
let(
|
||||
width = size[0],
|
||||
|
|
@ -92,7 +67,13 @@ function skin_sculpted_square_shape(size, delta, progress, thickness_difference)
|
|||
width - extra_width_this_slice - thickness_difference,
|
||||
height - extra_height_this_slice - thickness_difference
|
||||
]
|
||||
) new_side_rounded_square(square_size, $more_side_sculpting_factor * progress, extra_corner_radius_this_slice);
|
||||
) double_rounded_rectangle_profile(square_size - [extra_corner_radius_this_slice, extra_corner_radius_this_slice]/4, fn=36, r=extra_corner_radius_this_slice/1.5 + $more_side_sculpting_factor * progress);
|
||||
|
||||
/* offset(r = extra_corner_radius_this_slice) {
|
||||
offset(r = -extra_corner_radius_this_slice) {
|
||||
side_rounded_square(square_size, r = $more_side_sculpting_factor * progress);
|
||||
}
|
||||
} */
|
||||
|
||||
|
||||
module side_rounded_square(size, r) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
use <../functions.scad>
|
||||
include <../libraries/rounded_rectangle_profile.scad>
|
||||
|
||||
|
||||
// we do this weird key_shape_type check here because rounded_square uses
|
||||
// square_shape, and we want flat sides to work for that too.
|
||||
|
|
@ -21,23 +23,14 @@ module square_shape(size, delta, progress){
|
|||
// shape makes the sides flat by making the top a trapezoid.
|
||||
// This obviously doesn't work with rounded sides at all
|
||||
module flat_sided_square_shape(size, delta, progress) {
|
||||
polygon(skin_flat_sided_square_shape(size, delta, progress));
|
||||
polygon(points=[
|
||||
[(-size.x + (delta.x + extra_keytop_length_for_flat_sides()) * progress)/2, (-size.y + delta.y * progress)/2],
|
||||
[(size.x - (delta.x + extra_keytop_length_for_flat_sides()) * progress)/2,(-size.y + delta.y * progress)/2],
|
||||
[(size.x - (delta.x - extra_keytop_length_for_flat_sides()) * progress)/2, (size.y - delta.y * progress)/2],
|
||||
[(-size.x + (delta.x - extra_keytop_length_for_flat_sides()) * progress)/2, (size.y - delta.y * progress)/2]
|
||||
]);
|
||||
}
|
||||
|
||||
function skin_flat_sided_square_shape(size,delta,progress) = [
|
||||
[(-size.x + (delta.x + extra_keytop_length_for_flat_sides()) * progress)/2, (-size.y + delta.y * progress)/2],
|
||||
[(size.x - (delta.x + extra_keytop_length_for_flat_sides()) * progress)/2,(-size.y + delta.y * progress)/2],
|
||||
[(size.x - (delta.x - extra_keytop_length_for_flat_sides()) * progress)/2, (size.y - delta.y * progress)/2],
|
||||
[(-size.x + (delta.x - extra_keytop_length_for_flat_sides()) * progress)/2, (size.y - delta.y * progress)/2]
|
||||
];
|
||||
|
||||
function rectangle_profile(size) = [
|
||||
[-size.x/2, -size.y/2],
|
||||
[size.x/2, -size.y/2],
|
||||
[size.x/2, size.y/2],
|
||||
[-size.x/2, size.y/2],
|
||||
];
|
||||
|
||||
function skin_square_shape(size, delta, progress, thickness_difference) =
|
||||
let(
|
||||
width = size[0],
|
||||
|
|
@ -50,4 +43,4 @@ function skin_square_shape(size, delta, progress, thickness_difference) =
|
|||
width - width_difference - thickness_difference,
|
||||
height - height_difference - thickness_difference
|
||||
]
|
||||
) $key_shape_type == "flat_sided_square" ? skin_flat_sided_square_shape(size, delta, progress) : rectangle_profile(square_size);
|
||||
) rectangle_profile(square_size, fn=36);
|
||||
|
|
|
|||
|
|
@ -4,23 +4,22 @@ include <stems/box_cherry.scad>
|
|||
include <stems/alps.scad>
|
||||
include <stems/filled.scad>
|
||||
include <stems/cherry_stabilizer.scad>
|
||||
include <stems/custom.scad>
|
||||
|
||||
|
||||
//whole stem, alps or cherry, trimmed to fit
|
||||
module stem(stem_type, depth, slop, throw){
|
||||
module stem(stem_type, depth, slop){
|
||||
if (stem_type == "alps") {
|
||||
alps_stem(depth, slop, throw);
|
||||
alps_stem(depth, slop);
|
||||
} else if (stem_type == "cherry" || stem_type == "costar_stabilizer") {
|
||||
cherry_stem(depth, slop, throw);
|
||||
cherry_stem(depth, slop);
|
||||
} else if (stem_type == "rounded_cherry") {
|
||||
rounded_cherry_stem(depth, slop, throw);
|
||||
rounded_cherry_stem(depth, slop);
|
||||
} else if (stem_type == "box_cherry") {
|
||||
box_cherry_stem(depth, slop, throw);
|
||||
box_cherry_stem(depth, slop);
|
||||
} else if (stem_type == "filled") {
|
||||
filled_stem();
|
||||
} else if (stem_type == "cherry_stabilizer") {
|
||||
cherry_stabilizer_stem(depth, slop, throw);
|
||||
cherry_stabilizer_stem(depth, slop);
|
||||
} else if (stem_type == "disable") {
|
||||
children();
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
module alps_stem(depth, slop, throw){
|
||||
module alps_stem(depth, has_brim, slop){
|
||||
linear_extrude(height=depth) {
|
||||
square($alps_stem, center = true);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
include <../functions.scad>
|
||||
include <cherry.scad>
|
||||
|
||||
module box_cherry_stem(depth, slop, throw) {
|
||||
module box_cherry_stem(depth, slop) {
|
||||
difference(){
|
||||
// outside shape
|
||||
linear_extrude(height = depth) {
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ module inside_cherry_cross(slop) {
|
|||
}
|
||||
}
|
||||
|
||||
module cherry_stem(depth, slop, throw) {
|
||||
module cherry_stem(depth, slop) {
|
||||
difference(){
|
||||
// outside shape
|
||||
linear_extrude(height = depth) {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ module inside_cherry_stabilizer_cross(slop) {
|
|||
}
|
||||
}
|
||||
|
||||
module cherry_stabilizer_stem(depth, slop, throw) {
|
||||
module cherry_stabilizer_stem(depth, slop) {
|
||||
difference(){
|
||||
// outside shape
|
||||
linear_extrude(height = depth) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
module filled_stem(_depth, _slop, _throw) {
|
||||
module filled_stem() {
|
||||
// I broke the crap out of this stem type due to the changes I made around how stems are differenced
|
||||
// now that we just take the dish out of stems in order to support stuff like
|
||||
// bare stem keycaps (and buckling spring eventually) we can't just make a
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
include <../functions.scad>
|
||||
include <cherry.scad>
|
||||
|
||||
module rounded_cherry_stem(depth, slop, throw) {
|
||||
module rounded_cherry_stem(depth, slop) {
|
||||
difference(){
|
||||
cylinder(d=$rounded_cherry_stem_d, h=depth);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue