using System; using System.Collections.Generic; namespace Oni.Akira { internal class PolygonUtils { public static List ClipToPlane(List points, Plane plane) { var signs = new int[points.Count]; var negativeCount = 0; var positiveCount = 0; var start = 0; for (int i = 0; i < points.Count; i++) { signs[i] = RelativePosition(points[i], plane); if (signs[i] >= 0) positiveCount++; if (signs[i] <= 0) negativeCount++; } if (negativeCount == points.Count) return null; if (positiveCount == points.Count) return points; var newPoints = new List(); for (int i = 0; i < points.Count; i++) { int i0 = (i + start) % points.Count; int i1 = (i + start + 1) % points.Count; int s0 = signs[i0]; int s1 = signs[i1]; if (s0 >= 0) { newPoints.Add(points[i0]); if (s0 > 0 && s1 < 0) newPoints.Add(Intersect(points[i0], points[i1], plane)); } else { if (s0 < 0 && s1 > 0) newPoints.Add(Intersect(points[i1], points[i0], plane)); } } return newPoints; } private static Vector3 Intersect(Vector3 p0, Vector3 p1, Plane plane) { Vector3 dir = p1 - p0; float dND = plane.DotNormal(dir); if (Math.Abs(dND) < 1e-05f) throw new InvalidOperationException(); float distance = (-plane.D - plane.DotNormal(p0)) / dND; if (distance < 0.0f) { if (distance < -1e-05f) throw new InvalidOperationException(); return p0; } else { return p0 + dir * distance; } } private static int RelativePosition(Vector3 point, Plane plane) { float pos = plane.DotCoordinate(point); if (pos < -1e-05f) return -1; else if (pos > 1e-05f) return 1; else return 0; } } }