Detecting circle intersection from array - javascript

I have a canvas on which I can place circles wherever I click. I want to detect when any two circle intersect, so i am storing my coordinates in an array.
The radius of every circle is 30, so that is just hardcoded into my formula. That said, even when I place two on top of each other, it's not triggering my little filltext to let me know that it's working. I've tried many things. If someone could tell me why this isn't working, that would be appreciable. The parts where I place the dots works just fine; I just need to detect overlap.
window.onload = init;
function init() {
var canvas = document.getElementById("testCanvas");
var context = canvas.getContext("2d");
var newPath = false;
var circles = [];
canvas.onmousedown = function(e) {
newPath = true;
x = e.clientX - e.target.offsetLeft;
y = e.clientY - e.target.offsetTop;
context.moveTo(x, y);
context.beginPath();
context.arc(x, y, 30, 0, 2 * Math.PI, true);
var nextColor = randomColor();
context.fillStyle = nextColor;
context.fill();
var aCircle = [x, y];
function isIntersect(aCircle, circle) {
return Math.sqrt((aCircle[0]-circle.x) ** 2 + (aCircle[1] - circle.y) ** 2) < 30;
};
circles.forEach(circle => {
if (isIntersect(aCircle, circle)) {
context.fillText('INTERSECTED', 60, 160);
}
});
circles.push(aCircle);
context.closePath();
}
}

Multiply the radius by 2 since each circle has one...
window.onload = init;
function init() {
var canvas = document.getElementById("testCanvas");
var context = canvas.getContext("2d");
var newPath = false;
var circles = [];
canvas.onmousedown = function(e) {
newPath = true;
x = e.clientX - e.target.offsetLeft;
y = e.clientY - e.target.offsetTop;
context.moveTo(x, y);
context.beginPath();
context.arc(x, y, 30, 0, 2 * Math.PI, true);
var nextColor = '#123123' //randomColor();
context.fillStyle = nextColor;
context.fill();
var aCircle = [x, y];
function isIntersect(aCircle, circle) {
var radius = 30;
var dist = Math.hypot(aCircle[0]-circle[0], aCircle[1]-circle[1]);
return dist <= (radius * 2)
};
circles.forEach(circle => {
if (isIntersect(aCircle, circle)) {
console.log("intresected");
//context.fillText('INTERSECTED', 0, 0);
}
});
circles.push(aCircle);
context.closePath();
}
}
<canvas id='testCanvas'></canvas>

Related

All circles being drawn in the same colour [duplicate]

This question already has answers here:
Drawing lines with canvas by using for loop
(2 answers)
Closed 2 years ago.
Shortly summarized the problem is this. I want to draw two circles on the canvas with different colors. For some reason, they are drawn in the same color, even though the console log I have placed in is switching between "green" and "blue". Sorry that some of the variable names are in my native language if that poses a problem just ask and I'll translate it.
var bodyEl = document.querySelector("body");
var canvasEl = document.createElement("canvas");
var height = window.innerHeight;
var width = window.innerWidth;
canvasEl.height = height;
canvasEl.width = width;
bodyEl.appendChild(canvasEl);
var ctx = canvasEl.getContext("2d");
var obj = [];
class ball {
constructor(radius, farge, xPosisjon, yPosisjon) {
this.x = xPosisjon;
this.y = yPosisjon;
this.rad = radius;
this.farge = farge;
}
get areal() {
let areal = "areal: " + (Math.PI * this.rad * this.rad + "px");
return (areal);
}
tegn() {
//console.log(this.farge);
ctx.fillStyle = this.farge;
ctx.arc(this.x, this.y, this.rad, 0, 2 * Math.PI);
ctx.fill();
}
}
obj.push(new ball(20, "green", 100, 100));
obj.push(new ball(30, "blue", 500, 300));
setInterval(() => {
obj.forEach(x => {
x.tegn();
});
}, 30);
You need to add a ctx.beginPath().
The reason you are seeing the same color is related to the same problem found in this question: Drawing lines with canvas by using for loop. If you don't use beginPath(), you keep pushing draw commands to the same (root) path and then drawing the ever increasingly complex path.
You have to use beginPath to start a sub-path. ctx.fill() will close the sub-path. The closePath is optional.
The third, and an optional step, is to call closePath(). This method
tries to close the shape by drawing a straight line from the current
point to the start. If the shape has already been closed or there's
only one point in the list, this function does nothing.
var bodyEl = document.querySelector("body");
var canvasEl = document.createElement("canvas");
var height = window.innerHeight;
var width = window.innerWidth;
canvasEl.height = height;
canvasEl.width = width;
bodyEl.appendChild(canvasEl);
var ctx = canvasEl.getContext("2d");
var obj = [];
class ball {
constructor(radius, farge, xPosisjon, yPosisjon) {
this.x = xPosisjon;
this.y = yPosisjon;
this.rad = radius;
this.farge = farge;
}
get areal() {
let areal = "areal: " + (Math.PI * this.rad * this.rad + "px");
return (areal);
}
tegn() {
//console.log(this.farge);
ctx.beginPath();
ctx.fillStyle = this.farge;
ctx.arc(this.x, this.y, this.rad, 0, 2 * Math.PI);
ctx.fill();
}
}
obj.push(new ball(20, "green", 100, 100));
obj.push(new ball(30, "blue", 500, 300));
setInterval(() => {
ctx.clearRect(0,0,500,500);
obj.forEach(x => {
x.tegn();
});
}, 1000);
your paths are being drawn in the correct colors, but your second (blue) arc is drawing on top of your first (green) one. at the top of your tegn method, add a call to ctx.beginPath() to let your canvas know that your paths should be independent.
You missed beginPath.
var bodyEl = document.querySelector("body");
var canvasEl = document.createElement("canvas");
var height = window.innerHeight;
var width = window.innerWidth;
canvasEl.height = height;
canvasEl.width = width;
bodyEl.appendChild(canvasEl);
var ctx = canvasEl.getContext("2d");
var obj = [];
class ball {
constructor(radius, farge, xPosisjon, yPosisjon) {
this.x = xPosisjon;
this.y = yPosisjon;
this.rad = radius;
this.farge = farge;
}
get areal() {
let areal = "areal: " + (Math.PI * this.rad * this.rad + "px");
return (areal);
}
tegn() {
//console.log(this.farge);
ctx.beginPath();
ctx.fillStyle = this.farge;
ctx.arc(this.x, this.y, this.rad, 0, 2 * Math.PI);
ctx.fill();
}
}
obj.push(new ball(20, "green", 100, 100));
obj.push(new ball(30, "blue", 500, 300));
setInterval(() => {
obj.forEach(x => {
x.tegn();
});
}, 30);

Create a collision region on canvas elements That interacts with mouse Events

I want to create a collision region around a canvas element that enables me to interact with that element using mouse events width vanilla javascript.
To elaborate more on my problem here is the following:
at first I make an arc segment constructor with x, y, radius, beginAngle, endAngle, and a color arguments
var canvas = document.querySelector('canvas');
var ctx = canvas.getContext('2d');
/* arc class constructor */
function ArcSegment(x, y, radius, beginAngle, endAngle, segColor) {
this.x = x;
this.y = y;
this.radius = radius;
this.beginAngle = beginAngle;
this.endAngle = endAngle;
this.segColor = segColor;
this.update = function() {
this.draw();
}
this.draw = function(){
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, this.beginAngle, this.endAngle, false);
ctx.lineWidth = 20;
ctx.strokeStyle = this.segColor;
ctx.stroke();
}
}
Secondly, i add some value to create those arc segments
/* x, y, radius, startAngle, endAngle and color */
var centerX = canvas.width/2;
var centerY = canvas.height/2;
var radiuses = [
100,
120
];
var pi = Math.PI;
var segmentStart = [
pi/2,
0
];
var segmentRotation = [
1.4*pi,
0.2*pi
];
var segmentColors = [
"#133046",
"#15959F"
];
Then, i draw Them on the canvas.
var segment1 = new ArcSegment(centerX, centerY, radiuses[0], segmentStart[0], segmentStart[0]+segmentRotation[0], segmentColors[0]);
segment1.update();
var segment2 = new ArcSegment(centerX, centerY, radiuses[1], segmentStart[1], segmentStart[1]+segmentRotation[1], segmentColors[1]);
segment2.update();
and here is the result:
What i want now is a way to create a collision detection on top of each arc segment created, so when a mouse is clicked or moved on top of that specific arc segment
a sequence of events can occur (like a rotation animation for example or so...).
all the research i've done suggest to get the x and y value of a rectangle and calculate the distance of mouse position (mouse.x, mouse.y) and the length of the rectangle, but that method doesn't work with an arc segment with a lineWidth property.
Any help on the subject would be very appreciated.
Below is a pure mathematical approach, the key here is the code isPointInside
// Classes
function Arc(x, y, angle, arc, radius, colour, highlightColour) {
this.x = x;
this.y = y;
this.angle = angle;
this.arc = arc;
this.radius = radius;
this.colour = colour;
this.highlightColour = highlightColour;
this.highlighted = false;
this.lineWidth = 20;
}
Arc.prototype = {
isPointInside: function(x, y) {
var _x = x - this.x;
var _y = y - this.y;
var distance = Math.sqrt(_x * _x + _y * _y);
var invDistance = 1.0 / distance;
var angle = Math.acos(
_x * Math.cos(this.angle) * invDistance +
_y * Math.sin(this.angle) * invDistance
);
return distance > (this.radius - this.lineWidth/2) &&
distance < (this.radius + this.lineWidth/2) &&
angle < this.arc/2;
},
render: function(ctx) {
ctx.lineWidth = this.lineWidth;
ctx.strokeStyle = this.highlighted ? this.highlightColour : this.colour;
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, this.angle - this.arc/2, this.angle + this.arc/2, false );
ctx.stroke();
}
};
// Variables
var canvas = null;
var ctx = null;
var arcs = [];
// Functions
function draw() {
ctx.fillStyle = "gray";
ctx.fillRect(0, 0, 999, 999);
for (var i = 0; i < arcs.length; ++i) {
arcs[i].render(ctx);
}
}
// Event Listeners
function onMouseMove(e) {
var bounds = canvas.getBoundingClientRect();
var x = e.clientX - bounds.left;
var y = e.clientY - bounds.top;
for (var i = 0; i < arcs.length; ++i) {
arcs[i].highlighted = arcs[i].isPointInside(x, y);
}
draw();
}
// Entry Point
onload = function() {
canvas = document.getElementById("canvas");
canvas.onmousemove = onMouseMove;
ctx = canvas.getContext("2d");
arcs.push(new Arc(190, 75, 0.2, 1.8, 60, "blue", "lime"));
arcs.push(new Arc(90, 75, 3.5, 4.2, 60, "red", "lime"));
draw();
}
<canvas id="canvas"></canvas>

Limit values between two points on an arc?

I'm trying to adapt the code from a previous question on circular dial controls. The concept is pretty similar to this one, except I would like to define a range in which the dial cannot be selected. Consider the volume controls/dials in hardware; they often have these 'dead zones' where they can't be turned:
How can I replicate this in JavaScript? Here's the adapted code so far:
function Dial(size) {
var dial = this;
var canvas = document.querySelector('#c');
var ctx = canvas.getContext("2d");
var pi2 = Math.PI*2;
this.from = 0.75 * Math.PI;
this.to = 0.25 * Math.PI;
this.value = this.from;
var radius = size / 2 - 10;
this.draw = function() {
ctx.save();
ctx.clearRect(0,0,size,size);
ctx.translate(size/2,size/2);
ctx.beginPath();
ctx.strokeStyle = "silver";
ctx.lineWidth = 2;
ctx.arc(0, 0, radius, this.from, this.to);
ctx.stroke();
ctx.beginPath();
ctx.lineWidth = 1;
ctx.fillStyle = "green";
ctx.strokeStyle = "black";
ctx.arc(-radius*Math.sin(this.value),
-radius*Math.cos(this.value),
8, 0, 2*Math.PI);
ctx.fill();
ctx.stroke();
ctx.restore();
};
var getMousePos = function(canvas, evt) {
return {
x: event.pageX - canvas.offsetLeft,
y: event.pageY - canvas.offsetTop
};
};
var inBounds = function(pos) {
return Math.hypot(
size / 2 - radius * Math.sin(dial.value) - pos.x,
size / 2 - radius * Math.cos(dial.value) - pos.y
) <= 8;
};
canvas.addEventListener("mousemove", function(evt) {
var pos = getMousePos(canvas, evt);
if (dial.markerMoving) {
if (pos.x == size/2 && pos.y == size/2)
return;
dial.value = Math.atan2(size/2-pos.x,size/2-pos.y);
}
dial.draw();
}, false);
canvas.addEventListener("mousedown", function(evt) {
var pos = getMousePos(canvas, evt);
dial.markerMoving = inBounds(pos);
}, false);
canvas.addEventListener("mouseup", function(evt) {
dial.markerMoving = false;
}, false);
this.draw();
};
new Dial(150);
<canvas id="c"></canvas>
Bonus points if you can work out how to display a 'range' on the selection - from the starting point on the dial to the selection point.
Turns out it was pretty straightforward, using Math.abs.
function Dial(size) {
var dial = this;
var canvas = document.querySelector('#c');
var ctx = canvas.getContext("2d");
var pi2 = Math.PI*2;
this.from = 0.75 * Math.PI;
this.to = 0.25 * Math.PI;
this.value = this.from;
var radius = size / 2 - 10;
this.draw = function() {
ctx.save();
ctx.clearRect(0,0,size,size);
ctx.translate(size/2,size/2);
ctx.beginPath();
ctx.strokeStyle = "silver";
ctx.lineWidth = 2;
ctx.arc(0, 0, radius, this.from, this.to);
ctx.stroke();
ctx.beginPath();
ctx.lineWidth = 1;
ctx.fillStyle = "green";
ctx.strokeStyle = "black";
ctx.arc(-radius*Math.sin(this.value),
-radius*Math.cos(this.value),
8, 0, 2*Math.PI);
ctx.fill();
ctx.stroke();
ctx.restore();
};
var getMousePos = function(canvas, evt) {
return {
x: event.pageX - canvas.offsetLeft,
y: event.pageY - canvas.offsetTop
};
};
var inBounds = function(pos) {
return Math.hypot(
size / 2 - radius * Math.sin(dial.value) - pos.x,
size / 2 - radius * Math.cos(dial.value) - pos.y
) <= 8;
};
canvas.addEventListener("mousemove", function(evt) {
var pos = getMousePos(canvas, evt);
if (dial.markerMoving) {
if (pos.x == size/2 && pos.y == size/2) {
return;
}
var radians = Math.atan2(size/2-pos.x,size/2-pos.y);
if (Math.abs(radians) < dial.from) {
dial.value = radians;
dial.draw();
}
}
}, false);
canvas.addEventListener("mousedown", function(evt) {
var pos = getMousePos(canvas, evt);
dial.markerMoving = inBounds(pos);
}, false);
canvas.addEventListener("mouseup", function(evt) {
dial.markerMoving = false;
}, false);
this.draw();
};
new Dial(150);
<canvas id="c"></canvas>

HTML5 Canvas Arc redraw on hover

I have three arcs, the first one loads on page-load, the second one loads on mouse-over and the third one on mouse-out. I want the mouse-over-out effect to happen each time rather than just one time (as it is now).
here's the fiddle: http://jsfiddle.net/krish7878/7bX7n/
Here's the JS code:
var currentEndAngle = 0;
var currentStartAngle = 0;
var currentEndAngle2 = 0;
var currentStartAngle2 = 0;
var currentEndAngle3 = -0.5;
var currentStartAngle3 = -0.5;
var something = setInterval(draw, 5);
$("#canvas1").hover(
function(){
var something2 = setInterval(draw2, 5);
},
function(){
var something3 = setInterval(draw3, 5);
}
);
function draw() { /***************/
var can = document.getElementById('canvas1'); // GET LE CANVAS
var canvas = document.getElementById("canvas1");
var context = canvas.getContext("2d");
var x = canvas.width / 2;
var y = canvas.height / 2;
var radius;
var width;
var currentColor = "#00b5ff";
var radius = 100;
var width = 8;
var startAngle = currentStartAngle * Math.PI;
var endAngle = (currentEndAngle) * Math.PI;
if(currentEndAngle < 0.1){
currentEndAngle = currentEndAngle - 0.01;
}
if (currentEndAngle < -0.5){
clearInterval(something);
}
context.beginPath();
context.arc(x, y, radius, startAngle, endAngle, true);
context.lineWidth = width;
// line color
context.strokeStyle = currentColor;
context.stroke();
/************************************************/
}
function draw2() { /***************/
var can = document.getElementById('canvas1'); // GET LE CANVAS
var canvas = document.getElementById("canvas1");
var context = canvas.getContext("2d");
var x = canvas.width / 2;
var y = canvas.height / 2;
var radius;
var width;
var currentColor = "#000";
var radius = 100;
var width = 7;
var startAngle = currentStartAngle2 * Math.PI;
var endAngle = (currentEndAngle2) * Math.PI;
if(currentEndAngle2 < 0.1){
currentEndAngle2 = currentEndAngle2 - 0.01;
}
if (currentEndAngle2 < -0.55){
clearInterval(something2);
}
context.beginPath();
context.arc(x, y, radius, startAngle, endAngle, true);
context.lineWidth = width;
// line color
context.strokeStyle = currentColor;
context.stroke();
/*
context.beginPath();
context.clearRect ( 0 , 0 , 400 , 400 );
context.stroke():
/************************************************/
}
function draw3() { /***************/
var can = document.getElementById('canvas1'); // GET LE CANVAS
var canvas = document.getElementById("canvas1");
var context = canvas.getContext("2d");
var x = canvas.width / 2;
var y = canvas.height / 2;
var radius;
var width;
var currentColor = "#00b5ff";
var radius = 100;
var width = 8;
var startAngle = currentStartAngle3 * Math.PI;
var endAngle = (currentEndAngle3) * Math.PI;
if(currentEndAngle3 < 0){
currentEndAngle3 = currentEndAngle3 + 0.01;
}
if (currentEndAngle3 > 0){
clearInterval(something3);
}
context.beginPath();
context.arc(x, y, radius, startAngle, endAngle, false);
context.lineWidth = width;
// line color
context.strokeStyle = currentColor;
context.stroke();
/************************************************/
}
Code Explanation: there are three functions draw(), draw2(), draw3() - draw is run when the page loads, it draws a blue arc, draw2() is executed when mouse-over happens and draws a black line, draw3 is run when mouse-out happens.
Show I draw them on individual canvases and clear them individually or is there a method to get this done?
Here's one way to do it:
A Demo: http://jsfiddle.net/m1erickson/wMy4G/
Define an arc object
var arc={
cx:canvas.width/2,
cy:canvas.height/2,
radius:100,
startRadians:0,
endRadians:-Math.PI/2,
linewidth:8,
animationPercent:0,
animationRate:10,
animationDirection:0,
};
Draw a portion of the arc based on an animation point
function drawArc(arc,color){
var rStart=arc.startRadians;
var rEnd=arc.endRadians;
if(!arc.animationDirection==0){
if(arc.animationDirection>0){
rEnd=arc.animationPercent/100*(rEnd-rStart);
}else{
rEnd=(100-arc.animationPercent)/100*(rEnd-rStart);
}
}
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.beginPath();
ctx.arc(arc.cx,arc.cy,arc.radius,rStart,rEnd,true);
ctx.strokeStyle=color;
ctx.stroke();
}
Animate portions of the arc
function animate(time){
if(continueAnimation){RAF=requestAnimationFrame(animate);}
drawArc(arc,"blue");
arc.animationPercent+=arc.animationRate;
if(arc.animationPercent>=100){
continueAnimation=false;
}
}
React to hover events by drawing or undrawing the arc
$("#canvas").hover(
function(){
cancelAnimationFrame(RAF);
arc.animationPercent=0;
arc.animationDirection=1;
continueAnimation=true;
requestAnimationFrame(animate);
},
function(){
cancelAnimationFrame(RAF);
arc.animationPercent=0;
arc.animationDirection=-1;
continueAnimation=true;
requestAnimationFrame(animate);
}
);

Animation loop and scaling

Well I've got a few question to ask! Firstly What this code is doing is creating and drawing snowflakes with unique density which will all fall at a different rate. My first question is how do i make this loop continuous?
Secondly, I've translated my origin point(0,0) to the middle of the canvas (it was part of the criteria). I've now got this issue in which that when the snowfall is called it will either be drawn on the left side of the screen or the right, not both. How do i solve this?
Finally i know when doing animations that you have to clear the canvas after each re-drawing, however i haven't added this in and yet it still works fine?
//Check to see if the browser supports
//the addEventListener function
if(window.addEventListener)
{
window.addEventListener
(
'load', //this is the load event
onLoad, //this is the evemnt handler we going to write
false //useCapture boolen value
);
}
//the window load event handler
function onLoad(Xi, Yy) {
var canvas, context,treeObj, H, W, mp;
Xi = 0;
Yy = 0;
mp = 100;
canvas = document.getElementById('canvas');
context = canvas.getContext('2d');
W = window.innerWidth;
H = window.innerHeight;
canvas.width = W;
canvas.height = H;
context.translate(W/2, H/2);
var particles = [];
for(var i = 0; i < mp; i++) {
particles.push({
x: Math.random()*-W, //x
y: Math.random()*-H, //y
r: Math.random()*6+2, //radius
d: Math.random()* mp // density
})
}
treeObj = new Array();
var tree = new TTree(Xi, Yy);
treeObj.push(tree);
function drawCenterPot(){
context.beginPath();
context.lineWidth = "1";
context.strokeStyle = "Red";
context.moveTo(0,0);
context.lineTo(0,-H);
context.lineTo(0, H);
context.lineTo(-W, 0);
context.lineTo(W,0);
context.stroke();
context.closePath();
}
function drawMountain() {
context.beginPath();
context.fillStyle = "#FFFAF0";
context.lineWidth = "10";
context.strokeStyle = "Black";
context.moveTo(H,W);
context.bezierCurveTo(-H*10,W,H,W,H,W);
context.stroke();
context.fill();
}
function drawSky() {
var linearGrad = context.createLinearGradient(-100,-300, W/2,H);
linearGrad.addColorStop(0, "#000000");
linearGrad.addColorStop(1, "#004CB3");
context.beginPath();
context.fillStyle = linearGrad;
context.fillRect(-W/2, -H/2, W, H);
context.stroke();
context.fill();
drawMountain();
drawCenterPot();
}
function drawSnow(){
context.fillStyle = "White";
context.beginPath();
for(i = 0; i<mp; i++)
{
var p = particles[i];
context.moveTo(p.x,p.y);
context.arc(p.x, p.y, p.r, Math.PI*2, false);
}
context.fill();
}
function update() {
var angle = 0;
angle+=0.1;
for(var i=0; i<mp; i++) {
var p = particles[i];
p.x += Math.sin(angle) * 2;
p.y += Math.cos(angle+p.d) + 1 * p.r;
}
drawSky();
drawSnow();
draw();
}
function draw() {
for(var i =0; i < treeObj.length; i++)
{
context.save();
context.translate(Xi-H,Yy-W);
context.scale(1, 1);
treeObj[0].draw(context);
context.restore();
}
}
setInterval(update, 33);
}
About your animation:
What's happening is your flakes are falling out of view below the bottom of the canvas.
So when any flake's p.y+p.r > canvas.height you could:
destroy that flake and (optionally) add another falling from above the canvas
or
"recycle" that flake by changing its p.y to above the canvas.
About your design working without context.clearRect:
In your design, when you fill the whole canvas with "sky", you are effectively clearing the canvas.
About your flakes only falling on half the screen:
Instead of translating to mid-screen:
Don't translate at all and let p.x be any Math.random()*canvas.width

Categories

Resources