So I have built a circular random colour picker utility in javascript and HTML5 Canvas and all the components are dynamic, the size of the objects adjusts to the size of the screen and the spacing also adjusts to the size of the screen. Furthermore, if the user resizes the display the utility also dynamically resizes.
I am using an array to store the colours for the circles. When the circles are generated they use the first colour in the array, delete that colour from the array, and then shuffle the array.
The problem is that when the user resizes the display the colour array does not have enough colours left to draw all the circles, this is because the code removes used colours so that there are no duplicates. However, i tried to fix this by declaring a constant array of the colours called origColours and setting the colours array equal to the origColours array.
Below is the code I have written. I cannot see how or why the origColours array is being manipulated, hopefully you can help
:)
//########//SETUP
var canvas = document.getElementById("myCanvas");
var c = canvas.getContext("2d");
canvas.height = innerHeight;
canvas.width = innerWidth;
document.documentElement.style.overflow = 'hidden'; // firefox, chrome
document.body.scroll = "no"; // ie only
//########//COLORS
const origColours = ["#1c2133", "#2b6ea8", "#5d99bf", "#333968", "#000000", "#b000b0", "#0000aa", "#ff0000", "#00aaaa", "#7CFC00", "#00FF7F", "#8B0000", "#F0E68C"];
var colours = ["#1c2133", "#2b6ea8", "#5d99bf", "#333968", "#000000", "#b000b0", "#0000aa", "#ff0000", "#00aaaa", "#7CFC00", "#00FF7F", "#8B0000", "#F0E68C"];
//########//VARIABLES
var backgroundColour = 0;
var mouse = {
x: undefined,
y: undefined,
};
var key = {
keyCode: undefined,
}
var mainRadius = 0;
var smallRadius = 0;
var pointerCircle;
var circles = [];
//########//EVENTS
window.addEventListener("mousemove", function(event) {
mouse.x = event.x;
mouse.y = event.y;
})
window.addEventListener("keypress", function(event) {
key.keyCode = event.keyCode;
if (key.keyCode == 32) {
switchBg();
}
})
window.addEventListener('resize', function(event) {
canvas.width = innerWidth
canvas.height = innerHeight
setup();
})
//########//OBJECTS
function Circle(x, y, radius, colour) {
this.x = x;
this.y = y;
this.radius = radius;
//this.n = Math.floor(Math.random()*colours.length);
if (colour == undefined) {
//this.fill = colours[this.n];
this.fill = colours[0];
this.orignalFill = this.fill;
colours.shift();
colours = shuffleArray(colours);
} else {
this.fill = colour;
this.orignalFill = this.fill;
}
this.draw = function() {
c.fillStyle = this.fill;
c.strokeStyle = this.colour;
c.beginPath();
c.arc(this.x,this.y,this.radius,0,Math.PI*2);
c.fill();
}
this.update = function() {
//Bounce off the edges
// if (this.x + this.radius > innerWidth || this.x - this.radius < 0) {
// this.dx = -this.dx;
// }
// if (this.y + this.radius > innerHeight || this.y - this.radius < 0) {
// this.dy = -this.dy;
// }
//Move circle
// this.x += this.dx;
// this.y += this.dy;
//Draw the circle after all calculations have been made
this.draw();
}
}
//########//UTILITY FUNCTIONS
function shuffleArray(arr) {
var j, x, i;
for (i = arr.length - 1; i > 0; i--) {
j = Math.floor(Math.random() * (i + 1));
x = arr[i];
arr[i] = arr[j];
arr[j] = x;
}
return arr;
}
function checkCollisions(obj1, objs) {
for (var i = 0; i < objs.length; i++) {
if (checkCollision(obj1, objs[i])) {
return objs[i]
}
}
}
function renderCircles(arr) {
for (var i = 0; i < arr.length; i++) {
arr[i].update();
}
}
function checkCollision(object1, object2) {
var obj_s = getDistance(object1.x, object1.y, object2.x, object2.y);
if (obj_s < object1.radius + object2.radius) {
return true;
} else {
return false;
}
}
function getDistance(x1, y1, x2, y2) {
xs = x2 - x1;
ys = y2 - y1;
return Math.sqrt(Math.pow(xs, 2) + Math.pow(ys, 2));
}
function switchBg() {
if (backgroundColour == 0) {
document.body.style.backgroundColor = "black"
backgroundColour = 1
} else if (backgroundColour == 1) {
document.body.style.backgroundColor = "white"
backgroundColour = 0
}
}
//########//ANIMATION
function animate() {
requestAnimationFrame(animate);
c.clearRect(0,0,innerWidth,innerHeight);
pointerCircle.x = mouse.x;
pointerCircle.y = mouse.y;
var result = checkCollisions(pointerCircle, circles);
if (result != undefined) {
circles[0].fill = result.fill;
} else {
circles[0].fill = circles[0].orignalFill;
}
pointerCircle.update();
renderCircles(circles);
}
//########//RUNNING CODE
function setup() {
if (innerHeight >= innerWidth) {
mainRadius = innerWidth/6;
} else {
mainRadius = innerHeight/6;
}
smallRadius = mainRadius/2;
c.clearRect(0,0,innerWidth,innerHeight);
circles = [];
colours = origColours
pointerCircle = new Circle(0,0,1, "rgba(0,0,0,0)");
circles.push(new Circle(innerWidth/2, innerHeight/2, mainRadius, "white"));
circles.push(new Circle((innerWidth/2)-mainRadius*2, innerHeight/2, smallRadius));
circles.push(new Circle((innerWidth/2)+mainRadius*2, innerHeight/2, smallRadius));
circles.push(new Circle((innerWidth/2), (innerHeight/2)-mainRadius*2, smallRadius));
circles.push(new Circle((innerWidth/2), (innerHeight/2)+mainRadius*2, smallRadius));
var angCoE = mainRadius / 2 * 3;
circles.push(new Circle((innerWidth/2)+angCoE, (innerHeight/2)-angCoE, smallRadius));
circles.push(new Circle((innerWidth/2)+angCoE, (innerHeight/2)+angCoE, smallRadius));
circles.push(new Circle((innerWidth/2)-angCoE, (innerHeight/2)-angCoE, smallRadius));
circles.push(new Circle((innerWidth/2)-angCoE, (innerHeight/2)+angCoE, smallRadius));
}
setup();
animate();
Note: So I was a little too hasty and didn't read your question thoroughly enough. The real solution was posted by Hey24sheep and pooyan below -- I'm leaving this here to explain a different facet of the question.
Declaring a variable as const means you cannot change its value. If the variable in question holds a reference to an object (such as an array), this means you cannot make the variable refer to a different object.
For example, if you tried this:
const colors = [ 'red', 'green', 'blue' ];
colors = [ 'yellow', 'cyan', 'magenta' ];
This would fail, because you're trying to change what colors refers to. However, the array itself is a separate entity from your variable, and its properties are still free to be manipulated.
What you're looking for in this case is Object.freeze():
const colors = Object.freeze([ 'red', 'green', 'blue' ]);
Now you will find that you cannot add, remove, or change any elements of the array. And, since you delcared it with const, you cannot reassign the variable colors either.
Further info:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const
In JavaScript objects are passed and assigned by reference (more accurately the value of a reference), so colours is a reference to the same object.
Because you are doing this in your Setup function.
colours = origColours
You need to create a copy if you need to modify one and not the other.
Basically, the slice() operation clones the array and returns the reference to the new array
colours = origColours.slice();
you should clone your array instead of colours = origColours. one way to clone your array is colours = origColours.slice(0); otherwise when you change your colours array, your origColours would be effected too.
you can make a copy of the array and this will leave the original array untouched theres a few ways you can copy an array
colours = origColours.slice();
or if you're using es7 polyfills
colours = [...origColours]
const means you can't change the assignment but you can change the insides of the assignmeant
//you can do this
const a = [1, 2]; // [1]
a.pop()
console.log(a)
// but you cant do this
const i = 5;
i = 4; // erro
Related
I tried to create a canvas effect with fireworks, but the more you click, the faster it gets and it seems to accumulate on itself. When I listed the speed it was similar and did not correspond to what was happening there. I also tried to cancel the draw if it got out of the canvas but it didnĀ“t help.
Here is link https://dybcmwd8icxxdxiym4xkaw-on.drv.tw/canvasTest.html
var fireAr = [];
var expAr = [];
function Firework(x, y, maxY, maxX, cn, s, w, en) {
this.x = x;
this.y = y;
this.maxY = maxY;
this.maxX = maxX;
this.cn = cn;
this.s = s;
this.w = w;
this.en = en;
this.i = 0;
this.explosion = function() {
for (; this.i < this.en; this.i++) {
var ey = this.maxY;
var ex = this.maxX;
var ecn = Math.floor(Math.random() * color.length);
var esX = (Math.random() - 0.5) * 3;
var esY = (Math.random() - 0.5) * 3;
var ew = Math.random() * 10;
var t = true;
expAr.push(new Exp(ew, esX, esY, ex, ey, ecn, t));
}
for (var e = 0; e < expAr.length; e++) {
expAr[e].draw();
}
}
this.draw = function() {
if (this.y < this.maxY) {
this.explosion();
} else {
this.track();
this.y -= this.s;
}
}
}
function Exp(ew, esX, esY, ex, ey, ecn, t) {
this.ew = ew;
this.esX = esX;
this.esY = esY;
this.ex = ex;
this.ey = ey;
this.ecn = ecn;
this.t = t;
this.draw = function() {
if (this.t == true) {
c.beginPath();
c.shadowBlur = 20;
c.shadowColor = color[this.ecn];
c.rect(this.ex, this.ey, this.ew, this.ew);
c.fillStyle = color[this.ecn];
c.fill();
c.closePath();
this.ex += this.esX;
this.ey += this.esY;
}
}
}
window.addEventListener('click', function(event) {
var x = event.clientX;
var y = canvas.height;
mouse.clickX = event.clientX;
mouse.clickY = event.clientY;
var maxY = event.clientY;
var maxX = event.clientX;
var cn = Math.floor(Math.random() * color.length);
var s = Math.random() * 5 + 5;
var w = Math.random() * 20 + 2;
var en = Math.random() * 50 + 5;
fireAr.push(new Firework(x, y, maxY, maxX, cn, s, w, en));
});
function ani() {
requestAnimationFrame(ani);
c.clearRect(0, 0, canvas.width, canvas.height);
for (var i = 0; i < fireAr.length; i++) {
fireAr[i].draw();
}
}
ani();
I deleted some unnecessary parts in my opinion but if I'm wrong and I missed something I'll try to fix it
Here are a few simple ways you can improve performance:
Commenting out shadowBlur gives a noticeable boost. If you need shadows, see this answer which illustrates pre-rendering.
Try using fillRect and ctx.rotate() instead of drawing a path. Saving/rotating/restoring the canvas might be prohibitive, so you could use non-rotated rectangles.
Consider using a smaller canvas which is quicker to repaint than one that may fill the entire window.
Another issue is more subtle: Fireworks and Exps are being created (making objects is expensive!) and pushed onto arrays. But these arrays are never trimmed and objects are never reused after they've left the visible canvas. Eventually, the rendering loop gets bogged down by all of the computation for updating and rendering every object in the fireAr and expAr arrays.
A naive solution is to check for objects exiting the canvas and splice them from the expAr. Here's pseudocode:
for (let i = expAr.length - 1; i >= 0; i--) {
if (!inBounds(expAr[i], canvas)) {
expAr.splice(i, 1);
}
}
Iterate backwards since this mutates the array's length. inBounds is a function that checks an Exp object's x and y properties along with its size and the canvas width and height to determine if it has passed an edge. More pseudocode:
function inBounds(obj, canvas) {
return obj.x >= 0 && obj.x <= canvas.width &&
obj.y >= 0 && obj.y <= canvas.height;
}
This check isn't exactly correct since the rectangles are rotated. You could check each corner of the rectangle with a pointInRect function to ensure that at least one is inside the canvas.
Fireworks can be spliced out when they "explode".
splice is an expensive function that walks up to the entire array to shift items forward to fill in the vacated element. Splicing multiple items in a loop gives quadratic performance. This can be made linear by putting surviving fireworks in a new list and replacing the previous generation on each frame. Dead firework objects can be saved in a pool for reuse.
Beyond that, I strongly recommend using clear variable names.
this.cn = cn;
this.s = s;
this.w = w;
this.en = en;
this.i = 0;
These names have little or no meaning to an outside reader and are unlikely to mean much to you if you take a couple months away from the code. Use full words like "size", "width", etc.
Another side point is that it's a good idea to debounce your window resize listener.
Here's a quick proof of concept that illustrates the impact of shadowBlur and pruning dead elements.
const rnd = n => ~~(Math.random() * n);
const mouse = {pressed: false, x: 0, y: 0};
let fireworks = [];
let shouldSplice = false;
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
document.body.appendChild(canvas);
document.body.style.margin = 0;
canvas.style.background = "#111";
canvas.width = document.body.scrollWidth;
canvas.height = document.body.clientHeight;
ctx.shadowBlur = 0;
const fireworksAmt = document.querySelector("#fireworks-amt");
document.querySelector("input[type=range]").addEventListener("change", e => {
ctx.shadowBlur = e.target.value;
document.querySelector("#shadow-amt").textContent = ctx.shadowBlur;
});
document.querySelector("input[type=checkbox]").addEventListener("change", e => {
shouldSplice = !shouldSplice;
});
const createFireworks = (x, y) => {
const color = `hsl(${rnd(360)}, 100%, 60%)`;
return Array(rnd(20) + 1).fill().map(_ => ({
x: x,
y: y,
vx: Math.random() * 6 - 3,
vy: Math.random() * 6 - 3,
size: rnd(4) + 2,
color: color
}));
}
(function render() {
if (mouse.pressed) {
fireworks.push(...createFireworks(mouse.x, mouse.y));
}
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (const e of fireworks) {
e.x += e.vx;
e.y += e.vy;
e.vy += 0.03;
ctx.beginPath();
ctx.fillStyle = ctx.shadowColor = e.color;
ctx.arc(e.x, e.y, e.size, 0, Math.PI * 2);
ctx.fill();
if (shouldSplice) {
e.size -= 0.03;
if (e.size < 1) {
e.dead = true;
}
}
}
fireworks = fireworks.filter(e => !e.dead);
fireworksAmt.textContent = "fireworks: " + fireworks.length;
requestAnimationFrame(render);
})();
let debounce;
addEventListener("resize", e => {
clearTimeout(debounce);
debounce = setTimeout(() => {
canvas.width = document.body.scrollWidth;
canvas.height = document.body.clientHeight;
}, 100);
});
canvas.addEventListener("mousedown", e => {
mouse.pressed = true;
});
canvas.addEventListener("mouseup", e => {
mouse.pressed = false;
});
canvas.addEventListener("mousemove", e => {
mouse.x = e.offsetX;
mouse.y = e.offsetY;
});
* {
font-family: monospace;
user-select: none;
}
div > span, body > div {padding: 0.5em;}
canvas {display: block;}
<div>
<div id="fireworks-amt">fireworks: 0</div>
<div>
<label>splice? </label>
<input type="checkbox">
</div>
<div>
<label>shadowBlur (<span id="shadow-amt">0</span>): </label>
<input type="range" value=0>
</div>
</div>
I am working on a project these days. My goal is to change the color of the intersecting areas of the two squares. I have written the code which detects whenever two squares intersect but I cant figure out how to change the color of the intersecting area. Kindly help me with this.
var sketch = function (p) {
with(p) {
let squares = [];
let dragObject = null; // variable to hold the object being dragged
p.setup = function() {
createCanvas(600, 520);
button1 = createButton("Alpha");
button2 = createButton("Bravo");
button3 = createButton("Charlie");
button4 = createButton("Delta");
button5 = createButton("Echo");
button6 = createButton("Foxtrot");
button7 = createButton("Golf");
button8 = createButton("Hotel");
button9 = createButton("India");
button10 = createButton("Juliet");
button1.mousePressed(doSomething);
};
p.draw = function() {
background(25, 240, 255);
// if a square is being dragged, update its position
if (this.dragObject != null) {
this.dragObject.position.x = mouseX;
this.dragObject.position.y = mouseY;
}
//draw all squares
for (let i = 0; i < squares.length; i++) {
let s = squares[i];
s.show();
}
for (let i = 0; i < squares.length; i++) {
for (let j = i + 1; j < squares.length; j++) {
if (i != j && squares[i].collides(squares[j])) {
squares[i].changecolor();
}
}
}
};
p.mousePressed = function () {
if (this.dragObject == null) {
//ask every square if they are being "hit"
for (let i = 0; i < squares.length; i++) {
let s = squares[i];
if (s.hitTest()) {
//if so, set the drag object as this square and return
this.dragObject = s;
return;
}
}
//no squares are hit, create a new square.
let square = new Square(mouseX, mouseY);
squares.push(square);
}
};
//mouse is released, release the current dragged object if there is one
p.mouseReleased = function () {
this.dragObject = null;
};
class Square {
constructor(InitialX, InitialY) {
this.w = 60;
this.h = 60;
this.position = {
x: InitialX,
y: InitialY
};
}
//basic test of mouse position against square position and if its inside the rectangle
hitTest() {
let x = mouseX - this.position.x;
let y = mouseY - this.position.y;
return (x > 0 && x < this.w) && (y > 0 && y < this.h);
}
show() {
fill(50);
rect(this.position.x, this.position.y, this.w, this.h);
}
collides(sqr) {
if (this.position.x < sqr.position.x + sqr.w &&
this.position.x + this.w > sqr.position.x &&
this.position.y < sqr.position.y + sqr.h &&
this.position.y + this.h > sqr.position.y) {
return true;
}
return false;
}
changecolor() {
fill(random(255), random(255), random(255));
background(200, 255, 100);
for (let i = 0; i < squares.length; i++) {
let s = squares[i];
s.show();
}
}
}
function doSomething() {
// fill(230, 170, 90);
// ellipse(random(600), random(410), 30, 30);
squares.pop();
}
}
};
let node = document.createElement('div');
window.document.getElementById('p5-container').appendChild(node);
new p5(sketch, node);
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.js"></script>
<div id="p5-container"></div>
Lets think a bit how we could represent the intersecting area between two squares. Surely, one of the ways to do is simply to represent it as another rectangle, whose color we simply change based on the intercepting area. To draw a rectangle, we need to know the coordinates of the upper left corner, the width and the height. Therefore the challenge is to calculate those as we drag our squares around. This should be done in the draw() function. You already have the intersection check implemented, whats left is to calculate the new rectangle upper left point (newX, newY), width (newW) and height (newH).
In order to calculate the upper left corner, the width and height, we can add this to the block where we check for collision:
...
//block checking collision
if (i != j && squares[i].collides(squares[j])) {
squares[i].changecolor();
//set intersection color
fill(50);
//calculate parameters
newX = Math.max(squares[i].position.x, squares[j].position.x);
newY = Math.max(squares[i].position.y, squares[j].position.y);
newW = Math.min(squares[i].position.x + squares[i].w, squares[j].position.x + squares[j].w) - newX;
newH = Math.min(squares[i].position.y + squares[i].h, squares[j].position.y + squares[j].h) - newY;
//draw rectangle
rect(newX, newY, newW, newH);
}
Result:
Good day,
I am generating some circles with colors, sizes and positions. All of this things randomly.
But, my problem is that I do not want them to collide, so that no circle is inside another, not even a little bit.
The logic explained in detail within the code, I would like to know why the failure and why the infinite loop.
The important functions are:
checkSeparation and setPositions
window.addEventListener("load", draw);
function draw() {
var canvas = document.getElementById("balls"), // Get canvas
ctx = canvas.getContext("2d"); // Context
canvas.width = document.body.clientWidth; // Set canvas width
canvas.height = document.documentElement.scrollHeight; // Height
var cW = canvas.width, cH = canvas.height; // Save in vars
ctx.fillStyle = "#fff022"; // Paint background
ctx.fillRect(0, 0, cW, cH); // Coordinates to paint
var arrayOfBalls = createBalls(); // create all balls
setPositions(arrayOfBalls, cW, cH);
arrayOfBalls.forEach(ball => { // iterate balls to draw
ctx.beginPath(); // start the paint
ctx.fillStyle = ball.color;
ctx.arc(ball.x, ball.y, ball.radius, 0, (Math.PI/180) * 360, false); // draw the circle
ctx.fill(); // fill
ctx.closePath(); // end the paint
});
}
function Ball() {
this.x = 0; // x position of Ball
this.y = 0; // y position of Ball
this.radius = Math.floor(Math.random() * ( 30 - 10 + 1) + 10);
this.color = "";
}
Ball.prototype.setColor = function(){
for(var j = 0, hex = "0123456789ABCDEF", max = hex.length,
random, str = ""; j <= 6; j++, random = Math.floor(Math.random() * max), str += hex[random])
this.color = "#" + str;
};
function random(val, min) {
return Math.floor(Math.random() * val + min); // Random number
}
function checkSeparation(value, radius, toCompare) {
var min = value - radius, // Min border of circle
max = value + radius; // Max border of circle
// Why ? e.g => x position of circle + this radius it will be its right edge
for(; min <= max; min++) {
if(toCompare.includes(min)) return false;
/*
Since all the positions previously obtained, I add them to the array, in order to have a reference when verifying the other positions and that they do NOT collide.
Here I check if they collide.
In the range of:
[pos x - its radius, pos x + its radius]
*/
}
return true; // If they never collided, it returns true
}
function createBalls() {
var maxBalls = 50, // number of balls
balls = []; // array of balls
for(var j = 0; j < maxBalls; j++) { // create 50 balls
var newBall = new Ball(); // create ball
newBall.setColor(); // set the ball color
balls.push(newBall); //push the ball to the array of balls
}
return balls; // return all balls to draw later
}
function setPositions(balls, canvasW, canvasH) {
var savedPosX = [], // to save x pos of balls
savedPosY = []; // to save y pos of balls
for(var start = 0, max = balls.length; start < max; start++) {
var current = balls[start], // current ball
randomX = random(canvasW, current.radius), // get random value for x pos
randomY = random(canvasH, current.radius); // get random value for y pos
if(checkSeparation(randomX, current.radius, savedPosX)) {
current.x = randomX; // If it position, along with your radio does not touch another circle, I add the position
} else {
// start--; continue;
console.log("X: The above code causes an infinite loop");
}
if(checkSeparation(randomY, current.radius, savedPosY)) {
current.y = randomY;
} else {
// start--; continue;
console.log("Y: The above code causes an infinite loop");
}
}
}
body,html {
margin: 0; border: 0; padding: 0; overflow: hidden;
}
<canvas id="balls"></canvas>
In your code, you test possible collisions by means of arrays of already used x and y positions, but you never add new positions to these arrays. You also check the x and y coordinates separately, which means you are really testing a collision of a bounding box.
Two circles collide when the distance between their centres is smaller than the sum of their radii, so you could use:
function collides(balls, n, x, y, r) {
for (let i = 0; i < n; i++) {
let ball = balls[i];
let dx = ball.x - x;
let dy = ball.y - y;
let dd = dx*dx + dy*dy;
let rr = r + ball.radius;
if (dd < rr * rr) return true;
}
return false;
}
function setPositions(balls, canvasW, canvasH) {
for (let i = 0, max = balls.length; i < max; i++) {
let ball = balls[i],
r = ball.radius,
maxTries = 20;
ball.x = -canvasW;
ball.y = -canvasH;
for (let tries = 0; tries = maxTries; tries++) {
let x = random(canvasW - 2*r, r),
y = random(canvasH - 2*r, r);
if (!collides(balls, i, x, y, r)) {
ball.x = x;
ball.y = y;
break;
}
}
}
}
This is reasonably fast for 50 balls, but will be slow if you have more balls. In that case, some spatial data structures can speed up the collision search.
You must also guard against the case that no good place can be found. The code above gives up after 20 tries and moves the ball outside the visible canvas. You can improve the chances of placing balls by sorting the balls by radius and plaing the large balls first.
Finally, you add one hex digit too many to your random colour. (That for loop, where everything happens in the loop control is horrible, by the way.)
I'm trying to have my Particle object collide and reflect off my Slate object.
If I wanted to use an ellipse, it would be simple because I could just create a radius variable - can't do that with a rectangle.
It's something to do with the distance variable, I just can't figure it out.
var div;
var movers;
function setup() {
createCanvas(windowWidth,windowHeight);
background("#FDEDEB");
div = new Slate();
movers = new Particle();
}
function draw() {
background("#FDEDEB");
div.display();
movers.display();
movers.update();
movers.move();
if (movers.hits(div)) {
console.log("hit");
}
}
function Slate() {
this.x = 30;
this.y = height/2;
this.display = function() {
noStroke();
fill("#DF4655");
rect(this.x,this.y, 700, 200);
}
}
function Particle() {
this.pos = createVector(10,0);
this.vel = createVector(0,0);
this.acc = createVector(0.01,0.01);
this.history = [];
this.display = function() {
fill("#DF4655");
point(this.pos.x,this.pos.y);
//beginShape();
for(var j = 0; j < this.history.length; j++) {
var pos = this.history[j];
ellipse(pos.x,pos.y, 5, 3);
}
//endShape();
}
this.update = function() {
var v = createVector(this.pos.x,this.pos.y);
this.history.push(v);
if (this.history.length > 10) {
this.history.splice(0,1);
}
}
this.hits = function(div) {
// BOUNCE OFF SLATE
var d = dist(this.pos.x,this.pos.y,div.x,div.y);
if (d < 0) {
console.log('hits');
}
}
this.move = function() {
this.pos.add(this.vel);
this.vel.add(this.acc);
this.vel.limit(10);
for (var i = 0; i < this.history.length; i++) {
this.history[i].x += random(-2,2);
this.history[i].y += random(-2,2);
}
}
}
If the particle is a point (or can be represented as a point), you need to use point-rectangle collision detection. Basically you would need to check whether the point is between the left and right edges and the top and bottom edges of the rectangle.
if(pointX > rectX && pointX < rectX + rectWidth && pointY > rectY && pointY < rectY + rectHeight){
//the point is inside the rectangle
}
If the particle is an ellipse and you need to factor in the radius of that ellipse, then you're better off representing the particle as a rectangle, just for collision purposes. Then you can use rectangle-rectangle collision detection. This is also called a bounding box, and it's one of the most common ways to handle collision detection.
if(rectOneRight > rectTwoLeft && rectOneLeft < rectTwoRight && rectOneBottom > rectTwoTop && rectOneTop < rectTwoBottom){
//the rectangles are colliding
}
Shameless self-promotion: I wrote a tutorial on collision detection available here. It's for Processing, but everything is the same for P5.js.
I have a canvas, and I'm using geolocation, google maps and openweathermap to get the weather of where the user is. The weather will be taken and cause the game I'm making to also use that weather...
How can I check if two squares that are spawned onto the canvas overlap? This is the code for the rain, which looks nothing like rain at the moment...
function rectangle(x, y, w, h) {
var randomx = Math.floor(Math.random() * canvas.width - 50);
this.x = randomx || x || 0;
this.y = y || 0;
this.w = w || 0;
this.h = h || 0;
this.expired = false;
this.draw = function() {
cx.fillStyle = "blue";
cx.fillRect(this.x, this.y, this.w, this.h);
};
this.update = function() {
this.y++;
if (y > canvas.height) {
this.expired = true;
}
};
}
var rectangles = new Array();
function newRect() {
rectangles.push(new rectangle(window.randomx, window.y, 10, 10));
}
var timing = 0;
function loop() {
cx.clearRect(0, 0, canvas.width, canvas.height);
if (timing % 10 === 0) {
newRect();
}
for (var i = 0, l = rectangles.length; i < l; i++) {
rectangles[i].update();
rectangles[i].draw();
if (rectangles[i].expired) {
rectangles.splice(i, 1);
i--;
}
}
timing++;
requestAnimFrame(loop);
}
loop();
My assumption, is that I need to perform a 'hit test' to see if two rectangles in the array are in the same perimeter... I guess where my this.draw and this.update function are I should do something like...
this.hitTest = function () {
//Maths to check here
};
Then in the forloop do...
rectangles[i].hitTest();
But I'm not sure on the Maths or where to go from there...
Any help would be appreciated, thanks in advance!
You can extend your rectangle object like this:
function rectangle(x, y, w, h) {
... existing code here ...
}
rectangle.prototype.intersects = function(rect) {
return !( rect.x > (this.x + this.w) ||
(rect.x + rect.w) < this.x ||
rect.y > (this.y + this.h) ||
(rect.y + rect.h) < this.y);
}
Based on this code by Daniel Vassallo, adopted for your object.
Now simply call the function on the rectangle you want to compare with:
if ( rectangles[x].intersects(rectangles[y]) ) {
... they intersect ...
}
To check if a new rectangle intersects with an existing you can do:
function isOverlapping(myNewRect, rectangles) {
for(var i = 0, r; r = rectangles[i]; i++) {
if (myNewRect.intersect(r)) return true;
}
return false;
}