mirror of
https://github.com/BelfrySCAD/BOSL2.git
synced 2024-12-27 15:29:41 +00:00
Updated copyright years. Split math.scad up. Enabled attach for lots of shapes. Removed backwards compatability.
This commit is contained in:
parent
019aae4347
commit
cc36235736
29 changed files with 1995 additions and 1743 deletions
2
LICENSE
2
LICENSE
|
@ -1,6 +1,6 @@
|
|||
BSD 2-Clause License
|
||||
|
||||
Copyright (c) 2017, Revar Desmera
|
||||
Copyright (c) 2017-2019, Revar Desmera
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
|
486
arrays.scad
Normal file
486
arrays.scad
Normal file
|
@ -0,0 +1,486 @@
|
|||
//////////////////////////////////////////////////////////////////////
|
||||
// LibFile: arrays.scad
|
||||
// List and Array manipulation functions.
|
||||
// To use, add the following lines to the beginning of your file:
|
||||
// ```
|
||||
// use <BOSL2/std.scad>
|
||||
// ```
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
BSD 2-Clause License
|
||||
|
||||
Copyright (c) 2017-2019, Revar Desmera
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
// Section: Terminology
|
||||
// - **List**: An ordered collection of zero or more items. ie: ["a", "b", "c"]
|
||||
// - **Vector**: A list of numbers. ie: [4, 5, 6]
|
||||
// - **Array**: A nested list of lists, or list of lists of lists, or deeper. ie: [[2,3], [4,5], [6,7]]
|
||||
// - **Dimension**: The depth of nesting of lists in an array. A List is 1D. A list of lists is 2D. etc.
|
||||
|
||||
|
||||
// Section: List Operations
|
||||
|
||||
|
||||
// Function: replist()
|
||||
// Usage:
|
||||
// replist(val, n)
|
||||
// Description:
|
||||
// Generates a list or array of `n` copies of the given `list`.
|
||||
// If the count `n` is given as a list of counts, then this creates a
|
||||
// multi-dimensional array, filled with `val`.
|
||||
// Arguments:
|
||||
// val = The value to repeat to make the list or array.
|
||||
// n = The number of copies to make of `val`.
|
||||
// Example:
|
||||
// replist(1, 4); // Returns [1,1,1,1]
|
||||
// replist(8, [2,3]); // Returns [[8,8,8], [8,8,8]]
|
||||
// replist(0, [2,2,3]); // Returns [[[0,0,0],[0,0,0]], [[0,0,0],[0,0,0]]]
|
||||
// replist([1,2,3],3); // Returns [[1,2,3], [1,2,3], [1,2,3]]
|
||||
function replist(val, n, i=0) =
|
||||
is_num(n)? [for(j=[1:n]) val] :
|
||||
(i>=len(n))? val :
|
||||
[for (j=[1:n[i]]) replist(val, n, i+1)];
|
||||
|
||||
|
||||
// Function: in_list()
|
||||
// Description: Returns true if value `x` is in list `l`.
|
||||
// Arguments:
|
||||
// x = The value to search for.
|
||||
// l = The list to search.
|
||||
// idx = If given, searches the given subindexes for matches for `x`.
|
||||
// Example:
|
||||
// in_list("bar", ["foo", "bar", "baz"]); // Returns true.
|
||||
// in_list("bee", ["foo", "bar", "baz"]); // Returns false.
|
||||
// in_list("bar", [[2,"foo"], [4,"bar"], [3,"baz"]], idx=1); // Returns true.
|
||||
function in_list(x,l,idx=undef) = search([x], l, num_returns_per_match=1, index_col_num=idx) != [[]];
|
||||
|
||||
|
||||
// Function: slice()
|
||||
// Description:
|
||||
// Returns a slice of a list. The first item is index 0.
|
||||
// Negative indexes are counted back from the end. The last item is -1.
|
||||
// Arguments:
|
||||
// arr = The array/list to get the slice of.
|
||||
// st = The index of the first item to return.
|
||||
// end = The index after the last item to return, unless negative, in which case the last item to return.
|
||||
// Example:
|
||||
// slice([3,4,5,6,7,8,9], 3, 5); // Returns [6,7]
|
||||
// slice([3,4,5,6,7,8,9], 2, -1); // Returns [5,6,7,8,9]
|
||||
// slice([3,4,5,6,7,8,9], 1, 1); // Returns []
|
||||
// slice([3,4,5,6,7,8,9], 6, -1); // Returns [9]
|
||||
// slice([3,4,5,6,7,8,9], 2, -2); // Returns [5,6,7,8]
|
||||
function slice(arr,st,end) = let(
|
||||
s=st<0?(len(arr)+st):st,
|
||||
e=end<0?(len(arr)+end+1):end
|
||||
) (s==e)? [] : [for (i=[s:e-1]) if (e>s) arr[i]];
|
||||
|
||||
|
||||
// Function: select()
|
||||
// Description:
|
||||
// Returns a portion of a list, wrapping around past the beginning, if end<start.
|
||||
// The first item is index 0. Negative indexes are counted back from the end.
|
||||
// The last item is -1. If only the `start` index is given, returns just the value
|
||||
// at that position.
|
||||
// Usage:
|
||||
// select(list,start)
|
||||
// select(list,start,end)
|
||||
// Arguments:
|
||||
// list = The list to get the portion of.
|
||||
// start = The index of the first item.
|
||||
// end = The index of the last item.
|
||||
// Example:
|
||||
// l = [3,4,5,6,7,8,9];
|
||||
// select(l, 5, 6); // Returns [8,9]
|
||||
// select(l, 5, 8); // Returns [8,9,3,4]
|
||||
// select(l, 5, 2); // Returns [8,9,3,4,5]
|
||||
// select(l, -3, -1); // Returns [7,8,9]
|
||||
// select(l, 3, 3); // Returns [6]
|
||||
// select(l, 4); // Returns 7
|
||||
// select(l, -2); // Returns 8
|
||||
// select(l, [1:3]); // Returns [4,5,6]
|
||||
// select(l, [1,3]); // Returns [4,6]
|
||||
function select(list, start, end=undef) =
|
||||
let(l=len(list))
|
||||
(list==[])? [] :
|
||||
end==undef? (
|
||||
is_num(start)?
|
||||
let(s=(start%l+l)%l) list[s] :
|
||||
[for (i=start) list[(i%l+l)%l]]
|
||||
) : (
|
||||
let(s=(start%l+l)%l, e=(end%l+l)%l)
|
||||
(s<=e)?
|
||||
[for (i = [s:e]) list[i]] :
|
||||
concat([for (i = [s:l-1]) list[i]], [for (i = [0:e]) list[i]])
|
||||
);
|
||||
|
||||
|
||||
// Function: list_range()
|
||||
// Usage:
|
||||
// list_range(n, [s], [e], [step])
|
||||
// list_range(e, [step])
|
||||
// list_range(s, e, [step])
|
||||
// Description:
|
||||
// Returns a list, counting up from starting value `s`, by `step` increments,
|
||||
// until either `n` values are in the list, or it reaches the end value `e`.
|
||||
// Arguments:
|
||||
// n = Desired number of values in returned list, if given.
|
||||
// s = Starting value. Default: 0
|
||||
// e = Ending value to stop at, if given.
|
||||
// step = Amount to increment each value. Default: 1
|
||||
// Example:
|
||||
// list_range(4); // Returns [0,1,2,3]
|
||||
// list_range(n=4, step=2); // Returns [0,2,4,6]
|
||||
// list_range(n=4, s=3, step=3); // Returns [3,6,9,12]
|
||||
// list_range(n=4, s=3, e=9, step=3); // Returns [3,6,9]
|
||||
// list_range(e=3); // Returns [0,1,2,3]
|
||||
// list_range(e=6, step=2); // Returns [0,2,4,6]
|
||||
// list_range(s=3, e=5); // Returns [3,4,5]
|
||||
// list_range(s=3, e=8, step=2); // Returns [3,5,7]
|
||||
// list_range(s=4, e=8, step=2); // Returns [4,6,8]
|
||||
// list_range(n=4, s=[3,4], step=[2,3]); // Returns [[3,4], [5,7], [7,10], [9,13]]
|
||||
function list_range(n=undef, s=0, e=undef, step=1) =
|
||||
(n!=undef && e!=undef)? [for (i=[0:n-1]) let(v=s+step*i) if (v<=e) v] :
|
||||
(n!=undef)? [for (i=[0:n-1]) let(v=s+step*i) v] :
|
||||
(e!=undef)? [for (v=[s:step:e]) v] :
|
||||
assert(e!=undef||n!=undef, "Must supply one of `n` or `e`.");
|
||||
|
||||
|
||||
// Function: reverse()
|
||||
// Description: Reverses a list/array.
|
||||
// Arguments:
|
||||
// list = The list to reverse.
|
||||
// Example:
|
||||
// reverse([3,4,5,6]); // Returns [6,5,4,3]
|
||||
function reverse(list) = [ for (i = [len(list)-1 : -1 : 0]) list[i] ];
|
||||
|
||||
|
||||
// Function: list_remove()
|
||||
// Usage:
|
||||
// list_remove(list, elements)
|
||||
// Description:
|
||||
// Remove all items from `list` whose indexes are in `elements`.
|
||||
// Arguments:
|
||||
// list = The list to remove items from.
|
||||
// elements = The list of indexes of items to remove.
|
||||
function list_remove(list, elements) = [
|
||||
for (i = [0:len(list)-1]) if (!search(i, elements)) list[i]
|
||||
];
|
||||
|
||||
|
||||
// Function: list_insert()
|
||||
// Usage:
|
||||
// list_insert(list, pos, elements);
|
||||
// Description:
|
||||
// Insert `elements` into `list` before position `pos`.
|
||||
function list_insert(list, pos, elements) =
|
||||
concat(
|
||||
slice(list,0,pos),
|
||||
elements,
|
||||
(pos<len(list)? slide(list,pos,-1) : [])
|
||||
);
|
||||
|
||||
|
||||
// Function: list_shortest()
|
||||
// Description:
|
||||
// Returns the length of the shortest sublist in a list of lists.
|
||||
// Arguments:
|
||||
// vecs = A list of lists.
|
||||
function list_shortest(vecs) =
|
||||
min([for (v = vecs) len(v)]);
|
||||
|
||||
|
||||
// Function: list_longest()
|
||||
// Description:
|
||||
// Returns the length of the longest sublist in a list of lists.
|
||||
// Arguments:
|
||||
// vecs = A list of lists.
|
||||
function list_longest(vecs) =
|
||||
max([for (v = vecs) len(v)]);
|
||||
|
||||
|
||||
// Function: list_pad()
|
||||
// Description:
|
||||
// If the list `v` is shorter than `minlen` length, pad it to length with the value given in `fill`.
|
||||
// Arguments:
|
||||
// v = A list.
|
||||
// minlen = The minimum length to pad the list to.
|
||||
// fill = The value to pad the list with.
|
||||
function list_pad(v, minlen, fill=undef) =
|
||||
let(l=len(v)) [for (i=[0:max(l,minlen)-1]) i<l? v[i] : fill];
|
||||
|
||||
|
||||
// Function: list_trim()
|
||||
// Description:
|
||||
// If the list `v` is longer than `maxlen` length, truncates it to be `maxlen` items long.
|
||||
// Arguments:
|
||||
// v = A list.
|
||||
// minlen = The minimum length to pad the list to.
|
||||
function list_trim(v, maxlen) =
|
||||
maxlen<1? [] : [for (i=[0:min(len(v),maxlen)-1]) v[i]];
|
||||
|
||||
|
||||
// Function: list_fit()
|
||||
// Description:
|
||||
// If the list `v` is longer than `length` items long, truncates it to be exactly `length` items long.
|
||||
// If the list `v` is shorter than `length` items long, pad it to length with the value given in `fill`.
|
||||
// Arguments:
|
||||
// v = A list.
|
||||
// minlen = The minimum length to pad the list to.
|
||||
// fill = The value to pad the list with.
|
||||
function list_fit(v, length, fill) =
|
||||
let(l=len(v)) (l==length)? v : (l>length)? list_trim(v,length) : list_pad(v,length,fill);
|
||||
|
||||
|
||||
// Function: enumerate()
|
||||
// Description:
|
||||
// Returns a list, with each item of the given list `l` numbered in a sublist.
|
||||
// Something like: `[[0,l[0]], [1,l[1]], [2,l[2]], ...]`
|
||||
// Arguments:
|
||||
// l = List to enumerate.
|
||||
// idx = If given, enumerates just the given subindex items of `l`.
|
||||
// Example:
|
||||
// enumerate(["a","b","c"]); // Returns: [[0,"a"], [1,"b"], [2,"c"]]
|
||||
// enumerate([[88,"a"],[76,"b"],[21,"c"]], idx=1); // Returns: [[0,"a"], [1,"b"], [2,"c"]]
|
||||
// enumerate([["cat","a",12],["dog","b",10],["log","c",14]], idx=[1:2]); // Returns: [[0,"a",12], [1,"b",10], [2,"c",14]]
|
||||
function enumerate(l,idx=undef) =
|
||||
(l==[])? [] :
|
||||
(idx==undef)?
|
||||
[for (i=[0:len(l)-1]) [i,l[i]]] :
|
||||
[for (i=[0:len(l)-1]) concat([i], [for (j=idx) l[i][j]])];
|
||||
|
||||
|
||||
// Function: sort()
|
||||
// Usage:
|
||||
// sort(arr, [idx])
|
||||
// Description:
|
||||
// Sorts the given list using `compare_vals()`. Results are undefined if list elements are not of similar type.
|
||||
// Arguments:
|
||||
// arr = The list to sort.
|
||||
// idx = If given, the index, range, or list of indices of sublist items to compare.
|
||||
// Example:
|
||||
// l = [45,2,16,37,8,3,9,23,89,12,34];
|
||||
// sorted = sort(l); // Returns [2,3,8,9,12,16,23,34,37,45,89]
|
||||
function sort(arr, idx=undef) =
|
||||
(len(arr)<=1) ? arr :
|
||||
let(
|
||||
pivot = arr[floor(len(arr)/2)],
|
||||
pivotval = idx==undef? pivot : [for (i=idx) pivot[i]],
|
||||
compare = [
|
||||
for (entry = arr) let(
|
||||
val = idx==undef? entry : [for (i=idx) entry[i]],
|
||||
cmp = compare_vals(val, pivotval)
|
||||
) cmp
|
||||
],
|
||||
lesser = [ for (i = [0:len(arr)-1]) if (compare[i] < 0) arr[i] ],
|
||||
equal = [ for (i = [0:len(arr)-1]) if (compare[i] ==0) arr[i] ],
|
||||
greater = [ for (i = [0:len(arr)-1]) if (compare[i] > 0) arr[i] ]
|
||||
)
|
||||
concat(sort(lesser,idx), equal, sort(greater,idx));
|
||||
|
||||
|
||||
// Function: sortidx()
|
||||
// Description:
|
||||
// Given a list, calculates the sort order of the list, and returns
|
||||
// a list of indexes into the original list in that sorted order.
|
||||
// If you iterate the returned list in order, and use the list items
|
||||
// to index into the original list, you will be iterating the original
|
||||
// values in sorted order.
|
||||
// Example:
|
||||
// lst = ["d","b","e","c"];
|
||||
// idxs = sortidx(lst); // Returns: [1,3,0,2]
|
||||
// ordered = [for (i=idxs) lst[i]]; // Returns: ["b", "c", "d", "e"]
|
||||
// Example:
|
||||
// lst = [
|
||||
// ["foo", 88, [0,0,1], false],
|
||||
// ["bar", 90, [0,1,0], true],
|
||||
// ["baz", 89, [1,0,0], false],
|
||||
// ["qux", 23, [1,1,1], true]
|
||||
// ];
|
||||
// idxs1 = sortidx(lst, idx=1); // Returns: [3,0,2,1]
|
||||
// idxs2 = sortidx(lst, idx=0); // Returns: [1,2,0,3]
|
||||
// idxs3 = sortidx(lst, idx=[1,3]); // Returns: [3,0,2,1]
|
||||
function sortidx(l, idx=undef) =
|
||||
(l==[])? [] :
|
||||
let(
|
||||
ll=enumerate(l,idx=idx),
|
||||
sidx = [1:len(ll[0])-1]
|
||||
)
|
||||
array_subindex(sort(ll, idx=sidx), 0);
|
||||
|
||||
|
||||
// Function: unique()
|
||||
// Usage:
|
||||
// unique(arr);
|
||||
// Description:
|
||||
// Returns a sorted list with all repeated items removed.
|
||||
// Arguments:
|
||||
// arr = The list to uniquify.
|
||||
function unique(arr) =
|
||||
len(arr)<=1? arr : let(
|
||||
sorted = sort(arr)
|
||||
) [
|
||||
for (i=[0:len(sorted)-1])
|
||||
if (i==0 || (sorted[i] != sorted[i-1]))
|
||||
sorted[i]
|
||||
];
|
||||
|
||||
|
||||
|
||||
// Section: Array Manipulation
|
||||
|
||||
// Function: array_subindex()
|
||||
// Description:
|
||||
// For each array item, return the indexed subitem.
|
||||
// Returns a list of the values of each vector at the specfied
|
||||
// index list or range. If the index list or range has
|
||||
// only one entry the output list is flattened.
|
||||
// Arguments:
|
||||
// v = The given list of lists.
|
||||
// idx = The index, list of indices, or range of indices to fetch.
|
||||
// Example:
|
||||
// v = [[[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]];
|
||||
// array_subindex(v,2); // Returns [3, 7, 11, 15]
|
||||
// array_subindex(v,[2,1]); // Returns [[3, 2], [7, 6], [11, 10], [15, 14]]
|
||||
// array_subindex(v,[1:3]); // Returns [[2, 3, 4], [6, 7, 8], [10, 11, 12], [14, 15, 16]]
|
||||
function array_subindex(v, idx) = [
|
||||
for(val=v) let(value=[for(i=idx) val[i]])
|
||||
len(value)==1 ? value[0] : value
|
||||
];
|
||||
|
||||
|
||||
// Function: array_zip()
|
||||
// Usage:
|
||||
// array_zip(v1, v2, v3, [fit], [fill]);
|
||||
// array_zip(vecs, [fit], [fill]);
|
||||
// Description:
|
||||
// Zips together corresponding items from two or more lists.
|
||||
// Returns a list of lists, where each sublist contains corresponding
|
||||
// items from each of the input lists. `[[A1, B1, C1], [A2, B2, C2], ...]`
|
||||
// Arguments:
|
||||
// vecs = A list of two or more lists to zipper together.
|
||||
// fit = If `fit=="short"`, the zips together up to the length of the shortest list in vecs. If `fit=="long"`, then pads all lists to the length of the longest, using the value in `fill`. If `fit==false`, then requires all lists to be the same length. Default: false.
|
||||
// fill = The default value to fill in with if one or more lists if short. Default: undef
|
||||
// Example:
|
||||
// v1 = [1,2,3,4];
|
||||
// v2 = [5,6,7];
|
||||
// v3 = [8,9,10,11];
|
||||
// array_zip(v1,v3); // returns [[1,8], [2,9], [3,10], [4,11]]
|
||||
// array_zip([v1,v3]); // returns [[1,8], [2,9], [3,10], [4,11]]
|
||||
// array_zip([v1,v2], fit="short"); // returns [[1,5], [2,6], [3,7]]
|
||||
// array_zip([v1,v2], fit="long"); // returns [[1,5], [2,6], [3,7], [4,undef]]
|
||||
// array_zip([v1,v2], fit="long, fill=0); // returns [[1,5], [2,6], [3,7], [4,0]]
|
||||
// array_zip([v1,v2,v3], fit="long"); // returns [[1,5,8], [2,6,9], [3,7,10], [4,undef,11]]
|
||||
// Example:
|
||||
// v1 = [[1,2,3], [4,5,6], [7,8,9]];
|
||||
// v2 = [[20,19,18], [17,16,15], [14,13,12]];
|
||||
// array_zip(v1,v2); // Returns [[1,2,3,20,19,18], [4,5,6,17,16,15], [7,8,9,14,13,12]]
|
||||
function array_zip(vecs, v2, v3, fit=false, fill=undef) =
|
||||
(v3!=undef)? array_zip([vecs,v2,v3], fit=fit, fill=fill) :
|
||||
(v2!=undef)? array_zip([vecs,v2], fit=fit, fill=fill) :
|
||||
let(
|
||||
dummy1 = assert_in_list("fit", fit, [false, "short", "long"]),
|
||||
minlen = list_shortest(vecs),
|
||||
maxlen = list_longest(vecs),
|
||||
dummy2 = (fit==false)? assert(minlen==maxlen, "Input vectors must have the same length") : 0
|
||||
) (fit == "long")?
|
||||
[for(i=[0:maxlen-1]) [for(v=vecs) for(x=(i<len(v)? v[i] : (fill==undef)? [fill] : fill)) x] ] :
|
||||
[for(i=[0:minlen-1]) [for(v=vecs) for(x=v[i]) x] ];
|
||||
|
||||
|
||||
|
||||
// Function: array_group()
|
||||
// Description:
|
||||
// Takes a flat array of values, and groups items in sets of `cnt` length.
|
||||
// The opposite of this is `flatten()`.
|
||||
// Arguments:
|
||||
// v = The list of items to group.
|
||||
// cnt = The number of items to put in each grouping.
|
||||
// dflt = The default value to fill in with is the list is not a multiple of `cnt` items long.
|
||||
// Example:
|
||||
// v = [1,2,3,4,5,6];
|
||||
// array_group(v,2) returns [[1,2], [3,4], [5,6]]
|
||||
// array_group(v,3) returns [[1,2,3], [4,5,6]]
|
||||
// array_group(v,4,0) returns [[1,2,3,4], [5,6,0,0]]
|
||||
function array_group(v, cnt=2, dflt=0) = [for (i = [0:cnt:len(v)-1]) [for (j = [0:cnt-1]) default(v[i+j], dflt)]];
|
||||
|
||||
|
||||
// Function: flatten()
|
||||
// Description: Takes a list of lists and flattens it by one level.
|
||||
// Arguments:
|
||||
// l = List to flatten.
|
||||
// Example:
|
||||
// flatten([[1,2,3], [4,5,[6,7,8]]]) returns [1,2,3,4,5,[6,7,8]]
|
||||
function flatten(l) = [for (a = l) for (b = a) b];
|
||||
|
||||
|
||||
// Internal. Not exposed.
|
||||
function _array_dim_recurse(v) =
|
||||
!is_list(v[0])? (
|
||||
sum( [for(entry=v) is_list(entry) ? 1 : 0]) == 0 ? [] : [undef]
|
||||
) : let(
|
||||
firstlen = len(v[0]),
|
||||
first = sum( [for(entry = v) len(entry) == firstlen ? 0 : 1] ) == 0 ? firstlen : undef,
|
||||
leveldown = flatten(v)
|
||||
) is_list(leveldown[0])? (
|
||||
concat([first],_array_dim_recurse(leveldown))
|
||||
) : [first];
|
||||
|
||||
|
||||
// Function: array_dim()
|
||||
// Usage:
|
||||
// array_dim(v, [depth])
|
||||
// Description:
|
||||
// Returns the size of a multi-dimensional array. Returns a list of
|
||||
// dimension lengths. The length of `v` is the dimension `0`. The
|
||||
// length of the items in `v` is dimension `1`. The length of the
|
||||
// items in the items in `v` is dimension `2`, etc. For each dimension,
|
||||
// if the length of items at that depth is inconsistent, `undef` will
|
||||
// be returned. If no items of that dimension depth exist, `0` is
|
||||
// returned. Otherwise, the consistent length of items in that
|
||||
// dimensional depth is returned.
|
||||
// Arguments:
|
||||
// v = Array to get dimensions of.
|
||||
// depth = Dimension to get size of. If not given, returns a list of dimension lengths.
|
||||
// Examples:
|
||||
// array_dim([[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]]); // Returns [2,2,3]
|
||||
// array_dim([[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]], 0); // Returns 2
|
||||
// array_dim([[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]], 2); // Returns 3
|
||||
// array_dim([[[1,2,3],[4,5,6]],[[7,8,9]]]); // Returns [2,undef,3]
|
||||
function array_dim(v, depth=undef) =
|
||||
(depth == undef)? (
|
||||
concat([len(v)], _array_dim_recurse(v))
|
||||
) : (depth == 0)? (
|
||||
len(v)
|
||||
) : (
|
||||
let(dimlist = _array_dim_recurse(v))
|
||||
(depth > len(dimlist))? 0 : dimlist[depth-1]
|
||||
);
|
||||
|
||||
|
||||
|
||||
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap
|
|
@ -12,7 +12,7 @@
|
|||
/*
|
||||
BSD 2-Clause License
|
||||
|
||||
Copyright (c) 2017, Revar Desmera
|
||||
Copyright (c) 2017-2019, Revar Desmera
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
|
86
compat.scad
86
compat.scad
|
@ -11,7 +11,7 @@
|
|||
/*
|
||||
BSD 2-Clause License
|
||||
|
||||
Copyright (c) 2017, Revar Desmera
|
||||
Copyright (c) 2017-2019, Revar Desmera
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
@ -47,32 +47,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
// Arguments:
|
||||
// v = Value to pass through if not `undef`.
|
||||
// dflt = Value to return if `v` *is* `undef`.
|
||||
function default(v,dflt=undef) = v==undef? dflt : v;
|
||||
|
||||
|
||||
// Function: is_def()
|
||||
// Description: Returns true if given value is not `undef`.
|
||||
function is_def(v) = (version_num() > 20190100)? !is_undef(v) : (v != undef);
|
||||
|
||||
|
||||
// Function: is_str()
|
||||
// Description: Given a value, returns true if it is a string.
|
||||
function is_str(v) = (version_num() > 20190100)? is_string(v) : (v=="" || (is_def(v) && is_def(v[0]) && (len(str(v,v)) == len(v)*2)));
|
||||
|
||||
|
||||
// Function: is_boolean()
|
||||
// Description: Given a value, returns true if it is a boolean.
|
||||
function is_boolean(v) = (version_num() > 20190100)? is_bool(v) : (!is_str(v) && (str(v) == "true" || str(v) == "false"));
|
||||
|
||||
|
||||
// Function: is_scalar()
|
||||
// Description: Given a value, returns true if it is a scalar number.
|
||||
function is_scalar(v) = (version_num() > 20190100)? is_num(v) : (!is_boolean(v) && is_def(v+0));
|
||||
|
||||
|
||||
// Function: is_array()
|
||||
// Description: Given a value, returns true if it is an array/list/vector.
|
||||
function is_array(v) = (version_num() > 20190100)? is_list(v) : (v==[] || (is_def(v[0]) && !is_str(v) ));
|
||||
function default(v,dflt=undef) = is_undef(v)? dflt : v;
|
||||
|
||||
|
||||
// Function: get_radius()
|
||||
|
@ -88,23 +63,18 @@ function is_array(v) = (version_num() > 20190100)? is_list(v) : (v==[] || (is_de
|
|||
// d = Most general diameter.
|
||||
// dflt = Value to return if all other values given are `undef`.
|
||||
function get_radius(r1=undef, r=undef, d1=undef, d=undef, dflt=undef) = (
|
||||
is_def(r1)? r1 :
|
||||
is_def(d1)? d1/2 :
|
||||
is_def(r)? r :
|
||||
is_def(d)? d/2 :
|
||||
!is_undef(r1)? r1 :
|
||||
!is_undef(d1)? d1/2 :
|
||||
!is_undef(r)? r :
|
||||
!is_undef(d)? d/2 :
|
||||
dflt
|
||||
);
|
||||
|
||||
|
||||
// Function: Len()
|
||||
// Description:
|
||||
// Given an array, returns number of items in array. Otherwise returns `undef`.
|
||||
function Len(v) = is_array(v)? len(v) : undef;
|
||||
|
||||
|
||||
// Function: remove_undefs()
|
||||
// Description: Removes all `undef`s from a list.
|
||||
function remove_undefs(v) = [for (x = v) if (is_def(x)) x];
|
||||
function remove_undefs(v) = [for (x = v) if (!is_undef(x)) x];
|
||||
|
||||
|
||||
// Function: first_defined()
|
||||
|
@ -132,16 +102,12 @@ function any_defined(v) = len(remove_undefs(v))>0;
|
|||
// v = Value to return vector from.
|
||||
// dflt = Default value to set empty vector parts from.
|
||||
function scalar_vec3(v, dflt=undef) =
|
||||
!is_def(v)? undef :
|
||||
is_array(v)? [for (i=[0:2]) default(v[i], default(dflt, 0))] :
|
||||
is_def(dflt)? [v,dflt,dflt] : [v,v,v];
|
||||
is_undef(v)? undef :
|
||||
is_list(v)? [for (i=[0:2]) default(v[i], default(dflt, 0))] :
|
||||
!is_undef(dflt)? [v,dflt,dflt] : [v,v,v];
|
||||
|
||||
|
||||
|
||||
// Function: f_echo()
|
||||
// Description: If possible, echo a message from a function.
|
||||
function f_echo(msg) = (version_num() > 20190100)? echo(msg) : 0;
|
||||
|
||||
|
||||
// Section: Modules
|
||||
|
||||
|
@ -162,9 +128,9 @@ module assert_in_list(argname, val, l, idx=undef) {
|
|||
if (!succ) {
|
||||
msg = str(
|
||||
"In argument '", argname, "', ",
|
||||
(is_str(val)? str("\"", val, "\"") : val),
|
||||
(is_string(val)? str("\"", val, "\"") : val),
|
||||
" must be one of ",
|
||||
(is_def(idx)? [for (v=l) v[idx]] : l)
|
||||
(!is_undef(idx)? [for (v=l) v[idx]] : l)
|
||||
);
|
||||
assertion(succ, msg);
|
||||
}
|
||||
|
@ -175,9 +141,9 @@ function assert_in_list(argname, val, l, idx=undef) =
|
|||
succ? 0 : let(
|
||||
msg = str(
|
||||
"In argument '", argname, "', ",
|
||||
(is_str(val)? str("\"", val, "\"") : val),
|
||||
(is_string(val)? str("\"", val, "\"") : val),
|
||||
" must be one of ",
|
||||
(is_def(idx)? [for (v=l) v[idx]] : l)
|
||||
(!is_undef(idx)? [for (v=l) v[idx]] : l)
|
||||
)
|
||||
) assertion(succ, msg);
|
||||
|
||||
|
@ -193,17 +159,13 @@ function assert_in_list(argname, val, l, idx=undef) =
|
|||
// succ = If this is `false`, trigger the assertion.
|
||||
// msg = The message to emit if `succ` is `false`.
|
||||
module assertion(succ, msg) {
|
||||
if (version_num() > 20190100) {
|
||||
// assert() will echo the variable name, and `succ` looks confusing there. So we store it in FAILED.
|
||||
FAILED = succ;
|
||||
assert(FAILED, msg);
|
||||
} else if (!succ) {
|
||||
echo_error(msg);
|
||||
}
|
||||
// assert() will echo the variable name, and `succ` looks confusing there. So we store it in FAILED.
|
||||
FAILED = succ;
|
||||
assert(FAILED, msg);
|
||||
}
|
||||
|
||||
function assertion(succ, msg) =
|
||||
(version_num() > 20190100)? let(FAILED=succ) assert(FAILED, msg) : 0;
|
||||
let(FAILED=succ) assert(FAILED, msg);
|
||||
|
||||
|
||||
// Module: echo_error()
|
||||
|
@ -220,7 +182,7 @@ module echo_error(msg, pfx="ERROR") {
|
|||
}
|
||||
|
||||
function echo_error(msg, pfx="ERROR") =
|
||||
f_echo(str("<p style=\"background-color: #ffb0b0\"><b>", pfx, ":</b> ", msg, "</p>"));
|
||||
echo(str("<p style=\"background-color: #ffb0b0\"><b>", pfx, ":</b> ", msg, "</p>"));
|
||||
|
||||
|
||||
// Module: echo_warning()
|
||||
|
@ -237,7 +199,7 @@ module echo_warning(msg, pfx="WARNING") {
|
|||
}
|
||||
|
||||
function echo_warning(msg, pfx="WARNING") =
|
||||
f_echo(str("<p style=\"background-color: #ffffb0\"><b>", pfx, ":</b> ", msg, "</p>"));
|
||||
echo(str("<p style=\"background-color: #ffffb0\"><b>", pfx, ":</b> ", msg, "</p>"));
|
||||
|
||||
|
||||
// Module: deprecate()
|
||||
|
@ -253,7 +215,7 @@ module deprecate(name, suggest=undef) {
|
|||
echo_warning(pfx="DEPRECATED",
|
||||
str(
|
||||
"`<code>", name, "</code>` is deprecated and should not be used.",
|
||||
!is_def(suggest)? "" : str(
|
||||
is_undef(suggest)? "" : str(
|
||||
" You should use `<code>", suggest, "</code>` instead."
|
||||
)
|
||||
)
|
||||
|
@ -264,7 +226,7 @@ function deprecate(name, suggest=undef) =
|
|||
echo_warning(pfx="DEPRECATED",
|
||||
str(
|
||||
"`<code>", name, "</code>` is deprecated and should not be used.",
|
||||
!is_def(suggest)? "" : str(
|
||||
is_undef(suggest)? "" : str(
|
||||
" You should use `<code>", suggest, "</code>` instead."
|
||||
)
|
||||
)
|
||||
|
@ -286,7 +248,7 @@ module deprecate_argument(name, arg, suggest=undef) {
|
|||
"In `<code>", name, "</code>`, ",
|
||||
"the argument `<code>", arg, "</code>` ",
|
||||
"is deprecated and should not be used.",
|
||||
!is_def(suggest)? "" : str(
|
||||
is_undef(suggest)? "" : str(
|
||||
" You should use `<code>", suggest, "</code>` instead."
|
||||
)
|
||||
));
|
||||
|
@ -297,7 +259,7 @@ function deprecate_argument(name, arg, suggest=undef) =
|
|||
"In `<code>", name, "</code>`, ",
|
||||
"the argument `<code>", arg, "</code>` ",
|
||||
"is deprecated and should not be used.",
|
||||
!is_def(suggest)? "" : str(
|
||||
is_undef(suggest)? "" : str(
|
||||
" You should use `<code>", suggest, "</code>` instead."
|
||||
)
|
||||
));
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
/*
|
||||
BSD 2-Clause License
|
||||
|
||||
Copyright (c) 2017, Revar Desmera
|
||||
Copyright (c) 2017-2019, Revar Desmera
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
|
368
coords.scad
Normal file
368
coords.scad
Normal file
|
@ -0,0 +1,368 @@
|
|||
//////////////////////////////////////////////////////////////////////
|
||||
// LibFile: coords.scad
|
||||
// Coordinate transformations and coordinate system conversions.
|
||||
// To use, add the following lines to the beginning of your file:
|
||||
// ```
|
||||
// use <BOSL2/std.scad>
|
||||
// ```
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
BSD 2-Clause License
|
||||
|
||||
Copyright (c) 2017-2019, Revar Desmera
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
// Section: Coordinate Manipulation
|
||||
|
||||
// Function: point2d()
|
||||
// Description:
|
||||
// Returns a 2D vector/point from a 2D or 3D vector.
|
||||
// If given a 3D point, removes the Z coordinate.
|
||||
// Arguments:
|
||||
// p = The coordinates to force into a 2D vector/point.
|
||||
function point2d(p) = [for (i=[0:1]) (p[i]==undef)? 0 : p[i]];
|
||||
|
||||
|
||||
// Function: path2d()
|
||||
// Description:
|
||||
// Returns a list of 2D vectors/points from a list of 2D or 3D vectors/points.
|
||||
// If given a 3D point list, removes the Z coordinates from each point.
|
||||
// Arguments:
|
||||
// points = A list of 2D or 3D points/vectors.
|
||||
function path2d(points) = [for (point = points) point2d(point)];
|
||||
|
||||
|
||||
// Function: point3d()
|
||||
// Description:
|
||||
// Returns a 3D vector/point from a 2D or 3D vector.
|
||||
// Arguments:
|
||||
// p = The coordinates to force into a 3D vector/point.
|
||||
function point3d(p) = [for (i=[0:2]) (p[i]==undef)? 0 : p[i]];
|
||||
|
||||
|
||||
// Function: path3d()
|
||||
// Description:
|
||||
// Returns a list of 3D vectors/points from a list of 2D or 3D vectors/points.
|
||||
// Arguments:
|
||||
// points = A list of 2D or 3D points/vectors.
|
||||
function path3d(points) = [for (point = points) point3d(point)];
|
||||
|
||||
|
||||
// Function: translate_points()
|
||||
// Usage:
|
||||
// translate_points(pts, v);
|
||||
// Description:
|
||||
// Moves each point in an array by a given amount.
|
||||
// Arguments:
|
||||
// pts = List of points to translate.
|
||||
// v = Amount to translate points by.
|
||||
function translate_points(pts, v=[0,0,0]) = [for (pt = pts) pt+v];
|
||||
|
||||
|
||||
// Function: scale_points()
|
||||
// Usage:
|
||||
// scale_points(pts, v, [cp]);
|
||||
// Description:
|
||||
// Scales each point in an array by a given amount, around a given centerpoint.
|
||||
// Arguments:
|
||||
// pts = List of points to scale.
|
||||
// v = A vector with a scaling factor for each axis.
|
||||
// cp = Centerpoint to scale around.
|
||||
function scale_points(pts, v=[0,0,0], cp=[0,0,0]) = [for (pt = pts) [for (i = [0:len(pt)-1]) (pt[i]-cp[i])*v[i]+cp[i]]];
|
||||
|
||||
|
||||
// Function: rotate_points2d()
|
||||
// Usage:
|
||||
// rotate_points2d(pts, ang, [cp]);
|
||||
// Description:
|
||||
// Rotates each 2D point in an array by a given amount, around an optional centerpoint.
|
||||
// Arguments:
|
||||
// pts = List of 3D points to rotate.
|
||||
// ang = Angle to rotate by.
|
||||
// cp = 2D Centerpoint to rotate around. Default: `[0,0]`
|
||||
function rotate_points2d(pts, ang, cp=[0,0]) = let(
|
||||
m = matrix3_zrot(ang)
|
||||
) [for (pt = pts) m*point3d(pt-cp)+cp];
|
||||
|
||||
|
||||
// Function: rotate_points3d()
|
||||
// Usage:
|
||||
// rotate_points3d(pts, a, [cp], [reverse]);
|
||||
// rotate_points3d(pts, a, v, [cp], [reverse]);
|
||||
// rotate_points3d(pts, from, to, [a], [cp], [reverse]);
|
||||
// Description:
|
||||
// Rotates each 3D point in an array by a given amount, around a given centerpoint.
|
||||
// Arguments:
|
||||
// pts = List of points to rotate.
|
||||
// a = Rotation angle(s) in degrees.
|
||||
// v = If given, axis vector to rotate around.
|
||||
// cp = Centerpoint to rotate around.
|
||||
// from = If given, the vector to rotate something from. Used with `to`.
|
||||
// to = If given, the vector to rotate something to. Used with `from`.
|
||||
// reverse = If true, performs an exactly reversed rotation.
|
||||
function rotate_points3d(pts, a=0, v=undef, cp=[0,0,0], from=undef, to=undef, reverse=false) =
|
||||
assert(is_undef(from)==is_undef(to), "`from` and `to` must be given together.")
|
||||
let(
|
||||
mrot = reverse? (
|
||||
!is_undef(from)? (
|
||||
let (
|
||||
from = from / norm(from),
|
||||
to = to / norm(from),
|
||||
ang = vector_angle(from, to),
|
||||
v = vector_axis(from, to)
|
||||
)
|
||||
matrix4_rot_by_axis(from, -a) * matrix4_rot_by_axis(v, -ang)
|
||||
) : !is_undef(v)? (
|
||||
matrix4_rot_by_axis(v, -a)
|
||||
) : is_num(a)? (
|
||||
matrix4_zrot(-a)
|
||||
) : (
|
||||
matrix4_xrot(-a.x) * matrix4_yrot(-a.y) * matrix4_zrot(-a.z)
|
||||
)
|
||||
) : (
|
||||
!is_undef(from)? (
|
||||
let (
|
||||
from = from / norm(from),
|
||||
to = to / norm(from),
|
||||
ang = vector_angle(from, to),
|
||||
v = vector_axis(from, to)
|
||||
)
|
||||
matrix4_rot_by_axis(v, ang) * matrix4_rot_by_axis(from, a)
|
||||
) : !is_undef(v)? (
|
||||
matrix4_rot_by_axis(v, a)
|
||||
) : is_num(a)? (
|
||||
matrix4_zrot(a)
|
||||
) : (
|
||||
matrix4_zrot(a.z) * matrix4_yrot(a.y) * matrix4_xrot(a.x)
|
||||
)
|
||||
),
|
||||
m = matrix4_translate(cp) * mrot * matrix4_translate(-cp)
|
||||
) [for (pt = pts) point3d(m*concat(point3d(pt),[1]))];
|
||||
|
||||
|
||||
|
||||
// Section: Coordinate Systems
|
||||
|
||||
// Function: polar_to_xy()
|
||||
// Usage:
|
||||
// polar_to_xy(r, theta);
|
||||
// polar_to_xy([r, theta]);
|
||||
// Description:
|
||||
// Convert polar coordinates to 2D cartesian coordinates.
|
||||
// Returns [X,Y] cartesian coordinates.
|
||||
// Arguments:
|
||||
// r = distance from the origin.
|
||||
// theta = angle in degrees, counter-clockwise of X+.
|
||||
// Examples:
|
||||
// xy = polar_to_xy(20,30);
|
||||
// xy = polar_to_xy([40,60]);
|
||||
function polar_to_xy(r,theta=undef) = let(
|
||||
rad = theta==undef? r[0] : r,
|
||||
t = theta==undef? r[1] : theta
|
||||
) rad*[cos(t), sin(t)];
|
||||
|
||||
|
||||
// Function: xy_to_polar()
|
||||
// Usage:
|
||||
// xy_to_polar(x,y);
|
||||
// xy_to_polar([X,Y]);
|
||||
// Description:
|
||||
// Convert 2D cartesian coordinates to polar coordinates.
|
||||
// Returns [radius, theta] where theta is the angle counter-clockwise of X+.
|
||||
// Arguments:
|
||||
// x = X coordinate.
|
||||
// y = Y coordinate.
|
||||
// Examples:
|
||||
// plr = xy_to_polar(20,30);
|
||||
// plr = xy_to_polar([40,60]);
|
||||
function xy_to_polar(x,y=undef) = let(
|
||||
xx = y==undef? x[0] : x,
|
||||
yy = y==undef? x[1] : y
|
||||
) [norm([xx,yy]), atan2(yy,xx)];
|
||||
|
||||
|
||||
// Function: xyz_to_planar()
|
||||
// Usage:
|
||||
// xyz_to_planar(point, a, b, c);
|
||||
// Description:
|
||||
// Given three points defining a plane, returns the projected planar
|
||||
// [X,Y] coordinates of the closest point to a 3D `point`. The origin
|
||||
// of the planar coordinate system [0,0] will be at point `a`, and the
|
||||
// Y+ axis direction will be towards point `b`. This coordinate system
|
||||
// can be useful in taking a set of nearly coplanar points, and converting
|
||||
// them to a pure XY set of coordinates for manipulation, before convering
|
||||
// them back to the original 3D plane.
|
||||
function xyz_to_planar(point, a, b, c) = let(
|
||||
u = normalize(b-a),
|
||||
v = normalize(c-a),
|
||||
n = normalize(cross(u,v)),
|
||||
w = normalize(cross(n,u)),
|
||||
relpoint = point-a
|
||||
) [relpoint * w, relpoint * u];
|
||||
|
||||
|
||||
// Function: planar_to_xyz()
|
||||
// Usage:
|
||||
// planar_to_xyz(point, a, b, c);
|
||||
// Description:
|
||||
// Given three points defining a plane, converts a planar [X,Y]
|
||||
// coordinate to the actual corresponding 3D point on the plane.
|
||||
// The origin of the planar coordinate system [0,0] will be at point
|
||||
// `a`, and the Y+ axis direction will be towards point `b`.
|
||||
function planar_to_xyz(point, a, b, c) = let(
|
||||
u = normalize(b-a),
|
||||
v = normalize(c-a),
|
||||
n = normalize(cross(u,v)),
|
||||
w = normalize(cross(n,u))
|
||||
) a + point.x * w + point.y * u;
|
||||
|
||||
|
||||
// Function: cylindrical_to_xyz()
|
||||
// Usage:
|
||||
// cylindrical_to_xyz(r, theta, z)
|
||||
// cylindrical_to_xyz([r, theta, z])
|
||||
// Description:
|
||||
// Convert cylindrical coordinates to 3D cartesian coordinates. Returns [X,Y,Z] cartesian coordinates.
|
||||
// Arguments:
|
||||
// r = distance from the Z axis.
|
||||
// theta = angle in degrees, counter-clockwise of X+ on the XY plane.
|
||||
// z = Height above XY plane.
|
||||
// Examples:
|
||||
// xyz = cylindrical_to_xyz(20,30,40);
|
||||
// xyz = cylindrical_to_xyz([40,60,50]);
|
||||
function cylindrical_to_xyz(r,theta=undef,z=undef) = let(
|
||||
rad = theta==undef? r[0] : r,
|
||||
t = theta==undef? r[1] : theta,
|
||||
zed = theta==undef? r[2] : z
|
||||
) [rad*cos(t), rad*sin(t), zed];
|
||||
|
||||
|
||||
// Function: xyz_to_cylindrical()
|
||||
// Usage:
|
||||
// xyz_to_cylindrical(x,y,z)
|
||||
// xyz_to_cylindrical([X,Y,Z])
|
||||
// Description:
|
||||
// Convert 3D cartesian coordinates to cylindrical coordinates.
|
||||
// Returns [radius,theta,Z]. Theta is the angle counter-clockwise
|
||||
// of X+ on the XY plane. Z is height above the XY plane.
|
||||
// Arguments:
|
||||
// x = X coordinate.
|
||||
// y = Y coordinate.
|
||||
// z = Z coordinate.
|
||||
// Examples:
|
||||
// cyl = xyz_to_cylindrical(20,30,40);
|
||||
// cyl = xyz_to_cylindrical([40,50,70]);
|
||||
function xyz_to_cylindrical(x,y=undef,z=undef) = let(
|
||||
p = is_num(x)? [x, default(y,0), default(z,0)] : point3d(x)
|
||||
) [norm([p.x,p.y]), atan2(p.y,p.x), p.z];
|
||||
|
||||
|
||||
// Function: spherical_to_xyz()
|
||||
// Usage:
|
||||
// spherical_to_xyz(r, theta, phi);
|
||||
// spherical_to_xyz([r, theta, phi]);
|
||||
// Description:
|
||||
// Convert spherical coordinates to 3D cartesian coordinates.
|
||||
// Returns [X,Y,Z] cartesian coordinates.
|
||||
// Arguments:
|
||||
// r = distance from origin.
|
||||
// theta = angle in degrees, counter-clockwise of X+ on the XY plane.
|
||||
// phi = angle in degrees from the vertical Z+ axis.
|
||||
// Examples:
|
||||
// xyz = spherical_to_xyz(20,30,40);
|
||||
// xyz = spherical_to_xyz([40,60,50]);
|
||||
function spherical_to_xyz(r,theta=undef,phi=undef) = let(
|
||||
rad = theta==undef? r[0] : r,
|
||||
t = theta==undef? r[1] : theta,
|
||||
p = theta==undef? r[2] : phi
|
||||
) rad*[sin(p)*cos(t), sin(p)*sin(t), cos(p)];
|
||||
|
||||
|
||||
// Function: xyz_to_spherical()
|
||||
// Usage:
|
||||
// xyz_to_spherical(x,y,z)
|
||||
// xyz_to_spherical([X,Y,Z])
|
||||
// Description:
|
||||
// Convert 3D cartesian coordinates to spherical coordinates.
|
||||
// Returns [r,theta,phi], where phi is the angle from the Z+ pole,
|
||||
// and theta is degrees counter-clockwise of X+ on the XY plane.
|
||||
// Arguments:
|
||||
// x = X coordinate.
|
||||
// y = Y coordinate.
|
||||
// z = Z coordinate.
|
||||
// Examples:
|
||||
// sph = xyz_to_spherical(20,30,40);
|
||||
// sph = xyz_to_spherical([40,50,70]);
|
||||
function xyz_to_spherical(x,y=undef,z=undef) = let(
|
||||
p = is_num(x)? [x, default(y,0), default(z,0)] : point3d(x)
|
||||
) [norm(p), atan2(p.y,p.x), atan2(norm([p.x,p.y]),p.z)];
|
||||
|
||||
|
||||
// Function: altaz_to_xyz()
|
||||
// Usage:
|
||||
// altaz_to_xyz(alt, az, r);
|
||||
// altaz_to_xyz([alt, az, r]);
|
||||
// Description:
|
||||
// Convert altitude/azimuth/range coordinates to 3D cartesian coordinates.
|
||||
// Returns [X,Y,Z] cartesian coordinates.
|
||||
// Arguments:
|
||||
// alt = altitude angle in degrees above the XY plane.
|
||||
// az = azimuth angle in degrees clockwise of Y+ on the XY plane.
|
||||
// r = distance from origin.
|
||||
// Examples:
|
||||
// xyz = altaz_to_xyz(20,30,40);
|
||||
// xyz = altaz_to_xyz([40,60,50]);
|
||||
function altaz_to_xyz(alt,az=undef,r=undef) = let(
|
||||
p = az==undef? alt[0] : alt,
|
||||
t = 90 - (az==undef? alt[1] : az),
|
||||
rad = az==undef? alt[2] : r
|
||||
) rad*[cos(p)*cos(t), cos(p)*sin(t), sin(p)];
|
||||
|
||||
|
||||
// Function: xyz_to_altaz()
|
||||
// Usage:
|
||||
// xyz_to_altaz(x,y,z);
|
||||
// xyz_to_altaz([X,Y,Z]);
|
||||
// Description:
|
||||
// Convert 3D cartesian coordinates to altitude/azimuth/range coordinates.
|
||||
// Returns [altitude,azimuth,range], where altitude is angle above the
|
||||
// XY plane, azimuth is degrees clockwise of Y+ on the XY plane, and
|
||||
// range is the distance from the origin.
|
||||
// Arguments:
|
||||
// x = X coordinate.
|
||||
// y = Y coordinate.
|
||||
// z = Z coordinate.
|
||||
// Examples:
|
||||
// aa = xyz_to_altaz(20,30,40);
|
||||
// aa = xyz_to_altaz([40,50,70]);
|
||||
function xyz_to_altaz(x,y=undef,z=undef) = let(
|
||||
p = is_num(x)? [x, default(y,0), default(z,0)] : point3d(x)
|
||||
) [atan2(p.z,norm([p.x,p.y])), atan2(p.x,p.y), norm(p)];
|
||||
|
||||
|
||||
|
||||
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap
|
|
@ -13,7 +13,7 @@
|
|||
/*
|
||||
BSD 2-Clause License
|
||||
|
||||
Copyright (c) 2017, Revar Desmera
|
||||
Copyright (c) 2017-2019, Revar Desmera
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
|
306
geometry.scad
Normal file
306
geometry.scad
Normal file
|
@ -0,0 +1,306 @@
|
|||
//////////////////////////////////////////////////////////////////////
|
||||
// LibFile: geometry.scad
|
||||
// Geometry helpers.
|
||||
// To use, add the following lines to the beginning of your file:
|
||||
// ```
|
||||
// use <BOSL2/std.scad>
|
||||
// ```
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
BSD 2-Clause License
|
||||
|
||||
Copyright (c) 2017-2019, Revar Desmera
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
// Section: Lines and Triangles
|
||||
|
||||
// Function: point_on_segment()
|
||||
// Usage:
|
||||
// point_on_segment(point, edge);
|
||||
// Description:
|
||||
// Determine if the point is on the line segment between two points.
|
||||
// Returns true if yes, and false if not.
|
||||
// Arguments:
|
||||
// point = The point to check colinearity of.
|
||||
// edge = Array of two points forming the line segment to test against.
|
||||
function point_on_segment(point, edge) =
|
||||
point==edge[0] || point==edge[1] || // The point is an endpoint
|
||||
sign(edge[0].x-point.x)==sign(point.x-edge[1].x) // point is in between the
|
||||
&& sign(edge[0].y-point.y)==sign(point.y-edge[1].y) // edge endpoints
|
||||
&& point_left_of_segment(point, edge)==0; // and on the line defined by edge
|
||||
|
||||
|
||||
// Function: point_left_of_segment()
|
||||
// Usage:
|
||||
// point_left_of_segment(point, edge);
|
||||
// Description:
|
||||
// Return >0 if point is left of the line defined by edge.
|
||||
// Return =0 if point is on the line.
|
||||
// Return <0 if point is right of the line.
|
||||
// Arguments:
|
||||
// point = The point to check position of.
|
||||
// edge = Array of two points forming the line segment to test against.
|
||||
function point_left_of_segment(point, edge) =
|
||||
(edge[1].x-edge[0].x) * (point.y-edge[0].y) - (point.x-edge[0].x) * (edge[1].y-edge[0].y);
|
||||
|
||||
|
||||
// Internal non-exposed function.
|
||||
function _point_above_below_segment(point, edge) =
|
||||
edge[0].y <= point.y? (
|
||||
(edge[1].y > point.y && point_left_of_segment(point, edge) > 0)? 1 : 0
|
||||
) : (
|
||||
(edge[1].y <= point.y && point_left_of_segment(point, edge) < 0)? -1 : 0
|
||||
);
|
||||
|
||||
|
||||
// Function: right_of_line2d()
|
||||
// Usage:
|
||||
// right_of_line2d(line, pt)
|
||||
// Description:
|
||||
// Returns true if the given point is to the left of the given line.
|
||||
// Arguments:
|
||||
// line = A list of two points.
|
||||
// pt = The point to test.
|
||||
function right_of_line2d(line, pt) =
|
||||
triangle_area2d(line[0], line[1], pt) < 0;
|
||||
|
||||
|
||||
// Function: collinear()
|
||||
// Usage:
|
||||
// collinear(a, b, c, [eps]);
|
||||
// Description:
|
||||
// Returns true if three points are co-linear.
|
||||
// Arguments:
|
||||
// a = First point.
|
||||
// b = Second point.
|
||||
// c = Third point.
|
||||
// eps = Acceptable max angle variance. Default: EPSILON (1e-9) degrees.
|
||||
function collinear(a, b, c, eps=EPSILON) =
|
||||
abs(vector_angle(b-a,c-a)) < eps;
|
||||
|
||||
|
||||
// Function: collinear_indexed()
|
||||
// Usage:
|
||||
// collinear_indexed(points, a, b, c, [eps]);
|
||||
// Description:
|
||||
// Returns true if three points are co-linear.
|
||||
// Arguments:
|
||||
// points = A list of points.
|
||||
// a = Index in `points` of first point.
|
||||
// b = Index in `points` of second point.
|
||||
// c = Index in `points` of third point.
|
||||
// eps = Acceptable max angle variance. Default: EPSILON (1e-9) degrees.
|
||||
function collinear_indexed(points, a, b, c, eps=EPSILON) =
|
||||
let(
|
||||
p1=points[a],
|
||||
p2=points[b],
|
||||
p3=points[c]
|
||||
) abs(vector_angle(p2-p1,p3-p1)) < eps;
|
||||
|
||||
|
||||
// Function: triangle_area2d()
|
||||
// Usage:
|
||||
// triangle_area2d(a,b,c);
|
||||
// Description:
|
||||
// Returns the area of a triangle formed between three vertices.
|
||||
// Result will be negative if the points are in clockwise order.
|
||||
// Examples:
|
||||
// triangle_area2d([0,0], [5,10], [10,0]); // Returns -50
|
||||
// triangle_area2d([10,0], [5,10], [0,0]); // Returns 50
|
||||
function triangle_area2d(a,b,c) =
|
||||
(
|
||||
a.x * (b.y - c.y) +
|
||||
b.x * (c.y - a.y) +
|
||||
c.x * (a.y - b.y)
|
||||
) / 2;
|
||||
|
||||
|
||||
// Section: Planes
|
||||
|
||||
// Function: plane3pt()
|
||||
// Usage:
|
||||
// plane3pt(p1, p2, p3);
|
||||
// Description:
|
||||
// Generates the cartesian equation of a plane from three non-colinear points on the plane.
|
||||
// Returns [A,B,C,D] where Ax+By+Cz+D=0 is the equation of a plane.
|
||||
// Arguments:
|
||||
// p1 = The first point on the plane.
|
||||
// p2 = The second point on the plane.
|
||||
// p3 = The third point on the plane.
|
||||
function plane3pt(p1, p2, p3) =
|
||||
let(normal = normalize(cross(p3-p1, p2-p1))) concat(normal, [normal*p1]);
|
||||
|
||||
|
||||
// Function: plane3pt_indexed()
|
||||
// Usage:
|
||||
// plane3pt_indexed(points, i1, i2, i3);
|
||||
// Description:
|
||||
// Given a list of points, and the indexes of three of those points,
|
||||
// generates the cartesian equation of a plane that those points all
|
||||
// lie on. Requires that the three indexed points be non-collinear.
|
||||
// Returns [A,B,C,D] where Ax+By+Cz+D=0 is the equation of a plane.
|
||||
// Arguments:
|
||||
// points = A list of points.
|
||||
// i1 = The index into `points` of the first point on the plane.
|
||||
// i2 = The index into `points` of the second point on the plane.
|
||||
// i3 = The index into `points` of the third point on the plane.
|
||||
function plane3pt_indexed(points, i1, i2, i3) =
|
||||
let(
|
||||
p1 = points[i1],
|
||||
p2 = points[i2],
|
||||
p3 = points[i3],
|
||||
normal = normalize(cross(p3-p1, p2-p1))
|
||||
) concat(normal, [normal*p1]);
|
||||
|
||||
|
||||
// Function: distance_from_plane()
|
||||
// Usage:
|
||||
// distance_from_plane(plane, point)
|
||||
// Description:
|
||||
// Given a plane as [A,B,C,D] where the cartesian equation for that plane
|
||||
// is Ax+By+Cz+D=0, determines how far from that plane the given point is.
|
||||
// The returned distance will be positive if the point is in front of the
|
||||
// plane; on the same side of the plane as the normal of that plane points
|
||||
// towards. If the point is behind the plane, then the distance returned
|
||||
// will be negative. The normal of the plane is the same as [A,B,C].
|
||||
// Arguments:
|
||||
// plane = The [A,B,C,D] values for the equation of the plane.
|
||||
// point = The point to test.
|
||||
function distance_from_plane(plane, point) =
|
||||
[plane.x, plane.y, plane.z] * point - plane[3];
|
||||
|
||||
|
||||
// Function: coplanar()
|
||||
// Usage:
|
||||
// coplanar(plane, point);
|
||||
// Description:
|
||||
// Given a plane as [A,B,C,D] where the cartesian equation for that plane
|
||||
// is Ax+By+Cz+D=0, determines if the given point is on that plane.
|
||||
// Returns true if the point is on that plane.
|
||||
// Arguments:
|
||||
// plane = The [A,B,C,D] values for the equation of the plane.
|
||||
// point = The point to test.
|
||||
function coplanar(plane, point) =
|
||||
abs(distance_from_plane(plane, point)) <= EPSILON;
|
||||
|
||||
|
||||
// Function: in_front_of_plane()
|
||||
// Usage:
|
||||
// in_front_of_plane(plane, point);
|
||||
// Description:
|
||||
// Given a plane as [A,B,C,D] where the cartesian equation for that plane
|
||||
// is Ax+By+Cz+D=0, determines if the given point is on the side of that
|
||||
// plane that the normal points towards. The normal of the plane is the
|
||||
// same as [A,B,C].
|
||||
// Arguments:
|
||||
// plane = The [A,B,C,D] values for the equation of the plane.
|
||||
// point = The point to test.
|
||||
function in_front_of_plane(plane, point) =
|
||||
distance_from_plane(plane, point) > EPSILON;
|
||||
|
||||
|
||||
|
||||
// Section: Paths and Polygons
|
||||
|
||||
|
||||
// Function: simplify_path()
|
||||
// Description:
|
||||
// Takes a path and removes unnecessary collinear points.
|
||||
// Usage:
|
||||
// simplify_path(path, [eps])
|
||||
// Arguments:
|
||||
// path = A list of 2D path points.
|
||||
// eps = Largest angle variance allowed. Default: EPSILON (1-e9) degrees.
|
||||
function simplify_path(path, eps=EPSILON, _a=0, _b=2, _acc=[]) =
|
||||
(_b >= len(path))? concat([path[0]], _acc, [path[len(path)-1]]) :
|
||||
simplify_path(
|
||||
path, eps,
|
||||
(collinear_indexed(path, _a, _b-1, _b, eps=eps)? _a : _b-1),
|
||||
_b+1,
|
||||
(collinear_indexed(path, _a, _b-1, _b, eps=eps)? _acc : concat(_acc, [path[_b-1]]))
|
||||
);
|
||||
|
||||
|
||||
// Function: simplify_path_indexed()
|
||||
// Description:
|
||||
// Takes a list of points, and a path as a list of indexes into `points`,
|
||||
// and removes all path points that are unecessarily collinear.
|
||||
// Usage:
|
||||
// simplify_path_indexed(path, eps)
|
||||
// Arguments:
|
||||
// points = A list of points.
|
||||
// path = A list of indexes into `points` that forms a path.
|
||||
// eps = Largest angle variance allowed. Default: EPSILON (1-e9) degrees.
|
||||
function simplify_path_indexed(points, path, eps=EPSILON, _a=0, _b=2, _acc=[]) =
|
||||
(_b >= len(path))? concat([path[0]], _acc, [path[len(path)-1]]) :
|
||||
simplify_path_indexed(
|
||||
points, path, eps,
|
||||
(collinear_indexed(points, path[_a], path[_b-1], path[_b], eps=eps)? _a : _b-1),
|
||||
_b+1,
|
||||
(collinear_indexed(points, path[_a], path[_b-1], path[_b], eps=eps)? _acc : concat(_acc, [path[_b-1]]))
|
||||
);
|
||||
|
||||
|
||||
// Function: point_in_polygon()
|
||||
// Usage:
|
||||
// point_in_polygon(point, path)
|
||||
// Description:
|
||||
// This function tests whether the given point is inside, outside or on the boundary of
|
||||
// the specified polygon using the Winding Number method. (http://geomalgorithms.com/a03-_inclusion.html)
|
||||
// The polygon is given as a list of points, not including the repeated end point.
|
||||
// Returns -1 if the point is outside the polyon.
|
||||
// Returns 0 if the point is on the boundary.
|
||||
// Returns 1 if the point lies in the interior.
|
||||
// The polygon does not need to be simple: it can have self-intersections.
|
||||
// But the polygon cannot have holes (it must be simply connected).
|
||||
// Rounding error may give mixed results for points on or near the boundary.
|
||||
// Arguments:
|
||||
// point = The point to check position of.
|
||||
// path = The list of 2D path points forming the perimeter of the polygon.
|
||||
function point_in_polygon(point, path) =
|
||||
// Does the point lie on any edges? If so return 0.
|
||||
sum([for(i=[0:len(path)-1]) point_on_segment(point, select(path, i, i+1))?1:0])>0 ? 0 :
|
||||
// Otherwise compute winding number and return 1 for interior, -1 for exterior
|
||||
sum([for(i=[0:len(path)-1]) _point_above_below_segment(point, select(path, i, i+1))]) != 0 ? 1 : -1;
|
||||
|
||||
|
||||
// Function: pointlist_bounds()
|
||||
// Usage:
|
||||
// pointlist_bounds(pts);
|
||||
// Description:
|
||||
// Finds the bounds containing all the points in pts.
|
||||
// Returns [[minx, miny, minz], [maxx, maxy, maxz]]
|
||||
// Arguments:
|
||||
// pts = List of points.
|
||||
function pointlist_bounds(pts) = [
|
||||
[for (a=[0:2]) min([ for (x=pts) point3d(x)[a] ]) ],
|
||||
[for (a=[0:2]) max([ for (x=pts) point3d(x)[a] ]) ]
|
||||
];
|
||||
|
||||
|
||||
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap
|
|
@ -311,7 +311,7 @@ module gear(
|
|||
c = outer_radius(mm_per_tooth, number_of_teeth, clearance, interior);
|
||||
r = root_radius(mm_per_tooth, number_of_teeth, clearance, interior);
|
||||
p2 = p - (thickness*tan(bevelang));
|
||||
orient_and_align([p, p, thickness], orient, align) {
|
||||
orient_and_align([p, p, thickness], orient, align, chain=true) {
|
||||
difference() {
|
||||
linear_extrude(height=thickness, center=true, convexity=10, twist=twist, scale=p2/p, slices=slices) {
|
||||
gear2d(
|
||||
|
@ -338,6 +338,7 @@ module gear(
|
|||
}
|
||||
}
|
||||
}
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -373,7 +374,7 @@ module rack(
|
|||
d = dedendum(mm_per_tooth, clearance);
|
||||
xa = a * sin(pressure_angle);
|
||||
xd = d * sin(pressure_angle);
|
||||
orient_and_align([(number_of_teeth-1)*mm_per_tooth, height, thickness], orient, align, orig_orient=ORIENT_X) {
|
||||
orient_and_align([(number_of_teeth-1)*mm_per_tooth, height, thickness], orient, align, orig_orient=ORIENT_X, chain=true) {
|
||||
left((number_of_teeth-1)*mm_per_tooth/2) {
|
||||
linear_extrude(height = thickness, center = true, convexity = 10) {
|
||||
for (i = [0:number_of_teeth-1] ) {
|
||||
|
@ -394,6 +395,7 @@ module rack(
|
|||
}
|
||||
}
|
||||
}
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
63
joiners.scad
63
joiners.scad
|
@ -11,7 +11,7 @@
|
|||
/*
|
||||
BSD 2-Clause License
|
||||
|
||||
Copyright (c) 2017, Revar Desmera
|
||||
Copyright (c) 2017-2019, Revar Desmera
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
@ -63,20 +63,23 @@ module half_joiner_clear(h=20, w=10, a=30, clearance=0, overlap=0.01, orient=ORI
|
|||
guide_size = w/3;
|
||||
guide_width = 2*(dmnd_height/2-guide_size)*tan(a);
|
||||
|
||||
orient_and_align([w, guide_width, h], orient, align, orig_orient=ORIENT_Y) {
|
||||
yspread(overlap, n=overlap>0? 2 : 1) {
|
||||
difference() {
|
||||
// Diamonds.
|
||||
scale([w+clearance, dmnd_width/2, dmnd_height/2]) {
|
||||
xrot(45) cube(size=[1,sqrt(2),sqrt(2)], center=true);
|
||||
}
|
||||
// Blunt point of tab.
|
||||
yspread(guide_width+4) {
|
||||
cube(size=[(w+clearance)*1.05, 4, h*0.99], center=true);
|
||||
orient_and_align([w, guide_width, h], orient, align, orig_orient=ORIENT_Y, chain=true) {
|
||||
union() {
|
||||
yspread(overlap, n=overlap>0? 2 : 1) {
|
||||
difference() {
|
||||
// Diamonds.
|
||||
scale([w+clearance, dmnd_width/2, dmnd_height/2]) {
|
||||
xrot(45) cube(size=[1,sqrt(2),sqrt(2)], center=true);
|
||||
}
|
||||
// Blunt point of tab.
|
||||
yspread(guide_width+4) {
|
||||
cube(size=[(w+clearance)*1.05, 4, h*0.99], center=true);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (overlap>0) cube([w+clearance, overlap+0.001, h], center=true);
|
||||
}
|
||||
if (overlap>0) cube([w+clearance, overlap+0.001, h], center=true);
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -113,7 +116,7 @@ module half_joiner(h=20, w=10, l=10, a=30, screwsize=undef, guides=true, slop=PR
|
|||
}
|
||||
}
|
||||
render(convexity=12)
|
||||
orient_and_align([w, 2*l, h], orient, align, orig_orient=ORIENT_Y) {
|
||||
orient_and_align([w, 2*l, h], orient, align, orig_orient=ORIENT_Y, chain=true) {
|
||||
difference() {
|
||||
union() {
|
||||
// Make base.
|
||||
|
@ -159,6 +162,7 @@ module half_joiner(h=20, w=10, l=10, a=30, screwsize=undef, guides=true, slop=PR
|
|||
yrot(90) cylinder(r=screwsize*1.1/2, h=w+1, center=true, $fn=12);
|
||||
}
|
||||
}
|
||||
children();
|
||||
}
|
||||
}
|
||||
//half_joiner(screwsize=3, orient=ORIENT_Z, align=UP);
|
||||
|
@ -196,7 +200,7 @@ module half_joiner2(h=20, w=10, l=10, a=30, screwsize=undef, guides=true, orient
|
|||
}
|
||||
|
||||
render(convexity=12)
|
||||
orient_and_align([w, 2*l, h], orient, align, orig_orient=ORIENT_Y) {
|
||||
orient_and_align([w, 2*l, h], orient, align, orig_orient=ORIENT_Y, chain=true) {
|
||||
difference() {
|
||||
union () {
|
||||
fwd(l/2) cube(size=[w, l, h], center=true);
|
||||
|
@ -211,6 +215,7 @@ module half_joiner2(h=20, w=10, l=10, a=30, screwsize=undef, guides=true, orient
|
|||
xcyl(r=screwsize*1.1/2, l=w+1, $fn=12);
|
||||
}
|
||||
}
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -241,9 +246,12 @@ module joiner_clear(h=40, w=10, a=30, clearance=0, overlap=0.01, orient=ORIENT_Y
|
|||
guide_size = w/3;
|
||||
guide_width = 2*(dmnd_height/2-guide_size)*tan(a);
|
||||
|
||||
orient_and_align([w, guide_width, h], orient, align, orig_orient=ORIENT_Y) {
|
||||
up(h/4) half_joiner_clear(h=h/2.0-0.01, w=w, a=a, overlap=overlap, clearance=clearance);
|
||||
down(h/4) half_joiner_clear(h=h/2.0-0.01, w=w, a=a, overlap=overlap, clearance=-0.01);
|
||||
orient_and_align([w, guide_width, h], orient, align, orig_orient=ORIENT_Y, chain=true) {
|
||||
union() {
|
||||
up(h/4) half_joiner_clear(h=h/2.0-0.01, w=w, a=a, overlap=overlap, clearance=clearance);
|
||||
down(h/4) half_joiner_clear(h=h/2.0-0.01, w=w, a=a, overlap=overlap, clearance=-0.01);
|
||||
}
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -275,9 +283,12 @@ module joiner(h=40, w=10, l=10, a=30, screwsize=undef, guides=true, slop=PRINTER
|
|||
joiner_clear(h=h, w=w, a=a, clearance=0.1, orient=orient, align=align);
|
||||
}
|
||||
}
|
||||
orient_and_align([w, 2*l, h], orient, align, orig_orient=ORIENT_Y) {
|
||||
up(h/4) half_joiner(h=h/2, w=w, l=l, a=a, screwsize=screwsize, guides=guides, slop=slop);
|
||||
down(h/4) half_joiner2(h=h/2, w=w, l=l, a=a, screwsize=screwsize, guides=guides);
|
||||
orient_and_align([w, 2*l, h], orient, align, orig_orient=ORIENT_Y, chain=true) {
|
||||
union() {
|
||||
up(h/4) half_joiner(h=h/2, w=w, l=l, a=a, screwsize=screwsize, guides=guides, slop=slop);
|
||||
down(h/4) half_joiner2(h=h/2, w=w, l=l, a=a, screwsize=screwsize, guides=guides);
|
||||
}
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -311,10 +322,11 @@ module joiner_pair_clear(spacing=100, h=40, w=10, a=30, n=2, clearance=0, overla
|
|||
guide_size = w/3;
|
||||
guide_width = 2*(dmnd_height/2-guide_size)*tan(a);
|
||||
|
||||
orient_and_align([spacing+w, guide_width, h], orient, align, orig_orient=ORIENT_Y) {
|
||||
orient_and_align([spacing+w, guide_width, h], orient, align, orig_orient=ORIENT_Y, chain=true) {
|
||||
xspread(spacing, n=n) {
|
||||
joiner_clear(h=h, w=w, a=a, clearance=clearance, overlap=overlap);
|
||||
}
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -352,7 +364,7 @@ module joiner_pair(spacing=100, h=40, w=10, l=10, a=30, n=2, alternate=true, scr
|
|||
joiner_pair_clear(spacing=spacing, h=h, w=w, a=a, clearance=0.1, orient=orient, align=align);
|
||||
}
|
||||
}
|
||||
orient_and_align([spacing+w, 2*l, h], orient, align, orig_orient=ORIENT_Y) {
|
||||
orient_and_align([spacing+w, 2*l, h], orient, align, orig_orient=ORIENT_Y, chain=true) {
|
||||
left((n-1)*spacing/2) {
|
||||
for (i=[0:n-1]) {
|
||||
right(i*spacing) {
|
||||
|
@ -362,6 +374,7 @@ module joiner_pair(spacing=100, h=40, w=10, l=10, a=30, n=2, alternate=true, scr
|
|||
}
|
||||
}
|
||||
}
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -393,12 +406,13 @@ module joiner_quad_clear(xspacing=undef, yspacing=undef, spacing1=undef, spacing
|
|||
{
|
||||
spacing1 = first_defined([spacing1, xspacing, 100]);
|
||||
spacing2 = first_defined([spacing2, yspacing, 50]);
|
||||
orient_and_align([w+spacing1, spacing2, h], orient, align, orig_orient=ORIENT_Y) {
|
||||
orient_and_align([w+spacing1, spacing2, h], orient, align, orig_orient=ORIENT_Y, chain=true) {
|
||||
zrot_copies(n=2) {
|
||||
back(spacing2/2) {
|
||||
joiner_pair_clear(spacing=spacing1, n=n, h=h, w=w, a=a, clearance=clearance, overlap=overlap);
|
||||
}
|
||||
}
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -438,12 +452,13 @@ module joiner_quad(spacing1=undef, spacing2=undef, xspacing=undef, yspacing=unde
|
|||
joiner_quad_clear(spacing1=spacing1, spacing2=spacing2, h=h, w=w, a=a, clearance=0.1, orient=orient, align=align);
|
||||
}
|
||||
}
|
||||
orient_and_align([w+spacing1, spacing2, h], orient, align, orig_orient=ORIENT_Y) {
|
||||
orient_and_align([w+spacing1, spacing2, h], orient, align, orig_orient=ORIENT_Y, chain=true) {
|
||||
zrot_copies(n=2) {
|
||||
back(spacing2/2) {
|
||||
joiner_pair(spacing=spacing1, n=n, h=h, w=w, l=l, a=a, screwsize=screwsize, guides=guides, slop=slop);
|
||||
}
|
||||
}
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
/*
|
||||
BSD 2-Clause License
|
||||
|
||||
Copyright (c) 2017, Revar Desmera
|
||||
Copyright (c) 2017-2019, Revar Desmera
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
@ -111,7 +111,7 @@ module linear_bearing_housing(d=15, l=24, tab=7, gap=5, wall=3, tabwall=5, screw
|
|||
od = d+2*wall;
|
||||
ogap = gap+2*tabwall;
|
||||
tabh = tab/2+od/2*sqrt(2)-ogap/2;
|
||||
orient_and_align([l, od, od], orient, align, orig_orient=ORIENT_X) {
|
||||
orient_and_align([l, od, od], orient, align, orig_orient=ORIENT_X, chain=true) {
|
||||
difference() {
|
||||
union() {
|
||||
zrot(90) teardrop(r=od/2,h=l);
|
||||
|
@ -128,6 +128,7 @@ module linear_bearing_housing(d=15, l=24, tab=7, gap=5, wall=3, tabwall=5, screw
|
|||
xrot(90) metric_nut(size=screwsize, hole=false);
|
||||
}
|
||||
}
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -150,7 +151,7 @@ module lmXuu_housing(size=8, tab=7, gap=5, wall=3, tabwall=5, screwsize=3, orien
|
|||
{
|
||||
d = get_lmXuu_bearing_diam(size);
|
||||
l = get_lmXuu_bearing_length(size);
|
||||
linear_bearing_housing(d=d,l=l,tab=tab,gap=gap,wall=wall,tabwall=tabwall,screwsize=screwsize, orient=orient, align=align);
|
||||
linear_bearing_housing(d=d,l=l,tab=tab,gap=gap,wall=wall,tabwall=tabwall,screwsize=screwsize, orient=orient, align=align) children();
|
||||
}
|
||||
|
||||
|
||||
|
|
173
masks.scad
173
masks.scad
|
@ -10,7 +10,7 @@
|
|||
/*
|
||||
BSD 2-Clause License
|
||||
|
||||
Copyright (c) 2017, Revar Desmera
|
||||
Copyright (c) 2017-2019, Revar Desmera
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
@ -41,8 +41,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
// Module: angle_pie_mask()
|
||||
// Usage:
|
||||
// angle_pie_mask(r|d, l, ang, [orient], [align], [center]);
|
||||
// angle_pie_mask(r1|d1, r2|d2, l, ang, [orient], [align], [center]);
|
||||
// angle_pie_mask(r|d, l, ang, [orient], [align]);
|
||||
// angle_pie_mask(r1|d1, r2|d2, l, ang, [orient], [align]);
|
||||
// Description:
|
||||
// Creates a pie wedge shape that can be used to mask other shapes.
|
||||
// Arguments:
|
||||
|
@ -56,7 +56,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
// d2 = Upper diameter of cone that wedge is created from. (optional)
|
||||
// orient = Orientation of the pie slice. Use the ORIENT_ constants from constants.h. Default: ORIENT_Z.
|
||||
// align = Alignment of the pie slice. Use the constants from constants.h. Default: CENTER.
|
||||
// center = If true, centers vertically. If false, lift up to sit on top of the XY plane. Overrides `align`.
|
||||
// Example(FR):
|
||||
// angle_pie_mask(ang=30, d=100, l=20);
|
||||
module angle_pie_mask(
|
||||
|
@ -64,13 +63,14 @@ module angle_pie_mask(
|
|||
r=undef, r1=undef, r2=undef,
|
||||
d=undef, d1=undef, d2=undef,
|
||||
orient=ORIENT_Z, align=CENTER,
|
||||
h=undef, center=undef
|
||||
h=undef
|
||||
) {
|
||||
l = first_defined([l, h, 1]);
|
||||
r1 = get_radius(r1, r, d1, d, 10);
|
||||
r2 = get_radius(r2, r, d2, d, 10);
|
||||
orient_and_align([2*r1, 2*r1, l], orient, align, center=center) {
|
||||
orient_and_align([2*r1, 2*r1, l], orient, align, chain=true) {
|
||||
pie_slice(ang=ang, l=l+0.1, r1=r1, r2=r2, align=CENTER);
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -152,7 +152,7 @@ module cylinder_mask(
|
|||
cylinder_mask(l=l, r1=sc*r1, r2=sc*r2, chamfer1=cham1, chamfer2=cham2, chamfang1=ang1, chamfang2=ang2, fillet1=fil1, fillet2=fil2, orient=orient, from_end=from_end);
|
||||
}
|
||||
} else {
|
||||
orient_and_align([2*r1, 2*r1, l], orient, align) {
|
||||
orient_and_align([2*r1, 2*r1, l], orient, align, chain=true) {
|
||||
difference() {
|
||||
union() {
|
||||
chlen1 = cham1 / (from_end? 1 : tan(ang1));
|
||||
|
@ -168,6 +168,7 @@ module cylinder_mask(
|
|||
}
|
||||
cyl(r1=sc*r1, r2=sc*r2, l=l, chamfer1=cham1, chamfer2=cham2, chamfang1=ang1, chamfang2=ang2, from_end=from_end, fillet1=fil1, fillet2=fil2);
|
||||
}
|
||||
children();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -179,7 +180,7 @@ module cylinder_mask(
|
|||
|
||||
// Module: chamfer_mask()
|
||||
// Usage:
|
||||
// chamfer_mask(l, chamfer, [orient], [align], [center]);
|
||||
// chamfer_mask(l, chamfer, [orient], [align]);
|
||||
// Description:
|
||||
// Creates a shape that can be used to chamfer a 90 degree edge.
|
||||
// Difference it from the object to be chamfered. The center of
|
||||
|
@ -189,15 +190,15 @@ module cylinder_mask(
|
|||
// chamfer = Size of chamfer
|
||||
// orient = Orientation of the mask. Use the `ORIENT_` constants from `constants.h`. Default: vertical.
|
||||
// align = Alignment of the mask. Use the constants from `constants.h`. Default: centered.
|
||||
// center = If true, centers vertically. If false, lift up to sit on top of the XY plane. Overrides `align`.
|
||||
// Example:
|
||||
// difference() {
|
||||
// cube(50);
|
||||
// #chamfer_mask(l=50, chamfer=10, orient=ORIENT_X, align=RIGHT);
|
||||
// }
|
||||
module chamfer_mask(l=1, chamfer=1, orient=ORIENT_Z, align=CENTER, center=undef) {
|
||||
orient_and_align([chamfer, chamfer, l], orient, align, center=center) {
|
||||
module chamfer_mask(l=1, chamfer=1, orient=ORIENT_Z, align=CENTER) {
|
||||
orient_and_align([chamfer, chamfer, l], orient, align, chain=true) {
|
||||
cylinder(d=chamfer*2, h=l+0.1, center=true, $fn=4);
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -219,7 +220,7 @@ module chamfer_mask(l=1, chamfer=1, orient=ORIENT_Z, align=CENTER, center=undef)
|
|||
// #chamfer_mask_x(l=80, chamfer=20);
|
||||
// }
|
||||
module chamfer_mask_x(l=1.0, chamfer=1.0, align=CENTER) {
|
||||
chamfer_mask(l=l, chamfer=chamfer, orient=ORIENT_X, align=align);
|
||||
chamfer_mask(l=l, chamfer=chamfer, orient=ORIENT_X, align=align) children();
|
||||
}
|
||||
|
||||
|
||||
|
@ -240,7 +241,7 @@ module chamfer_mask_x(l=1.0, chamfer=1.0, align=CENTER) {
|
|||
// right(80) #chamfer_mask_y(l=80, chamfer=20);
|
||||
// }
|
||||
module chamfer_mask_y(l=1.0, chamfer=1.0, align=CENTER) {
|
||||
chamfer_mask(l=l, chamfer=chamfer, orient=ORIENT_Y, align=align);
|
||||
chamfer_mask(l=l, chamfer=chamfer, orient=ORIENT_Y, align=align) children();
|
||||
}
|
||||
|
||||
|
||||
|
@ -261,7 +262,7 @@ module chamfer_mask_y(l=1.0, chamfer=1.0, align=CENTER) {
|
|||
// #chamfer_mask_z(l=80, chamfer=20);
|
||||
// }
|
||||
module chamfer_mask_z(l=1.0, chamfer=1.0, align=CENTER) {
|
||||
chamfer_mask(l=l, chamfer=chamfer, orient=ORIENT_Z, align=align);
|
||||
chamfer_mask(l=l, chamfer=chamfer, orient=ORIENT_Z, align=align) children();
|
||||
}
|
||||
|
||||
|
||||
|
@ -325,7 +326,7 @@ module chamfer(chamfer=1, size=[1,1,1], edges=EDGES_ALL)
|
|||
module chamfer_cylinder_mask(r=1.0, d=undef, chamfer=0.25, ang=45, from_end=false, orient=ORIENT_Z)
|
||||
{
|
||||
r = get_radius(r=r, d=d, dflt=1);
|
||||
rot(orient) cylinder_mask(l=chamfer*3, r=r, chamfer2=chamfer, chamfang2=ang, from_end=from_end, ends_only=true, align=DOWN);
|
||||
rot(orient) cylinder_mask(l=chamfer*3, r=r, chamfer2=chamfer, chamfang2=ang, from_end=from_end, ends_only=true, align=DOWN) children();
|
||||
}
|
||||
|
||||
|
||||
|
@ -343,6 +344,8 @@ module chamfer_cylinder_mask(r=1.0, d=undef, chamfer=0.25, ang=45, from_end=fals
|
|||
// ang = Angle of chamfer in degrees from vertical. (Default: 45)
|
||||
// from_end = If true, chamfer size is measured from end of hole. If false, chamfer is measured outset from the radius of the hole. (Default: false)
|
||||
// overage = The extra thickness of the mask. Default: `0.1`.
|
||||
// orient = Orientation of the mask. Use the `ORIENT_` constants from `constants.h`. Default: `ORIENT_Z`.
|
||||
// align = Alignment of the mask. Use the constants from `constants.h`. Default: `CENTER`.
|
||||
// Example:
|
||||
// difference() {
|
||||
// cube(100, center=true);
|
||||
|
@ -351,18 +354,18 @@ module chamfer_cylinder_mask(r=1.0, d=undef, chamfer=0.25, ang=45, from_end=fals
|
|||
// }
|
||||
// Example:
|
||||
// chamfer_hole_mask(d=100, chamfer=25, ang=30, overage=10);
|
||||
module chamfer_hole_mask(r=undef, d=undef, chamfer=0.25, ang=45, from_end=false, overage=0.1)
|
||||
module chamfer_hole_mask(r=undef, d=undef, chamfer=0.25, ang=45, from_end=false, overage=0.1, orient=ORIENT_Z, align=CENTER)
|
||||
{
|
||||
r = get_radius(r=r, d=d, dflt=1);
|
||||
h = chamfer * (from_end? 1 : tan(90-ang));
|
||||
r2 = r + chamfer * (from_end? tan(ang) : 1);
|
||||
$fn = segs(r);
|
||||
difference() {
|
||||
orient_and_align([2*r, 2*r, h*2], orient, align, size2=[2*r2, 2*r2], chain=true) {
|
||||
union() {
|
||||
cylinder(r=r2, h=overage, center=false);
|
||||
down(h) cylinder(r1=r, r2=r2, h=h, center=false);
|
||||
}
|
||||
cylinder(r=r-overage, h=h*2.1+overage, center=true);
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -372,7 +375,7 @@ module chamfer_hole_mask(r=undef, d=undef, chamfer=0.25, ang=45, from_end=false,
|
|||
|
||||
// Module: fillet_mask()
|
||||
// Usage:
|
||||
// fillet_mask(l|h, r, [orient], [align], [center])
|
||||
// fillet_mask(l|h, r, [orient], [align])
|
||||
// Description:
|
||||
// Creates a shape that can be used to fillet a vertical 90 degree edge.
|
||||
// Difference it from the object to be filletted. The center of the mask
|
||||
|
@ -382,30 +385,30 @@ module chamfer_hole_mask(r=undef, d=undef, chamfer=0.25, ang=45, from_end=false,
|
|||
// r = Radius of the fillet.
|
||||
// orient = Orientation of the mask. Use the `ORIENT_` constants from `constants.h`. Default: vertical.
|
||||
// align = Alignment of the mask. Use the constants from `constants.h`. Default: centered.
|
||||
// center = If true, centers vertically. If false, lift up to sit on top of the XY plane. Overrides `align`.
|
||||
// Example:
|
||||
// difference() {
|
||||
// cube(size=100, center=false);
|
||||
// #fillet_mask(l=100, r=25, orient=ORIENT_Z, align=UP);
|
||||
// }
|
||||
module fillet_mask(l=undef, r=1.0, orient=ORIENT_Z, align=CENTER, h=undef, center=undef)
|
||||
module fillet_mask(l=undef, r=1.0, orient=ORIENT_Z, align=CENTER, h=undef)
|
||||
{
|
||||
l = first_defined([l, h, 1]);
|
||||
sides = quantup(segs(r),4);
|
||||
orient_and_align([2*r, 2*r, l], orient, align, center=center) {
|
||||
orient_and_align([2*r, 2*r, l], orient, align, chain=true) {
|
||||
linear_extrude(height=l+0.1, convexity=4, center=true) {
|
||||
difference() {
|
||||
square(2*r, center=true);
|
||||
xspread(2*r) yspread(2*r) circle(r=r, $fn=sides);
|
||||
}
|
||||
}
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Module: fillet_mask_x()
|
||||
// Usage:
|
||||
// fillet_mask_x(l, r, [align], [center])
|
||||
// fillet_mask_x(l, r, [align])
|
||||
// Description:
|
||||
// Creates a shape that can be used to fillet a 90 degree edge oriented
|
||||
// along the X axis. Difference it from the object to be filletted.
|
||||
|
@ -415,18 +418,17 @@ module fillet_mask(l=undef, r=1.0, orient=ORIENT_Z, align=CENTER, h=undef, cente
|
|||
// l = Length of mask.
|
||||
// r = Radius of the fillet.
|
||||
// align = Alignment of the mask. Use the constants from `constants.h`. Default: centered.
|
||||
// center = If true, centers vertically. If false, lift up to sit on top of the XY plane. Overrides `align`.
|
||||
// Example:
|
||||
// difference() {
|
||||
// cube(size=100, center=false);
|
||||
// #fillet_mask_x(l=100, r=25, align=RIGHT);
|
||||
// }
|
||||
module fillet_mask_x(l=1.0, r=1.0, align=CENTER) fillet_mask(l=l, r=r, orient=ORIENT_X, align=align);
|
||||
module fillet_mask_x(l=1.0, r=1.0, align=CENTER) fillet_mask(l=l, r=r, orient=ORIENT_X, align=align) children();
|
||||
|
||||
|
||||
// Module: fillet_mask_y()
|
||||
// Usage:
|
||||
// fillet_mask_y(l, r, [align], [center])
|
||||
// fillet_mask_y(l, r, [align])
|
||||
// Description:
|
||||
// Creates a shape that can be used to fillet a 90 degree edge oriented
|
||||
// along the Y axis. Difference it from the object to be filletted.
|
||||
|
@ -436,18 +438,17 @@ module fillet_mask_x(l=1.0, r=1.0, align=CENTER) fillet_mask(l=l, r=r, orient=OR
|
|||
// l = Length of mask.
|
||||
// r = Radius of the fillet.
|
||||
// align = Alignment of the mask. Use the constants from `constants.h`. Default: centered.
|
||||
// center = If true, centers vertically. If false, lift up to sit on top of the XY plane. Overrides `align`.
|
||||
// Example:
|
||||
// difference() {
|
||||
// cube(size=100, center=false);
|
||||
// right(100) #fillet_mask_y(l=100, r=25, align=BACK);
|
||||
// }
|
||||
module fillet_mask_y(l=1.0, r=1.0, align=CENTER) fillet_mask(l=l, r=r, orient=ORIENT_Y, align=align);
|
||||
module fillet_mask_y(l=1.0, r=1.0, align=CENTER) fillet_mask(l=l, r=r, orient=ORIENT_Y, align=align) children();
|
||||
|
||||
|
||||
// Module: fillet_mask_z()
|
||||
// Usage:
|
||||
// fillet_mask_z(l, r, [align], [center])
|
||||
// fillet_mask_z(l, r, [align])
|
||||
// Description:
|
||||
// Creates a shape that can be used to fillet a 90 degree edge oriented
|
||||
// along the Z axis. Difference it from the object to be filletted.
|
||||
|
@ -457,13 +458,12 @@ module fillet_mask_y(l=1.0, r=1.0, align=CENTER) fillet_mask(l=l, r=r, orient=OR
|
|||
// l = Length of mask.
|
||||
// r = Radius of the fillet.
|
||||
// align = Alignment of the mask. Use the constants from `constants.h`. Default: centered.
|
||||
// center = If true, centers vertically. If false, lift up to sit on top of the XY plane. Overrides `align`.
|
||||
// Example:
|
||||
// difference() {
|
||||
// cube(size=100, center=false);
|
||||
// #fillet_mask_z(l=100, r=25, align=UP);
|
||||
// }
|
||||
module fillet_mask_z(l=1.0, r=1.0, align=CENTER) fillet_mask(l=l, r=r, orient=ORIENT_Z, align=align);
|
||||
module fillet_mask_z(l=1.0, r=1.0, align=CENTER) fillet_mask(l=l, r=r, orient=ORIENT_Z, align=align) children();
|
||||
|
||||
|
||||
// Module: fillet()
|
||||
|
@ -505,7 +505,7 @@ module fillet(fillet=1, size=[1,1,1], edges=EDGES_ALL)
|
|||
|
||||
// Module: fillet_angled_edge_mask()
|
||||
// Usage:
|
||||
// fillet_angled_edge_mask(h, r, [ang], [center]);
|
||||
// fillet_angled_edge_mask(h, r, [ang], [orient], [align]);
|
||||
// Description:
|
||||
// Creates a vertical mask that can be used to fillet the edge where two
|
||||
// face meet, at any arbitrary angle. Difference it from the object to
|
||||
|
@ -515,35 +515,39 @@ module fillet(fillet=1, size=[1,1,1], edges=EDGES_ALL)
|
|||
// h = height of vertical mask.
|
||||
// r = radius of the fillet.
|
||||
// ang = angle that the planes meet at.
|
||||
// center = If true, vertically center mask.
|
||||
// orient = Orientation of the mask. Use the `ORIENT_` constants from `constants.h`. Default: `ORIENT_Z`.
|
||||
// align = Alignment of the mask. Use the constants from `constants.h`. Default: `CENTER`.
|
||||
// Example:
|
||||
// difference() {
|
||||
// angle_pie_mask(ang=70, h=50, d=100);
|
||||
// #fillet_angled_edge_mask(h=51, r=20.0, ang=70, $fn=32);
|
||||
// }
|
||||
module fillet_angled_edge_mask(h=1.0, r=1.0, ang=90, center=true)
|
||||
module fillet_angled_edge_mask(h=1.0, r=1.0, ang=90, orient=ORIENT_Z, align=CENTER)
|
||||
{
|
||||
sweep = 180-ang;
|
||||
n = ceil(segs(r)*sweep/360);
|
||||
x = r*sin(90-(ang/2))/sin(ang/2);
|
||||
linear_extrude(height=h, convexity=4, center=center) {
|
||||
polygon(
|
||||
points=concat(
|
||||
[for (i = [0:n]) let (a=90+ang+i*sweep/n) [r*cos(a)+x, r*sin(a)+r]],
|
||||
[for (i = [0:n]) let (a=90+i*sweep/n) [r*cos(a)+x, r*sin(a)-r]],
|
||||
[
|
||||
[min(-1, r*cos(270-ang)+x-1), r*sin(270-ang)-r],
|
||||
[min(-1, r*cos(90+ang)+x-1), r*sin(90+ang)+r],
|
||||
]
|
||||
)
|
||||
);
|
||||
orient_and_align([2*x,2*r,h], orient, align, chain=true) {
|
||||
linear_extrude(height=h, convexity=4, center=true) {
|
||||
polygon(
|
||||
points=concat(
|
||||
[for (i = [0:n]) let (a=90+ang+i*sweep/n) [r*cos(a)+x, r*sin(a)+r]],
|
||||
[for (i = [0:n]) let (a=90+i*sweep/n) [r*cos(a)+x, r*sin(a)-r]],
|
||||
[
|
||||
[min(-1, r*cos(270-ang)+x-1), r*sin(270-ang)-r],
|
||||
[min(-1, r*cos(90+ang)+x-1), r*sin(90+ang)+r],
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Module: fillet_angled_corner_mask()
|
||||
// Usage:
|
||||
// fillet_angled_corner_mask(fillet, ang);
|
||||
// fillet_angled_corner_mask(fillet, ang, [orient], [align]);
|
||||
// Description:
|
||||
// Creates a shape that can be used to fillet the corner of an angle.
|
||||
// Difference it from the object to be filletted. The center of the mask
|
||||
|
@ -551,6 +555,8 @@ module fillet_angled_edge_mask(h=1.0, r=1.0, ang=90, center=true)
|
|||
// Arguments:
|
||||
// fillet = radius of the fillet.
|
||||
// ang = angle between planes that you need to fillet the corner of.
|
||||
// orient = Orientation of the mask. Use the `ORIENT_` constants from `constants.h`. Default: `ORIENT_Z`.
|
||||
// align = Alignment of the mask. Use the constants from `constants.h`. Default: `CENTER`.
|
||||
// Example:
|
||||
// ang=60;
|
||||
// difference() {
|
||||
|
@ -561,36 +567,41 @@ module fillet_angled_edge_mask(h=1.0, r=1.0, ang=90, center=true)
|
|||
// }
|
||||
// fillet_angled_edge_mask(h=51, r=20, ang=ang);
|
||||
// }
|
||||
module fillet_angled_corner_mask(fillet=1.0, ang=90)
|
||||
module fillet_angled_corner_mask(fillet=1.0, ang=90, orient=ORIENT_Z, align=CENTER)
|
||||
{
|
||||
dx = fillet / tan(ang/2);
|
||||
dx2 = dx / cos(ang/2) + 1;
|
||||
fn = quantup(segs(fillet), 4);
|
||||
difference() {
|
||||
down(fillet) cylinder(r=dx/cos(ang/2)+1, h=fillet+1, center=false);
|
||||
yflip_copy() {
|
||||
translate([dx, fillet, -fillet]) {
|
||||
hull() {
|
||||
sphere(r=fillet, $fn=fn);
|
||||
down(fillet*3) sphere(r=fillet, $fn=fn);
|
||||
zrot_copies([0,ang]) {
|
||||
right(fillet*3) sphere(r=fillet, $fn=fn);
|
||||
orient_and_align([2*dx2, 2*dx2, fillet*2], orient, align, chain=true) {
|
||||
difference() {
|
||||
down(fillet) cylinder(r=dx2, h=fillet+1, center=false);
|
||||
yflip_copy() {
|
||||
translate([dx, fillet, -fillet]) {
|
||||
hull() {
|
||||
sphere(r=fillet, $fn=fn);
|
||||
down(fillet*3) sphere(r=fillet, $fn=fn);
|
||||
zrot_copies([0,ang]) {
|
||||
right(fillet*3) sphere(r=fillet, $fn=fn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Module: fillet_corner_mask()
|
||||
// Usage:
|
||||
// fillet_corner_mask(r);
|
||||
// fillet_corner_mask(r, [align]);
|
||||
// Description:
|
||||
// Creates a shape that you can use to round 90 degree corners on a fillet.
|
||||
// Difference it from the object to be filletted. The center of the mask
|
||||
// object should align exactly with the corner to be filletted.
|
||||
// Arguments:
|
||||
// r = radius of corner fillet.
|
||||
// align = Alignment of the mask. Use the constants from `constants.h`. Default: `CENTER`.
|
||||
// Example:
|
||||
// fillet_corner_mask(r=20.0);
|
||||
// Example:
|
||||
|
@ -601,20 +612,23 @@ module fillet_angled_corner_mask(fillet=1.0, ang=90)
|
|||
// translate([15, 25, 0]) fillet_mask_z(l=81, r=15);
|
||||
// translate([15, 25, 40]) #fillet_corner_mask(r=15);
|
||||
// }
|
||||
module fillet_corner_mask(r=1.0)
|
||||
module fillet_corner_mask(r=1.0, align=CENTER)
|
||||
{
|
||||
difference() {
|
||||
cube(size=r*2, center=true);
|
||||
grid3d(n=[2,2,2], spacing=r*2-0.05) {
|
||||
sphere(r=r);
|
||||
orient_and_align([2*r, 2*r, 2*r], ORIENT_Z, align, chain=true) {
|
||||
difference() {
|
||||
cube(size=r*2, center=true);
|
||||
grid3d(n=[2,2,2], spacing=r*2-0.05) {
|
||||
sphere(r=r);
|
||||
}
|
||||
}
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Module: fillet_cylinder_mask()
|
||||
// Usage:
|
||||
// fillet_cylinder_mask(r, fillet, [xtilt], [ytilt]);
|
||||
// fillet_cylinder_mask(r, fillet);
|
||||
// Description:
|
||||
// Create a mask that can be used to round the end of a cylinder.
|
||||
// Difference it from the cylinder to be filletted. The center of the
|
||||
|
@ -623,8 +637,6 @@ module fillet_corner_mask(r=1.0)
|
|||
// Arguments:
|
||||
// r = radius of cylinder to fillet. (Default: 1.0)
|
||||
// fillet = radius of the edge filleting. (Default: 0.25)
|
||||
// xtilt = angle of tilt of end of cylinder in the X direction. (Default: 0)
|
||||
// ytilt = angle of tilt of end of cylinder in the Y direction. (Default: 0)
|
||||
// Example:
|
||||
// difference() {
|
||||
// cylinder(r=50, h=50, center=false);
|
||||
|
@ -633,22 +645,18 @@ module fillet_corner_mask(r=1.0)
|
|||
// Example:
|
||||
// difference() {
|
||||
// cylinder(r=50, h=100, center=false);
|
||||
// up(75) fillet_cylinder_mask(r=50, fillet=10, xtilt=30);
|
||||
// up(75) fillet_cylinder_mask(r=50, fillet=10);
|
||||
// }
|
||||
module fillet_cylinder_mask(r=1.0, fillet=0.25, xtilt=0, ytilt=0)
|
||||
module fillet_cylinder_mask(r=1.0, fillet=0.25)
|
||||
{
|
||||
skew_xz(za=xtilt) {
|
||||
skew_yz(za=ytilt) {
|
||||
cylinder_mask(l=fillet*3, r=r, fillet2=fillet, overage=fillet+2*r*sin(max(xtilt,ytilt)), ends_only=true, align=DOWN);
|
||||
}
|
||||
}
|
||||
cylinder_mask(l=fillet*3, r=r, fillet2=fillet, overage=fillet, ends_only=true, align=DOWN) children();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Module: fillet_hole_mask()
|
||||
// Usage:
|
||||
// fillet_hole_mask(r|d, fillet, [xtilt], [ytilt]);
|
||||
// fillet_hole_mask(r|d, fillet);
|
||||
// Description:
|
||||
// Create a mask that can be used to round the edge of a circular hole.
|
||||
// Difference it from the hole to be filletted. The center of the
|
||||
|
@ -658,9 +666,9 @@ module fillet_cylinder_mask(r=1.0, fillet=0.25, xtilt=0, ytilt=0)
|
|||
// r = Radius of hole to fillet.
|
||||
// d = Diameter of hole to fillet.
|
||||
// fillet = Radius of the filleting. (Default: 0.25)
|
||||
// xtilt = Angle of tilt of end of cylinder in the X direction. (Default: 0)
|
||||
// ytilt = Angle of tilt of end of cylinder in the Y direction. (Default: 0)
|
||||
// overage = The extra thickness of the mask. Default: `0.1`.
|
||||
// orient = Orientation of the mask. Use the `ORIENT_` constants from `constants.h`. Default: `ORIENT_Z`.
|
||||
// align = Alignment of the mask. Use the constants from `constants.h`. Default: `CENTER`.
|
||||
// Example:
|
||||
// difference() {
|
||||
// cube([150,150,100], center=true);
|
||||
|
@ -669,18 +677,17 @@ module fillet_cylinder_mask(r=1.0, fillet=0.25, xtilt=0, ytilt=0)
|
|||
// }
|
||||
// Example:
|
||||
// fillet_hole_mask(r=40, fillet=20, $fa=2, $fs=2);
|
||||
module fillet_hole_mask(r=undef, d=undef, fillet=0.25, overage=0.1, xtilt=0, ytilt=0)
|
||||
module fillet_hole_mask(r=undef, d=undef, fillet=0.25, overage=0.1, orient=ORIENT_Z, align=CENTER)
|
||||
{
|
||||
r = get_radius(r=r, d=d, dflt=1);
|
||||
skew_xz(za=xtilt) {
|
||||
skew_yz(za=ytilt) {
|
||||
rotate_extrude(convexity=4) {
|
||||
difference() {
|
||||
right(r-overage) fwd(fillet) square(fillet+overage, center=false);
|
||||
right(r+fillet) fwd(fillet) circle(r=fillet);
|
||||
}
|
||||
orient_and_align([2*(r+fillet), 2*(r+fillet), fillet*2], orient, align, chain=true) {
|
||||
rotate_extrude(convexity=4) {
|
||||
difference() {
|
||||
right(r-overage) fwd(fillet) square(fillet+overage, center=false);
|
||||
right(r+fillet) fwd(fillet) circle(r=fillet);
|
||||
}
|
||||
}
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
352
matrices.scad
Normal file
352
matrices.scad
Normal file
|
@ -0,0 +1,352 @@
|
|||
//////////////////////////////////////////////////////////////////////
|
||||
// LibFile: matrices.scad
|
||||
// Matrix math and affine transformation matrices.
|
||||
// To use, add the following lines to the beginning of your file:
|
||||
// ```
|
||||
// use <BOSL2/std.scad>
|
||||
// ```
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
BSD 2-Clause License
|
||||
|
||||
Copyright (c) 2017-2019, Revar Desmera
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
// Section: Matrix Manipulation
|
||||
|
||||
// Function: ident()
|
||||
// Description: Create an `n` by `n` identity matrix.
|
||||
// Arguments:
|
||||
// n = The size of the identity matrix square, `n` by `n`.
|
||||
function ident(n) = [for (i = [0:n-1]) [for (j = [0:n-1]) (i==j)?1:0]];
|
||||
|
||||
|
||||
// Function: matrix_transpose()
|
||||
// Description: Returns the transposition of the given matrix.
|
||||
// Example:
|
||||
// m = [
|
||||
// [11,12,13,14],
|
||||
// [21,22,23,24],
|
||||
// [31,32,33,34],
|
||||
// [41,42,43,44]
|
||||
// ];
|
||||
// tm = matrix_transpose(m);
|
||||
// // Returns:
|
||||
// // [
|
||||
// // [11,21,31,41],
|
||||
// // [12,22,32,42],
|
||||
// // [13,23,33,43],
|
||||
// // [14,24,34,44]
|
||||
// // ]
|
||||
function matrix_transpose(m) = [for (i=[0:len(m[0])-1]) [for (j=[0:len(m)-1]) m[j][i]]];
|
||||
|
||||
|
||||
|
||||
// Function: mat3_to_mat4()
|
||||
// Description: Takes a 3x3 matrix and returns its 4x4 affine equivalent.
|
||||
function mat3_to_mat4(m) = concat(
|
||||
[for (r = [0:2])
|
||||
concat(
|
||||
[for (c = [0:2]) m[r][c]],
|
||||
[0]
|
||||
)
|
||||
],
|
||||
[[0, 0, 0, 1]]
|
||||
);
|
||||
|
||||
|
||||
|
||||
// Section: Affine Transformation 3x3 Matrices
|
||||
|
||||
|
||||
// Function: matrix3_translate()
|
||||
// Description:
|
||||
// Returns the 3x3 matrix to perform a 2D translation.
|
||||
// Arguments:
|
||||
// v = 2D Offset to translate by. [X,Y]
|
||||
function matrix3_translate(v) = [
|
||||
[1, 0, v.x],
|
||||
[0, 1, v.y],
|
||||
[0 ,0, 1]
|
||||
];
|
||||
|
||||
|
||||
// Function: matrix3_scale()
|
||||
// Description:
|
||||
// Returns the 3x3 matrix to perform a 2D scaling transformation.
|
||||
// Arguments:
|
||||
// v = 2D vector of scaling factors. [X,Y]
|
||||
function matrix3_scale(v) = [
|
||||
[v.x, 0, 0],
|
||||
[ 0, v.y, 0],
|
||||
[ 0, 0, 1]
|
||||
];
|
||||
|
||||
|
||||
// Function: matrix3_zrot()
|
||||
// Description:
|
||||
// Returns the 3x3 matrix to perform a rotation of a 2D vector around the Z axis.
|
||||
// Arguments:
|
||||
// ang = Number of degrees to rotate.
|
||||
function matrix3_zrot(ang) = [
|
||||
[cos(ang), -sin(ang), 0],
|
||||
[sin(ang), cos(ang), 0],
|
||||
[ 0, 0, 1]
|
||||
];
|
||||
|
||||
|
||||
// Function: matrix3_skew()
|
||||
// Usage:
|
||||
// matrix3_skew(xa, ya)
|
||||
// Description:
|
||||
// Returns the 3x3 matrix to skew a 2D vector along the XY plane.
|
||||
// Arguments:
|
||||
// xa = Skew angle, in degrees, in the direction of the X axis.
|
||||
// ya = Skew angle, in degrees, in the direction of the Y axis.
|
||||
function matrix3_skew(xa, ya) = [
|
||||
[1, tan(xa), 0],
|
||||
[tan(ya), 1, 0],
|
||||
[0, 0, 1]
|
||||
];
|
||||
|
||||
|
||||
// Function: matrix3_mult()
|
||||
// Usage:
|
||||
// matrix3_mult(matrices)
|
||||
// Description:
|
||||
// Returns a 3x3 transformation matrix which results from applying each matrix in `matrices` in order.
|
||||
// Arguments:
|
||||
// matrices = A list of 3x3 matrices.
|
||||
// m = Optional starting matrix to apply everything to.
|
||||
function matrix3_mult(matrices, m=ident(3), i=0) =
|
||||
(i>=len(matrices))? m :
|
||||
let (newmat = is_undef(m)? matrices[i] : matrices[i] * m)
|
||||
matrix3_mult(matrices, m=newmat, i=i+1);
|
||||
|
||||
|
||||
// Function: matrix3_apply()
|
||||
// Usage:
|
||||
// matrix3_apply(pts, matrices)
|
||||
// Description:
|
||||
// Given a list of transformation matrices, applies them in order to the points in the point list.
|
||||
// Arguments:
|
||||
// pts = A list of 2D points to transform.
|
||||
// matrices = A list of 3x3 matrices to apply, in order.
|
||||
// Example:
|
||||
// npts = matrix3_apply(
|
||||
// pts = [for (x=[0:3]) [5*x,0]],
|
||||
// matrices =[
|
||||
// matrix3_scale([3,1]),
|
||||
// matrix3_rot(90),
|
||||
// matrix3_translate([5,5])
|
||||
// ]
|
||||
// ); // Returns [[5,5], [5,20], [5,35], [5,50]]
|
||||
function matrix3_apply(pts, matrices) =
|
||||
let(m = matrix3_mult(matrices))
|
||||
[for (p = pts) point2d(m * concat(point2d(p),[1]))];
|
||||
|
||||
|
||||
|
||||
// Section: Affine Transformation 4x4 Matrices
|
||||
|
||||
|
||||
// Function: matrix4_translate()
|
||||
// Description:
|
||||
// Returns the 4x4 matrix to perform a 3D translation.
|
||||
// Arguments:
|
||||
// v = 3D offset to translate by. [X,Y,Z]
|
||||
function matrix4_translate(v) = [
|
||||
[1, 0, 0, v.x],
|
||||
[0, 1, 0, v.y],
|
||||
[0, 0, 1, v.z],
|
||||
[0 ,0, 0, 1]
|
||||
];
|
||||
|
||||
|
||||
// Function: matrix4_scale()
|
||||
// Description:
|
||||
// Returns the 4x4 matrix to perform a 3D scaling transformation.
|
||||
// Arguments:
|
||||
// v = 3D vector of scaling factors. [X,Y,Z]
|
||||
function matrix4_scale(v) = [
|
||||
[v.x, 0, 0, 0],
|
||||
[ 0, v.y, 0, 0],
|
||||
[ 0, 0, v.z, 0],
|
||||
[ 0, 0, 0, 1]
|
||||
];
|
||||
|
||||
|
||||
// Function: matrix4_xrot()
|
||||
// Description:
|
||||
// Returns the 4x4 matrix to perform a rotation of a 3D vector around the X axis.
|
||||
// Arguments:
|
||||
// ang = number of degrees to rotate.
|
||||
function matrix4_xrot(ang) = [
|
||||
[1, 0, 0, 0],
|
||||
[0, cos(ang), -sin(ang), 0],
|
||||
[0, sin(ang), cos(ang), 0],
|
||||
[0, 0, 0, 1]
|
||||
];
|
||||
|
||||
|
||||
// Function: matrix4_yrot()
|
||||
// Description:
|
||||
// Returns the 4x4 matrix to perform a rotation of a 3D vector around the Y axis.
|
||||
// Arguments:
|
||||
// ang = Number of degrees to rotate.
|
||||
function matrix4_yrot(ang) = [
|
||||
[ cos(ang), 0, sin(ang), 0],
|
||||
[ 0, 1, 0, 0],
|
||||
[-sin(ang), 0, cos(ang), 0],
|
||||
[ 0, 0, 0, 1]
|
||||
];
|
||||
|
||||
|
||||
// Function: matrix4_zrot()
|
||||
// Usage:
|
||||
// matrix4_zrot(ang)
|
||||
// Description:
|
||||
// Returns the 4x4 matrix to perform a rotation of a 3D vector around the Z axis.
|
||||
// Arguments:
|
||||
// ang = number of degrees to rotate.
|
||||
function matrix4_zrot(ang) = [
|
||||
[cos(ang), -sin(ang), 0, 0],
|
||||
[sin(ang), cos(ang), 0, 0],
|
||||
[ 0, 0, 1, 0],
|
||||
[ 0, 0, 0, 1]
|
||||
];
|
||||
|
||||
|
||||
// Function: matrix4_rot_by_axis()
|
||||
// Usage:
|
||||
// matrix4_rot_by_axis(u, ang);
|
||||
// Description:
|
||||
// Returns the 4x4 matrix to perform a rotation of a 3D vector around an axis.
|
||||
// Arguments:
|
||||
// u = 3D axis vector to rotate around.
|
||||
// ang = number of degrees to rotate.
|
||||
function matrix4_rot_by_axis(u, ang) = let(
|
||||
u = normalize(u),
|
||||
c = cos(ang),
|
||||
c2 = 1-c,
|
||||
s = sin(ang)
|
||||
) [
|
||||
[u[0]*u[0]*c2+c , u[0]*u[1]*c2-u[2]*s, u[0]*u[2]*c2+u[1]*s, 0],
|
||||
[u[1]*u[0]*c2+u[2]*s, u[1]*u[1]*c2+c , u[1]*u[2]*c2-u[0]*s, 0],
|
||||
[u[2]*u[0]*c2-u[1]*s, u[2]*u[1]*c2+u[0]*s, u[2]*u[2]*c2+c , 0],
|
||||
[ 0, 0, 0, 1]
|
||||
];
|
||||
|
||||
|
||||
// Function: matrix4_skew_xy()
|
||||
// Usage:
|
||||
// matrix4_skew_xy(xa, ya)
|
||||
// Description:
|
||||
// Returns the 4x4 matrix to perform a skew transformation along the XY plane..
|
||||
// Arguments:
|
||||
// xa = Skew angle, in degrees, in the direction of the X axis.
|
||||
// ya = Skew angle, in degrees, in the direction of the Y axis.
|
||||
function matrix4_skew_xy(xa, ya) = [
|
||||
[1, 0, tan(xa), 0],
|
||||
[0, 1, tan(ya), 0],
|
||||
[0, 0, 1, 0],
|
||||
[0, 0, 0, 1]
|
||||
];
|
||||
|
||||
|
||||
// Function: matrix4_skew_xz()
|
||||
// Usage:
|
||||
// matrix4_skew_xz(xa, za)
|
||||
// Description:
|
||||
// Returns the 4x4 matrix to perform a skew transformation along the XZ plane.
|
||||
// Arguments:
|
||||
// xa = Skew angle, in degrees, in the direction of the X axis.
|
||||
// za = Skew angle, in degrees, in the direction of the Z axis.
|
||||
function matrix4_skew_xz(xa, za) = [
|
||||
[1, tan(xa), 0, 0],
|
||||
[0, 1, 0, 0],
|
||||
[0, tan(za), 1, 0],
|
||||
[0, 0, 0, 1]
|
||||
];
|
||||
|
||||
|
||||
// Function: matrix4_skew_yz()
|
||||
// Usage:
|
||||
// matrix4_skew_yz(ya, za)
|
||||
// Description:
|
||||
// Returns the 4x4 matrix to perform a skew transformation along the YZ plane.
|
||||
// Arguments:
|
||||
// ya = Skew angle, in degrees, in the direction of the Y axis.
|
||||
// za = Skew angle, in degrees, in the direction of the Z axis.
|
||||
function matrix4_skew_yz(ya, za) = [
|
||||
[ 1, 0, 0, 0],
|
||||
[tan(ya), 1, 0, 0],
|
||||
[tan(za), 0, 1, 0],
|
||||
[ 0, 0, 0, 1]
|
||||
];
|
||||
|
||||
|
||||
// Function: matrix4_mult()
|
||||
// Usage:
|
||||
// matrix4_mult(matrices)
|
||||
// Description:
|
||||
// Returns a 4x4 transformation matrix which results from applying each matrix in `matrices` in order.
|
||||
// Arguments:
|
||||
// matrices = A list of 4x4 matrices.
|
||||
// m = Optional starting matrix to apply everything to.
|
||||
function matrix4_mult(matrices, m=ident(4), i=0) =
|
||||
(i>=len(matrices))? m :
|
||||
let (newmat = is_undef(m)? matrices[i] : matrices[i] * m)
|
||||
matrix4_mult(matrices, m=newmat, i=i+1);
|
||||
|
||||
|
||||
// Function: matrix4_apply()
|
||||
// Usage:
|
||||
// matrix4_apply(pts, matrices)
|
||||
// Description:
|
||||
// Given a list of transformation matrices, applies them in order to the points in the point list.
|
||||
// Arguments:
|
||||
// pts = A list of 3D points to transform.
|
||||
// matrices = A list of 4x4 matrices to apply, in order.
|
||||
// Example:
|
||||
// npts = matrix4_apply(
|
||||
// pts = [for (x=[0:3]) [5*x,0,0]],
|
||||
// matrices =[
|
||||
// matrix4_scale([2,1,1]),
|
||||
// matrix4_zrot(90),
|
||||
// matrix4_translate([5,5,10])
|
||||
// ]
|
||||
// ); // Returns [[5,5,10], [5,15,10], [5,25,10], [5,35,10]]
|
||||
|
||||
function matrix4_apply(pts, matrices) =
|
||||
let(m = matrix4_mult(matrices))
|
||||
[for (p = pts) point3d(m * concat(point3d(p),[1]))];
|
||||
|
||||
|
||||
|
||||
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap
|
|
@ -14,7 +14,7 @@
|
|||
/*
|
||||
BSD 2-Clause License
|
||||
|
||||
Copyright (c) 2017, Revar Desmera
|
||||
Copyright (c) 2017-2019, Revar Desmera
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
@ -418,7 +418,7 @@ module screw(
|
|||
["base", [0,0,-headlen/2+screwlen/2]],
|
||||
["sunken", [0,0,(headlen+screwlen)/2-0.01]]
|
||||
];
|
||||
orient_and_align([headsize, headsize, headlen+screwlen], orient, algn, alignments=alignments) {
|
||||
orient_and_align([headsize, headsize, headlen+screwlen], orient, algn, alignments=alignments, chain=true) {
|
||||
down(headlen/2-screwlen/2) {
|
||||
down(screwlen/2) {
|
||||
if (pitch == undef) {
|
||||
|
@ -429,6 +429,7 @@ module screw(
|
|||
}
|
||||
up(headlen/2) cylinder(r=headsize/2, h=headlen, center=true, $fn=sides*2);
|
||||
}
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -530,7 +531,7 @@ module metric_bolt(
|
|||
];
|
||||
|
||||
color("silver")
|
||||
orient_and_align([D+flange, D+flange, headlen+l], orient, align, alignments=alignments) {
|
||||
orient_and_align([D+flange, D+flange, headlen+l], orient, align, alignments=alignments, chain=true) {
|
||||
up(base) {
|
||||
difference() {
|
||||
union() {
|
||||
|
@ -624,6 +625,7 @@ module metric_bolt(
|
|||
}
|
||||
}
|
||||
}
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -670,7 +672,7 @@ module metric_nut(
|
|||
bevtop = (dcirc - D)/2;
|
||||
|
||||
color("silver")
|
||||
orient_and_align([dcirc+flange, dcirc+flange, H], orient, align, center) {
|
||||
orient_and_align([dcirc+flange, dcirc+flange, H], orient, align, center, chain=true) {
|
||||
difference() {
|
||||
union() {
|
||||
difference() {
|
||||
|
@ -707,6 +709,7 @@ module metric_nut(
|
|||
}
|
||||
}
|
||||
}
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
/*
|
||||
BSD 2-Clause License
|
||||
|
||||
Copyright (c) 2017, Revar Desmera
|
||||
Copyright (c) 2017-2019, Revar Desmera
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
@ -142,23 +142,25 @@ module nema11_stepper(h=24, shaft=5, shaft_len=20, orient=ORIENT_Z, align=DOWN)
|
|||
screw_size = nema_motor_screw_size(size);
|
||||
screw_depth = nema_motor_screw_depth(size);
|
||||
|
||||
orient_and_align([motor_width, motor_width, h], orient, align, orig_align=DOWN) {
|
||||
difference() {
|
||||
color([0.4, 0.4, 0.4])
|
||||
cuboid(size=[motor_width, motor_width, h], chamfer=2, edges=EDGES_Z_ALL, align=DOWN);
|
||||
color("silver")
|
||||
xspread(screw_spacing)
|
||||
yspread(screw_spacing)
|
||||
cyl(r=screw_size/2, h=screw_depth*2, $fn=max(12,segs(screw_size/2)));
|
||||
}
|
||||
color([0.6, 0.6, 0.6]) {
|
||||
orient_and_align([motor_width, motor_width, h], orient, align, orig_align=DOWN, chain=true) {
|
||||
union() {
|
||||
difference() {
|
||||
cylinder(h=plinth_height, d=plinth_diam);
|
||||
cyl(h=plinth_height*3, d=shaft+0.75);
|
||||
color([0.4, 0.4, 0.4])
|
||||
cuboid(size=[motor_width, motor_width, h], chamfer=2, edges=EDGES_Z_ALL, align=DOWN);
|
||||
color("silver")
|
||||
xspread(screw_spacing)
|
||||
yspread(screw_spacing)
|
||||
cyl(r=screw_size/2, h=screw_depth*2, $fn=max(12,segs(screw_size/2)));
|
||||
}
|
||||
color([0.6, 0.6, 0.6]) {
|
||||
difference() {
|
||||
cylinder(h=plinth_height, d=plinth_diam);
|
||||
cyl(h=plinth_height*3, d=shaft+0.75);
|
||||
}
|
||||
}
|
||||
color("silver") cylinder(h=shaft_len, d=shaft, $fn=max(12,segs(shaft/2)));
|
||||
}
|
||||
color("silver")
|
||||
cylinder(h=shaft_len, d=shaft, $fn=max(12,segs(shaft/2)));
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -184,23 +186,25 @@ module nema14_stepper(h=24, shaft=5, shaft_len=24, orient=ORIENT_Z, align=DOWN)
|
|||
screw_size = nema_motor_screw_size(size);
|
||||
screw_depth = nema_motor_screw_depth(size);
|
||||
|
||||
orient_and_align([motor_width, motor_width, h], orient, align, orig_align=DOWN) {
|
||||
difference() {
|
||||
color([0.4, 0.4, 0.4])
|
||||
cuboid(size=[motor_width, motor_width, h], chamfer=2, edges=EDGES_Z_ALL, align=DOWN);
|
||||
color("silver")
|
||||
xspread(screw_spacing)
|
||||
yspread(screw_spacing)
|
||||
cyl(d=screw_size, h=screw_depth*2, $fn=max(12,segs(screw_size/2)));
|
||||
}
|
||||
color([0.6, 0.6, 0.6]) {
|
||||
orient_and_align([motor_width, motor_width, h], orient, align, orig_align=DOWN, chain=true) {
|
||||
union() {
|
||||
difference() {
|
||||
cylinder(h=plinth_height, d=plinth_diam);
|
||||
cyl(h=plinth_height*3, d=shaft+0.75);
|
||||
color([0.4, 0.4, 0.4])
|
||||
cuboid(size=[motor_width, motor_width, h], chamfer=2, edges=EDGES_Z_ALL, align=DOWN);
|
||||
color("silver")
|
||||
xspread(screw_spacing)
|
||||
yspread(screw_spacing)
|
||||
cyl(d=screw_size, h=screw_depth*2, $fn=max(12,segs(screw_size/2)));
|
||||
}
|
||||
color([0.6, 0.6, 0.6]) {
|
||||
difference() {
|
||||
cylinder(h=plinth_height, d=plinth_diam);
|
||||
cyl(h=plinth_height*3, d=shaft+0.75);
|
||||
}
|
||||
}
|
||||
color("silver") cyl(h=shaft_len, d=shaft, align=UP, $fn=max(12,segs(shaft/2)));
|
||||
}
|
||||
color("silver")
|
||||
cyl(h=shaft_len, d=shaft, align=UP, $fn=max(12,segs(shaft/2)));
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -226,41 +230,44 @@ module nema17_stepper(h=34, shaft=5, shaft_len=20, orient=ORIENT_Z, align=DOWN)
|
|||
screw_size = nema_motor_screw_size(size);
|
||||
screw_depth = nema_motor_screw_depth(size);
|
||||
|
||||
orient_and_align([motor_width, motor_width, h], orient, align, orig_align=DOWN) {
|
||||
difference() {
|
||||
color([0.4, 0.4, 0.4])
|
||||
cuboid([motor_width, motor_width, h], chamfer=2, edges=EDGES_Z_ALL, align=DOWN);
|
||||
color("silver")
|
||||
xspread(screw_spacing)
|
||||
yspread(screw_spacing)
|
||||
cyl(d=screw_size, h=screw_depth*2, $fn=max(12,segs(screw_size/2)));
|
||||
}
|
||||
color([0.6, 0.6, 0.6]) {
|
||||
orient_and_align([motor_width, motor_width, h], orient, align, orig_align=DOWN, chain=true) {
|
||||
union() {
|
||||
difference() {
|
||||
cylinder(h=plinth_height, d=plinth_diam);
|
||||
cyl(h=plinth_height*3, d=shaft+0.75);
|
||||
color([0.4, 0.4, 0.4])
|
||||
cuboid([motor_width, motor_width, h], chamfer=2, edges=EDGES_Z_ALL, align=DOWN);
|
||||
color("silver")
|
||||
xspread(screw_spacing)
|
||||
yspread(screw_spacing)
|
||||
cyl(d=screw_size, h=screw_depth*2, $fn=max(12,segs(screw_size/2)));
|
||||
}
|
||||
}
|
||||
color([0.9, 0.9, 0.9]) {
|
||||
down(h-motor_width/12) {
|
||||
fwd(motor_width/2+motor_width/24/2-0.1) {
|
||||
difference() {
|
||||
cube(size=[motor_width/8, motor_width/24, motor_width/8], center=true);
|
||||
cyl(d=motor_width/8-2, h=motor_width/6, orient=ORIENT_Y, $fn=12);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
color("silver") {
|
||||
difference() {
|
||||
cylinder(h=shaft_len, d=shaft, $fn=max(12,segs(shaft/2)));
|
||||
up(shaft_len/2+1) {
|
||||
right(shaft-0.75) {
|
||||
cube([shaft, shaft, shaft_len], center=true);
|
||||
color([0.6, 0.6, 0.6]) {
|
||||
difference() {
|
||||
cylinder(h=plinth_height, d=plinth_diam);
|
||||
cyl(h=plinth_height*3, d=shaft+0.75);
|
||||
}
|
||||
}
|
||||
color([0.9, 0.9, 0.9]) {
|
||||
down(h-motor_width/12) {
|
||||
fwd(motor_width/2+motor_width/24/2-0.1) {
|
||||
difference() {
|
||||
cube(size=[motor_width/8, motor_width/24, motor_width/8], center=true);
|
||||
cyl(d=motor_width/8-2, h=motor_width/6, orient=ORIENT_Y, $fn=12);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
color("silver") {
|
||||
difference() {
|
||||
cylinder(h=shaft_len, d=shaft, $fn=max(12,segs(shaft/2)));
|
||||
up(shaft_len/2+1) {
|
||||
right(shaft-0.75) {
|
||||
cube([shaft, shaft, shaft_len], center=true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -287,7 +294,7 @@ module nema23_stepper(h=50, shaft=6.35, shaft_len=25, orient=ORIENT_Z, align=DOW
|
|||
screw_depth = nema_motor_screw_depth(size);
|
||||
|
||||
screw_inset = motor_width - screw_spacing + 1;
|
||||
orient_and_align([motor_width, motor_width, h], orient, align, orig_align=DOWN) {
|
||||
orient_and_align([motor_width, motor_width, h], orient, align, orig_align=DOWN, chain=true) {
|
||||
difference() {
|
||||
union() {
|
||||
color([0.4, 0.4, 0.4])
|
||||
|
@ -306,6 +313,7 @@ module nema23_stepper(h=50, shaft=6.35, shaft_len=25, orient=ORIENT_Z, align=DOW
|
|||
}
|
||||
}
|
||||
}
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -332,7 +340,7 @@ module nema34_stepper(h=75, shaft=12.7, shaft_len=32, orient=ORIENT_Z, align=DOW
|
|||
screw_depth = nema_motor_screw_depth(size);
|
||||
|
||||
screw_inset = motor_width - screw_spacing + 1;
|
||||
orient_and_align([motor_width, motor_width, h], orient, align, orig_align=DOWN) {
|
||||
orient_and_align([motor_width, motor_width, h], orient, align, orig_align=DOWN, chain=true) {
|
||||
difference() {
|
||||
union() {
|
||||
color([0.4, 0.4, 0.4])
|
||||
|
@ -351,6 +359,7 @@ module nema34_stepper(h=75, shaft=12.7, shaft_len=32, orient=ORIENT_Z, align=DOW
|
|||
}
|
||||
}
|
||||
}
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -382,7 +391,7 @@ module nema_mount_holes(size=17, depth=5, l=5, slop=PRINTER_SLOP, orient=ORIENT_
|
|||
screw_spacing = nema_motor_screw_spacing(size);
|
||||
screw_size = nema_motor_screw_size(size)+slop;
|
||||
|
||||
orient_and_align([motor_width, motor_width, l], orient, align) {
|
||||
orient_and_align([motor_width, motor_width, l], orient, align, chain=true) {
|
||||
union() {
|
||||
xspread(screw_spacing) {
|
||||
yspread(screw_spacing) {
|
||||
|
@ -396,15 +405,16 @@ module nema_mount_holes(size=17, depth=5, l=5, slop=PRINTER_SLOP, orient=ORIENT_
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (l>0) {
|
||||
union () {
|
||||
yspread(l) cyl(h=depth, d=plinth_diam);
|
||||
cube([plinth_diam, l, depth], center=true);
|
||||
if (l>0) {
|
||||
union () {
|
||||
yspread(l) cyl(h=depth, d=plinth_diam);
|
||||
cube([plinth_diam, l, depth], center=true);
|
||||
}
|
||||
} else {
|
||||
cyl(h=depth, d=plinth_diam);
|
||||
}
|
||||
} else {
|
||||
cyl(h=depth, d=plinth_diam);
|
||||
}
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -424,7 +434,7 @@ module nema_mount_holes(size=17, depth=5, l=5, slop=PRINTER_SLOP, orient=ORIENT_
|
|||
// nema11_mount_holes(depth=5, l=0);
|
||||
module nema11_mount_holes(depth=5, l=5, slop=PRINTER_SLOP, orient=ORIENT_Z, align=CENTER)
|
||||
{
|
||||
nema_mount_holes(size=11, depth=depth, l=l, slop=slop, orient=orient, align=align);
|
||||
nema_mount_holes(size=11, depth=depth, l=l, slop=slop, orient=orient, align=align) children();
|
||||
}
|
||||
|
||||
|
||||
|
@ -443,7 +453,7 @@ module nema11_mount_holes(depth=5, l=5, slop=PRINTER_SLOP, orient=ORIENT_Z, alig
|
|||
// nema14_mount_holes(depth=5, l=0);
|
||||
module nema14_mount_holes(depth=5, l=5, slop=PRINTER_SLOP, orient=ORIENT_Z, align=CENTER)
|
||||
{
|
||||
nema_mount_holes(size=14, depth=depth, l=l, slop=slop, orient=orient, align=align);
|
||||
nema_mount_holes(size=14, depth=depth, l=l, slop=slop, orient=orient, align=align) children();
|
||||
}
|
||||
|
||||
|
||||
|
@ -462,7 +472,7 @@ module nema14_mount_holes(depth=5, l=5, slop=PRINTER_SLOP, orient=ORIENT_Z, alig
|
|||
// nema17_mount_holes(depth=5, l=0);
|
||||
module nema17_mount_holes(depth=5, l=5, slop=PRINTER_SLOP, orient=ORIENT_Z, align=CENTER)
|
||||
{
|
||||
nema_mount_holes(size=17, depth=depth, l=l, slop=slop, orient=orient, align=align);
|
||||
nema_mount_holes(size=17, depth=depth, l=l, slop=slop, orient=orient, align=align) children();
|
||||
}
|
||||
|
||||
|
||||
|
@ -481,7 +491,7 @@ module nema17_mount_holes(depth=5, l=5, slop=PRINTER_SLOP, orient=ORIENT_Z, alig
|
|||
// nema23_mount_holes(depth=5, l=0);
|
||||
module nema23_mount_holes(depth=5, l=5, slop=PRINTER_SLOP, orient=ORIENT_Z, align=CENTER)
|
||||
{
|
||||
nema_mount_holes(size=23, depth=depth, l=l, slop=slop, orient=orient, align=align);
|
||||
nema_mount_holes(size=23, depth=depth, l=l, slop=slop, orient=orient, align=align) children();
|
||||
}
|
||||
|
||||
|
||||
|
@ -500,7 +510,7 @@ module nema23_mount_holes(depth=5, l=5, slop=PRINTER_SLOP, orient=ORIENT_Z, alig
|
|||
// nema34_mount_holes(depth=5, l=0);
|
||||
module nema34_mount_holes(depth=5, l=5, slop=PRINTER_SLOP, orient=ORIENT_Z, align=CENTER)
|
||||
{
|
||||
nema_mount_holes(size=34, depth=depth, l=l, slop=slop, orient=orient, align=align);
|
||||
nema_mount_holes(size=34, depth=depth, l=l, slop=slop, orient=orient, align=align) children();
|
||||
}
|
||||
|
||||
|
||||
|
@ -519,7 +529,7 @@ module nema34_mount_holes(depth=5, l=5, slop=PRINTER_SLOP, orient=ORIENT_Z, alig
|
|||
// nema34_mount_holes(depth=5, l=0);
|
||||
module nema34_mount_holes(depth=5, l=5, slop=PRINTER_SLOP, orient=ORIENT_Z, align=CENTER)
|
||||
{
|
||||
nema_mount_holes(size=34, depth=depth, l=l, slop=slop, orient=orient, align=align);
|
||||
nema_mount_holes(size=34, depth=depth, l=l, slop=slop, orient=orient, align=align) children();
|
||||
}
|
||||
|
||||
|
||||
|
|
10
paths.scad
10
paths.scad
|
@ -13,7 +13,7 @@
|
|||
/*
|
||||
BSD 2-Clause License
|
||||
|
||||
Copyright (c) 2017, Revar Desmera
|
||||
Copyright (c) 2017-2019, Revar Desmera
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
@ -233,7 +233,7 @@ module extrude_from_to(pt1, pt2, convexity=undef, twist=undef, scale=undef, slic
|
|||
// circle(r=40, $fn=6);
|
||||
module extrude_2d_hollow(wall=2, height=50, twist=90, slices=60, center=undef, orient=ORIENT_Z, align=UP)
|
||||
{
|
||||
orient_and_align([0,0,height], orient, align, center) {
|
||||
orient_and_align([0,0,height], orient, align, center, chain=true) {
|
||||
linear_extrude(height=height, twist=twist, slices=slices, center=true) {
|
||||
difference() {
|
||||
children();
|
||||
|
@ -242,6 +242,7 @@ module extrude_2d_hollow(wall=2, height=50, twist=90, slices=60, center=undef, o
|
|||
}
|
||||
}
|
||||
}
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -300,8 +301,9 @@ module extrude_2dpath_along_spiral(polyline, h, r, twist=360, center=undef, orie
|
|||
);
|
||||
|
||||
tri_faces = triangulate_faces(poly_points, poly_faces);
|
||||
orient_and_align([r,r,h], orient, align, center) {
|
||||
orient_and_align([r,r,h], orient, align, center, chain=true) {
|
||||
polyhedron(points=poly_points, faces=tri_faces, convexity=10);
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -461,7 +463,7 @@ module trace_polyline(pline, N=1, showpts=false, size=1, color="yellow") {
|
|||
// );
|
||||
module debug_polygon(points, paths=undef, convexity=2, size=1)
|
||||
{
|
||||
pths = (!is_def(paths))? [for (i=[0:len(points)-1]) i] : is_scalar(paths[0])? [paths] : paths;
|
||||
pths = is_undef(paths)? [for (i=[0:len(points)-1]) i] : is_num(paths[0])? [paths] : paths;
|
||||
echo(points=points);
|
||||
echo(paths=paths);
|
||||
linear_extrude(height=0.01, convexity=convexity, center=true) {
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
/*
|
||||
BSD 2-Clause License
|
||||
|
||||
Copyright (c) 2017, Revar Desmera
|
||||
Copyright (c) 2017-2019, Revar Desmera
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
@ -61,7 +61,7 @@ module phillips_drive(size="#2", shaft=6, l=20, orient=ORIENT_Z, align=UP) {
|
|||
r = radidx == []? 0 : rads[radidx][1];
|
||||
h = (r/2)/tan(ang);
|
||||
cr = r/2;
|
||||
orient_and_align([shaft, shaft, l], orient, align) {
|
||||
orient_and_align([shaft, shaft, l], orient, align, chain=true) {
|
||||
down(l/2) {
|
||||
difference() {
|
||||
intersection() {
|
||||
|
@ -97,6 +97,7 @@ module phillips_drive(size="#2", shaft=6, l=20, orient=ORIENT_Z, align=UP) {
|
|||
}
|
||||
}
|
||||
}
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
/*
|
||||
BSD 2-Clause License
|
||||
|
||||
Copyright (c) 2017, Revar Desmera
|
||||
Copyright (c) 2017-2019, Revar Desmera
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
/*
|
||||
BSD 2-Clause License
|
||||
|
||||
Copyright (c) 2017, Revar Desmera
|
||||
Copyright (c) 2017-2019, Revar Desmera
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
|
44
shapes.scad
44
shapes.scad
|
@ -10,7 +10,7 @@
|
|||
/*
|
||||
BSD 2-Clause License
|
||||
|
||||
Copyright (c) 2017, Revar Desmera
|
||||
Copyright (c) 2017-2019, Revar Desmera
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
@ -87,8 +87,8 @@ module cuboid(
|
|||
center=undef
|
||||
) {
|
||||
size = scalar_vec3(size);
|
||||
if (is_def(p1)) {
|
||||
if (is_def(p2)) {
|
||||
if (!is_undef(p1)) {
|
||||
if (!is_undef(p2)) {
|
||||
translate([for (v=array_zip([p1,p2],0)) min(v)]) {
|
||||
cuboid(size=vabs(p2-p1), chamfer=chamfer, fillet=fillet, edges=edges, trimcorners=trimcorners, align=ALLPOS) children();
|
||||
}
|
||||
|
@ -98,8 +98,8 @@ module cuboid(
|
|||
}
|
||||
}
|
||||
} else {
|
||||
if (chamfer != undef) assertion(chamfer <= min(size)/2, "chamfer must be smaller than half the cube width, length, or height.");
|
||||
if (fillet != undef) assertion(fillet <= min(size)/2, "fillet must be smaller than half the cube width, length, or height.");
|
||||
if (chamfer != undef) assert(chamfer <= min(size)/2, "chamfer must be smaller than half the cube width, length, or height.");
|
||||
if (fillet != undef) assert(fillet <= min(size)/2, "fillet must be smaller than half the cube width, length, or height.");
|
||||
majrots = [[0,90,0], [90,0,0], [0,0,0]];
|
||||
orient_and_align(size, ORIENT_Z, align, center=center, noncentered=ALLPOS, chain=true) {
|
||||
if (chamfer != undef) {
|
||||
|
@ -608,30 +608,30 @@ module cyl(
|
|||
fil1 = first_defined([fillet1, fillet]);
|
||||
fil2 = first_defined([fillet2, fillet]);
|
||||
if (chamfer != undef) {
|
||||
assertion(chamfer <= r1, "chamfer is larger than the r1 radius of the cylinder.");
|
||||
assertion(chamfer <= r2, "chamfer is larger than the r2 radius of the cylinder.");
|
||||
assertion(chamfer <= l/2, "chamfer is larger than half the length of the cylinder.");
|
||||
assert(chamfer <= r1, "chamfer is larger than the r1 radius of the cylinder.");
|
||||
assert(chamfer <= r2, "chamfer is larger than the r2 radius of the cylinder.");
|
||||
assert(chamfer <= l/2, "chamfer is larger than half the length of the cylinder.");
|
||||
}
|
||||
if (cham1 != undef) {
|
||||
assertion(cham1 <= r1, "chamfer1 is larger than the r1 radius of the cylinder.");
|
||||
assertion(cham1 <= l/2, "chamfer1 is larger than half the length of the cylinder.");
|
||||
assert(cham1 <= r1, "chamfer1 is larger than the r1 radius of the cylinder.");
|
||||
assert(cham1 <= l/2, "chamfer1 is larger than half the length of the cylinder.");
|
||||
}
|
||||
if (cham2 != undef) {
|
||||
assertion(cham2 <= r2, "chamfer2 is larger than the r2 radius of the cylinder.");
|
||||
assertion(cham2 <= l/2, "chamfer2 is larger than half the length of the cylinder.");
|
||||
assert(cham2 <= r2, "chamfer2 is larger than the r2 radius of the cylinder.");
|
||||
assert(cham2 <= l/2, "chamfer2 is larger than half the length of the cylinder.");
|
||||
}
|
||||
if (fillet != undef) {
|
||||
assertion(fillet <= r1, "fillet is larger than the r1 radius of the cylinder.");
|
||||
assertion(fillet <= r2, "fillet is larger than the r2 radius of the cylinder.");
|
||||
assertion(fillet <= l/2, "fillet is larger than half the length of the cylinder.");
|
||||
assert(fillet <= r1, "fillet is larger than the r1 radius of the cylinder.");
|
||||
assert(fillet <= r2, "fillet is larger than the r2 radius of the cylinder.");
|
||||
assert(fillet <= l/2, "fillet is larger than half the length of the cylinder.");
|
||||
}
|
||||
if (fil1 != undef) {
|
||||
assertion(fil1 <= r1, "fillet1 is larger than the r1 radius of the cylinder.");
|
||||
assertion(fil1 <= l/2, "fillet1 is larger than half the length of the cylinder.");
|
||||
assert(fil1 <= r1, "fillet1 is larger than the r1 radius of the cylinder.");
|
||||
assert(fil1 <= l/2, "fillet1 is larger than half the length of the cylinder.");
|
||||
}
|
||||
if (fil2 != undef) {
|
||||
assertion(fil2 <= r2, "fillet2 is larger than the r1 radius of the cylinder.");
|
||||
assertion(fil2 <= l/2, "fillet2 is larger than half the length of the cylinder.");
|
||||
assert(fil2 <= r2, "fillet2 is larger than the r1 radius of the cylinder.");
|
||||
assert(fil2 <= l/2, "fillet2 is larger than half the length of the cylinder.");
|
||||
}
|
||||
|
||||
dy1 = first_defined([cham1, fil1, 0]);
|
||||
|
@ -924,8 +924,8 @@ module tube(
|
|||
r2 = first_defined([or2, od2/2, r2, d2/2, or, od/2, r, d/2, ir2+wall, id2/2+wall, ir+wall, id/2+wall]);
|
||||
ir1 = first_defined([ir1, id1/2, ir, id/2, r1-wall, d1/2-wall, r-wall, d/2-wall]);
|
||||
ir2 = first_defined([ir2, id2/2, ir, id/2, r2-wall, d2/2-wall, r-wall, d/2-wall]);
|
||||
assertion(ir1 <= r1, "Inner radius is larger than outer radius.");
|
||||
assertion(ir2 <= r2, "Inner radius is larger than outer radius.");
|
||||
assert(ir1 <= r1, "Inner radius is larger than outer radius.");
|
||||
assert(ir2 <= r2, "Inner radius is larger than outer radius.");
|
||||
sides = segs(max(r1,r2));
|
||||
size = [r1*2,r1*2,h];
|
||||
size2 = [r2*2,r2*2,h];
|
||||
|
@ -1113,7 +1113,7 @@ module teardrop2d(r=1, d=undef, ang=45, cap_h=undef)
|
|||
cord = 2 * r * cos(ang);
|
||||
cord_h = r * sin(ang);
|
||||
tip_y = (cord/2)/tan(ang);
|
||||
cap_h = min((is_def(cap_h)? cap_h : tip_y+cord_h), tip_y+cord_h);
|
||||
cap_h = min((!is_undef(cap_h)? cap_h : tip_y+cord_h), tip_y+cord_h);
|
||||
cap_w = cord * (1 - (cap_h - cord_h)/tip_y);
|
||||
difference() {
|
||||
hull() {
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
/*
|
||||
BSD 2-Clause License
|
||||
|
||||
Copyright (c) 2017, Revar Desmera
|
||||
Copyright (c) 2017-2019, Revar Desmera
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
@ -63,7 +63,7 @@ module slider(l=30, w=10, h=10, base=10, wall=5, ang=30, slop=PRINTER_SLOP, orie
|
|||
full_width = w + 2*wall;
|
||||
full_height = h + base;
|
||||
|
||||
orient_and_align([full_width, l, h+2*base], orient, align, orig_orient=ORIENT_Y) {
|
||||
orient_and_align([full_width, l, h+2*base], orient, align, orig_orient=ORIENT_Y, chain=true) {
|
||||
down(base+h/2) {
|
||||
// Base
|
||||
cuboid([full_width, l, base-slop], chamfer=2, edges=EDGE_TOP_FR+EDGE_TOP_BK+EDGES_Z_ALL, align=UP);
|
||||
|
@ -81,6 +81,7 @@ module slider(l=30, w=10, h=10, base=10, wall=5, ang=30, slop=PRINTER_SLOP, orie
|
|||
}
|
||||
}
|
||||
}
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -127,7 +128,7 @@ module rail(l=30, w=10, h=10, chamfer=1.0, ang=30, orient=ORIENT_Y, align=UP)
|
|||
y1 = l/2;
|
||||
y2 = y1 - attack_len * cos(attack_ang);
|
||||
|
||||
orient_and_align([w, l, h], orient, align, orig_orient=ORIENT_Y) {
|
||||
orient_and_align([w, l, h], orient, align, orig_orient=ORIENT_Y, chain=true) {
|
||||
polyhedron(
|
||||
convexity=4,
|
||||
points=[
|
||||
|
@ -222,6 +223,7 @@ module rail(l=30, w=10, h=10, chamfer=1.0, ang=30, orient=ORIENT_Y, align=UP)
|
|||
[13, 21, 6],
|
||||
]
|
||||
);
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
9
std.scad
9
std.scad
|
@ -10,7 +10,7 @@
|
|||
/*
|
||||
BSD 2-Clause License
|
||||
|
||||
Copyright (c) 2017, Revar Desmera
|
||||
Copyright (c) 2017-2019, Revar Desmera
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
@ -39,10 +39,15 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
include <BOSL2/constants.scad>
|
||||
include <BOSL2/compat.scad>
|
||||
include <BOSL2/math.scad>
|
||||
include <BOSL2/arrays.scad>
|
||||
include <BOSL2/vector.scad>
|
||||
include <BOSL2/matrices.scad>
|
||||
include <BOSL2/coords.scad>
|
||||
include <BOSL2/geometry.scad>
|
||||
include <BOSL2/transforms.scad>
|
||||
include <BOSL2/primitives.scad>
|
||||
include <BOSL2/shapes.scad>
|
||||
include <BOSL2/masks.scad>
|
||||
include <BOSL2/primitives.scad>
|
||||
|
||||
|
||||
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
/*
|
||||
BSD 2-Clause License
|
||||
|
||||
Copyright (c) 2017, Revar Desmera
|
||||
Copyright (c) 2017-2019, Revar Desmera
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
@ -218,12 +218,13 @@ module trapezoidal_threaded_rod(
|
|||
) otri
|
||||
]
|
||||
);
|
||||
orient_and_align([d,d,l], orient, align, center) {
|
||||
orient_and_align([d,d,l], orient, align, center, chain=true) {
|
||||
difference() {
|
||||
polyhedron(points=poly_points, faces=poly_faces, convexity=threads*starts*2);
|
||||
zspread(l+4*pitch*starts) cube([d+1, d+1, 4*pitch*starts], center=true);
|
||||
if (bevel) cylinder_mask(d=d, l=l+0.01, chamfer=depth);
|
||||
}
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -269,7 +270,7 @@ module trapezoidal_threaded_nut(
|
|||
align=CENTER
|
||||
) {
|
||||
depth = min((thread_depth==undef? pitch/2 : thread_depth), pitch/2/tan(thread_angle));
|
||||
orient_and_align([od/cos(30),od,h], orient, align) {
|
||||
orient_and_align([od/cos(30),od,h], orient, align, chain=true) {
|
||||
difference() {
|
||||
cylinder(d=od/cos(30), h=h, center=true, $fn=6);
|
||||
zspread(slop, n=slop>0?2:1) {
|
||||
|
@ -291,6 +292,7 @@ module trapezoidal_threaded_nut(
|
|||
}
|
||||
}
|
||||
}
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -320,7 +322,7 @@ module threaded_rod(d=10, l=100, pitch=2, left_handed=false, bevel=false, orient
|
|||
bevel=bevel,
|
||||
orient=orient,
|
||||
align=align
|
||||
);
|
||||
) children();
|
||||
}
|
||||
|
||||
|
||||
|
@ -354,7 +356,7 @@ module threaded_nut(
|
|||
left_handed=left_handed,
|
||||
bevel=bevel, slop=slop,
|
||||
orient=orient, align=align
|
||||
);
|
||||
) children();
|
||||
}
|
||||
|
||||
|
||||
|
@ -392,7 +394,7 @@ module metric_trapezoidal_threaded_rod(
|
|||
bevel=bevel,
|
||||
orient=orient,
|
||||
align=align
|
||||
);
|
||||
) children();
|
||||
}
|
||||
|
||||
|
||||
|
@ -433,7 +435,7 @@ module metric_trapezoidal_threaded_nut(
|
|||
slop=slop,
|
||||
orient=orient,
|
||||
align=align
|
||||
);
|
||||
) children();
|
||||
}
|
||||
|
||||
|
||||
|
@ -476,7 +478,7 @@ module acme_threaded_rod(
|
|||
bevel=bevel,
|
||||
orient=orient,
|
||||
align=align
|
||||
);
|
||||
) children();
|
||||
}
|
||||
|
||||
|
||||
|
@ -521,7 +523,7 @@ module acme_threaded_nut(
|
|||
slop=slop,
|
||||
orient=orient,
|
||||
align=align
|
||||
);
|
||||
) children();
|
||||
}
|
||||
|
||||
|
||||
|
@ -558,7 +560,7 @@ module square_threaded_rod(
|
|||
starts=starts,
|
||||
orient=orient,
|
||||
align=align
|
||||
);
|
||||
) children();
|
||||
}
|
||||
|
||||
|
||||
|
@ -599,7 +601,7 @@ module square_threaded_nut(
|
|||
slop=slop,
|
||||
orient=orient,
|
||||
align=align
|
||||
);
|
||||
) children();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
/*
|
||||
BSD 2-Clause License
|
||||
|
||||
Copyright (c) 2017, Revar Desmera
|
||||
Copyright (c) 2017-2019, Revar Desmera
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
@ -212,10 +212,11 @@ module torx_drive2d(size) {
|
|||
// torx_drive(size=30, l=10, $fa=1, $fs=1);
|
||||
module torx_drive(size, l=5, center=undef, orient=ORIENT_Z, align=UP) {
|
||||
od = torx_outer_diam(size);
|
||||
orient_and_align([od, od, l], orient, align, center) {
|
||||
orient_and_align([od, od, l], orient, align, center, chain=true) {
|
||||
linear_extrude(height=l, convexity=4, center=true) {
|
||||
torx_drive2d(size);
|
||||
}
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
/*
|
||||
BSD 2-Clause License
|
||||
|
||||
Copyright (c) 2017, Revar Desmera
|
||||
Copyright (c) 2017-2019, Revar Desmera
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
@ -267,10 +267,10 @@ module up(z=0) translate([0,0,z]) children();
|
|||
// rot(from=UP, to=LEFT+BACK) cube([2,4,9]);
|
||||
module rot(a=0, v=undef, cp=undef, from=undef, to=undef, reverse=false)
|
||||
{
|
||||
if (is_def(cp)) {
|
||||
if (!is_undef(cp)) {
|
||||
translate(cp) rot(a=a, v=v, from=from, to=to, reverse=reverse) translate(-cp) children();
|
||||
} else if (is_def(from)) {
|
||||
assertion(is_def(to), "`from` and `to` should be used together.");
|
||||
} else if (!is_undef(from)) {
|
||||
assert(!is_undef(to), "`from` and `to` should be used together.");
|
||||
axis = vector_axis(from, to);
|
||||
ang = vector_angle(from, to);
|
||||
if (ang < 0.0001 && a == 0) {
|
||||
|
@ -283,9 +283,9 @@ module rot(a=0, v=undef, cp=undef, from=undef, to=undef, reverse=false)
|
|||
} else if (a == 0) {
|
||||
children(); // May be slightly faster?
|
||||
} else if (reverse) {
|
||||
if (is_def(v)) {
|
||||
if (!is_undef(v)) {
|
||||
rotate(a=-a, v=v) children();
|
||||
} else if (is_scalar(a)) {
|
||||
} else if (is_num(a)) {
|
||||
rotate(-a) children();
|
||||
} else {
|
||||
rotate([-a[0],0,0]) rotate([0,-a[1],0]) rotate([0,0,-a[2]]) children();
|
||||
|
@ -315,7 +315,7 @@ module xrot(a=0, cp=undef)
|
|||
{
|
||||
if (a==0) {
|
||||
children(); // May be slightly faster?
|
||||
} else if (is_def(cp)) {
|
||||
} else if (!is_undef(cp)) {
|
||||
translate(cp) rotate([a, 0, 0]) translate(-cp) children();
|
||||
} else {
|
||||
rotate([a, 0, 0]) children();
|
||||
|
@ -342,7 +342,7 @@ module yrot(a=0, cp=undef)
|
|||
{
|
||||
if (a==0) {
|
||||
children(); // May be slightly faster?
|
||||
} else if (is_def(cp)) {
|
||||
} else if (!is_undef(cp)) {
|
||||
translate(cp) rotate([0, a, 0]) translate(-cp) children();
|
||||
} else {
|
||||
rotate([0, a, 0]) children();
|
||||
|
@ -369,7 +369,7 @@ module zrot(a=0, cp=undef)
|
|||
{
|
||||
if (a==0) {
|
||||
children(); // May be slightly faster?
|
||||
} else if (is_def(cp)) {
|
||||
} else if (!is_undef(cp)) {
|
||||
translate(cp) rotate(a) translate(-cp) children();
|
||||
} else {
|
||||
rotate(a) children();
|
||||
|
@ -637,23 +637,23 @@ module place_copies(a=[[0,0,0]])
|
|||
module spread(p1=undef, p2=undef, spacing=undef, l=undef, n=undef)
|
||||
{
|
||||
ll = (
|
||||
is_def(l)? scalar_vec3(l, 0) :
|
||||
(is_def(spacing) && is_def(n))? (n * scalar_vec3(spacing, 0)) :
|
||||
(is_def(p1) && is_def(p2))? point3d(p2-p1) :
|
||||
!is_undef(l)? scalar_vec3(l, 0) :
|
||||
(!is_undef(spacing) && !is_undef(n))? (n * scalar_vec3(spacing, 0)) :
|
||||
(!is_undef(p1) && !is_undef(p2))? point3d(p2-p1) :
|
||||
undef
|
||||
);
|
||||
cnt = (
|
||||
is_def(n)? n :
|
||||
(is_def(spacing) && is_def(ll))? floor(norm(ll) / norm(scalar_vec3(spacing, 0)) + 1.000001) :
|
||||
!is_undef(n)? n :
|
||||
(!is_undef(spacing) && !is_undef(ll))? floor(norm(ll) / norm(scalar_vec3(spacing, 0)) + 1.000001) :
|
||||
2
|
||||
);
|
||||
spc = (
|
||||
!is_def(spacing)? (ll/(cnt-1)) :
|
||||
is_scalar(spacing) && is_def(ll)? (ll/(cnt-1)) :
|
||||
is_undef(spacing)? (ll/(cnt-1)) :
|
||||
is_num(spacing) && !is_undef(ll)? (ll/(cnt-1)) :
|
||||
scalar_vec3(spacing, 0)
|
||||
);
|
||||
assertion(is_def(cnt), "Need two of `spacing`, 'l', 'n', or `p1`/`p2` arguments in `spread()`.");
|
||||
spos = is_def(p1)? point3d(p1) : -(cnt-1)/2 * spc;
|
||||
assert(!is_undef(cnt), "Need two of `spacing`, 'l', 'n', or `p1`/`p2` arguments in `spread()`.");
|
||||
spos = !is_undef(p1)? point3d(p1) : -(cnt-1)/2 * spc;
|
||||
for (i=[0 : cnt-1]) {
|
||||
pos = i * spc + spos;
|
||||
$pos = pos;
|
||||
|
@ -800,9 +800,9 @@ module zspread(spacing=undef, n=undef, l=undef, sp=undef)
|
|||
module distribute(spacing=undef, sizes=undef, dir=RIGHT, l=undef)
|
||||
{
|
||||
gaps = ($children < 2)? [0] :
|
||||
is_def(sizes)? [for (i=[0:$children-2]) sizes[i]/2 + sizes[i+1]/2] :
|
||||
!is_undef(sizes)? [for (i=[0:$children-2]) sizes[i]/2 + sizes[i+1]/2] :
|
||||
[for (i=[0:$children-2]) 0];
|
||||
spc = is_def(l)? ((l - sum(gaps)) / ($children-1)) : default(spacing, 10);
|
||||
spc = !is_undef(l)? ((l - sum(gaps)) / ($children-1)) : default(spacing, 10);
|
||||
gaps2 = [for (gap = gaps) gap+spc];
|
||||
spos = dir * -sum(gaps2)/2;
|
||||
for (i=[0:$children-1]) {
|
||||
|
@ -845,9 +845,9 @@ module xdistribute(spacing=10, sizes=undef, l=undef)
|
|||
{
|
||||
dir = RIGHT;
|
||||
gaps = ($children < 2)? [0] :
|
||||
is_def(sizes)? [for (i=[0:$children-2]) sizes[i]/2 + sizes[i+1]/2] :
|
||||
!is_undef(sizes)? [for (i=[0:$children-2]) sizes[i]/2 + sizes[i+1]/2] :
|
||||
[for (i=[0:$children-2]) 0];
|
||||
spc = is_def(l)? ((l - sum(gaps)) / ($children-1)) : default(spacing, 10);
|
||||
spc = !is_undef(l)? ((l - sum(gaps)) / ($children-1)) : default(spacing, 10);
|
||||
gaps2 = [for (gap = gaps) gap+spc];
|
||||
spos = dir * -sum(gaps2)/2;
|
||||
for (i=[0:$children-1]) {
|
||||
|
@ -890,9 +890,9 @@ module ydistribute(spacing=10, sizes=undef, l=undef)
|
|||
{
|
||||
dir = BACK;
|
||||
gaps = ($children < 2)? [0] :
|
||||
is_def(sizes)? [for (i=[0:$children-2]) sizes[i]/2 + sizes[i+1]/2] :
|
||||
!is_undef(sizes)? [for (i=[0:$children-2]) sizes[i]/2 + sizes[i+1]/2] :
|
||||
[for (i=[0:$children-2]) 0];
|
||||
spc = is_def(l)? ((l - sum(gaps)) / ($children-1)) : default(spacing, 10);
|
||||
spc = !is_undef(l)? ((l - sum(gaps)) / ($children-1)) : default(spacing, 10);
|
||||
gaps2 = [for (gap = gaps) gap+spc];
|
||||
spos = dir * -sum(gaps2)/2;
|
||||
for (i=[0:$children-1]) {
|
||||
|
@ -935,9 +935,9 @@ module zdistribute(spacing=10, sizes=undef, l=undef)
|
|||
{
|
||||
dir = UP;
|
||||
gaps = ($children < 2)? [0] :
|
||||
is_def(sizes)? [for (i=[0:$children-2]) sizes[i]/2 + sizes[i+1]/2] :
|
||||
!is_undef(sizes)? [for (i=[0:$children-2]) sizes[i]/2 + sizes[i+1]/2] :
|
||||
[for (i=[0:$children-2]) 0];
|
||||
spc = is_def(l)? ((l - sum(gaps)) / ($children-1)) : default(spacing, 10);
|
||||
spc = !is_undef(l)? ((l - sum(gaps)) / ($children-1)) : default(spacing, 10);
|
||||
gaps2 = [for (gap = gaps) gap+spc];
|
||||
spos = dir * -sum(gaps2)/2;
|
||||
for (i=[0:$children-1]) {
|
||||
|
@ -1006,9 +1006,9 @@ module grid2d(size=undef, spacing=undef, cols=undef, rows=undef, stagger=false,
|
|||
{
|
||||
assert_in_list("stagger", stagger, [false, true, "alt"]);
|
||||
scl = vmul(scalar_vec3(scale, 1), (stagger!=false? [0.5, sin(60), 0] : [1,1,0]));
|
||||
if (is_def(size)) {
|
||||
if (!is_undef(size)) {
|
||||
siz = scalar_vec3(size);
|
||||
if (is_def(spacing)) {
|
||||
if (!is_undef(spacing)) {
|
||||
spc = vmul(scalar_vec3(spacing), scl);
|
||||
maxcols = ceil(siz[0]/spc[0]);
|
||||
maxrows = ceil(siz[1]/spc[1]);
|
||||
|
@ -1018,11 +1018,11 @@ module grid2d(size=undef, spacing=undef, cols=undef, rows=undef, stagger=false,
|
|||
grid2d(spacing=spc, cols=cols, rows=rows, stagger=stagger, scale=scale, in_poly=in_poly, orient=orient, align=align) children();
|
||||
}
|
||||
} else {
|
||||
spc = is_array(spacing)? spacing : vmul(scalar_vec3(spacing), scl);
|
||||
bounds = is_def(in_poly)? pointlist_bounds(in_poly) : undef;
|
||||
bnds = is_def(bounds)? [for (a=[0:1]) 2*max(vabs([ for (i=[0,1]) bounds[i][a] ]))+1 ] : undef;
|
||||
mcols = is_def(cols)? cols : (is_def(spc) && is_def(bnds))? quantup(ceil(bnds[0]/spc[0])-1, 4)+1 : undef;
|
||||
mrows = is_def(rows)? rows : (is_def(spc) && is_def(bnds))? quantup(ceil(bnds[1]/spc[1])-1, 4)+1 : undef;
|
||||
spc = is_list(spacing)? spacing : vmul(scalar_vec3(spacing), scl);
|
||||
bounds = !is_undef(in_poly)? pointlist_bounds(in_poly) : undef;
|
||||
bnds = !is_undef(bounds)? [for (a=[0:1]) 2*max(vabs([ for (i=[0,1]) bounds[i][a] ]))+1 ] : undef;
|
||||
mcols = !is_undef(cols)? cols : (!is_undef(spc) && !is_undef(bnds))? quantup(ceil(bnds[0]/spc[0])-1, 4)+1 : undef;
|
||||
mrows = !is_undef(rows)? rows : (!is_undef(spc) && !is_undef(bnds))? quantup(ceil(bnds[1]/spc[1])-1, 4)+1 : undef;
|
||||
siz = vmul(spc, [mcols-1, mrows-1, 0]);
|
||||
staggermod = (stagger == "alt")? 1 : 0;
|
||||
if (stagger == false) {
|
||||
|
@ -1030,7 +1030,7 @@ module grid2d(size=undef, spacing=undef, cols=undef, rows=undef, stagger=false,
|
|||
for (row = [0:mrows-1]) {
|
||||
for (col = [0:mcols-1]) {
|
||||
pos = [col*spc[0], row*spc[1]] - point2d(siz/2);
|
||||
if (!is_def(in_poly) || point_in_polygon(pos, in_poly)>=0) {
|
||||
if (is_undef(in_poly) || point_in_polygon(pos, in_poly)>=0) {
|
||||
$col = col;
|
||||
$row = row;
|
||||
$pos = pos;
|
||||
|
@ -1050,7 +1050,7 @@ module grid2d(size=undef, spacing=undef, cols=undef, rows=undef, stagger=false,
|
|||
for (col = [0:rowcols-1]) {
|
||||
rowdx = (row%2 != staggermod)? spc[0] : 0;
|
||||
pos = [2*col*spc[0]+rowdx, row*spc[1]] - point2d(siz/2);
|
||||
if (!is_def(in_poly) || point_in_polygon(pos, in_poly)>=0) {
|
||||
if (is_undef(in_poly) || point_in_polygon(pos, in_poly)>=0) {
|
||||
$col = col * 2 + ((row%2!=staggermod)? 1 : 0);
|
||||
$row = row;
|
||||
$pos = pos;
|
||||
|
@ -1101,7 +1101,7 @@ module grid3d(xa=[0], ya=[0], za=[0], n=undef, spacing=undef)
|
|||
{
|
||||
n = scalar_vec3(n, 1);
|
||||
spacing = scalar_vec3(spacing, undef);
|
||||
if (is_def(n) && is_def(spacing)) {
|
||||
if (!is_undef(n) && !is_undef(spacing)) {
|
||||
for (xi = [0:n.x-1]) {
|
||||
for (yi = [0:n.y-1]) {
|
||||
for (zi = [0:n.z-1]) {
|
||||
|
@ -1182,7 +1182,7 @@ module rot_copies(rots=[], v=undef, cp=[0,0,0], count=undef, n=undef, sa=0, offs
|
|||
{
|
||||
cnt = first_defined([count, n]);
|
||||
sang = sa + offset;
|
||||
angs = is_def(cnt)? (cnt<=0? [] : [for (i=[0:cnt-1]) i/cnt*360+sang]) : rots;
|
||||
angs = !is_undef(cnt)? (cnt<=0? [] : [for (i=[0:cnt-1]) i/cnt*360+sang]) : rots;
|
||||
if (cp != [0,0,0]) {
|
||||
translate(cp) rot_copies(rots=rots, v=v, n=cnt, sa=sang, delta=delta, subrot=subrot) children();
|
||||
} else if (subrot) {
|
||||
|
@ -1756,7 +1756,7 @@ module zflip_copy(offset=0, cp=[0,0,0])
|
|||
// half_of([1,1], planar=true) circle(d=50);
|
||||
module half_of(v=UP, cp=[0,0,0], s=100, planar=false)
|
||||
{
|
||||
cp = is_scalar(cp)? cp*normalize(v) : cp;
|
||||
cp = is_num(cp)? cp*normalize(v) : cp;
|
||||
if (cp != [0,0,0]) {
|
||||
translate(cp) half_of(v=v, s=s, planar=planar) translate(-cp) children();
|
||||
} else if (planar) {
|
||||
|
@ -1801,7 +1801,7 @@ module half_of(v=UP, cp=[0,0,0], s=100, planar=false)
|
|||
module top_half(s=100, cp=[0,0,0], planar=false)
|
||||
{
|
||||
dir = planar? BACK : UP;
|
||||
cp = is_scalar(cp)? cp*dir : cp;
|
||||
cp = is_num(cp)? cp*dir : cp;
|
||||
translate(cp) difference() {
|
||||
translate(-cp) children();
|
||||
translate(-dir*s/2) {
|
||||
|
@ -1838,7 +1838,7 @@ module top_half(s=100, cp=[0,0,0], planar=false)
|
|||
module bottom_half(s=100, cp=[0,0,0], planar=false)
|
||||
{
|
||||
dir = planar? FWD : DOWN;
|
||||
cp = is_scalar(cp)? cp*dir : cp;
|
||||
cp = is_num(cp)? cp*dir : cp;
|
||||
translate(cp) difference() {
|
||||
translate(-cp) children();
|
||||
translate(-dir*s/2) {
|
||||
|
@ -1875,7 +1875,7 @@ module bottom_half(s=100, cp=[0,0,0], planar=false)
|
|||
module left_half(s=100, cp=[0,0,0], planar=false)
|
||||
{
|
||||
dir = LEFT;
|
||||
cp = is_scalar(cp)? cp*dir : cp;
|
||||
cp = is_num(cp)? cp*dir : cp;
|
||||
translate(cp) difference() {
|
||||
translate(-cp) children();
|
||||
translate(-dir*s/2) {
|
||||
|
@ -1912,7 +1912,7 @@ module left_half(s=100, cp=[0,0,0], planar=false)
|
|||
module right_half(s=100, cp=[0,0,0], planar=false)
|
||||
{
|
||||
dir = RIGHT;
|
||||
cp = is_scalar(cp)? cp*dir : cp;
|
||||
cp = is_num(cp)? cp*dir : cp;
|
||||
translate(cp) difference() {
|
||||
translate(-cp) children();
|
||||
translate(-dir*s/2) {
|
||||
|
@ -1949,7 +1949,7 @@ module right_half(s=100, cp=[0,0,0], planar=false)
|
|||
module front_half(s=100, cp=[0,0,0], planar=false)
|
||||
{
|
||||
dir = FWD;
|
||||
cp = is_scalar(cp)? cp*dir : cp;
|
||||
cp = is_num(cp)? cp*dir : cp;
|
||||
translate(cp) difference() {
|
||||
translate(-cp) children();
|
||||
translate(-dir*s/2) {
|
||||
|
@ -1986,7 +1986,7 @@ module front_half(s=100, cp=[0,0,0], planar=false)
|
|||
module back_half(s=100, cp=[0,0,0], planar=false)
|
||||
{
|
||||
dir = BACK;
|
||||
cp = is_scalar(cp)? cp*dir : cp;
|
||||
cp = is_num(cp)? cp*dir : cp;
|
||||
translate(cp) difference() {
|
||||
translate(-cp) children();
|
||||
translate(-dir*s/2) {
|
||||
|
@ -2156,7 +2156,7 @@ module round2d(r, or, ir)
|
|||
// shell2d(8,or=16,ir=8,round=16,fill=8) {square([40,100], center=true); square([100,40], center=true);}
|
||||
module shell2d(thickness, or=0, ir=0, fill=0, round=0)
|
||||
{
|
||||
thickness = is_scalar(thickness)? (
|
||||
thickness = is_num(thickness)? (
|
||||
thickness<0? [thickness,0] : [0,thickness]
|
||||
) : (thickness[0]>thickness[1])? (
|
||||
[thickness[1],thickness[0]]
|
||||
|
@ -2224,7 +2224,7 @@ module orient_and_align(
|
|||
) {
|
||||
size2 = point2d(default(size2, size));
|
||||
shift = point2d(shift);
|
||||
align = is_def(center)? (center? CENTER : noncentered) : align;
|
||||
align = !is_undef(center)? (center? CENTER : noncentered) : align;
|
||||
m = matrix4_mult(concat(
|
||||
(orig_align==CENTER)? [] : [
|
||||
// If original alignment is not centered, center it.
|
||||
|
@ -2316,7 +2316,7 @@ function find_connector(align, h, size, size2=undef, shift=[0,0], extra_conns=[]
|
|||
shift = point3d(shift),
|
||||
size = point3d(point2d(size)),
|
||||
size2 = (size2!=undef)? point3d(point2d(size2)) : size,
|
||||
found = !is_str(align)? [] : search([align], extra_conns, num_returns_per_match=1)[0]
|
||||
found = !is_string(align)? [] : search([align], extra_conns, num_returns_per_match=1)[0]
|
||||
) (found!=[])? extra_conns[found] : let(
|
||||
top = [-size2/2+shift, shift, size2/2+shift],
|
||||
bot = [-size/2, CENTER, size/2],
|
||||
|
@ -2356,7 +2356,7 @@ function find_connector(align, h, size, size2=undef, shift=[0,0], extra_conns=[]
|
|||
// }
|
||||
module attach(name, to=undef, overlap=undef, norot=false)
|
||||
{
|
||||
assertion($parent_size != undef, "No object to attach to!");
|
||||
assert($parent_size != undef, "No object to attach to!");
|
||||
overlap = (overlap!=undef)? overlap : $overlap;
|
||||
conn = find_connector(name, $parent_size.z, point2d($parent_size), size2=$parent_size2, shift=$parent_shift, extra_conns=$parent_conns);
|
||||
pos = conn[1];
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
/*
|
||||
BSD 2-Clause License
|
||||
|
||||
Copyright (c) 2017, Revar Desmera
|
||||
Copyright (c) 2017-2019, Revar Desmera
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
|
112
vectors.scad
Normal file
112
vectors.scad
Normal file
|
@ -0,0 +1,112 @@
|
|||
//////////////////////////////////////////////////////////////////////
|
||||
// LibFile: vectors.scad
|
||||
// Vector math functions.
|
||||
// To use, add the following lines to the beginning of your file:
|
||||
// ```
|
||||
// use <BOSL2/std.scad>
|
||||
// ```
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
BSD 2-Clause License
|
||||
|
||||
Copyright (c) 2017-2019, Revar Desmera
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
// Section: Vector Manipulation
|
||||
|
||||
// Function: vmul()
|
||||
// Description:
|
||||
// Element-wise vector multiplication. Multiplies each element of vector `v1` by
|
||||
// the corresponding element of vector `v2`. Returns a vector of the products.
|
||||
// Arguments:
|
||||
// v1 = The first vector.
|
||||
// v2 = The second vector.
|
||||
// Example:
|
||||
// vmul([3,4,5], [8,7,6]); // Returns [24, 28, 30]
|
||||
function vmul(v1, v2) = [for (i = [0:len(v1)-1]) v1[i]*v2[i]];
|
||||
|
||||
|
||||
// Function: vdiv()
|
||||
// Description:
|
||||
// Element-wise vector division. Divides each element of vector `v1` by
|
||||
// the corresponding element of vector `v2`. Returns a vector of the quotients.
|
||||
// Arguments:
|
||||
// v1 = The first vector.
|
||||
// v2 = The second vector.
|
||||
// Example:
|
||||
// vdiv([24,28,30], [8,7,6]); // Returns [3, 4, 5]
|
||||
function vdiv(v1, v2) = [for (i = [0:len(v1)-1]) v1[i]/v2[i]];
|
||||
|
||||
|
||||
// Function: vabs()
|
||||
// Description: Returns a vector of the absolute value of each element of vector `v`.
|
||||
// Arguments:
|
||||
// v = The vector to get the absolute values of.
|
||||
function vabs(v) = [for (x=v) abs(x)];
|
||||
|
||||
|
||||
// Function: normalize()
|
||||
// Description:
|
||||
// Returns unit length normalized version of vector v.
|
||||
// Arguments:
|
||||
// v = The vector to normalize.
|
||||
function normalize(v) = v/norm(v);
|
||||
|
||||
|
||||
// Function: vector_angle()
|
||||
// Usage:
|
||||
// vector_angle(v1,v2);
|
||||
// Description:
|
||||
// Returns angle in degrees between two vectors of similar dimensions.
|
||||
// Arguments:
|
||||
// v1 = First vector.
|
||||
// v2 = Second vector.
|
||||
// NOTE: constrain() corrects crazy FP rounding errors that exceed acos()'s domain.
|
||||
function vector_angle(v1,v2) = acos(constrain((v1*v2)/(norm(v1)*norm(v2)), -1, 1));
|
||||
|
||||
|
||||
// Function: vector_axis()
|
||||
// Usage:
|
||||
// vector_xis(v1,v2);
|
||||
// Description:
|
||||
// Returns the vector perpendicular to both of the given vectors.
|
||||
// Arguments:
|
||||
// v1 = First vector.
|
||||
// v2 = Second vector.
|
||||
function vector_axis(v1,v2) =
|
||||
let(
|
||||
eps = 1e-6,
|
||||
v1 = point3d(v1/norm(v1)),
|
||||
v2 = point3d(v2/norm(v2)),
|
||||
v3 = (norm(v1-v2) > eps && norm(v1+v2) > eps)? v2 :
|
||||
(norm(vabs(v2)-UP) > eps)? UP :
|
||||
RIGHT
|
||||
) normalize(cross(v1,v3));
|
||||
|
||||
|
||||
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap
|
|
@ -13,7 +13,7 @@
|
|||
/*
|
||||
BSD 2-Clause License
|
||||
|
||||
Copyright (c) 2017, Revar Desmera
|
||||
Copyright (c) 2017-2019, Revar Desmera
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
|
Loading…
Reference in a new issue