/// <summary> /// Converts a CSG polygon to a poly2tri polygon (including holes) /// </summary> /// <param name="polygon">the polygon to convert</param> /// <returns>a CSG polygon to a poly2tri polygon (including holes)</returns> /// private static Poly2Tri.Polygon fromCSGPolygon(Polygon polygon) { // convert polygon List <Poly2Tri.PolygonPoint> points = new List <Poly2Tri.PolygonPoint>(); foreach (Vertex v in polygon.vertices) { Poly2Tri.PolygonPoint vp = new Poly2Tri.PolygonPoint(v.pos.x(), v.pos.y(), v.pos.z()); points.Add(vp); } Poly2Tri.Polygon result = new Poly2Tri.Polygon(points); // convert holes List <Polygon> holesOfPresult = polygon.getStorage().getValue <List <Polygon> >(Edge.KEY_POLYGON_HOLES); if (holesOfPresult != null) { List <Polygon> holesOfP = holesOfPresult; holesOfP.ForEach(hP => result.addHole(fromCSGPolygon(hP))); } return(result); }
/// <summary> /// Combines two polygons into one CSG object. Polygons p1 and p2 are treated as top and /// bottom of a tube segment with p1 and p2 as the profile. <b>Note:</b> both polygons must have the /// same number of vertices. This method does not guarantee intersection-free CSGs. It is in the /// responsibility of the caller to ensure that the orientation of p1 and p2 allow for /// intersection-free combination of both. /// </summary> /// <param name="p1">first polygon</param> /// <param name="p2">second polygon</param> /// <param name="bottom">defines whether to close the bottom of the tube</param> /// <param name="top">defines whether to close the top of the tube</param> /// <returns>List of polygons</returns> /// public static List <Polygon> combine(Polygon p1, Polygon p2, bool bottom, bool top) { List <Polygon> newPolygons = new List <Polygon>(); if (p1.vertices.Count != p2.vertices.Count) { throw new Exception("Polygons must have the same number of vertices"); } int numVertices = p1.vertices.Count; if (bottom) { newPolygons.Add(p1.flipped()); } for (int i = 0; i < numVertices; i++) { int nexti = (i + 1) % numVertices; IVector3d bottomV1 = p1.vertices[i].pos; IVector3d topV1 = p2.vertices[i].pos; IVector3d bottomV2 = p1.vertices[nexti].pos; IVector3d topV2 = p2.vertices[nexti].pos; List <IVector3d> pPoints; pPoints = new List <IVector3d> { bottomV2, topV2, topV1 }; newPolygons.Add(Polygon.fromPoints(pPoints, p1.getStorage())); pPoints = new List <IVector3d> { bottomV2, topV1, bottomV1 }; newPolygons.Add(Polygon.fromPoints(pPoints, p1.getStorage())); } if (top) { newPolygons.Add(p2); } return(newPolygons); }
public static List <Polygon> concaveToConvex(Polygon concave) { List <Polygon> result = new List <Polygon>(); IVector3d normal = concave.vertices[0].normal.clone(); bool cw = !Extrude.isCCW(concave); Poly2Tri.Polygon p = fromCSGPolygon(concave); Poly2Tri.Poly2Tri.triangulate(p); List <Poly2Tri.DelaunayTriangle> triangles = p.getTriangles(); List <Vertex> triPoints = new List <Vertex>(); foreach (Poly2Tri.DelaunayTriangle t in triangles) { int counter = 0; foreach (Poly2Tri.TriangulationPoint tp in t.points) { triPoints.Add(new Vertex( Vector3d.xyz(tp.getX(), tp.getY(), tp.getZ()), normal)); if (counter == 2) { if (!cw) { triPoints.Reverse(); } Polygon poly = new Polygon( triPoints, concave.getStorage()); result.Add(poly); counter = 0; triPoints = new List <Vertex>(); } else { counter++; } } } return(result); }
private static CSG extrude(IVector3d dir, Polygon polygon1) { List <Polygon> newPolygons = new List <Polygon>(); if (dir.z() < 0) { throw new ArgumentException("z < 0 currently not supported for extrude: " + dir); } newPolygons.AddRange(PolygonUtil.concaveToConvex(polygon1)); Polygon polygon2 = polygon1.translated(dir); int numvertices = polygon1.vertices.Count; for (int i = 0; i < numvertices; i++) { int nexti = (i + 1) % numvertices; IVector3d bottomV1 = polygon1.vertices[i].pos; IVector3d topV1 = polygon2.vertices[i].pos; IVector3d bottomV2 = polygon1.vertices[nexti].pos; IVector3d topV2 = polygon2.vertices[nexti].pos; List <IVector3d> pPoints = new List <IVector3d> { bottomV2, topV2, topV1, bottomV1 }; newPolygons.Add(Polygon.fromPoints(pPoints, polygon1.getStorage())); } polygon2 = polygon2.flipped(); List <Polygon> topPolygons = PolygonUtil.concaveToConvex(polygon2); newPolygons.AddRange(topPolygons); return(CSG.fromPolygons(newPolygons)); }
private static List <Polygon> extrude(IVector3d dir, Polygon polygon1, bool top, bool bottom) { List <Polygon> newPolygons = new List <Polygon>(); if (bottom) { newPolygons.AddRange(PolygonUtil.concaveToConvex(polygon1)); } Polygon polygon2 = polygon1.translated(dir); Transform rot = Transform.unity(); IVector3d a = polygon2.getPlane().getNormal().normalized(); IVector3d b = dir.normalized(); IVector3d c = a.crossed(b); double l = c.magnitude(); // sine of angle if (l > 1e-9) { IVector3d axis = c.times(1.0 / l); double angle = a.angle(b); double sx = 0; double sy = 0; double sz = 0; int n = polygon2.vertices.Count; foreach (Vertex v in polygon2.vertices) { sx += v.pos.x(); sy += v.pos.y(); sz += v.pos.z(); } IVector3d center = Vector3d.xyz(sx / n, sy / n, sz / n); rot = rot.rot(center, axis, angle * Math.PI / 180.0); foreach (Vertex v in polygon2.vertices) { v.pos = rot.transform(v.pos); } } int numvertices = polygon1.vertices.Count; for (int i = 0; i < numvertices; i++) { int nexti = (i + 1) % numvertices; IVector3d bottomV1 = polygon1.vertices[i].pos; IVector3d topV1 = polygon2.vertices[i].pos; IVector3d bottomV2 = polygon1.vertices[nexti].pos; IVector3d topV2 = polygon2.vertices[nexti].pos; List <IVector3d> pPoints = new List <IVector3d> { bottomV2, topV2, topV1, bottomV1 }; newPolygons.Add(Polygon.fromPoints(pPoints, polygon1.getStorage())); } polygon2 = polygon2.flipped(); List <Polygon> topPolygons = PolygonUtil.concaveToConvex(polygon2); if (top) { newPolygons.AddRange(topPolygons); } return(newPolygons); }
/// <summary> /// Splits a <see cref="Polygon"/> by this plane if needed. After that it puts the /// polygons or the polygon fragments in the appropriate lists /// (<c>front</c>, <c>back</c>). Coplanar polygons go into either /// <c>coplanarFront</c>, <c>coplanarBack</c> depending on their /// orientation with respect to this plane. Polygons in front or back of this /// plane go into either <c>front</c> or <c>back</c>. /// </summary> /// <param name="polygon">polygon to split</param> /// <param name="coplanarFront">"coplanar front" polygons</param> /// <param name="coplanarBack">"coplanar back" polygons</param> /// <param name="front">front polygons</param> /// <param name="back">back polgons</param> /// public void splitPolygon( Polygon polygon, List <Polygon> coplanarFront, List <Polygon> coplanarBack, List <Polygon> front, List <Polygon> back) { const int COPLANAR = 0; const int FRONT = 1; const int BACK = 2; const int SPANNING = 3; // == some in the FRONT + some in the BACK // Classify each point as well as the entire polygon into one of the // above four classes. int polygonType = 0; List <int> types = new List <int>(polygon.vertices.Count); for (int i = 0; i < polygon.vertices.Count; i++) { double t = this.normal.dot(polygon.vertices[i].pos) - this.dist; int type = (t < -Plane.EPSILON) ? BACK : (t > Plane.EPSILON) ? FRONT : COPLANAR; polygonType |= type; types.Add(type); } //System.out.println("> switching"); // Put the polygon in the correct list, splitting it when necessary. switch (polygonType) { case COPLANAR: //System.out.println(" -> coplanar"); (this.normal.dot(polygon._csg_plane.normal) > 0 ? coplanarFront : coplanarBack).Add(polygon); break; case FRONT: //System.out.println(" -> front"); front.Add(polygon); break; case BACK: //System.out.println(" -> back"); back.Add(polygon); break; case SPANNING: //System.out.println(" -> spanning"); List <Vertex> f = new List <Vertex>(); List <Vertex> b = new List <Vertex>(); for (int i = 0; i < polygon.vertices.Count; i++) { int j = (i + 1) % polygon.vertices.Count; int ti = types[i]; int tj = types[j]; Vertex vi = polygon.vertices[i]; Vertex vj = polygon.vertices[j]; if (ti != BACK) { f.Add(vi); } if (ti != FRONT) { b.Add(ti != BACK ? vi.clone() : vi); } if ((ti | tj) == SPANNING) { double t = (this.dist - this.normal.dot(vi.pos)) / this.normal.dot(vj.pos.minus(vi.pos)); Vertex v = vi.interpolate(vj, t); f.Add(v); b.Add(v.clone()); } } if (f.Count >= 3) { front.Add(new Polygon(f, polygon.getStorage())); } if (b.Count >= 3) { back.Add(new Polygon(b, polygon.getStorage())); } break; } }