Calculate if line crosses circle, weird behavior at certain angles - javascript

Okay, I want to be able to calculate whether a line crosses a circle(at least a part of the line inside the circle). I found several answers to this, but I thought they were too complicated so I came up with this. I'm no math guy, so I'm kinda stuck now. When the line is aligned vertically the "radius >= Math.sqrt(len * len + len * len - o);" becomes true( with 45° angles it becomes 0). I have no clue why this happens. Thanks :)
function lineInCircle(sx, sy, x, y, cx, cy, radius) {
cx -= sx; x -= sx; //sx is the first point's x position
cy -= sy; y -= sy;//sy is the first point's y position
len = Math.sqrt((cy * cy) + (cx * cx))//hypotenuse of circle (cy, cx) to (0, 0) (with offset)
atanx = Math.atan(y / x); //angle of (0, 0) to (x, y) in radians
atany = atanx - Math.atan(cy / cx); //to center
var o = 2 * len * len * Math.cos(atany);
var o = o < 0 ? -o:o//Had to do this, at some point the value can become inverted
return radius >= Math.sqrt(len * len + len * len - o);
}
Edit:
function lineInCircle(sx, sy, x, y, cx, cy, radius) {
cx -= sx; x -= sx; //sx is the first point's x position
cy -= sy; y -= sy;//sy is the first point's y position
ctp = Math.sin(Math.atan(y / x) - Math.atan(cy / cx)) * Math.sqrt((cy * cy) + (cx * cx));
return radius >= ctp && ctp >= -radius;
}
Works pretty much the same but is faster. The problem is that it calculates an infinite line. How would I fix that?
Edit 2:
function lineInCircle(sx, sy, x, y, cx, cy, radius) {
cx -= sx; x -= sx;
cy -= sy; y -= sy;
var h = Math.sqrt(cy * cy + cx * cx)
ctp = Math.sin(Math.atan(y / x) - Math.atan(cy / cx)) * h;
sideb = Math.sqrt(h * h - ctp * ctp);
line = Math.sqrt(x * x + y * y)
if (sideb - radius > line) {return false}
return radius >= ctp && ctp >= -radius;
}
Partial fix, doesn't go on to infinity for one direction from the line(line end)
Edit 3:
A bit longer but more than twice as fast, back to square one
function lineInCircle2(sx, sy, x, y, cx, cy, radius) {
var ysy = y - sy
var xsx = x - sx
var k = ((y-sy) * (cx-sx) - (x-sx) * (cy-sy)) / (ysy * ysy + xsx * xsx)
var ncx = cx - k * (y-sy)
var ncy = cy + k * (x-sx)
ncx -= cx
ncy -= cy
var ctp = Math.sqrt(ncx * ncx + ncy * ncy)
return radius >= ctp && ctp >= -radius;
}
Edit 4:
Success!
function lineInCircle(sx, sy, x, y, cx, cy, radius) {
if (sx > cx + radius && x > cx + radius || x < cx - radius && sx < cx - radius) {return false;}
if (sy > cy + radius && y > cy + radius || y < cy - radius && sy < cy - radius) {return false;}
var k = ((y - sy) * (cx - sx) - (x - sx) * (cy - sy)) / ((y - sy) * (y - sy) + (x - sx) * (x - sx))
var ncx = k * (y - sy)
var ncy = k * (x - sx)
return radius >= Math.sqrt(ncx * ncx + ncy * ncy);
}
Does exactly what I want, I optimized it down to 4.5 - 4.6 seconds for 100000000 iterations compared for 10+ secs for the first version and still is much more accurate(meaning no more weird behavior in certain angles). I'm satisfied :D

Too much work. Find the normal that passes through the center, and see if the intersection is closer than the radius.

function lineInCircle(sx, sy, x, y, cx, cy, radius) {
if (sx > cx + radius && x > cx + radius || x < cx - radius && sx < cx - radius) {return false;}
if (sy > cy + radius && y > cy + radius || y < cy - radius && sy < cy - radius) {return false;}
var k = ((y - sy) * (cx - sx) - (x - sx) * (cy - sy)) / ((y - sy) * (y - sy) + (x - sx) * (x - sx))
var ncx = k * (y - sy)
var ncy = k * (x - sx)
return radius >= Math.sqrt(ncx * ncx + ncy * ncy);
}
Takes about 4.5 - 4.6 seconds for 100000000 iterations to finish on my machine.

Related

Is this code for determining if a circle and line SEGMENT intersects correct?

It's apparently very hard to find the answer to whether a line segment and circle intersect. For example, if you google you'll find this question and even the top two answers seem wrong.
The accepted answer has a comment saying: This seems to compute the intersection of a circle with a line, not a segment Scroll down to the next answer and you'll find another comment saying Isn't this answer in incomplete? It finds whether a circle and line intersect, not a line segment.
I've then tried to search for a function for determining if just a segment intersects a circle, but to no avail. The best I could find is a pseudocode explanation here.
I've tried to adapt his code and while it seems to work, it seems overly verbose and I'm not sure if my implementation is correct. I'm asking whether or not this is correct and if it is, is there indeed no better way of determining this? What is the ideal way of determining if a line segment and circle intersects? Please note, I only need to know if they intersect, not where they intersect.
I've provided a full demo reproduction below so you can also visualize it.
function lineSegmentIntersectsCircle(x1, y1, x2, y2, cx, cy, r) {
let deltaX = x2 - x1;
let deltaY = y2 - y1;
let mag = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
let unitX = deltaX / mag;
let unitY = deltaY / mag;
let d = (cx - x1) * unitY - (cy - y1) * unitX;
if (d < -r || d > r) { return false; }
let x1CXDelta = x1 - cx;
let y1CYDelta = y1 - cy;
let x2CXDelta = x2 - cx;
let y2CYDelta = y2 - cy;
let pointOneWithinCircle = x1CXDelta * x1CXDelta + y1CYDelta * y1CYDelta < r * r;
if (pointOneWithinCircle) { return true; }
let pointTwoWithinCircle = x2CXDelta * x2CXDelta + y2CYDelta * y2CYDelta < r * r;
if (pointTwoWithinCircle) { return true; }
let foo = unitX * x1 + unitY * y1;
let bar = unitX * cx + unitY * cy;
let baz = unitX * x2 + unitY * y2;
return (foo < bar && bar < baz) || (baz < bar && bar < foo);
}
let ctx = document.querySelector("canvas").getContext("2d");
function drawCircle(xCenter, yCenter, radius) {
ctx.beginPath();
ctx.arc(xCenter, yCenter, radius, 0, 2 * Math.PI);
ctx.fill();
}
function drawLine(x1, y1, x2, y2) {
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.stroke();
}
let circleX = 340;
let circleY = 250;
let circleR = 60;
let lineX1 = 50;
let lineY1 = 350;
let lineX2 = 285;
let lineY2 = 250;
draw = () => {
ctx.fillStyle = "#b2c7ef";
ctx.fillRect(0, 0, 800, 800);
ctx.fillStyle = "#ffffff";
drawCircle(circleX, circleY, circleR);
drawLine(lineX1, lineY1, lineX2, lineY2);
}
console.log(lineSegmentIntersectsCircle(lineX1, lineY1, lineX2, lineY2, circleX, circleY, circleR))
draw();
canvas { display: flex; margin: 0 auto; }
<canvas width="400" height="400"></canvas>
I think it would be a simpler to (1) compute the line-disk intersection, which is either empty, a point, or a line segment (2) test whether the intersection intersects the line segment.
The points of the line are ((1-t) x1 + t x2, (1-t) y1 + t y2) for all real t. Let x(t) = (1-t) x1 + t x2 - cx and y(t) = (1-t) y1 + t y2 - cy. The line-disk intersection is nonempty if and only if the polynomial x(t)^2 + y(t)^2 - r^2 = 0 has real roots t1 <= t2. In this case, the line-disk intersection is all t in [t1, t2]. The line segment is all t in [0, 1]. The two intersect if and only if t1 <= 1 and t2 >= 0.
Computing t1 and t2 requires a square root, which we can avoid. Let a, b, c be such that x(t)^2 + y(t)^2 - r^2 = a t^2 + b t + c. We have t1 + t2 = -b/a and t1 t2 = c/a.
The roots t1 and t2 are real if and only if b^2 - 4 a c >= 0.
The condition t1 <= 1 is false if and only if t1 - 1 > 0 and t2 - 1 > 0, which in turn is true if and only if (t1 - 1) + (t2 - 1) > 0 and (t1 - 1) (t2 - 1) > 0, which is equivalent to -b/a - 2 > 0 and c/a + b/a + 1 > 0. Since a > 0, this simplifies to -b > 2 a and c + b + a > 0.
The condition t2 >= 0 is false if and only if t1 < 0 and t2 < 0, which in turn is true if and only if t1 + t2 = -b/a < 0 and t1 t2 = c/a > 0. Since a > 0, this simplifies to b > 0 and c > 0.
Implementation in Javascript.
function lineSegmentIntersectsCircleOptimized(x1, y1, x2, y2, cx, cy, r) {
let x_linear = x2 - x1;
let x_constant = x1 - cx;
let y_linear = y2 - y1;
let y_constant = y1 - cy;
let a = x_linear * x_linear + y_linear * y_linear;
let half_b = x_linear * x_constant + y_linear * y_constant;
let c = x_constant * x_constant + y_constant * y_constant - r * r;
return (
half_b * half_b >= a * c &&
(-half_b <= a || c + half_b + half_b + a <= 0) &&
(half_b <= 0 || c <= 0)
);
}
function lineSegmentIntersectsCircle(x1, y1, x2, y2, cx, cy, r) {
let deltaX = x2 - x1;
let deltaY = y2 - y1;
let mag = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
let unitX = deltaX / mag;
let unitY = deltaY / mag;
let d = (cx - x1) * unitY - (cy - y1) * unitX;
if (d < -r || d > r) {
return false;
}
let x1CXDelta = x1 - cx;
let y1CYDelta = y1 - cy;
let x2CXDelta = x2 - cx;
let y2CYDelta = y2 - cy;
let pointOneWithinCircle =
x1CXDelta * x1CXDelta + y1CYDelta * y1CYDelta < r * r;
if (pointOneWithinCircle) {
return true;
}
let pointTwoWithinCircle =
x2CXDelta * x2CXDelta + y2CYDelta * y2CYDelta < r * r;
if (pointTwoWithinCircle) {
return true;
}
let foo = unitX * x1 + unitY * y1;
let bar = unitX * cx + unitY * cy;
let baz = unitX * x2 + unitY * y2;
return (foo < bar && bar < baz) || (baz < bar && bar < foo);
}
function test() {
for (let i = 0; i < 10000000; i++) {
let x1 = Math.random();
let y1 = Math.random();
let x2 = Math.random();
let y2 = Math.random();
let cx = Math.random();
let cy = Math.random();
let r = Math.random();
if (
lineSegmentIntersectsCircle(x1, y1, x2, y2, cx, cy, r) !=
lineSegmentIntersectsCircleOptimized(x1, y1, x2, y2, cx, cy, r)
) {
console.log("bad");
break;
}
}
}
test();

How to draw. arcs in javascript?

How to implement rounded corners in javascript?
Following is the code in C# for drawing rounded corners.
Some geometry with Paint:
0. You have a corner:
![Corner][1]
1. You know the coordinates of corner points, let it be P1, P2 and P:
![Points of corner][2]
2. Now you can get vectors from points and angle between vectors:
![Vectors and angle][3]
angle = atan(PY - P1Y, PX - P1X) - atan(PY - P2Y, PX - P2X)
3. Get the length of segment between angular point and the points of intersection with the circle.
![Segment][4]
segment = PC1 = PC2 = radius / |tan(angle / 2)|
4. Here you need to check the length of segment and the minimal length from PP1 and PP2:
![Minimal length][5]
Length of PP1:
PP1 = sqrt((PX - P1X)2 + (PY - P1Y)2)
Length of PP2:
PP2 = sqrt((PX - P2X)2 + (PY - P2Y)2)
If segment > PP1 or segment > PP2 then you need to decrease the radius:
min = Min(PP1, PP2) (for polygon is better to divide this value by 2)
segment > min ?
segment = min
radius = segment * |tan(angle / 2)|
5. Get the length of PO:
PO = sqrt(radius2 + segment2)
6. Get the C1X and C1Y by the proportion between the coordinates of the vector, length of vector and the length of the segment:
![Coordinates of PC1][6]
Proportion:
(PX - C1X) / (PX - P1X) = PC1 / PP1
So:
C1X = PX - (PX - P1X) * PC1 / PP1
The same for C1Y:
C1Y = PY - (PY - P1Y) * PC1 / PP1
7. Get the C2X and C2Y by the same way:
C2X = PX - (PX - P2X) * PC2 / PP2
C2Y = PY - (PY - P2Y) * PC2 / PP2
8. Now you can use the addition of vectors PC1 and PC2 to find the centre of circle by the same way by proportion:
![Addition of vectors][7]
(PX - OX) / (PX - CX) = PO / PC
(PY - OY) / (PY - CY) = PO / PC
Here:
CX = C1X + C2X - PX
CY = C1Y + C2Y - PY
PC = sqrt((PX - CX)2 + (PY - CY)2)
Let:
dx = PX - CX = PX * 2 - C1X - C2X
dy = PY - CY = PY * 2 - C1Y - C2Y
So:
PC = sqrt(dx2 + dy2)
OX = PX - dx * PO / PC
OY = PY - dy * PO / PC
9. Here you can draw an arc. For this you need to get start angle and end angle of arc:
![Arc][8]
Found it [here][9]:
startAngle = atan((C1Y - OY) / (C1X - OX))
endAngle = atan((C2Y - OY) / (C2X - OX))
10. At last you need to get a sweep angle and make some checks for it:
![Sweep angle][10]
sweepAngle = endAngle - startAngle
If sweepAngle < 0 then swap startAngle and endAngle, and invert sweepAngle:
sweepAngle < 0 ?
sweepAngle = - sweepAngle
startAngle = endAngle
Check if sweepAngle > 180 degrees:
sweepAngle > 180 ?
sweepAngle = 180 - sweepAngle
11. And now you can draw a rounded corner:
![The result][11]
Some geometry with c#:
private void DrawRoundedCorner(Graphics graphics, PointF angularPoint,
PointF p1, PointF p2, float radius)
{
//Vector 1
double dx1 = angularPoint.X - p1.X;
double dy1 = angularPoint.Y - p1.Y;
//Vector 2
double dx2 = angularPoint.X - p2.X;
double dy2 = angularPoint.Y - p2.Y;
//Angle between vector 1 and vector 2 divided by 2
double angle = (Math.Atan2(dy1, dx1) - Math.Atan2(dy2, dx2)) / 2;
// The length of segment between angular point and the
// points of intersection with the circle of a given radius
double tan = Math.Abs(Math.Tan(angle));
double segment = radius / tan;
//Check the segment
double length1 = GetLength(dx1, dy1);
double length2 = GetLength(dx2, dy2);
double length = Math.Min(length1, length2);
if (segment > length)
{
segment = length;
radius = (float)(length * tan);
}
// Points of intersection are calculated by the proportion between
// the coordinates of the vector, length of vector and the length of the segment.
var p1Cross = GetProportionPoint(angularPoint, segment, length1, dx1, dy1);
var p2Cross = GetProportionPoint(angularPoint, segment, length2, dx2, dy2);
// Calculation of the coordinates of the circle
// center by the addition of angular vectors.
double dx = angularPoint.X * 2 - p1Cross.X - p2Cross.X;
double dy = angularPoint.Y * 2 - p1Cross.Y - p2Cross.Y;
double L = GetLength(dx, dy);
double d = GetLength(segment, radius);
var circlePoint = GetProportionPoint(angularPoint, d, L, dx, dy);
//StartAngle and EndAngle of arc
var startAngle = Math.Atan2(p1Cross.Y - circlePoint.Y, p1Cross.X - circlePoint.X);
var endAngle = Math.Atan2(p2Cross.Y - circlePoint.Y, p2Cross.X - circlePoint.X);
//Sweep angle
var sweepAngle = endAngle - startAngle;
//Some additional checks
if (sweepAngle < 0)
{
startAngle = endAngle;
sweepAngle = -sweepAngle;
}
if (sweepAngle > Math.PI)
sweepAngle = Math.PI - sweepAngle;
//Draw result using graphics
var pen = new Pen(Color.Black);
graphics.Clear(Color.White);
graphics.SmoothingMode = SmoothingMode.AntiAlias;
graphics.DrawLine(pen, p1, p1Cross);
graphics.DrawLine(pen, p2, p2Cross);
var left = circlePoint.X - radius;
var top = circlePoint.Y - radius;
var diameter = 2 * radius;
var degreeFactor = 180 / Math.PI;
graphics.DrawArc(pen, left, top, diameter, diameter,
(float)(startAngle * degreeFactor),
(float)(sweepAngle * degreeFactor));
}
private double GetLength(double dx, double dy)
{
return Math.Sqrt(dx * dx + dy * dy);
}
private PointF GetProportionPoint(PointF point, double segment,
double length, double dx, double dy)
{
double factor = segment / length;
return new PointF((float)(point.X - dx * factor),
(float)(point.Y - dy * factor));
}
To get points of arc you can use this:
//One point for each degree. But in some cases it will be necessary
// to use more points. Just change a degreeFactor.
int pointsCount = (int)Math.Abs(sweepAngle * degreeFactor);
int sign = Math.Sign(sweepAngle);
PointF[] points = new PointF[pointsCount];
for (int i = 0; i < pointsCount; ++i)
{
var pointX =
(float)(circlePoint.X
+ Math.Cos(startAngle + sign * (double)i / degreeFactor)
* radius);
var pointY =
(float)(circlePoint.Y
+ Math.Sin(startAngle + sign * (double)i / degreeFactor)
* radius);
points[i] = new PointF(pointX, pointY);
}
I've implemented this is javascript:
let radius = 10;
const angle =
Math.atan(p.x - p1.x, p.x - p1.x) - Math.atan(p.y - p2.y, p.x - p2.x);
let segment = radius / Math.abs(Math.tan(angle / 2));
const pp1 = Math.sqrt(Math.pow(p.x - p1.x, 2) + Math.pow(p.y - p1.y, 2));
const pp2 = Math.sqrt(Math.pow(p.x - p2.x, 2) + Math.pow(p.y - p2.y, 2));
const min = Math.min(pp1, pp2);
if (segment > min) {
segment = min;
radius = segment * Math.abs(Math.tan(angle / 2));
}
const po = Math.sqrt(Math.pow(radius, 2) + Math.pow(segment, 2));
const r = 10;
const c1x = p.x - ((p.x - p1.x) * segment) / pp1;
const c1y = p.y - ((p.y - p1.y) * segment) / pp1;
const c2x = p.x - ((p.x - p2.x) * segment) / pp2;
const c2y = p.y - ((p.y - p2.y) * segment) / pp2;
const dx = p.x * 2 - c1x - c2x;
const dy = p.y * 2 - c1y - c2y;
const pc = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2));
const ox = p.x - (dx * po) / pc;
const oy = p.y - (dy * po) / pc;
let startAngle = Math.atan((c1y - oy) / (c1x - ox));
let endAngle = Math.atan((c2y - oy) / (c2x - ox));
let sweepAngle = endAngle - startAngle;
if (sweepAngle < 0) {
sweepAngle = -sweepAngle;
startAngle = endAngle;
}
if (sweepAngle > 180) sweepAngle = 180 - sweepAngle;
But the issue is that the rounded corners are not getting drawn as expected!
The drawing of arc in c# is different from html canvas arc.
So how can I use the above data to draw it in html5 canvas?
To draw arcs in the html5 canvas, you can use ctx.arc(xPos, yPos, radius, startAngle, endAngle, (optional boolean, defaults to false) counterClockwise);
Documentation can be found at W3Schools.com
More general html5 canvas documentation can also be found at W3Schools.com

3d sphere using javascript

I have been trying to learn creating basic 3d sphere using html5 canvas and I am new to 3d programming. When I googled, I found this incredibly useful article.You can find working example there.
function drawEllipse(ox, oy, oz, or, alpha, beta, gamma, hidden) {
for (var theta=0; theta <=2 * Math.PI; theta += 0.02) {
var px = ox + or * Math.cos(theta);
var py = oy;
var pz = oz + or * Math.sin(theta);
var a = transform(px, py, pz, alpha, beta, gamma);
if (!hidden || a[2] >= 0) {
drawDot(radius * a[0], radius * a[1]);
}
}
}
function drawDot(x, y) {
ctx.fillRect(x, y, 1, 1);
}
/* 3d rotation matrix */
function transform(x, y, z, rz, rx, ry) {
return [
((x * Math.sin(rz) - y * Math.cos(rz)) * Math.sin(rx) + z * Math.cos(rx)) * Math.sin(ry) + (x * Math.cos(rz) - y * Math.sin(rz)) * Math.cos(ry),
(x * Math.sin(rz) - y * Math.cos(rz)) * Math.cos(rx) - z * Math.sin(rx),
((x * Math.sin(rz) - y * Math.cos(rz)) * Math.sin(rx) + z * Math.cos(rx)) * Math.cos(ry) - (x * Math.cos(rz) - y * Math.sin(rz)) * Math.sin(ry)
]
}
/*latitude circles*/
for (var latitude = 0; latitude < Math.PI / 2; latitude += Math.PI / 18) {
drawEllipse(0, Math.sin(latitude), 0,Math.cos(latitude),0, alpha, gamma,true);
drawEllipse(0, -Math.sin(latitude), 0,Math.cos(latitude),0, alpha, gamma,true);
}
/*longitude circles*/
for (var longitude=0 ; longitude < Math.PI; longitude += Math.PI / 18) {
drawEllipse(0, 0, 0,1,longitude, Math.PI / 2 + alpha, gamma,true);
} // alpha and gamma are the x and y axis rotation angles
But the only problem is the direction of rotation when dragging the mouse on the sphere. Yaw and Pitch direction is not correct. Rotating left to right(yaw) working fine. But rotating top to bottom(pitch) is different. Can anyone please find a solution ?

How would you calculate the velocities for particles bouncing off eachother?

I have made this piece of code using canvas to portray some particles that fight over their designated color. Currently I have made the particle "put" their colors on the other particles, if they collide and I've also made them "suck" the other particles' mass, if within a certain radius. I want to take it to the next level by also, when a ball have hit another ball, make them bounce away from each other, by somehow negating the velocity in a way that has something to do with the relative angle to the other particle. How would one implement this? I can't quite wrap my head around this, although I study science in high school.
Codepen: http://codepen.io/dremp/pen/fzxvK?editors=001
This is the snippet that detects if particles have hit eachother.
function update() {
paintCanvas();
for(var i = 0; i < Particles.length; i++) {
p = Particles[i];
p.x += p.vx;
p.y += p.vy;
ctx.beginPath();
ctx.arc(p.x, p.y, p.radius, 0 * Math.PI, 2 * Math.PI, false);
var drawMethod = (fillParticles === true) ? 'fill':'stroke';
ctx[drawMethod]();
ctx[drawMethod+"Style"] = p.color;
if (p.x + p.radius > W || p.x - p.radius < 0) p.vx = p.vx * - 1 + 0.05;
if (p.y + p.radius > H || p.y - p.radius < 0) p.vy = p.vy * - 1 + 0.05;
if (p.x + p.radius > W) p.vx -= 0.05;
else if (p.x - p.radius < 0) p.vx += 0.05;
if (p.y + p.radius > H) p.vy -= 0.05;
else if (p.y - p.radius < 0) p.vy += 0.05;
for(var j = i + 1; j < Particles.length; j++) {
p2 = Particles[j];
var disance,
distanceX = p.x - p2.x,
distanceY = p.y - p2.y,
color;
distance = Math.sqrt(distanceX * distanceX + distanceY * distanceY);
if(distance <= p.radius + p2.radius) {
p2.color = p.color;
color = p.color;
var ax = distanceX / 1000,
ay = distanceY / 1000;
p.x += ax;
p.y += ay;
}
else if(distance <= p.radius * 2 + 50) {
ctx.lineWidth = 2;
ctx.strokeStyle = p.color;
ctx.beginPath();
ctx.moveTo(p.x,p.y);
ctx.lineTo(p2.x,p2.y);
ctx.stroke();
ctx.beginPath();
p.radius += 0.005;
p2.radius -= 0.005;
if(p.radius > 50) p.radius -= 0.01;
if(p2.radius < 5) p2.radius += 0.01;
}
}
}
requestAnimationFrame(update);
}
You need to calculate impulse:
// Find normal from other to me
normX = p.x - p2.x;
normY = p.y - p2.y;
// make into unit vector
normLength = Math.sqrt( normX * normX + normY * normY);
normX = normX / normLength;
normY = normY / normLength;
// Get projection of movement vectors onto normal
// (Dot prod each with norm)
myProj = (p.vx * normX) + (p.vy * normY);
otherProj = (p2.vx * normX) + (p2.vy * normY);
// Now, factor in impulse, derived from
// Conservation of Energy / Conservation of Momentum
impulse = ( 2 * (myProj - otherProj) );
p.mass = 1; // Replace with "mass" calculation (based on area?)
p2.mass = 1;
impulse = impulse / (p.mass + p2.mass);
p.vx = p.vx - (impulse * p2.mass * normX);
p.vy = p.vy - (impulse * p2.mass * normY);
p2.vx = p2.vx + (impulse * p.mass * normX);
p2.vy = p2.vy + (impulse * p.mass * normY);
Demo: http://codepen.io/gengel/pen/pFHGa
If you have different balls of different sizes flying around, you should change those p.mass lines to reflect that (area would probably work - (pi * r)^2 )
(edited to replace "P" with "impulse" since P was taken)

Collision detection between a line and a circle in JavaScript

I'm looking for a definitive answer, maybe a function cos I'm slow, that will determine if a line segment and circle have collided, in javascript (working with canvas)
A function like the one below that simply returns true if collided or false if not would be awesome. I might even donate a baby to you.
function isCollided(lineP1x, lineP1y, lineP2x, lineP2y, circlex, circley, radius) {
...
}
I've found plenty of formulas, like this one, but they are over my head.
Here you will need some Math:
This is the basic concept if you don't know how to solve equations in general. I will leave the rest of the thinking to you. ;) Figuring out CD's length isn't that hard.
If you are asking how, that's how:
Finding collisions in JavaScript is kind of complicated.
I Spent about a day and a half to make it perfect.. Hope this helps.
function collisionCircleLine(circle,line){ // Both are objects
var side1 = Math.sqrt(Math.pow(circle.x - line.p1.x,2) + Math.pow(circle.y - line.p1.y,2)); // Thats the pythagoras theoram If I can spell it right
var side2 = Math.sqrt(Math.pow(circle.x - line.p2.x,2) + Math.pow(circle.y - line.p2.y,2));
var base = Math.sqrt(Math.pow(line.p2.x - line.p1.x,2) + Math.pow(line.p2.y - line.p1.y,2));
if(circle.radius > side1 || circle.radius > side2)
return true;
var angle1 = Math.atan2( line.p2.x - line.p1.x, line.p2.y - line.p1.y ) - Math.atan2( circle.x - line.p1.x, circle.y - line.p1.y ); // Some complicated Math
var angle2 = Math.atan2( line.p1.x - line.p2.x, line.p1.y - line.p2.y ) - Math.atan2( circle.x - line.p2.x, circle.y - line.p2.y ); // Some complicated Math again
if(angle1 > Math.PI / 2 || angle2 > Math.PI / 2) // Making sure if any angle is an obtuse one and Math.PI / 2 = 90 deg
return false;
// Now if none are true then
var semiperimeter = (side1 + side2 + base) / 2;
var areaOfTriangle = Math.sqrt( semiperimeter * (semiperimeter - side1) * (semiperimeter - side2) * (semiperimeter - base) ); // Heron's formula for the area
var height = 2*areaOfTriangle/base;
if( height < circle.radius )
return true;
else
return false;
}
And that is how you do it..
Matt DesLauriers published a Javascript library for this problem at https://www.npmjs.com/package/line-circle-collision. The API is straightforward:
var circle = [5, 5],
radius = 25,
a = [5, 6],
b = [10, 10]
var hit = collide(a, b, circle, radius)
function pointCircleCollide(point, circle, r) {
if (r===0) return false
var dx = circle[0] - point[0]
var dy = circle[1] - point[1]
return dx * dx + dy * dy <= r * r
}
var tmp = [0, 0]
function lineCircleCollide(a, b, circle, radius, nearest) {
//check to see if start or end points lie within circle
if (pointCircleCollide(a, circle, radius)) {
if (nearest) {
nearest[0] = a[0]
nearest[1] = a[1]
}
return true
} if (pointCircleCollide(b, circle, radius)) {
if (nearest) {
nearest[0] = b[0]
nearest[1] = b[1]
}
return true
}
var x1 = a[0],
y1 = a[1],
x2 = b[0],
y2 = b[1],
cx = circle[0],
cy = circle[1]
//vector d
var dx = x2 - x1
var dy = y2 - y1
//vector lc
var lcx = cx - x1
var lcy = cy - y1
//project lc onto d, resulting in vector p
var dLen2 = dx * dx + dy * dy //len2 of d
var px = dx
var py = dy
if (dLen2 > 0) {
var dp = (lcx * dx + lcy * dy) / dLen2
px *= dp
py *= dp
}
if (!nearest)
nearest = tmp
nearest[0] = x1 + px
nearest[1] = y1 + py
//len2 of p
var pLen2 = px * px + py * py
//check collision
return pointCircleCollide(nearest, circle, radius)
&& pLen2 <= dLen2 && (px * dx + py * dy) >= 0
}
var circle = [5, 5],
radius = 25,
a = [5, 6],
b = [10, 10]
var hit = lineCircleCollide(a, b, circle, radius)

Categories

Resources