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

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

Related

Running an animated loop in PIXI.JS

I am trying to create a bottle pouring animation that loops through 5 different bottles, each bottle is sideways and has a different amount of liquid pouring out. I am trying to display the first bottle and then the second after 60ms, then the 3rd after 60ms, and so on and so on. I need to remove the previous bottle and add the next bottle in the exact same place. I am wondering what the most concise way to do with would be, I have tried with several setTimout functions but the code has some bugs and is not concise at all. I have researched PIXI.Timer but am struggling to understand how to set 5 different sprites and loop through them. Let me know if you have any ideas or direction. I will post my function with setTimout used below:
setTimeout(() => {
let pour1Texture = new PIXI.Texture.from(require('#/assets/items/bottle/pouring/pouring bottle1.png'))
let pour1 = new PIXI.Sprite.from(pour1Texture)
sprites.push(pour1)
pour1.position.x = 438;
pour1.position.y = -40;
labBenchComponent.pixiApp.stage.addChild(
pour1
);
},1000)
setTimeout(() => {
labBenchComponent.pixiApp.stage.removeChild(sprites.pop())
const pour2Texture = new PIXI.Texture.from(require('#/assets/items/bottle/pouring/pouring bottle2.png'))
const pour2 = new PIXI.Sprite.from(pour2Texture)
pour2.position.x = 438;
pour2.position.y = -10;
sprites.push(pour2)
labBenchComponent.pixiApp.stage.addChild(
pour2
);
}, 1000)
setTimeout(() => {
labBenchComponent.pixiApp.stage.removeChild(sprites.pop())
const pour3Texture = new PIXI.Texture.from(require('#/assets/items/bottle/pouring/pouring bottle2.png'))
const pour3 = new PIXI.Sprite.from(pour3Texture)
pour3.position.x = 438;
pour3.position.y = 10;
sprites.push(pour3)
labBenchComponent.pixiApp.stage.addChild(
pour3
);
}, 1000)
I figured it out. Instead of using a tick use PIXI.AnimatedSprite like the following:
import * as PIXI from 'pixi.js-legacy';
export default function pourBottle() {
let textureArray = [];
for (let i = 0; i < 5; i++)
{
let texture = {
texture: PIXI.Texture.from(require(`#/assets/items/bottle/pouring/pouring bottle${i+1}.png`)),
time: 100,
};
textureArray.push(texture);
};
let animatedSprite = new PIXI.AnimatedSprite(textureArray);
return animatedSprite;
}

Dynamically create any number of sequentially firing functions

(JavaScript) I have a function that deals player cards in a nice sequential fashion: Player, Dealer, Player, Dealer. Below is that part of the function which sequentially moves cards to the viewport.
setTimeout(()=>{
player1.style.top = `70%`;
player1.style.left = `30%`;
player1.style.transform = `rotate(${Math.floor(Math.random() * rotationMax)}deg)`;
setTimeout(() => {
dealer1.style.top = `8%`;
dealer1.style.left = `30%`
dealer1.style.transform = `rotate(${Math.floor(Math.random() * rotationMax)+1}deg)`;
setTimeout(() => {
player2.style.top = `70%`;
player2.style.left = `50%`
player2.style.transform = `rotate(${Math.floor(Math.random() * rotationMax)}deg)`;
setTimeout(() => {
flippedCard.style.top = '8%';
flippedCard.style.left = '44%';
}, 200)}, 200)}, 100)}, 200)
You can see that this block works only with a set number of cards (in this case 4). I am not yet good enough in Javascript to create function that would dynamically generate any number of cards to be dealt.
Can someone point me in the right direction? Specific question: how do you dynamically generate tasks that run one after another.
Make an array of dealer cards and player cards, and figure out the differences in the left you want for each. Then iterate over the arrays, delaying with await to make the code flat and readable:
const delay200 = () => new Promise(res => setTimeout(res, 200);
const playerCards = [player1, player2];
const dealerCards = [dealer1, dealer2];
const playerLeftIncrement = 20; // eg: 30%, then 50%, then 70%; adjust as needed
const dealerLeftIncrement = 14; // eg: 30%, then 44%, then 58%; adjust as needed
const applyStyle = (card, left) => {
Object.assign(
card.style,
{
top: '70%',
left,
transform: `rotate(${Math.floor(Math.random() * rotationMax)}deg)`,
}
);
};
for (let i = 0; i < playerCards.length; i++) {
applyStyle(playerCards[i], `${30 + i * playerLeftIncrement}%`);
await delay200();
applyStyle(dealerCards[i], `${30 + i * dealerLeftIncrement}%`);
await delay200();
}
It would be useful to have a function that looks something like:
callFunctionsWithDelays(functions, delays)
That would avoid the nested look to your code, and make it easy to dynamically generate. I'd write this using async/await syntax:
async function callFunctionsWithDelays(functions, delays) {
for (i = 0; i < functions.length; i++) {
functions[i].call()
await new Promise(resolve, setTimeout(resolve, delays[i]))
}
}

Computing the gradient of the loss using Tensorflow.js

I am trying to compute the gradient of a loss, with relation to a network's trainable weights using Tensorflow.js in order to apply these gradients to my network's weight. In python, this is easily done using the tf.gradients() functions, which takes two minimum inputs representing dx and dy.
However, I am not able to reproduce the behavior in Tensorflow.js. I am not sure wether my understanding of the gradient of the loss w.r.t the weights is wrong, or if my code contains mistakes.
I have spent some time analysing the core code of the tfjs-node package to understand how it is done when we call the function tf.model.fit(), but with little success so far.
let model = build_model(); //Two stacked dense layers followed by two parallel dense layers for the output
let loss = compute_loss(...); //This function returns a tf.Tensor of shape [1] containing the mean loss for the batch.
const f = () => loss;
const grad = tf.variableGrads(f);
grad(model.getWeights());
The model.getWeights() function returns an array of tf.variable(), so I assumed the function would compute dL/dW for each layer, which I could apply later to my network's weights, however, that's not quite the case as I get this error :
Error: Cannot compute gradient of y=f(x) with respect to x. Make sure that the f you passed encloses all operations that lead from x to y.
I don't quite understand what does this error means.
How am I supposed to compute the gradient (analog to tf.gradients() in Python) of the loss using Tensorflow.js then ?
Edit :
This is the function computing the loss :
function compute_loss(done, new_state, memory, agent, gamma=0.99) {
let reward_sum = 0.;
if(done) {
reward_sum = 0.;
} else {
reward_sum = agent.call(tf.oneHot(new_state, 12).reshape([1, 9, 12]))
.values.flatten().get(0);
}
let discounted_rewards = [];
let memory_reward_rev = memory.rewards;
for(let reward of memory_reward_rev.reverse()) {
reward_sum = reward + gamma * reward_sum;
discounted_rewards.push(reward_sum);
}
discounted_rewards.reverse();
let onehot_states = [];
for(let state of memory.states) {
onehot_states.push(tf.oneHot(state, 12));
}
let init_onehot = onehot_states[0];
for(let i=1; i<onehot_states.length;i++) {
init_onehot = init_onehot.concat(onehot_states[i]);
}
let log_val = agent.call(
init_onehot.reshape([memory.states.length, 9, 12])
);
let disc_reward_tensor = tf.tensor(discounted_rewards);
let advantage = disc_reward_tensor.reshapeAs(log_val.values).sub(log_val.values);
let value_loss = advantage.square();
log_val.values.print();
let policy = tf.softmax(log_val.logits);
let logits_cpy = log_val.logits.clone();
let entropy = policy.mul(logits_cpy.mul(tf.scalar(-1)));
entropy = entropy.sum();
let memory_actions = [];
for(let i=0; i< memory.actions.length; i++) {
memory_actions.push(new Array(2000).fill(0));
memory_actions[i][memory.actions[i]] = 1;
}
memory_actions = tf.tensor(memory_actions);
let policy_loss = tf.losses.softmaxCrossEntropy(memory_actions.reshape([memory.actions.length, 2000]), log_val.logits);
let value_loss_copy = value_loss.clone();
let entropy_mul = (entropy.mul(tf.scalar(0.01))).mul(tf.scalar(-1));
let total_loss_1 = value_loss_copy.mul(tf.scalar(0.5, dtype='float32'));
let total_loss_2 = total_loss_1.add(policy_loss);
let total_loss = total_loss_2.add(entropy_mul);
total_loss.print();
return total_loss.mean();
}
EDIT 2:
I managed to use the compute_loss as the loss function specified on model.compile(). But then, it is required that it takes only two inputs (predictions, labels), so it's not working out for me, as I want to input multiple parameters.
I am trully lost on the matter.
The error says it all.
Your issue has to do with tf.variableGrads. loss should be a scalar computed using all available tf tensors operators. loss should not return a tensor as indicated in your question.
Here is an example of what loss should be:
const a = tf.variable(tf.tensor1d([3, 4]));
const b = tf.variable(tf.tensor1d([5, 6]));
const x = tf.tensor1d([1, 2]);
const f = () => a.mul(x.square()).add(b.mul(x)).sum(); // f is a function
// df/da = x ^ 2, df/db = x
const {value, grads} = tf.variableGrads(f); // gradient of f as respect of each variable
Object.keys(grads).forEach(varName => grads[varName].print());
/!\ Notice that the gradient is calculated as respect of variables created using tf.variable
Update:
You're not computing the gradients as it should be. Here is the fix.
function compute_loss(done, new_state, memory, agent, gamma=0.99) {
const f = () => { let reward_sum = 0.;
if(done) {
reward_sum = 0.;
} else {
reward_sum = agent.call(tf.oneHot(new_state, 12).reshape([1, 9, 12]))
.values.flatten().get(0);
}
let discounted_rewards = [];
let memory_reward_rev = memory.rewards;
for(let reward of memory_reward_rev.reverse()) {
reward_sum = reward + gamma * reward_sum;
discounted_rewards.push(reward_sum);
}
discounted_rewards.reverse();
let onehot_states = [];
for(let state of memory.states) {
onehot_states.push(tf.oneHot(state, 12));
}
let init_onehot = onehot_states[0];
for(let i=1; i<onehot_states.length;i++) {
init_onehot = init_onehot.concat(onehot_states[i]);
}
let log_val = agent.call(
init_onehot.reshape([memory.states.length, 9, 12])
);
let disc_reward_tensor = tf.tensor(discounted_rewards);
let advantage = disc_reward_tensor.reshapeAs(log_val.values).sub(log_val.values);
let value_loss = advantage.square();
log_val.values.print();
let policy = tf.softmax(log_val.logits);
let logits_cpy = log_val.logits.clone();
let entropy = policy.mul(logits_cpy.mul(tf.scalar(-1)));
entropy = entropy.sum();
let memory_actions = [];
for(let i=0; i< memory.actions.length; i++) {
memory_actions.push(new Array(2000).fill(0));
memory_actions[i][memory.actions[i]] = 1;
}
memory_actions = tf.tensor(memory_actions);
let policy_loss = tf.losses.softmaxCrossEntropy(memory_actions.reshape([memory.actions.length, 2000]), log_val.logits);
let value_loss_copy = value_loss.clone();
let entropy_mul = (entropy.mul(tf.scalar(0.01))).mul(tf.scalar(-1));
let total_loss_1 = value_loss_copy.mul(tf.scalar(0.5, dtype='float32'));
let total_loss_2 = total_loss_1.add(policy_loss);
let total_loss = total_loss_2.add(entropy_mul);
total_loss.print();
return total_loss.mean().asScalar();
}
return tf.variableGrads(f);
}
Notice that you can quickly run into a memory consumption issue. It will advisable to surround the function differentiated with tf.tidy to dispose of the tensors.

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));
// ...
}

Slice path into two separate paths using paper.js

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

Categories

Resources