-
Notifications
You must be signed in to change notification settings - Fork 174
Description
Describe the bug
Things which rely on _partition_cutpath leave partial subpaths at the edges.
Here's the logic:
function _partition_cutpath(l, h, cutsize, cutpath, gap, cutpath_centered) =
let(
//--snip--
reps_raw = ceil(l/(cutsize.x+gap)),
reps = reps_raw%2==0 && cutpath_centered ? reps_raw+1 : reps_raw,
cplen = (cutsize.x+gap) * reps,
path = deduplicate(concat(
[[-l/2, cutpath[0].y*cutsize.y]],
[for (i=[0:1:reps-1], pt=cutpath) v_mul(pt,cutsize)+[i*(cutsize.x+gap)+gap/2-cplen/2,0]],
[[ l/2, cutpath[len(cutpath)-1].y*cutsize.y]]
)),
//--snip--
Code To Reproduce Bug
include <BOSL2/std.scad>
include <BOSL2/partitions.scad>
partition(cutsize=[10,5], cutpath="hammerhead")
cuboid([47,10,5]);
The interesting part is that 47 isn't divisible by 10.
Here's a more interesting example, pay particular attention to the gap = 3 line:
include <BOSL2/std.scad>
include <BOSL2/partitions.scad>
l = 47;
cutsize=[10,5];
ycopies(n=5, spacing=30)
{
gap = $idx;
xdistribute(25)
{
ydistribute(15)
{
partition(cutsize=cutsize, gap=gap, cutpath="dovetail") cuboid([l,10,5]);
}
fwd(10)
text(str(
"gap = ", gap,
"; cutsize.x+gap = ", cutsize.x+gap,
"; f = ", floor(l / (cutsize.x+gap)),
"; c = ", ceil(l / (cutsize.x+gap)),
"; rem = ", l % (cutsize.x+gap)));
}
}
It's a problem whenever the cutpath does not have x always go up.
Expected behavior
High-level: No extra floating bits at the ends.
More specifically, if there are reps subpaths, there will be reps-1 gaps. Pick the biggest reps such that reps*cutsize.x + (reps-1)*gap <= l and, if cutpath_centered, ensure reps is odd.
I think that means something like this:
function _partition_cutpath(l, h, cutsize, cutpath, gap, cutpath_centered) =
let(
check = assert(is_finite(l))
assert(is_finite(h))
assert(is_finite(gap))
assert(is_finite(cutsize) || is_vector(cutsize,2))
+ assert(l >= cutsize.x)
assert(is_string(cutpath) || is_path(cutpath,2)),
cutsize = is_vector(cutsize)? cutsize : [cutsize*2, cutsize],
cutpath = is_path(cutpath)? cutpath :
_partition_subpath(cutpath),
+ // pick the biggest reps_raw such that cplen will be <= l
+ reps_raw = 1+floor((l - cutsize.x)/(cutsize.x + gap)),
- reps_raw = ceil(l/(cutsize.x+gap)),
+ // if cutpath_centered, make sure reps is odd so a subpath will be centered on the cutpath
+ // be sure to always pick a smaller value so we don't truncate the path
+ reps = reps_raw%2==0 && cutpath_centered ? reps_raw-1 : reps_raw,
- reps = reps_raw%2==0 && cutpath_centered ? reps_raw+1 : reps_raw,
+ // there will be reps copies of the subpath and reps-1 gaps
+ cplen = reps*cutsize.x + (reps-1)*gap,
- cplen = (cutsize.x+gap) * reps,
path = deduplicate(concat(
- [[-cplen/2, cutpath[0].y*cutsize.y]],
+ [[-l/2, cutpath[0].y*cutsize.y]],
- [for (i=[0:1:reps-1], pt=cutpath) v_mul(pt,cutsize)+[i*(cutsize.x+gap)+gap/2-cplen/2,0]],
+ [for (i=[0:1:reps-1], pt=cutpath) v_mul(pt,cutsize)+[i*(cutsize.x+gap),0]],
- [[ l/2, cutpath[len(cutpath)-1].y*cutsize.y]]
+ [[ cplen/2, cutpath[len(cutpath)-1].y*cutsize.y]]
)),Even though I'm presenting the above as a diff, I haven't tested it. I don't know what the policy is for breaking changes, but I only started looking at this because I was wrestling with tweaking parameters to try to get the preview to look the way I wanted.
If you like this approach, I'm happy to make a PR and do some validation. I just wanted to start by checking to see if you need a more backward-compatible solution.
Screenshots
See above.
Additional context
Add any other context about the problem here.
- OpenSCAD Version: 2025.06.22 (git de4f48109)
- Other libraries used: none
There's some other odd stuff, not really related to this bug, in the function.
It takes an h parameter (and callers pass in the extrusion height) which isn't used.
I don't think this code is really necessary as long as the cutpath points have 0 <= x <= 1:
stidxs = [for (i = idx(path)) if (path[i].x < -l/2) i],
enidxs = [for (i = idx(path)) if (path[i].x > +l/2) i],
stidx = stidxs? last(stidxs) : 0,
enidx = enidxs? enidxs[0] : -1,
trunc = select(path, stidx, enidx)
But, if someone "scribbles outside the lines", silently truncating might be a surprise.