So I would like to project coordinates from one rectangle in javascript to another, in essence, scaling coordinates from one rectangle to another. How would I do that?
function Rectangle(maxX, minX, maxY, minY) {
this.maxX = maxX;
this.minX = minX;
this.maxY = maxY;
this.minY = minY;
}
So 2 Rectangles with different size and coordinates. I want to draw into one rectangle and scale that proportionally into the other rectangle.
Something like this?
var src = new Rectangle(6, 0, 6, 2);
var dst = new Rectangle(4, 2, 8, 0);
function Rectangle(maxX, minX, maxY, minY) {
this.maxX = maxX;
this.minX = minX;
this.maxY = maxY;
this.minY = minY;
}
var canvas = document.querySelector("canvas");
var width = canvas.width, height = canvas.height;
var w = width / 2 + 0.5, h = height / 2 + 0.5;
var sx = width / 20, sy = height / 20;
var context = canvas.getContext("2d");
context.strokeStyle = "#808080";
context.fillStyle = "#000080";
tween(src, dst, 25, 1, 1000);
function tween(src, dst, fps, seconds, delay) {
var frames = fps * seconds;
var srcMaxX = src.maxX;
var srcMinX = src.minX;
var srcMaxY = src.maxY;
var srcMinY = src.minY;
var maxX = dst.maxX - srcMaxX;
var minX = dst.minX - srcMinX;
var maxY = dst.maxY - srcMaxY;
var minY = dst.minY - srcMinY;
drawRectangle(srcMaxX, srcMinX, srcMaxY, srcMinY);
setTimeout(function loop(frame, delay) {
var dstMaxX = srcMaxX + maxX * frame / frames;
var dstMinX = srcMinX + minX * frame / frames;
var dstMaxY = srcMaxY + maxY * frame / frames;
var dstMinY = srcMinY + minY * frame / frames;
drawRectangle(dstMaxX, dstMinX, dstMaxY, dstMinY);
if (frame < frames) setTimeout(loop, delay, frame + 1, delay);
}, delay, 1, 1000 / fps);
}
function drawRectangle(maxX, minX, maxY, minY) {
context.clearRect(0, 0, width, height);
drawLine(0, h, width, h);
drawLine(w, 0, w, height);
var x = w + sx * minX;
var y = h - sy * maxY;
var wth = sx * (maxX - minX);
var hgt = sy * (maxY - minY);
context.fillRect(x, y, wth, hgt);
}
function drawLine(x1, y1, x2, y2) {
context.beginPath();
context.moveTo(x1, y1);
context.lineTo(x2, y2);
context.stroke();
}
canvas {
border: 1px solid #000000;
}
<canvas width="400" height="400"></canvas>
Hope this helps.
With these functions:
function Point(x, y) {
this.x = x;
this.y = y;
}
function Rect(xMin, xMax, yMin, yMax) {
this.xMin = xMin;
this.xMax = xMax;
this.yMin = yMin;
this.yMax = yMax;
}
Rect.prototype.width = function() {
return this.xMax - this.xMin;
};
Rect.prototype.height = function() {
return this.yMax - this.yMin;
};
function RectProjection(a, b) {
var xScale = b.width() / a.width();
var yScale = b.height() / a.height();
var xOffset = b.xMin - xScale * a.xMin;
var yOffset = b.yMin - yScale * a.yMin;
return function (p) {
return Point(xScale * p.x + xOffset, yScale * p.y + yOffset);
};
}
You can then do something like this:
var proj = projection(new Rectangle(0, 2, 0, 3), new Rectangle(2, 6, 2, 8));
var q = proj(new Point(1, 2));
Where q is Point(4, 6).
Related
I have got this function but i am unable to get the path to which the slider when dragged should change the background color of the circle...
$(document).ready(function () {
$("#circle").append('<canvas id="slideColorTracker" class="slideColor"></canvas>');
var canvas = document.getElementById("slideColorTracker");
var context = canvas.getContext('2d');
var $circle = $("#circle"),
$handler = $("#handler"),
$p = $("#test").val(),
handlerW2 = $handler.width() / 2,
radius = ($circle.width()/2)+10,
offset = $circle.offset(),
elementPos = { x: offset.left, y: offset.top },
mHold = 0,
PI = Math.PI / 180
$handler.mousedown(function () {
mHold = 1;
var dim = $("#slideColorTracker");
if ($(".slideColor").length > 1) {
$("#slideColorTracker").remove();
}
});
$(document).mousemove(function (e){
move(e);
}).mouseup(function () { mHold = 0 });
function move(e) {
if (mHold) {
debugger
var deg = 180;
var startAngle = 4.72;
var mousePos = { x: e.pageX - elementPos.x, y: e.pageY - elementPos.y }
var atan = Math.atan2(mousePos.x - radius, mousePos.y - radius);
var deg = -atan / PI + 180;
var percentage = (deg * 100 / 360) | 0;
var endAngle = percentage;
var X = Math.round(radius * Math.sin(deg * PI)),
Y = Math.round(radius * -Math.cos(deg * PI));
var cw = X + radius - handlerW2 - 10; //context.canvas.width /2;
var ch = Y + radius - handlerW2 - 10;
$handler.css({
left: X + radius - handlerW2 - 10,
top: Y + radius - handlerW2 - 10,
})
context.beginPath();
context.arc(($circle.width() / 2), ($circle.height() / 2), radius+20, startAngle, endAngle, false);
context.lineWidth = 15;
context.strokeStyle = 'black';
context.stroke();
$("#test").val(percentage);
$circle.css({ borderColor: "hsl(200,70%," + (percentage * 70 / 100 + 30) + "%)" });
}
}
});
I do not want to use any plugin!
Any help will be much appreciated, since i am new to jquery!
Your $handler does not drag for me unless I change it to this:
$handler.css({
left: mousePos.x,
top: mousePos.y,
position:'absolute'
})
When dragged passed $cirlce.width / 2 it does stroke it correctly.
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....
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>
I've a screen being redraw every 25ms, and images are flickering, here is my code
var FRAME_RATE = 40;
var intervalTime = 1000/FRAME_RATE;
gameLoop();
function gameLoop(){
context.clearRect(0, 0, 640, 640);
renderMap();
window.setTimeout(gameLoop, intervalTime);
}
and here is renderMap() function
function renderMap(){
var startX = playerX - (screenW / 2);
var startY = playerY - (screenH / 2);
maxX = playerX + (screenW / 2);
maxY = playerY + (screenH / 2);
$.getJSON('mapa3.json', function(json){
for (x = startX; x < maxX; x=x+32){
for (y = startY; y < maxY; y=y+32){
intTile = json.layers[0].data[((y/32)* 100) + (x/32)];
context.putImageData(getTile(intTile - 1), x - startX, y - startY);
}
}
});
var imgCharacter = new Image();
imgCharacter.src = 'char.png';
var posX = (screenW - imgCharacter.width) / 2;
var posY = (screenH - imgCharacter.height) / 2;
imgCharacter.onload = function(){context.drawImage(imgCharacter, posX, posY)}
}
What changes do I need to make to the code to stop flickering?
I believe it is because you are loading the image each iteration. Try putting the var imgCharacter..., the following line, and the image's onload function outside of renderMap so it is only ran once
var imgCharacter = new Image();
imgCharacter.onload = function () {
renderMap();
}
imgCharacter.src = 'char.png';
function renderMap() {
var startX = playerX - (screenW / 2);
var startY = playerY - (screenH / 2);
maxX = playerX + (screenW / 2);
maxY = playerY + (screenH / 2);
$.getJSON('mapa3.json', function (json) {
for (x = startX; x < maxX; x = x + 32) {
for (y = startY; y < maxY; y = y + 32) {
intTile = json.layers[0].data[((y / 32) * 100) + (x / 32)];
context.putImageData(getTile(intTile - 1), x - startX, y - startY);
}
}
});
var posX = (screenW - imgCharacter.width) / 2;
var posY = (screenH - imgCharacter.height) / 2;
context.drawImage(imgCharacter, posX, posY)
}
Thank you to markE for letting me know the onload function also needs to go outside renderMap, I overlooked it the first time
Load all images and other data before draw and storage them inside array.
Better use requestAnimationFrame.
Remember, getting JSON (or other data) can take some time.
This might be more a geometry related question, but I'm trying to constrain a controller within an area of a circle. I know I have to touch the Math.sin() and Math.cos() methods, but my attemps so far have been fruitless so far.
Here is the jsfiddle:
So far I've been able to constrain it to an invisible square. http://jsfiddle.net/maGVK/
So I finally was able to complete this with a bit of everyone's help.
var pointerEl = document.getElementById("pointer");
var canvasEl = document.getElementById("canvas");
var canvas = {
width: canvasEl.offsetWidth,
height: canvasEl.offsetHeight,
top: canvasEl.offsetTop,
left: canvasEl.offsetLeft
};
canvas.center = [canvas.left + canvas.width / 2, canvas.top + canvas.height / 2];
canvas.radius = canvas.width / 2;
window.onmousemove = function(e) {
var result = limit(e.x, e.y);
pointer.style.left = result.x + "px";
pointer.style.top = result.y + "px";
}
function limit(x, y) {
var dist = distance([x, y], canvas.center);
if (dist <= canvas.radius) {
return {x: x, y: y};
}
else {
x = x - canvas.center[0];
y = y - canvas.center[1];
var radians = Math.atan2(y, x)
return {
x: Math.cos(radians) * canvas.radius + canvas.center[0],
y: Math.sin(radians) * canvas.radius + canvas.center[1]
}
}
}
function distance(dot1, dot2) {
var x1 = dot1[0],
y1 = dot1[1],
x2 = dot2[0],
y2 = dot2[1];
return Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2));
}
You can see the result here:
http://jsfiddle.net/7Asn6/
var pointerEl = document.getElementById("pointer");
var canvasEl = document.getElementById("canvas");
var canvas = {
width: canvasEl.offsetWidth,
height: canvasEl.offsetHeight,
top: canvasEl.offsetTop,
left: canvasEl.offsetLeft
};
canvas.center = [canvas.left + canvas.width / 2, canvas.top + canvas.height / 2];
canvas.radius = canvas.width / 2;
window.onmousemove = function(e) {
var result = limit(e.x, e.y);
if (!result.limit) {
pointer.style.left = result.x + "px";
pointer.style.top = result.y + "px";
}
}
function limit(x, y) {
var dist = distance([x, y], canvas.center);
if (dist <= canvas.radius) {
return {x: x, y: y};
} else {
return {limit: true};
}
}
function distance(dot1, dot2) {
var x1 = dot1[0],
y1 = dot1[1],
x2 = dot2[0],
y2 = dot2[1];
return Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2));
}
this could do the work, though the movement is not smooth....that will need more geometry knowledge...
fiddle: http://jsfiddle.net/cRxMa/
This arithmetic is trivial as long as you normalize each data point (prospective position), which i have tried to do in the function below:
function locatePoint(canvas_size, next_position) {
// canvas_size & next_position are both 2-element arrays
// (w, h) & (x, y)
dist = function(x, y) {
return Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
};
x = next_position[0];
y = next_position[1];
rescaledX = x/(canvas_size[0]/2);
rescaledY = y/(canvas_size[1]/2);
if (distance(x, y) <= 1) {
// the base case; position is w/in the circle
}
else {
// position is outside the circle, so perhaps
// do something like random select a new position, then
// call this function again (recursively) passing in
// that new position
}
}
so in the simple diagram below, i have just inscribed a unit circle (r=1) inside a square whose sides are r*2. Your canvas dimensions do not have to be square though. To further simplify the calculation, you only need to consider one of the four quadrants--the upper right quadrant, let's say. The reason is that the Euclidean distance formula squares each coordinate value, so negative values become positive.
Put another way, the simplest way is to imagine a circle inscribed in your canvas and whose center is also the center of your canvas (so (0, 0) is the center not the upper left-hand corner); next, both canvas and circle are shrunk until the circle has radius = 1. Hopefully i have captured this in the function above.
Hi and thanks for sharing your solution.
Your jsfiddle helps me a lot to constraint the movement of a rotation handle.
Here's my solution using jQuery :
function getBall(xVal, yVal, dxVal, dyVal, rVal, colorVal) {
var ball = {
x: xVal,
lastX: xVal,
y: yVal,
lastY: yVal,
dx: dxVal,
dy: dyVal,
r: rVal,
color: colorVal,
normX: 0,
normY: 0
};
return ball;
}
var canvas = document.getElementById("myCanvas");
var xLabel = document.getElementById("x");
var yLabel = document.getElementById("y");
var dxLabel = document.getElementById("dx");
var dyLabel = document.getElementById("dy");
var ctx = canvas.getContext("2d");
var containerR = 200;
canvas.width = containerR * 2;
canvas.height = containerR * 2;
canvas.style["border-radius"] = containerR + "px";
var balls = [
getBall(containerR, containerR * 2 - 30, 2, -2, 20, "#0095DD"),
getBall(containerR, containerR * 2 - 50, 3, -3, 30, "#DD9500"),
getBall(containerR, containerR * 2 - 60, -3, 4, 10, "#00DD95"),
getBall(containerR, containerR * 2 / 5, -1.5, 3, 40, "#DD0095")
];
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (var i = 0; i < balls.length; i++) {
var curBall = balls[i];
ctx.beginPath();
ctx.arc(curBall.x, curBall.y, curBall.r, 0, Math.PI * 2);
ctx.fillStyle = curBall.color;
ctx.fill();
ctx.closePath();
curBall.lastX = curBall.x;
curBall.lastY = curBall.y;
curBall.x += curBall.dx;
curBall.y += curBall.dy;
var dx = curBall.x - containerR;
var dy = curBall.y - containerR;
var distanceFromCenter = Math.sqrt(dx * dx + dy * dy);
if (distanceFromCenter >= containerR - curBall.r) {
var normalMagnitude = distanceFromCenter;
var normalX = dx / normalMagnitude;
var normalY = dy / normalMagnitude;
var tangentX = -normalY;
var tangentY = normalX;
var normalSpeed = -(normalX * curBall.dx + normalY * curBall.dy);
var tangentSpeed = tangentX * curBall.dx + tangentY * curBall.dy;
curBall.dx = normalSpeed * normalX + tangentSpeed * tangentX;
curBall.dy = normalSpeed * normalY + tangentSpeed * tangentY;
}
xLabel.innerText = "x: " + curBall.x;
yLabel.innerText = "y: " + curBall.y;
dxLabel.innerText = "dx: " + curBall.dx;
dyLabel.innerText = "dy: " + curBall.dy;
}
requestAnimationFrame(draw);
}
draw();
canvas { background: #eee; }
<div id="x"></div>
<div id="y"></div>
<div id="dx"></div>
<div id="dy"></div>
<canvas id="myCanvas"></canvas>
Hope this help someone.