1
0
Fork 0

feat(core): Added Path.clean() method. Closes #3038 and #3056

This is a fix for bug #3038 which was investigated by @BenJamesBen
who also proposed a fix in PR #3056

However, after discussing the matter, we agreed it would be better
to have a generic method in core to guard against the issue of
spurious drawing operations.

This commit adds the `Path.clean()` method that does exactly that,
as well as its documentation.
This commit is contained in:
joostdecock 2022-12-03 17:25:19 +01:00
parent d9862326b2
commit fab1e2f877
2 changed files with 90 additions and 0 deletions

View file

@ -0,0 +1,61 @@
---
title: Path.clean()
---
The `Path.clean()` method removes spurious drawing operations from a path.
A _spurious_ drawing operation is one that has no effect, but can still cause
problems if left in place. For example, a line from a given point to the same
given point will not cause any problems as such, but can trip up things like
path offset and other methods. For this reason, such drawing operations can be
cleaned up with the `Path.clean()` method.
As this method is called under the hood to guard against various scenarios
where spurious segments could cause an issue, you should have no need to call
this method yourself explicitly, but it's there if you need it. path that you
pass it.
## Signature
```js
Path path.clean()
```
## Example
<Example caption="Example of the Path.clean() method">
```js
({ Point, points, Path, paths, snippets, Snippet, part }) => {
points.A = new Point(10, 10)
points.B = new Point(10, 20)
points.C = new Point(10, 30)
points.D = new Point(90, 10)
points.E = new Point(90, 20)
points.F = new Point(90, 30)
paths.a = new Path()
.move(points.A)
.line(points.C)
.line(points.B)
.line(points.B) // spurious op
.line(points.E)
.line(points.F)
.curve_(points.F, points.F) // another spurious op
.line(points.D)
.addClass('lining')
paths.b = paths.a
.clone()
.clean()
.addClass('interfacing')
paths.a.addText(`${paths.a.ops.length} ops in a`, 'center fill-lining')
paths.b.addText(`${paths.b.ops.length} ops in b`, 'center fill-note')
.attr('data-text-dy', 7)
return part
}
```
</Example>

View file

@ -166,6 +166,34 @@ Path.prototype.bbox = function () {
return __bbbbox(bbs)
}
/**
* Returns this after cleaning out in-place path operations
*
* Cleaned means that any in-place ops will be removed
* An in-place op is when a drawing operation doesn't draw anything
* like a line from the point to the same point
*
* @return {Path} this - This, but cleaned
*/
Path.prototype.clean = function () {
const ops = []
for (const i in this.ops) {
const op = this.ops[i]
if (['move', 'close', 'noop'].includes(op.type)) ops.push(op)
else if (op.type === 'line') {
if (!op.to.sitsRoughlyOn(cur)) ops.push(op)
} else if (op.type === 'curve') {
if (!(op.cp1.sitsRoughlyOn(cur) && op.cp2.sitsRoughlyOn(cur) && op.to.sitsRoughlyOn(cur)))
ops.push(ops)
}
const cur = op?.to
}
if (ops.length < this.ops.length) this.ops = ops
return this
}
/**
* Returns a deep copy of this path
*
@ -1117,6 +1145,7 @@ function __asPath(bezier, log = false) {
new Point(bezier.points[2].x, bezier.points[2].y),
new Point(bezier.points[3].x, bezier.points[3].y)
)
.clean()
}
/**