some ISO enter updates
This commit is contained in:
parent
dcc13b7d35
commit
5f60c1889b
117
key.scad
117
key.scad
|
@ -135,6 +135,23 @@ module rounded_shape() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//corollary is shape_hull
|
||||||
|
module ISOEnterShapeHull(thickness_difference, depth_difference, modifier){
|
||||||
|
|
||||||
|
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,19.05 * 0.5,0])
|
||||||
|
linear_extrude(height = height, scale = [width_scale, height_scale]) {
|
||||||
|
// TODO completely making up these numbers here
|
||||||
|
// 0.86mm is from the unit function, 18.16 - 19.02. no idea what the 18 is, shows me for not leaving better comments
|
||||||
|
translate([0,-19.05 * 0.5,0])
|
||||||
|
fakeISOEnter(thickness_difference);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// basic key shape, no dish, no inside
|
// basic key shape, no dish, no inside
|
||||||
// modifier multiplies the height and top differences of the shape,
|
// modifier multiplies the height and top differences of the shape,
|
||||||
// which is only used for dishing to cut the dish off correctly
|
// which is only used for dishing to cut the dish off correctly
|
||||||
|
@ -158,18 +175,25 @@ module shape_hull(thickness_difference, depth_difference, modifier, extra_slices
|
||||||
|
|
||||||
module shape_slice(index, total, thickness_difference, depth_difference, modifier) {
|
module shape_slice(index, total, thickness_difference, depth_difference, modifier) {
|
||||||
progress = index / (total);
|
progress = index / (total);
|
||||||
|
|
||||||
// TODO extract these out somehow so you can make custom rounded sides
|
// TODO extract these out somehow so you can make custom rounded sides
|
||||||
|
// makes the sides bow
|
||||||
extra_side_size = $enable_side_sculpting ? (total - index)/4 : 0;
|
extra_side_size = $enable_side_sculpting ? (total - index)/4 : 0;
|
||||||
|
// makes the rounded corners of the keycap grow larger as they move upwards
|
||||||
extra_corner_size = $enable_side_sculpting ? pow(progress, 2) : 0;
|
extra_corner_size = $enable_side_sculpting ? pow(progress, 2) : 0;
|
||||||
|
|
||||||
|
// width and height differences for this slice
|
||||||
|
extra_width_difference = ($width_difference - extra_side_size) * progress * modifier;
|
||||||
|
extra_height_difference = ($height_difference - extra_side_size) * progress * modifier;
|
||||||
|
|
||||||
translate([
|
translate([
|
||||||
0,
|
0,
|
||||||
$top_skew * progress,
|
$top_skew * progress,
|
||||||
($total_depth * modifier - depth_difference) * progress
|
($total_depth * modifier - depth_difference) * progress
|
||||||
]) rotate([-$top_tilt / $key_height * progress,0,0]){
|
]) rotate([-$top_tilt / $key_height * progress,0,0]){
|
||||||
roundedRect([
|
roundedRect([
|
||||||
total_key_width() - thickness_difference - (($width_difference - extra_side_size) * progress * modifier),
|
total_key_width() - thickness_difference - extra_width_difference,
|
||||||
total_key_height() - thickness_difference - (($height_difference - extra_side_size) * progress * modifier),
|
total_key_height() - thickness_difference - extra_height_difference,
|
||||||
.001
|
.001
|
||||||
],$corner_radius + extra_corner_size);
|
],$corner_radius + extra_corner_size);
|
||||||
}
|
}
|
||||||
|
@ -311,92 +335,3 @@ module example_key(){
|
||||||
}
|
}
|
||||||
|
|
||||||
example_key();
|
example_key();
|
||||||
//minkowski_key();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Experimental stuff, except not really anymore
|
|
||||||
|
|
||||||
// corollary is roundedRect
|
|
||||||
// NOT 3D
|
|
||||||
module fakeISOEnter(thickness_difference = 0){
|
|
||||||
// 1u is the space taken upy by a 1u keycap.
|
|
||||||
// unit is the space taken up by a unit space for a keycap.
|
|
||||||
// formula is 1u + unit *(length - 1)
|
|
||||||
|
|
||||||
// t is all modifications to the polygon array
|
|
||||||
t = $corner_radius + thickness_difference/2;
|
|
||||||
|
|
||||||
function unit(length) = 19.02 * (length) + (18.16 - 19.02);
|
|
||||||
|
|
||||||
pointArray = [
|
|
||||||
[ 0 + t, 0 + t],
|
|
||||||
[unit(1.5) - t, 0 + t],
|
|
||||||
[unit(1.5) - t, unit(1) - t],
|
|
||||||
[unit(1.25) - t, unit(1) - t],
|
|
||||||
[unit(1.25) - t, unit(2) - t],
|
|
||||||
[ 0 + t, unit(2) - t]
|
|
||||||
];
|
|
||||||
|
|
||||||
offset(r=$corner_radius) {
|
|
||||||
polygon(points=pointArray, center=true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//corollary is shape_hull
|
|
||||||
module ISOEnterShapeHull(thickness_difference, depth_difference, modifier){
|
|
||||||
// TODO move this somewhere
|
|
||||||
function unit(length) = 19.02 * (length) + (18.16 - 19.02);
|
|
||||||
|
|
||||||
height = $total_depth - depth_difference;
|
|
||||||
width_scale = top_total_key_width() / total_key_width();
|
|
||||||
height_scale = top_total_key_height() / total_key_height();
|
|
||||||
|
|
||||||
linear_extrude(height = height, scale = [width_scale, height_scale]) {
|
|
||||||
|
|
||||||
// TODO completely making up these numbers here
|
|
||||||
// 0.86mm is from the unit function, 18.16 - 19.02. no idea what the 18 is, shows me for not leaving better comments
|
|
||||||
translate([unit(-.5), unit(-1) + 0.86]) fakeISOEnter(thickness_difference);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// old stuff
|
|
||||||
|
|
||||||
// old non-sliced shape hull
|
|
||||||
|
|
||||||
/*module oldshape_hull(thickness_difference, depth_difference, modifier){
|
|
||||||
if ($ISOEnter) {
|
|
||||||
ISOEnterShapeHull(thickness_difference, depth_difference, modifier);
|
|
||||||
} else {
|
|
||||||
hull(){
|
|
||||||
// $bottom_key_width + ($key_length -1) * unit is the correct length of the
|
|
||||||
// key. only 1u of the key should be $bottom_key_width long; all others
|
|
||||||
// should be 1u
|
|
||||||
roundedRect([total_key_width() - thickness_difference, total_key_height() - thickness_difference, .001],$corner_radius);
|
|
||||||
|
|
||||||
//depth_difference outside of modifier because that doesnt make sense
|
|
||||||
translate([0,$top_skew,$total_depth * modifier - depth_difference]){
|
|
||||||
rotate([-$top_tilt / $key_height,0,0]){
|
|
||||||
roundedRect([
|
|
||||||
total_key_width() - thickness_difference - $width_difference * modifier,
|
|
||||||
total_key_height() - thickness_difference - $height_difference * modifier,
|
|
||||||
.001
|
|
||||||
],$corner_radius);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
|
@ -418,4 +418,5 @@ module legend(text, inset=false) {
|
||||||
children();
|
children();
|
||||||
}
|
}
|
||||||
|
|
||||||
translate_u(0, 0) oem_row(1) cherry() key();
|
translate_u(1.125, 0.5) fake_iso_enter() cherry() key();
|
||||||
|
translate_u(0, 0) dcs_row(2) cherry() key();
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2014 Oskar Linde
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -0,0 +1,81 @@
|
||||||
|
scad-utils
|
||||||
|
==========
|
||||||
|
|
||||||
|
Utility libraries for OpenSCAD
|
||||||
|
|
||||||
|
Morphology
|
||||||
|
----------
|
||||||
|
|
||||||
|
contains basic 2D morphology operations
|
||||||
|
|
||||||
|
inset(d=1) - creates a polygon at an offset d inside a 2D shape
|
||||||
|
outset(d=1) - creates a polygon at an offset d outside a 2D shape
|
||||||
|
fillet(r=1) - adds fillets of radius r to all concave corners of a 2D shape
|
||||||
|
rounding(r=1) - adds rounding to all convex corners of a 2D shape
|
||||||
|
shell(d,center=false) - makes a shell of width d along the edge of a 2D shape
|
||||||
|
- positive values of d places the shell on the outside
|
||||||
|
- negative values of d places the shell on the inside
|
||||||
|
- center=true and positive d places the shell centered on the edge
|
||||||
|
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
With a basic sample polygon shape,
|
||||||
|
|
||||||
|
module shape() {
|
||||||
|
polygon([[0,0],[1,0],[1.5,1],[2.5,1],[2,-1],[0,-1]]);
|
||||||
|
}
|
||||||
|
|
||||||
|
and `$fn=32;`.
|
||||||
|
|
||||||
|
|
||||||
|
* `inset(d=0.3) shape();`
|
||||||
|
|
||||||
|
![](http://oskarlinde.github.io/scad-utils/img/morph-0.png)
|
||||||
|
|
||||||
|
|
||||||
|
* `outset(d=0.3) shape();`
|
||||||
|
|
||||||
|
![](http://oskarlinde.github.io/scad-utils/img/morph-1.png)
|
||||||
|
|
||||||
|
|
||||||
|
* `rounding(r=0.3) shape();`
|
||||||
|
|
||||||
|
![](http://oskarlinde.github.io/scad-utils/img/morph-2.png)
|
||||||
|
|
||||||
|
|
||||||
|
* `fillet(r=0.3) shape();`
|
||||||
|
|
||||||
|
![](http://oskarlinde.github.io/scad-utils/img/morph-3.png)
|
||||||
|
|
||||||
|
|
||||||
|
*`shell(d=0.3) shape();`
|
||||||
|
|
||||||
|
![](http://oskarlinde.github.io/scad-utils/img/morph-4.png)
|
||||||
|
|
||||||
|
|
||||||
|
*`shell(d=-0.3) shape();`
|
||||||
|
|
||||||
|
![](http://oskarlinde.github.io/scad-utils/img/morph-5.png)
|
||||||
|
|
||||||
|
|
||||||
|
*`shell(d=0.3,center=true) shape();`
|
||||||
|
|
||||||
|
![](http://oskarlinde.github.io/scad-utils/img/morph-6.png)
|
||||||
|
|
||||||
|
|
||||||
|
Mirror
|
||||||
|
------
|
||||||
|
|
||||||
|
contains simple mirroring functions
|
||||||
|
|
||||||
|
mirror_x()
|
||||||
|
mirror_y()
|
||||||
|
mirror_z()
|
||||||
|
|
||||||
|
example:
|
||||||
|
|
||||||
|
module arrow(l=1,w=.6,t=0.15) {
|
||||||
|
mirror_y() polygon([[0,0],[l,0],[l-w/2,w/2],[l-w/2-sqrt(2)*t,w/2],[l-t/2-sqrt(2)*t,t/2],[0,t/2]]);
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,324 @@
|
||||||
|
|
||||||
|
// NOTE: this code uses
|
||||||
|
// * experimental let() syntax
|
||||||
|
// * experimental list comprehension syntax
|
||||||
|
// * search() bugfix and feature addition
|
||||||
|
// * vector min()/max()
|
||||||
|
|
||||||
|
// Calculates the convex hull of a set of points.
|
||||||
|
// The result is expressed in point indices.
|
||||||
|
// If the points are collinear (or 2d), the result is a convex
|
||||||
|
// polygon [i1,i2,i3,...], otherwise a triangular
|
||||||
|
// polyhedron [[i1,i2,i3],[i2,i3,i4],...]
|
||||||
|
|
||||||
|
function hull(points) =
|
||||||
|
!(len(points) > 0) ? [] :
|
||||||
|
len(points[0]) == 2 ? convexhull2d(points) :
|
||||||
|
len(points[0]) == 3 ? convexhull3d(points) : [];
|
||||||
|
|
||||||
|
epsilon = 1e-9;
|
||||||
|
|
||||||
|
// 2d version
|
||||||
|
function convexhull2d(points) =
|
||||||
|
len(points) < 3 ? [] : let(
|
||||||
|
a=0, b=1,
|
||||||
|
|
||||||
|
c = find_first_noncollinear([a,b], points, 2)
|
||||||
|
|
||||||
|
) c == len(points) ? convexhull_collinear(points) : let(
|
||||||
|
|
||||||
|
remaining = [ for (i = [2:len(points)-1]) if (i != c) i ],
|
||||||
|
|
||||||
|
polygon = area_2d(points[a], points[b], points[c]) > 0 ? [a,b,c] : [b,a,c]
|
||||||
|
|
||||||
|
) convex_hull_iterative_2d(points, polygon, remaining);
|
||||||
|
|
||||||
|
|
||||||
|
// Adds the remaining points one by one to the convex hull
|
||||||
|
function convex_hull_iterative_2d(points, polygon, remaining, i_=0) = i_ >= len(remaining) ? polygon :
|
||||||
|
let (
|
||||||
|
// pick a point
|
||||||
|
i = remaining[i_],
|
||||||
|
|
||||||
|
// find the segments that are in conflict with the point (point not inside)
|
||||||
|
conflicts = find_conflicting_segments(points, polygon, points[i])
|
||||||
|
|
||||||
|
// no conflicts, skip point and move on
|
||||||
|
) len(conflicts) == 0 ? convex_hull_iterative_2d(points, polygon, remaining, i_+1) : let(
|
||||||
|
|
||||||
|
// find the first conflicting segment and the first not conflicting
|
||||||
|
// conflict will be sorted, if not wrapping around, do it the easy way
|
||||||
|
polygon = remove_conflicts_and_insert_point(polygon, conflicts, i)
|
||||||
|
) convex_hull_iterative_2d(
|
||||||
|
points,
|
||||||
|
polygon,
|
||||||
|
remaining,
|
||||||
|
i_+1
|
||||||
|
);
|
||||||
|
|
||||||
|
function find_conflicting_segments(points, polygon, point) = [
|
||||||
|
for (i = [0:len(polygon)-1]) let(j = (i+1) % len(polygon))
|
||||||
|
if (area_2d(points[polygon[i]], points[polygon[j]], point) < 0)
|
||||||
|
i
|
||||||
|
];
|
||||||
|
|
||||||
|
// remove the conflicting segments from the polygon
|
||||||
|
function remove_conflicts_and_insert_point(polygon, conflicts, point) =
|
||||||
|
conflicts[0] == 0 ? let(
|
||||||
|
nonconflicting = [ for(i = [0:len(polygon)-1]) if (!contains(conflicts, i)) i ],
|
||||||
|
new_indices = concat(nonconflicting, (nonconflicting[len(nonconflicting)-1]+1) % len(polygon)),
|
||||||
|
polygon = concat([ for (i = new_indices) polygon[i] ], point)
|
||||||
|
) polygon : let(
|
||||||
|
prior_to_first_conflict = [ for(i = [0:1:min(conflicts)]) polygon[i] ],
|
||||||
|
after_last_conflict = [ for(i = [max(conflicts)+1:1:len(polygon)-1]) polygon[i] ],
|
||||||
|
polygon = concat(prior_to_first_conflict, point, after_last_conflict)
|
||||||
|
) polygon;
|
||||||
|
|
||||||
|
|
||||||
|
// 3d version
|
||||||
|
function convexhull3d(points) =
|
||||||
|
len(points) < 3 ? [ for(i = [0:1:len(points)-1]) i ] : let (
|
||||||
|
|
||||||
|
// start with a single triangle
|
||||||
|
a=0, b=1, c=2,
|
||||||
|
plane = plane(points,a,b,c),
|
||||||
|
|
||||||
|
d = find_first_noncoplanar(plane, points, 3)
|
||||||
|
|
||||||
|
) d == len(points) ? /* all coplanar*/ let (
|
||||||
|
|
||||||
|
pts2d = [ for (p = points) plane_project(p, points[a], points[b], points[c]) ],
|
||||||
|
hull2d = convexhull2d(pts2d)
|
||||||
|
|
||||||
|
) hull2d : let(
|
||||||
|
|
||||||
|
remaining = [for (i = [3:len(points)-1]) if (i != d) i],
|
||||||
|
|
||||||
|
// Build an initial tetrahedron
|
||||||
|
|
||||||
|
// swap b,c if d is in front of triangle t
|
||||||
|
bc = in_front(plane, points[d]) ? [c,b] : [b,c],
|
||||||
|
b = bc[0], c = bc[1],
|
||||||
|
|
||||||
|
triangles = [
|
||||||
|
[a,b,c],
|
||||||
|
[d,b,a],
|
||||||
|
[c,d,a],
|
||||||
|
[b,d,c],
|
||||||
|
],
|
||||||
|
|
||||||
|
// calculate the plane equations
|
||||||
|
planes = [ for (t = triangles) plane(points, t[0], t[1], t[2]) ]
|
||||||
|
|
||||||
|
) convex_hull_iterative(points, triangles, planes, remaining);
|
||||||
|
|
||||||
|
// A plane equation (normal, offset)
|
||||||
|
function plane(points, a, b, c) = let(
|
||||||
|
normal = unit(cross(points[c]-points[a], points[b]-points[a]))
|
||||||
|
) [
|
||||||
|
normal,
|
||||||
|
normal * points[a]
|
||||||
|
];
|
||||||
|
|
||||||
|
// Adds the remaining points one by one to the convex hull
|
||||||
|
function convex_hull_iterative(points, triangles, planes, remaining, i_=0) = i_ >= len(remaining) ? triangles :
|
||||||
|
let (
|
||||||
|
// pick a point
|
||||||
|
i = remaining[i_],
|
||||||
|
|
||||||
|
// find the triangles that are in conflict with the point (point not inside)
|
||||||
|
conflicts = find_conflicts(points[i], planes),
|
||||||
|
|
||||||
|
// for all triangles that are in conflict, collect their halfedges
|
||||||
|
halfedges = [
|
||||||
|
for(c = conflicts)
|
||||||
|
for(i = [0:2]) let(j = (i+1)%3)
|
||||||
|
[triangles[c][i], triangles[c][j]]
|
||||||
|
],
|
||||||
|
|
||||||
|
// find the outer perimeter of the set of conflicting triangles
|
||||||
|
horizon = remove_internal_edges(halfedges),
|
||||||
|
|
||||||
|
// generate a new triangle for each horizon halfedge together with the picked point i
|
||||||
|
new_triangles = [ for (h = horizon) concat(h,i) ],
|
||||||
|
|
||||||
|
// calculate the corresponding plane equations
|
||||||
|
new_planes = [ for (t = new_triangles) plane(points, t[0], t[1], t[2]) ]
|
||||||
|
|
||||||
|
) convex_hull_iterative(
|
||||||
|
points,
|
||||||
|
// remove the conflicting triangles and add the new ones
|
||||||
|
concat(remove_elements(triangles, conflicts), new_triangles),
|
||||||
|
concat(remove_elements(planes, conflicts), new_planes),
|
||||||
|
remaining,
|
||||||
|
i_+1
|
||||||
|
);
|
||||||
|
|
||||||
|
function convexhull_collinear(points) = let(
|
||||||
|
n = points[1] - points[0],
|
||||||
|
a = points[0],
|
||||||
|
points1d = [ for(p = points) (p-a)*n ],
|
||||||
|
min_i = min_index(points1d),
|
||||||
|
max_i = max_index(points1d)
|
||||||
|
) [ min_i, max_i ];
|
||||||
|
|
||||||
|
function min_index(values,min_,min_i_,i_) =
|
||||||
|
i_ == undef ? min_index(values,values[0],0,1) :
|
||||||
|
i_ >= len(values) ? min_i_ :
|
||||||
|
values[i_] < min_ ? min_index(values,values[i_],i_,i_+1)
|
||||||
|
: min_index(values,min_,min_i_,i_+1);
|
||||||
|
|
||||||
|
function max_index(values,max_,max_i_,i_) =
|
||||||
|
i_ == undef ? max_index(values,values[0],0,1) :
|
||||||
|
i_ >= len(values) ? max_i_ :
|
||||||
|
values[i_] > max_ ? max_index(values,values[i_],i_,i_+1)
|
||||||
|
: max_index(values,max_,max_i_,i_+1);
|
||||||
|
|
||||||
|
function remove_elements(array, elements) = [
|
||||||
|
for (i = [0:len(array)-1])
|
||||||
|
if (!search(i, elements))
|
||||||
|
array[i]
|
||||||
|
];
|
||||||
|
|
||||||
|
function remove_internal_edges(halfedges) = [
|
||||||
|
for (h = halfedges)
|
||||||
|
if (!contains(halfedges, reverse(h)))
|
||||||
|
h
|
||||||
|
];
|
||||||
|
|
||||||
|
function plane_project(point, a, b, c) = let(
|
||||||
|
u = b-a,
|
||||||
|
v = c-a,
|
||||||
|
n = cross(u,v),
|
||||||
|
w = cross(n,u),
|
||||||
|
relpoint = point-a
|
||||||
|
) [relpoint * u, relpoint * w];
|
||||||
|
|
||||||
|
function plane_unproject(point, a, b, c) = let(
|
||||||
|
u = b-a,
|
||||||
|
v = c-a,
|
||||||
|
n = cross(u,v),
|
||||||
|
w = cross(n,u)
|
||||||
|
) a + point[0] * u + point[1] * w;
|
||||||
|
|
||||||
|
function reverse(arr) = [ for (i = [len(arr)-1:-1:0]) arr[i] ];
|
||||||
|
|
||||||
|
function contains(arr, element) = search([element],arr)[0] != [] ? true : false;
|
||||||
|
|
||||||
|
function find_conflicts(point, planes) = [
|
||||||
|
for (i = [0:len(planes)-1])
|
||||||
|
if (in_front(planes[i], point))
|
||||||
|
i
|
||||||
|
];
|
||||||
|
|
||||||
|
function find_first_noncollinear(line, points, i) =
|
||||||
|
i >= len(points) ? len(points) :
|
||||||
|
collinear(points[line[0]],
|
||||||
|
points[line[1]],
|
||||||
|
points[i]) ? find_first_noncollinear(line, points, i+1)
|
||||||
|
: i;
|
||||||
|
|
||||||
|
function find_first_noncoplanar(plane, points, i) =
|
||||||
|
i >= len(points) ? len(points) :
|
||||||
|
coplanar(plane, points[i]) ? find_first_noncoplanar(plane, points, i+1)
|
||||||
|
: i;
|
||||||
|
|
||||||
|
function distance(plane, point) = plane[0] * point - plane[1];
|
||||||
|
|
||||||
|
function in_front(plane, point) = distance(plane, point) > epsilon;
|
||||||
|
|
||||||
|
function coplanar(plane, point) = abs(distance(plane,point)) <= epsilon;
|
||||||
|
|
||||||
|
function unit(v) = v/norm(v);
|
||||||
|
|
||||||
|
function area_2d(a,b,c) = (
|
||||||
|
a[0] * (b[1] - c[1]) +
|
||||||
|
b[0] * (c[1] - a[1]) +
|
||||||
|
c[0] * (a[1] - b[1])) / 2;
|
||||||
|
|
||||||
|
function collinear(a,b,c) = abs(area_2d(a,b,c)) < epsilon;
|
||||||
|
|
||||||
|
function spherical(cartesian) = [
|
||||||
|
atan2(cartesian[1], cartesian[0]),
|
||||||
|
asin(cartesian[2])
|
||||||
|
];
|
||||||
|
|
||||||
|
function cartesian(spherical) = [
|
||||||
|
cos(spherical[1]) * cos(spherical[0]),
|
||||||
|
cos(spherical[1]) * sin(spherical[0]),
|
||||||
|
sin(spherical[1])
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
/// TESTCODE
|
||||||
|
|
||||||
|
|
||||||
|
phi = 1.618033988749895;
|
||||||
|
|
||||||
|
testpoints_on_sphere = [ for(p =
|
||||||
|
[
|
||||||
|
[1,phi,0], [-1,phi,0], [1,-phi,0], [-1,-phi,0],
|
||||||
|
[0,1,phi], [0,-1,phi], [0,1,-phi], [0,-1,-phi],
|
||||||
|
[phi,0,1], [-phi,0,1], [phi,0,-1], [-phi,0,-1]
|
||||||
|
])
|
||||||
|
unit(p)
|
||||||
|
];
|
||||||
|
|
||||||
|
testpoints_spherical = [ for(p = testpoints_on_sphere) spherical(p) ];
|
||||||
|
testpoints_circular = [ for(a = [0:15:360-epsilon]) [cos(a),sin(a)] ];
|
||||||
|
|
||||||
|
testpoints_coplanar = let(u = unit([1,3,7]), v = unit([-2,1,-2])) [ for(i = [1:10]) rands(-1,1,1)[0] * u + rands(-1,1,1)[0] * v ];
|
||||||
|
|
||||||
|
testpoints_collinear_2d = let(u = unit([5,3])) [ for(i = [1:20]) rands(-1,1,1)[0] * u ];
|
||||||
|
testpoints_collinear_3d = let(u = unit([5,3,-5])) [ for(i = [1:20]) rands(-1,1,1)[0] * u ];
|
||||||
|
|
||||||
|
testpoints2d = 20 * [for (i = [1:10]) concat(rands(-1,1,2))];
|
||||||
|
testpoints3d = 20 * [for (i = [1:50]) concat(rands(-1,1,3))];
|
||||||
|
|
||||||
|
// All points are on the sphere, no point should be red
|
||||||
|
translate([-50,0]) visualize_hull(20*testpoints_on_sphere);
|
||||||
|
|
||||||
|
// 2D points
|
||||||
|
translate([50,0]) visualize_hull(testpoints2d);
|
||||||
|
|
||||||
|
// All points on a circle, no point should be red
|
||||||
|
translate([0,50]) visualize_hull(20*testpoints_circular);
|
||||||
|
|
||||||
|
// All points 3d but collinear
|
||||||
|
translate([0,-50]) visualize_hull(20*testpoints_coplanar);
|
||||||
|
|
||||||
|
// Collinear
|
||||||
|
translate([50,50]) visualize_hull(20*testpoints_collinear_2d);
|
||||||
|
|
||||||
|
// Collinear
|
||||||
|
translate([-50,50]) visualize_hull(20*testpoints_collinear_3d);
|
||||||
|
|
||||||
|
// 3D points
|
||||||
|
visualize_hull(testpoints3d);
|
||||||
|
|
||||||
|
|
||||||
|
module visualize_hull(points) {
|
||||||
|
|
||||||
|
hull = hull(points);
|
||||||
|
|
||||||
|
%if (len(hull) > 0 && len(hull[0]) > 0)
|
||||||
|
polyhedron(points=points, faces = hull);
|
||||||
|
else
|
||||||
|
polyhedron(points=points, faces = [hull]);
|
||||||
|
|
||||||
|
for (i = [0:len(points)-1]) assign(p = points[i], $fn = 16) {
|
||||||
|
translate(p) {
|
||||||
|
if (hull_contains_index(hull,i)) {
|
||||||
|
color("blue") sphere(1);
|
||||||
|
} else {
|
||||||
|
color("red") sphere(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function hull_contains_index(hull, index) =
|
||||||
|
search(index,hull,1,0) ||
|
||||||
|
search(index,hull,1,1) ||
|
||||||
|
search(index,hull,1,2);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
// very minimal set of linalg functions needed by so3, se3 etc.
|
||||||
|
|
||||||
|
// cross and norm are builtins
|
||||||
|
//function cross(x,y) = [x[1]*y[2]-x[2]*y[1], x[2]*y[0]-x[0]*y[2], x[0]*y[1]-x[1]*y[0]];
|
||||||
|
//function norm(v) = sqrt(v*v);
|
||||||
|
|
||||||
|
function vec3(p) = len(p) < 3 ? concat(p,0) : p;
|
||||||
|
function vec4(p) = let (v3=vec3(p)) len(v3) < 4 ? concat(v3,1) : v3;
|
||||||
|
function unit(v) = v/norm(v);
|
||||||
|
|
||||||
|
function identity3()=[[1,0,0],[0,1,0],[0,0,1]];
|
||||||
|
function identity4()=[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]];
|
||||||
|
|
||||||
|
|
||||||
|
function take3(v) = [v[0],v[1],v[2]];
|
||||||
|
function tail3(v) = [v[3],v[4],v[5]];
|
||||||
|
function rotation_part(m) = [take3(m[0]),take3(m[1]),take3(m[2])];
|
||||||
|
function rot_trace(m) = m[0][0] + m[1][1] + m[2][2];
|
||||||
|
function rot_cos_angle(m) = (rot_trace(m)-1)/2;
|
||||||
|
|
||||||
|
function rotation_part(m) = [take3(m[0]),take3(m[1]),take3(m[2])];
|
||||||
|
function translation_part(m) = [m[0][3],m[1][3],m[2][3]];
|
||||||
|
function transpose_3(m) = [[m[0][0],m[1][0],m[2][0]],[m[0][1],m[1][1],m[2][1]],[m[0][2],m[1][2],m[2][2]]];
|
||||||
|
function transpose_4(m) = [[m[0][0],m[1][0],m[2][0],m[3][0]],
|
||||||
|
[m[0][1],m[1][1],m[2][1],m[3][1]],
|
||||||
|
[m[0][2],m[1][2],m[2][2],m[3][2]],
|
||||||
|
[m[0][3],m[1][3],m[2][3],m[3][3]]];
|
||||||
|
function invert_rt(m) = construct_Rt(transpose_3(rotation_part(m)), -(transpose_3(rotation_part(m)) * translation_part(m)));
|
||||||
|
function construct_Rt(R,t) = [concat(R[0],t[0]),concat(R[1],t[1]),concat(R[2],t[2]),[0,0,0,1]];
|
||||||
|
|
||||||
|
// Hadamard product of n-dimensional arrays
|
||||||
|
function hadamard(a,b) = !(len(a)>0) ? a*b : [ for(i = [0:len(a)-1]) hadamard(a[i],b[i]) ];
|
|
@ -0,0 +1,48 @@
|
||||||
|
// List helpers
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Flattens a list one level:
|
||||||
|
|
||||||
|
flatten([[0,1],[2,3]]) => [0,1,2,3]
|
||||||
|
*/
|
||||||
|
function flatten(list) = [ for (i = list, v = i) v ];
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Creates a list from a range:
|
||||||
|
|
||||||
|
range([0:2:6]) => [0,2,4,6]
|
||||||
|
*/
|
||||||
|
function range(r) = [ for(x=r) x ];
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Reverses a list:
|
||||||
|
|
||||||
|
reverse([1,2,3]) => [3,2,1]
|
||||||
|
*/
|
||||||
|
function reverse(list) = [for (i = [len(list)-1:-1:0]) list[i]];
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Extracts a subarray from index begin (inclusive) to end (exclusive)
|
||||||
|
FIXME: Change name to use list instead of array?
|
||||||
|
|
||||||
|
subarray([1,2,3,4], 1, 2) => [2,3]
|
||||||
|
*/
|
||||||
|
function subarray(list,begin=0,end=-1) = [
|
||||||
|
let(end = end < 0 ? len(list) : end)
|
||||||
|
for (i = [begin : 1 : end-1])
|
||||||
|
list[i]
|
||||||
|
];
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Returns a copy of a list with the element at index i set to x
|
||||||
|
|
||||||
|
set([1,2,3,4], 2, 5) => [1,2,5,4]
|
||||||
|
*/
|
||||||
|
function set(list, i, x) = [for (i_=[0:len(list)-1]) i == i_ ? x : list[i_]];
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Remove element from the list by index.
|
||||||
|
remove([4,3,2,1],1) => [4,2,1]
|
||||||
|
*/
|
||||||
|
function remove(list, i) = [for (i_=[0:1:len(list)-2]) list[i_ < i ? i_ : i_ + 1]];
|
|
@ -0,0 +1,30 @@
|
||||||
|
// Copyright (c) 2013 Oskar Linde. All rights reserved.
|
||||||
|
// License: BSD
|
||||||
|
//
|
||||||
|
// This library contains simple mirroring functions
|
||||||
|
//
|
||||||
|
// mirror_x()
|
||||||
|
// mirror_y()
|
||||||
|
// mirror_z()
|
||||||
|
|
||||||
|
|
||||||
|
module mirror_x() {
|
||||||
|
union() {
|
||||||
|
child();
|
||||||
|
scale([-1,1,1]) child();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module mirror_y() {
|
||||||
|
union() {
|
||||||
|
child();
|
||||||
|
scale([1,-1,1]) child();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module mirror_z() {
|
||||||
|
union() {
|
||||||
|
child();
|
||||||
|
scale([1,1,-1]) child();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,109 @@
|
||||||
|
// Copyright (c) 2013 Oskar Linde. All rights reserved.
|
||||||
|
// License: BSD
|
||||||
|
//
|
||||||
|
// This library contains basic 2D morphology operations
|
||||||
|
//
|
||||||
|
// outset(d=1) - creates a polygon at an offset d outside a 2D shape
|
||||||
|
// inset(d=1) - creates a polygon at an offset d inside a 2D shape
|
||||||
|
// fillet(r=1) - adds fillets of radius r to all concave corners of a 2D shape
|
||||||
|
// rounding(r=1) - adds rounding to all convex corners of a 2D shape
|
||||||
|
// shell(d,center=false) - makes a shell of width d along the edge of a 2D shape
|
||||||
|
// - positive values of d places the shell on the outside
|
||||||
|
// - negative values of d places the shell on the inside
|
||||||
|
// - center=true and positive d places the shell centered on the edge
|
||||||
|
|
||||||
|
module outset(d=1) {
|
||||||
|
// Bug workaround for older OpenSCAD versions
|
||||||
|
if (version_num() < 20130424) render() outset_extruded(d) child();
|
||||||
|
else minkowski() {
|
||||||
|
circle(r=d);
|
||||||
|
child();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module outset_extruded(d=1) {
|
||||||
|
projection(cut=true) minkowski() {
|
||||||
|
cylinder(r=d);
|
||||||
|
linear_extrude(center=true) child();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module inset(d=1) {
|
||||||
|
render() inverse() outset(d=d) inverse() child();
|
||||||
|
}
|
||||||
|
|
||||||
|
module fillet(r=1) {
|
||||||
|
inset(d=r) render() outset(d=r) child();
|
||||||
|
}
|
||||||
|
|
||||||
|
module rounding(r=1) {
|
||||||
|
outset(d=r) inset(d=r) child();
|
||||||
|
}
|
||||||
|
|
||||||
|
module shell(d,center=false) {
|
||||||
|
if (center && d > 0) {
|
||||||
|
difference() {
|
||||||
|
outset(d=d/2) child();
|
||||||
|
inset(d=d/2) child();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!center && d > 0) {
|
||||||
|
difference() {
|
||||||
|
outset(d=d) child();
|
||||||
|
child();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!center && d < 0) {
|
||||||
|
difference() {
|
||||||
|
child();
|
||||||
|
inset(d=-d) child();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (d == 0) child();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Below are for internal use only
|
||||||
|
|
||||||
|
module inverse() {
|
||||||
|
difference() {
|
||||||
|
square(1e5,center=true);
|
||||||
|
child();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TEST CODE
|
||||||
|
|
||||||
|
use <mirror.scad>
|
||||||
|
|
||||||
|
module arrow(l=1,w=.6,t=0.15) {
|
||||||
|
mirror_y() polygon([[0,0],[l,0],[l-w/2,w/2],[l-w/2-sqrt(2)*t,w/2],[l-t/2-sqrt(2)*t,t/2],[0,t/2]]);
|
||||||
|
}
|
||||||
|
|
||||||
|
module shape() {
|
||||||
|
polygon([[0,0],[1,0],[1.5,1],[2.5,1],[2,-1],[0,-1]]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(0) assign($fn=32) {
|
||||||
|
|
||||||
|
for (p = [0:10*3-1]) assign(o=floor(p/3)) {
|
||||||
|
translate([(p%3)*2.5,-o*3]) {
|
||||||
|
//%if (p % 3 == 1) translate([0,0,1]) shape();
|
||||||
|
if (p % 3 == 0) shape();
|
||||||
|
if (p % 3 == 1) translate([0.6,0]) arrow();
|
||||||
|
if (p % 3 == 2) {
|
||||||
|
if (o == 0) inset(d=0.3) shape();
|
||||||
|
if (o == 1) outset(d=0.3) shape();
|
||||||
|
if (o == 2) rounding(r=0.3) shape();
|
||||||
|
if (o == 3) fillet(r=0.3) shape();
|
||||||
|
if (o == 4) shell(d=0.3) shape();
|
||||||
|
if (o == 5) shell(d=-0.3) shape();
|
||||||
|
if (o == 6) shell(d=0.3,center=true) shape();
|
||||||
|
if (o == 7) rounding(r=0.3) fillet(r=0.3) shape();
|
||||||
|
if (o == 8) shell(d=0.3,center=true) fillet(r=0.3) rounding(r=0.3) shape();
|
||||||
|
if (o == 9) shell(d=-0.3) fillet(r=0.3) rounding(r=0.3) shape();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
use <linalg.scad>
|
||||||
|
use <so3.scad>
|
||||||
|
|
||||||
|
function combine_se3_exp(w, ABt) = construct_Rt(rodrigues_so3_exp(w, ABt[0], ABt[1]), ABt[2]);
|
||||||
|
|
||||||
|
// [A,B,t]
|
||||||
|
function se3_exp_1(t,w) = concat(
|
||||||
|
so3_exp_1(w*w),
|
||||||
|
[t + 0.5 * cross(w,t)]
|
||||||
|
);
|
||||||
|
|
||||||
|
function se3_exp_2(t,w) = se3_exp_2_0(t,w,w*w);
|
||||||
|
function se3_exp_2_0(t,w,theta_sq) =
|
||||||
|
se3_exp_23(
|
||||||
|
so3_exp_2(theta_sq),
|
||||||
|
C = (1.0 - theta_sq/20) / 6,
|
||||||
|
t=t,w=w);
|
||||||
|
|
||||||
|
function se3_exp_3(t,w) = se3_exp_3_0(t,w,sqrt(w*w)*180/PI,1/sqrt(w*w));
|
||||||
|
|
||||||
|
function se3_exp_3_0(t,w,theta_deg,inv_theta) =
|
||||||
|
se3_exp_23(
|
||||||
|
so3_exp_3_0(theta_deg = theta_deg, inv_theta = inv_theta),
|
||||||
|
C = (1 - sin(theta_deg) * inv_theta) * (inv_theta * inv_theta),
|
||||||
|
t=t,w=w);
|
||||||
|
|
||||||
|
function se3_exp_23(AB,C,t,w) =
|
||||||
|
[AB[0], AB[1], t + AB[1] * cross(w,t) + C * cross(w,cross(w,t)) ];
|
||||||
|
|
||||||
|
function se3_exp(mu) = se3_exp_0(t=take3(mu),w=tail3(mu)/180*PI);
|
||||||
|
|
||||||
|
function se3_exp_0(t,w) =
|
||||||
|
combine_se3_exp(w,
|
||||||
|
// Evaluate by Taylor expansion when near 0
|
||||||
|
w*w < 1e-8
|
||||||
|
? se3_exp_1(t,w)
|
||||||
|
: w*w < 1e-6
|
||||||
|
? se3_exp_2(t,w)
|
||||||
|
: se3_exp_3(t,w)
|
||||||
|
);
|
||||||
|
|
||||||
|
function se3_ln(m) = se3_ln_to_deg(se3_ln_rad(m));
|
||||||
|
function se3_ln_to_deg(v) = concat(take3(v),tail3(v)*180/PI);
|
||||||
|
|
||||||
|
function se3_ln_rad(m) = se3_ln_0(m,
|
||||||
|
rot = so3_ln_rad(rotation_part(m)));
|
||||||
|
function se3_ln_0(m,rot) = se3_ln_1(m,rot,
|
||||||
|
theta = sqrt(rot*rot));
|
||||||
|
function se3_ln_1(m,rot,theta) = se3_ln_2(m,rot,theta,
|
||||||
|
shtot = theta > 0.00001 ? sin(theta/2*180/PI)/theta : 0.5,
|
||||||
|
halfrotator = so3_exp_rad(rot * -.5));
|
||||||
|
function se3_ln_2(m,rot,theta,shtot,halfrotator) =
|
||||||
|
concat( (halfrotator * translation_part(m) -
|
||||||
|
(theta > 0.001
|
||||||
|
? rot * ((translation_part(m) * rot) * (1-2*shtot) / (rot*rot))
|
||||||
|
: rot * ((translation_part(m) * rot)/24)
|
||||||
|
)) / (2 * shtot), rot);
|
||||||
|
|
||||||
|
__se3_test = [20,-40,60,-80,100,-120];
|
||||||
|
echo(UNITTEST_se3=norm(__se3_test-se3_ln(se3_exp(__se3_test))) < 1e-8);
|
|
@ -0,0 +1,16 @@
|
||||||
|
function square(size) = [[-size,-size], [-size,size], [size,size], [size,-size]] / 2;
|
||||||
|
|
||||||
|
function circle(r) = [for (i=[0:$fn-1]) let (a=i*360/$fn) r * [cos(a), sin(a)]];
|
||||||
|
|
||||||
|
function regular(r, n) = circle(r, $fn=n);
|
||||||
|
|
||||||
|
function rectangle_profile(size=[1,1]) = [
|
||||||
|
// The first point is the anchor point, put it on the point corresponding to [cos(0),sin(0)]
|
||||||
|
[ size[0]/2, 0],
|
||||||
|
[ size[0]/2, size[1]/2],
|
||||||
|
[-size[0]/2, size[1]/2],
|
||||||
|
[-size[0]/2, -size[1]/2],
|
||||||
|
[ size[0]/2, -size[1]/2],
|
||||||
|
];
|
||||||
|
|
||||||
|
// FIXME: Move rectangle and rounded rectangle from extrusion
|
|
@ -0,0 +1,82 @@
|
||||||
|
// so3
|
||||||
|
|
||||||
|
use <linalg.scad>
|
||||||
|
|
||||||
|
function rodrigues_so3_exp(w, A, B) = [
|
||||||
|
[1.0 - B*(w[1]*w[1] + w[2]*w[2]), B*(w[0]*w[1]) - A*w[2], B*(w[0]*w[2]) + A*w[1]],
|
||||||
|
[B*(w[0]*w[1]) + A*w[2], 1.0 - B*(w[0]*w[0] + w[2]*w[2]), B*(w[1]*w[2]) - A*w[0]],
|
||||||
|
[B*(w[0]*w[2]) - A*w[1], B*(w[1]*w[2]) + A*w[0], 1.0 - B*(w[0]*w[0] + w[1]*w[1])]
|
||||||
|
];
|
||||||
|
|
||||||
|
function so3_exp(w) = so3_exp_rad(w/180*PI);
|
||||||
|
function so3_exp_rad(w) =
|
||||||
|
combine_so3_exp(w,
|
||||||
|
w*w < 1e-8
|
||||||
|
? so3_exp_1(w*w)
|
||||||
|
: w*w < 1e-6
|
||||||
|
? so3_exp_2(w*w)
|
||||||
|
: so3_exp_3(w*w));
|
||||||
|
|
||||||
|
function combine_so3_exp(w,AB) = rodrigues_so3_exp(w,AB[0],AB[1]);
|
||||||
|
|
||||||
|
// Taylor series expansions close to 0
|
||||||
|
function so3_exp_1(theta_sq) = [
|
||||||
|
1 - 1/6*theta_sq,
|
||||||
|
0.5
|
||||||
|
];
|
||||||
|
|
||||||
|
function so3_exp_2(theta_sq) = [
|
||||||
|
1.0 - theta_sq * (1.0 - theta_sq/20) / 6,
|
||||||
|
0.5 - 0.25/6 * theta_sq
|
||||||
|
];
|
||||||
|
|
||||||
|
function so3_exp_3_0(theta_deg, inv_theta) = [
|
||||||
|
sin(theta_deg) * inv_theta,
|
||||||
|
(1 - cos(theta_deg)) * (inv_theta * inv_theta)
|
||||||
|
];
|
||||||
|
|
||||||
|
function so3_exp_3(theta_sq) = so3_exp_3_0(sqrt(theta_sq)*180/PI, 1/sqrt(theta_sq));
|
||||||
|
|
||||||
|
|
||||||
|
function rot_axis_part(m) = [m[2][1] - m[1][2], m[0][2] - m[2][0], m[1][0] - m[0][1]]*0.5;
|
||||||
|
|
||||||
|
function so3_ln(m) = 180/PI*so3_ln_rad(m);
|
||||||
|
function so3_ln_rad(m) = so3_ln_0(m,
|
||||||
|
cos_angle = rot_cos_angle(m),
|
||||||
|
preliminary_result = rot_axis_part(m));
|
||||||
|
|
||||||
|
function so3_ln_0(m, cos_angle, preliminary_result) =
|
||||||
|
so3_ln_1(m, cos_angle, preliminary_result,
|
||||||
|
sin_angle_abs = sqrt(preliminary_result*preliminary_result));
|
||||||
|
|
||||||
|
function so3_ln_1(m, cos_angle, preliminary_result, sin_angle_abs) =
|
||||||
|
cos_angle > sqrt(1/2)
|
||||||
|
? sin_angle_abs > 0
|
||||||
|
? preliminary_result * asin(sin_angle_abs)*PI/180 / sin_angle_abs
|
||||||
|
: preliminary_result
|
||||||
|
: cos_angle > -sqrt(1/2)
|
||||||
|
? preliminary_result * acos(cos_angle)*PI/180 / sin_angle_abs
|
||||||
|
: so3_get_symmetric_part_rotation(
|
||||||
|
preliminary_result,
|
||||||
|
m,
|
||||||
|
angle = PI - asin(sin_angle_abs)*PI/180,
|
||||||
|
d0 = m[0][0] - cos_angle,
|
||||||
|
d1 = m[1][1] - cos_angle,
|
||||||
|
d2 = m[2][2] - cos_angle
|
||||||
|
);
|
||||||
|
|
||||||
|
function so3_get_symmetric_part_rotation(preliminary_result, m, angle, d0, d1, d2) =
|
||||||
|
so3_get_symmetric_part_rotation_0(preliminary_result,angle,so3_largest_column(m, d0, d1, d2));
|
||||||
|
|
||||||
|
function so3_get_symmetric_part_rotation_0(preliminary_result, angle, c_max) =
|
||||||
|
angle * unit(c_max * preliminary_result < 0 ? -c_max : c_max);
|
||||||
|
|
||||||
|
function so3_largest_column(m, d0, d1, d2) =
|
||||||
|
d0*d0 > d1*d1 && d0*d0 > d2*d2
|
||||||
|
? [d0, (m[1][0]+m[0][1])/2, (m[0][2]+m[2][0])/2]
|
||||||
|
: d1*d1 > d2*d2
|
||||||
|
? [(m[1][0]+m[0][1])/2, d1, (m[2][1]+m[1][2])/2]
|
||||||
|
: [(m[0][2]+m[2][0])/2, (m[2][1]+m[1][2])/2, d2];
|
||||||
|
|
||||||
|
__so3_test = [12,-125,110];
|
||||||
|
echo(UNITTEST_so3=norm(__so3_test-so3_ln(so3_exp(__so3_test))) < 1e-8);
|
|
@ -0,0 +1,113 @@
|
||||||
|
// Spline module for scad-util library
|
||||||
|
// Author Sergei Kuzmin, 2014.
|
||||||
|
|
||||||
|
// For n+1 given point and hense n intervals returns the spline coefficient matrix.
|
||||||
|
// param p defines the anchor points.
|
||||||
|
// File defines two functions: spline_args and spline.
|
||||||
|
// example usage:
|
||||||
|
// spl1 = spline_args(point, v1=[0,1,0], closed=false);
|
||||||
|
// interpolated_points = [for(t=[0:0.1:len(point)-1]) spline(spl1, t)]
|
||||||
|
|
||||||
|
use <linalg.scad>
|
||||||
|
use <lists.scad>
|
||||||
|
|
||||||
|
q1=[[1,0,0,0],[1,1,1,1],[0,1,2,3],[0,0,1,3]];
|
||||||
|
q1inv=[[1,0,0,0],[-3,3,-2,1],[3,-3,3,-2],[-1,1,-1,1]];
|
||||||
|
q2=[[0,0,0,0],[0,0,0,0],[0,-1,0,0],[0,0,-1,0]];
|
||||||
|
qn1i2=-q1inv*q2;
|
||||||
|
z3=[0,0,0];
|
||||||
|
z4=[0,0,0,0];
|
||||||
|
|
||||||
|
function matrix_power(m,n)= n==0? (len(m)==3?identity3():identity4()) :
|
||||||
|
n==1 ? m : (n%2==1) ? matrix_power(m*m,floor(n/2))*m : matrix_power(m*m,n/2);
|
||||||
|
|
||||||
|
function det(m) = let(r=[for(i=[0:1:len(m)-1]) i]) det_help(m, 0, r);
|
||||||
|
// Construction indices list is inefficient, but currently there is no way to imperatively
|
||||||
|
// assign to a list element
|
||||||
|
function det_help(m, i, r) = len(r) == 0 ? 1 :
|
||||||
|
m[len(m)-len(r)][r[i]]*det_help(m,0,remove(r,i)) - (i+1<len(r)? det_help(m, i+1, r) : 0);
|
||||||
|
|
||||||
|
function matrix_invert(m) = let(r=[for(i=[0:len(m)-1]) i]) [for(i=r) [for(j=r)
|
||||||
|
((i+j)%2==0 ? 1:-1) * matrix_minor(m,0,remove(r,j),remove(r,i))]] / det(m);
|
||||||
|
function matrix_minor(m,k,ri, rj) = let(len_r=len(ri)) len_r == 0 ? 1 :
|
||||||
|
m[ri[0]][rj[k]]*matrix_minor(m,0,remove(ri,0),remove(rj,k)) - (k+1<len_r?matrix_minor(m,k+1,ri,rj) : 0);
|
||||||
|
|
||||||
|
function spline_u(i,p) = [p[i],p[i+1],z3,z3];
|
||||||
|
|
||||||
|
function spline_args(p, closed=false, v1=undef, v2=undef)=len(p)<2 ? []:
|
||||||
|
let(q3=closed?q2:[z4, z4, v1==undef?[0,0,1,0]:[0,1,0,0], z4],
|
||||||
|
q4=closed?q1:[[1,0,0,0], [1,1,1,1], z4, v2==undef?[0,0,1,3]:[0,1,2,3]],
|
||||||
|
pcnt=closed? len(p) + 1 : len(p),
|
||||||
|
un=[p[pcnt-2],p[closed?0:pcnt-1],v1==undef?z4:v1, v2==undef?z4:v2],
|
||||||
|
sn=matrix_invert(q4+q3*matrix_power(qn1i2,pcnt-2))*(un-q3*q1inv*spline_helper(0, pcnt, p)))
|
||||||
|
// result[i+1] recurrently defines result[i]. This is O(n) runtime with imperative language and
|
||||||
|
// may be O(n^2) if OpenSCAD doesn't cache spline_si(i+1).
|
||||||
|
[for(i=[0:pcnt-2]) spline_si(i, pcnt-2, p, sn)];
|
||||||
|
|
||||||
|
// n is number of points including pseudopoint for closed contour
|
||||||
|
// Weird construct cause there is no if statement for functions
|
||||||
|
function spline_helper(i, n, p) = let(u=[p[i], p[i+1], z3, z3]) i+3>=n? u : u-q2*q1inv*spline_helper(i+1, n, p);
|
||||||
|
|
||||||
|
// knowing s[j+1], calculate s[j]. Stop when found s[i]
|
||||||
|
function spline_si(i,n, p, sn) = i == n ? sn : q1inv*(spline_u(i,p)-q2*spline_si(i+1, n, p, sn));
|
||||||
|
|
||||||
|
// Takes array of (3n+1) points or (2n + 2) points, if tangent segments are symmetric.
|
||||||
|
// For non-symmetric version input is: point0, normal0, neg_normal1, point1, normal1, ... neg_normal_n, point_n
|
||||||
|
// For symmetric version: point0, normal0, point1, normal1, ... , normal_n_sub_1, point_n
|
||||||
|
// In the second case second tangent is constructed from the next tangent by symmetric map.
|
||||||
|
// I.e. if current points are p0,p1,p2 then anchor points are p0 and p2, first tangent defined by p1-p0,
|
||||||
|
// second tangent defined by p3-p2.
|
||||||
|
// Return array of coefficients accepted by spline(), spline_tan() and similar
|
||||||
|
function bezier3_args(p, symmetric=false) = let(step=symmetric?2:3)
|
||||||
|
[for(i=[0:step:len(p)-3]) [[1,0,0,0],[-3,3,0,0],[3,-6,3,0],[-1,3,-3,1]]*
|
||||||
|
(symmetric?[p[i],p[i]+p[i+1],p[i+2]-p[i+3],p[i+2]] : [p[i], p[i]+p[i+1], p[i+3]+p[i+2], p[i+3]])];
|
||||||
|
|
||||||
|
// s - spline arguments calculated by spline_args
|
||||||
|
// t - defines point on curve. each segment length is 1. I.e. t= 0..1 is first segment, t=1..2 - second.
|
||||||
|
function spline(s, t)= let(i=t>=len(s)?len(s)-1: floor(t), t2=t-i) [1,t2,t2*t2,t2*t2*t2]*s[i];
|
||||||
|
|
||||||
|
function spline_tan(s, t)= let(i=t>=len(s)?len(s)-1: floor(t), t2=t-i) [0,1,2*t2,3*t2*t2]*s[i];
|
||||||
|
function spline_tan_unit(s, t)= unit(spline_tan(s,t));
|
||||||
|
function spline_d2(s,t)= let(i=t>=len(s)?len(s)-1: floor(t), t2=t-i) [0,0,2,6*t2]*s[i];
|
||||||
|
function spline_binormal_unit(s,t)= unit(cross(spline_tan(s, t), spline_d2(s,t)));
|
||||||
|
function spline_normal_unit(s,t)= unit(cross(spline_tan(s, t), spline_binormal_unit(s,t)));
|
||||||
|
|
||||||
|
function spline_transform(s, t)=
|
||||||
|
construct_Rt(transpose_3([spline_normal_unit(s,t), spline_binormal_unit(s,t), spline_tan_unit(s,t)]), spline(s,t));
|
||||||
|
|
||||||
|
// Unit tests
|
||||||
|
__s = spline_args([[0,10,0], [10,0,0],[0,-5,2]], v1=[0,1,0], v2=[-1,0,0], closed=true);
|
||||||
|
for(t=[0:0.01:len(__s)]) translate(spline(__s, t))
|
||||||
|
cube([0.2,0.2,0.2], center=true);
|
||||||
|
|
||||||
|
__s1=spline_args([[0,0,0],[0,0,15], [26,0,26+15]], /*v1=[0,0,100],*/ v2=[40,0,0]);
|
||||||
|
for(t=[0:0.01:len(s1)]) translate(spline(__s1, t))
|
||||||
|
cube([0.2,0.2,0.2], center=true);
|
||||||
|
|
||||||
|
__s2=bezier3_args([[0,0,0],[0,0,10],[0,0,15],[0,0,26*0.552284],[26,0,41],[26*0.552284,0,0]],symmetric=true);
|
||||||
|
echo(__s2);
|
||||||
|
for(t=[0:0.01:len(__s2)]) translate(spline(__s2, t))
|
||||||
|
cube([0.2,0.2,0.2], center=true);
|
||||||
|
|
||||||
|
// Rotation methods taken from list-comprehension-demos/sweep.scad to demonstrate normal and binormal
|
||||||
|
// Normally spline_transform is more convenient
|
||||||
|
function __rotation_from_axis(x,y,z) = [[x[0],y[0],z[0]],[x[1],y[1],z[1]],[x[2],y[2],z[2]]];
|
||||||
|
function __rotate_from_to(a,b,_axis=[]) =
|
||||||
|
len(_axis) == 0
|
||||||
|
? __rotate_from_to(a,b,unit(cross(a,b)))
|
||||||
|
: _axis*_axis >= 0.99 ? __rotation_from_axis(unit(b),_axis,cross(_axis,unit(b))) *
|
||||||
|
transpose_3(__rotation_from_axis(unit(a),_axis,cross(_axis,unit(a)))) : identity3();
|
||||||
|
|
||||||
|
__s3 = spline_args([[0,10,0], [6,6,0], [10,0,0],[0,-5,4]], v1=[0,1,0], v2=[-1,0,0], closed=true);
|
||||||
|
for(t=[0:0.05:len(__s3)]) translate(spline(__s3, t)) {
|
||||||
|
translate([0,0,3]) multmatrix(m=__rotate_from_to([0,0,1],spline_normal_unit(__s3,t)))
|
||||||
|
cylinder(r1=0.1, r2=0, h=1, $fn=3);
|
||||||
|
translate([0,0,6]) multmatrix(m=__rotate_from_to([0,0,1],spline_binormal_unit(__s3,t)))
|
||||||
|
cylinder(r1=0.1, r2=0, h=1, $fn=3);
|
||||||
|
}
|
||||||
|
|
||||||
|
translate([0,0,9]) for(t=[0:0.025:len(__s3)])
|
||||||
|
multmatrix(spline_transform(__s3,t)) cube([1,1,0.1],center=true);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
use <so3.scad>
|
||||||
|
|
||||||
|
function val(a=undef,default=undef) = a == undef ? default : a;
|
||||||
|
function vec_is_undef(x,index_=0) = index_ >= len(x) ? true :
|
||||||
|
is_undef(x[index_]) && vec_is_undef(x,index_+1);
|
||||||
|
|
||||||
|
function is_undef(x) = len(x) > 0 ? vec_is_undef(x) : x == undef;
|
||||||
|
// Either a or b, but not both
|
||||||
|
function either(a,b,default=undef) = is_undef(a) ? (is_undef(b) ? default : b) : is_undef(b) ? a : undef;
|
||||||
|
|
||||||
|
function translationv(left=undef,right=undef,up=undef,down=undef,forward=undef,backward=undef,translation=undef) =
|
||||||
|
translationv_2(
|
||||||
|
x = either(up,-down),
|
||||||
|
y = either(right,-left),
|
||||||
|
z = either(forward,-backward),
|
||||||
|
translation = translation);
|
||||||
|
|
||||||
|
function translationv_2(x,y,z,translation) =
|
||||||
|
x == undef && y == undef && z == undef ? translation :
|
||||||
|
is_undef(translation) ? [val(x,0),val(y,0),val(z,0)]
|
||||||
|
: undef;
|
||||||
|
|
||||||
|
function rotationv(pitch=undef,yaw=undef,roll=undef,rotation=undef) =
|
||||||
|
rotation == undef ? [val(yaw,0),val(pitch,0),val(roll,0)] :
|
||||||
|
pitch == undef && yaw == undef && roll == undef ? rotation :
|
||||||
|
undef;
|
||||||
|
|
||||||
|
function trajectory(
|
||||||
|
left=undef, right=undef,
|
||||||
|
up=undef, down=undef,
|
||||||
|
forward=undef, backward=undef,
|
||||||
|
translation=undef,
|
||||||
|
|
||||||
|
pitch=undef,
|
||||||
|
yaw=undef,
|
||||||
|
roll=undef,
|
||||||
|
rotation=undef
|
||||||
|
) = concat(
|
||||||
|
translationv(left=left,right=right,up=up,down=down,forward=forward,backward=backward,translation=translation),
|
||||||
|
rotationv(pitch=pitch,yaw=yaw,roll=roll,rotation=rotation)
|
||||||
|
);
|
||||||
|
|
||||||
|
function rotationm(rotation=undef,pitch=undef,yaw=undef,roll=undef) = so3_exp(rotationv(rotation=rotation,pitch=pitch,yaw=yaw,roll=roll));
|
|
@ -0,0 +1,89 @@
|
||||||
|
use <linalg.scad>
|
||||||
|
use <se3.scad>
|
||||||
|
|
||||||
|
function left_multiply(a,bs,i_=0) = i_ >= len(bs) ? [] :
|
||||||
|
concat([
|
||||||
|
a * bs[i_]
|
||||||
|
], left_multiply(a,bs,i_+1));
|
||||||
|
|
||||||
|
|
||||||
|
function right_multiply(as,b,i_=0) = i_ >= len(as) ? [] :
|
||||||
|
concat([
|
||||||
|
as[i_] * b
|
||||||
|
], right_multiply(as,b,i_+1));
|
||||||
|
|
||||||
|
function quantize_trajectory(trajectory,step=undef,start_position=0,steps=undef,i_=0,length_=undef) =
|
||||||
|
length_ == undef ? quantize_trajectory(
|
||||||
|
trajectory=trajectory,
|
||||||
|
start_position=(step==undef?norm(take3(trajectory))/steps*start_position:start_position),
|
||||||
|
length_=norm(take3(trajectory)),
|
||||||
|
step=step,steps=steps,i_=i_) :
|
||||||
|
(steps==undef?start_position > length_:i_>=steps) ? [] :
|
||||||
|
concat([
|
||||||
|
// if steps is defined, ignore start_position
|
||||||
|
se3_exp(trajectory*(steps==undef ? start_position/length_
|
||||||
|
: i_/(steps>1?steps-1:1)))
|
||||||
|
], quantize_trajectory(trajectory=trajectory,step=step,start_position=(steps==undef?start_position+step:start_position),steps=steps,i_=i_+1,length_=length_));
|
||||||
|
|
||||||
|
function close_trajectory_loop(trajectories) = concat(trajectories,[se3_ln(invert_rt(trajectories_end_position(trajectories)))]);
|
||||||
|
|
||||||
|
function quantize_trajectories(trajectories,step=undef,start_position=0,steps=undef,loop=false,last_=identity4(),i_=0,current_length_=undef,j_=0) =
|
||||||
|
// due to quantization differences, the last step may be missed. In that case, add it:
|
||||||
|
loop==true ? quantize_trajectories(
|
||||||
|
trajectories=close_trajectory_loop(trajectories),
|
||||||
|
step=step,
|
||||||
|
start_position = start_position,
|
||||||
|
steps=steps,
|
||||||
|
loop=false,
|
||||||
|
last_=last_,
|
||||||
|
i_=i_,
|
||||||
|
current_length_=current_length_,
|
||||||
|
j_=j_) :
|
||||||
|
i_ >= len(trajectories) ? (j_ < steps ? [last_] : []) :
|
||||||
|
current_length_ == undef ?
|
||||||
|
quantize_trajectories(
|
||||||
|
trajectories=trajectories,
|
||||||
|
step = (step == undef ? trajectories_length(trajectories) / steps : step),
|
||||||
|
start_position = (step == undef ? start_position * trajectories_length(trajectories) / steps : start_position),
|
||||||
|
steps=steps,
|
||||||
|
loop=loop,
|
||||||
|
last_=last_,
|
||||||
|
i_=i_,
|
||||||
|
current_length_=norm(take3(trajectories[i_])),
|
||||||
|
j_=j_) :
|
||||||
|
concat(
|
||||||
|
left_multiply(last_,quantize_trajectory(
|
||||||
|
trajectory=trajectories[i_],
|
||||||
|
start_position=start_position,
|
||||||
|
step=step)),
|
||||||
|
quantize_trajectories(
|
||||||
|
trajectories=trajectories,
|
||||||
|
step=step,
|
||||||
|
start_position = start_position > current_length_
|
||||||
|
? start_position - current_length_
|
||||||
|
: step - ((current_length_-start_position) % step),
|
||||||
|
steps=steps,
|
||||||
|
loop=loop,
|
||||||
|
last_=last_ * se3_exp(trajectories[i_]),
|
||||||
|
i_=i_+1,
|
||||||
|
current_length_ = undef,
|
||||||
|
j_=j_+len(
|
||||||
|
|
||||||
|
quantize_trajectory(
|
||||||
|
trajectory=trajectories[i_],
|
||||||
|
start_position=start_position,
|
||||||
|
step=step
|
||||||
|
|
||||||
|
))
|
||||||
|
))
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
|
function trajectories_length(trajectories, i_=0) = i_ >= len(trajectories) ? 0
|
||||||
|
: norm(take3(trajectories[i_])) + trajectories_length(trajectories,i_+1);
|
||||||
|
|
||||||
|
|
||||||
|
function trajectories_end_position(rt,i_=0,last_=identity4()) =
|
||||||
|
i_ >= len(rt) ? last_ :
|
||||||
|
trajectories_end_position(rt, i_+1, last_ * se3_exp(rt[i_]));
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
use <se3.scad>
|
||||||
|
use <linalg.scad>
|
||||||
|
use <lists.scad>
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Creates a rotation matrix
|
||||||
|
|
||||||
|
xyz = euler angles = rz * ry * rx
|
||||||
|
axis = rotation_axis * rotation_angle
|
||||||
|
*/
|
||||||
|
function rotation(xyz=undef, axis=undef) =
|
||||||
|
xyz != undef && axis != undef ? undef :
|
||||||
|
xyz == undef ? se3_exp([0,0,0,axis[0],axis[1],axis[2]]) :
|
||||||
|
len(xyz) == undef ? rotation(axis=[0,0,xyz]) :
|
||||||
|
(len(xyz) >= 3 ? rotation(axis=[0,0,xyz[2]]) : identity4()) *
|
||||||
|
(len(xyz) >= 2 ? rotation(axis=[0,xyz[1],0]) : identity4()) *
|
||||||
|
(len(xyz) >= 1 ? rotation(axis=[xyz[0],0,0]) : identity4());
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Creates a scaling matrix
|
||||||
|
*/
|
||||||
|
function scaling(v) = [
|
||||||
|
[v[0],0,0,0],
|
||||||
|
[0,v[1],0,0],
|
||||||
|
[0,0,v[2],0],
|
||||||
|
[0,0,0,1],
|
||||||
|
];
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Creates a translation matrix
|
||||||
|
*/
|
||||||
|
function translation(v) = [
|
||||||
|
[1,0,0,v[0]],
|
||||||
|
[0,1,0,v[1]],
|
||||||
|
[0,0,1,v[2]],
|
||||||
|
[0,0,0,1],
|
||||||
|
];
|
||||||
|
|
||||||
|
// Convert between cartesian and homogenous coordinates
|
||||||
|
function project(x) = subarray(x,end=len(x)-1) / x[len(x)-1];
|
||||||
|
|
||||||
|
function transform(m, list) = [for (p=list) project(m * vec4(p))];
|
||||||
|
function to_3d(list) = [ for(v = list) vec3(v) ];
|
|
@ -0,0 +1,137 @@
|
||||||
|
use <scad-utils/transformations.scad>
|
||||||
|
use <scad-utils/lists.scad>
|
||||||
|
|
||||||
|
// Skin a set of profiles with a polyhedral mesh
|
||||||
|
module skin(profiles, loop=false /* unimplemented */) {
|
||||||
|
P = max_len(profiles);
|
||||||
|
N = len(profiles);
|
||||||
|
|
||||||
|
profiles = [
|
||||||
|
for (p = profiles)
|
||||||
|
for (pp = augment_profile(to_3d(p),P))
|
||||||
|
pp
|
||||||
|
];
|
||||||
|
|
||||||
|
function quad(i,P,o) = [[o+i, o+i+P, o+i%P+P+1], [o+i, o+i%P+P+1, o+i%P+1]];
|
||||||
|
|
||||||
|
function profile_triangles(tindex) = [
|
||||||
|
for (index = [0:P-1])
|
||||||
|
let (qs = quad(index+1, P, P*(tindex-1)-1))
|
||||||
|
for (q = qs) q
|
||||||
|
];
|
||||||
|
|
||||||
|
triangles = [
|
||||||
|
for(index = [1:N-1])
|
||||||
|
for(t = profile_triangles(index))
|
||||||
|
t
|
||||||
|
];
|
||||||
|
|
||||||
|
start_cap = [range([0:P-1])];
|
||||||
|
end_cap = [range([P*N-1 : -1 : P*(N-1)])];
|
||||||
|
|
||||||
|
polyhedron(convexity=2, points=profiles, faces=concat(start_cap, triangles, end_cap));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Augments the profile with steiner points making the total number of vertices n
|
||||||
|
function augment_profile(profile, n) =
|
||||||
|
subdivide(profile,insert_extra_vertices_0([profile_lengths(profile),dup(0,len(profile))],n-len(profile))[1]);
|
||||||
|
|
||||||
|
function subdivide(profile,subdivisions) = let (N=len(profile)) [
|
||||||
|
for (i = [0:N-1])
|
||||||
|
let(n = len(subdivisions)>0 ? subdivisions[i] : subdivisions)
|
||||||
|
for (p = interpolate(profile[i],profile[(i+1)%N],n+1))
|
||||||
|
p
|
||||||
|
];
|
||||||
|
|
||||||
|
function interpolate(a,b,subdivisions) = [
|
||||||
|
for (index = [0:subdivisions-1])
|
||||||
|
let(t = index/subdivisions)
|
||||||
|
a*(1-t)+b*t
|
||||||
|
];
|
||||||
|
|
||||||
|
function distribute_extra_vertex(lengths_count,ma_=-1) = ma_<0 ? distribute_extra_vertex(lengths_count, max_element(lengths_count[0])) :
|
||||||
|
concat([set(lengths_count[0],ma_,lengths_count[0][ma_] * (lengths_count[1][ma_]+1) / (lengths_count[1][ma_]+2))], [increment(lengths_count[1],max_element(lengths_count[0]),1)]);
|
||||||
|
|
||||||
|
function insert_extra_vertices_0(lengths_count,n_extra) = n_extra <= 0 ? lengths_count :
|
||||||
|
insert_extra_vertices_0(distribute_extra_vertex(lengths_count),n_extra-1);
|
||||||
|
|
||||||
|
// Find the index of the maximum element of arr
|
||||||
|
function max_element(arr,ma_,ma_i_=-1,i_=0) = i_ >= len(arr) ? ma_i_ :
|
||||||
|
i_ == 0 || arr[i_] > ma_ ? max_element(arr,arr[i_],i_,i_+1) : max_element(arr,ma_,ma_i_,i_+1);
|
||||||
|
|
||||||
|
function max_len(arr) = max([for (i=arr) len(i)]);
|
||||||
|
|
||||||
|
function increment(arr,i,x=1) = set(arr,i,arr[i]+x);
|
||||||
|
|
||||||
|
function profile_lengths(profile) = [
|
||||||
|
for (i = [0:len(profile)-1])
|
||||||
|
profile_segment_length(profile,i)
|
||||||
|
];
|
||||||
|
|
||||||
|
function profile_segment_length(profile,i) = norm(profile[(i+1)%len(profile)] - profile[i]);
|
||||||
|
|
||||||
|
// Generates an array with n copies of value (default 0)
|
||||||
|
function dup(value=0,n) = [for (i = [1:n]) value];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
use <scad-utils/transformations.scad>
|
||||||
|
use <scad-utils/trajectory_path.scad>
|
||||||
|
use <scad-utils/trajectory.scad>
|
||||||
|
use <scad-utils/shapes.scad>
|
||||||
|
|
||||||
|
module fakeISOEnter(thickness_difference = 0){
|
||||||
|
// 1u is the space taken upy by a 1u keycap.
|
||||||
|
// unit is the space taken up by a unit space for a keycap.
|
||||||
|
// formula is 1u + unit *(length - 1)
|
||||||
|
|
||||||
|
// t is all modifications to the polygon array
|
||||||
|
t = thickness_difference/2 - (19.02 - 18.16);
|
||||||
|
|
||||||
|
function unit(length) = 19.02 * length;
|
||||||
|
|
||||||
|
pointArray = [
|
||||||
|
[19.05 * (-.5) + t, 19.05 * (-1) + t],
|
||||||
|
[19.05 * (0.5) - t, 19.05 * (-1) + t],
|
||||||
|
[19.05 * (0.5) - t, 19.05 * (1) - t],
|
||||||
|
[19.05 * (-0.75) + t, 19.05 * (1) - t],
|
||||||
|
[19.05 * (-0.75) + t, 19.05 * (0) + t],
|
||||||
|
[19.05 * (-0.5) + t, 19.05 * (0) + t]
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
/*translate([unit(-.5), unit(-1) + 0.86]){*/
|
||||||
|
minkowski() {
|
||||||
|
circle($corner_radius, $fn=20);
|
||||||
|
offset(r=-$corner_radius * 2, $fn=20) polygon(points=pointArray);
|
||||||
|
}
|
||||||
|
/*}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
function isoEnter() = [
|
||||||
|
[19.05 * (-.5) + (19.02 - 18.16), 19.05 * (-1) + (19.02 - 18.16)],
|
||||||
|
[19.05 * (0.5) - (19.02 - 18.16), 19.05 * (-1) + (19.02 - 18.16)],
|
||||||
|
[19.05 * (0.5) - (19.02 - 18.16), 19.05 * (1) - (19.02 - 18.16)],
|
||||||
|
[19.05 * (-0.75) + (19.02 - 18.16), 19.05 * (1) - (19.02 - 18.16)],
|
||||||
|
[19.05 * (-0.75) + (19.02 - 18.16), 19.05 * (0) + (19.02 - 18.16)],
|
||||||
|
[19.05 * (-0.5) + (19.02 - 18.16), 19.05 * (0) + (19.02 - 18.16)]
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
path_definition = [
|
||||||
|
trajectory(forward = 10, roll = 0),
|
||||||
|
];
|
||||||
|
|
||||||
|
// sweep
|
||||||
|
path = quantize_trajectories(path_definition, steps=100);
|
||||||
|
|
||||||
|
// skin
|
||||||
|
myLen = len(path)-1;
|
||||||
|
trans = [ for (i=[0:len(path)-1]) transform(path[i], isoEnter()) ];
|
||||||
|
|
||||||
|
translate([0,10,0])
|
||||||
|
skin(trans);
|
43
util.scad
43
util.scad
|
@ -1,7 +1,6 @@
|
||||||
$fs=.1;
|
$fs=.1;
|
||||||
|
|
||||||
//centered
|
// centered
|
||||||
// offset, who knew?
|
|
||||||
module roundedRect(size, radius, center=true) {
|
module roundedRect(size, radius, center=true) {
|
||||||
linear_extrude(height = size[2]){
|
linear_extrude(height = size[2]){
|
||||||
roundedSquare([size[0], size[1]], radius, center=center);
|
roundedSquare([size[0], size[1]], radius, center=center);
|
||||||
|
@ -14,25 +13,35 @@ module roundedSquare(size, radius, center = true) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module oldroundedRect(size, radius) {
|
// corollary is roundedRect
|
||||||
x = size[0];
|
// NOT 3D
|
||||||
y = size[1];
|
module fakeISOEnter(thickness_difference = 0){
|
||||||
z = size[2];
|
// 1u is the space taken upy by a 1u keycap.
|
||||||
|
// unit is the space taken up by a unit space for a keycap.
|
||||||
|
// formula is 1u + unit *(length - 1)
|
||||||
|
|
||||||
translate([-x/2,-y/2,0])
|
// t is all modifications to the polygon array
|
||||||
linear_extrude(height=z)
|
// t used to contain a corner radius adjustment, but due to
|
||||||
hull() {
|
// the offset we need that expansion back
|
||||||
translate([radius, radius, 0])
|
t = (thickness_difference - (19.05 - 18.16));
|
||||||
circle(r=radius);
|
|
||||||
|
|
||||||
translate([x - radius, radius, 0])
|
function unit(length) = 19.05 * length;
|
||||||
circle(r=radius);
|
|
||||||
|
|
||||||
translate([x - radius, y - radius, 0])
|
pointArray = [
|
||||||
circle(r=radius);
|
[unit(-.625) + t, unit(-1) + t],
|
||||||
|
[unit(0.625) - t, unit(-1) + t],
|
||||||
|
[unit(0.625) - t, unit(1) - t],
|
||||||
|
[unit(-0.875) + t, unit(1) - t],
|
||||||
|
[unit(-0.875) + t, unit(0) + t],
|
||||||
|
[unit(-0.625) + t, unit(0) + t]
|
||||||
|
];
|
||||||
|
|
||||||
translate([radius, y - radius, 0])
|
minkowski(){
|
||||||
circle(r=radius);
|
circle(r=$corner_radius);
|
||||||
|
// gives us rounded inner corner
|
||||||
|
offset(r=-$corner_radius*2) {
|
||||||
|
polygon(points=pointArray);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue