mirror of
https://github.com/rsheldiii/KeyV2.git
synced 2024-11-30 00:16:10 +00:00
Better full sculpting, preliminary skin extrusion, better supports
This commit is contained in:
parent
0e3e1a9ecb
commit
f0a7b27e8c
@ -28,3 +28,7 @@ function total_key_height(delta = 0) = $bottom_key_height + $unit * ($key_height
|
|||||||
// actual mm key width and height at the top
|
// actual mm key width and height at the top
|
||||||
function top_total_key_width() = $bottom_key_width + ($unit * ($key_length - 1)) - $width_difference;
|
function top_total_key_width() = $bottom_key_width + ($unit * ($key_length - 1)) - $width_difference;
|
||||||
function top_total_key_height() = $bottom_key_height + ($unit * ($key_height - 1)) - $height_difference;
|
function top_total_key_height() = $bottom_key_height + ($unit * ($key_height - 1)) - $height_difference;
|
||||||
|
|
||||||
|
function side_tilt(column) = asin($unit * column / $double_sculpt_radius);
|
||||||
|
// tan of 0 is 0, division by 0 is nan, so we have to guard
|
||||||
|
function extra_side_tilt_height(column) = side_tilt(column) ? ($double_sculpt_radius - (unit * abs(column)) / tan(abs(side_tilt(column)))) : 0;
|
||||||
|
85
src/key.scad
85
src/key.scad
@ -9,6 +9,12 @@ include <key_features.scad>
|
|||||||
|
|
||||||
include <libraries/geodesic_sphere.scad>
|
include <libraries/geodesic_sphere.scad>
|
||||||
|
|
||||||
|
// for skin hulls
|
||||||
|
use <libraries/scad-utils/transformations.scad>
|
||||||
|
use <libraries/scad-utils/lists.scad>
|
||||||
|
use <libraries/scad-utils/shapes.scad>
|
||||||
|
use <libraries/skin.scad>
|
||||||
|
|
||||||
|
|
||||||
/* [Hidden] */
|
/* [Hidden] */
|
||||||
SMALLEST_POSSIBLE = 1/128;
|
SMALLEST_POSSIBLE = 1/128;
|
||||||
@ -27,8 +33,25 @@ module shape(thickness_difference, depth_difference){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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. no longer includes dish
|
||||||
|
// randomly doesnt work sometimes
|
||||||
|
// the dish doesn't _quite_ reach as far as it should
|
||||||
module rounded_shape() {
|
module rounded_shape() {
|
||||||
|
dished(-$minkowski_radius, $inverted_dish) {
|
||||||
|
color(blue) minkowski(){
|
||||||
|
// half minkowski in the z direction
|
||||||
|
color(blue) shape_hull($minkowski_radius * 2, $minkowski_radius/2, $inverted_dish ? 2 : 0);
|
||||||
|
/* cube($minkowski_radius); */
|
||||||
|
sphere(r=$minkowski_radius, $fn=48);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* %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() {
|
||||||
color(blue) minkowski(){
|
color(blue) 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);
|
||||||
@ -39,7 +62,8 @@ module rounded_shape() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// basic key shape, no dish, no inside
|
// basic key shape, no dish, no inside
|
||||||
@ -48,7 +72,9 @@ module rounded_shape() {
|
|||||||
// 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 ($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);
|
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);
|
||||||
@ -56,6 +82,40 @@ module shape_hull(thickness_difference, depth_difference, extra_slices = 0){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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(thickness_difference),
|
||||||
|
total_key_height(thickness_difference),
|
||||||
|
],
|
||||||
|
[$width_difference, $height_difference],
|
||||||
|
progress
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
// 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){
|
||||||
@ -66,7 +126,10 @@ module linear_extrude_shape_hull(thickness_difference, depth_difference, extra_s
|
|||||||
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)],
|
||||||
|
[$width_difference, $height_difference]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -120,7 +183,6 @@ module top_placement(depth_difference) {
|
|||||||
top_tilt_by_height = -$top_tilt / $key_height;
|
top_tilt_by_height = -$top_tilt / $key_height;
|
||||||
top_tilt_y_by_length = $double_sculpted ? (-$top_tilt_y / $key_length) : 0;
|
top_tilt_y_by_length = $double_sculpted ? (-$top_tilt_y / $key_length) : 0;
|
||||||
|
|
||||||
|
|
||||||
translate([$top_skew_x + $dish_skew_x, $top_skew + $dish_skew_y, $total_depth - depth_difference]){
|
translate([$top_skew_x + $dish_skew_x, $top_skew + $dish_skew_y, $total_depth - depth_difference]){
|
||||||
rotate([top_tilt_by_height, top_tilt_y_by_length,0]){
|
rotate([top_tilt_by_height, top_tilt_y_by_length,0]){
|
||||||
children();
|
children();
|
||||||
@ -133,7 +195,7 @@ module _dish() {
|
|||||||
dish(top_total_key_width() + $dish_overdraw_width, top_total_key_height() + $dish_overdraw_height, $dish_depth, $inverted_dish);
|
dish(top_total_key_width() + $dish_overdraw_width, top_total_key_height() + $dish_overdraw_height, $dish_depth, $inverted_dish);
|
||||||
}
|
}
|
||||||
|
|
||||||
module envelope(depth_difference) {
|
module envelope(depth_difference=0) {
|
||||||
s = 1.5;
|
s = 1.5;
|
||||||
hull(){
|
hull(){
|
||||||
cube([total_key_width() * s, total_key_height() * s, 0.01], center = true);
|
cube([total_key_width() * s, total_key_height() * s, 0.01], center = true);
|
||||||
@ -243,8 +305,7 @@ module clearance_check() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// legends / artisan support
|
module legends(depth) {
|
||||||
module artisan(depth) {
|
|
||||||
top_of_key() {
|
top_of_key() {
|
||||||
// outset legend
|
// outset legend
|
||||||
if (len($legends) > 0) {
|
if (len($legends) > 0) {
|
||||||
@ -252,6 +313,12 @@ module artisan(depth) {
|
|||||||
keytext($legends[i][0], $legends[i][1], $legends[i][2], depth);
|
keytext($legends[i][0], $legends[i][1], $legends[i][2], depth);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// legends / artisan support
|
||||||
|
module artisan(depth) {
|
||||||
|
top_of_key() {
|
||||||
// artisan objects / outset shape legends
|
// artisan objects / outset shape legends
|
||||||
children();
|
children();
|
||||||
}
|
}
|
||||||
@ -283,12 +350,14 @@ module key(inset = false) {
|
|||||||
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(0) children();
|
if(!inset) artisan(0) children();
|
||||||
|
if($outset_legends) legends(0);
|
||||||
// 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();
|
||||||
|
if(!$outset_legends) legends(0.3);
|
||||||
// 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();
|
||||||
|
@ -11,3 +11,5 @@ include <layouts/lets_split/default.scad>
|
|||||||
include <layouts/project_zen/default.scad>
|
include <layouts/project_zen/default.scad>
|
||||||
|
|
||||||
include <layouts/60_percent/default.scad>
|
include <layouts/60_percent/default.scad>
|
||||||
|
include <layouts/gherkin/default.scad>
|
||||||
|
include <layouts/gherkin/gherkin_bump.scad>
|
||||||
|
@ -8,6 +8,7 @@ include <key_profiles/dsa.scad>
|
|||||||
include <key_profiles/sa.scad>
|
include <key_profiles/sa.scad>
|
||||||
include <key_profiles/g20.scad>
|
include <key_profiles/g20.scad>
|
||||||
include <key_profiles/hipro.scad>
|
include <key_profiles/hipro.scad>
|
||||||
|
include <key_profiles/grid.scad>
|
||||||
|
|
||||||
// man, wouldn't it be so cool if functions were first order
|
// man, wouldn't it be so cool if functions were first order
|
||||||
module key_profile(key_profile_type, row, column=0) {
|
module key_profile(key_profile_type, row, column=0) {
|
||||||
@ -23,6 +24,8 @@ module key_profile(key_profile_type, row, column=0) {
|
|||||||
g20_row(row, column) children();
|
g20_row(row, column) children();
|
||||||
} else if (key_profile_type == "hipro") {
|
} else if (key_profile_type == "hipro") {
|
||||||
hipro_row(row, column) children();
|
hipro_row(row, column) children();
|
||||||
|
} else if (key_profile_type == "grid") {
|
||||||
|
grid_row(row, column) children();
|
||||||
} else if (key_profile_type == "disable") {
|
} else if (key_profile_type == "disable") {
|
||||||
children();
|
children();
|
||||||
} else {
|
} else {
|
||||||
|
@ -5,32 +5,41 @@ module dcs_row(row=3, column=0) {
|
|||||||
$width_difference = 6;
|
$width_difference = 6;
|
||||||
$height_difference = 4;
|
$height_difference = 4;
|
||||||
$dish_type = "cylindrical";
|
$dish_type = "cylindrical";
|
||||||
$dish_depth = 1;
|
$dish_depth = 0.5;
|
||||||
$dish_skew_x = 0;
|
$dish_skew_x = 0;
|
||||||
$dish_skew_y = 0;
|
$dish_skew_y = 0;
|
||||||
$top_skew = 1.75;
|
$top_skew = 1.75;
|
||||||
|
|
||||||
$top_tilt_y = column * 3.5;
|
$top_tilt_y = side_tilt(column);
|
||||||
|
extra_height = extra_side_tilt_height(column);
|
||||||
|
|
||||||
// hack so you can do these in a for loop
|
// this dish depth should match the depth of the uberdishing in fully sculpted mode
|
||||||
|
// but it doesn't, and it's very slight for any reasonable double sculpting
|
||||||
|
/* $dish_depth = $double_sculpt_radius - sin(acos(top_total_key_width()/2 /$double_sculpt_radius)) * $double_sculpt_radius; */
|
||||||
|
|
||||||
|
/* echo("DISH DEPTH", $dish_depth, "column", column); */
|
||||||
|
|
||||||
|
// 5/0 is a hack so you can do these in a for loop
|
||||||
if (row == 5 || row == 0) {
|
if (row == 5 || row == 0) {
|
||||||
$total_depth = 11.5;
|
$total_depth = 11.5 + extra_height;
|
||||||
$top_tilt = -6;
|
$top_tilt = -6;
|
||||||
|
|
||||||
children();
|
children();
|
||||||
} else if (row == 1) {
|
} else if (row == 1) {
|
||||||
$total_depth = 8.5;
|
$total_depth = 8.5 + extra_height;
|
||||||
$top_tilt = -1;
|
$top_tilt = -1;
|
||||||
|
|
||||||
children();
|
children();
|
||||||
} else if (row == 2) {
|
} else if (row == 2) {
|
||||||
$total_depth = 7.5;
|
$total_depth = 7.5 + extra_height;
|
||||||
$top_tilt = 3;
|
$top_tilt = 3;
|
||||||
children();
|
children();
|
||||||
} else if (row == 3) {
|
} else if (row == 3) {
|
||||||
$total_depth = 6;
|
$total_depth = 6 + extra_height;
|
||||||
$top_tilt = 7;
|
$top_tilt = 7;
|
||||||
children();
|
children();
|
||||||
} else if (row == 4) {
|
} else if (row == 4) {
|
||||||
$total_depth = 6;
|
$total_depth = 6 + extra_height;
|
||||||
$top_tilt = 16;
|
$top_tilt = 16;
|
||||||
children();
|
children();
|
||||||
} else {
|
} else {
|
||||||
|
@ -14,7 +14,7 @@ module dsa_row(row=3, column = 0) {
|
|||||||
$enable_side_sculpting = true;
|
$enable_side_sculpting = true;
|
||||||
$corner_radius = 0.25;
|
$corner_radius = 0.25;
|
||||||
|
|
||||||
$top_tilt_y = column * 3;
|
$top_tilt_y = column * 3 * $double_sculpt_modifier;
|
||||||
|
|
||||||
depth_raisers = [0, 3.5, 1, 0, 1, 3];
|
depth_raisers = [0, 3.5, 1, 0, 1, 3];
|
||||||
if (row == 5) {
|
if (row == 5) {
|
||||||
|
@ -16,7 +16,7 @@ module g20_row(row=3, column = 0) {
|
|||||||
//also,
|
//also,
|
||||||
$rounded_key = true;
|
$rounded_key = true;
|
||||||
|
|
||||||
$top_tilt_y = column * 3;
|
$top_tilt_y = column * 3 * $double_sculpt_modifier;
|
||||||
$total_depth = 6 + abs((row-3) * 0.5);
|
$total_depth = 6 + abs((row-3) * 0.5);
|
||||||
|
|
||||||
if (row == 5 || row == 0) {
|
if (row == 5 || row == 0) {
|
||||||
|
45
src/key_profiles/grid.scad
Normal file
45
src/key_profiles/grid.scad
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
module grid_row(row=3, column = 0) {
|
||||||
|
$bottom_key_width = 18.16;
|
||||||
|
$bottom_key_height = 18.16;
|
||||||
|
$width_difference = 0.2;
|
||||||
|
$height_difference = 0.2;
|
||||||
|
$top_tilt = 0;
|
||||||
|
$top_skew = 0;
|
||||||
|
$dish_type = "old spherical";
|
||||||
|
// something weird is going on with this and legends - can't put it below 1.2 or they won't show
|
||||||
|
$dish_depth = 1;
|
||||||
|
$dish_skew_x = 0;
|
||||||
|
$dish_skew_y = 0;
|
||||||
|
|
||||||
|
$linear_extrude_shape = true;
|
||||||
|
|
||||||
|
|
||||||
|
$dish_overdraw_width = -8;
|
||||||
|
$dish_overdraw_height = -8;
|
||||||
|
|
||||||
|
$minkowski_radius = 0.5;
|
||||||
|
//also,
|
||||||
|
/* $rounded_key = true; */
|
||||||
|
|
||||||
|
$top_tilt_y = column * 3 * $double_sculpt_modifier;
|
||||||
|
$total_depth = 6 + abs((row-3) * 0.5);
|
||||||
|
|
||||||
|
if (row == 5 || row == 0) {
|
||||||
|
/* $top_tilt = -18.55; */
|
||||||
|
children();
|
||||||
|
} else if (row == 1) {
|
||||||
|
/* $top_tilt = (row-3) * 7 + 2.5; */
|
||||||
|
children();
|
||||||
|
} else if (row == 2) {
|
||||||
|
/* $top_tilt = (row-3) * 7 + 2.5; */
|
||||||
|
children();
|
||||||
|
} else if (row == 3) {
|
||||||
|
/* $top_tilt = (row-3) * 7 + 2.5; */
|
||||||
|
children();
|
||||||
|
} else if (row == 4) {
|
||||||
|
/* $top_tilt = (row-3) * 7 + 2.5; */
|
||||||
|
children();
|
||||||
|
} else {
|
||||||
|
children();
|
||||||
|
}
|
||||||
|
}
|
@ -8,7 +8,7 @@ module hipro_row(row=3, column=0) {
|
|||||||
$width_difference = ($bottom_key_width - 12.3);
|
$width_difference = ($bottom_key_width - 12.3);
|
||||||
$height_difference = ($bottom_key_height - 12.65);
|
$height_difference = ($bottom_key_height - 12.65);
|
||||||
$dish_type = "spherical";
|
$dish_type = "spherical";
|
||||||
$dish_depth = 0.9;
|
$dish_depth = 0.75;
|
||||||
$dish_skew_x = 0;
|
$dish_skew_x = 0;
|
||||||
$dish_skew_y = 0;
|
$dish_skew_y = 0;
|
||||||
$top_skew = 0;
|
$top_skew = 0;
|
||||||
@ -17,23 +17,24 @@ module hipro_row(row=3, column=0) {
|
|||||||
// do you even minkowski bro
|
// do you even minkowski bro
|
||||||
$corner_radius = 0.25;
|
$corner_radius = 0.25;
|
||||||
|
|
||||||
$top_tilt_y = column * 3;
|
$top_tilt_y = side_tilt(column);
|
||||||
|
extra_height = extra_side_tilt_height(column);
|
||||||
|
|
||||||
if (row == 1){
|
if (row <= 1){
|
||||||
$total_depth = 12.7;
|
$total_depth = 13.7 + extra_height;
|
||||||
// TODO I didn't change these yet
|
// TODO I didn't change these yet
|
||||||
$top_tilt = -13;
|
$top_tilt = -13;
|
||||||
children();
|
children();
|
||||||
} else if (row == 2) {
|
} else if (row == 2) {
|
||||||
$total_depth = 10.1;
|
$total_depth = 11.1 + extra_height;
|
||||||
$top_tilt = -7;
|
$top_tilt = -7;
|
||||||
children();
|
children();
|
||||||
} else if (row == 3) {
|
} else if (row == 3) {
|
||||||
$total_depth = 10.1;
|
$total_depth = 11.1 + extra_height;
|
||||||
$top_tilt = 7;
|
$top_tilt = 7;
|
||||||
children();
|
children();
|
||||||
} else if (row == 4 || row == 5){
|
} else if (row == 4 || row == 5){
|
||||||
$total_depth = 11.25;
|
$total_depth = 12.25 + extra_height;
|
||||||
$top_tilt = 13;
|
$top_tilt = 13;
|
||||||
children();
|
children();
|
||||||
} else {
|
} else {
|
||||||
|
@ -10,7 +10,7 @@ module oem_row(row=3) {
|
|||||||
$top_skew = 1.75;
|
$top_skew = 1.75;
|
||||||
$stem_inset = 1.2;
|
$stem_inset = 1.2;
|
||||||
|
|
||||||
$top_tilt_y = column * 3;
|
$top_tilt_y = column * 3 * $double_sculpt_modifier;
|
||||||
|
|
||||||
if (row == 5 || row == 0) {
|
if (row == 5 || row == 0) {
|
||||||
$total_depth = 11.2;
|
$total_depth = 11.2;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
module sa_row(n=3) {
|
module sa_row(n=3, column=0) {
|
||||||
$key_shape_type = "sculpted_square";
|
$key_shape_type = "sculpted_square";
|
||||||
$bottom_key_width = 18.4;
|
$bottom_key_width = 18.4;
|
||||||
$bottom_key_height = 18.4;
|
$bottom_key_height = 18.4;
|
||||||
@ -13,27 +13,36 @@ module sa_row(n=3) {
|
|||||||
// 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;
|
||||||
|
|
||||||
|
// this is _incredibly_ intensive
|
||||||
|
/* $rounded_key = true; */
|
||||||
|
|
||||||
|
$top_tilt_y = side_tilt(column);
|
||||||
|
extra_height = extra_side_tilt_height(column);
|
||||||
|
|
||||||
// 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){
|
if (n <= 1){
|
||||||
$total_depth = 14.89;
|
$total_depth = 14.89 + extra_height;
|
||||||
$top_tilt = -13;
|
$top_tilt = -13;
|
||||||
children();
|
children();
|
||||||
} else if (n == 2) {
|
} else if (n == 2) {
|
||||||
$total_depth = 12.925;
|
$total_depth = 12.925 + extra_height;
|
||||||
$top_tilt = -7;
|
$top_tilt = -7;
|
||||||
children();
|
children();
|
||||||
} else if (n == 3 || n == 5) {
|
} else if (n == 3) {
|
||||||
$total_depth = 12.5;
|
$total_depth = 12.5 + extra_height;
|
||||||
$top_tilt = 0;
|
$top_tilt = 0;
|
||||||
children();
|
children();
|
||||||
} else if (n == 4){
|
} else if (n == 4){
|
||||||
$total_depth = 12.925;
|
$total_depth = 12.925 + extra_height;
|
||||||
$top_tilt = 7;
|
$top_tilt = 7;
|
||||||
children();
|
children();
|
||||||
} else {
|
} else {
|
||||||
|
$total_depth = 12.5 + extra_height;
|
||||||
|
$top_tilt = 0;
|
||||||
children();
|
children();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
12
src/layouts/gherkin/default.scad
Normal file
12
src/layouts/gherkin/default.scad
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
include <../layout.scad>
|
||||||
|
|
||||||
|
// negative numbers are used for spacing
|
||||||
|
gherkin_mapping = [
|
||||||
|
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||||
|
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||||
|
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||||
|
];
|
||||||
|
|
||||||
|
module gherkin_default(profile) {
|
||||||
|
layout(gherkin_mapping, profile, row_sculpting_offset=1);
|
||||||
|
}
|
20
src/layouts/gherkin/gherkin_bump.scad
Normal file
20
src/layouts/gherkin/gherkin_bump.scad
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
include <../layout.scad>
|
||||||
|
|
||||||
|
// negative numbers are used for spacing
|
||||||
|
gherkin_bump_mapping = [
|
||||||
|
[1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1],
|
||||||
|
[1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1],
|
||||||
|
[1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1],
|
||||||
|
[-1, -1, -1, -1, 2, -1, -1, -1, -1],
|
||||||
|
];
|
||||||
|
|
||||||
|
gherkin_bump_legends = [
|
||||||
|
["q", "w", "e", "r", "t", "", "y", "u", "i", "o", "p"],
|
||||||
|
["a", "s", "d", "f", "g", "", "h", "j", "k", "l", ";"],
|
||||||
|
["z", "x", "c", "v", "b", "", "n", "m", ",", ".", "/"],
|
||||||
|
["", "", "", "", "", "", "", "", ""],
|
||||||
|
];
|
||||||
|
|
||||||
|
module gherkin_bump_layout(profile, row_sculpting_offset=1, column_override=undef) {
|
||||||
|
layout(gherkin_bump_mapping, profile, legends=gherkin_bump_legends, row_sculpting_offset=row_sculpting_offset, column_override=column_override, column_sculpt_profile="cresting_wave");
|
||||||
|
}
|
@ -5,24 +5,38 @@ function abs_sum(list, x=0) =
|
|||||||
x + abs(list[0]) :
|
x + abs(list[0]) :
|
||||||
abs_sum([for (x = [1: len(list) - 1]) list[x]], x+abs(list[0]));
|
abs_sum([for (x = [1: len(list) - 1]) list[x]], x+abs(list[0]));
|
||||||
|
|
||||||
module layout(list, profile="dcs", row_sculpting_offset=0, row_override=undef) {
|
function 2hands(index, total) = ((index+0.5) % (total/2)) - (total/4);
|
||||||
|
function cresting_wave(index, total, mod=5) = (index < total/2) ? (((index + 0.5) / total)*mod) : -(mod - ((index + 0.5) / total * mod));
|
||||||
|
function 1hand(index, total) = (index % (total)) - (total/2);
|
||||||
|
|
||||||
|
|
||||||
|
// chooses between all the sculpting options
|
||||||
|
// checks if column is smack in middle of row - if so, no sculpting
|
||||||
|
// since we are zero indexed, the 7th row has an index of 6 and is the center of 13. 6*2+1 = 13
|
||||||
|
function double_sculpted_column(column, row_length, column_sculpt_profile) =
|
||||||
|
(column*2 + 1 == row_length) ?
|
||||||
|
0 : (column_sculpt_profile == "2hands") ?
|
||||||
|
2hands(column, row_length) : (column_sculpt_profile == "1hand") ?
|
||||||
|
1hand(column, row_length) : (column_sculpt_profile == "cresting_wave") ?
|
||||||
|
cresting_wave(column, row_length) : 0;
|
||||||
|
|
||||||
|
module layout(list, profile="dcs", legends=undef, row_sculpting_offset=0, row_override=undef, column_sculpt_profile="2hands", column_override=undef) {
|
||||||
for (row = [0:len(list)-1]){
|
for (row = [0:len(list)-1]){
|
||||||
echo("**ROW**:", row);
|
echo("**ROW**:", row);
|
||||||
row_length = len(list[row]);
|
row_length = len(list[row]);
|
||||||
for(column = [0:len(list[row])-1]) {
|
|
||||||
|
for(column = column_override ? column_override : [0:len(list[row])-1]) {
|
||||||
row_sculpting = (row_override != undef ? row_override : row) + row_sculpting_offset;
|
row_sculpting = (row_override != undef ? row_override : row) + row_sculpting_offset;
|
||||||
key_length = list[row][column];
|
key_length = list[row][column];
|
||||||
|
column_value = double_sculpted_column(column, row_length, column_sculpt_profile);
|
||||||
// check if column is smack in middle of row - if so, no sculpting
|
|
||||||
// otherwise try to make two half-moon shapes, one for each side of the board
|
|
||||||
// since we are zero indexed, the 7th row has an index of 6 and is the center of 13. 6*2+1 = 13
|
|
||||||
double_sculpted_column = (column*2 + 1 == row_length) ? 0 : (column % (row_length/2)) - (row_length/4);
|
|
||||||
|
|
||||||
echo("double_sculpted_column", double_sculpted_column);
|
|
||||||
column_distance = abs_sum([for (x = [0 : column]) list[row][x]]);
|
column_distance = abs_sum([for (x = [0 : column]) list[row][x]]);
|
||||||
|
|
||||||
|
echo("\t**COLUMN**", "column_value", column_value, "column_distance", column_distance);
|
||||||
|
|
||||||
|
// supports negative values for nonexistent keys
|
||||||
if (key_length >= 1) {
|
if (key_length >= 1) {
|
||||||
translate_u(column_distance - (key_length/2), -row) key_profile(profile, row_sculpting, double_sculpted_column) u(key_length) cherry() { // (row+4) % 5 + 1
|
translate_u(column_distance - (key_length/2), -row) {
|
||||||
|
key_profile(profile, row_sculpting, column_value) u(key_length) legend(legends ? legends[row][column] : "") cherry() { // (row+4) % 5 + 1
|
||||||
if (key_length == 6.25) {
|
if (key_length == 6.25) {
|
||||||
spacebar() key();
|
spacebar() key();
|
||||||
} else if (key_length == 2.25) {
|
} else if (key_length == 2.25) {
|
||||||
@ -38,4 +52,5 @@ module layout(list, profile="dcs", row_sculpting_offset=0, row_override=undef) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,6 @@ preonic_layout_mapping = [
|
|||||||
[1, 1, 1, 1, 1, 0, 2, 0, 1, 1, 1, 1, 1]
|
[1, 1, 1, 1, 1, 0, 2, 0, 1, 1, 1, 1, 1]
|
||||||
];
|
];
|
||||||
|
|
||||||
module preonic_default(profile) {
|
module preonic_default(profile, column_sculpt_profile="2hands") {
|
||||||
layout(preonic_layout_mapping, profile);
|
layout(preonic_layout_mapping, profile, column_sculpt_profile=column_sculpt_profile);
|
||||||
}
|
}
|
||||||
|
17
src/libraries/rounded_rectangle_profile.scad
Normal file
17
src/libraries/rounded_rectangle_profile.scad
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
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 sign_x(i,n) =
|
||||||
|
i < n/4 || i > n-n/4 ? 1 :
|
||||||
|
i > n/4 && i < n-n/4 ? -1 :
|
||||||
|
0;
|
||||||
|
|
||||||
|
function sign_y(i,n) =
|
||||||
|
i > 0 && i < n/2 ? 1 :
|
||||||
|
i > n/2 ? -1 :
|
||||||
|
0;
|
@ -17,6 +17,9 @@ $inverted_dish = false;
|
|||||||
|
|
||||||
// set this to true if you are making double sculpted keycaps
|
// set this to true if you are making double sculpted keycaps
|
||||||
$double_sculpted = false;
|
$double_sculpted = false;
|
||||||
|
// change aggressiveness of double sculpting
|
||||||
|
// this is the radius of the cylinder the keytops are placed on
|
||||||
|
$double_sculpt_radius = 200;
|
||||||
|
|
||||||
|
|
||||||
// Support type. default is "flared" for easy FDM printing; bars are more realistic, and flat could be for artisans
|
// Support type. default is "flared" for easy FDM printing; bars are more realistic, and flat could be for artisans
|
||||||
@ -25,6 +28,10 @@ $support_type = "flared"; // [flared, bars, flat, disable]
|
|||||||
// Supports for the stem, as it often comes off during printing. Reccommended for most machines
|
// Supports for the stem, as it often comes off during printing. Reccommended for most machines
|
||||||
$stem_support_type = "tines"; // [tines, brim, disabled]
|
$stem_support_type = "tines"; // [tines, brim, disabled]
|
||||||
|
|
||||||
|
// enable to have stem support extend past the keycap bottom, to (hopefully) the next
|
||||||
|
// keycap. only works on tines right now
|
||||||
|
$extra_long_stem_support = false;
|
||||||
|
|
||||||
/* [Advanced] */
|
/* [Advanced] */
|
||||||
|
|
||||||
/* Key */
|
/* Key */
|
||||||
@ -110,6 +117,10 @@ $clearance_check = false;
|
|||||||
// Use linear_extrude instead of hull slices to make the shape of the key
|
// Use linear_extrude instead of hull slices to make the shape of the key
|
||||||
// Should be faster, also required for concave shapes
|
// Should be faster, also required for concave shapes
|
||||||
$linear_extrude_shape = false;
|
$linear_extrude_shape = false;
|
||||||
|
|
||||||
|
// brand new, more correct, hopefully faster, lots more work
|
||||||
|
// warns in trajectory.scad but it looks benign
|
||||||
|
$skin_extrude_shape = false;
|
||||||
//should the key be rounded? unnecessary for most printers, and very slow
|
//should the key be rounded? unnecessary for most printers, and very slow
|
||||||
$rounded_key = false;
|
$rounded_key = false;
|
||||||
//minkowski radius. radius of sphere used in minkowski sum for minkowski_key function. 1.75 for G20
|
//minkowski radius. radius of sphere used in minkowski sum for minkowski_key function. 1.75 for G20
|
||||||
@ -132,6 +143,10 @@ $key_bump_edge = 0.4;
|
|||||||
// Currently does not work with thingiverse customizer, and actually breaks it
|
// Currently does not work with thingiverse customizer, and actually breaks it
|
||||||
$legends = [];
|
$legends = [];
|
||||||
|
|
||||||
|
// make legends outset instead of inset.
|
||||||
|
// broken off from artisan support since who wants outset legends?
|
||||||
|
$outset_legends = false;
|
||||||
|
|
||||||
// Dimensions of alps stem
|
// Dimensions of alps stem
|
||||||
$alps_stem = [4.45, 2.25];
|
$alps_stem = [4.45, 2.25];
|
||||||
|
|
||||||
|
@ -7,6 +7,10 @@ include <shapes/rounded_square.scad>
|
|||||||
include <shapes/square.scad>
|
include <shapes/square.scad>
|
||||||
include <shapes/oblong.scad>
|
include <shapes/oblong.scad>
|
||||||
|
|
||||||
|
// size: at progress 0, the shape is supposed to be this size
|
||||||
|
// delta: at progress 1, the keycap is supposed to be size - delta
|
||||||
|
// progress: how far along the transition you are.
|
||||||
|
// it's not always linear - specifically sculpted_square
|
||||||
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);
|
||||||
@ -22,3 +26,10 @@ module key_shape(size, delta, progress = 0) {
|
|||||||
echo("Warning: unsupported $key_shape_type");
|
echo("Warning: unsupported $key_shape_type");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function skin_key_shape(size, delta, progress = 0) =
|
||||||
|
$key_shape_type == "rounded_square" ?
|
||||||
|
skin_rounded_square(size, delta, progress) :
|
||||||
|
$key_shape_type == "sculpted_square" ?
|
||||||
|
skin_sculpted_square_shape(size, delta, progress) :
|
||||||
|
echo("Warning: unsupported $key_shape_type for skin shape. disable skin_extrude_shape or pick a new shape");
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
include <../libraries/rounded_rectangle_profile.scad>
|
||||||
|
|
||||||
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];
|
||||||
@ -20,3 +22,8 @@ module rounded_square_shape(size, delta, progress, center = true) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// for skin
|
||||||
|
|
||||||
|
function skin_rounded_square(size, delta, progress) =
|
||||||
|
rounded_rectangle_profile(size - (delta * progress), fn=36, r=$corner_radius);
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
include <../libraries/rounded_rectangle_profile.scad>
|
||||||
|
|
||||||
// rounded square shape with additional sculpting functions to better approximate
|
// rounded square shape with additional sculpting functions to better approximate
|
||||||
|
|
||||||
// When sculpting sides, how much in should the tops come
|
// When sculpting sides, how much in should the tops come
|
||||||
@ -42,6 +44,38 @@ module sculpted_square_shape(size, delta, progress) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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) =
|
||||||
|
let(
|
||||||
|
width = size[0],
|
||||||
|
height = size[1],
|
||||||
|
|
||||||
|
width_difference = delta[0],
|
||||||
|
height_difference = delta[1],
|
||||||
|
// makes the sides bow
|
||||||
|
extra_side_size = side_sculpting(progress),
|
||||||
|
// makes the rounded corners of the keycap grow larger as they move upwards
|
||||||
|
extra_corner_size = corner_sculpting(progress),
|
||||||
|
|
||||||
|
// computed values for this slice
|
||||||
|
extra_width_this_slice = (width_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),
|
||||||
|
|
||||||
|
square_size = [
|
||||||
|
width - extra_width_this_slice,
|
||||||
|
height - extra_height_this_slice
|
||||||
|
]
|
||||||
|
) 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) {
|
module side_rounded_square(size, r) {
|
||||||
iw = size.x - 2 * r;
|
iw = size.x - 2 * r;
|
||||||
ih = size.y - 2 * r;
|
ih = size.y - 2 * r;
|
||||||
|
@ -1,28 +1,64 @@
|
|||||||
include <../functions.scad>
|
include <../functions.scad>
|
||||||
include <../stems/cherry.scad>
|
include <../stems/cherry.scad>
|
||||||
|
|
||||||
// $wall_thickness/4 to reduce coincident faces
|
|
||||||
module centered_tines(stem_support_height) {
|
module centered_tines(stem_support_height) {
|
||||||
if ($key_length < 2) translate([0,0,$stem_support_height / 2]) cube([total_key_width($wall_thickness)+$wall_thickness/4, 1, $stem_support_height], center = true);
|
if ($key_length < 2) {
|
||||||
translate([0,0,$stem_support_height / 2]) cube([1, total_key_height($wall_thickness), $stem_support_height], center = true);
|
translate([0,0,$stem_support_height / 2]) {
|
||||||
|
cube([total_key_width(), 0.5, $stem_support_height], center = true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
translate([0,0,$stem_support_height / 2]) {
|
||||||
|
cube([
|
||||||
|
1,
|
||||||
|
total_key_height(),
|
||||||
|
$stem_support_height
|
||||||
|
],
|
||||||
|
center = true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module tines_support(stem_type, stem_support_height, slop) {
|
module tines_support(stem_type, stem_support_height, slop) {
|
||||||
|
extra_height = $extra_long_stem_support ? ($unit - total_key_height()) + 0.1 : 0; // fudge
|
||||||
|
extra_width = $extra_long_stem_support ? ($unit - total_key_width()) + 0.1 : 0;
|
||||||
|
|
||||||
if (stem_type == "cherry" || stem_type == "costar_stabilizer") {
|
if (stem_type == "cherry" || stem_type == "costar_stabilizer") {
|
||||||
difference () {
|
difference () {
|
||||||
union() {
|
union() {
|
||||||
if ($key_length < 2) translate([0,0,$stem_support_height / 2]) cube([total_key_width($wall_thickness)+$wall_thickness/4, 1, $stem_support_height], center = true);
|
if ($key_length < 2) {
|
||||||
translate([1.15,0,$stem_support_height / 2]) cube([.5, total_key_height($wall_thickness), $stem_support_height], center = true);
|
translate([0,0,$stem_support_height / 2]) {
|
||||||
translate([-1.15,0,$stem_support_height / 2]) cube([.5, total_key_height($wall_thickness), $stem_support_height], center = true);
|
cube([
|
||||||
|
total_key_width() + extra_width*2,
|
||||||
|
0.5,
|
||||||
|
$stem_support_height
|
||||||
|
], center = true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2 vertical tines holding either side of the cruciform
|
||||||
|
for (x = [1.15, -1.15]) {
|
||||||
|
translate([x,0,$stem_support_height / 2]) {
|
||||||
|
cube([
|
||||||
|
0.5,
|
||||||
|
total_key_height() + extra_height*2, // this is to extend past
|
||||||
|
$stem_support_height
|
||||||
|
], center = true);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inside_cherry_cross(slop);
|
inside_cherry_cross(slop);
|
||||||
}
|
}
|
||||||
} else if (stem_type == "cherry_stabilizer") {
|
} else if (stem_type == "cherry_stabilizer") {
|
||||||
difference () {
|
difference () {
|
||||||
union() {
|
for (x = [1.15, -1.15]) {
|
||||||
translate([1.15,0,$stem_support_height / 2]) cube([1, total_key_height($wall_thickness), $stem_support_height], center = true);
|
translate([x,0,$stem_support_height / 2]) {
|
||||||
translate([-1.15,0,$stem_support_height / 2]) cube([1, total_key_height($wall_thickness), $stem_support_height], center = true);
|
cube([
|
||||||
|
1,
|
||||||
|
total_key_height($wall_thickness),
|
||||||
|
$stem_support_height
|
||||||
|
], center = true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inside_cherry_stabilizer_cross(slop);
|
inside_cherry_stabilizer_cross(slop);
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
include <../functions.scad>
|
include <../functions.scad>
|
||||||
|
// TODO this define doesn't do anything besides tell me I used flat() in this file
|
||||||
|
// is it better than not having it at all?
|
||||||
|
include <./flat.scad>
|
||||||
|
|
||||||
// figures out the scale factor needed to make a 45 degree wall
|
// figures out the scale factor needed to make a 45 degree wall
|
||||||
function scale_for_45(height, starting_size) = (height * 2 + starting_size) / starting_size;
|
function scale_for_45(height, starting_size) = (height * 2 + starting_size) / starting_size;
|
||||||
@ -6,6 +9,11 @@ function scale_for_45(height, starting_size) = (height * 2 + starting_size) / st
|
|||||||
// complicated since we want the different stems to work well
|
// complicated since we want the different stems to work well
|
||||||
// also kind of messy... oh well
|
// also kind of messy... oh well
|
||||||
module flared(stem_type, loft, height) {
|
module flared(stem_type, loft, height) {
|
||||||
|
// flat support. straight flat support has a tendency to shear off; flared support
|
||||||
|
// all the way to the top has a tendency to warp the outside of the keycap.
|
||||||
|
// hopefully the compromise is both
|
||||||
|
flat(stem_type, loft + height/4, height);
|
||||||
|
|
||||||
translate([0,0,loft]){
|
translate([0,0,loft]){
|
||||||
if (stem_type == "rounded_cherry") {
|
if (stem_type == "rounded_cherry") {
|
||||||
linear_extrude(height=height, scale = scale_for_45(height, $rounded_cherry_stem_d)){
|
linear_extrude(height=height, scale = scale_for_45(height, $rounded_cherry_stem_d)){
|
||||||
|
Loading…
Reference in New Issue
Block a user