1
0
Fork 0

fix(core): Handle path splits on start or end points

In some edge cases, like when splitting a path on or very close to its
start or end point, `Path.split()` will return an array in which one of
the elements is not a Path object, but rather an empty array.

That is rather combersome to check for, so this changes that behaviour
and will return null for such cases.

I've also updated the documentation to clarify this behaviour.

Fixes #6816

This also fixes a bug when a path was being split on its end point.
This commit is contained in:
joostdecock 2024-06-12 13:24:42 +02:00
parent 7e9b8690b9
commit eded9e9d3a
3 changed files with 63 additions and 7 deletions

View file

@ -45,3 +45,25 @@ array path.split(Point splitPoint)
} }
``` ```
</Example> </Example>
## Notes
### The returned array will hold null for edge cases
Typically, the returned array will hold a `Path` object for each half.
But in some cases, one of the array entries can hold `null` if the split failed to find a path.
For example because you are splitting a `Path` on its start or end point.
```mjs
// Return value for a normal case
[Path, Path]
// Return value when calling Path.split() on/near the path's start point
[null, Path]
// Return value when calling Path.split() on/near the path's end point
[Path, null]
```
### This method will snap the split point to start or end points
This method will also _snap_ to the start or end point if you are splitting a path
(very) close to it, as it checks with [`Point.sitsRoughlyOn()`](/reference/api/point/sitsroughlyon).

View file

@ -898,7 +898,12 @@ Path.prototype.split = function (point) {
break break
} }
if (path.ops[1].type === 'line') { if (path.ops[1].type === 'line') {
if (pointOnLine(path.ops[0].to, path.ops[1].to, point)) { if (path.ops[1].to.sitsRoughlyOn(point)) {
pi++
firstHalf = divided.slice(0, pi)
secondHalf = divided.slice(pi)
break
} else if (pointOnLine(path.ops[0].to, path.ops[1].to, point)) {
firstHalf = divided.slice(0, pi) firstHalf = divided.slice(0, pi)
firstHalf.push(new Path().__withLog(this.log).move(path.ops[0].to).line(point)) firstHalf.push(new Path().__withLog(this.log).move(path.ops[0].to).line(point))
pi++ pi++
@ -953,8 +958,9 @@ Path.prototype.split = function (point) {
} }
} }
if (firstHalf.length > 0) firstHalf = __joinPaths(firstHalf) firstHalf = firstHalf.length > 0 && firstHalf[0].ops.length > 1 ? __joinPaths(firstHalf) : null
if (secondHalf.length > 0) secondHalf = __joinPaths(secondHalf) secondHalf =
secondHalf.length > 0 && secondHalf[0].ops.length > 1 ? __joinPaths(secondHalf) : null
return [firstHalf, secondHalf] return [firstHalf, secondHalf]
} }

View file

@ -661,10 +661,10 @@ describe('Path', () => {
const test = new Path().move(a).line(b).line(c) const test = new Path().move(a).line(b).line(c)
let halves = test.split(new Point(10.1, 29.9)) let halves = test.split(new Point(10.1, 29.9))
expect(halves[0].ops[1].to.x).to.equal(10.1) expect(halves[0].ops[1].to.x).to.equal(10)
expect(halves[0].ops[1].to.y).to.equal(29.9) expect(halves[0].ops[1].to.y).to.equal(30)
expect(halves[1].ops[0].to.x).to.equal(10.1) expect(halves[1].ops[0].to.x).to.equal(10)
expect(halves[1].ops[0].to.y).to.equal(29.9) expect(halves[1].ops[0].to.y).to.equal(30)
}) })
it('Should split a path on a curve joint', () => { it('Should split a path on a curve joint', () => {
@ -680,6 +680,34 @@ describe('Path', () => {
expect(halves[1].ops[0].to.y).to.equal(30) expect(halves[1].ops[0].to.y).to.equal(30)
}) })
// Issue 6816: https://github.com/freesewing/freesewing/issues/6816
it('Should split a path on the start of that same path', () => {
const A = new Point(45, 60)
const B = new Point(10, 30)
const test = new Path().move(A).line(B)
let halves = test.split(A)
expect(halves[0]).to.equal(null)
expect(halves[1].ops[0].to.x).to.equal(45)
expect(halves[1].ops[0].to.y).to.equal(60)
expect(halves[1].ops[1].to.x).to.equal(10)
expect(halves[1].ops[1].to.y).to.equal(30)
})
// Issue 6816: https://github.com/freesewing/freesewing/issues/6816
it('Should split a path on the end of that same path', () => {
const A = new Point(45, 60)
const B = new Point(10, 30)
const test = new Path().move(A).line(B)
let halves = test.split(B)
expect(halves[1]).to.equal(null)
expect(halves[0].ops[0].to.x).to.equal(45)
expect(halves[0].ops[0].to.y).to.equal(60)
expect(halves[0].ops[1].to.x).to.equal(10)
expect(halves[0].ops[1].to.y).to.equal(30)
})
it('Should determine the angle on a path', () => { it('Should determine the angle on a path', () => {
const a = new Point(0, 0) const a = new Point(0, 0)
const b = new Point(0, 40) const b = new Point(0, 40)