ShadowEditor/THREE/Math/Triangle.cs
2018-12-09 15:16:44 +08:00

299 lines
9.1 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using _Math = System.Math;
namespace THREE
{
/// <summary>
/// @author bhouston / http://clara.io
/// @author mrdoob / http://mrdoob.com/
/// @author tengge / https://github.com/tengge1
/// </summary>
public class Triangle
{
public Vector3 a;
public Vector3 b;
public Vector3 c;
public Triangle(Vector3 a = null, Vector3 b = null, Vector3 c = null)
{
this.a = a ?? new Vector3();
this.b = b ?? new Vector3();
this.c = c ?? new Vector3();
}
public Vector3 GetNormal(Vector3 a, Vector3 b, Vector3 c, Vector3 target = null)
{
var v0 = new Vector3();
if (target == null)
{
Console.WriteLine("THREE.Triangle: .getNormal() target is now required");
target = new Vector3();
}
target.SubVectors(c, b);
v0.SubVectors(a, b);
target.Cross(v0);
var targetLengthSq = target.LengthSq();
if (targetLengthSq > 0)
{
return target.MultiplyScalar(1 / _Math.Sqrt(targetLengthSq));
}
return target.Set(0, 0, 0);
}
// static/instance method to calculate barycentric coordinates
// based on: http://www.blackpawn.com/texts/pointinpoly/default.html
public Vector3 GetBarycoord(Vector3 point, Vector3 a, Vector3 b, Vector3 c, Vector3 target = null)
{
var v0 = new Vector3();
var v1 = new Vector3();
var v2 = new Vector3();
v0.SubVectors(c, a);
v1.SubVectors(b, a);
v2.SubVectors(point, a);
var dot00 = v0.Dot(v0);
var dot01 = v0.Dot(v1);
var dot02 = v0.Dot(v2);
var dot11 = v1.Dot(v1);
var dot12 = v1.Dot(v2);
var denom = (dot00 * dot11 - dot01 * dot01);
if (target == null)
{
Console.WriteLine("THREE.Triangle: .getBarycoord() target is now required");
target = new Vector3();
}
// collinear or singular triangle
if (denom == 0)
{
// arbitrary location outside of triangle?
// not sure if this is the best idea, maybe should be returning undefined
return target.Set(-2, -1, -1);
}
var invDenom = 1 / denom;
var u = (dot11 * dot02 - dot01 * dot12) * invDenom;
var v = (dot00 * dot12 - dot01 * dot02) * invDenom;
// barycentric coordinates must always sum to 1
return target.Set(1 - u - v, v, u);
}
public bool ContainsPoint(Vector3 point, Vector3 a, Vector3 b, Vector3 c)
{
var v1 = new Vector3();
this.GetBarycoord(point, a, b, c, v1);
return (v1.x >= 0) && (v1.y >= 0) && ((v1.x + v1.y) <= 1);
}
public Vector2 GetUV(Vector3 point, Vector3 p1, Vector3 p2, Vector3 p3, Vector2 uv1, Vector2 uv2, Vector2 uv3, Vector2 target)
{
var barycoord = new Vector3();
this.GetBarycoord(point, p1, p2, p3, barycoord);
target.Set(0, 0);
target.AddScaledVector(uv1, barycoord.x);
target.AddScaledVector(uv2, barycoord.y);
target.AddScaledVector(uv3, barycoord.z);
return target;
}
public Triangle Set(Vector3 a, Vector3 b, Vector3 c)
{
this.a.Copy(a);
this.b.Copy(b);
this.c.Copy(c);
return this;
}
public Triangle SetFromPointsAndIndices(Vector3[] points, int i0, int i1, int i2)
{
this.a.Copy(points[i0]);
this.b.Copy(points[i1]);
this.c.Copy(points[i2]);
return this;
}
public Triangle Clone()
{
return new Triangle().Copy(this);
}
public Triangle Copy(Triangle triangle)
{
this.a.Copy(triangle.a);
this.b.Copy(triangle.b);
this.c.Copy(triangle.c);
return this;
}
public double GetArea()
{
var v0 = new Vector3();
var v1 = new Vector3();
v0.SubVectors(this.c, this.b);
v1.SubVectors(this.a, this.b);
return v0.Cross(v1).Length() * 0.5;
}
public Vector3 GetMidpoint(Vector3 target = null)
{
if (target == null)
{
Console.WriteLine("THREE.Triangle: .getMidpoint() target is now required");
target = new Vector3();
}
return target.AddVectors(this.a, this.b).Add(this.c).MultiplyScalar(1 / 3);
}
public Vector3 GetNormal(Vector3 target)
{
return this.GetNormal(this.a, this.b, this.c, target);
}
public Plane GetPlane(Plane target = null)
{
if (target == null)
{
Console.WriteLine("THREE.Triangle: .getPlane() target is now required");
target = new Plane();
}
return target.SetFromCoplanarPoints(this.a, this.b, this.c);
}
public Vector3 GetBarycoord(Vector3 point, Vector3 target)
{
return this.GetBarycoord(point, this.a, this.b, this.c, target);
}
public bool ContainsPoint(Vector3 point)
{
return this.ContainsPoint(point, this.a, this.b, this.c);
}
public Vector2 GetUV(Vector3 point, Vector2 uv1, Vector2 uv2, Vector2 uv3, Vector2 result)
{
return this.GetUV(point, this.a, this.b, this.c, uv1, uv2, uv3, result);
}
public bool IntersectsBox(Box3 box)
{
return box.IntersectsTriangle(this);
}
public Vector3 ClosestPointToPoint(Vector3 p, Vector3 target = null)
{
var vab = new Vector3();
var vac = new Vector3();
var vbc = new Vector3();
var vap = new Vector3();
var vbp = new Vector3();
var vcp = new Vector3();
if (target == null)
{
Console.WriteLine("THREE.Triangle: .closestPointToPoint() target is now required");
target = new Vector3();
}
Vector3 a = this.a, b = this.b, c = this.c;
double v, w;
// algorithm thanks to Real-Time Collision Detection by Christer Ericson,
// published by Morgan Kaufmann Publishers, (c) 2005 Elsevier Inc.,
// under the accompanying license; see chapter 5.1.5 for detailed explanation.
// basically, we're distinguishing which of the voronoi regions of the triangle
// the point lies in with the minimum amount of redundant computation.
vab.SubVectors(b, a);
vac.SubVectors(c, a);
vap.SubVectors(p, a);
var d1 = vab.Dot(vap);
var d2 = vac.Dot(vap);
if (d1 <= 0 && d2 <= 0)
{
// vertex region of A; barycentric coords (1, 0, 0)
return target.Copy(a);
}
vbp.SubVectors(p, b);
var d3 = vab.Dot(vbp);
var d4 = vac.Dot(vbp);
if (d3 >= 0 && d4 <= d3)
{
// vertex region of B; barycentric coords (0, 1, 0)
return target.Copy(b);
}
var vc = d1 * d4 - d3 * d2;
if (vc <= 0 && d1 >= 0 && d3 <= 0)
{
v = d1 / (d1 - d3);
// edge region of AB; barycentric coords (1-v, v, 0)
return target.Copy(a).AddScaledVector(vab, v);
}
vcp.SubVectors(p, c);
var d5 = vab.Dot(vcp);
var d6 = vac.Dot(vcp);
if (d6 >= 0 && d5 <= d6)
{
// vertex region of C; barycentric coords (0, 0, 1)
return target.Copy(c);
}
var vb = d5 * d2 - d1 * d6;
if (vb <= 0 && d2 >= 0 && d6 <= 0)
{
w = d2 / (d2 - d6);
// edge region of AC; barycentric coords (1-w, 0, w)
return target.Copy(a).AddScaledVector(vac, w);
}
var va = d3 * d6 - d5 * d4;
if (va <= 0 && (d4 - d3) >= 0 && (d5 - d6) >= 0)
{
vbc.SubVectors(c, b);
w = (d4 - d3) / ((d4 - d3) + (d5 - d6));
// edge region of BC; barycentric coords (0, 1-w, w)
return target.Copy(b).AddScaledVector(vbc, w); // edge region of BC
}
// face region
var denom = 1 / (va + vb + vc);
// u = va * denom
v = vb * denom;
w = vc * denom;
return target.Copy(a).AddScaledVector(vab, v).AddScaledVector(vac, w);
}
public bool Equals(Triangle triangle)
{
return triangle.a.Equals(this.a) && triangle.b.Equals(this.b) && triangle.c.Equals(this.c);
}
}
}