/// <summary> Clip this triangle by a plane </summary> /// <param name="plane">The clipping plane to clip the triangle with</param> /// <returns>The points that are left after clipping the triangle</returns> public Vector3[] GetClippedPoints(AxisAlignedPlane plane) { Vector3 v0 = P1 - plane.Position, v1 = P2 - plane.Position, v2 = P3 - plane.Position, v3; const float clipEpsilon = 0.00001f, clipEpsilon2 = 0.01f; // Distances to the plane (this is an array parallel to v[], stored as a vec3) Vector3 dist = new Vector3(Vector3.Dot(v0, plane.Normal), Vector3.Dot(v1, plane.Normal), Vector3.Dot(v2, plane.Normal)); if (dist.X < clipEpsilon2 && dist.Y < clipEpsilon2 && dist.Z < clipEpsilon2) { // Case 1 (all clipped) return(Array.Empty <Vector3>()); } if (dist.X > -clipEpsilon && dist.Y > -clipEpsilon && dist.Z > -clipEpsilon) { // Case 2 (none clipped) return(new Vector3[] { v0, v1, v2 }); } // There are either 1 or 2 vertices above the clipping plane bool above0 = dist.X >= 0; bool above1 = dist.Y >= 0; bool above2 = dist.Z >= 0; bool nextIsAbove; // Find the CCW - most vertex above the plane if (above1 && !above0) { // Cycle once CCW. Use v3 as a temp nextIsAbove = above2; v3 = v0; v0 = v1; v1 = v2; v2 = v3; dist = new Vector3(dist.Y, dist.Z, dist.X); } else if (above2 && !above1) { // Cycle once CW. Use v3 as a temp nextIsAbove = above0; v3 = v2; v2 = v1; v1 = v0; v0 = v3; dist = new Vector3(dist.Z, dist.X, dist.Y); } else { nextIsAbove = above1; } // We always need to clip v2 - v0 v3 = Vector3.Lerp(v0, v2, dist[0] / (dist[0] - dist[2])); if (nextIsAbove) { // Case 3 (quadrilateral) v2 = Vector3.Lerp(v1, v2, dist[1] / (dist[1] - dist[2])); return(new Vector3[] { v0, v1, v2, v3 }); } else { // Case 4 (triangle) v1 = Vector3.Lerp(v0, v1, dist[0] / (dist[0] - dist[1])); v2 = v3; return(new Vector3[] { v0, v1, v2 }); } }
public void GetClippedPoints_AllClipped() { AxisAlignedPlane plane = new AxisAlignedPlane(new Vector3(0, 0, -1), Vector3.Zero); for (int i = 0; i < 100; i++) { Triangle triangle = Utils.Random.Triangle(0f, 1f); Vector3[] points = triangle.GetClippedPoints(plane); Assert.AreEqual(points.Length, 0); } }
public void Clip() { for (int i = 0; i < 100; i++) { Primitive primitive = Utils.Random.Sphere(); Vector3[] bounds = primitive.Bounds; AxisAlignedPlane plane = new AxisAlignedPlane(Utils.Random.UnitVector(), primitive.Position); PrimitiveFragment fragment = primitive.Clip(plane); Vector3[] clipBounds = fragment.Bounds; Assert.IsTrue(bounds[0] != clipBounds[0] || bounds[1] != clipBounds[1]); } }
/// <summary> Clip the <see cref="PrimitiveFragment"/> by a <paramref name="plane"/></summary> /// <param name="plane">The <see cref="AxisAlignedPlane"/> to clip the <see cref="PrimitiveFragment"/> with</param> /// <returns>A new <see cref="PrimitiveFragment"/> with clipped bounds</returns> public override IEnumerable <PrimitiveFragment> Clip(AxisAlignedPlane plane) { foreach (IShape shape in Shape.Clip(plane)) { if (shape == Shape) { yield return(this); } else { yield return(new PrimitiveFragment(Original, shape)); } } }
public virtual IEnumerable <ISceneObject> Clip(AxisAlignedPlane plane) { foreach (IShape shape in Shape.Clip(plane)) { if (shape == Shape) { yield return(this); } else { yield return(new PrimitiveFragment(this, shape)); } } }
/// <summary> Clip the AABB of the primitive with an axis-aligned plane </summary> /// <param name="plane">The plane to clip the AABB with</param> /// <returns>The bounds of the clipped AABB</returns> public virtual PrimitiveFragment?Clip(AxisAlignedPlane plane) { Vector3[] bounds = Bounds; Vector3 min = bounds[0]; Vector3 max = bounds[1]; if (plane.Normal == Vector3.UnitX) { min.X = Math.Max(min.X, plane.Position.X); } else if (plane.Normal == -Vector3.UnitX) { max.X = Math.Min(max.X, plane.Position.X); } else if (plane.Normal == Vector3.UnitY) { min.Y = Math.Max(min.Y, plane.Position.Y); } else if (plane.Normal == -Vector3.UnitY) { max.Y = Math.Min(max.Y, plane.Position.Y); } else if (plane.Normal == Vector3.UnitZ) { min.Z = Math.Max(min.Z, plane.Position.Z); } else if (plane.Normal == -Vector3.UnitZ) { max.Z = Math.Min(max.Z, plane.Position.Z); } else { throw new ArgumentException("Can't clip if plane is not axis-aligned"); } if (max.X < min.X || max.Y < min.Y || max.Z < min.Z) { return(null); } else { return(new PrimitiveFragment(this, new Vector3[] { min, max })); } }
public void GetClippedPoints_TwoClipped() { Vector3 P1 = new Vector3(1, 0, 1); Vector3 P2 = new Vector3(0, 0, 1); Vector3 P3 = new Vector3(0, 0, -1); Triangle triangle = new Triangle(P1, P2, P3); AxisAlignedPlane plane = new AxisAlignedPlane(new Vector3(0, 0, 1), Vector3.Zero); List <Vector3> points = new List <Vector3>(triangle.GetClippedPoints(plane)); Assert.AreEqual(points.Count, 4); CollectionAssert.Contains(points, triangle.P1); CollectionAssert.Contains(points, triangle.P2); CollectionAssert.DoesNotContain(points, triangle.P3); Vector3 P31 = Vector3.Lerp(P3, P2, 0.5f); Vector3 P32 = Vector3.Lerp(P3, P1, 0.5f); CollectionAssert.Contains(points, P31); CollectionAssert.Contains(points, P32); }
public void GetClippedPoints_OneClipped() { Vector3 P1 = new Vector3(1, 0, 1); Vector3 P2 = new Vector3(0, 0, 1); Vector3 P3 = new Vector3(0, 0, -1); Triangle triangle = new Triangle(P1, P2, P3); AxisAlignedPlane plane = new AxisAlignedPlane(new Vector3(0, 0, -1), Vector3.Zero); Vector3[] points = triangle.GetClippedPoints(plane); Assert.AreEqual(points.Length, 3); CollectionAssert.DoesNotContain(points, triangle.P1); CollectionAssert.DoesNotContain(points, triangle.P2); CollectionAssert.Contains(points, triangle.P3); Vector3 P11 = Vector3.Lerp(P1, P3, 0.5f); Vector3 P21 = Vector3.Lerp(P2, P3, 0.5f); CollectionAssert.Contains(points, P11); CollectionAssert.Contains(points, P21); }
/// <summary> Clip the <see cref="AxisAlignedBox"/> by a <paramref name="plane"/> </summary> /// <param name="plane">The <see cref="AxisAlignedPlane"/> to clip the <see cref="AxisAlignedBox"/> with</param> /// <returns>A new clipped <see cref="AxisAlignedBox"/> if it's not clipped entirely</returns> public IEnumerable <AxisAlignedBox> Clip(AxisAlignedPlane plane) { Position3 minCorner = MinCorner; Position3 maxCorner = MaxCorner; if (plane.Normal == Normal3.UnitX) { minCorner = new(Position1.Max(MinCorner.X, plane.Position.X), MinCorner.Y, MinCorner.Z); } else if (plane.Normal == -Normal3.UnitX) { maxCorner = new(Position1.Min(MinCorner.X, plane.Position.X), MaxCorner.Y, MaxCorner.Z); } else if (plane.Normal == Normal3.UnitY) { minCorner = new(MinCorner.X, Position1.Max(MinCorner.Y, plane.Position.Y), MinCorner.Z); } else if (plane.Normal == -Normal3.UnitY) { maxCorner = new(MaxCorner.X, Position1.Min(MaxCorner.Y, plane.Position.Y), MaxCorner.Z); } else if (plane.Normal == Normal3.UnitZ) { minCorner = new(MinCorner.X, MinCorner.Y, Position1.Max(MinCorner.Z, plane.Position.Z)); } else if (plane.Normal == -Normal3.UnitZ) { maxCorner = new(MaxCorner.X, MaxCorner.Y, Position1.Min(MaxCorner.Z, plane.Position.Z)); } if (minCorner == MinCorner && maxCorner == MaxCorner) { yield return(this); } else if (minCorner.X < maxCorner.X && minCorner.Y < maxCorner.Y && minCorner.Z < maxCorner.Z) { yield return(new AxisAlignedBox(minCorner, maxCorner)); } }
public IEnumerable <ISceneObject> Clip(AxisAlignedPlane plane) { throw new NotImplementedException("Split items and clip items on the border"); }