public void SetPoints(List <Vector3m> points, List <List <Vector3m> > holes = null, Vector3m normal = null) { if (points == null || points.Count < 3) { throw new ArgumentException("No list or an empty list passed"); } if (normal == null) { CalcNormal(points); } else { Normal = normal; } _mainPointList = new Polygon(); LinkAndAddToList(_mainPointList, points); if (holes != null) { _holes = new List <Polygon>(); for (int i = 0; i < holes.Count; i++) { Polygon p = new Polygon(); LinkAndAddToList(p, holes[i]); _holes.Add(p); } } Result = new List <Vector3m>(); }
public static Vector3m PlaneNormal(Vector3m v0, Vector3m v1, Vector3m v2) { Vector3m a = v1 - v0; Vector3m b = v2 - v0; return(a.Cross(b)); }
private ConnectionEdge FindLargest(Polygon testHole) { Rational maximum = 0; ConnectionEdge maxEdge = null; Vector3m v0 = testHole.Start.Origin; Vector3m v1 = testHole.Start.Next.Origin; foreach (var connectionEdge in testHole.GetPolygonCirculator()) { // we take the first two points as a reference line if (Misc.GetOrientation(v0, v1, connectionEdge.Origin, Normal) < 0) { var r = Misc.PointLineDistance(v0, v1, connectionEdge.Origin); if (r > maximum) { maximum = r; maxEdge = connectionEdge; } } } if (maxEdge == null) { return(testHole.Start); } return(maxEdge); }
public Vector3m Cross(Vector3m a) { return(new Vector3m( this.Y * a.Z - this.Z * a.Y, this.Z * a.X - this.X * a.Z, this.X * a.Y - this.Y * a.X )); }
public static bool PointInOrOnTriangle(Vector3m prevPoint, Vector3m curPoint, Vector3m nextPoint, Vector3m nonConvexPoint, Vector3m normal) { var res0 = Misc.GetOrientation(prevPoint, nonConvexPoint, curPoint, normal); var res1 = Misc.GetOrientation(curPoint, nonConvexPoint, nextPoint, normal); var res2 = Misc.GetOrientation(nextPoint, nonConvexPoint, prevPoint, normal); return(res0 != 1 && res1 != 1 && res2 != 1); }
// calculating normal using Newell's method private void CalcNormal(List <Vector3m> points) { Vector3m normal = Vector3m.Zero(); for (var i = 0; i < points.Count; i++) { var j = (i + 1) % (points.Count); normal.X += (points[i].Y - points[j].Y) * (points[i].Z + points[j].Z); normal.Y += (points[i].Z - points[j].Z) * (points[i].X + points[j].X); normal.Z += (points[i].X - points[j].X) * (points[i].Y + points[j].Y); } Normal = normal; }
public bool Contains(Vector3m vector2M, out Vector3m res) { foreach (var connectionEdge in GetPolygonCirculator()) { if (connectionEdge.Origin.Equals(vector2M)) { res = connectionEdge.Origin; return(true); } } res = null; return(false); }
public static int GetOrientation(Vector3m v0, Vector3m v1, Vector3m v2, Vector3m normal) { var res = (v0 - v1).Cross(v2 - v1); if (res.LengthSquared() == 0) { return(0); } if (res.X.Sign != normal.X.Sign || res.Y.Sign != normal.Y.Sign || res.Z.Sign != normal.Z.Sign) { return(1); } return(-1); }
public static int GetOrientation(Vector3m v0, Vector3m v1, Vector3m v2, Vector3m normal) { var res = (v0 - v1).Cross(v2 - v1); if (res.LengthSquared() == 0) { return(0); } if (Math.Sign(res.X) != Math.Sign(normal.X) || Math.Sign(res.Y) != Math.Sign(normal.Y) || Math.Sign(res.Z) != Math.Sign(normal.Z)) { return(1); } return(-1); }
private bool IsPointInTriangle(Vector3m prevPoint, Vector3m curPoint, Vector3m nextPoint, List <ConnectionEdge> nonConvexPoints) { foreach (var nonConvexPoint in nonConvexPoints) { if (nonConvexPoint.Origin == prevPoint || nonConvexPoint.Origin == curPoint || nonConvexPoint.Origin == nextPoint) { continue; } if (Misc.PointInOrOnTriangle(prevPoint, curPoint, nextPoint, nonConvexPoint.Origin, Normal)) { return(true); } } return(false); }
public bool RaySegmentIntersection(out Vector3m intersection, out Rational distanceSquared, Vector3m linePoint1, Vector3m lineVec1, Vector3m linePoint3, Vector3m linePoint4, Vector3m direction) { var lineVec2 = linePoint4 - linePoint3; Vector3m lineVec3 = linePoint3 - linePoint1; Vector3m crossVec1and2 = lineVec1.Cross(lineVec2); Vector3m crossVec3and2 = lineVec3.Cross(lineVec2); var res = Misc.PointLineDistance(linePoint3, linePoint4, linePoint1); if (res == 0) // line and ray are collinear { var p = linePoint1 + lineVec1; var res2 = Misc.PointLineDistance(linePoint3, linePoint4, p); if (res2 == 0) { var s = linePoint3 - linePoint1; if (s.X == direction.X && s.Y == direction.Y && s.Z == direction.Z) { intersection = linePoint3; distanceSquared = s.LengthSquared(); return(true); } } } //is coplanar, and not parallel if (/*planarFactor == 0.0f && */ crossVec1and2.LengthSquared() > 0) { var s = crossVec3and2.Dot(crossVec1and2) / crossVec1and2.LengthSquared(); if (s >= 0) { intersection = linePoint1 + (lineVec1 * s); distanceSquared = (lineVec1 * s).LengthSquared(); if ((intersection - linePoint3).LengthSquared() + (intersection - linePoint4).LengthSquared() <= lineVec2.LengthSquared()) { return(true); } } } intersection = Vector3m.Zero(); distanceSquared = 0; return(false); }
private ConnectionEdge FindMinimumAngle(List <ConnectionEdge> candidates, Vector3m M, Vector3m direction) { Rational angle = -double.MaxValue; ConnectionEdge result = null; foreach (var R in candidates) { var a = direction; var b = R.Origin - M; var num = a.Dot(b) * a.Dot(b); var denom = b.Dot(b); var res = num / denom; if (res > angle) { result = R; angle = res; } } return(result); }
public List <short> TriangulateAndReturnIndices() { EarClipping EarClipper = new EarClipping(); EarClipper.SetPoints(Vertices, null, Normal); EarClipper.Triangulate(); var Result = EarClipper.Result; List <short> Indices = new List <short>(); for (short Index = 0; Index < Result.Count; Index++) { Vector3m Vertex = Result[Index]; for (short IndexPoint = 0; IndexPoint < Vertices.Count; IndexPoint++) { if (Vertex.Equals(Vertices[IndexPoint])) { Indices.Add(IndexPoint); } } } return(Indices); }
// Is testPoint between a and b in ccw order? // > 0 if strictly yes // < 0 if strictly no // = 0 if testPoint lies either on a or on b public static int IsBetween(Vector3m Origin, Vector3m a, Vector3m b, Vector3m testPoint, Vector3m normal) { var psca = GetOrientation(Origin, a, testPoint, normal); var pscb = GetOrientation(Origin, b, testPoint, normal); // where does b in relation to a lie? Left, right or collinear? var psb = GetOrientation(Origin, a, b, normal); if (psb > 0) // left { // if left then testPoint lies between a and b iff testPoint left of a AND testPoint right of b if (psca > 0 && pscb < 0) { return(1); } if (psca == 0) { var t = a - Origin; var t2 = testPoint - Origin; if (Math.Sign(t.X) != Math.Sign(t2.X) || Math.Sign(t.Y) != Math.Sign(t2.Y)) { return(-1); } return(0); } else if (pscb == 0) { var t = b - Origin; var t2 = testPoint - Origin; if (Math.Sign(t.X) != Math.Sign(t2.X) || Math.Sign(t.Y) != Math.Sign(t2.Y)) { return(-1); } return(0); } } else if (psb < 0) // right { // if right then testPoint lies between a and b iff testPoint left of a OR testPoint right of b if (psca > 0 || pscb < 0) { return(1); } if (psca == 0) { var t = a - Origin; var t2 = testPoint - Origin; if (Math.Sign(t.X) != Math.Sign(t2.X) || Math.Sign(t.Y) != Math.Sign(t2.Y)) { return(1); } return(0); } else if (pscb == 0) { var t = b - Origin; var t2 = testPoint - Origin; if (Math.Sign(t.X) != Math.Sign(t2.X) || Math.Sign(t.Y) != Math.Sign(t2.Y)) { return(1); } return(0); } } else if (psb == 0) { if (psca > 0) { return(1); } else if (psca < 0) { return(-1); } else { return(0); } } return(-1); }
/** * 建立自訂義的Mesh */ public void CreateEarClippingMesh(List <Vector3> posList, float height, MeshFilter meshFilter) { Mesh mesh = new Mesh(); meshFilter.mesh = mesh; mesh.Clear(); //EarClipping EarClipperLib.EarClipping earClipping = new EarClipperLib.EarClipping(); List <EarClipperLib.Vector3m> points = new List <EarClipperLib.Vector3m>(); for (int i = 0; i < posList.Count; i++) { EarClipperLib.Vector3m point = new EarClipperLib.Vector3m(posList[i].x, posList[i].y, posList[i].z); points.Add(point); } earClipping.SetPoints(points); earClipping.Triangulate(); var res = earClipping.Result; res.Reverse(); int vert = 0; #region Vertices Vector3[] vertices = new Vector3[res.Count * 2 + 6 * posList.Count]; //Top cap for (int i = 0; i < res.Count; i += 3) { vertices[vert++] = new Vector3(res[i].X, res[i].Y, res[i].Z); vertices[vert++] = new Vector3(res[i + 1].X, res[i + 1].Y, res[i + 1].Z); vertices[vert++] = new Vector3(res[i + 2].X, res[i + 2].Y, res[i + 2].Z); } //Buttom cap for (int i = 0; i < res.Count; i += 3) { vertices[vert++] = new Vector3(res[i + 2].X, res[i + 2].Y, res[i + 2].Z) - Vector3.up * height; vertices[vert++] = new Vector3(res[i + 1].X, res[i + 1].Y, res[i + 1].Z) - Vector3.up * height; vertices[vert++] = new Vector3(res[i].X, res[i].Y, res[i].Z) - Vector3.up * height; } // Sides for (int i = 0; i < posList.Count; i++) { vertices[vert++] = posList[i]; vertices[vert++] = posList[(i + 1) % posList.Count]; vertices[vert++] = posList[i] - Vector3.up * height; vertices[vert++] = posList[i] - Vector3.up * height; vertices[vert++] = posList[(i + 1) % posList.Count]; vertices[vert++] = posList[(i + 1) % posList.Count] - Vector3.up * height; } #endregion #region Normales Vector3[] normales = new Vector3[vertices.Length]; vert = 0; for (int i = 0; i < normales.Length; i += 3) { Vector3 nor = Vector3.Cross(vertices[i + 1] - vertices[i], vertices[i + 2] - vertices[i]).normalized; for (int j = 0; j < 3; j++) { normales[vert++] = nor; } } #endregion #region Triangles int[] triangles = new int[vertices.Length]; vert = 0; for (int i = 0; i < triangles.Length; i++) { triangles[vert++] = i; } #endregion mesh.vertices = vertices; mesh.normals = normales; mesh.triangles = triangles; mesh.RecalculateBounds(); }
private Candidate FindPointI(ConnectionEdge M, List <Polygon> polygons, int holeIndex, Vector3m direction) { Candidate candidate = new Candidate(); for (int i = 0; i < polygons.Count; i++) { if (i == holeIndex) // Don't test the hole with itself { continue; } foreach (var connectionEdge in polygons[i].GetPolygonCirculator()) { Rational rayDistanceSquared; Vector3m intersectionPoint; if (RaySegmentIntersection(out intersectionPoint, out rayDistanceSquared, M.Origin, direction, connectionEdge.Origin, connectionEdge.Next.Origin, direction)) { if (rayDistanceSquared == candidate.currentDistance) // if this is an M/I edge, then both edge and his twin have the same distance; we take the edge where the point is on the left side { if (Misc.GetOrientation(connectionEdge.Origin, connectionEdge.Next.Origin, M.Origin, Normal) == 1) { candidate.currentDistance = rayDistanceSquared; candidate.Origin = connectionEdge; candidate.PolyIndex = i; candidate.I = intersectionPoint; } } else if (rayDistanceSquared < candidate.currentDistance) { candidate.currentDistance = rayDistanceSquared; candidate.Origin = connectionEdge; candidate.PolyIndex = i; candidate.I = intersectionPoint; } } } } return(candidate); }
private ConnectionEdge FindVisiblePoint(Candidate I, List <Polygon> polygons, ConnectionEdge M, Vector3m direction) { ConnectionEdge P = null; if (I.Origin.Origin.X > I.Origin.Next.Origin.X) { P = I.Origin; } else { P = I.Origin.Next; } List <ConnectionEdge> nonConvexPoints = FindNonConvexPoints(polygons[I.PolyIndex]); nonConvexPoints.Remove(P); var m = M.Origin; var i = I.I; var p = P.Origin; List <ConnectionEdge> candidates = new List <ConnectionEdge>(); // invert i and p if triangle is oriented CW if (Misc.GetOrientation(m, i, p, Normal) == -1) { var tmp = i; i = p; p = tmp; } foreach (var nonConvexPoint in nonConvexPoints) { if (Misc.PointInOrOnTriangle(m, i, p, nonConvexPoint.Origin, Normal)) { candidates.Add(nonConvexPoint); } } if (candidates.Count == 0) { return(P); } return(FindMinimumAngle(candidates, m, direction)); }
public Vector3m Plus(Vector3m a) { return(new Vector3m(this.X + a.X, this.Y + a.Y, this.Z + a.Z)); }
public void Triangulate() { if (Normal.Equals(Vector3m.Zero())) { throw new Exception("The input is not a valid polygon"); } if (_holes != null && _holes.Count > 0) { ProcessHoles(); } List <ConnectionEdge> nonConvexPoints = FindNonConvexPoints(_mainPointList); if (nonConvexPoints.Count == _mainPointList.PointCount) { throw new ArgumentException("The triangle input is not valid"); } while (_mainPointList.PointCount > 2) { bool guard = false; foreach (var cur in _mainPointList.GetPolygonCirculator()) { if (!IsConvex(cur)) { continue; } if (!IsPointInTriangle(cur.Prev.Origin, cur.Origin, cur.Next.Origin, nonConvexPoints)) { // cut off ear guard = true; Result.Add(cur.Prev.Origin); Result.Add(cur.Origin); Result.Add(cur.Next.Origin); // Check if prev and next are still nonconvex. If not, then remove from non convex list if (IsConvex(cur.Prev)) { int index = nonConvexPoints.FindIndex(x => x == cur.Prev); if (index >= 0) { nonConvexPoints.RemoveAt(index); } } if (IsConvex(cur.Next)) { int index = nonConvexPoints.FindIndex(x => x == cur.Next); if (index >= 0) { nonConvexPoints.RemoveAt(index); } } _mainPointList.Remove(cur); break; } } if (PointsOnLine(_mainPointList)) { break; } if (!guard) { throw new Exception("No progression. The input must be wrong"); } } }
public Vector3m(Vector3m v) { X = v.X; Y = v.Y; Z = v.Z; }
public Vector3m Lerp(Vector3m a, float t) { return(this.Plus(a.Minus(this).Times(t))); }
public ConnectionEdge(Vector3m p0, Polygon parentPolygon) { Origin = p0; Polygon = parentPolygon; AddIncidentEdge(this); }
public Vector3m Minus(Vector3m a) { return(new Vector3m(this.X - a.X, this.Y - a.Y, this.Z - a.Z)); }
public Vector3m Lerp(Vector3m a, Rational t) { return(this.Plus(a.Minus(this).Times(t))); }
public Rational Dot(Vector3m a) { return(this.X * a.X + this.Y * a.Y + this.Z * a.Z); }
public void SetNormal(double X, double Y, double Z) { Normal = new Vector3m(X, Y, Z); }
public bool SameDirection(Vector3m he) { var res = this.Cross(he); return(res.X == 0 && res.Y == 0 && res.Z == 0); }
public static float PointLineDistance(Vector3m p1, Vector3m p2, Vector3m p3) { return((p2 - p1).Cross(p3 - p1).LengthSquared()); }
public float Dot(Vector3m a) { return(this.X * a.X + this.Y * a.Y + this.Z * a.Z); }