Finding Circle-Line collision (as points, not bools) C# - c#

So unlike everyone here, I'm trying to find the intersections from circle-line collision.
The points are taken from the user input.
float cx; //circle center x
float cy; //circle center y
float px; //point x
float py; //point y
float vx; //vector x
float vy; //vector y
float vySq = vy * vy;
float vxSq = vx * vx;
float cxSq = cx * cx;
float rSq = r * r;
float thatPart = ( ( (vy * px) / vx) + py - cy); //so I don't have to re-type it 3 different times
float a = 1 + (vySq/vxSq);
float b = (2 * cx) + ( (2 * vy) * thatPart / vx);
float c = cxSq + ( thatPart * thatPart ) - rSq;
float x1 = QUADRATIC(a, b, c, true);
float x2 = QUADRATIC(a, b, c, false);
float y1 = ((vy * x1) - (vy * px) + (vx * py)) / vx;
float y2 = ((vy * x2) - (vy * px) + (vx * py)) / vx;
My QUADRATIC function 100% works, so I am not worried about that.
It's a, b, and c. I receive the wrong values for them. And all this math is based on a pdf given in math class. (here)
So... what am I doing wrong?
Thanks.

The equations in both the PDF and the source code seem unnecessarily complicated, so I will re-establish them in a clenear way (or at least one that pleases me).
The vector from the center of the circle to any point S on the line can be written as
CS = CP + t V
where t is an arbitrary parameter.
S belongs to the circle when
CS² = R² = (CP + t V)²
giving the quadratic equation in t:
V² t² + 2 CP.V t + CP² - R² = 0
When you have the values of t, plug in the first equation.

Math Treatment
You have a line with coordinates (x,y) = (px + vx*t, py + vy*t) where t is an arbitrary parameter.
The line intersects the circle when its coordinates solve x^2 + y^2 = r^2.
This leads to the following quadratic equation, to be solved for t.
t^2 + 2*t*b + a = 0
a = (px^2+py^2-r^2)/(vx^2+vy^2)
b = (px*vx+py*vy)/(vx^2+vy^2)
The two solutions are:
t = -b ± sqrt(b^2-a)
Use the two t values into (x,y) to get the coordinates.
Sample Code
static class Program
{
static void Main(string[] args)
{
var ray = new Ray() {
Point = new Vector2(-3, 1),
Direction = new Vector2(2, 0.5f)
};
var circle = new Circle
{
Center = new Vector2(1, 1),
Radius = 4
};
var points = Geometry.Intersect(circle, ray);
if(points.Length>0)
{
foreach(var point in points)
{
Console.WriteLine(point.ToString());
}
}
else
{
Console.WriteLine("Circle and Ray do not intersect");
}
}
}
Object Oriented Models
public class Circle
{
public Vector2 Center { get; set; }
public float Radius { get; set; }
}
public class Ray
{
public Vector2 Point { get; set; }
public Vector2 Direction { get; set; }
public Vector2 Along(float t)
{
return Point + t*Direction;
}
}
public static class Geometry
{
public static Vector2[] Intersect(Circle circle, Ray ray)
{
float a = (ray.Point.LengthSquared()-circle.Radius*circle.Radius)/ray.Direction.LengthSquared();
float b = Vector2.Dot(ray.Point, ray.Direction)/ray.Direction.LengthSquared();
if(b*b-a>0)
{
// two intersection points
float t1 = -b-(float)Math.Sqrt(b*b-a);
float t2 = -b+(float)Math.Sqrt(b*b-a);
return new Vector2[] {
ray.Along(t1),
ray.Along(t2),
};
}
else if(b*b-a==0)
{
// one intersection point
float t = -b;
return new Vector2[] { ray.Along(t) };
}
else
{
// no intersection, return empty array
return new Vector2[0];
}
}
}
NOTE: Code uses System.Numerics library

Related

How can I fix this imperfect circle I made using LineRenderer?

So I made this shape which I applied to a sprite via this script:
using UnityEngine;
using System.Collections;
public class CircleShapeGenerator : MonoBehaviour
{
public int segments = 100;
public float radius = 1;
public Color c1 = new Color( 1, 1, 1, 0.1f );
public Color c2 = new Color( 1, 1, 1, 0.1f );
LineRenderer line;
void Start ()
{
line = gameObject.AddComponent<LineRenderer>();
line.material = new Material(Shader.Find("Particles/Additive"));
line.SetWidth(0.05F, 0.05F);
line.SetVertexCount (segments + 1);
line.useWorldSpace = false;
}
void Update()
{
line.SetColors(c1, c2);
float angle = 20f;
for (int i = 0; i < (segments + 1); i++)
{
float x = Mathf.Sin (Mathf.Deg2Rad * angle) * radius;
float y = Mathf.Cos (Mathf.Deg2Rad * angle) * radius;
line.SetPosition( i, new Vector3( x,y,0) );
angle += (360f / segments);
}
}
}
As you can see in the screenshot, the start and end do not connect as they should. How can I fix this? I found this snippet of code on the entire internet but all give this result. Can somebody fix this or provide, perhaps, a spline solution? I think its overkill to go to a Shader solution (0 experience with shaders).
This solution could be a little bit complicated. But it'll works.
The idea is
1) Draw first segment as small segmented area.
2) Draw from seconds to last -1 segments as big segment.
3) Draw last segment as small segmented area too.
It makes seamless edge between the start segment and the end segment.
And total segment's count is not too many.
total segment = segment + 2 * subsegment
This is sample code.
using UnityEngine;
using System.Collections;
public class CircleShapeGenerator : MonoBehaviour {
public int segments = 100;
public int edgeSegments = 10;
public float radius = 1f;
int vertCount;
float increAngle, increAngleEdge;
public Color c1 = new Color( 1, 1, 1, 1f );
public Color c2 = new Color( 1, 1, 1, 1f );
LineRenderer line;
void Start ()
{
vertCount = segments + 2*edgeSegments - 2 + 1;
increAngle = 360f / segments;
increAngleEdge = increAngle/edgeSegments;
line = gameObject.AddComponent<LineRenderer>();
line.material = new Material(Shader.Find("Particles/Additive"));
line.SetWidth(0.05F, 0.05F);
line.SetVertexCount (vertCount);
line.useWorldSpace = false;
}
void Update()
{
line.SetColors(c1, c2);
//draw first segment
float angle = 0;
for (int i = 0; i < edgeSegments; i++)
{
float x = Mathf.Sin (Mathf.Deg2Rad * angle) * radius;
float y = Mathf.Cos (Mathf.Deg2Rad * angle) * radius;
line.SetPosition( i, new Vector3(x, y, 0) );
angle += increAngleEdge;
}
//draw from seconds to last-1 segment
angle -= increAngleEdge;
for (int i = 0; i < segments-2; i++)
{
angle += increAngle;
float x = Mathf.Sin (Mathf.Deg2Rad * angle) * radius;
float y = Mathf.Cos (Mathf.Deg2Rad * angle) * radius;
line.SetPosition( edgeSegments + i, new Vector3(x, y, 0) );
}
//draw last segment
for (int i = 0; i < edgeSegments+1; i++)
{
angle += increAngleEdge;
float x = Mathf.Sin (Mathf.Deg2Rad * angle) * radius;
float y = Mathf.Cos (Mathf.Deg2Rad * angle) * radius;
line.SetPosition( edgeSegments + segments - 2 + i, new Vector3(x, y, 0) );
}
}
}

Stuck with collision detection in circle pong in xna

I am making clone of Circle pong game in xna. I am not able to get collision of rotating pad with ball. I can't use farseer physics in this game as it is in mono game.
I found one method for it but it also not seem to work.
private void EllasticCollisionPhysics(Ball ball, GoScreen pad)
{
//find normal vector
Vector2 normal = new Vector2(pad.padV.X - ball.ballRectangle.X, pad.padV.Y - ball.ballRectangle.Y);
//find normal vector's modulus, i.e. length
float normalmod = (float)Math.Sqrt(Math.Pow(normal.X, 2) + Math.Pow(normal.Y, 2));
//find unitnormal vector
Vector2 unitnormal = new Vector2((pad.padV.X - ball.ballRectangle.X) / normalmod, (pad.padV.Y - ball.ballRectangle.Y) / normalmod);
//find tangent vector
Vector2 unittan = new Vector2(-1 * unitnormal.Y, unitnormal.X);
//first ball normal speed before collision
float inormalspeedb = unitnormal.X * velocity.X + unitnormal.Y * velocity.Y;
//first ball tangential speed
float itanspeed = unittan.X * velocity.X + unittan.Y * velocity.Y;
//Calculate normal speeds after the collision
//Calculate first ball Velocity vector components (tangential and normal)
Vector2 inormala = new Vector2(unitnormal.X * inormalspeedb, unitnormal.Y * inormalspeedb);
Vector2 itana = new Vector2(unittan.X * itanspeed, unittan.Y * itanspeed);
//Add Vector components to each balls' Velocity
velocity = Vector2.Add(inormala, itana);
You don't need farseer physics for that, what you need is circle to rectangle collisition funciton.
// circle is object with x,y,r parameters
bool intersects(CircleType circle, RectType rect)
{
circleDistance.x = abs(circle.x - rect.x);
circleDistance.y = abs(circle.y - rect.y);
if (circleDistance.x > (rect.width/2 + circle.r)) { return false; }
if (circleDistance.y > (rect.height/2 + circle.r)) { return false; }
if (circleDistance.x <= (rect.width/2)) { return true; }
if (circleDistance.y <= (rect.height/2)) { return true; }
cornerDistance_sq = (circleDistance.x - rect.width/2)^2 +
(circleDistance.y - rect.height/2)^2;
return (cornerDistance_sq <= (circle.r^2));
}
Added CircleType and RectType
public class CircleType
{
public int x;
public int y;
public decimal r;
}
public class RectType
{
public int x;
public int y;
public int width;
public int height;
}

Bezier not working correctly

I'm trying to implement a simple bezier curve into my Unity game but it no seems to work.
here is a screenshot of how is being drawn:
The 4 green spheres are the Bezier's pivots, and the white spheres are the actual bezier being calculated. As you see, its kinda incorrect.
Here's my bezier code (i took it from Unity's forums):
using UnityEngine;
[System.Serializable]
public class Bezier : System.Object
{
public Vector3 p0;
public Vector3 p1;
public Vector3 p2;
public Vector3 p3;
public float ti = 0f;
private Vector3 b0 = Vector3.zero;
private Vector3 b1 = Vector3.zero;
private Vector3 b2 = Vector3.zero;
private Vector3 b3 = Vector3.zero;
private float Ax;
private float Ay;
private float Az;
private float Bx;
private float By;
private float Bz;
private float Cx;
private float Cy;
private float Cz;
// Init function v0 = 1st point, v1 = handle of the 1st point , v2 = handle of the 2nd point, v3 = 2nd point
// handle1 = v0 + v1
// handle2 = v3 + v2
public Bezier( Vector3 v0, Vector3 v1, Vector3 v2, Vector3 v3 )
{
this.p0 = v0;
this.p1 = v1;
this.p2 = v2;
this.p3 = v3;
}
// 0.0 >= t <= 1.0
public Vector3 GetPointAtTime( float t )
{
this.CheckConstant();
float t2 = t * t;
float t3 = t * t * t;
float x = this.Ax * t3 + this.Bx * t2 + this.Cx * t + p0.x;
float y = this.Ay * t3 + this.By * t2 + this.Cy * t + p0.y;
float z = this.Az * t3 + this.Bz * t2 + this.Cz * t + p0.z;
return new Vector3( x, y, z );
}
private void SetConstant()
{
this.Cx = 3f * ( ( this.p0.x + this.p1.x ) - this.p0.x );
this.Bx = 3f * ( ( this.p3.x + this.p2.x ) - ( this.p0.x + this.p1.x ) ) - this.Cx;
this.Ax = this.p3.x - this.p0.x - this.Cx - this.Bx;
this.Cy = 3f * ( ( this.p0.y + this.p1.y ) - this.p0.y );
this.By = 3f * ( ( this.p3.y + this.p2.y ) - ( this.p0.y + this.p1.y ) ) - this.Cy;
this.Ay = this.p3.y - this.p0.y - this.Cy - this.By;
this.Cz = 3f * ( ( this.p0.z + this.p1.z ) - this.p0.z );
this.Bz = 3f * ( ( this.p3.z + this.p2.z ) - ( this.p0.z + this.p1.z ) ) - this.Cz;
this.Az = this.p3.z - this.p0.z - this.Cz - this.Bz;
}
// Check if p0, p1, p2 or p3 have changed
private void CheckConstant()
{
if( this.p0 != this.b0 || this.p1 != this.b1 || this.p2 != this.b2 || this.p3 != this.b3 )
{
this.SetConstant();
this.b0 = this.p0;
this.b1 = this.p1;
this.b2 = this.p2;
this.b3 = this.p3;
}
}
}
And here is my implementation script (it's on a empty gameobject in the scene):
public class arrowPath : MonoBehaviour {
public Transform[] pontos=new Transform[4];
public Bezier bezier;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
bezier=new Bezier(pontos[0].position, pontos[1].position, pontos[2].position, pontos[3].position);
for(float u= 0.0f; u<=1.0f; u+=0.05f)
{
Graphics.DrawMesh(pontos[0].GetComponent<MeshFilter>().sharedMesh, bezier.GetPointAtTime(u), Quaternion.Euler(Vector3.zero), pontos[0].renderer.sharedMaterial, 0);
}
}
}
The "pontos" array is Transform array to save the spheres. I fill it via Unity's inspector.
I appreciate any help.
Thanks.
It looks very much like that GetPointAtTime is intended to generate the Bezier point. If that's the case, you're not evaluating the correct function at all: for a cubic Bezier, the function is:
a ‧ t³ + 3 ‧ b ‧ t² ‧ (1-t) + 3 ‧ c ‧ t ‧ (1-t)² + d ‧ (1-t)³
So your code isn't using binomials, it's simply using a straight polynomial of t which is going to generate really wrong things =)
So, changing your function:
public float ComputeBezierValue( float t, float a, float b, float c, float d )
{
float t2 = t * t,
t3 = t2 * t,
mt = 1 - t,
mt2 = mt * mt,
mt3 = mt2 * mt;
return a * t3 + 3 * b * t2 * mt + 3 * c * t * mt2 + d * mt3;
}
public Vector3 GetPointAtTime( float t )
{
this.CheckConstant();
float x = ComputeBezierValue(t, this.Ax, this.Bx, this.Cx, this.Dx);
float y = ComputeBezierValue(t, this.Ay, this.By, this.Cy, this.Dy);
float z = ComputeBezierValue(t, this.Az, this.Bz, this.Cz, this.Dz);
return new Vector3( x, y, z );
}
Also note I've written this to explicitly use four coordinates. Cubic Bezier curves need all four, Quadratic only use three (but are generally very poor for representing uniformly curving segments like circular/elliptical segments, as well as sinoids)

Ray Tracing C# Triangle Intersection

So basicly I want to reflect a ray over a triangle. Here's my ray class
public sealed class Ray
{
public readonly Point3D Source;
public readonly Point3D Direction;
public readonly Color Light;
public Ray(Point3D source, Point3D direction, Color light)
{
if (source == direction)
{
throw new ArgumentException("Source and Direction cannot be equal");
}
this.Source = source;
this.Direction = direction;
this.Light = light;
}
}
Heres my Point3D class
public struct Point3D : IEquatable<Point3D>
{
public static readonly Point3D Zero = new Point3D();
public float X;
public float Y;
public float Z;
public Point3D(float x, float y, float z)
{
this.X = x;
this.Y = y;
this.Z = z;
}
public override bool Equals(object obj)
{
if (!(obj is Point3D))
{
return false;
}
return this.Equals((Point3D)obj);
}
public static bool operator ==(Point3D one, Point3D two)
{
return one.Equals(two);
}
public static bool operator !=(Point3D one, Point3D two)
{
return !one.Equals(two);
}
public static Point3D operator *(float n, Point3D v)
{
return new Point3D(v.X * n, v.Y * n, v.Z * n);
}
public static Point3D operator +(Point3D v1, Point3D v2)
{
return new Point3D(v1.X + v2.X, v1.Y + v2.Y, v1.Z + v2.Z);
}
public static Point3D operator -(Point3D v1, Point3D v2)
{
return new Point3D(v1.X - v2.X, v1.Y - v2.Y, v1.Z - v2.Z);
}
public static float operator *(Point3D v1, Point3D v2)
{
return (v1.X * v2.X) + (v1.Y * v2.Y) + (v1.Z * v2.Z);
}
public static float Magnitude(Point3D v)
{
return (float)Math.Sqrt(v * v);
}
public static Point3D Normalize(Point3D v)
{
float mag = Magnitude(v);
float div = (mag == 0) ? float.PositiveInfinity : (1 / mag);
return div * v;
}
public static Point3D Cross(Point3D v1, Point3D v2)
{
return new Point3D(((v1.Y * v2.Z) - (v1.Z * v2.Y)),
((v1.Z * v2.X) - (v1.X * v2.Z)),
((v1.X * v2.Y) - (v1.Y * v2.X)));
}
/// <summary>
/// doesnt take square root
/// </summary>
public static float FastDistance(Point3D v1, Point3D v2)
{
float x = v1.X - v2.X;
x *= x;
float y = v1.Y - v2.Y;
y *= y;
float z = v1.Z - v2.Z;
z *= z;
return x + y + z;
}
/// <summary>
/// Takes square root:
/// </summary>
public static float Distance(Point3D v1, Point3D v2)
{
return (float)Math.Sqrt(Point3D.FastDistance(v1, v2));
}
public override int GetHashCode()
{
return this.X.GetHashCode()
^ this.Y.GetHashCode()
^ this.Y.GetHashCode();
}
public override string ToString()
{
return this.X + ", " + this.Y + ", " + this.Z;
}
public bool Equals(Point3D other)
{
return this.X == other.X
&& this.Y == other.Y
&& this.Z == other.Z;
}
}
and finally here is where I need my method to be realized.
public interface ITriangleAccess
{
Triangle3D Find(Ray ray, out Point3D crossPoint);
}
public sealed class TriangleAccess : ITriangleAccess
{
private readonly List<KeyValuePair<float, Triangle3D>> trianglesByX;
private readonly List<Triangle3D> allTriangles;
public TriangleAccess(Body[] bodies)
{
if (null == bodies)
{
throw new ArgumentNullException("bodies");
}
this.allTriangles = bodies.SelectMany((x) => x.Parts).ToList();
this.trianglesByX = bodies.SelectMany((x) => x.Parts).SelectMany((y) => new KeyValuePair<float, Triangle3D>[]
{
new KeyValuePair<float,Triangle3D>(y.Point1.X,y),
new KeyValuePair<float,Triangle3D>(y.Point2.X,y),
new KeyValuePair<float,Triangle3D>(y.Point3.X,y)
}).ToList();
}
public Triangle3D Find(Ray ray, out Point3D crossPoint)
{
crossPoint = Point3D.Zero;
List<Triangle3D> relevant = this.GetRelevantTriangles(ray);
Triangle3D absoluteTriangle = null;
float min = float.MaxValue;
foreach (Triangle3D item in relevant)
{
Point3D currentCrossPoint;
if (this.RayIntersectTriangle(ray, item, out currentCrossPoint))
{
float distance = Point3D.Distance(ray.Source, currentCrossPoint);
if (distance < min)
{
absoluteTriangle = item;
crossPoint = currentCrossPoint;
min = distance;
}
}
}
return absoluteTriangle;
}
public Ray Reflect(Ray ray, Point3D crossPoint, Triangle3D intersect)
{
//need this to be realized
//please help
}
/// <summary>
/// TODO: Finish this Up:
/// </summary>
/// <param name="ray"></param>
/// <returns></returns>
private List<Triangle3D> GetRelevantTriangles(Ray ray)
{
return this.allTriangles;
}
private bool RayIntersectTriangle(Ray ray, Triangle3D triangle, out Point3D crossPoint)
{
// Find vectors for two edges sharing vert0
Point3D edge1 = triangle.Point2 - triangle.Point1;
Point3D edge2 = triangle.Point3 - triangle.Point1;
// Begin calculating determinant - also used to calculate barycentricU parameter
Point3D pvec = Point3D.Cross(ray.Direction, edge2);
// If determinant is near zero, ray lies in plane of triangle
float det = edge1 * pvec;
if (det < 0.0001f)
{
crossPoint = Point3D.Zero;
return false;
}
// Calculate distance from vert0 to ray origin
Point3D tvec = ray.Source - triangle.Point1;
// Calculate barycentricU parameter and test bounds
float barycentricU = tvec * pvec;
if (barycentricU < 0.0f || barycentricU > det)
{
crossPoint = Point3D.Zero;
return false;
}
// Prepare to test barycentricV parameter
Point3D qvec = Point3D.Cross(tvec, edge1);
// Calculate barycentricV parameter and test bounds
float barycentricV = ray.Direction * qvec;
if (barycentricV < 0.0f || barycentricU + barycentricV > det)
{
crossPoint = Point3D.Zero;
return false;
}
// Calculate pickDistance, scale parameters, ray intersects triangle
float pickDistance = edge2 * qvec;
float fInvDet = 1.0f / det;
pickDistance *= fInvDet;
barycentricU *= fInvDet;
barycentricV *= fInvDet;
crossPoint = MathHelper.BaryCentric(triangle, barycentricU, barycentricV);
return true;
}
}
Thank you.
P.S have patience with me... I'm just 15 :)
I'm assuming that if you have a ray that intersects with a triangle, your Reflect function should return a ray that originates at that intersection and heads off in a new direction (like light on a mirror).
That said, we already have the ray source (crossPoint).
The formula for the reflected vector (direction of the ray) is R=V−2N(V⋅N) where V is the inverted direction of the incoming ray (as if it originated from the object) and N is the normal vector of the triangle.
With your code, you should just need to something like:
public Ray Reflect(Ray ray, Point3D crossPoint, Triangle3D intersect)
{
Point3D V = -ray.Direction.Normalize();
return new Ray(crossPoint,
V - 2 * intersect.Normal * (V * intersect.Normal),
Color.white);
}
Also, if your triangle structure doesn't have the normal, you calculate it using the cross product of the vectors that form each side.
public Ray Reflect(Ray ray, Point3D crossPoint, Triangle3D intersect)
{
// find normal of intersect triangle
Point3D normal = Point3D.Cross( intersect.Point2 - intersect.Point1,
intersect.Point3 - intersect.Point1 );
normal = Point3D.Normalize( normal );
// find ray part before intersection
Point3D inbound = crossPoint - ray.Source;
// find projection of inbound ray to normal
Point3D projection = (normal * inbound) * normal;
// find lateral component of inbound ray
Point3D lateral = inbound - projection;
// find outbound direction
Point3D direction = (ray.Source + 2 * lateral) - crossPoint;
direction = Point3D.Normalize( direction );
// I assume your direction is unit vector (magnitude = 1)
// if not multiply it with magnitude you want to be
// direction = ... * direction;
return new Ray( crossPoint, direction, ray.Color );
}

Points on a circle by angle not spaced evenly [duplicate]

This question already has an answer here:
Finding Points On Perimeter Of a Circle
(1 answer)
Closed 10 years ago.
Given:
x = originX + radius * Cos(angle);
y = originY + radius * Sin(angle);
Why would the points not be evenly distributed around the edge of the circle?
Image of the result:
class Circle
{
public Vector2 Origin { get; set; }
public float Radius { get; set; }
public List<Vector2> Points { get; set; }
private Texture2D _texture;
public Circle(float radius, Vector2 origin, ContentManager content)
{
Radius = radius;
Origin = origin;
_texture = content.Load<Texture2D>("pixel");
Points = new List<Vector2>();
//i = angle
for (int i = 0; i < 360; i++)
{
float x = origin.X + radius * (float)Math.Cos(i);
float y = origin.Y + radius * (float)Math.Sin(i);
Points.Add(new Vector2(x, y));
}
}
public void Draw(SpriteBatch spriteBatch)
{
for (int i = 0; i < Points.Count; i++)
spriteBatch.Draw(_texture, Points[i], new Rectangle(0, 0, _texture.Width, _texture.Height), Color.Red);
}
}
Math.Cos and Math.Sin take the angle in radians as opposed to degrees, your code should be:
float x = origin.X + radius * (float)Math.Cos(i*Math.PI/180.0);
float y = origin.Y + radius * (float)Math.Sin(i*Math.PI/180.0);
Points:
1) Math.Trig functions use Radians, not Degrees.
2) For this kind of precision, you would be better off using double instead of float.
3) The computer graphics/gaming pros avoid expensive functions like Sin and Cos, and instead use incremental integer pixel oriented approaches, like Bresenham's Algorithms, that give results as good or better than the straight-forward trigonometric math calculations and are much faster.

Categories