Whats the best way to create an Arc on a Custom Mesh - c#

Can anyone point me in the right direction as to what am I doing wrong here. My line does get created but but when I move the cursor up to create an angle in the line created to form an arc, simply nothin happens.
private void CreateArc(PlaneEx plane, Vector2 p0, Vector2 p1, Vector2 p2)
{
this.spots_.Clear();
ArcToolProperties arcToolProperties = this.Properties<ArcToolProperties>();
List<Vector2> outPoints;
if (!MathUtil.CreateArc(new Edge2D(p0, p1), p2, arcToolProperties.segment, out outPoints, out Vector2 _))
{
this.spots_.Add(this.first_);
this.spots_.Add(this.second_);
this.arcCenter_ = (this.first_.pos + this.second_.pos) / 2f;
}
else
{
for (int index = 0; index < outPoints.Count; ++index)
{
this.spots_.Add(this.first_);
first_.pos = plane.FromPlaneCoord(outPoints[index]);
first_.snapStatus = ESnapStatus.None;
}
this.arcCenter_ = outPoints.Count % 2 != 0 ? plane.FromPlaneCoord(outPoints[outPoints.Count / 2]) : plane.FromPlaneCoord((outPoints[outPoints.Count / 2] + outPoints[outPoints.Count / 2 - 1]) / 2f);
if (this.spots_.Count <= 0)
return;
this.spots_[0] = this.first_;
this.spots_[this.spots_.Count - 1] = this.second_;
}

Related

Unwanted Duplicate Triangle when selecting a Triangle from Mesh with Barycentric Selection. - Unity

What I'm trying to do is select a specific triangle on any given mesh, so I'm using the Barycentric algorithm. However, as seen here, there are some strange behaviors at certain locations and always an unwanted second triangle. Best guess, it has something to do with inaccurate float precision of the Vector3.
Here is the Script I'm Currently Using:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpecificTriangleIndex : MonoBehaviour
{
public Camera m_camera;
public LayerMask layermask;
public Vector3[] vertices;
public Vector3[] normals;
public int[] triangles;
public List<int> triangleIndices = new List<int>(); ///Returned Triangle Indices -- What it thinks is selected.
public List<Vector3> baryAllens = new List<Vector3>(); ///Position of Verticies of Returned Triangles
private RaycastHit hit;
private Transform objectHit;
private Vector3 bary;
void Update()
{
if (Input.GetMouseButton(0))
{
EobardThawne();
Raycast();
}
}
void Raycast()
{
Ray ray = m_camera.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit, layermask))
{
objectHit = hit.transform;
MeshFilter meshFilter = hit.transform.gameObject.GetComponent<MeshFilter>();
Mesh mesh = meshFilter.sharedMesh;
vertices = mesh.vertices;
normals = mesh.normals;
triangles = mesh.triangles;
Vector3 p = transform.InverseTransformPoint(hit.point);
for (int i = 0; i < triangles.Length; i += 3)
{
Vector3 a = vertices[triangles[i]];
Vector3 b = vertices[triangles[i + 1]];
Vector3 c = vertices[triangles[i + 2]];
bary = GetBarycentric(a, b, c, p);
if (InTriangle(bary))
{
triangleIndices.Add(i / 3);
baryAllens.Add(a + objectHit.position);
baryAllens.Add(b + objectHit.position);
baryAllens.Add(c + objectHit.position);
}
}
for (int i = 0; i < baryAllens.Count; i += 3)
{
Vector3 v0 = baryAllens[i];
Vector3 v1 = baryAllens[i + 1];
Vector3 v2 = baryAllens[i + 2];
Debug.DrawLine(v0, v1, Color.green);
Debug.DrawLine(v1, v2, Color.green);
Debug.DrawLine(v2, v0, Color.green);
}
}
}
Vector3 GetBarycentric(Vector2 v1, Vector2 v2, Vector2 v3, Vector2 p)
{
Vector3 B = new Vector3();
B.x = ((v2.y - v3.y) * (p.x - v3.x) + (v3.x - v2.x) * (p.y - v3.y)) /
((v2.y - v3.y) * (v1.x - v3.x) + (v3.x - v2.x) * (v1.y - v3.y));
B.y = ((v3.y - v1.y) * (p.x - v3.x) + (v1.x - v3.x) * (p.y - v3.y)) /
((v3.y - v1.y) * (v2.x - v3.x) + (v1.x - v3.x) * (v2.y - v3.y));
B.z = 1 - B.x - B.y;
return B;
}
bool InTriangle(Vector3 barycentric)
{
return (barycentric.x >= 0.0f) && (barycentric.x <= 1.0f)
&& (barycentric.y >= 0.0f) && (barycentric.y <= 1.0f)
&& (barycentric.z >= 0.0f); //(barycentric.z <= 1.0f)
}
private void OnDrawGizmos()
{
Gizmos.color = Color.yellow;
Gizmos.DrawSphere(hit.point, .01f);
Gizmos.color = Color.cyan;
foreach (int i in triangleIndices)
{
Gizmos.DrawSphere(FindCenter(i * 3), .01f);
Debug.DrawLine(hit.point, FindCenter(i * 3), Color.red);
}
}
public void EobardThawne()
{
triangleIndices.Clear();
baryAllens.Clear();
}
public Vector3 FindCenter(int i)
{
Vector3 v0 = transform.TransformPoint(vertices[triangles[i]]);
Vector3 v1 = transform.TransformPoint(vertices[triangles[i + 1]]);
Vector3 v2 = transform.TransformPoint(vertices[triangles[i + 2]]);
Vector3 center = (v0 + v1 + v2) / 3;
return center;
}
}
Your triangles are duplicated because you fully ignore the Z axis!
You do all your triangle math assuming 2D space and only using Vector2!
=> Of course you get a second triangle since on your round objects there will always be two triangles overlapping along the Z axis!
You will have to use an actual 3D "point in triangle" test like e.g. the one from this thread
bool PointInTriangle(Vector3 a, Vector3 b, Vector3 c, Vector3 p)
{
Vector3 d, e;
double w1, w2;
d = b - a;
e = c - a;
if (Mathf.Approximately(e.y, 0))
{
e.y = 0.0001f;
}
w1 = (e.x * (a.y - p.y) + e.y * (p.x - a.x)) / (d.x * e.y - d.y * e.x);
w2 = (p.y - a.y - w1 * d.y) / e.y;
return (w1 >= 0f) && (w2 >= 0.0) && ((w1 + w2) <= 1.0);
}
And then personally I would add some proper classes instead of arrays and do e.g.
public class SpecificTriangleIndex : MonoBehaviour
{
public Camera m_camera;
public LayerMask layermask;
public class HitInfo
{
public Vector3 Point;
public Triangle Triangle = new Triangle();
}
public class Triangle
{
public Vector3 A;
public Vector3 B;
public Vector3 C;
public Vector3 Center => (A + B + C) / 3f;
}
private HitInfo currenHit;
private void Update()
{
if (Input.GetMouseButton(0))
{
Raycast();
}
}
private void Raycast()
{
var ray = m_camera.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out var hit, float.PositiveInfinity, layermask))
{
var meshFilter = hit.transform.gameObject.GetComponent<MeshFilter>();
var mesh = meshFilter.sharedMesh;
var vertices = mesh.vertices;
var triangles = mesh.triangles;
// Here you need to go into the local space of the hit not yourself!
var p = hit.transform.InverseTransformPoint(hit.point);
for (var i = 0; i < triangles.Length; i += 3)
{
var a = vertices[triangles[i]];
var b = vertices[triangles[i + 1]];
var c = vertices[triangles[i + 2]];
if (PointInTriangle(a, b, c, p))
{
if (currenHit == null)
{
currenHit = new HitInfo();
}
currenHit.Point = hit.point;
// as before you also want to convert back using the hit transform, not your own
currenHit.Triangle.A = hit.transform.TransformPoint(a);
currenHit.Triangle.B = hit.transform.TransformPoint(b);
currenHit.Triangle.C = hit.transform.TransformPoint(c);
// we only want one triangle anyway so we can skip the remaining triangles
break;
}
}
}
else
{
currenHit = null;
}
}
private static bool PointInTriangle(Vector3 a, Vector3 b, Vector3 c, Vector3 p)
{
var d = b - a;
var e = c - a;
if (Mathf.Approximately(e.y, 0))
{
e.y = 0.0001f;
}
double w1 = (e.x * (a.y - p.y) + e.y * (p.x - a.x)) / (d.x * e.y - d.y * e.x);
var w2 = (p.y - a.y - w1 * d.y) / e.y;
return (w1 >= 0f) && (w2 >= 0.0) && ((w1 + w2) <= 1.0);
}
private void OnDrawGizmos()
{
// only draw if there is a hit
if (currenHit == null) return;
Gizmos.color = Color.yellow;
Gizmos.DrawSphere(currenHit.Point, .01f);
Gizmos.color = Color.cyan;
Gizmos.DrawSphere(currenHit.Triangle.Center, .01f);
Gizmos.color = Color.red;
Gizmos.DrawLine(currenHit.Point, currenHit.Triangle.Center);
Gizmos.color = Color.green;
Gizmos.DrawLine(currenHit.Triangle.A, currenHit.Triangle.B);
Gizmos.DrawLine(currenHit.Triangle.B, currenHit.Triangle.C);
Gizmos.DrawLine(currenHit.Triangle.C, currenHit.Triangle.A);
}
}
Maybe hard to see but this is how it looks like now

How to find out if 2 triangles on a 2D plane rotated in 3D space are intersecting?

I'm trying to create a method in C# and unity, that takes in 2 triangles(the positions of their vertices) and returns true if the triangles are touching(not jammed into each other, just properly aligned and touching), and false if they are not. I divided it into tests, they must have opposite normals, they must be on the same 2D plane(which may be rotated), and must be intersecting on that 2D plane. How do I do that last step?
bool TrianglesTouching(Vector3[] triangle1, Vector3[] triangle2)
{
bool touching = false;
// Step 1: Success
bool facing = false;
Vector3 normal1 = SurfaceNormal(triangle1[0], triangle1[1], triangle1[2]);
Vector3 normal2 = SurfaceNormal(triangle2[0], triangle2[1], triangle2[2]);
facing = Util.RoundVector3(normal1, 0.0001f) == Util.RoundVector3(normal2 * -1f, 0.0001f);
// Step 2: Success
bool onSamePlane = false;
Plane plane = new Plane();
plane.Set3Points(triangle1[0], triangle1[1], triangle1[2]);
onSamePlane = Util.RoundToNearest(plane.GetDistanceToPoint(triangle2[0]), 0.0001f) == 0f; // point 0 is an arbitrary vertex of the triangle, we could have found the position of the triangle by averaging the position of its vertices, but that isnt necessary in this case. Any vertex position of this triangle will do just fine.
// Step 3: What do I do here?
bool intersectingOnPlane = false;
// Some logic to figure out if they're intersecting. Please fill this in for me, StackOverflow wizards.
touching = facing && onSamePlane && intersectingOnPlane;
return touching;
}
Diagram of what I mean:
enter image description here
I would recommend Habrador - Use math to solve problems in Unity with C# - Chapter 6 Are two triangles in 2D space intersecting?. Really check it out since it explains it way more details there.
Basically it approaches this in the classical 3 steps:
Step 1. Approximate the triangles with rectangles and test if they intersect.
Step 2. Check if any of the sides of one triangle is intersecting with any of the sides of the other triangle.
Step 3. Check if any of the corners of one triangle is inside the other triangle, and vice versa.
First it uses some helper types
//To store triangle data to get cleaner code
public struct Triangle
{
//Corners of the triangle
public Vector3 p1, p2, p3;
//The 3 line segments that make up this triangle
public LineSegment[] lineSegments;
public Triangle(Vector3 p1, Vector3 p2, Vector3 p3)
{
this.p1 = p1;
this.p2 = p2;
this.p3 = p3;
lineSegments = new LineSegment[3];
lineSegments[0] = new LineSegment(p1, p2);
lineSegments[1] = new LineSegment(p2, p3);
lineSegments[2] = new LineSegment(p3, p1);
}
}
//To create a line segment
public struct LineSegment
{
//Start/end coordinates
public Vector3 p1, p2;
public LineSegment(Vector3 p1, Vector3 p2)
{
this.p1 = p1;
this.p2 = p2;
}
}
And then it checks the 3 steps consecutive in
//The triangle-triangle intersection in 2D algorithm
bool IsTriangleTriangleIntersecting(Triangle triangle1, Triangle triangle2)
{
bool isIntersecting = false;
//Step 1. AABB intersection
if (IsIntersectingAABB(triangle1, triangle2))
{
//Step 2. Line segment - triangle intersection
if (AreAnyLineSegmentsIntersecting(triangle1, triangle2))
{
isIntersecting = true;
}
//Step 3. Point in triangle intersection - if one of the triangles is inside the other
else if (AreCornersIntersecting(triangle1, triangle2))
{
isIntersecting = true;
}
}
return isIntersecting;
}
The implementation of the 3 steps itself
//
// STEP 1 - Intersection: AABB
//
//Approximate the triangles with rectangles and see if they intersect with the AABB intersection algorithm
bool IsIntersectingAABB(Triangle t1, Triangle t2)
{
//Find the size of the bounding box
//Triangle 1
float t1_minX = Mathf.Min(t1.p1.x, Mathf.Min(t1.p2.x, t1.p3.x));
float t1_maxX = Mathf.Max(t1.p1.x, Mathf.Max(t1.p2.x, t1.p3.x));
float t1_minZ = Mathf.Min(t1.p1.z, Mathf.Min(t1.p2.z, t1.p3.z));
float t1_maxZ = Mathf.Max(t1.p1.z, Mathf.Max(t1.p2.z, t1.p3.z));
//Triangle 2
float t2_minX = Mathf.Min(t2.p1.x, Mathf.Min(t2.p2.x, t2.p3.x));
float t2_maxX = Mathf.Max(t2.p1.x, Mathf.Max(t2.p2.x, t2.p3.x));
float t2_minZ = Mathf.Min(t2.p1.z, Mathf.Min(t2.p2.z, t2.p3.z));
float t2_maxZ = Mathf.Max(t2.p1.z, Mathf.Max(t2.p2.z, t2.p3.z));
//Are the rectangles intersecting?
//If the min of one box in one dimension is greater than the max of another box then the boxes are not intersecting
//They have to intersect in 2 dimensions. We have to test if box 1 is to the left or box 2 and vice versa
bool isIntersecting = true;
//X axis
if (t1_minX > t2_maxX)
{
isIntersecting = false;
}
else if (t2_minX > t1_maxX)
{
isIntersecting = false;
}
//Z axis
else if (t1_minZ > t2_maxZ)
{
isIntersecting = false;
}
else if (t2_minZ > t1_maxZ)
{
isIntersecting = false;
}
//Debugging to display the approximated rectangles
//Box 1
Vector3 BB_1_centerPos = new Vector3((t1_maxX - t1_minX) * 0.5f + t1_minX, -0.2f, (t1_maxZ - t1_minZ) * 0.5f + t1_minZ);
box1Obj.transform.position = BB_1_centerPos;
box1Obj.transform.localScale = new Vector3(t1_maxX - t1_minX, 0.1f, t1_maxZ - t1_minZ);
//Box 2
Vector3 BB_2_centerPos = new Vector3((t2_maxX - t2_minX) * 0.5f + t2_minX, -0.2f, (t2_maxZ - t2_minZ) * 0.5f + t2_minZ);
box2Obj.transform.position = BB_2_centerPos;
box2Obj.transform.localScale = new Vector3(t2_maxX - t2_minX, 0.1f, t2_maxZ - t2_minZ);
return isIntersecting;
}
//
// STEP 2 - Intersection: Line segment - triangle
//
//Check if any of the edges that make up one of the triangles is intersecting with any of
//the edges of the other triangle
bool AreAnyLineSegmentsIntersecting(Triangle t1, Triangle t2)
{
bool isIntersecting = false;
//Loop through all edges
for (int i = 0; i < t1.lineSegments.Length; i++)
{
for (int j = 0; j < t2.lineSegments.Length; j++)
{
//The start/end coordinates of the current line segments
Vector3 t1_p1 = t1.lineSegments[i].p1;
Vector3 t1_p2 = t1.lineSegments[i].p2;
Vector3 t2_p1 = t2.lineSegments[j].p1;
Vector3 t2_p2 = t2.lineSegments[j].p2;
//Are they intersecting?
if (AreLineSegmentsIntersecting(t1_p1, t1_p2, t2_p1, t2_p2))
{
isIntersecting = true;
//To stop the outer for loop
i = int.MaxValue - 1;
break;
}
}
}
return isIntersecting;
}
//Check if 2 line segments are intersecting in 2d space
//http://thirdpartyninjas.com/blog/2008/10/07/line-segment-intersection/
//p1 and p2 belong to line 1, p3 and p4 belong to line 2
bool AreLineSegmentsIntersecting(Vector3 p1, Vector3 p2, Vector3 p3, Vector3 p4)
{
bool isIntersecting = false;
float denominator = (p4.z - p3.z) * (p2.x - p1.x) - (p4.x - p3.x) * (p2.z - p1.z);
//Make sure the denominator is != 0, if 0 the lines are parallel
if (denominator != 0)
{
float u_a = ((p4.x - p3.x) * (p1.z - p3.z) - (p4.z - p3.z) * (p1.x - p3.x)) / denominator;
float u_b = ((p2.x - p1.x) * (p1.z - p3.z) - (p2.z - p1.z) * (p1.x - p3.x)) / denominator;
//Is intersecting if u_a and u_b are between 0 and 1
if (u_a >= 0 && u_a <= 1 && u_b >= 0 && u_b <= 1)
{
isIntersecting = true;
}
}
return isIntersecting;
}
//
// STEP 3 - Intersection: Point in triangle
//
//There's a possibility that one of the triangles is smaller than the other
//So we have to check if any of the triangle's corners is inside the other triangle
bool AreCornersIntersecting(Triangle t1, Triangle t2)
{
bool isIntersecting = false;
//We only have to test one corner from each triangle
//Triangle 1 in triangle 2
if (IsPointInTriangle(t1.p1, t2.p1, t2.p2, t2.p3))
{
isIntersecting = true;
}
//Triangle 2 in triangle 1
else if (IsPointInTriangle(t2.p1, t1.p1, t1.p2, t1.p3))
{
isIntersecting = true;
}
return isIntersecting;
}
//Is a point p inside a triangle p1-p2-p3?
//From http://totologic.blogspot.se/2014/01/accurate-point-in-triangle-test.html
bool IsPointInTriangle(Vector3 p, Vector3 p1, Vector3 p2, Vector3 p3)
{
bool isWithinTriangle = false;
float denominator = ((p2.z - p3.z) * (p1.x - p3.x) + (p3.x - p2.x) * (p1.z - p3.z));
float a = ((p2.z - p3.z) * (p.x - p3.x) + (p3.x - p2.x) * (p.z - p3.z)) / denominator;
float b = ((p3.z - p1.z) * (p.x - p3.x) + (p1.x - p3.x) * (p.z - p3.z)) / denominator;
float c = 1 - a - b;
//The point is within the triangle if 0 <= a <= 1 and 0 <= b <= 1 and 0 <= c <= 1
if (a >= 0f && a <= 1f && b >= 0f && b <= 1f && c >= 0f && c <= 1f)
{
isWithinTriangle = true;
}
return isWithinTriangle;
}
Soooo I decided to rework and refactor this a little
/// <summary>
/// To store triangle data to get cleaner code
/// </summary>
[Serializable]
public struct Triangle2D
{
/// <summary>
/// To create a line segment
/// </summary>
private struct LineSegment2D
{
//Start/end coordinates
private readonly Vector2 _p1;
private readonly Vector2 _p2;
public LineSegment2D(Vector2 p1, Vector2 p2)
{
_p1 = p1;
_p2 = p2;
}
/// <summary>
/// Check if 2 line segments are intersecting in 2d space
/// <para>http://thirdpartyninjas.com/blog/2008/10/07/line-segment-intersection/</para>
/// </summary>
public bool IsIntersecting(LineSegment2D other)
{
var p3 = other._p1;
var p4 = other._p2;
var denominator = (p4.y - p3.y) * (_p2.x - _p1.x) - (p4.x - p3.x) * (_p2.y - _p1.y);
//Make sure the denominator is != 0, if 0 the lines are parallel
if (denominator == 0) return false;
var u_a = ((p4.x - p3.x) * (_p1.y - p3.y) - (p4.y - p3.y) * (_p1.x - p3.x)) / denominator;
var u_b = ((_p2.x - _p1.x) * (_p1.y - p3.y) - (_p2.y - _p1.y) * (_p1.x - p3.x)) / denominator;
//Is intersecting if u_a and u_b are between 0 and 1
return 0 <= u_a && u_a <= 1 && 0 <= u_b && u_b <= 1;
}
}
//Corners of the triangle
// Can be edited via the Inspector
[SerializeField] private Vector2 _a;
[SerializeField] private Vector2 _b;
[SerializeField] private Vector2 _c;
//The 3 line segments that make up this triangle
private LineSegment2D[] _lineSegments;
public Triangle2D(Vector2 a, Vector2 b, Vector2 c)
{
_a = a;
_b = b;
_c = c;
_lineSegments = new[]
{
new LineSegment2D(a, b),
new LineSegment2D(b, c),
new LineSegment2D(c, a)
};
}
/// <summary>
/// Is this triangle intersecting the other one?
/// </summary>
public bool IsIntersecting(Triangle2D other)
{
//Step 1. AABB intersection
if (IsIntersectingAABB(other))
{
//Step 2. Line segment - triangle intersection
if (AreAnyLineSegmentsIntersecting(other))
{
return true;
}
//Step 3. Point in triangle intersection - if one of the triangles is inside the other
if (AreCornersIntersecting(other))
{
return true;
}
}
return false;
}
/// <summary>
/// Is a point p inside this triangle
///<para>From http://totologic.blogspot.se/2014/01/accurate-point-in-triangle-test.html</para>
/// </summary>
public bool IsPointInTriangle(Vector2 p)
{
var denominator = (_b.y - _c.y) * (_a.x - _c.x) + (_c.x - _b.x) * (_a.y - _c.y);
var a = ((_b.y - _c.y) * (p.x - _c.x) + (_c.x - _b.x) * (p.y - _c.y)) / denominator;
var b = ((_c.y - _a.y) * (p.x - _c.x) + (_a.x - _c.x) * (p.y - _c.y)) / denominator;
var c = 1 - a - b;
return 0f <= a && a <= 1f && 0f <= b && b <= 1f && 0f <= c && c <= 1f;
}
/// <summary>
/// Approximate the triangles with rectangles and see if they intersect with the AABB intersection algorithm
/// </summary>
private bool IsIntersectingAABB(Triangle2D other)
{
//Find the size of the bounding boxes
// Bounding box Triangle 1
var t1_minX = Mathf.Min(_a.x, _b.x, _c.x);
var t1_maxX = Mathf.Max(_a.x, _b.x, _c.x);
var t1_minY = Mathf.Min(_a.y, _b.y, _c.y);
var t1_maxY = Mathf.Max(_a.y, _b.y, _c.y);
// Bounding box Triangle 2
var t2_minX = Mathf.Min(other._a.x, other._b.x, other._c.x);
var t2_maxX = Mathf.Max(other._a.x, other._b.x, other._c.x);
var t2_minY = Mathf.Min(other._a.y, other._b.y, other._c.y);
var t2_maxY = Mathf.Max(other._a.y, other._b.y, other._c.y);
//Are the rectangles intersecting?
//If the min of one box in one dimension is greater than the max of another box then the boxes are not intersecting
//They have to intersect in 2 dimensions. We have to test if box 1 is to the left or box 2 and vice versa
return t1_minX <= t2_maxX && t2_minX <= t1_maxX && t1_minY <= t2_maxY && t2_minY <= t1_maxY;
}
/// <summary>
/// Check if any of the edges that make up one of the triangles is intersecting with any of the edges of the other triangle
/// </summary>
private bool AreAnyLineSegmentsIntersecting(Triangle2D triangle2)
{
//Loop through all edges
foreach (var line1 in _lineSegments)
{
foreach (var line2 in triangle2._lineSegments)
{
//Are they intersecting?
if (line1.IsIntersecting(line2))
{
return true;
}
}
}
return false;
}
/// <summary>
/// There's a possibility that one of the triangles is smaller than the other
/// <para>So we have to check if any of the triangle's corners is inside the other triangle</para>
/// </summary>
private bool AreCornersIntersecting(Triangle2D other)
{
//We only have to test one corner from each triangle (otherwise the line segments would already intersect anyway)
//Triangle 1 in triangle 2 or //Triangle 2 in triangle 1
return other.IsPointInTriangle(_a) || IsPointInTriangle(other._a);
}
}
And finally the usage would be e.g.
// Create the two triangles structs
var triangle1 = new Triangle2D(triangle1[0], triangle1[1], triangle1[2]);
var triangle2 = new Triangle2D(triangle2[0], triangle2[1], triangle2[2]);
bool intersectingOnPlane = triangle1.IsIntersecting(triangle2);

Line of Sight with Ray in XNA/Monogame

I'm trying to create a Line of Sight method for an enemy class. However, it always returns false, no matter how close the player is to the enemy or whether the ray passes through any blocks to get to the player.
public virtual bool PlayerInLOS()
{
Vector3 middleOfPlayer = new Vector3(Level.Player.Position.X, Level.Player.Position.Y - Level.Player.BoundingRectangle.Height / 2, 0);
Vector3 middleOfEnemy = new Vector3(Position.X, Position.Y - localBounds.Height / 2, 0);
Vector3 direction = middleOfPlayer - middleOfEnemy;
float distanceToPlayer = Vector3.Distance(middleOfEnemy, middleOfPlayer);
if (direction != Vector3.Zero)
direction.Normalize();
Ray lineOfSight = new Ray(middleOfEnemy, direction);
float? lineToPlayer = lineOfSight.Intersects(Level.Player.BoundingBox);
foreach (BoundingBox box in Level.boundingBoxes)
{
float? distanceToIntersect = lineOfSight.Intersects(box);
if (distanceToIntersect == null)
continue;
else if (distanceToIntersect < visionLength && distanceToIntersect < distanceToPlayer && distanceToIntersect != null)
return false;
}
// Never gets to this part because it always returns before it exits the for loop
if (lineToPlayer < visionLength)
return true;
else return false;
}
Any ideas? Thanks.
I ended up fixing the problem by implementing an entirely different solution: checking every Vector2 along the distance between the enemy and the player. Works perfectly.
public bool CanSeePlayer()
{
Vector2 middleOfPlayer = new Vector2(Level.Player.Position.X, Level.Player.Position.Y - Level.Player.BoundingRectangle.Height / 2);
Vector2 middleOfEnemy = new Vector2(Position.X, Position.Y - localBounds.Height / 2);
Vector2 direction = middleOfPlayer - middleOfEnemy;
float distanceToPlayer = Vector2.Distance(middleOfEnemy, middleOfPlayer);
if (visionLength > distanceToPlayer) // If the enemy can see farther than the player's distance,
{
if (direction != Vector2.Zero)
direction.Normalize();
for (int y = 0; y < Level.tiles.GetLength(1); ++y) // loop through every tile,
{
for (int x = 0; x < Level.tiles.GetLength(0); ++x)
{
if (Level.GetCollision(x, y) != TileCollision.Passable) // and if the block is solid,
{
Vector2 currentPos = middleOfEnemy;
float lengthOfLine = 0.0f;
Rectangle tileRect = new Rectangle(x * Tile.Width, y * Tile.Height, Tile.Width, Tile.Height);
while (lengthOfLine < distanceToPlayer + 1.0f) // check every point along the line
{
currentPos += direction;
if (tileRect.Contains(currentPos)) // to see if the tile contains it.
{
return false;
}
lengthOfLine = Vector2.Distance(middleOfEnemy, currentPos);
}
}
}
}
// If every tile does not contain a single point along the line from the enemy to the player,
return true;
}
return false;
}
If you need to check if enemy is within angle of sight, and in some distance you could try this code.
public static bool InLOS(float AngleDistance, float PositionDistance, Vector2 PositionA, Vector2 PositionB, float AngleB)
{
float AngleBetween = (float)Math.Atan2((PositionA.Y - PositionB.Y), (PositionA.X - PositionB.X));
if ((AngleBetween <= (AngleB + (AngleDistance / 2f / 100f))) && (AngleBetween >= (AngleB - (AngleDistance / 2f / 100f))) && (Vector2.Distance(PositionA, PositionB) <= PositionDistance)) return true;
else return false;
}
credits: https://gamedev.stackexchange.com/questions/26813/xna-2d-line-of-sight-check

Separation Steering Algorithm

Ok so this site has helped me alot before i had an account, but now the net is not helping me so i'll post it myself :)
I am looking (i am yet another one) to do something like this:
tinykeep.com/dungen/
Now the first step(Generating the rooms , goes well), but this is the first time ever I encounter Flocking behaviour algorithms, and my maths really arent that terrific.
So i tried and came up with a non working algorithm, what i did is started from next standard separation algorithm: gamedevelopment.tutsplus.com/tutorials/the-three-simple-rules-of-flocking-behaviors-alignment-cohesion-and-separation--gamedev-3444 (sorry for this text but it wouldn't let me post more than 2 links without 10 rep), And changed it to my liking. Like i said before it's really not doing anything, if i find how i'll post some debug results.
Here's my code:
class Room {
Vector3 position;
Vector3 size;
int area;
public Room(Vector3 position, Vector3 size, int area) : this(position, size){
this.area = area;
}
public Room(Vector3 position, Vector3 size){
this.position = position;
this.size = size;
if(area != null)
this.area = (int)(size.x * size.z);
}
public Vector3 GetPosition(){
return position;
}
public Vector3 GetSize(){
return size;
}
public void SetPosition(Vector3 position){
this.position = position;
}
public Boolean TooCloseTo(Room room){
return position.x + size.x / 2 > room.GetPosition().x - room.GetSize().x / 2 || position.z + size.z / 2 > room.GetPosition().z - room.GetSize().z / 2 ||
position.x - size.x / 2 < room.GetPosition().x + room.GetSize().x / 2 || position.z - size.z / 2 < room.GetPosition().z + room.GetSize().z / 2;
}
public override string ToString ()
{
return string.Format ("[Room]\nPosition:{0}\nSize:{1}\nArea:{2}", position, size, area);
}
}
These are part of the Mapgenerator class, the rest of this class works fine and seems unimportant to me, but if you feel the need to see it, let me know and i'll post it.
void SeparateRooms(){
foreach (Room room in rooms) {
//Debug.Log (room);
Vector3 oldPos = room.GetPosition ();
Vector3 separation = computeSeparation (room);
//Debug.Log (separation);
Vector3 newPos = new Vector3 (oldPos.x += separation.x, oldPos.y, oldPos.z += separation.z);
room.SetPosition (newPos);
//Debug.Log (room);
}
}
Vector3 computeSeparation(Room agent) {
int neighbours = 0;
Vector3 v = new Vector3 ();
foreach (Room room in rooms) {
if (room != agent) {
if (agent.TooCloseTo (room)) {
v.x += Difference(room, agent, "x");
v.z += Difference(room, agent, "z");
neighbours++;
}
}
}
if (neighbours == 0)
return v;
v.x /= neighbours;
v.z /= neighbours;
v.x *= -1;
v.z *= -1;
v.Normalize ();
return v;
}
float Difference(Room room, Room agent, string type){
switch (type) {
case "x":
float xBottom = (room.GetPosition ().x + room.GetSize ().x / 2) - (agent.GetPosition ().x - agent.GetSize ().x / 2);
float xTop = (agent.GetPosition ().x + agent.GetSize ().x / 2) - (room.GetPosition ().x - room.GetSize ().x / 2);
return xBottom > 0 ? xBottom : xTop;
break;
case "z":
float xRight= (room.GetPosition ().z + room.GetSize ().z / 2) - (agent.GetPosition ().z - agent.GetSize ().z / 2);
float xLeft= (agent.GetPosition ().z + agent.GetSize ().z / 2) - (room.GetPosition ().z - room.GetSize ().z / 2);
return xRight > 0 ? xRight : xLeft;
default:
return 0;
break;
}
}
I was expecting the difference to be numbers like 4.0 and 8.0 and so on, instead i get 0.9 and 0.3 and so on, i'll copy an output:
Debug log
I hope that is readable.
Explanation: Each three lines are the info about the room:
1. the original room
2. the (what's supposed to be the) overlap between the room and the next
3. the new position of the room
EDIT: Ok thanks to the comments i'm getting pretty normal results for overlap now, but the rooms are still overlapping after i run the algorithm, i think this might have to do something with only iterating once over every room... Any comments on that?
I really hope some of you might be able to point me in the right direction, or tell me what is wrong in my reasoning.
Thank you!
EDIT: Filling up rooms:
public void Generate(int level) {
int levelModifier = (int)(Math.Round ((double)Mathf.Log (level, 2.5f),1) * 10);
int mean = 25;
int SD = 5;
for (int i = 0; i < 30 + levelModifier; i++) {
Vector3 position = getPInCircle (20);
int area = (int)SimpleRNG.GetNormal (mean, SD);
if (area < mean - SD)
continue;
Vector3 size = calculateSize (area);
Room room = new Room (position, size);
rooms.Add (room);
}
SeparateRooms ();
}
Vector3 calculateSize(int area) {
float k = UnityEngine.Random.Range (2.0f, area / 2.0f);
float l = area / k;
int shortSide;
int longSide;
if (Mathf.Round (k) > Mathf.Round (l)) {
shortSide = Mathf.RoundToInt (k);
longSide = Mathf.RoundToInt (l);
} else {
shortSide = Mathf.RoundToInt (l);
longSide = Mathf.RoundToInt (k);
}
//Debug.Log (shortSide);
//Debug.Log (longSide);
Vector3 size;
if (SimpleRNG.GetUniform () < 0.5) {
size = new Vector3 (Actualise(shortSide), 0, Actualise(longSide));
} else {
size = new Vector3 (Actualise(longSide), 0, Actualise(shortSide));
}
return size;
}
The SimpleRNG is John D. Cook's Namespace, open for download at www.codeproject.com/Articles/25172/Simple-Random-Number-Generation

Rotating a circle with mouse with direction

I am rotating a circle with a mouse but I want there to be a limit on how far the circle can be rotated. (Lets say 3 full times). When it reaches it's limit it can no longer be turned in that same direction, but the opposite direction it can be. I got it stopping after the max turns but now I'm trying to find the direction and every time my mouse passes the x-axis of the circle the direction changes because atan2 gives me the angle relative to the x-axis. So the new mouse position is in one quadrant and the last position is in another quadrant so subtracting these angles doesn't give me what I want. Did I explain this well? Any suggestions?
private void HelmPb_MouseMove(object sender, MouseEventArgs e)
{
if ((e.Button != MouseButtons.Left) || _dontTurn) return;
double angle = OffsetAngle();
Point temp = MousePosition;
float degrees = Convert.ToSingle(angle - _offsetAngle);
float diff = _lastHelmAngle - degrees;
float absDiff = Math.Abs(diff) % 360;
if (absDiff > 180) absDiff = 360 - absDiff;
double angle1 = Math.Atan2(_lastHelmPoint.Y, _lastHelmPoint.X);
if (angle1 < 0) angle1 += 2*Math.PI;
double angle2 = Math.Atan2(temp.Y, temp.X);
if (angle2 < 0) angle2 += 2*Math.PI;
double direction = angle1 - angle2;
direction = direction*(180/Math.PI);
_deltaHelmTurn += Convert.ToSingle(absDiff);
if (_deltaHelmTurn >= (_maxHelmTurn*360.0))
{
if (direction > 0 && _lastHelmDirection > 0)
{
_deltaHelmTurn = Convert.ToSingle(_maxHelmTurn*360.0);
degrees = Convert.ToSingle(_maxHelmTurn*360.0)%360;
}
}
_lastHelmDirection = direction;
_lastHelmPoint = MousePosition;
_lastHelmAngle = Convert.ToSingle(degrees);
_sameHelmRotation = Convert.ToSingle(degrees);
HelmPb.Image.Dispose();
WaterDepthPlot.Update();
HelmPb.Image = RotateImage(_originalHelmImage, -degrees);
HelmPb.Update();
}
private double OffsetAngle()
{
int helmXMid = HelmPb.PointToScreen(Point.Empty).X + (HelmPb.Width / 2);
int helmYMid = HelmPb.PointToScreen(Point.Empty).Y + (HelmPb.Height / 2);
double angle = AngleFromPoints(MousePosition, new Point(helmXMid, helmYMid));
return angle;
}
private double AngleFromPoints(Point pt1, Point pt2)
{
Point p = new Point(pt1.X - pt2.X, pt1.Y - pt2.Y);
double alpha;
if (p.Y == 0) alpha = p.X > 0 ? 0d : 180d;
else
{
double f = 1d * p.X / (Math.Sqrt(p.X * p.X + p.Y * p.Y));
alpha = Math.Acos(f) * 180d / Math.PI;
if (p.Y > 0) alpha = 360d - alpha;
}
return alpha;
}
The direction of rotation between two vectors is equivalent to the sign of their cross product.
//Returns 1 for CCW, -1 for CW, 0 for no change
private int Direction(Point from, Point to)
{
double cross = (from.x * to.y) - (from.y * to.x);
return Math.Sign(cross);
}

Categories