How to divide multiple shapes in Paper.js like in Illustrator with Pathfinderpaper.js random triangle shapeHow to extend Shape class in paperjs to create new shapes like polygonHow to start with paper.js?Grouping shapes in paper.jsPaper.js not drawing shapesPaper.js doesn't draw multiple shapesIn Paper.js, when .subtract divides the shape into multiple regions, is it possible to separate them?Toggling multiple tools on Paper.jsHow to change default unit in Paper.js?How to create a hole on a Path in Paper.js?
Is future tense in English really a myth?
Do Sobolev spaces contain nowhere differentiable functions?
Where on Earth is it easiest to survive in the wilderness?
Can you pop microwave popcorn on a stove?
How can I hint that my character isn't real?
More than three domains hosted on the same IP address
How to best explain that you are taking pictures in a space for practice reasons?
At what point does a land become controlled?
Round away from zero
Can taking my 1-week-old on a 6-7 hours journey in the car lead to medical complications?
How do English-speaking kids loudly request something?
What quests do you need to stop at before you make an enemy of a faction for each faction?
How to make a pipe-divided tuple?
Bit floating sequence
Get a MPS file using NEOS/GAMS web interface
Relationship between speed and cadence?
Compiler optimization of bitwise not operation
Why has Marx's "Das Kapital" been translated to "Capital" in English and not "The Capital"
Why do we buy the Mazur Swindle in knot theory?
Contractor cut joist hangers to make them fit
How should Thaumaturgy's "three times as loud as normal" be interpreted?
Project Euler Problem 45
How many attacks exactly do I get combining Dual Wielder feat with Two-Weapon Fighting style?
pipe command output to convert?
How to divide multiple shapes in Paper.js like in Illustrator with Pathfinder
paper.js random triangle shapeHow to extend Shape class in paperjs to create new shapes like polygonHow to start with paper.js?Grouping shapes in paper.jsPaper.js not drawing shapesPaper.js doesn't draw multiple shapesIn Paper.js, when .subtract divides the shape into multiple regions, is it possible to separate them?Toggling multiple tools on Paper.jsHow to change default unit in Paper.js?How to create a hole on a Path in Paper.js?
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty margin-bottom:0;
I have multiple overlapping squares in Paper.js, and I'd like to separate all the overlapping shapes into their own. You can do exactly this in Illustrator with the pathfinder divide. Before I attempt to just loop through all overlapping shapes and divide them with each other with what might have to be some nested loops I think, I'm wondering if there's a better way.
Example in Illustrator
I want to turn all these squares:
https://i.imgur.com/PPRi9M9.png
into pieces like this
https://i.imgur.com/xTFS8jP.png
(moved the pieces away from each other so you can see how they're separated)
paperjs
add a comment |
I have multiple overlapping squares in Paper.js, and I'd like to separate all the overlapping shapes into their own. You can do exactly this in Illustrator with the pathfinder divide. Before I attempt to just loop through all overlapping shapes and divide them with each other with what might have to be some nested loops I think, I'm wondering if there's a better way.
Example in Illustrator
I want to turn all these squares:
https://i.imgur.com/PPRi9M9.png
into pieces like this
https://i.imgur.com/xTFS8jP.png
(moved the pieces away from each other so you can see how they're separated)
paperjs
add a comment |
I have multiple overlapping squares in Paper.js, and I'd like to separate all the overlapping shapes into their own. You can do exactly this in Illustrator with the pathfinder divide. Before I attempt to just loop through all overlapping shapes and divide them with each other with what might have to be some nested loops I think, I'm wondering if there's a better way.
Example in Illustrator
I want to turn all these squares:
https://i.imgur.com/PPRi9M9.png
into pieces like this
https://i.imgur.com/xTFS8jP.png
(moved the pieces away from each other so you can see how they're separated)
paperjs
I have multiple overlapping squares in Paper.js, and I'd like to separate all the overlapping shapes into their own. You can do exactly this in Illustrator with the pathfinder divide. Before I attempt to just loop through all overlapping shapes and divide them with each other with what might have to be some nested loops I think, I'm wondering if there's a better way.
Example in Illustrator
I want to turn all these squares:
https://i.imgur.com/PPRi9M9.png
into pieces like this
https://i.imgur.com/xTFS8jP.png
(moved the pieces away from each other so you can see how they're separated)
paperjs
paperjs
asked Mar 28 at 6:12
jepjep
313 bronze badges
313 bronze badges
add a comment |
add a comment |
2 Answers
2
active
oldest
votes
I ended up going with my own solution which sounded more practical and simple than @arthur's answer. Not sure about which would be more performant though. To summarize, I map what blocks are overlapping with each other with a nested loop and Path.intersects(path), then do another nested loop to divide each block with its overlapping blocks with Path.divide(path) which will cut the original path with whatever path you're dividing it with.
Here's my actual code I'm using in my project with comments.
setupGrid()
// Setup block row and column positions
for (let i = 0;i < this.total;i++)
let x
let y
if (!odd(i))
x = firstColumnStartX + (this.size/2)
y = firstColumnStartY + ((i/2) * (this.size + this.gap)) + (this.size/2)
else
x = secondColumnStartX + (this.size/2)
y = secondColumnStartY + (Math.floor(i/2) * (this.size + this.gap)) + (this.size/2)
this.blocks.push(new paper.Path.Rectangle(
position: [x, y],
size: this.size,
strokeColor: '#ff000050'
))
// Setup array to check what blocks are intersecting
const intersects = []
// Setup empty array with a nested array mapped to other blocks [5 x [5 x undefined]]
for (let i = 0;i < this.total;i++)
intersects[i] = new Array(this.total).fill(undefined)
// Intersect checking
for (let i = 0;i < this.total;i++)
const block = this.blocks[i]
for (let _i = 0;_i < this.total;_i++)
const otherBlock = this.blocks[_i]
if (block !== otherBlock && intersects[i][_i] === undefined)
intersects[_i][i] = intersects[i][_i] = block.intersects(otherBlock)
// First loop through all blocks
for (let i = 0;i < this.total;i++)
let block = this.blocks[i]
// Then loop through other blocks only if they were intersected with the original block
for (let _i = 0;_i < this.total;_i++)
const otherBlock = this.blocks[_i]
if (intersects[i][_i])
/* divide returns
pieces: array of separated pieces that would be inside the original block's boundaries
leftoverBlock: what's leftover of the other block if the original block was subtracted from it
*/
const divide = this.divide(block, otherBlock)
block.remove()
otherBlock.remove()
// Override current block with the array of pieces
block = this.blocks[i] = divide.pieces
// Override other block with leftover
this.blocks[_i] = divide.leftoverBlock
// Don't let other block divide with original block since we already did it here
intersects[_i][i] = undefined
// Set random color for each piece to check if successful
for (let i = 0;i < this.blocks.length;i++)
let block = this.blocks[i]
if (block instanceof Array)
for (let _i = 0;_i < block.length;_i++)
block[_i].fillColor = new paper.Color(Math.random(), Math.random(), Math.random(), 0.1)
else
block.fillColor = new paper.Color(Math.random(), Math.random(), Math.random(), 0.1)
// Divide blockA with blockB and expand
divideBlocks(blockA, blockB, pieces = [])
const divideA = blockA.divide(blockB)
if (divideA instanceof paper.CompoundPath)
for (let i = divideA.children.length;i--;)
const child = divideA.children[i]
child.insertAbove(divideA)
pieces.push(child)
divideA.remove()
else
pieces.push(divideA)
return pieces
// Divide group (array of paths) with divider
divideGroup(children, divider, pieces = [], parent)
for (let i = children.length;i--;)
const child = children[i]
if (parent)
child.insertAbove(parent)
if (child.intersects(divider))
this.divideBlocks(child, divider, pieces)
else
pieces.push(child)
// Subtract group (array of paths) from block
subtractGroupFromBlock(block, group)
let oldBlock
let newBlock = block
for (let i = group.length;i--;)
const child = group[i]
if (child.intersects(block))
newBlock = newBlock.subtract(child)
if (oldBlock)
oldBlock.remove()
oldBlock = newBlock
return newBlock
// Check what kind of divide method to use
divide(blockA, blockB)
const pieces = []
let leftoverBlock
if (blockA instanceof paper.Path)
this.divideBlocks(blockA, blockB, pieces)
leftoverBlock = blockB.subtract(blockA)
else if (blockA instanceof Array)
this.divideGroup(blockA, blockB, pieces)
leftoverBlock = this.subtractGroupFromBlock(blockB, blockA)
return
pieces,
leftoverBlock
My blocks set with random colors to differentiate each shape:
Overlapping blocks before:
https://i.imgur.com/j9ZSUC5.png
Overlapping blocks separated into pieces:
https://i.imgur.com/mc83IH6.png
Did you check that your algorithm create the rectangle at the center of the 2nd image in your question ?
– arthur.sw
Mar 29 at 11:11
1
No it doesn't, I skipped that feature for now since I don't really need it in my current block setup. Thank you for your answer though I might try out your solution at some point if I need to.
– jep
Mar 29 at 21:59
add a comment |
Since you want to create shapes which did not exist before (according to your example, you want the operation to create the inner rectangle), I think you will have to loop over all overlapping shapes, compute intersections with Path.getIntersections(path[, include]), and re-create new paths from existing ones.
Once you computed all intersections, you will have to loop through all vertices, always rotating in the same direction, and create the new paths.
Take one (random) vertex, find the connected vertex "with the smallest angle" (it should work with currentVertex.getDirectedAngle(connectedVertex)
) ; set the current vertex as visited and continue until you find the first vertex again. Create a shape, and redo this algorithm until you visited all vertices.
You could also use Path.intersect(path[, options])
but I don't think it would help you.
add a comment |
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/4.0/"u003ecc by-sa 4.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55391163%2fhow-to-divide-multiple-shapes-in-paper-js-like-in-illustrator-with-pathfinder%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
I ended up going with my own solution which sounded more practical and simple than @arthur's answer. Not sure about which would be more performant though. To summarize, I map what blocks are overlapping with each other with a nested loop and Path.intersects(path), then do another nested loop to divide each block with its overlapping blocks with Path.divide(path) which will cut the original path with whatever path you're dividing it with.
Here's my actual code I'm using in my project with comments.
setupGrid()
// Setup block row and column positions
for (let i = 0;i < this.total;i++)
let x
let y
if (!odd(i))
x = firstColumnStartX + (this.size/2)
y = firstColumnStartY + ((i/2) * (this.size + this.gap)) + (this.size/2)
else
x = secondColumnStartX + (this.size/2)
y = secondColumnStartY + (Math.floor(i/2) * (this.size + this.gap)) + (this.size/2)
this.blocks.push(new paper.Path.Rectangle(
position: [x, y],
size: this.size,
strokeColor: '#ff000050'
))
// Setup array to check what blocks are intersecting
const intersects = []
// Setup empty array with a nested array mapped to other blocks [5 x [5 x undefined]]
for (let i = 0;i < this.total;i++)
intersects[i] = new Array(this.total).fill(undefined)
// Intersect checking
for (let i = 0;i < this.total;i++)
const block = this.blocks[i]
for (let _i = 0;_i < this.total;_i++)
const otherBlock = this.blocks[_i]
if (block !== otherBlock && intersects[i][_i] === undefined)
intersects[_i][i] = intersects[i][_i] = block.intersects(otherBlock)
// First loop through all blocks
for (let i = 0;i < this.total;i++)
let block = this.blocks[i]
// Then loop through other blocks only if they were intersected with the original block
for (let _i = 0;_i < this.total;_i++)
const otherBlock = this.blocks[_i]
if (intersects[i][_i])
/* divide returns
pieces: array of separated pieces that would be inside the original block's boundaries
leftoverBlock: what's leftover of the other block if the original block was subtracted from it
*/
const divide = this.divide(block, otherBlock)
block.remove()
otherBlock.remove()
// Override current block with the array of pieces
block = this.blocks[i] = divide.pieces
// Override other block with leftover
this.blocks[_i] = divide.leftoverBlock
// Don't let other block divide with original block since we already did it here
intersects[_i][i] = undefined
// Set random color for each piece to check if successful
for (let i = 0;i < this.blocks.length;i++)
let block = this.blocks[i]
if (block instanceof Array)
for (let _i = 0;_i < block.length;_i++)
block[_i].fillColor = new paper.Color(Math.random(), Math.random(), Math.random(), 0.1)
else
block.fillColor = new paper.Color(Math.random(), Math.random(), Math.random(), 0.1)
// Divide blockA with blockB and expand
divideBlocks(blockA, blockB, pieces = [])
const divideA = blockA.divide(blockB)
if (divideA instanceof paper.CompoundPath)
for (let i = divideA.children.length;i--;)
const child = divideA.children[i]
child.insertAbove(divideA)
pieces.push(child)
divideA.remove()
else
pieces.push(divideA)
return pieces
// Divide group (array of paths) with divider
divideGroup(children, divider, pieces = [], parent)
for (let i = children.length;i--;)
const child = children[i]
if (parent)
child.insertAbove(parent)
if (child.intersects(divider))
this.divideBlocks(child, divider, pieces)
else
pieces.push(child)
// Subtract group (array of paths) from block
subtractGroupFromBlock(block, group)
let oldBlock
let newBlock = block
for (let i = group.length;i--;)
const child = group[i]
if (child.intersects(block))
newBlock = newBlock.subtract(child)
if (oldBlock)
oldBlock.remove()
oldBlock = newBlock
return newBlock
// Check what kind of divide method to use
divide(blockA, blockB)
const pieces = []
let leftoverBlock
if (blockA instanceof paper.Path)
this.divideBlocks(blockA, blockB, pieces)
leftoverBlock = blockB.subtract(blockA)
else if (blockA instanceof Array)
this.divideGroup(blockA, blockB, pieces)
leftoverBlock = this.subtractGroupFromBlock(blockB, blockA)
return
pieces,
leftoverBlock
My blocks set with random colors to differentiate each shape:
Overlapping blocks before:
https://i.imgur.com/j9ZSUC5.png
Overlapping blocks separated into pieces:
https://i.imgur.com/mc83IH6.png
Did you check that your algorithm create the rectangle at the center of the 2nd image in your question ?
– arthur.sw
Mar 29 at 11:11
1
No it doesn't, I skipped that feature for now since I don't really need it in my current block setup. Thank you for your answer though I might try out your solution at some point if I need to.
– jep
Mar 29 at 21:59
add a comment |
I ended up going with my own solution which sounded more practical and simple than @arthur's answer. Not sure about which would be more performant though. To summarize, I map what blocks are overlapping with each other with a nested loop and Path.intersects(path), then do another nested loop to divide each block with its overlapping blocks with Path.divide(path) which will cut the original path with whatever path you're dividing it with.
Here's my actual code I'm using in my project with comments.
setupGrid()
// Setup block row and column positions
for (let i = 0;i < this.total;i++)
let x
let y
if (!odd(i))
x = firstColumnStartX + (this.size/2)
y = firstColumnStartY + ((i/2) * (this.size + this.gap)) + (this.size/2)
else
x = secondColumnStartX + (this.size/2)
y = secondColumnStartY + (Math.floor(i/2) * (this.size + this.gap)) + (this.size/2)
this.blocks.push(new paper.Path.Rectangle(
position: [x, y],
size: this.size,
strokeColor: '#ff000050'
))
// Setup array to check what blocks are intersecting
const intersects = []
// Setup empty array with a nested array mapped to other blocks [5 x [5 x undefined]]
for (let i = 0;i < this.total;i++)
intersects[i] = new Array(this.total).fill(undefined)
// Intersect checking
for (let i = 0;i < this.total;i++)
const block = this.blocks[i]
for (let _i = 0;_i < this.total;_i++)
const otherBlock = this.blocks[_i]
if (block !== otherBlock && intersects[i][_i] === undefined)
intersects[_i][i] = intersects[i][_i] = block.intersects(otherBlock)
// First loop through all blocks
for (let i = 0;i < this.total;i++)
let block = this.blocks[i]
// Then loop through other blocks only if they were intersected with the original block
for (let _i = 0;_i < this.total;_i++)
const otherBlock = this.blocks[_i]
if (intersects[i][_i])
/* divide returns
pieces: array of separated pieces that would be inside the original block's boundaries
leftoverBlock: what's leftover of the other block if the original block was subtracted from it
*/
const divide = this.divide(block, otherBlock)
block.remove()
otherBlock.remove()
// Override current block with the array of pieces
block = this.blocks[i] = divide.pieces
// Override other block with leftover
this.blocks[_i] = divide.leftoverBlock
// Don't let other block divide with original block since we already did it here
intersects[_i][i] = undefined
// Set random color for each piece to check if successful
for (let i = 0;i < this.blocks.length;i++)
let block = this.blocks[i]
if (block instanceof Array)
for (let _i = 0;_i < block.length;_i++)
block[_i].fillColor = new paper.Color(Math.random(), Math.random(), Math.random(), 0.1)
else
block.fillColor = new paper.Color(Math.random(), Math.random(), Math.random(), 0.1)
// Divide blockA with blockB and expand
divideBlocks(blockA, blockB, pieces = [])
const divideA = blockA.divide(blockB)
if (divideA instanceof paper.CompoundPath)
for (let i = divideA.children.length;i--;)
const child = divideA.children[i]
child.insertAbove(divideA)
pieces.push(child)
divideA.remove()
else
pieces.push(divideA)
return pieces
// Divide group (array of paths) with divider
divideGroup(children, divider, pieces = [], parent)
for (let i = children.length;i--;)
const child = children[i]
if (parent)
child.insertAbove(parent)
if (child.intersects(divider))
this.divideBlocks(child, divider, pieces)
else
pieces.push(child)
// Subtract group (array of paths) from block
subtractGroupFromBlock(block, group)
let oldBlock
let newBlock = block
for (let i = group.length;i--;)
const child = group[i]
if (child.intersects(block))
newBlock = newBlock.subtract(child)
if (oldBlock)
oldBlock.remove()
oldBlock = newBlock
return newBlock
// Check what kind of divide method to use
divide(blockA, blockB)
const pieces = []
let leftoverBlock
if (blockA instanceof paper.Path)
this.divideBlocks(blockA, blockB, pieces)
leftoverBlock = blockB.subtract(blockA)
else if (blockA instanceof Array)
this.divideGroup(blockA, blockB, pieces)
leftoverBlock = this.subtractGroupFromBlock(blockB, blockA)
return
pieces,
leftoverBlock
My blocks set with random colors to differentiate each shape:
Overlapping blocks before:
https://i.imgur.com/j9ZSUC5.png
Overlapping blocks separated into pieces:
https://i.imgur.com/mc83IH6.png
Did you check that your algorithm create the rectangle at the center of the 2nd image in your question ?
– arthur.sw
Mar 29 at 11:11
1
No it doesn't, I skipped that feature for now since I don't really need it in my current block setup. Thank you for your answer though I might try out your solution at some point if I need to.
– jep
Mar 29 at 21:59
add a comment |
I ended up going with my own solution which sounded more practical and simple than @arthur's answer. Not sure about which would be more performant though. To summarize, I map what blocks are overlapping with each other with a nested loop and Path.intersects(path), then do another nested loop to divide each block with its overlapping blocks with Path.divide(path) which will cut the original path with whatever path you're dividing it with.
Here's my actual code I'm using in my project with comments.
setupGrid()
// Setup block row and column positions
for (let i = 0;i < this.total;i++)
let x
let y
if (!odd(i))
x = firstColumnStartX + (this.size/2)
y = firstColumnStartY + ((i/2) * (this.size + this.gap)) + (this.size/2)
else
x = secondColumnStartX + (this.size/2)
y = secondColumnStartY + (Math.floor(i/2) * (this.size + this.gap)) + (this.size/2)
this.blocks.push(new paper.Path.Rectangle(
position: [x, y],
size: this.size,
strokeColor: '#ff000050'
))
// Setup array to check what blocks are intersecting
const intersects = []
// Setup empty array with a nested array mapped to other blocks [5 x [5 x undefined]]
for (let i = 0;i < this.total;i++)
intersects[i] = new Array(this.total).fill(undefined)
// Intersect checking
for (let i = 0;i < this.total;i++)
const block = this.blocks[i]
for (let _i = 0;_i < this.total;_i++)
const otherBlock = this.blocks[_i]
if (block !== otherBlock && intersects[i][_i] === undefined)
intersects[_i][i] = intersects[i][_i] = block.intersects(otherBlock)
// First loop through all blocks
for (let i = 0;i < this.total;i++)
let block = this.blocks[i]
// Then loop through other blocks only if they were intersected with the original block
for (let _i = 0;_i < this.total;_i++)
const otherBlock = this.blocks[_i]
if (intersects[i][_i])
/* divide returns
pieces: array of separated pieces that would be inside the original block's boundaries
leftoverBlock: what's leftover of the other block if the original block was subtracted from it
*/
const divide = this.divide(block, otherBlock)
block.remove()
otherBlock.remove()
// Override current block with the array of pieces
block = this.blocks[i] = divide.pieces
// Override other block with leftover
this.blocks[_i] = divide.leftoverBlock
// Don't let other block divide with original block since we already did it here
intersects[_i][i] = undefined
// Set random color for each piece to check if successful
for (let i = 0;i < this.blocks.length;i++)
let block = this.blocks[i]
if (block instanceof Array)
for (let _i = 0;_i < block.length;_i++)
block[_i].fillColor = new paper.Color(Math.random(), Math.random(), Math.random(), 0.1)
else
block.fillColor = new paper.Color(Math.random(), Math.random(), Math.random(), 0.1)
// Divide blockA with blockB and expand
divideBlocks(blockA, blockB, pieces = [])
const divideA = blockA.divide(blockB)
if (divideA instanceof paper.CompoundPath)
for (let i = divideA.children.length;i--;)
const child = divideA.children[i]
child.insertAbove(divideA)
pieces.push(child)
divideA.remove()
else
pieces.push(divideA)
return pieces
// Divide group (array of paths) with divider
divideGroup(children, divider, pieces = [], parent)
for (let i = children.length;i--;)
const child = children[i]
if (parent)
child.insertAbove(parent)
if (child.intersects(divider))
this.divideBlocks(child, divider, pieces)
else
pieces.push(child)
// Subtract group (array of paths) from block
subtractGroupFromBlock(block, group)
let oldBlock
let newBlock = block
for (let i = group.length;i--;)
const child = group[i]
if (child.intersects(block))
newBlock = newBlock.subtract(child)
if (oldBlock)
oldBlock.remove()
oldBlock = newBlock
return newBlock
// Check what kind of divide method to use
divide(blockA, blockB)
const pieces = []
let leftoverBlock
if (blockA instanceof paper.Path)
this.divideBlocks(blockA, blockB, pieces)
leftoverBlock = blockB.subtract(blockA)
else if (blockA instanceof Array)
this.divideGroup(blockA, blockB, pieces)
leftoverBlock = this.subtractGroupFromBlock(blockB, blockA)
return
pieces,
leftoverBlock
My blocks set with random colors to differentiate each shape:
Overlapping blocks before:
https://i.imgur.com/j9ZSUC5.png
Overlapping blocks separated into pieces:
https://i.imgur.com/mc83IH6.png
I ended up going with my own solution which sounded more practical and simple than @arthur's answer. Not sure about which would be more performant though. To summarize, I map what blocks are overlapping with each other with a nested loop and Path.intersects(path), then do another nested loop to divide each block with its overlapping blocks with Path.divide(path) which will cut the original path with whatever path you're dividing it with.
Here's my actual code I'm using in my project with comments.
setupGrid()
// Setup block row and column positions
for (let i = 0;i < this.total;i++)
let x
let y
if (!odd(i))
x = firstColumnStartX + (this.size/2)
y = firstColumnStartY + ((i/2) * (this.size + this.gap)) + (this.size/2)
else
x = secondColumnStartX + (this.size/2)
y = secondColumnStartY + (Math.floor(i/2) * (this.size + this.gap)) + (this.size/2)
this.blocks.push(new paper.Path.Rectangle(
position: [x, y],
size: this.size,
strokeColor: '#ff000050'
))
// Setup array to check what blocks are intersecting
const intersects = []
// Setup empty array with a nested array mapped to other blocks [5 x [5 x undefined]]
for (let i = 0;i < this.total;i++)
intersects[i] = new Array(this.total).fill(undefined)
// Intersect checking
for (let i = 0;i < this.total;i++)
const block = this.blocks[i]
for (let _i = 0;_i < this.total;_i++)
const otherBlock = this.blocks[_i]
if (block !== otherBlock && intersects[i][_i] === undefined)
intersects[_i][i] = intersects[i][_i] = block.intersects(otherBlock)
// First loop through all blocks
for (let i = 0;i < this.total;i++)
let block = this.blocks[i]
// Then loop through other blocks only if they were intersected with the original block
for (let _i = 0;_i < this.total;_i++)
const otherBlock = this.blocks[_i]
if (intersects[i][_i])
/* divide returns
pieces: array of separated pieces that would be inside the original block's boundaries
leftoverBlock: what's leftover of the other block if the original block was subtracted from it
*/
const divide = this.divide(block, otherBlock)
block.remove()
otherBlock.remove()
// Override current block with the array of pieces
block = this.blocks[i] = divide.pieces
// Override other block with leftover
this.blocks[_i] = divide.leftoverBlock
// Don't let other block divide with original block since we already did it here
intersects[_i][i] = undefined
// Set random color for each piece to check if successful
for (let i = 0;i < this.blocks.length;i++)
let block = this.blocks[i]
if (block instanceof Array)
for (let _i = 0;_i < block.length;_i++)
block[_i].fillColor = new paper.Color(Math.random(), Math.random(), Math.random(), 0.1)
else
block.fillColor = new paper.Color(Math.random(), Math.random(), Math.random(), 0.1)
// Divide blockA with blockB and expand
divideBlocks(blockA, blockB, pieces = [])
const divideA = blockA.divide(blockB)
if (divideA instanceof paper.CompoundPath)
for (let i = divideA.children.length;i--;)
const child = divideA.children[i]
child.insertAbove(divideA)
pieces.push(child)
divideA.remove()
else
pieces.push(divideA)
return pieces
// Divide group (array of paths) with divider
divideGroup(children, divider, pieces = [], parent)
for (let i = children.length;i--;)
const child = children[i]
if (parent)
child.insertAbove(parent)
if (child.intersects(divider))
this.divideBlocks(child, divider, pieces)
else
pieces.push(child)
// Subtract group (array of paths) from block
subtractGroupFromBlock(block, group)
let oldBlock
let newBlock = block
for (let i = group.length;i--;)
const child = group[i]
if (child.intersects(block))
newBlock = newBlock.subtract(child)
if (oldBlock)
oldBlock.remove()
oldBlock = newBlock
return newBlock
// Check what kind of divide method to use
divide(blockA, blockB)
const pieces = []
let leftoverBlock
if (blockA instanceof paper.Path)
this.divideBlocks(blockA, blockB, pieces)
leftoverBlock = blockB.subtract(blockA)
else if (blockA instanceof Array)
this.divideGroup(blockA, blockB, pieces)
leftoverBlock = this.subtractGroupFromBlock(blockB, blockA)
return
pieces,
leftoverBlock
My blocks set with random colors to differentiate each shape:
Overlapping blocks before:
https://i.imgur.com/j9ZSUC5.png
Overlapping blocks separated into pieces:
https://i.imgur.com/mc83IH6.png
answered Mar 29 at 1:30
jepjep
313 bronze badges
313 bronze badges
Did you check that your algorithm create the rectangle at the center of the 2nd image in your question ?
– arthur.sw
Mar 29 at 11:11
1
No it doesn't, I skipped that feature for now since I don't really need it in my current block setup. Thank you for your answer though I might try out your solution at some point if I need to.
– jep
Mar 29 at 21:59
add a comment |
Did you check that your algorithm create the rectangle at the center of the 2nd image in your question ?
– arthur.sw
Mar 29 at 11:11
1
No it doesn't, I skipped that feature for now since I don't really need it in my current block setup. Thank you for your answer though I might try out your solution at some point if I need to.
– jep
Mar 29 at 21:59
Did you check that your algorithm create the rectangle at the center of the 2nd image in your question ?
– arthur.sw
Mar 29 at 11:11
Did you check that your algorithm create the rectangle at the center of the 2nd image in your question ?
– arthur.sw
Mar 29 at 11:11
1
1
No it doesn't, I skipped that feature for now since I don't really need it in my current block setup. Thank you for your answer though I might try out your solution at some point if I need to.
– jep
Mar 29 at 21:59
No it doesn't, I skipped that feature for now since I don't really need it in my current block setup. Thank you for your answer though I might try out your solution at some point if I need to.
– jep
Mar 29 at 21:59
add a comment |
Since you want to create shapes which did not exist before (according to your example, you want the operation to create the inner rectangle), I think you will have to loop over all overlapping shapes, compute intersections with Path.getIntersections(path[, include]), and re-create new paths from existing ones.
Once you computed all intersections, you will have to loop through all vertices, always rotating in the same direction, and create the new paths.
Take one (random) vertex, find the connected vertex "with the smallest angle" (it should work with currentVertex.getDirectedAngle(connectedVertex)
) ; set the current vertex as visited and continue until you find the first vertex again. Create a shape, and redo this algorithm until you visited all vertices.
You could also use Path.intersect(path[, options])
but I don't think it would help you.
add a comment |
Since you want to create shapes which did not exist before (according to your example, you want the operation to create the inner rectangle), I think you will have to loop over all overlapping shapes, compute intersections with Path.getIntersections(path[, include]), and re-create new paths from existing ones.
Once you computed all intersections, you will have to loop through all vertices, always rotating in the same direction, and create the new paths.
Take one (random) vertex, find the connected vertex "with the smallest angle" (it should work with currentVertex.getDirectedAngle(connectedVertex)
) ; set the current vertex as visited and continue until you find the first vertex again. Create a shape, and redo this algorithm until you visited all vertices.
You could also use Path.intersect(path[, options])
but I don't think it would help you.
add a comment |
Since you want to create shapes which did not exist before (according to your example, you want the operation to create the inner rectangle), I think you will have to loop over all overlapping shapes, compute intersections with Path.getIntersections(path[, include]), and re-create new paths from existing ones.
Once you computed all intersections, you will have to loop through all vertices, always rotating in the same direction, and create the new paths.
Take one (random) vertex, find the connected vertex "with the smallest angle" (it should work with currentVertex.getDirectedAngle(connectedVertex)
) ; set the current vertex as visited and continue until you find the first vertex again. Create a shape, and redo this algorithm until you visited all vertices.
You could also use Path.intersect(path[, options])
but I don't think it would help you.
Since you want to create shapes which did not exist before (according to your example, you want the operation to create the inner rectangle), I think you will have to loop over all overlapping shapes, compute intersections with Path.getIntersections(path[, include]), and re-create new paths from existing ones.
Once you computed all intersections, you will have to loop through all vertices, always rotating in the same direction, and create the new paths.
Take one (random) vertex, find the connected vertex "with the smallest angle" (it should work with currentVertex.getDirectedAngle(connectedVertex)
) ; set the current vertex as visited and continue until you find the first vertex again. Create a shape, and redo this algorithm until you visited all vertices.
You could also use Path.intersect(path[, options])
but I don't think it would help you.
answered Mar 28 at 10:50
arthur.swarthur.sw
6,2415 gold badges29 silver badges66 bronze badges
6,2415 gold badges29 silver badges66 bronze badges
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55391163%2fhow-to-divide-multiple-shapes-in-paper-js-like-in-illustrator-with-pathfinder%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown