Slice path into two separate paths using paper.js - javascript

I need to 'slice' a big path into two smaller paths using a line. For example, you may have the following configuration:
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:
I tried to do the following:
p1.closed = true;
p2.closed = true;
p1.position.x += 10;
I got the necessary result:
But is there a way to make it more clever?

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.

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
})

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
...

Related

All mesh instances reset to (0,0,0) before they lerp to new position when count changes

I am currently working on my graduation project in which mesh instances are re positioned over time. Besides the positions of the instances, the count of the mesh instances can also change over time.
Based on the following code examples, I managed to build this functionality.
https://jsfiddle.net/ew1tyz63/2/
https://threejs.org/examples/?q=dynami#webgl_instancing_dynamic
However, the problem appears when I want to lerp the positions of the instances. The position of all instances is resets to (0,0,0) when the count of the mesh instances changes.
I've created a codesandbox that reproduces this. The code has been forked from https://codesandbox.io/s/x8ric by James Wesc and tweaked a bit to clarify the issue.
My problem appears when you change the count of the instances by dragging the slider. The position of all instances is resets to (0,0,0).
Is there a way to stop the reset and only update the new instances when the count changes?
This is a link to the code sandbox.
https://codesandbox.io/s/instanced-mesh-lerping-positions-forked-d03ckr?file=/src/App.tsx
I added a snippet to the code as well!
Thanks in advance!!
const tempObject = new Object3D();
const tempMatrix = new Matrix4();
const tempVector = new Vector3();
const tempVector2 = new Vector3();
type XYZ = [number, number, number];
const data = dataJSON as Array<{ p1: XYZ; p2: XYZ }>;
const pos = new Vector3(10, 1, 1);
const YourCanvas = withControls(Canvas);
const Boxes: React.FC = () => {
const count = useControl("count", {
type: "number",
value: 1000,
min: 100,
max: 1000,
distance: 0.1
});
const ref = useRef<InstancedMesh>(null!);
React.useEffect(() => {
if (ref.current) {
ref.current.instanceMatrix.setUsage(THREE.DynamicDrawUsage);
}
}, []);
useFrame(({ clock: { elapsedTime } }) => {
const t = Math.floor(elapsedTime / 5) % 2;
for (let i = 0; i < count; i++) {
ref.current.getMatrixAt(i, tempMatrix);
tempVector.setFromMatrixPosition(tempMatrix);
const toPosition = t ? data[i].p1 : data[i].p2;
// Resets positions of all instances when count changes
// tempVector2.set(toPosition[0], toPosition[1], toPosition[2])
// tempObject.position.lerpVectors(tempVector, tempVector2, 0.01)
// Only updating positions of new instances when count changes
tempObject.position.set(toPosition[0], toPosition[1], toPosition[2]);
tempObject.updateMatrix();
ref.current.setMatrixAt(i, tempObject.matrix);
}
ref.current.instanceMatrix.needsUpdate = true;
});
return (
<instancedMesh
ref={ref}
args={[
new THREE.BoxGeometry(1.0, 1.0, 1.0, 1.0),
new THREE.MeshStandardMaterial({ color: new THREE.Color("#00ff00") }),
count
]}
></instancedMesh>
);
};

Passing parameters into ES6 closure (for multiple P5.js sketches)

I am trying to make a 'generic' P5.js sketch that I can tweak based on a passed-in parameter, with the intent being to be able to generate multiple sketches on a single page to show how different inputs work side-by-side.
Following the guide I see syntax like this (and I've extended it to fill in multiple divs:
const s = ( sketch ) => {
let x = 100;
let y = 100;
sketch.setup = () => {
sketch.createCanvas(500, 500);
console.log(idx);
};
sketch.draw = () => {
sketch.background(100);
sketch.fill(255);
sketch.rect(x,y,50,50);
sketch.text
};
};
let myp5_1 = new p5(s, document.getElementById('p5-sketch1'));
let myp5_2 = new p5(s, document.getElementById('p5-sketch2'));
let myp5_3 = new p5(s, document.getElementById('p5-sketch3'));
I am not great with ES6, but I'm struggling with passing a set of parameters in to be able to tweak the P5.js code.
What I would like to do is to pass in, say, an ID variable into each instance of s and have the sketch execute differently, rather than making three separate const s calls and duplicating data.
Create a function that takes idx and returns the original function.
const s = (idx) => ( sketch ) => {
let x = 100;
let y = 100;
sketch.setup = () => {
sketch.createCanvas(500, 500);
console.log(idx);
};
sketch.draw = () => {
sketch.background(100);
sketch.fill(255);
sketch.rect(x,y,50,50);
sketch.text
};
};
let myp5_1 = new p5(s(0), document.getElementById('p5-sketch1'));
let myp5_2 = new p5(s(1), document.getElementById('p5-sketch2'));
let myp5_3 = new p5(s(2), document.getElementById('p5-sketch3'))

Functional Programming: Calling a Curried Function

I'm implementing the game Tic Tac Toe/Naughts and Crosses in a functional programming style and have stumbled across a hurdle with curried functions.
I have a reoccurring pattern of functions in the form func(width, height, index) which I then wish to curry, binding width and height and leaving curriedFunc(index).
However the problem arises when I have functions that expect one of these curried functions to be defined at compile-time.
They cannot be defined at compile time, because they need input from the user to then bind the values to the function.
Below is some example code of the pattern I've encountered.
// Board indexes:
// 0 | 1 | 2
// ---+---+---
// 3 | 4 | 5
// ---+---+---
// 6 | 7 | 8
const getRowNumGivenWidth = w => i => Math.floor(i/w);
// I want to be able to declare nextIndexInRowGivenWidth() here, outside of main()
// but getRowNum() needs to be defined beforehand
const main = () => {
// User input:
const width = 3;
// ...
const getRowNum = getRowNumGivenWidth(width);
const nextIndexInRowGivenWidth = width => currentIndex => {
const rowNum = getRowNum(currentIndex);
const nextIndex = currentIndex + 1;
if (getRowNum(nextIndex) != rowNum)
result = nextIndex - width;
else
result = nextIndex;
return result;
};
const nextIndexInRow = nextIndexInRowGivenWidth(width);
const board = [0, 1, 2, 3, 4, 5, 6, 7, 8];
board.map(x => console.log(x, " -> ", nextIndexInRow(x)));
// ...
}
main();
The only way I can think of solving this is to pass the curried function as an argument (to nextIndexInRowGivenWidth() in this example).
However I don't think this is ideal as if a function requires a few similarly curried functions at run-time, it quickly becomes unwieldy to define and curry said function.
The ideal solution would be if I could somehow make the binding of the values dynamic, suppose I could put the declaration getRowNum = getRowNumGivenWidth(width); before main(). This way I could call something like getRowNum(someInt) to initialise getRowNum() which I could then use in other functions that are already expecting it to be defined.
As this is a reoccurring pattern in my code, I was wondering if there is a design pattern to achieve this.
I think you are looking for
const getRowNumGivenWidth = w => i => Math.floor(i/w);
const nextIndexInRowGivenWidth = width => {
const getRowNum = getRowNumGivenWidth(width);
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
return currentIndex => {
const nextIndex = currentIndex + 1;
if (getRowNum(nextIndex) != getRowNum(currentIndex))
return nextIndex - width;
else
return nextIndex;
};
};
const main = () => {
// User input:
const width = 3;
const nextIndexInRow = nextIndexInRowGivenWidth(width);
// ...
}
Alternatively, you could define that nextIndexInRowGiven… function not with the width as the first curried parameter, but with getRowNum itself as the parameter:
const getRowNumGivenWidth = w => i => Math.floor(i/w);
const nextIndexInRowGivenRowNumGetter = getRowNum => currentIndex => {
const nextIndex = currentIndex + 1;
if (getRowNum(nextIndex) != getRowNum(currentIndex))
return nextIndex - width;
else
return nextIndex;
};
const main = () => {
// User input:
const width = 3;
const nextIndexInRow = nextIndexInRowGivenRowNumGetter(getRowNumGivenWidth(width));
// ...
}

Constantly increasing memory usage when passing around huge arrays to webworker

I am currently doing some 3d modeling using babylonjs. I need to create a pressure map from given pressure at specific points. I am doing that using IDW. However this means that even with my map being a size of 70x90 grid requires me to have an array of 25200 (4 rgba values for each pixel) entries. Then this buffer is passed to a RawTexture for assigning it to a material, that is overlaid on the object
I am using a web worker, because I have to update the pressure values every 100ms and I don't want to block the main thread.The issue occurs when I am return that array (created in calculate function) from a service worker.
For some reason the memory usage just keeps going up, without stopping. It eventually goes up to around 1.5 gigabytes and I have to kill it.
The question : Is there any way to prevent this and what could be causing such high memory usage?
Worker:
// #flow
import { find, propEq, both } from 'ramda';
import { colorFromValue } from './color';
import { inverseDistance, distanceValues } from './math';
const findPoint = (x: number, y: number) =>
find(both(propEq('x', x), propEq('y', y)));
const distanceDict = {};
/* eslint-disable */
function calculate(options: Object, pList: Array<*>) {
const points = pList || [];
const { height, width } = options;
const gridWidth = width * 4;
const grid = new Uint8Array(options.width * options.height * 4);
for (let y = 0; y < height; y += 1) {
const rW = y * gridWidth;
for (let i = 0; i < gridWidth; i += 4) {
const index = i + rW;
const x = i / 4;
const dictKey = `${x}--${y}`;
let bottoms = distanceDict[dictKey];
if (bottoms === undefined) {
bottoms = distanceValues(points, x, y);
distanceDict[dictKey] = bottoms;
}
const point = findPoint(x, y)(points);
const value = point !== undefined && point !== null ?
point.value : inverseDistance(points, bottoms);
const color = colorFromValue(value);
grid[index] = color[0];
grid[index + 1] = color[1];
grid[index + 2] = color[2];
grid[index + 3] = 255;
}
}
return grid;
}
self.onmessage = (e) => {
const { points, options } = e.data;
const grid = calculate(options, points);
self.postMessage(grid.buffer, [grid.buffer]);
};
Painting:
modifyNodes = (points: Array<*>) => new Promise((res, rej) => {
this.worker.onmessage = (e) => {
this._texture.update(new Uint8Array(e.data));
res();
}
const data = {
options: this._options,
points,
};
this.worker.postMessage(data);
})
So it seems the issue was in the colorFromValue function that was memoized. Because the values had quite few decimal points it could create up to 9! new entries into cache, so it drove up the memory usage...

Get the intersection of n arrays

Using ES6's Set, given two arrays we can get the intersection like so:
let a = new Set([1,2,3])
let b = new Set([1,2,4])
let intersect = new Set([...a].filter(i => b.has(i)));
How can we get the intersection of n arrays?
Update:
I'm trying to wrap my head around this for the following use case. I have a two dimensional array with at least one element.
parts.forEach(part => {
intersection = new Set()
})
How would you get the intersection of each element (array) in parts?
Assuming you have some function function intersect(set1, set2) {...} that can intersect two sets, you can get the intersection of an array of sets using reduce:
function intersect(a, b) {
return new Set(a.filter(i => b.has(i)));
}
var sets = [new Set([1,2,3]), ...];
var intersection = sets.reduce(intersect);
You can create an intersect helper function using a combination of Array methods like .filter(), .map(), and .every().
This answer is inspired by the comment above from Xufox, who mentioned using Array#every in a filter predicate.
function intersect (first = [], ...rest) {
rest = rest.map(array => new Set(array))
return first.filter(e => rest.every(set => set.has(e)))
}
let parts = [
[1, 2, 3],
[1, 2, 4],
[1, 5, 2]
]
console.log(
intersect(...parts)
)
ES6 still has a while
This is the type of function that can easily cause long lags due to excessive amounts of processing. This is more true with the unquestioning and even preferential use of ES6 and array methods like reduce, filter etc, over simple old fashioned loops like while and for.
When calculating the intersection of many sets the amount of work done per iteration should go down if an item has been found not to be part of the intersection. Because forEach can not break you are forced to still iterate all elements. Adding some code to avoid doing the search if the current item has been found to not belong can improve the performance, but it is a real kludge.
The is also the tendency to just create whole new datasets just to remove a single item from an array, set, or map. This is a very bad habit that i see more and more of as people adopt the ES5 way.
Get the intersection of n sets.
So to the problem at hand. Find the intersection of many sets.
Solution B
A typical ES6 solution
function intersectB(firstSet, ...sets) {
// function to intercept two sets
var intersect = (a,b) => {
return new Set([...a].filter(item => b.has(item)))
};
// iterate all sets comparing the first set to each.
sets.forEach(sItem => firstSet = intersect(firstSet, sItem));
// return the result.
return firstSet;
}
var sets = [new Set([1,2,3,4]), new Set([1,2,4,6,8]), new Set([1,3,4,6,8])];
var inter = intersectB(...sets);
console.log([...inter]);
Works well and for the simple test case execution time is under a millisecond. But in my book it is a memory hogging knot of inefficiency, creating arrays, and sets at every line almost and iterating whole sets when the outcome is already known.
Let's give it some more work. 100 sets, with up to 10000 items over 10 tests each with differing amount of matching items. Most of the intercepts will return empty sets.
Warning will cause page to hang up to one whole second... :(
// Create a set of numbers from 0 and < count
// With a odds for any number occurring to be odds
// return as a new set;
function createLargeSet(count,odds){
var numbers = new Set();
while(count-- > 0){
if(Math.random() < odds){
numbers.add(count);
}
}
return numbers;
}
// create a array of large sets
function bigArrayOfSets(setCount,setMaxSize,odds){
var bigSets = [];
for(var i = 0; i < setCount; i ++){
bigSets.push(createLargeSet(setMaxSize,odds));
}
return bigSets;
}
function intersectB(firstSet, ...sets) {
var intersect = (a,b) => {
return new Set([...a].filter(item => b.has(item)))
};
sets.forEach(sItem => firstSet = intersect(firstSet, sItem));
return firstSet;
}
var testSets = [];
for(var i = 0.1; i <= 1; i += 0.1){
testSets.push(bigArrayOfSets(100,10000,i));
}
var now = performance.now();
testSets.forEach(testDat => intersectB(...testDat));
var time = performance.now() - now;
console.log("Execution time : " + time);
Solution A
A better way, not as fancy but much more efficient.
function intersectA(firstSet,...sets) {
var count = sets.length;
var result = new Set(firstSet); // Only create one copy of the set
firstSet.forEach(item => {
var i = count;
var allHave = true;
while(i--){
allHave = sets[i].has(item)
if(!allHave) { break } // loop only until item fails test
}
if(!allHave){
result.delete(item); // remove item from set rather than
// create a whole new set
}
})
return result;
}
Compare
So now let's compare both, if you are feeling lucky try and guess the performance difference, it's a good way to gage your understanding of Javascript execution.
// Create a set of numbers from 0 and < count
// With a odds for any number occurring to be odds
// return as a new set;
function createLargeSet(count,odds){
var numbers = new Set();
while(count-- > 0){
if(Math.random() < odds){
numbers.add(count);
}
}
return numbers;
}
// create a array of large sets
function bigArrayOfSets(setCount,setMaxSize,odds){
var bigSets = [];
for(var i = 0; i < setCount; i ++){
bigSets.push(createLargeSet(setMaxSize,odds));
}
return bigSets;
}
function intersectA(firstSet,...sets) {
var count = sets.length;
var result = new Set(firstSet); // Only create one copy of the set
firstSet.forEach(item => {
var i = count;
var allHave = true;
while(i--){
allHave = sets[i].has(item)
if(!allHave) { break } // loop only until item fails test
}
if(!allHave){
result.delete(item); // remove item from set rather than
// create a whole new set
}
})
return result;
}
function intersectB(firstSet, ...sets) {
var intersect = (a,b) => {
return new Set([...a].filter(item => b.has(item)))
};
sets.forEach(sItem => firstSet = intersect(firstSet, sItem));
return firstSet;
}
var testSets = [];
for(var i = 0.1; i <= 1; i += 0.1){
testSets.push(bigArrayOfSets(100,10000,i));
}
var now = performance.now();
testSets.forEach(testDat => intersectB(...testDat));
var time = performance.now() - now;
console.log("Execution time 'intersectB' : " + time);
var now = performance.now();
testSets.forEach(testDat => intersectA(...testDat));
var time = performance.now() - now;
console.log("Execution time 'intersectA' : " + time);
As you can see using a simple while loop may not be a cool as using filter but the performance benefit is huge, and something to keep in mind next time you are writing that perfect 3 line ES6 array manipulation function. Dont forget about for and while.
The most efficient algorithm for intersecting n arrays is the one implemented in fast_array_intersect. It runs in O(n), where n is the total number of elements in all the arrays.
The base principle is simple: iterate over all the arrays, storing the number of times you see each element in a map. Then filter the smallest array, to return only the elements that have been seen in all the arrays. (source code).
You can use the library with a simple :
import intersect from 'fast_array_intersect'
intersect([[1,2,3], [1,2,6]]) // --> [1,2]
OK i guess the most efficient way of performing the Array intersection is by utilizing a Map or Hash object. Here I test 1000 arrays each with ~1000 random integer items among 1..175 for an intersection. The result is obtained in less than 100msec.
function setIntersection(a){
var m = new Map(),
r = new Set(),
l = a.length;
a.forEach(sa => new Set(sa).forEach(n => m.has(n) ? m.set(n,m.get(n)+1)
: m.set(n,1)));
m.forEach((v,k) => v === l && r.add(k));
return r;
}
var testSets = Array(1000).fill().map(_ => Array(1000).fill().map(_ => ~~(Math.random()*175+1)));
console.time("int");
result = setIntersection(testSets);
console.timeEnd("int");
console.log(JSON.stringify([...result]));

Categories

Resources