Why circles are vibrating on collision (Canvas) - javascript

I have been creating a clone of agar.io and I don't understand why the circles start vibrating when they touch each other. Below is my code:
var
canvas,
ctx,
width = innerWidth,
height = innerHeight,
mouseX = 0,
mouseY = 0;
var
camera = {
x: 0,
y: 0,
update: function(obj) {
this.x = obj.x - width / 2;
this.y = obj.y - height / 2;
}
},
player = {
defaultMass: 54,
x: 0,
y: 0,
blobs: [],
update: function() {
for (var i = 0; i < this.blobs.length; i++) {
var x = mouseX + camera.x - this.blobs[i].x;
var y = mouseY + camera.y - this.blobs[i].y;
var length = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
var speed = 54 / this.blobs[i].mass;
this.blobs[i].velX = x / length * speed * Math.min(1, Math.pow(x / this.blobs[i].mass, 2));
this.blobs[i].velY = y / length * speed * Math.min(1, Math.pow(x / this.blobs[i].mass, 2));
this.blobs[i].x += this.blobs[i].velX;
this.blobs[i].y += this.blobs[i].velY;
for (var j = 0; j < this.blobs.length; j++) {
if (j != i && this.blobs[i] !== undefined) {
var blob1 = this.blobs[i];
var blob2 = this.blobs[j];
var dist = Math.sqrt(Math.pow(blob2.x - blob1.x, 2) + Math.pow(blob2.y - blob1.y, 2));
if (dist < blob1.mass + blob2.mass) {
if (this.blobs[i].x < this.blobs[j].x) {
this.blobs[i].x--;
} else if (this.blobs[i].x > this.blobs[j].x) {
this.blobs[i].x++;
}
if (this.blobs[i].y < this.blobs[j].y) {
this.blobs[i].y--;
} else if ((this.blobs[i].y > this.blobs[j].y)) {
this.blobs[i].y++;
}
}
}
}
}
this.x += (mouseX - width / 2) / (width / 2) * 1;
this.y += (mouseY - height / 2) / (height / 2) * 1
},
split: function(cell) {
cell.mass /= 2;
this.blobs.push({
x: cell.x,
y: cell.y,
mass: cell.mass
});
},
draw: function() {
for (var i = 0; i < this.blobs.length; i++) {
ctx.fillStyle = "red";
ctx.beginPath();
ctx.arc(-camera.x + this.blobs[i].x, -camera.y + this.blobs[i].y, this.blobs[i].mass, 0, Math.PI * 2);
ctx.fill();
ctx.closePath();
}
}
};
function handleMouseMove(e) {
mouseX = e.clientX;
mouseY = e.clientY;
}
function setup() {
canvas = document.getElementById("game");
ctx = canvas.getContext("2d");
canvas.width = width;
canvas.height = height;
addEventListener("mousemove", handleMouseMove);
player.blobs.push({
x: 0,
y: 0,
mass: player.defaultMass
});
player.blobs.push({
x: 100,
y: 100,
mass: player.defaultMass / 2
});
player.blobs.push({
x: 100,
y: 100,
mass: player.defaultMass * 2
});
var loop = function() {
update();
draw();
requestAnimationFrame(loop);
}
requestAnimationFrame(loop);
}
function update() {
camera.update(player.blobs[0]);
player.update();
}
function draw() {
ctx.fillStyle = "#fff";
ctx.fillRect(0, 0, width, height);
player.draw();
}
setup();
body {
margin: 0;
padding: 0;
}
<canvas id="game">kindly update your browser.</canvas>

Separating circles
Your separation code was not correct. Use the vector between them to get the new pos.
The vector between them
To find if two circles are intercepting find the length of the vector from one to the next
The two circles.
var cir1 = {x : 100, y : 100, r : 120}; // r is the radius
var cir2 = {x : 250, y : 280, r : 150}; // r is the radius
The vector from cir2 to cir1
var vx = cir2.x - cir1.x;
var vy = cir2.y - cir1.y;
The length of the vector
var len = Math.sqrt(x * x + y * y);
// or use the ES6 Math.hypot function
/* var len = Math.hypot(x,y); */
The circles overlap if the sum of the radii is greater than the length of the vector between them
if(cir1.r + cir2.r > len){ // circles overlap
Normalise the vector
If they overlap you need to move one away from the other. There are many ways to do this, the simplest way is to move one circle along the line between them.
First normalise the vector from cir1 to cir2 by dividing by its (vector) length.
vx \= len;
vy \= len;
Note that the length could be zero. If this happens then you will get NaN in further calculations. If you suspect you may get one circle at the same location as another the easiest way to deal with the zero move one circle a little.
// replace the two lines above with
if(len === 0){ // circles are on top of each other
vx = 1; // move the circle (abstracted into the vector)
}else{
vx \= len; // normalise the vector
vy \= len;
}
Move circle/s to just touch
Now you have the normalised vector which is 1 unit long you can make it any length you need by multiplying the two scalars vx, vy with the desired length which in this case is the sum of the two circles radii.
var mx = vx * (cir1.r + cir2.r); // move distance
var my = vy * (cir1.r + cir2.r);
.Only use one of the following methods.
You can now position one of the circles the correct distance so that they just touch
// move cir1
cir1.x = cir2.x - mx;
cir1.y = cir2.y - my;
Or move the second circle
cir2.x = cir1.x + mx;
cir2.y = cir1.y + my;
Or move both circles but you will have to first find the proportional center between the two
var pLen = cir1.r / (cir1.r + cir2.r); // find the ratio of the radii
var cx = cir1.x + pLen * vx * len; // find the proportional center between
var cy = cir1.y + pLen * vy * len; // the two circles
Then move both circles away from that point by their radii
cir1.x = cx - vx * cir1.r; // move circle 1 away from the shared center
cir1.y = cy - vy * cir1.r;
cir2.x = cx + vx * cir2.r; // move circle 2 away from the shared center
cir2.y = cy + vy * cir2.r;
DEMO
Copy of OP's snippet with mods to fix problem by moving the the first circle blob1 away from the second blob2 and assuming they will never be at the same spot (no divide by zero)
var
canvas,
ctx,
width = innerWidth,
height = innerHeight,
mouseX = 0,
mouseY = 0;
var
camera = {
x: 0,
y: 0,
update: function(obj) {
this.x = obj.x - width / 2;
this.y = obj.y - height / 2;
}
},
player = {
defaultMass: 54,
x: 0,
y: 0,
blobs: [],
update: function() {
for (var i = 0; i < this.blobs.length; i++) {
var x = mouseX + camera.x - this.blobs[i].x;
var y = mouseY + camera.y - this.blobs[i].y;
var length = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
var speed = 54 / this.blobs[i].mass;
this.blobs[i].velX = x / length * speed * Math.min(1, Math.pow(x / this.blobs[i].mass, 2));
this.blobs[i].velY = y / length * speed * Math.min(1, Math.pow(x / this.blobs[i].mass, 2));
this.blobs[i].x += this.blobs[i].velX;
this.blobs[i].y += this.blobs[i].velY;
for (var j = 0; j < this.blobs.length; j++) {
if (j != i && this.blobs[i] !== undefined) {
var blob1 = this.blobs[i];
var blob2 = this.blobs[j];
var x = blob2.x - blob1.x; // get the vector from blob1 to blob2
var y = blob2.y - blob1.y; //
var dist = Math.sqrt(x * x + y * y); // get the distance between the two blobs
if (dist < blob1.mass + blob2.mass) { // if the distance is less than the 2 radius
// if there is overlap move blob one along the line between the two the distance of the two radius
x /= dist; // normalize the vector. This makes the vector 1 unit long
y /= dist;
// multiplying the normalised vector by the correct distance between the two
// and subtracting that distance from the blob 2 give the new pos of
// blob 1
blob1.x = blob2.x - x * (blob1.mass + blob2.mass);
blob1.y = blob2.y - y * (blob1.mass + blob2.mass);
}
}
}
}
this.x += (mouseX - width / 2) / (width / 2) * 1;
this.y += (mouseY - height / 2) / (height / 2) * 1
},
split: function(cell) {
cell.mass /= 2;
this.blobs.push({
x: cell.x,
y: cell.y,
mass: cell.mass
});
},
draw: function() {
for (var i = 0; i < this.blobs.length; i++) {
ctx.fillStyle = "red";
ctx.beginPath();
ctx.arc(-camera.x + this.blobs[i].x, -camera.y + this.blobs[i].y, this.blobs[i].mass, 0, Math.PI * 2);
ctx.fill();
ctx.closePath();
}
}
};
function handleMouseMove(e) {
mouseX = e.clientX;
mouseY = e.clientY;
}
function setup() {
canvas = document.getElementById("game");
ctx = canvas.getContext("2d");
canvas.width = width;
canvas.height = height;
addEventListener("mousemove", handleMouseMove);
player.blobs.push({
x: 0,
y: 0,
mass: player.defaultMass
});
player.blobs.push({
x: 100,
y: 100,
mass: player.defaultMass / 2
});
player.blobs.push({
x: 100,
y: 100,
mass: player.defaultMass * 2
});
var loop = function() {
update();
draw();
requestAnimationFrame(loop);
}
requestAnimationFrame(loop);
}
function update() {
camera.update(player.blobs[0]);
player.update();
}
function draw() {
ctx.fillStyle = "#fff";
ctx.fillRect(0, 0, width, height);
player.draw();
}
setup();
body {
margin: 0;
padding: 0;
}
<canvas id="game">kindly update your browser.</canvas>

var
canvas,
ctx,
width = innerWidth,
height = innerHeight,
mouseX = 0,
mouseY = 0;
var
camera = {
x: 0,
y: 0,
update: function(obj) {
this.x = obj.x - width / 2;
this.y = obj.y - height / 2;
}
},
player = {
defaultMass: 54,
x: 0,
y: 0,
blobs: [],
update: function() {
for (var i = 0; i < this.blobs.length; i++) {
var x = mouseX + camera.x - this.blobs[i].x;
var y = mouseY + camera.y - this.blobs[i].y;
var length = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
var speed = 54 / this.blobs[i].mass;
this.blobs[i].velX = x / length * speed * Math.min(1, Math.pow(x / this.blobs[i].mass, 2));
this.blobs[i].velY = y / length * speed * Math.min(1, Math.pow(x / this.blobs[i].mass, 2));
this.blobs[i].x += this.blobs[i].velX;
this.blobs[i].y += this.blobs[i].velY;
for (var j = 0; j < this.blobs.length; j++) {
if (j != i && this.blobs[i] !== undefined) {
var blob1 = this.blobs[i];
var blob2 = this.blobs[j];
var dist = Math.sqrt(Math.pow(blob2.x - blob1.x, 2) + Math.pow(blob2.y - blob1.y, 2));
if (dist < blob1.mass + blob2.mass) {
if (this.blobs[i].x < this.blobs[j].x) {
this.blobs[i].x--;
} else if (this.blobs[i].x > this.blobs[j].x) {
this.blobs[i].x++;
}
if (this.blobs[i].y < this.blobs[j].y) {
this.blobs[i].y--;
} else if ((this.blobs[i].y > this.blobs[j].y)) {
this.blobs[i].y++;
}
}
}
}
}
this.x += (mouseX - width / 2) / (width / 2) * 1;
this.y += (mouseY - height / 2) / (height / 2) * 1
},
split: function(cell) {
cell.mass /= 2;
this.blobs.push({
x: cell.x,
y: cell.y,
mass: cell.mass
});
},
draw: function() {
for (var i = 0; i < this.blobs.length; i++) {
ctx.fillStyle = "red";
ctx.beginPath();
ctx.arc(-camera.x + this.blobs[i].x, -camera.y + this.blobs[i].y, this.blobs[i].mass, 0, Math.PI * 2);
ctx.fill();
ctx.closePath();
}
}
};
function handleMouseMove(e) {
mouseX = e.clientX;
mouseY = e.clientY;
}
function setup() {
canvas = document.getElementById("game");
ctx = canvas.getContext("2d");
canvas.width = width;
canvas.height = height;
addEventListener("mousemove", handleMouseMove);
player.blobs.push({
x: 0,
y: 0,
mass: player.defaultMass
});
player.blobs.push({
x: 100,
y: 100,
mass: player.defaultMass / 2
});
player.blobs.push({
x: 100,
y: 100,
mass: player.defaultMass * 2
});
var loop = function() {
update();
draw();
requestAnimationFrame(loop);
}
requestAnimationFrame(loop);
}
function update() {
camera.update(player.blobs[0]);
player.update();
}
function draw() {
ctx.fillStyle = "#fff";
ctx.fillRect(0, 0, width, height);
player.draw();
}
setup();
body {
margin: 0;
padding: 0;
}
<canvas id="game">kindly update your browser.</canvas>

Related

how to load next string when i erase particle text?

I am new to vanilla js and webgl content. I created a particle text which get distorted on mouse hover. and whose particles are connected with each other with a line. the code is as follows.
const canvas = document.getElementById("canvas1");
const dpr = window.devicePixelRatio;
//console.log(dpr);
const ctx = canvas.getContext("2d");
ctx.scale(dpr, dpr);
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
let particleArray = [];
let adjustX = 10;
let adjustY = 0;
//handle mouse
const mouse = {
x: null,
y: null,
radius: 30,
};
window.addEventListener("mousemove", function (event) {
mouse.x = event.x;
mouse.y = event.y;
//console.log(mouse.x, mouse.y);
});
ctx.fillStyle = "white";
ctx.font = "35px verdana";
ctx.fillText("aman ", 0, 30);
const textCoordinates = ctx.getImageData(0, 0, 100, 100);
class Particle {
constructor(x, y) {
this.x = x + Math.random() * 6 - 3;
this.y = y + Math.random() * 6 - 3;
this.size = 1;
this.baseX = this.x;
this.baseY = this.y;
this.density = Math.random() * 10 + 50;
}
draw() {
ctx.fillStyle = "white";
ctx.beginPath();
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
ctx.closePath();
ctx.fill();
}
//distance between mouse and particle
update() {
let dx = mouse.x - this.x;
let dy = mouse.y - this.y;
let distance = Math.sqrt(dx * dx + dy * dy);
let forceDirectionX = dx / distance;
let forceDirectionY = dy / distance;
let maxDistance = mouse.radius;
let force = (maxDistance - distance) / maxDistance;
let directionX = forceDirectionX * force * this.density;
let directionY = forceDirectionY * force * this.density;
if (distance < mouse.radius) {
this.x -= directionX;
this.y -= directionY;
} else {
if (this.x !== this.baseX) {
let dx = this.x - this.baseX;
this.x += dx / 10;
}
if (this.y !== this.baseY) {
let dy = this.y - this.baseY;
this.y += dy / 10;
}
}
}
}
//console.log(textCoordinates);
function init() {
particleArray = [];
for (let y = 0, y2 = textCoordinates.height; y < y2; y++) {
for (let x = 0, x2 = textCoordinates.width; x < x2; x++) {
if (
textCoordinates.data[y * 4 * textCoordinates.width + x * 4 + 3] > 128
) {
let positionX = x + adjustX;
let positionY = y + adjustY;
particleArray.push(new Particle(positionX * 4, positionY * 4));
}
}
}
}
init();
//console.log(particleArray);
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (let i = 0; i < particleArray.length; i++) {
particleArray[i].draw();
particleArray[i].update();
}
connect();
requestAnimationFrame(animate);
}
animate();
function connect() {
let opacityValue = 1;
for (let a = 0; a < particleArray.length; a++) {
for (let b = a; b < particleArray.length; b++) {
let dx = particleArray[a].x - particleArray[b].x;
let dy = particleArray[a].y - particleArray[b].y;
let distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 14) {
opacityValue = 0.5;
ctx.strokeStyle = "rgb(200,150,160," + opacityValue + ")";
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(particleArray[a].x, particleArray[a].y);
ctx.lineTo(particleArray[b].x, particleArray[b].y);
ctx.stroke();
}
}
}
}
now what I want to do is that, when I erase this particle text, next text must appear. just like on this website https://animal.cc/ .
can anyone please help me in this :))
(This isn't three.js related)
When you determine "this particle text" has been erased, you can do this in animate():
ctx.fillText("NEW ", 0, 30);
textCoordinates = ctx.getImageData(0, 0, 100, 100);
init();
EDIT: code for comment (below)
for (let y = 0, y2 = textCoordinates.height; y < y2; y++) {
for (let x = 0, x2 = textCoordinates.width; x < x2; x++) {

adding custom animation in canvas html5

this might be somewhat difficult but i wil still ask, so i made a starfield ,now what i want to do is to have my stars( a pair each) connected to eachother by a line ,now this line will expand as the stars move forward and disappear when the stars move out of the canvas .any help would be appreciated here this is difficult i have the logic but i seem unable to follow the correct way to implement it
function randomRange(minVal, maxVal) {
return Math.floor(Math.random() * (maxVal - minVal - 1)) + minVal;
}
function initStars() {
for (var i = 0; i < stars.length; i++) {
stars[i] = {
x: randomRange(-25, 25),
y: randomRange(-25, 25),
z: randomRange(1, MAX_DEPTH)
}
}
}
function degToRad(deg) {
radians = (deg * Math.PI / 180) - Math.PI / 2;
return radians;
}
function animate() {
var halfWidth = canvas.width / 2;
var halfHeight = canvas.height / 2;
ctx.fillStyle = "rgb(0,0,0)";
ctx.fillRect(0, 0, canvas.width, canvas.height);
for (var i = 0; i < stars.length; i++) {
stars[i].z -= 0.2;
if (stars[i].z <= 0) {
stars[i].x = randomRange(-25, 25);
stars[i].y = randomRange(-25, 25);
stars[i].z = MAX_DEPTH;
}
var k = 128.0 / stars[i].z;
var px = stars[i].x * k + halfWidth;
var py = stars[i].y * k + halfHeight;
if (px >= 0 && px <= 1500 && py >= 0 && py <= 1500) {
var size = (1 - stars[i].z / 32.0) * 5;
var shade = parseInt((1 - stars[i].z / 32.0) * 750);
ctx.fillStyle = "rgb(" + shade + "," + shade + "," + shade + ")";
ctx.beginPath();
ctx.arc(px, py, size, degToRad(0), degToRad(360));
ctx.fill();
}
}
}
function animate() {
var halfWidth = canvas.width / 2;
var halfHeight = canvas.height / 2;
ctx.fillStyle = "rgb(0,0,0)";
ctx.fillRect(0, 0, canvas.width, canvas.height);
for (var i = 0; i < stars.length; i++) {
stars[i].z -= 0.2;
if (stars[i].z <= 0) {
stars[i].x = randomRange(-25, 25);
stars[i].y = randomRange(-25, 25);
stars[i].z = MAX_DEPTH;
}
var k = 128.0 / stars[i].z;
var px = stars[i].x * k + halfWidth;
var py = stars[i].y * k + halfHeight;
if (px >= 0 && px <= 1500 && py >= 0 && py <= 1500) {
var size = (1 - stars[i].z / 32.0) * 5;
var shade = parseInt((1 - stars[i].z / 32.0) * 750);
ctx.fillStyle = "rgb(" + shade + "," + shade + "," + shade + ")";
ctx.beginPath();
ctx.arc(px, py, size, degToRad(0), degToRad(360));
ctx.fill();
}
}
}
<!DOCTYPE html5>
<html>
<head>
<title>stars</title>
<script src="convergis.js"></script>
<script>
MAX_DEPTH = 32;
var canvas, ctx;
var stars = new Array(500);
window.onload = function() {
canvas = document.getElementById("tutorial");
if( canvas && canvas.getContext ) {
ctx = canvas.getContext("2d");
initStars();
setInterval(animate,17);
}
}
</script>
</head>
<body>
<canvas id='tutorial' width='1500' height='1500'>
</canvas>
</body>
</html>
You could just say you want a lightspeed effect!
One way very cheap way to do it is to paint the background with some transparency. You can also render a set of points close together in order to make the illusion of the effect.
The good way to do it is shaders since they will allow you to add glow and some other nice image trickery that will make it look better. Here is a good example: https://www.shadertoy.com/view/Xdl3D2
Below I used the canvas api lineTo and even with a fixed line width, it's a pretty good final result.
var MAX_DEPTH = 64;
var LINELENGTH = 0.1;
var stars = new Array(500);
var canvas = document.getElementById("tutorial");
canvas.width = innerWidth;
canvas.height = innerHeight;
var ctx = canvas.getContext("2d");
initStars();
setInterval(animate,17);
function randomRange(minVal, maxVal) {
return Math.floor(Math.random() * (maxVal - minVal - 1)) + minVal;
}
function initStars() {
for (var i = 0; i < stars.length; i++) {
stars[i] = {
x: randomRange(-25, 25),
y: randomRange(-25, 25),
z: randomRange(1, MAX_DEPTH)
}
}
}
function degToRad(deg) {
radians = (deg * Math.PI / 180) - Math.PI / 2;
return radians;
}
function animate() {
var halfWidth = canvas.width / 2;
var halfHeight = canvas.height / 2;
ctx.fillStyle = "rgba(0,0,0,1)";
ctx.fillRect(0, 0, canvas.width, canvas.height);
for (var i = 0; i < stars.length; i++) {
stars[i].z -= 0.5;
if (stars[i].z <= 0) {
stars[i].x = randomRange(-25, 25);
stars[i].y = randomRange(-25, 25);
stars[i].z = MAX_DEPTH;
}
var k = 254.0 / stars[i].z;
var px = stars[i].x * k + halfWidth;
var py = stars[i].y * k + halfHeight;
if (px >= 0 && px <= 1500 && py >= 0 && py <= 1500) {
var size = (1 - stars[i].z / 32.0) * 2;
var shade = parseInt((1 - stars[i].z / 32.0) * 750);
ctx.strokeStyle = "rgb(" + shade + "," + shade + "," + shade + ")";
ctx.lineWidth = size;
ctx.beginPath();
ctx.moveTo(px,py);
var ox = size * (px - halfWidth) * LINELENGTH;
var oy = size * (py - halfHeight) * LINELENGTH;
ctx.lineTo(px + ox, py + oy);
ctx.stroke();
}
}
}
<canvas id='tutorial' width='1500' height='1500'></canvas>

How to draw/Form circle with two points?

I need to draw a circle and i have only two points.Now i need to find center point and radius of the circle? You can form the circle in clock wise direction.
Thanks in advance
Here is a Brute Force approach to the problem.
EDIT
Added a max iterations limit to cut off calculations if the line between the two points is almost straight along x (meaning a radius would be nearing Infinity)
Also animations, because that makes everything better :)
var canvas = document.body.appendChild(document.createElement("canvas"));
var ctx = canvas.getContext("2d");
canvas.width = 1000;
canvas.height = 1000;
var points = [
{ x: parseInt(prompt("x1", "110")), y: parseInt(prompt("y1", "120")), r: 5 },
{ x: parseInt(prompt("x2", "110")), y: parseInt(prompt("y2", "60")), r: 5 },
];
function calculateRemainingPoint(points, x, precision, maxIteration) {
if (x === void 0) { x = 0; }
if (precision === void 0) { precision = 0.001; }
if (maxIteration === void 0) { maxIteration = 100000; }
var newPoint = {
x: x,
y: (points[0].y + points[1].y) / 2,
r: 50
};
var d0 = distance(points[0].x, points[0].y, x, newPoint.y);
var d1 = distance(points[1].x, points[1].y, x, newPoint.y);
var iteration = 0;
//Bruteforce approach
while (Math.abs(d0 - d1) > precision && iteration < maxIteration) {
var oldDiff = Math.abs(d0 - d1);
var oldY = newPoint.y;
iteration++;
newPoint.y += oldDiff / 10;
d0 = distance(points[0].x, points[0].y, x, newPoint.y);
d1 = distance(points[1].x, points[1].y, x, newPoint.y);
var diff_1 = Math.abs(d0 - d1);
if (diff_1 > oldDiff) {
newPoint.y = oldY - oldDiff / 10;
d0 = distance(points[0].x, points[0].y, x, newPoint.y);
d1 = distance(points[1].x, points[1].y, x, newPoint.y);
}
}
var diff = (points[0].x + points[1].x) / points[0].x;
newPoint.r = d0;
return newPoint;
}
points.push(calculateRemainingPoint(points));
function distance(x1, y1, x2, y2) {
var a = x1 - x2;
var b = y1 - y2;
return Math.sqrt(a * a + b * b);
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.moveTo(-canvas.width, canvas.height / 2);
ctx.lineTo(canvas.width, canvas.height / 2);
ctx.stroke();
ctx.closePath();
ctx.beginPath();
ctx.moveTo(canvas.width / 2, -canvas.height);
ctx.lineTo(canvas.width / 2, canvas.height);
ctx.stroke();
ctx.closePath();
for (var pointIndex = 0; pointIndex < points.length; pointIndex++) {
var point = points[pointIndex];
ctx.beginPath();
ctx.arc(point.x + canvas.width / 2, canvas.height / 2 - point.y, point.r, 0, Math.PI * 2);
ctx.arc(point.x + canvas.width / 2, canvas.height / 2 - point.y, 2, 0, Math.PI * 2);
ctx.stroke();
ctx.closePath();
}
}
setInterval(function () {
points = points.slice(0, 2);
points[Math.floor(Math.random() * points.length) % points.length][Math.random() > 0.5 ? 'x' : 'y'] = Math.random() * canvas.width - canvas.width / 2;
setTimeout(function () {
points.push(calculateRemainingPoint(points));
requestAnimationFrame(draw);
}, 1000 / 60);
}, 1000);
draw();
No that is impossible.
Create two circles with the same radius at centerpoints A + B. At the intersection of these two circles create an circle with the same radius....
Then make the same with an other radius....

filling circle with hexagons

I'm trying to find a way to put as much hexagons in a circle as possible. So far the best result I have obtained is by generating hexagons from the center outward in a circular shape.
But I think my calculation to get the maximum hexagon circles is wrong, especially the part where I use the Math.ceil() and Math.Floor functions to round down/up some values.
When using Math.ceil(), hexagons are sometimes overlapping the circle.
When using Math.floor() on the other hand , it sometimes leaves too much space between the last circle of hexagons and the circle's border.
var c_el = document.getElementById("myCanvas");
var ctx = c_el.getContext("2d");
var canvas_width = c_el.clientWidth;
var canvas_height = c_el.clientHeight;
var PI=Math.PI;
var PI2=PI*2;
var hexCircle = {
r: 110, /// radius
pos: {
x: (canvas_width / 2),
y: (canvas_height / 2)
}
};
var hexagon = {
r: 20,
pos:{
x: 0,
y: 0
},
space: 1
};
drawHexCircle( hexCircle, hexagon );
function drawHexCircle(hc, hex ) {
drawCircle(hc);
var hcr = Math.ceil( Math.sqrt(3) * (hc.r / 2) );
var hr = Math.ceil( ( Math.sqrt(3) * (hex.r / 2) ) ) + hexagon.space; // hexRadius
var circles = Math.ceil( ( hcr / hr ) / 2 );
drawHex( hc.pos.x , hc.pos.y, hex.r ); //center hex ///
for (var i = 1; i<=circles; i++) {
for (var j = 0; j<6; j++) {
var currentX = hc.pos.x+Math.cos(j*PI2/6)*hr*2*i;
var currentY = hc.pos.y+Math.sin(j*PI2/6)*hr*2*i;
drawHex( currentX,currentY, hex.r );
for (var k = 1; k<i; k++) {
var newX = currentX + Math.cos((j*PI2/6+PI2/3))*hr*2*k;
var newY = currentY + Math.sin((j*PI2/6+PI2/3))*hr*2*k;
drawHex( newX,newY, hex.r );
}
}
}
}
function drawHex(x, y, r){
ctx.beginPath();
ctx.moveTo(x,y-r);
for (var i = 0; i<=6; i++) {
ctx.lineTo(x+Math.cos((i*PI2/6-PI2/4))*r,y+Math.sin((i*PI2/6-PI2/4))*r);
}
ctx.closePath();
ctx.stroke();
}
function drawCircle( circle ){
ctx.beginPath();
ctx.arc(circle.pos.x, circle.pos.y, circle.r, 0, 2 * Math.PI);
ctx.closePath();
ctx.stroke();
}
<canvas id="myCanvas" width="350" height="350" style="border:1px solid #d3d3d3;">
If all the points on the hexagon are within the circle, the hexagon is within the circle. I don't think there's a simpler way than doing the distance calculation.
I'm not sure how to select the optimal fill point, (but here's a js snippet proving that the middle isn't always it). It's possible that when you say "hexagon circle" you mean hexagon made out of hexagons, in which case the snippet proves nothing :)
I made the hexagon sides 2/11ths the radius of the circle and spaced them by 5% the side length.
var hex = {x:0, y:0, r:10};
var circle = {x:100, y:100, r:100};
var spacing = 1.05;
var SQRT_3 = Math.sqrt(3);
var hexagon_offsets = [
{x: 1/2, y: -SQRT_3 / 2},
{x: 1, y: 0},
{x: 1/2, y: SQRT_3 / 2},
{x: -1/2, y: SQRT_3 / 2},
{x: -1, y: 0},
{x: -1/2, y: -SQRT_3 / 2}
];
var bs = document.body.style;
var ds = document.documentElement.style;
bs.height = bs.width = ds.height = ds.width = "100%";
bs.border = bs.margin = bs.padding = 0;
var c = document.createElement("canvas");
c.style.display = "block";
c.addEventListener("mousemove", follow, false);
document.body.appendChild(c);
var ctx = c.getContext("2d");
window.addEventListener("resize", redraw);
redraw();
function follow(e) {
hex.x = e.clientX;
hex.y = e.clientY;
redraw();
}
function drawCircle() {
ctx.strokeStyle = "black";
ctx.beginPath();
ctx.arc(circle.x, circle.y, circle.r, 0, 2 * Math.PI, true);
ctx.closePath();
ctx.stroke();
}
function is_in_circle(p) {
return Math.pow(p.x - circle.x, 2) + Math.pow(p.y - circle.y, 2) < Math.pow(circle.r, 2);
}
function drawLine(a, b) {
var within = is_in_circle(a) && is_in_circle(b);
ctx.strokeStyle = within ? "green": "red";
ctx.beginPath();
ctx.moveTo(a.x, a.y);
ctx.lineTo(b.x, b.y);
ctx.closePath();
ctx.stroke();
return within;
}
function drawShape(shape) {
var within = true;
for (var i = 0; i < shape.length; i++) {
within = drawLine(shape[i % shape.length], shape[(i + 1) % shape.length]) && within;
}
if (!within) return false;
ctx.fillStyle = "green";
ctx.beginPath();
ctx.moveTo(shape[0].x, shape[0].y);
for (var i = 1; i <= shape.length; i++) {
ctx.lineTo(shape[i % shape.length].x, shape[i % shape.length].y);
}
ctx.closePath();
ctx.fill();
return true;
}
function calculate_hexagon(x, y, r) {
return hexagon_offsets.map(function (offset) {
return {x: x + r * offset.x, y: y + r * offset.y};
})
}
function drawHexGrid() {
var hex_count = 0;
var grid_space = calculate_hexagon(0, 0, hex.r * spacing);
var y = hex.y;
var x = hex.x;
while (y > 0) {
y += grid_space[0].y * 3;
x += grid_space[0].x * 3;
}
while (y < c.height) {
x %= grid_space[1].x * 3;
while (x < c.width) {
var hexagon = calculate_hexagon(x, y, hex.r);
if (drawShape(hexagon)) hex_count++;
x += 3 * grid_space[1].x;
}
y += grid_space[3].y;
x += grid_space[3].x;
x += 2 * grid_space[1].x;
}
return hex_count;
}
function redraw() {
c.width = window.innerWidth;
c.height = window.innerHeight;
circle.x = c.width / 2;
circle.y = c.height / 2;
circle.r = Math.min(circle.x, circle.y) * 0.9;
hex.r = circle.r * (20 / 110);
ctx.clearRect(0, 0, c.width, c.height);
var hex_count = drawHexGrid();
drawCircle();
ctx.fillStyle = "rgb(0, 0, 50)";
ctx.font = "40px serif";
ctx.fillText(hex_count + " hexes within circle", 20, 40);
}

Circle animation random color

Hi I try to create an animation with a circle. The function drawRandom(drawFunctions) should pic one of the three drawcircle functions and should bring it on the canvas. Now the problem is that this function become executed every second (main loop) and the circle change his colour. How can I fix that?
window.onload = window.onresize = function() {
var C = 1; // canvas width to viewport width ratio
var el = document.getElementById("myCanvas");
var viewportWidth = window.innerWidth;
var viewportHeight = window.innerHeight;
var canvasWidth = viewportWidth * C;
var canvasHeight = viewportHeight;
el.style.position = "fixed";
el.setAttribute("width", canvasWidth);
el.setAttribute("height", canvasHeight);
var x = canvasWidth / 100;
var y = canvasHeight / 100;
var ballx = canvasWidth / 100;
var n;
window.ctx = el.getContext("2d");
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
// draw triangles
function init() {
ballx;
return setInterval(main_loop, 1000);
}
function drawcircle1()
{
var radius = x * 5;
ctx.beginPath();
ctx.arc(ballx * 108, canvasHeight / 2, radius, 0, 2 * Math.PI, false);
ctx.fillStyle = 'yellow';
ctx.fill();
}
function drawcircle2()
{
var radius = x * 5;
ctx.beginPath();
ctx.arc(ballx * 108, canvasHeight / 2, radius, 0, 2 * Math.PI, false);
ctx.fillStyle = 'blue';
ctx.fill();
}
function drawcircle3()
{
var radius = x * 5;
ctx.beginPath();
ctx.arc(ballx * 105, canvasHeight / 2, radius, 0, 2 * Math.PI, false);
ctx.fillStyle = 'orange';
ctx.fill();
}
function draw() {
var counterClockwise = false;
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
//first halfarc
ctx.beginPath();
ctx.arc(x * 80, y * 80, y * 10, 0 * Math.PI, 1 * Math.PI, counterClockwise);
ctx.lineWidth = y * 1;
ctx.strokeStyle = 'black';
ctx.stroke();
// draw stop button
ctx.beginPath();
ctx.moveTo(x * 87, y * 2);
ctx.lineTo(x * 87, y * 10);
ctx.lineWidth = x;
ctx.stroke();
ctx.beginPath();
ctx.moveTo(x * 95, y * 2);
ctx.lineTo(x * 95, y * 10);
ctx.lineWidth = x;
ctx.stroke();
function drawRandom(drawFunctions){
//generate a random index
var randomIndex = Math.floor(Math.random() * drawFunctions.length);
//call the function
drawFunctions[randomIndex]();
}
drawRandom([drawcircle1, drawcircle2, drawcircle3]);
}
function update() {
ballx -= 0.1;
if (ballx < 0) {
ballx = -radius;
}
}
function main_loop() {
draw();
update();
collisiondetection();
}
init();
function initi() {
console.log('init');
// Get a reference to our touch-sensitive element
var touchzone = document.getElementById("myCanvas");
// Add an event handler for the touchstart event
touchzone.addEventListener("mousedown", touchHandler, false);
}
function touchHandler(event) {
// Get a reference to our coordinates div
var can = document.getElementById("myCanvas");
// Write the coordinates of the touch to the div
if (event.pageX < x * 50 && event.pageY > y * 10) {
ballx += 1;
} else if (event.pageX > x * 50 && event.pageY > y * 10 ) {
ballx -= 1;
}
console.log(event, x, ballx);
draw();
}
initi();
draw();
}
Take a look at my code that I wrote:
var lastTime = 0;
function requestMyAnimationFrame(callback, time)
{
var t = time || 16;
var currTime = new Date().getTime();
var timeToCall = Math.max(0, t - (currTime - lastTime));
var id = window.setTimeout(function(){ callback(currTime + timeToCall); }, timeToCall);
lastTime = currTime + timeToCall;
return id;
}
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
canvas.width = window.innerWidth - 20;
canvas.height = window.innerHeight - 20;
canvas.style.width = canvas.width + "px";
canvas.style.height = canvas.height + "px";
var circles = [];
var mouse =
{
x: 0,
y: 0
}
function getCoordinates(x, y)
{
return "(" + x + ", " + y + ")";
}
function getRatio(n, d)
{
// prevent division by 0
if (d === 0 || n === 0)
{
return 0;
}
else
{
return n/d;
}
}
function Circle(x,y,d,b,s,c)
{
this.x = x;
this.y = y;
this.diameter = Math.round(d);
this.radius = Math.round(d/2);
this.bounciness = b;
this.speed = s;
this.color = c;
this.deltaX = 0;
this.deltaY = 0;
this.drawnPosition = "";
this.fill = function()
{
context.beginPath();
context.arc(this.x+this.radius,this.y+this.radius,this.radius,0,Math.PI*2,false);
context.closePath();
context.fill();
}
this.clear = function()
{
context.fillStyle = "#ffffff";
this.fill();
}
this.draw = function()
{
if (this.drawnPosition !== getCoordinates(this.x, this.y))
{
context.fillStyle = this.color;
// if commented, the circle will be drawn if it is in the same position
//this.drawnPosition = getCoordinates(this.x, this.y);
this.fill();
}
}
this.keepInBounds = function()
{
if (this.x < 0)
{
this.x = 0;
this.deltaX *= -1 * this.bounciness;
}
else if (this.x + this.diameter > canvas.width)
{
this.x = canvas.width - this.diameter;
this.deltaX *= -1 * this.bounciness;
}
if (this.y < 0)
{
this.y = 0;
this.deltaY *= -1 * this.bounciness;
}
else if (this.y+this.diameter > canvas.height)
{
this.y = canvas.height - this.diameter;
this.deltaY *= -1 * this.bounciness;
}
}
this.followMouse = function()
{
// deltaX/deltaY will currently cause the circles to "orbit" around the cursor forever unless it hits a wall
var centerX = Math.round(this.x + this.radius);
var centerY = Math.round(this.y + this.radius);
if (centerX < mouse.x)
{
// circle is to the left of the mouse, so move the circle to the right
this.deltaX += this.speed;
}
else if (centerX > mouse.x)
{
// circle is to the right of the mouse, so move the circle to the left
this.deltaX -= this.speed;
}
else
{
//this.deltaX = 0;
}
if (centerY < mouse.y)
{
// circle is above the mouse, so move the circle downwards
this.deltaY += this.speed;
}
else if (centerY > mouse.y)
{
// circle is under the mouse, so move the circle upwards
this.deltaY -= this.speed;
}
else
{
//this.deltaY = 0;
}
this.x += this.deltaX;
this.y += this.deltaY;
this.x = Math.round(this.x);
this.y = Math.round(this.y);
}
}
function getRandomDecimal(min, max)
{
return Math.random() * (max-min) + min;
}
function getRoundedNum(min, max)
{
return Math.round(getRandomDecimal(min, max));
}
function getRandomColor()
{
// array of three colors
var colors = [];
// go through loop and add three integers between 0 and 255 (min and max color values)
for (var i = 0; i < 3; i++)
{
colors[i] = getRoundedNum(0, 255);
}
// return rgb value (RED, GREEN, BLUE)
return "rgb(" + colors[0] + "," + colors[1] + ", " + colors[2] + ")";
}
function createCircle(i)
{
// diameter of circle
var minDiameter = 25;
var maxDiameter = 50;
// bounciness of circle (changes speed if it hits a wall)
var minBounciness = 0.2;
var maxBounciness = 0.65;
// speed of circle (how fast it moves)
var minSpeed = 0.3;
var maxSpeed = 0.45;
// getRoundedNum returns a random integer and getRandomDecimal returns a random decimal
var x = getRoundedNum(0, canvas.width);
var y = getRoundedNum(0, canvas.height);
var d = getRoundedNum(minDiameter, maxDiameter);
var c = getRandomColor();
var b = getRandomDecimal(minBounciness, maxBounciness);
var s = getRandomDecimal(minSpeed, maxSpeed);
// create the circle with x, y, diameter, bounciness, speed, and color
circles[i] = new Circle(x,y,d,b,s,c);
}
function makeCircles()
{
var maxCircles = getRoundedNum(2, 5);
for (var i = 0; i < maxCircles; i++)
{
createCircle(i);
}
}
function drawCircles()
{
var ii = 0;
for (var i = 0; ii < circles.length; i++)
{
if (circles[i])
{
circles[i].draw();
ii++;
}
}
}
function clearCircles()
{
var ii = 0;
for (var i = 0; ii < circles.length; i++)
{
if (circles[i])
{
circles[i].clear();
ii++;
}
}
}
function updateCircles()
{
var ii = 0;
for (var i = 0; ii < circles.length; i++)
{
if (circles[i])
{
circles[i].keepInBounds();
circles[i].followMouse();
ii++;
}
}
}
function update()
{
requestMyAnimationFrame(update,10);
updateCircles();
}
function draw()
{
requestMyAnimationFrame(draw,1000/60);
context.clearRect(0,0,canvas.width,canvas.height);
drawCircles();
}
window.addEventListener("load", function()
{
window.addEventListener("mousemove", function(e)
{
mouse.x = e.layerX || e.offsetX;
mouse.y = e.layerY || e.offsetY;
});
makeCircles();
update();
draw();
});

Categories