How to do a task outside of draw loop in p5js - javascript

I'm trying to convert an image into a picture that only has a certain amount of colors in p5.js. I am doing this by picking num random colors. Then, I'm going through every pixel in the image with loadPixels() and giving it a score. This score is determined by how close the color is to one of the random colors. I do this by adding the difference individually from the rgb values and then choosing the lowest score. For example, if I had num = [color(100, 100, 100), color(200, 200, 200)] and I was looking at a pixel that has color(100, 150, 110) then the score would be not 240 (the difference between the image pixel and the 200 color) but instead 60. This all works fine. Next, what I'm trying to do is make sure that every pixel has a score under scoreBoundary. Originally, this variable is set to 0. If the score of a pixel is more than scoreBoundary, I alter the closest random color to bring the pixel's score under scoreBoundary. This all should work fine. After a few loops (I haven't decided how many yet, maybe it has to do with the average pixel score or something), if there is still a pixel over scoreBoundary, I increase scoreBoundary. Eventually, I should have the best picture I can have with num colors. (If you want to know, I'm trying to make a sort of Cross-Stitch Clone.)
Now for my problem. I want to make a temporary progress bar, because this method is very slow. Originally, I tried putting text that said pixelNum + " out of " + pixels.length. However, since this process was in a for loop, this didn't show up. So, I tried putting this process in the draw loop. Now the text shows up, but it's unbearably slow. After about a minute, I was maybe 1% done with the first loop (and remember, there will be thousands) because it was only performing one calculation per frame. Now I ask you two things, and either solution helps.
One, is there a way to have a process that works as fast as it can away from the draw loop? For example, can I have this process run multiple times per frame (without just adding i < previousBoundary * howMuchFasterIWantItToBe) while still being linked to draw for the progress bar?
Two, is there another way to go about this? Another library or method that reduces the number of colors in an image to num?

You don't really show any code that is reproducible to explain what issue you are having.
In any case, what you are looking for (for your first question) is an async function.
SEIZURE WARNING FOR PHOTOSENSITIVITY
Seriously, this example sketch will wreck your eyes but you can see two ellipses working on different schedules because of the async function
Importantly, I had to append the start of the sketch with /*jshint esversion: 9 */ for the editor to allow async to be used:
/*jshint esversion: 9 */
let thing = 10;
let thing2 = 10;
function setup() {
createCanvas(400, 400);
}
function draw() {
background(220);
drawOther();
for(let i = 0; i < 1000; i++){
if(thing <= 400){
thing += 2;
}else{
thing = 10;
}
fill(255)
stroke(0);
ellipse(width, height, thing, thing);
}
}
const drawOther = async () => {
for(let i = 0; i < 100; i++){
if(thing2 <= 400){
thing2 += 2;
}else{
thing2 = 10;
}
fill(0);
stroke(255)
ellipse(0, 0, thing2, thing2);
}
}
You can read more about async functions here but basically, it does what you want - to launch a separate thread of activity that won't block your main loop.
If you want to return anything from an async function you would need to read about await and promises as well.

Related

Is there a way to nest a loop in JavaScript until a condition is met? Like a while loop but nested?

I'm trying to code an algorithm that tests to see if a game is possible to be won, given a randomly shuffled deck. I came up with a super simplified solitaire-esque game with like 15 cards, because I'm just messing around and I don't want to try a game with too many options until I've figured out a way to do it. My goal is to test every possible combination of moves, so I wrote a function that returns an array of possible moves when given the state of the board. What I need help with is calling that function every time I change the board. Right now my code calling the function looks like this:
let moves = possibleMoves(piles);
if (moves.length >= 1){
for (let move of moves) {
let nB = piles.slice(0);
nB[move.from].pop();
nB[move.to].push({card: move.card, x: move.to, y: nB[move.to].length});
let m0s = possibleMoves(nB);
if (m0s.length >= 1) {
for (let m0 of m0s){
let nB0 = nB.slice(0);
nB0[m0.from].pop();
nB0[m0.to].push({card: m0.card, x: m0.to, y: nB0[m0.to].length})
}
}
}
}
But obviously I can't just nest that loop infinitely until there are no possible moves (note, I still haven't written code for when there are no possible moves but I'll get to it). The reason I can't just do a while loop is because I want to check every possible move after every possible move, without actually changing the board. Is there a better way to do this?
A recursive function maybe what you are looking for
function recursive(piles){
let moves = possibleMoves(piles);
if (moves.length >= 1){
for (let move of moves) {
let nB = piles.slice(0);
nB[move.from].pop();
nB[move.to].push({card: move.card, x: move.to, y: nB[move.to].length});
recursive(nB)
}
}else{
return;
}}

Way to see if whole canvas has been painted in one color. Javascript + processing.js

I'm still a beginner at javascript, and I'm making a game about dying the whole screen white while the paint brush becomes smaller and smaller until in completely disappears.
I wanted to know, is there a simple way to figure out if the whole canvas has been painted, so I can put a winning screen?
I'm using the processing.js library, here is my code, if it's of any use:
background(255,0,0);
var eight = 100;
var draw = function(){
strokeWeight(eight);
point(mouseX,mouseY);
eight -= 0.2;
if(eight<0){
noStroke();
}
Here's a modestly efficient way of determining if the user has whited every pixel
Create an array where each canvas pixel is represented by an array element.
var pixels=new Array(canvas.width*canvas.height);
Initially fill the array with all zeros.
Create a variable that hold the # of unique pixels whited out so far.
var whited=0;
When the user passes over a pixel, see if the pixel has already been whited. If it hasn't been whited, change its array value to 1 and increment the whited variable.
var n = mouseY * canvas.width + mouseX
if(pixels[n]=0){
pixels[n]=1;
whited++;
}
You have a winner if the value of whited equals the number of pixels on the canvas.
if(whited==pixels.length){
alert('You have won!');
}
A thought: Instead of making the user find every (tiny) missed pixel, you might consider making a grid so the user has an easier time finding that 1 (larger) missed grid cell instead of finding one missed pixel in a sea of white.
You can go over all the pixels and check if they are not white
for (var i=0;i<imgData.data.length;i+=4)
{
if(imgData.data[i]==0&&imgData.data[i+1]==0&&imgData.data[i+2]==0&&imgData.data[i]+3==0){alert("white pixel")}
}
http://www.w3schools.com/tags/canvas_getimagedata.asp
Since you're using Processing, just walk over the pixels:
void setup() {
...
}
void draw() {
...
}
void yourCheckFunction() {
loadPixels();
boolean allWhite = true;
for(int c: pixels) {
if(brightness(c) < 255) {
// we found a not-white pixel!
allWhite = false;
break;
}
}
if (allWhite) {
// the paint surface is entirely white.
} else {
// there are non-white patches left
}
}
There are lots of ways to optimize this (like chopping up the surface into distinct areas with their own administrative true/false value so you can first check if they were all-white on a previous run, and if so, you don't need to recheck them) but this covers the basics:
assume the canvas is all white pixels
try to invalidate that assumption by finding a not-white pixel
immediately stop checking if you do
if there are none, your loop will end "naturally"
Alternatively, you can track how many pixels your user's action have painted. Once that number of pixels is equal to width*height, all pixels must necessarily be white (see markE's answer for that)

Javascript: Simple Particle Motion, Particle Elastically Bouncing Off Other Particle

I've created this rather simple javascript; balls or 'molecules' moving around the screen. I was hoping to add to the functionality that when one ball comes into contact with another, they swap velocities. We don't need to worry about any angles, just when they come into contact with each other, the velocities swap. (Instead of changing the velocities though, in the code linked I've just coded a colour change)
I've been trying to call the function 'someplace' to recognise when the molecules touch, but I've had no luck with that. I don't really understand why.
Link to code:
http://jsbin.com/arokuz/5/
There seems to be three main problems:
The molecules seem to be randomly changing, rather than when two molecules touch.
When one sets the array to have say, 3 molecules, only two appear, the first is actually there, but unresponsive to .fillstyle changes, so invisible against the canvas
With the function method I would only be able to recognise when molecules in series (1 and 2 or 4 and 5) in the array touch...how could I check all the molecules?
You are only comparing a molecule with 2 other ones, which in fact might be anywhere.
Collision detection is a topic quite hard to solve, but if you want to have your idea
working quickly you might go for a n^2 algorithm with 2 nested for loops.
the code is quite expected :
// collision
for(var t = 0; t < molecules.length-1; t++)
for(var tt = t+1; tt < molecules.length; tt++) {
var p1 = molecules[t];
var p2 = molecules[tt];
if (sq(p1.x-p2.x) +sq(p1.y-p2.y) < sq(p1.radius+p2.radius) )
{
p1.collided = 8; // will diplay for next 8 frames
p2.collided = 8; // .
}
}
the fiddle is here :
http://jsbin.com/arokuz/10
The reason only two appear when three are made isn't because the first one doesn't render it is rather the last one doesn't, this is because of how you draw them by comparing its distance with the next one in the list - as it is the last there is no next and thus throws a null error and continues (check the console).
The reason why they seem to "randomly" detect collisions or not is because they are not checking against all other molecules - only the next in the list, unfortunately the only simply way to do it would be to go through all other balls for every ball and checking.
To get the molecules to detect distance you could use the pythagorean theorem, I typically use it such as:
var distx = Math.abs(molecule1.x - molecule2.x);
var disty = Math.abs(molecule1.x - molecule2.y);
var mindist = molecule1.radius + molecule2.radius;
return Math.sqrt(distx*distx+disty*disty) < mindist;

Maths and Easing For Javascript/Canvas Preload Animation

I'm building an image preload animation, that is a circle/pie that gets drawn. Each 'slice' is totalImages / imagesLoaded. So if there are four images and 2 have loaded, it should draw to 180 over time.
I'm using requestAnimFrame, which is working great, and I've got a deltaTime setup to restrict animation to time, however I'm having trouble getting my head around the maths. The closest I can get is that it animates and eases to near where it's supposed to be, but then the value increments become smaller and smaller. Essentially it will never reach the completed value. (90 degrees, if one image has loaded, as an example).
var totalImages = 4;
var imagesLoaded = 1;
var currentArc = 0;
function drawPie(){
var newArc = 360 / totalImages * this.imagesLoaded // Get the value in degrees that we should animate to
var percentage = (isNaN(currentArc / newArc) || !isFinite(currentArc / newArc)) || (currentArc / newArc) > 1 ? 1 : (currentArc / newArc); // Dividing these two numbers sometimes returned NaN or Infinity/-Infinity, so this is a fallback
var easeValue = easeInOutExpo(percentage, 0, newArc, 1);
//This animates continuously (Obviously), because it's just constantly adding to itself:
currentArc += easedValue * this.time.delta;
OR
//This never reaches the full amount, as increments get infinitely smaller
currentArc += (newArc - easedValue) * this.time.delta;
}
function easeInOutExpo(t, b, c, d){
return c*((t=t/d-1)*t*t + 1) + b;
}
I feel like I've got all the right elements and values. I'm just putting them together incorrectly.
Any and all help appreciated.
You've got the idea of easing. The reality is that at some point you cap the value.
If you're up for a little learning, you can brush up on Zeno's paradoxes (the appropriate one here being Achilles and the Tortoise) -- it's really short... The Dichotomy Paradox is the other side of the same coin.
Basically, you're only ever half-way there, regardless of where "here" or "there" may be, and thus you can never take a "final"-step.
And when dealing with fractions, such as with easing, that's very true. You can always get smaller.
So the solution is just to clamp it. Set a minimum amount that you can move in an update (2px or 1px, or 0.5px... or play around).
The alternative (which ends up being similar, but perhaps a bit jumpier), is to set a threshold distance. To say "As soon as it's within 4px of home, snap to the end", or switch to a linear model, rather than continuing the easing.

How to loop a drawLine so it repeats itself 10 times

I am currently trying to create a sudoku grid in javascript, to do this I need to set up a loop so one line re-appears 10 times with a gap of 20 pixels between each one. So far I have:
var canvas;
canvas = openGraphics();
var x;
var y;
var gap;
x = 20;
y = 20;
gap = 25;
canvas.drawLine(20, 20, 20, 245);
canvas.paint();
How would you recommend to do this?
As you already stated, you have to use a looping construct.
The Mozilla Developer Network has good documentation on these.
But honestly, I think you should rather read their JavaScript Guide before trying to write a Game, otherwise you'll end up bumping into a ton of dead ends and you will soon loose the interest in making the game at all.
Also, please stay on MDN when searching looking JavaScript help, since there are a lot of sites on the Internet that have bad, old, broken code example and help.
Especially stay away from w3schools.
There are various looping constructs available to programmers, the 2 most common ones are the for loop and the while loop.
The for loop is good for "looping" a number of times and the while loop is for looping while some value is "true".
In this case, you know the number of lines that you need to draw so the for loop is best matched
This is example code for the for loop. What happens is that the code between the { and } is run multiple time. Each time the loop runs, variable i gets larger by 1, starting from 0. This continues until the condition i<numberTimesToLoop becomes false.
for(var i=0;i<numberTimesToLoop;i++)
{
document.write("i = " + i);
document.write("<br />");
}
Not that I want to do your assignment for you, but in a lot of cases its easier to learn from seeing actual code. (I demonstrate and I find plenty of students who on seeing an answer can immediately recognise it in the future. Of course I give them the theory first and if it doesn't click I try this method...)
This modification to your current code will draw your vertical lines.
var canvas;
canvas = openGraphics();
var x;
var y;
var gap;
x = 20;
y = 20;
gap = 25;
var currentX = 20;
for(var i=0;i<10;i++)
{
canvas.drawLine(currentX, 20, currentX, 245);
currentX = currentX + gap;
}
canvas.paint();
(Not really sure what the problem with the w3schools website is)

Categories