// Note: This method is not optimized! Code is simplified for clarity! // for example: Plane.Distance / Plane.OnSide should be inlined manually and shouldn't use enums, but floating point values directly! public PolygonSplitResult PolygonSplit(Plane cuttingPlane, Vector3 translation, ref Polygon inputPolygon, out Polygon outsidePolygon) { HalfEdge prev = Edges[inputPolygon.FirstIndex]; HalfEdge current = Edges[prev.NextIndex]; HalfEdge next = Edges[current.NextIndex]; HalfEdge last = next; HalfEdge enterEdge = null; HalfEdge exitEdge = null; var prevVertex = Vertices[prev.VertexIndex]; var prevDistance = cuttingPlane.Distance(prevVertex); // distance to previous vertex var prevSide = Plane.OnSide(prevDistance); // side of plane of previous vertex var currentVertex = Vertices[current.VertexIndex]; var currentDistance = cuttingPlane.Distance(currentVertex); // distance to current vertex var currentSide = Plane.OnSide(currentDistance); // side of plane of current vertex do { var nextVertex = Vertices[next.VertexIndex]; var nextDistance = cuttingPlane.Distance(nextVertex); // distance to next vertex var nextSide = Plane.OnSide(nextDistance); // side of plane of next vertex if (prevSide != currentSide) // check if edge crossed the plane ... { if (currentSide != PlaneSideResult.Intersects) // prev:inside/outside - current:inside/outside - next:?? { if (prevSide != PlaneSideResult.Intersects) // prev:inside/outside - current:outside - next:?? { // Calculate intersection of edge with plane split the edge into two, inserting the new vertex var newVertex = Plane.Intersection(prevVertex, currentVertex, prevDistance, currentDistance); var newEdge = EdgeSplit(current, newVertex); if (prevSide == PlaneSideResult.Inside) // prev:inside - current:outside - next:?? { //edge01 exits: // // outside // 1 // * // ......./........ intersect // / // 0 // inside exitEdge = current; } else if (prevSide == PlaneSideResult.Outside) // prev:outside - current:inside - next:?? { //edge01 enters: // // outside // 0 // \ // .......\........ intersect // * // 1 // inside enterEdge = current; } prevDistance = 0; prev = Edges[prev.NextIndex]; prevSide = PlaneSideResult.Intersects; if (exitEdge != null && enterEdge != null) break; current = Edges[prev.NextIndex]; currentVertex = Vertices[current.VertexIndex]; next = Edges[current.NextIndex]; nextVertex = Vertices[next.VertexIndex]; } } else // prev:?? - current:intersects - next:?? { if (prevSide == PlaneSideResult.Intersects || // prev:intersects - current:intersects - next:?? nextSide == PlaneSideResult.Intersects || // prev:?? - current:intersects - next:intersects prevSide == nextSide) // prev:inside/outde - current:intersects - next:inside/outde { if (prevSide == PlaneSideResult.Inside || // prev:inside - current:intersects - next:intersects/inside nextSide == PlaneSideResult.Inside) // prev:intersects/inside - current:intersects - next:inside { // outside // 0 1 // --------*....... intersect // \ // 2 // inside // // outside // 1 2 // ........*------- intersect // / // 0 // inside // // outside // 1 //........*....... intersect // / \ // 0 2 // inside // prevSide = PlaneSideResult.Inside; enterEdge = exitEdge = null; break; } else if (prevSide == PlaneSideResult.Outside || // prev:outside - current:intersects - next:intersects/outside nextSide == PlaneSideResult.Outside) // prev:intersects/outside - current:intersects - next:outside { // outside // 2 // / //..------*....... intersect // 0 1 // inside // // outside // 0 // \ //........*------- intersect // 1 2 // inside // // outside // 0 2 // \ / //........*....... intersect // 1 // inside // prevSide = PlaneSideResult.Outside; enterEdge = exitEdge = null; break; } } else // prev:inside/outside - current:intersects - next:inside/outside { if (prevSide == PlaneSideResult.Inside) // prev:inside - current:intersects - next:outside { //find exit edge: // // outside // 2 // 1 / // ........*....... intersect // / // 0 // inside exitEdge = current; if (enterEdge != null) break; } else // prev:outside - current:intersects - next:inside { //find enter edge: // // outside // 0 // \ 1 // ........*....... intersect // \ // 2 // inside enterEdge = current; if (exitEdge != null) break; } } } } prev = current; current = next; next = Edges[next.NextIndex]; prevDistance = currentDistance; currentDistance = nextDistance; prevSide = currentSide; currentSide = nextSide; prevVertex = currentVertex; currentVertex = nextVertex; } while (next != last); // We should never have only one edge crossing the plane .. Debug.Assert((enterEdge == null) == (exitEdge == null)); // Check if we have an edge that exits and an edge that enters the plane and split the polygon into two if we do if (enterEdge != null && exitEdge != null) { //enter . // . // =====>*-----> // . // //outside . inside // . // <-----*<===== // . // . exit outsidePolygon = new Polygon(); var outsidePolygonIndex = (short)this.Polygons.Count; this.Polygons.Add(outsidePolygon); var outsideEdge = new HalfEdge(); var outsideEdgeIndex = (short)Edges.Count; var insideEdge = new HalfEdge(); var insideEdgeIndex = (short)(outsideEdgeIndex + 1); outsideEdge.TwinIndex = insideEdgeIndex; insideEdge.TwinIndex = outsideEdgeIndex; //insideEdge.PolygonIndex = inputPolygonIndex;// index does not change outsideEdge.PolygonIndex = outsidePolygonIndex; outsideEdge.VertexIndex = exitEdge.VertexIndex; insideEdge.VertexIndex = enterEdge.VertexIndex; outsideEdge.NextIndex = exitEdge.NextIndex; insideEdge.NextIndex = enterEdge.NextIndex; exitEdge.NextIndex = insideEdgeIndex; enterEdge.NextIndex = outsideEdgeIndex; outsidePolygon.FirstIndex = outsideEdgeIndex; inputPolygon.FirstIndex = insideEdgeIndex; outsidePolygon.Visible = inputPolygon.Visible; outsidePolygon.Category = inputPolygon.Category; outsidePolygon.PlaneIndex = inputPolygon.PlaneIndex; Edges.Add(outsideEdge); Edges.Add(insideEdge); // calculate the bounds of the polygons outsidePolygon.Bounds.Clear(); var first = Edges[outsidePolygon.FirstIndex]; var iterator = first; do { outsidePolygon.Bounds.Add(Vertices[iterator.VertexIndex]); iterator.PolygonIndex = outsidePolygonIndex; iterator = Edges[iterator.NextIndex]; } while (iterator != first); inputPolygon.Bounds.Clear(); first = Edges[inputPolygon.FirstIndex]; iterator = first; do { inputPolygon.Bounds.Add(Vertices[iterator.VertexIndex]); iterator = Edges[iterator.NextIndex]; } while (iterator != first); return PolygonSplitResult.Split; } else { outsidePolygon = null; switch (prevSide) { case PlaneSideResult.Inside: return PolygonSplitResult.CompletelyInside; case PlaneSideResult.Outside: return PolygonSplitResult.CompletelyOutside; default: case PlaneSideResult.Intersects: { var polygonPlane = Planes[inputPolygon.PlaneIndex]; var result = Vector3.Dot(polygonPlane.Normal, cuttingPlane.Normal); if (result > 0) return PolygonSplitResult.PlaneAligned; else return PolygonSplitResult.PlaneOppositeAligned; } } } }
static float DistanceToCylinder(Vector3 cy1, Vector3 cy2, Vector3 point) { float distance = 0; if ((cy1 - cy2).Length < 0.0001) { return (cy1 - point).Length; } Plane p = new Plane(); p.normal = cy1 - cy2; p.normal.Normalize(); p.point = cy2; float d1 = p.Distance(point); p.normal = -p.normal; p.point = cy1; float d2 = p.Distance(point); if (float.IsNaN(d2) || float.IsNaN(d1)) { } if (Math.Sign(d1) == Math.Sign(d2)) { // Inside Endpoints Vector3 pt = point - p.normal * d2; distance = (pt - p.point).Length; } else { distance = (float)Math.Min((point - cy1).Length, (point - cy2).Length); } return distance; }
public Intersect CalculateIntersect(Vector3 location, Vector3 direction) { Intersect i = new Intersect(); i.intersectPoint = new Vector3(0, 0, 0); i.intersects = false; Plane plane = new Plane(this); float distance = plane.Distance(location, direction); //Console.WriteLine("Distance = {0}", distance); i.distance = distance; i.intersectPoint = distance * direction + location; if (distance < 0) { i.intersects = false; } else { i.intersects = ObjLoader.IsPointInPolygon(this, i.intersectPoint); } return i; }
/// <summary> /// Trim interior loops from the line /// </summary> public void Clean(Vector3 normal) { List<int> remove = new List<int>(); for (int i = 0; i < indices.Count(); i++) { Vector3 v1 = GetVertex(i); Vector3 v2 = GetVertex((i + 1) % indices.Count()); for (int j = 2; j < indices.Count() - 1; j++) { int j1 = (j + i) % indices.Count(); int j2 = (j + i + 1) % indices.Count(); Vector3 v3 = GetVertex(j1); Vector3 v4 = GetVertex(j2); Vector3 n1 = v2 - v1; Vector3 n2 = v4 - v3; n1.Normalize(); n2.Normalize(); if ((n1 - n2).Length < 0.0001f) { // No crossing, same direction continue; } Vector3 perp = Vector3.Cross(normal, n2); Plane p = new Plane (); //GL.Begin(BeginMode.Lines); //GL.Vertex3(v4); //GL.Vertex3(v4 + perp * 100); //GL.End(); p.normal = perp; p.point = v3; float distance = p.Distance(v1, n1); if (distance > 0 && distance < (v2 - v1).Length && (Vector3.Dot(perp, n1) < 0))//!float.IsInfinity(distance)) { Vector3 point = n1 * distance + v1; if (DistanceToCylinder (v3, v4, point) < 0.01f) { for (int k = 0; k < j; k++) { int removeIndex = (i + 1) % indices.Count(); indices.RemoveAt(removeIndex); } vertices.Add(point); indices.Insert(i + 1, vertices.Count() - 1); //GL.PointSize(15); //GL.Color3(Color.Turquoise); //GL.Begin(BeginMode.Points); //GL.Vertex3(point); // //GL.End(); //GL.PointSize(1); i = i - 1; continue; } } } } LineLoop n = new LineLoop(this); this.indices = n.indices; this.vertices = n.vertices; }
private PolyLine TrianglePlaneIntersect(Face f, Plane p) { PolyLine polyLine = new PolyLine(); float epsilon = 0.01f; // TODO: Auto compute based on scale float epsilon_unit = 0.00001f; // Unit size epsilon value Vector3 f_normal = f.Normal; f_normal.Normalize(); p.normal.Normalize(); if ((f_normal - p.normal).Length < epsilon_unit || (f_normal + p.normal).Length < epsilon_unit) { // No intersection } else { Vector3 expected_direction = Vector3.Cross(f.Normal, p.normal); // Assume we're dealing with triangles only int verts = f.vertices.Count(); if (verts != 3) { throw new Exception("The number of vertices is not 3!"); } float[] d = new float[3]; for (int i = 0; i < 3; i++) { d[i] = p.Distance(f.vertices[i]); } for (int i = 0; i < 3; i++) { // Is the line on the plane? if (Math.Abs(d[i]) < epsilon && Math.Abs(d[(i + 1) % 3]) < epsilon) { polyLine.points.Add(f.vertices[i]); polyLine.points.Add(f.vertices[(i + 1) % 3]); break; } } if (polyLine.points.Count() == 0) { // Line not on a plain: might have an intersection with a point and the opposite line for (int i = 0; i < 3; i++) { float d1 = d[i]; float d2 = d[(i + 1) % 3]; float d3 = d[(i + 2) % 3]; if (Math.Abs(d[i]) < epsilon && Math.Sign(d2) != Math.Sign(d3)) { d2 = Math.Abs(d2); d3 = Math.Abs(d3); // One negative, one positive float total = d2 + d3; Vector3 result = (f.vertices[(i + 1) % 3] * d3 + f.vertices[(i + 2) % 3] * d2) / total; polyLine.points.Add(f.vertices[i]); polyLine.points.Add(result); break; } } if (polyLine.points.Count() == 0) { // No edge in plane and no point + line intersect: maybe two lines intersect? for (int i = 0; i < 3; i++) { // Intersection with an edge if (Math.Sign(d[i]) != Math.Sign(d[(i + 1) % 3])) { float d1 = Math.Abs(d[i]); float d2 = Math.Abs(d[(i + 1) % 3]); float total = d1 + d2; Vector3 result = (f.vertices[i] * d2 + f.vertices[(i + 1) % 3] * d1) / total; polyLine.points.Add(result); if (polyLine.points.Count() == 2) { break; } } } } } if (polyLine.points.Count() >= 2) { //DrawCone1(polyLine.points[0], polyLine.points[1]); Vector3 direction = polyLine.points[1] - polyLine.points[0]; if (Vector3.Dot(direction, expected_direction) < 0) { PolyLine reversed = new PolyLine(); reversed.points.Add(polyLine.points[1]); reversed.points.Add(polyLine.points[0]); polyLine = reversed; } // // // Color[] colors = new Color[] { Color.DarkRed, Color.LightGreen, Color.DarkBlue }; // int i = 0; // GL.Begin(BeginMode.LineLoop); // foreach (Vector3 v in polyLine.points) // { // GL.Color3(colors[i++]); // GL.Vertex3(v); // // } // GL.End(); // // GL.PointSize(10); // GL.Color3(Color.Orange); // GL.Begin(BeginMode.Points); // foreach (Vector3 v in polyLine.points) // { // GL.Vertex3(v); // } // GL.End(); // GL.PointSize(1); } } return polyLine; }