mirror of
https://github.com/BelfrySCAD/BOSL2.git
synced 2024-12-29 16:29:40 +00:00
list_remove_values, in_list bugfixes
remove quantization from region booleans (bugfix)
This commit is contained in:
parent
e7d07da426
commit
ea0ec79790
3 changed files with 151 additions and 76 deletions
79
lists.scad
79
lists.scad
|
@ -151,13 +151,25 @@ function list_shape(v, depth=undef) =
|
||||||
// a = in_list("bar", ["foo", "bar", "baz"]); // Returns true.
|
// a = in_list("bar", ["foo", "bar", "baz"]); // Returns true.
|
||||||
// b = in_list("bee", ["foo", "bar", "baz"]); // Returns false.
|
// b = in_list("bee", ["foo", "bar", "baz"]); // Returns false.
|
||||||
// c = in_list("bar", [[2,"foo"], [4,"bar"], [3,"baz"]], idx=1); // Returns true.
|
// c = in_list("bar", [[2,"foo"], [4,"bar"], [3,"baz"]], idx=1); // Returns true.
|
||||||
|
|
||||||
|
// Note: a huge complication occurs because OpenSCAD's search() finds
|
||||||
|
// index i as a hits if the val equals list[i] but also if val equals list[i][0].
|
||||||
|
// This means every hit needs to be checked to see if it's actually a hit,
|
||||||
|
// and if the first hit is a mismatch we have to keep searching.
|
||||||
|
// We assume that the normal case doesn't have mixed data, and try first
|
||||||
|
// with just one hit, but if this finds a mismatch then we try again
|
||||||
|
// with all hits, which could be slow for long lists.
|
||||||
function in_list(val,list,idx) =
|
function in_list(val,list,idx) =
|
||||||
assert( is_list(list) && (is_undef(idx) || is_finite(idx)),
|
assert(is_list(list),"Input is not a list")
|
||||||
"Invalid input." )
|
assert(is_undef(idx) || is_finite(idx), "Invalid idx value.")
|
||||||
let( s = search([val], list, num_returns_per_match=1, index_col_num=idx)[0] )
|
let( firsthit = search([val], list, num_returns_per_match=1, index_col_num=idx)[0] )
|
||||||
s==[] || s==[[]] ? false
|
firsthit==[] ? false
|
||||||
: is_undef(idx) ? val==list[s]
|
: is_undef(idx) && val==list[firsthit] ? true
|
||||||
: val==list[s][idx];
|
: is_def(idx) && val==list[firsthit][idx] ? true
|
||||||
|
// first hit was found but didn't match, so try again with all hits
|
||||||
|
: let ( allhits = search([val], list, 0, idx)[0])
|
||||||
|
is_undef(idx) ? [for(hit=allhits) if (list[hit]==val) 1] != []
|
||||||
|
: [for(hit=allhits) if (list[hit][idx]==val) 1] != [];
|
||||||
|
|
||||||
|
|
||||||
// Function: add_scalar()
|
// Function: add_scalar()
|
||||||
|
@ -723,7 +735,7 @@ function list_remove(list, ind) =
|
||||||
: assert( is_vector(ind), "Invalid index list in list_remove")
|
: assert( is_vector(ind), "Invalid index list in list_remove")
|
||||||
let(sres = search(count(list),ind,1))
|
let(sres = search(count(list),ind,1))
|
||||||
[
|
[
|
||||||
for(i=[0:len(list)-1])
|
for(i=idx(list))
|
||||||
if (sres[i] == [])
|
if (sres[i] == [])
|
||||||
list[i]
|
list[i]
|
||||||
];
|
];
|
||||||
|
@ -741,13 +753,22 @@ function list_remove(list, ind) =
|
||||||
// Topics: List Handling
|
// Topics: List Handling
|
||||||
// See Also: list_set(), list_insert(), list_remove()
|
// See Also: list_set(), list_insert(), list_remove()
|
||||||
// Description:
|
// Description:
|
||||||
// Removes the first, or all instances of the given `values` from the `list`.
|
// Removes the first, or all instances of the given value or list of values from the list.
|
||||||
// Returns the modified list.
|
// If you specify `all=false` and list a value twice then the first two instances will be removed.
|
||||||
|
// Note that if you want to remove a list value such as `[3,4]` then you must give it as
|
||||||
|
// a singleton list, or it will be interpreted as a list of two scalars to remove.
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// list = The list to modify.
|
// list = The list to modify.
|
||||||
// values = The values to remove from the list.
|
// values = The value or list of values to remove from the list.
|
||||||
// all = If true, remove all instances of the value `value` from the list `list`. If false, remove only the first. Default: false
|
// all = If true, remove all instances of the value `value` from the list `list`. If false, remove only the first. Default: false
|
||||||
// Example:
|
// Example:
|
||||||
|
// test = [3,4,[5,6],7,5,[5,6],4,[6,5],7,[4,4]];
|
||||||
|
// a=list_remove_values(test,4); // Returns: [3, [5, 6], 7, 5, [5, 6], 4, [6, 5], 7, [4, 4]]
|
||||||
|
// b=list_remove_values(test,[4,4]); // Returns: [3, [5, 6], 7, 5, [5, 6], [6, 5], 7, [4, 4]]
|
||||||
|
// c=list_remove_values(test,[4,7]); // Returns: [3, [5, 6], 5, [5, 6], 4, [6, 5], 7, [4, 4]]
|
||||||
|
// d=list_remove_values(test,[5,6]); // Returns: [3, 4, [5, 6], 7, [5, 6], 4, [6, 5], 7, [4, 4]]
|
||||||
|
// e=list_remove_values(test,[[5,6]]); // Returns: [3,4,7,5,[5,6],4,[6,5],7,[4,4]]
|
||||||
|
// f=list_remove_values(test,[[5,6]],all=true); // Returns: [3,4,7,5,4,[6,5],7,[4,4]]
|
||||||
// animals = ["bat", "cat", "rat", "dog", "bat", "rat"];
|
// animals = ["bat", "cat", "rat", "dog", "bat", "rat"];
|
||||||
// animals2 = list_remove_values(animals, "rat"); // Returns: ["bat","cat","dog","bat","rat"]
|
// animals2 = list_remove_values(animals, "rat"); // Returns: ["bat","cat","dog","bat","rat"]
|
||||||
// nonflying = list_remove_values(animals, "bat", all=true); // Returns: ["cat","rat","dog","rat"]
|
// nonflying = list_remove_values(animals, "bat", all=true); // Returns: ["cat","rat","dog","rat"]
|
||||||
|
@ -755,13 +776,39 @@ function list_remove(list, ind) =
|
||||||
// domestic = list_remove_values(animals, ["bat","rat"], all=true); // Returns: ["cat","dog"]
|
// domestic = list_remove_values(animals, ["bat","rat"], all=true); // Returns: ["cat","dog"]
|
||||||
// animals4 = list_remove_values(animals, ["tucan","rat"], all=true); // Returns: ["bat","cat","dog","bat"]
|
// animals4 = list_remove_values(animals, ["tucan","rat"], all=true); // Returns: ["bat","cat","dog","bat"]
|
||||||
function list_remove_values(list,values=[],all=false) =
|
function list_remove_values(list,values=[],all=false) =
|
||||||
assert(is_list(list))
|
|
||||||
!is_list(values)? list_remove_values(list, values=[values], all=all) :
|
!is_list(values)? list_remove_values(list, values=[values], all=all) :
|
||||||
let(
|
assert(is_list(list), "Invalid list")
|
||||||
idxs = all? flatten(search(values,list,0)) : search(values,list,1),
|
len(values)==0 ? list :
|
||||||
uidxs = unique(idxs)
|
len(values)==1 ?
|
||||||
) list_remove(list,uidxs);
|
(
|
||||||
|
!all ?
|
||||||
|
(
|
||||||
|
let(firsthit = search(values,list,1)[0])
|
||||||
|
firsthit==[] ? list
|
||||||
|
: list[firsthit]==values[0] ? list_remove(list,firsthit)
|
||||||
|
: let(allhits = search(values,list,0)[0],
|
||||||
|
allind = [for(i=allhits) if (list[i]==values[0]) i]
|
||||||
|
)
|
||||||
|
allind==[] ? list : list_remove(list,min(allind))
|
||||||
|
)
|
||||||
|
:
|
||||||
|
(
|
||||||
|
let(allhits = search(values,list,0)[0],
|
||||||
|
allind = [for(i=allhits) if (list[i]==values[0]) i]
|
||||||
|
)
|
||||||
|
allind==[] ? list : list_remove(list,allind)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
:!all ? list_remove_values(list_remove_values(list, values[0],all=all), list_tail(values),all=all)
|
||||||
|
:
|
||||||
|
[
|
||||||
|
for(i=idx(list))
|
||||||
|
let(hit=search([list[i]],values,0)[0])
|
||||||
|
if (hit==[]) list[i]
|
||||||
|
else
|
||||||
|
let(check = [for(j=hit) if (values[j]==list[i]) 1])
|
||||||
|
if (check==[]) list[i]
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
// Section: List Length Manipulation
|
// Section: List Length Manipulation
|
||||||
|
|
131
regions.scad
131
regions.scad
|
@ -840,40 +840,51 @@ function offset(
|
||||||
/// "S" - the subpath is on the 2nd region's border and the two regions interiors are on the same side of the subpath
|
/// "S" - the subpath is on the 2nd region's border and the two regions interiors are on the same side of the subpath
|
||||||
/// "U" - the subpath is on the 2nd region's border and the two regions meet at the subpath from opposite sides
|
/// "U" - the subpath is on the 2nd region's border and the two regions meet at the subpath from opposite sides
|
||||||
/// You specify which type of subpaths to keep with a string of the desired types such as "OS".
|
/// You specify which type of subpaths to keep with a string of the desired types such as "OS".
|
||||||
function _filter_region_parts(region1, region2, keep1, keep2, eps=EPSILON) =
|
function _filter_region_parts(region1, region2, keep, eps=EPSILON) =
|
||||||
// We have to compute common vertices between paths in the region because
|
// We have to compute common vertices between paths in the region because
|
||||||
// they can be places where the path must be cut, even though they aren't
|
// they can be places where the path must be cut, even though they aren't
|
||||||
// found my the split_path function.
|
// found my the split_path function.
|
||||||
let(
|
let(
|
||||||
keep = [keep1,keep2],
|
|
||||||
subpaths = split_region_at_region_crossings(region1,region2,eps=eps),
|
subpaths = split_region_at_region_crossings(region1,region2,eps=eps),
|
||||||
regions=[region1,region2]
|
regions=[force_region(region1),
|
||||||
|
force_region(region2)]
|
||||||
)
|
)
|
||||||
_assemble_path_fragments(
|
_assemble_path_fragments(
|
||||||
[for(i=[0:1])
|
[for(i=[0:1])
|
||||||
let(
|
let(
|
||||||
keepS = search("S",keep[i])!=[],
|
keepS = search("S",keep[i])!=[],
|
||||||
keepU = search("U",keep[i])!=[],
|
keepU = search("U",keep[i])!=[],
|
||||||
keepoutside = search("O",keep[i]) !=[],
|
keepoutside = search("O",keep[i]) !=[],
|
||||||
keepinside = search("I",keep[i]) !=[],
|
keepinside = search("I",keep[i]) !=[],
|
||||||
all_subpaths = flatten(subpaths[i])
|
all_subpaths = flatten(subpaths[i])
|
||||||
)
|
)
|
||||||
for (subpath = all_subpaths)
|
for (subpath = all_subpaths)
|
||||||
let(
|
let(
|
||||||
midpt = mean([subpath[0], subpath[1]]),
|
midpt = mean([subpath[0], subpath[1]]),
|
||||||
rel = point_in_region(midpt,regions[1-i],eps=eps),
|
rel = point_in_region(midpt,regions[1-i],eps=eps),
|
||||||
keepthis = rel<0 ? keepoutside
|
keepthis = rel<0 ? keepoutside
|
||||||
: rel>0 ? keepinside
|
: rel>0 ? keepinside
|
||||||
: !(keepS || keepU) ? false
|
: !(keepS || keepU) ? false
|
||||||
: let(
|
: let(
|
||||||
sidept = midpt + 0.01*line_normal(subpath[0],subpath[1]),
|
sidept = midpt + 0.01*line_normal(subpath[0],subpath[1]),
|
||||||
rel1 = point_in_region(sidept,region1,eps=eps)>0,
|
rel1 = point_in_region(sidept,regions[0],eps=eps)>0,
|
||||||
rel2 = point_in_region(sidept,region2,eps=eps)>0
|
rel2 = point_in_region(sidept,regions[1],eps=eps)>0
|
||||||
)
|
)
|
||||||
rel1==rel2 ? keepS : keepU
|
rel1==rel2 ? keepS : keepU
|
||||||
)
|
)
|
||||||
if (keepthis) subpath
|
if (keepthis) subpath
|
||||||
]);
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
function _list_three(a,b,c) =
|
||||||
|
is_undef(b) ? a :
|
||||||
|
[
|
||||||
|
a,
|
||||||
|
if (is_def(b)) b,
|
||||||
|
if (is_def(c)) c
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Function&Module: union()
|
// Function&Module: union()
|
||||||
|
@ -894,12 +905,12 @@ function _filter_region_parts(region1, region2, keep1, keep2, eps=EPSILON) =
|
||||||
// color("green") region(union(shape1,shape2));
|
// color("green") region(union(shape1,shape2));
|
||||||
// for (shape = [shape1,shape2]) color("red") stroke(shape, width=0.5, closed=true);
|
// for (shape = [shape1,shape2]) color("red") stroke(shape, width=0.5, closed=true);
|
||||||
function union(regions=[],b=undef,c=undef,eps=EPSILON) =
|
function union(regions=[],b=undef,c=undef,eps=EPSILON) =
|
||||||
b!=undef? union(concat([regions],[b],c==undef?[]:[c]), eps=eps) :
|
let(regions=_list_three(regions,b,c))
|
||||||
len(regions)==0? [] :
|
len(regions)==0? [] :
|
||||||
len(regions)==1? regions[0] :
|
len(regions)==1? regions[0] :
|
||||||
let(regions=[for (r=regions) quant(is_path(r)? [r] : r, 1/65536)])
|
let(regions=[for (r=regions) is_path(r)? [r] : r])
|
||||||
union([
|
union([
|
||||||
_filter_region_parts(regions[0],regions[1],"OS", "O", eps=eps),
|
_filter_region_parts(regions[0],regions[1],["OS", "O"], eps=eps),
|
||||||
for (i=[2:1:len(regions)-1]) regions[i]
|
for (i=[2:1:len(regions)-1]) regions[i]
|
||||||
],
|
],
|
||||||
eps=eps
|
eps=eps
|
||||||
|
@ -925,17 +936,17 @@ function union(regions=[],b=undef,c=undef,eps=EPSILON) =
|
||||||
// for (shape = [shape1,shape2]) color("red") stroke(shape, width=0.5, closed=true);
|
// for (shape = [shape1,shape2]) color("red") stroke(shape, width=0.5, closed=true);
|
||||||
// color("green") region(difference(shape1,shape2));
|
// color("green") region(difference(shape1,shape2));
|
||||||
function difference(regions=[],b=undef,c=undef,eps=EPSILON) =
|
function difference(regions=[],b=undef,c=undef,eps=EPSILON) =
|
||||||
b!=undef? difference(concat([regions],[b],c==undef?[]:[c]), eps=eps) :
|
let(regions = _list_three(regions,b,c))
|
||||||
len(regions)==0? [] :
|
len(regions)==0? []
|
||||||
len(regions)==1? regions[0] :
|
: len(regions)==1? regions[0]
|
||||||
regions[0]==[] ? [] :
|
: regions[0]==[] ? []
|
||||||
let(regions=[for (r=regions) quant(is_path(r)? [r] : r, 1/65536)])
|
: let(regions=[for (r=regions) is_path(r)? [r] : r])
|
||||||
difference([
|
difference([
|
||||||
_filter_region_parts(regions[0],regions[1],"OU", "I", eps=eps),
|
_filter_region_parts(regions[0],regions[1],["OU", "I"], eps=eps),
|
||||||
for (i=[2:1:len(regions)-1]) regions[i]
|
for (i=[2:1:len(regions)-1]) regions[i]
|
||||||
],
|
],
|
||||||
eps=eps
|
eps=eps
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
// Function&Module: intersection()
|
// Function&Module: intersection()
|
||||||
|
@ -956,17 +967,16 @@ function difference(regions=[],b=undef,c=undef,eps=EPSILON) =
|
||||||
// for (shape = [shape1,shape2]) color("red") stroke(shape, width=0.5, closed=true);
|
// for (shape = [shape1,shape2]) color("red") stroke(shape, width=0.5, closed=true);
|
||||||
// color("green") region(intersection(shape1,shape2));
|
// color("green") region(intersection(shape1,shape2));
|
||||||
function intersection(regions=[],b=undef,c=undef,eps=EPSILON) =
|
function intersection(regions=[],b=undef,c=undef,eps=EPSILON) =
|
||||||
b!=undef? intersection(concat([regions],[b],c==undef?[]:[c]),eps=eps)
|
let(regions = _list_three(regions,b,c))
|
||||||
: len(regions)==0 ? []
|
len(regions)==0 ? []
|
||||||
: len(regions)==1? regions[0]
|
: len(regions)==1? regions[0]
|
||||||
: regions[0]==[] || regions[1]==[] ? []
|
: regions[0]==[] || regions[1]==[] ? []
|
||||||
: let(regions=[for (r=regions) quant(is_path(r)? [r] : r, 1/65536)])
|
: intersection([
|
||||||
intersection([
|
_filter_region_parts(regions[0],regions[1],["IS","I"],eps=eps),
|
||||||
_filter_region_parts(regions[0],regions[1],"IS","I",eps=eps),
|
for (i=[2:1:len(regions)-1]) regions[i]
|
||||||
for (i=[2:1:len(regions)-1]) regions[i]
|
],
|
||||||
],
|
eps=eps
|
||||||
eps=eps
|
);
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -995,16 +1005,17 @@ function intersection(regions=[],b=undef,c=undef,eps=EPSILON) =
|
||||||
// circle(d=40);
|
// circle(d=40);
|
||||||
// }
|
// }
|
||||||
function exclusive_or(regions=[],b=undef,c=undef,eps=EPSILON) =
|
function exclusive_or(regions=[],b=undef,c=undef,eps=EPSILON) =
|
||||||
b!=undef? exclusive_or([regions, b, if(is_def(c)) c],eps=eps) :
|
let(regions = _list_three(regions,b,c))
|
||||||
len(regions)==0? [] :
|
len(regions)==0? []
|
||||||
len(regions)==1? regions[0] :
|
: len(regions)==1? regions[0]
|
||||||
let(regions=[for (r=regions) is_path(r)? [r] : r])
|
: regions[0]==[] ? exclusive_or(list_tail(regions))
|
||||||
exclusive_or([
|
: regions[1]==[] ? exclusive_or(list_remove(regions,1))
|
||||||
_filter_region_parts(regions[0],regions[1],"IO","IO",eps=eps),
|
: exclusive_or([
|
||||||
for (i=[2:1:len(regions)-1]) regions[i]
|
_filter_region_parts(regions[0],regions[1],["IO","IO"],eps=eps),
|
||||||
],
|
for (i=[2:1:len(regions)-1]) regions[i]
|
||||||
eps=eps
|
],
|
||||||
);
|
eps=eps
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
module exclusive_or() {
|
module exclusive_or() {
|
||||||
|
|
|
@ -174,6 +174,23 @@ module test_list_remove_values() {
|
||||||
assert(list_remove_values(animals, ["bat","rat"]) == ["cat","dog","bat","rat"]);
|
assert(list_remove_values(animals, ["bat","rat"]) == ["cat","dog","bat","rat"]);
|
||||||
assert(list_remove_values(animals, ["bat","rat"], all=true) == ["cat","dog"]);
|
assert(list_remove_values(animals, ["bat","rat"], all=true) == ["cat","dog"]);
|
||||||
assert(list_remove_values(animals, ["tucan","rat"], all=true) == ["bat","cat","dog","bat"]);
|
assert(list_remove_values(animals, ["tucan","rat"], all=true) == ["bat","cat","dog","bat"]);
|
||||||
|
|
||||||
|
test = [3,4,[5,6],7,5,[5,6],4,[6,5],7,[4,4]];
|
||||||
|
assert_equal(list_remove_values(test,4), [3, [5, 6], 7, 5, [5, 6], 4, [6, 5], 7, [4, 4]]);
|
||||||
|
assert_equal(list_remove_values(test,[4,4]), [3, [5, 6], 7, 5, [5, 6], [6, 5], 7, [4, 4]]);
|
||||||
|
assert_equal(list_remove_values(test,[4,7]), [3, [5, 6], 5, [5, 6], 4, [6, 5], 7, [4, 4]]);
|
||||||
|
assert_equal(list_remove_values(test,[5,6]), [3, 4, [5, 6], 7, [5, 6], 4, [6, 5], 7, [4, 4]]);
|
||||||
|
assert_equal(list_remove_values(test,[[5,6]]), [3,4,7,5,[5,6],4,[6,5],7,[4,4]]);
|
||||||
|
assert_equal(list_remove_values(test,[[5,6]],all=true), [3,4,7,5,4,[6,5],7,[4,4]]);
|
||||||
|
assert_equal(list_remove_values(test,4,all=true), [3, [5, 6], 7, 5, [5, 6], [6, 5],7, [4, 4]]);
|
||||||
|
assert_equal(list_remove_values(test,[4,7],all=true), [3, [5, 6], 5, [5, 6], [6, 5], [4, 4]]);
|
||||||
|
assert_equal(list_remove_values(test,[]),test);
|
||||||
|
assert_equal(list_remove_values(test,[],all=true),test);
|
||||||
|
assert_equal(list_remove_values(test,99), test);
|
||||||
|
assert_equal(list_remove_values(test,99,all=true), test);
|
||||||
|
assert_equal(list_remove_values(test,[99,100],all=true), test);
|
||||||
|
assert_equal(list_remove_values(test,[99,100]), test);
|
||||||
|
|
||||||
}
|
}
|
||||||
test_list_remove_values();
|
test_list_remove_values();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue