Question

I need to 'slice' a big path into two smaller paths using a line. For example, you may have the following configuration:

Example of slicing

After the operation, I should have two distinct closed paths. I probably should find two intersections and use Path.split function to split rectangle path, but I don't fully understand paper.js API and I am not sure about the best way to do that using exactly paper.js API.

For example, I split the original rectangle by doing the following commands:

 var starting_shape = new paper.Path.Rectangle([paper.view.center.x - 200, paper.view.center.y - 200], 400);
 starting_shape.strokeColor = "#aaa";
 starting_shape.strokeWidth = 2;
 starting_shape.fullySelected = true;

 var p1 = starting_shape.split(starting_shape.getNearestLocation([paper.view.center.x - 40, paper.view.center.y - 250]));
 var p2 = starting_shape.split(starting_shape.getNearestLocation([paper.view.center.x + 50, paper.view.center.y + 250]));

And I get the following:

Splitting rectangle

I tried to do the following:

p1.closed = true;
p2.closed = true;

p1.position.x += 10;

I got the necessary result:

Result

But is there a way to make it more clever?

Was it helpful?

Solution

Yes, you can use path.divide(path2) to perform a division boolean operation. If you clone the project from github, there's a test for all boolean functions in Examples > Scripts > BooleanOperations.html

I don't believe this currently works as you would like with just a line. It seems to be more stable with closed paths.

OTHER TIPS

The splitUsingPath function here can split in two a complex shape using path, even one with a curve.

const rectangle = new Shape.Rectangle(new Point(200, 200), new Size(300, 300)).toPath();
const path = new Path([
    new Point(300, 150),
    new Segment(new Point(325, 350), new Point(-90, -90), new Point(90, 90)),
    new Point(400, 550)
])

rectangle.strokeColor = 'black'
path.strokeColor = 'black'

const splitUsingPath = (target, path) => {
    const paths = [path];
    const targets = [target];
    
    const originalTarget = target.clone({ insert: false })
    const intersections = target.getIntersections(path)

    intersections.forEach(location => {
      const newTarget = target.splitAt(location)
      const isNew = newTarget !== target
      
      if (isNew) targets.push(newTarget)
    
      paths.forEach(path => {
          const offset = path.getOffsetOf(location.point)
          const pathLocation = path.getLocationAt(offset)

          if (pathLocation) {
                paths.push(path.splitAt(pathLocation))
          }
      })
    })

    const innerPath = paths.find(p => 
        originalTarget.contains(p.bounds.center))

    paths
        .filter(path => path !== innerPath)
        .forEach(item => item.remove())

    targets.forEach((target, i) => {
        const isFirst = i === 0
        const innerPathCopy = isFirst ? innerPath : innerPath.clone()
        
        target.join(innerPathCopy, innerPathCopy.length)
        target.closed = true
    })

    return targets
}

const splitPaths = splitUsingPath(rectangle, path)

splitPaths.forEach((path, i) => {
    path.position.x += i * -10
})

enter image description here

This is a great answer that I've used many times. I noticed a little room for improvement though. Sometimes the resulting sliced path has segments (those originated from the slicing path) in wrong order. That causes segment handles pointing to the opposite directions than intended and results in path deformation.

I added a check and fix:

...

targets.forEach((target, i) => {
  const isFirst = i === 0
  const innerPathCopy = isFirst ? innerPath : innerPath.clone()

  // THE FIX -------------------------------
  // Check if the starting point of the slicing path and the ending point of the target path are at the same point (or very near). 
  // If so, reverse the slicing path direction and fix the segment handle directions.

  if (innerPathCopy.getPointAt(0).isClose(target.getPointAt(target.length), 0.1)) innerPathCopy.reverse()

  // THE FIX -------------------------------    

  target.join(innerPathCopy, innerPathCopy.length)
  target.closed = true

 ...

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top