public static CSolid ReduceSolid(Solid A) { CSolid s = new CSolid(); s.faces = new List <CFace>(); s.vertices = new List <CPoint>(); foreach (Face a in A.faces) { if (a.isPlanar()) { CFace ca = new CFace(); List <Point> aPoints = a.GetPoints(); ca.points = new int[aPoints.Count]; ca.solid = s; for (int i = 0; i < aPoints.Count; ++i) { int index = s.vertices.FindIndex(x => x.position == aPoints[i].position); if (index == -1) { s.vertices.Add(new CPoint(aPoints[i])); ca.points[i] = s.vertices.Count - 1; } else { ca.points[i] = index; } } s.faces.Add(ca); } else { List <Point> aPoints = a.GetPoints(); for (int i = 0; i < aPoints.Count; ++i) { int index = s.vertices.FindIndex(x => x.position == aPoints[i].position); if (index == -1) { s.vertices.Add(new CPoint(aPoints[i])); } } for (int i = 0; i < a.subTriangles.Count; i += 3) { CFace ct = new CFace(); ct.solid = s; ct.points = new int[] { s.vertices.FindIndex(x => x.position == a.subTriangles[i]), s.vertices.FindIndex(x => x.position == a.subTriangles[i + 1]), s.vertices.FindIndex(x => x.position == a.subTriangles[i + 2]) }; s.faces.Add(ct); } } } return(s); }
public static List <List <Vector3> > CrossSection(CSolid B, CFace a) { Vector3[] bTrans = new Vector3[B.vertices.Count]; Vector3[] aTrans = new Vector3[a.points.Length]; for (int i = 0; i < a.points.Length; ++i) { aTrans[i] = a.solid.vertices[a.points[i]].position; } for (int i = 0; i < B.vertices.Count; ++i) { bTrans[i] = B.vertices[i].position; } aTrans = PolyTransform(aTrans, a); bTrans = PolyTransform(bTrans, a); List <List <Vector3> > edges = new List <List <Vector3> >(); float epsilonSquared = 0.00001f; foreach (CFace b in B.faces) { List <Vector3> intersections = new List <Vector3>(); for (int i = 0; i < b.points.Length; ++i) { int j = (i + 1) % b.points.Length; int pi = b.points[i]; int pj = b.points[j]; if (Mathf.Sign(bTrans[pi].y) != Mathf.Sign(bTrans[pj].y)) { float t = -bTrans[pi].y / (bTrans[pj].y - bTrans[pi].y); intersections.Add(bTrans[pi] + (bTrans[pj] - bTrans[pi]) * t); } } if (intersections.Count > 1) { for (int i = intersections.Count - 1; i >= 0; --i) { for (int j = 0; j < i; ++j) { if ((intersections[j] - intersections[i]).sqrMagnitude <= epsilonSquared) { intersections.RemoveAt(i); i = Mathf.Min(i, intersections.Count - 1); } } } } if (intersections.Count > 1) { Vector3 edgeDir = intersections[intersections.Count - 1] - intersections[0]; intersections.Sort((x, y) => (int)Mathf.Sign(Vector3.Dot(y - x, edgeDir))); for (int i = 0; i < intersections.Count / 2; ++i) { List <Vector3> edge = new List <Vector3>(); edge.Add(intersections[i * 2]); edge.Add(intersections[i * 2 + 1]); edges.Add(edge); } } else if (intersections.Count == 1) { List <Vector3> edge = new List <Vector3>(); edge.Add(intersections[0]); edges.Add(edge); } } for (int i = edges.Count - 1; i >= 0; --i) { for (int j = 0; j < edges.Count; ++j) { if (i == j) { continue; } if ((edges[j][edges[j].Count - 1] - edges[i][0]).sqrMagnitude <= epsilonSquared) { for (int k = 1; k < edges[i].Count; ++k) { edges[j].Add(edges[i][k]); } edges[i].Clear(); edges.RemoveAt(i); break; } else if ((edges[j][edges[j].Count - 1] - edges[i][edges[i].Count - 1]).sqrMagnitude <= epsilonSquared) { for (int k = edges[i].Count - 2; k >= 0; --k) { edges[j].Add(edges[i][k]); } edges[i].Clear(); edges.RemoveAt(i); break; } else if ((edges[j][0] - edges[i][edges[i].Count - 1]).sqrMagnitude <= epsilonSquared) { for (int k = edges[i].Count - 2; k >= 0; --k) { edges[j].Insert(0, edges[i][k]); } edges[i].Clear(); edges.RemoveAt(i); break; } else if ((edges[j][0] - edges[i][0]).sqrMagnitude <= epsilonSquared) { for (int k = 1; k < edges[i].Count - 1; ++k) { edges[j].Insert(0, edges[i][k]); } edges[i].Clear(); edges.RemoveAt(i); break; } } } for (int i = edges.Count - 1; i >= 0; --i) { if (edges[i].Count < 3) { edges.RemoveAt(i); } else if ((edges[i][edges[i].Count - 1] - edges[i][0]).sqrMagnitude <= epsilonSquared) { edges[i].RemoveAt(edges[i].Count - 1); } } return(edges); }
public static Solid Clip(Solid A, Solid B, ClipMode mode) { List <CFace> Ai, Ax, Ao, Bi, Bx, Bo; CSolid Ac = ReduceSolid(A); CSolid Bc = ReduceSolid(B); //Divvy(Ac, Bc, out Ai, out Ax, out Ao); //Divvy(Bc, Ac, out Bi, out Bx, out Bo); Ai = new List <CFace>(); Ao = new List <CFace>(); Bi = new List <CFace>(); Bo = new List <CFace>(); foreach (CFace a in Ac.faces) { Vector3[] p = PolyTransform(a.GetVerts(), a); List <List <Vector3> > cross = CrossSection(Bc, a); foreach (List <Vector3> ql in cross) { Vector3[] q = ql.ToArray(); List <Vector3[]> pi, po; Clip2D(p, q, out pi, out po); foreach (Vector3[] poly in pi) { Ai.Add(Ac.MapFace(DePolyTransform(poly, a))); } foreach (Vector3[] poly in po) { Ao.Add(Ac.MapFace(DePolyTransform(poly, a))); } } if (cross.Count == 0) { if (B.ContainsPoint(Ac.vertices[a.points[0]].position)) { Ai.Add(a); } else { Ao.Add(a); } } } foreach (CFace b in Bc.faces) { Vector3[] p = PolyTransform(b.GetVerts(), b); List <List <Vector3> > cross = CrossSection(Ac, b); foreach (List <Vector3> ql in cross) { Vector3[] q = ql.ToArray(); List <Vector3[]> pi, po; Clip2D(p, q, out pi, out po); foreach (Vector3[] poly in pi) { Bi.Add(Bc.MapFace(DePolyTransform(poly, b))); } foreach (Vector3[] poly in po) { Bo.Add(Bc.MapFace(DePolyTransform(poly, b))); } } if (cross.Count == 0) { if (A.ContainsPoint(Bc.vertices[b.points[0]].position)) { Bi.Add(b); } else { Bo.Add(b); } } } //Set up merged CSolid CSolid Cc; Cc.vertices = new List <CPoint>(); Cc.faces = new List <CFace>(); int ACount = Ac.vertices.Count; Cc.vertices.AddRange(Ac.vertices); Cc.vertices.AddRange(Bc.vertices); //find points that are probably the same int[] map = new int[Cc.vertices.Count]; float epsilon = 0.005f; for (int i = 0; i < Cc.vertices.Count; ++i) { map[i] = -1; } for (int i = Cc.vertices.Count - 1; i >= 0; --i) { for (int j = 0; j <= i; ++j) { if (Vector3.Distance(Cc.vertices[i].position, Cc.vertices[j].position) < epsilon) { if (Cc.vertices[i].id != -1) { Cc.vertices[j].SetId(Cc.vertices[i].id); } map[i] = j; break; } } } //Add verts to merged CSolid if (mode == ClipMode.Subtract) { foreach (CFace f in Ao) { for (int i = 0; i < f.points.Length; ++i) { f.points[i] = map[f.points[i]]; } } Cc.faces.AddRange(Ao); foreach (CFace f in Bi) { for (int i = 0; i < f.points.Length; ++i) { f.points[i] = map[f.points[i] + ACount]; } } Cc.faces.AddRange(Bi); } else if (mode == ClipMode.Add) { //Merge A and B into C, using faces Ao, Bi, adding to point indices of Bi respectively (TODO) } else if (mode == ClipMode.Intersect) { //Merge A and B into C, using faces Ai, Bi, adding to point indices of Bi respectively (TODO) } //Find used vertices bool[] used = new bool[Cc.vertices.Count]; foreach (CFace f in Cc.faces) { foreach (int i in f.points) { used[i] = true; } } //Map duplicate points, create new points, delete unused points Point[] points = new Point[Cc.vertices.Count]; //Sparse Array Dictionary <int, Edge>[] edgeTo = new Dictionary <int, Edge> [Cc.vertices.Count]; //Sparse grid for (int i = 0; i < Cc.vertices.Count; ++i) { if (used[i]) { if (map[i] == i) { points[i] = Cc.vertices[i].id == -1 ? new Point(Cc.vertices[i].position) : DCGBase.all[Cc.vertices[i].id] as Point; Cc.vertices[i].SetId(points[i].elementID); edgeTo[i] = new Dictionary <int, Edge>(); } } else { if (Cc.vertices[i].id != -1) { DCGBase.all[Cc.vertices[i].id].Remove(); } } } //Nuke A and B foreach (Point p in A.getPoints()) { for (int i = p.edges.Count - 1; i >= 0; --i) { p.edges[i].Remove(); } } foreach (Point p in B.getPoints()) { for (int i = p.edges.Count - 1; i >= 0; --i) { p.edges[i].Remove(); } } //Create new edges/faces in DCG Face[] faces = new Face[Cc.faces.Count]; for (int i = 0; i < Cc.faces.Count; ++i) { CFace f = Cc.faces[i]; Edge[] edges = new Edge[f.points.Length]; Debug.Log(f.points.Length); for (int j = 0; j < f.points.Length; ++j) { int k = (j + 1) % f.points.Length; int pk = f.points[k]; int pj = f.points[j]; bool kcj = edgeTo[pk].ContainsKey(pj); bool jck = edgeTo[pj].ContainsKey(pk); if (jck) { edges[j] = edgeTo[pj][pk]; if (!kcj) { edgeTo[pk].Add(pj, edges[j]); } } else if (kcj) { edges[j] = edgeTo[pk][pj]; if (!jck) { edgeTo[pj].Add(pk, edges[j]); } } else { edges[j] = new Edge(points[pj], points[pk]); //if (!jck) edgeTo[pj].Add(pk, edges[j]); //if (!kcj) edgeTo[pk].Add(pj, edges[j]); } //Debug.DrawLine(edges[j].points[0].position, edges[j].points[1].position, new Color(Random.value, Random.value, Random.value), 1000, false); } //Debug.Log(edges.Length); faces[i] = new Face(new List <Edge>(edges)); } //Create new solid from C return(new Solid(new List <Face>(faces))); //return new Solid(); //TODO eventually, if we have time: //Relate generated Faces and Edges to existing Faces and Edges, in order to reduce new objects being created or to copy materials/etc. }