What I am aiming to do is arc the position of a circle towards the position of the mouse cursor, this all being relative to the world viewed through the canvas. To keep a handle on the speed at which the circle moves I decided to make a boundary larger than the circle, if the mouse is outside the boundary then the "position" of the mouse is brought to the boundary so that when I arc towards the coords, if they arent super far from the position of the circle it doesnt move at crazy speeds. I have this working and this is the code which does it:
dx = Game.controls.mouseX - (this.x - xView); // get the distance between the x coords
dy = Game.controls.mouseY - (this.y - yView); // get the distance between the y coords
radii = this.radius + 1; // +1 because the "radius" of the mouse is 1
if((dx * dx) + (dy * dy) > radii * radii) // is the mouse not over the player?
{
if((dx * dx) + (dy * dy) < 301 * 301)
{
this.x += ((Game.controls.mouseX - (this.x - xView)) * 2 / (this.mass)) + step;
this.y += ((Game.controls.mouseY - (this.y - yView)) * 2 / (this.mass)) + step;
}
else
{
mx = Game.controls.mouseX;
my = Game.controls.mouseY;
do
{
dx = mx - (this.x - xView);
dy = my - (this.y - yView);
mx += (((this.x - xView) - mx) * 2 / (this.mass)) + step;
my += (((this.y - yView) - my) * 2 / (this.mass)) + step;
} while((dx * dx) + (dy * dy) > 301 * 301)
this.x += ((mx - (this.x - xView)) * 2 / (this.mass)) + step;
this.y += ((my - (this.y - yView)) * 2 / (this.mass)) + step;
}
}
The magic for 'outside the boundary' lies withing the do while. This is the best fix I could come up with and I cant see this as being an elegant or fast solution and am wondering what the proper course of action should be.
Im no artist but hopefully this image helps to illustrate what I am trying to achieve. The Black dot is the mouse pos, the black circle is the circle and the red circle is the boundary I have specified. I want to get the coords marked by the X.
Your question is a special case of Circle line-segment collision detection algorithm?, in this case with B and C being the same points, so you can use your center point for both of them.
That solution is given in C, but it translates to JavaScript very easily, just replace float with var, use Math.sqrt() and so on...
Oh, and then there is a JvaScript version here: Calculate the point of intersection of circle and line through the center, that's more appropriate :-)
If the black circle is in the center of the red circle and you have the radius of the red circle
// c is circle center
// mouse is the mouse position. Should have properties x,y
// radius is the circle radius;
// returns the point on the line where the circle intercepts it else it returns undefined.
function findX(c, mouse, radius)
var v = {};
// get the vector to the mouse
v.x = mouse.x - c.x;
v.y = mouse.y - c.y;
var scale = radius / Math.hypot(v.x,v.y);
if(scale < 1){ // is it outside the circle
return {
x : c.x + v.x * scale,
y : c.y + v.y * scale
};
}
return;
}
And if the the line start is not the center then a general purpose line circle intercept function will solve the problem. If the line starts inside the circle the function will return just one point. If the line is not long enough it will return an empty array..
// p1,p2 are the start and end points of a line
// returns an array empty if no points found or one or two points depending on the number of intercepts found
// If two points found the first point in the array is the point closest to the line start (p1)
function circleLineIntercept(circle,radius,p1,p2){
var v1 = {};
var v2 = {};
var ret = [];
var u1,u2,b,c,d;
// line as vector
v1.x = p2.x - p1.x;
v1.y = p2.y - p1.y;
// vector to circle center
v2.x = p1.x - circle.x;
v2.y = p1.y - circle.y;
// dot of line and circle
b = (v1.x * v2.x + v1.y * v2.y) * -2;
// length of line squared * 2
c = 2 * (v1.x * v1.x + v1.y * v1.y);
// some math to solve the two triangles made by the intercept points, the circle center and the perpendicular line to the line.
d = Math.sqrt(b * b - 2 * c * (v2.x * v2.x + v2.y * v2.y - radius * radius));
// will give a NaN if no solution
if(isNaN(d)){ // no intercept
return ret;
}
// get the unit distance of each intercept to the line
u1 = (b - d) / c;
u2 = (b + d) / c;
// check the intercept is on the line segment
if(u1 <= 1 && u1 >= 0){
ret.push({x:line.p1.x + v1.x * u1, y : line.p1.y + v1.y * u1 });
}
// check the intercept is on the line segment
if(u2 <= 1 && u2 >= 0){
ret.push({x:line.p1.x + v1.x * u2, y : line.p1.y + v1.y * u2});
}
return ret;
}
Related
I know this has been asked a lot, but personally, i couldn't find a suitable answer. Pretty much everywhere, the function looks something like:
function rccol(rect, circle){
var dx = Math.abs(circle.x - (rect.x + rect.width / 2));
var dy = Math.abs(circle.y - (rect.y + rect.height / 2));
if(dx > circle.radius + rect.width / 2) return false;
if(dy > circle.radius + rect.height / 2) return false;
if(dx <= rect.width) return true;
if(dy <= rect.height) return true;
var dx = dx - rect.width;
var dy = dy - rect.height
return((dx * dx + dy * dy) <= (circle.radius * circle.radius));
}
And that works perfectly. The issue is that I'm using it for collision detection in a game, where the circle is the player's collision box, and the square is let's say a wall. I need to be able to determine where the contact occurred so that i can actually prevent the "player" from going into the "wall" so the simple boolean that gets returned, doesn't really work in my use-case.
Having circle center coordinates (cx, cy), you can calculate squared distance from circle center to rectangle.
dx = Max(Abs(cx - rect.center.x) - rect.width / 2, 0)
dy = Max(Abs(cy - rect.center.y) - rect.height / 2, 0)
SquaredDistance = dx * dx + dy * dy
When circle is outside of rectangle initially, it is possible to remove Max call.
dx = Abs(cx - rect.center.x) - rect.width / 2
dy = Abs(cy - rect.center.y) - rect.height / 2
Also we can remove Abs for known initial position (note - upto moment of sign change!)
if cx >= rect.center.x:
dx = cx - rect.center.x - rect.width / 2
else:
dx = - cx + rect.center.x - rect.width / 2
To avoid a lot od different cases, we can virtually put player in the first quadrant relative to rectangle center and correspondingly change coordinates and velocity components
if cx0 < 0:
cx0 = - cx0
vx = -vx
if cy0 < 0:
cy0 = - cy0
vy = -vy
To register collision moment, you have to substitute coordinates with parametric equations using start point and velocity vector components (vx, vy)
cx = cx0 + vx * t
cy = cy0 + vy * t
and solve (quadratic) equation for t
SquaredDistance - Radius^2 = 0
(cx0 + vx * t - rect.center.x - rect.width/2)^2 +
(cy0 + vy * t - rect.center.y - rect.height/2)^2 -
Radius^2 = 0
Ater that you can get zero (no collision), one (touch event) or two roots (moment of collision and moment of exit).
The above expression was taken from the below method.
I know that to rotate a point around the center we have to
Move the point to the origin
Make the rotation and
Move the point back
But the pieces I don't get my head around are:
r[0] = x * Math.cos(angle) - y * Math.sin(angle);
^
|
why we use the minus sign here?
r[1] = x * Math.sin(angle) + y * Math.cos(angle);
^
|
And why here we use plus sign instead of minus?
Vec2.prototype.rotate = function (center, angle) {
//rotate in counterclockwise
var r = [];
var x = this.x - center.x;
var y = this.y - center.y;
r[0] = x * Math.cos(angle) - y * Math.sin(angle);
r[1] = x * Math.sin(angle) + y * Math.cos(angle);
r[0] += center.x;
r[1] += center.y;
return new Vec2(r[0], r[1]);
};
The book was to be great but it doesn't explain most of the code it simply spits out.
I got it! Just saw a video of Dr Peyam on transformation matrix.
To get x' and y' we multiply the transformation matrix by the current coordinate (x,y)
enter image description here
I am trying to get a function to calculate if a line, that is defined by 2 coordinates, intersects a circle, defined by a coordinate and a radius. I previously used these 2 functions
getLineEquation(point1, point2) {
var lineObj = {
gradient: (point1.latitude - point2.latitude) / (point1.longitude - point2.longitude)
};
lineObj.yIntercept = point1.latitude - lineObj.gradient * point1.longitude;
return lineObj;
}
checkIntercept(y, m, circle) {
// y: y intercept of line
// m: gradient of line
// get a,b,c values
var a = 1 + (m * m);
var b = -circle.longitude * 2 + (m * (y - circle.latitude)) * 2;
var c = (circle.longitude * circle.longitude) + ((y - circle.latitude)* (y - circle.latitude)) - (circle.range * circle.range);
// get discriminant
var d = (b * b) - 4 * a * c;
if (d >= 0) {
// insert into quadratic formula
var intersections = [
(-b + Math.sqrt((b * b) - 4 * a * c)) / (2 * a),
(-b - Math.sqrt((b * b) - 4 * a * c)) / (2 * a)
];
if (d == 0) {
// only 1 intersection
return [intersections[0]];
}
return intersections;
}
// no intersection
return false;
}
but this didn't work as it converted the 2 points into an infinite line, which I don't want as it would return false readings for circles that aren't actually between the 2 points.
How could I fix these functions to make the calculation into a finite line?
Here is a possible approach.
If one of the two vertices is inside the circle, and the other is outside, then there will be an intersection. (This means two distance calculations.)
If both vertices lie inside the circle, there is no intersection.
If the first two steps weren't decisive, find s, the projection of the circle center to the line (p,q).
If the distance of s to the circle center is larger than the radius, there is no intersection.
Otherwise, if s is between a and b (so s_x between p_x and q_x, and also s_y between p_y and q_y, to include the case of horizontal and vertical edges), then there is an intersection, otherwise not.
To project a point on a line, you basically make a line equation in the form (px,py) + t * (dx,dy) with dx = qx - px and dy = qy - py for two points (px,py) and (qx,qy). The perpendicular line through a point (cx,cy) is then (cx,cy) + t * (-dy,dx). Setting both equations equal finds the intersection.
Or, if you prefer straight formulas:
sx = (d*px + (px - qx)*((cx - px)*(px - qx) + (cy - py)*(py - qy)))/d
sy = (d*py + (py - qy)*((cx - px)*(px - qx) + (cy - py)*(py - qy)))/d
with
d = (px - qx)^2 + (py - qy)^2
I'm doing a graph theory project, and I need to show the edge weight above each of the edges.
currently I'm using this method:
var x1; //starting point
var x2; //ending point
function setup() {
createCanvas(640, 480);
x1 = createVector(random(0, width/2), random(0, height/2)); //random position to the upper left
x2 = createVector(random(width/2, width), random(height/2, height)); //random position to the lower right
}
function draw() {
background(200);
stroke(0);
line(x1.x, x1.y, x2.x, x2.y); //draw a line beetween the points
d = dist(x1.x, x1.y, x2.x, x2.y);
var angle = atan2(x1.y - x2.y, x1.x - x2.x); // gets the angle of the line
textAlign(CENTER);
text("X1", x1.x + 5, x1.y + 5); // just to show where is the begining
text("X2", x2.x - 5, x2.y - 5); // just to show there is the end
fill(0);
signalx = x1.x > x2.x ? -1 : 1; // if the start is to the right of the end
signaly = x1.y > x2.y ? -1 : 1; // if the start is below the end
// I think i need to use the angle here
text(42, (x1.x + (d / 2) * signalx), (x1.y + (d / 2) * signaly));
}
the problem is that the result, well, is not as expected:
The idea is that the text I'm showing (42, the edge weight) is a little bit above the middle of the line, what is currently not happening.
I know that I have to take the angle of the line into consideration, but not sure where.
Thanks for any help, and if there's any need of more information let me know.
What you want to do is use linear interpolation. First, find the equation of the line in slope-intercept form, so you can solve for y (when you know x). (I'm just going to rename x1 to p1 and x2 to p2 for clarity.)
(math)
// with points (x1, y1) and (x2, y2)
y - y1 = m*(x - x1) // point-slope form (just a step)
y - y1 = m*x - m*x1
y = m*x - m*x1 + y1 // slope-intercept
Then, since x is the midpoint of the line, x equals the average of the two endpoints. And then calculate y, based on the above equation:
(code)
float m = (p2.y - p1.y) / (p2.x - p1.x);
int x = (x2 + x1) / 2;
int y = m*x - m*p1.x + p1.y;
So I have an object rotating around an origin point. Once I rotate and then change the origin point. My object seems to jump positions. After the jump it rotates fine... Need help finding the pattern/why it's jumping and what I need to do to stop it.
Here's the rotation code:
adjustMapTransform = function (_x, _y) {
var x = _x + (map.width/2);
var y = _y + (map.height/2);
//apply scale here
var originPoint = {
x:originXInt,
y:originYInt
};
var mapOrigin = {
x:map.x + (map.width/2),
y:map.y + (map.height/2)
};
//at scale 1
var difference = {
x:mapOrigin.x - originPoint.x,
y:mapOrigin.y - originPoint.y
};
x += (difference.x * scale) - difference.x;
y += (difference.y * scale) - difference.y;
var viewportMapCentre = {
x: originXInt,
y: originYInt
}
var rotatedPoint = {};
var angle = (rotation) * Math.PI / 180.0;
var s = Math.sin(angle);
var c = Math.cos(angle);
// translate point back to origin:
x -= viewportMapCentre.x;
y -= viewportMapCentre.y;
// rotate point
var xnew = x * c - y * s;
var ynew = x * s + y * c;
// translate point back:
x = xnew + viewportMapCentre.x - (map.width/2);
y = ynew + viewportMapCentre.y - (map.height/2);
var coords = {
x:x,
y:y
};
return coords;
}
Also here is a JS Fiddle project that you can play around in to give you a better idea of what's happening.
EDITED LINK - Got rid of the originY bug and scaling bug
https://jsfiddle.net/fionoble/6k8sfkdL/13/
Thanks!
The direction of rotation is a consequence of the sign you pick for the elements in your rotation matrix. [This is Rodrigues formula for rotation in two dimensions]. So to rotate in the opposite direction simply subtract your y cosine term rather than your y sine term.
Also you might try looking at different potential representations of your data.
If you use the symmetric representation of the line between your points you can avoid shifting and instead simply transform your coordinates.
Take your origin [with respect to your rotation], c_0, to be the constant offset in the symmetric form.
You have for a point p relative to c_0:
var A = (p.x - c_0.x);
var B = (p.y - c_0.y);
//This is the symmetric form.
(p.x - c_0.x)/A = (p.y - c_0.y)/B
which will be true under a change of coordinates and for any point on the line (which also takes care of scaling/dilation).
Then after the change of coordinates for rotation you have [noting that this rotation has the opposite sense, not the same as yours].
//This is the symmetric form of the line incident on your rotated point
//and on the center of its rotation
((p.x - c_0.x) * c + (p.y - c_0.y) * s)/A = ((p.x - c_0.x) * s - (p.y - c_0.y) * c)/B
so, multiplying out we get
(pn.x - c_0.x) * B * c + (pn.y - c_0.y) * B * s = (pn.x - c_0.x) * A * s - (pn.y - c_0.y) * A * c
rearrangement gives
(pn.x - c_0.x) * (B * c - A * s) = - (pn.y - c_0.y) * (B * s + A * c)
pn.y = -(pn.x - c_0.x) * (B * c - A * s) / (B * s + A * c) + c_0.y;
for any scaling.