public static HullVoronoiExploder_Response ShootHull(ITriangleIndexed[] convexHull, Tuple<Point3D, Vector3D, double>[] shots, HullVoronoiExploder_Options options = null) { options = options ?? new HullVoronoiExploder_Options(); var aabb = Math3D.GetAABB(convexHull); double aabbLen = (aabb.Item2 - aabb.Item1).Length; // Create a voronoi, and intersect with the hull Tuple<HullVoronoiExploder_Response, bool> retVal = null; for (int cntr = 0; cntr < 15; cntr++) { try { retVal = SplitHull(convexHull, shots, options, aabbLen); break; } catch (Exception) { // Every once in a while, there is an error with the voronoi, or voronoi intersecting the hull, etc. Just try // again with new random points } } if (retVal == null) return null; else if (!retVal.Item2) return retVal.Item1; // Figure out velocities retVal.Item1.Velocities = GetVelocities(retVal.Item1.Shards, retVal.Item1.Hits, Math.Sqrt(aabbLen / 2), options); return retVal.Item1; }
// Asteroid public AsteroidOrMineralDefinition(PartSeparator_Part part, ITriangleIndexed[] asteroidTriangles, double asteroidRadius) { this.IsAsteroid = true; this.Part = part; this.AsteroidTriangles = asteroidTriangles; this.AsteroidRadius = asteroidRadius; this.MineralDefinition = null; }
//NOTE: This can return null public static Point3D[] GetIntersection(ITriangleIndexed[] hull, ITriangle triangle) { //TODO: It's possible that the test triangle is touching the hull, but not really intersecting with it. If that happens, then only // one or two points will be shared, but no real intersection // See if any of the points are inside int[] insidePoints = GetInsidePoints(hull, triangle); Point3D[] retVal = null; if (insidePoints.Length == 0) { // No points are inside the hull, but edges may punch all the way through retVal = AllOutside(hull, triangle); } else if (insidePoints.Length == 1) { // One point is inside the hull. See where its edges intersect the hull retVal = OneInside(hull, triangle, insidePoints[0]); } else if (insidePoints.Length == 2) { // Two points are inside the hull. See where their edges intersect the hull retVal = TwoInside(hull, triangle, insidePoints[0], insidePoints[1]); } else if (insidePoints.Length == 3) { // The triangle is completely inside the hull retVal = new Point3D[] { triangle.Point0, triangle.Point1, triangle.Point2 }; } else { throw new ApplicationException(""); } if (retVal != null) { //TODO: Make sure the returned polygon's normal is the same direction as the triangle passed in } // Exit Function return retVal; }
private static Tuple<HullVoronoiExploder_Response, bool> SplitHull(ITriangleIndexed[] convexHull, Tuple<Point3D, Vector3D, double>[] shots, HullVoronoiExploder_Options options, double aabbLen) { #region intersect with the hull var hits = shots. Select(o => new HullVoronoiExploder_ShotHit() { Shot = o, Hit = GetHit(convexHull, o.Item1, o.Item2, o.Item3), }). Where(o => o.Hit != null). ToArray(); if (hits.Length == 0) { return null; } #endregion #region voronoi Point3D[] controlPoints = GetVoronoiCtrlPoints(hits, convexHull, options.MinCount, options.MaxCount, aabbLen); VoronoiResult3D voronoi = Math3D.GetVoronoi(controlPoints, true); if (voronoi == null) { return null; } #endregion // There is enough to start populating the response HullVoronoiExploder_Response retVal = new HullVoronoiExploder_Response() { Hits = hits, ControlPoints = controlPoints, Voronoi = voronoi, }; #region intersect voronoi and hull // Intersect Tuple<int, ITriangleIndexed[]>[] shards = null; try { shards = Math3D.GetIntersection_Hull_Voronoi_full(convexHull, voronoi); } catch (Exception) { return Tuple.Create(retVal, false); } if (shards == null) { return Tuple.Create(retVal, false); } // Smooth if (options.ShouldSmoothShards) { shards = shards. Select(o => Tuple.Create(o.Item1, Asteroid.SmoothTriangles(o.Item2))). ToArray(); } // Validate shards = shards. Where(o => o.Item2 != null && o.Item2.Length >= 3). Where(o => { Vector3D firstNormal = o.Item2[0].NormalUnit; return o.Item2.Skip(1).Any(p => !Math.Abs(Vector3D.DotProduct(firstNormal, p.NormalUnit)).IsNearValue(1)); }). ToArray(); if (shards.Length == 0) { return Tuple.Create(retVal, false); } #endregion #region populate shards retVal.Shards = shards. Select(o => { var aabb = Math3D.GetAABB(o.Item2); double radius = Math.Sqrt((aabb.Item2 - aabb.Item1).Length / 2); Point3D center = Math3D.GetCenter(TriangleIndexed.GetUsedPoints(o.Item2)); Vector3D centerVect = center.ToVector(); Point3D[] allPoints = o.Item2[0].AllPoints. Select(p => p - centerVect). ToArray(); TriangleIndexed[] shiftedTriangles = o.Item2. Select(p => new TriangleIndexed(p.Index0, p.Index1, p.Index2, allPoints)). ToArray(); return new HullVoronoiExploder_Shard() { VoronoiControlPointIndex = o.Item1, Hull_ParentCoords = o.Item2, Hull_Centered = shiftedTriangles, Radius = radius, Center_ParentCoords = center, }; }). Where(o => o != null). ToArray(); #endregion return Tuple.Create(retVal, true); }
private static Point3D[] GetVoronoiCtrlPoints_TwoLines(Tuple<Point3D, ITriangle, double>[][] hits, ITriangleIndexed[] asteroid) { if (hits.Length != 2 || hits.Any(o => o.Length != 2)) { throw new ArgumentException("This method expects 2 hits, each with two endpoints"); } // Examine hits Tuple<int, double>[] hitsByLength = hits. Select((o, i) => Tuple.Create(i, (o[1].Item1 - o[0].Item1).Length)). OrderByDescending(o => o.Item2). ToArray(); double totalLength = hitsByLength.Sum(o => o.Item2); Tuple<int, double>[] hitsByPercentLength = hitsByLength. Select(o => Tuple.Create(o.Item1, o.Item2 / totalLength)). ToArray(); // Define control point cones var aabb = Math3D.GetAABB(asteroid); double aabbLen = (aabb.Item2 - aabb.Item1).Length; double entryRadius = aabbLen * .03; double exitRadius = aabbLen * .15; double maxAxisLength = aabbLen * .75; List<Point3D> retVal = new List<Point3D>(); //TODO: Instead of a fixed number here, figure out how many control points to make based on the total //length of the hits in relation to the volume of the asteroid for (int cntr = 0; cntr < 3; cntr++) { int index = UtilityCore.GetIndexIntoList(StaticRandom.NextDouble(), hitsByPercentLength); retVal.AddRange(ExplosionForceWorker.GetVoronoiCtrlPoints_AroundLine_Cone(hits[index][0].Item1, hits[index][1].Item1, StaticRandom.Next(2, 5), entryRadius, exitRadius, maxAxisLength)); } return retVal.ToArray(); #region OLD THOUGHTS //DIFFICULT: This should be doable, but seems more difficult than it's worth (also very hardcoded and specific - not very natural) // Draw a line between the two entry points, and another line between the two exit points // (or reverse one line if the shots come from opposite directions) // // Choose a 5th point that is the midpoint of the segment: Math3D.GetClosestPoints_Line_Line() // // No choose control points that will have those 4 triangles as edge faces //FAIL: The plate is very likely not coplanar // Create a plate with the four hitpoints as verticies // // Choose two points on either side of the plate (equidistant) #endregion }
private void AddHull(ITriangleIndexed[] triangles, bool drawFaces, bool drawLines, bool drawNormals, bool nearlyTransparent, bool softFaces, bool isBrightLines) { Visual3D[] visuals = GetHull(triangles, drawFaces, drawLines, drawNormals, nearlyTransparent, softFaces, isBrightLines); foreach (Visual3D visual in visuals) { _viewport.Children.Add(visual); _visuals.Add(visual); } }
private void DrawAsteroid(ITriangleIndexed[] hull) { var visuals = GetAsteroid(hull); foreach (Visual3D visual in visuals.Item2) { _viewport.Children.Add(visual); _visuals.Add(visual); } }
private static double GetAsteroidMass(double radius, ITriangleIndexed[] triangles) { double volume = 4d / 3d * Math.PI * radius * radius * radius; return ASTEROIDDENSITY * volume; }
private static double GetLength(ITriangleIndexed triangle, TriangleEdge edge, SortedList<Tuple<int, int>, double> lengths) { return GetLength(triangle.GetIndex(edge, true), triangle.GetIndex(edge, false), lengths); }
/// <summary> /// If this triangle is long and thin, then this will decide whether to remove a link, or merge the two close brains /// </summary> /// <returns> /// Null : This is not a long thin triangle. Move along /// Item1=True : Merge the two brains connected by Item2 /// Item1=False : Remove the Item2 link /// </returns> private static Tuple<bool, TriangleEdge> PruneBrainLinks_LongThin(ITriangleIndexed triangle, SortedList<Tuple<int, int>, double> all) { var lengths = new[] { TriangleEdge.Edge_01, TriangleEdge.Edge_12, TriangleEdge.Edge_20 }. Select(o => new { Edge = o, Length = GetLength(triangle, o, all) }). OrderBy(o => o.Length). ToArray(); if (lengths[0].Length / lengths[1].Length < BRAINPRUNE_RATIO_SKINNY && lengths[0].Length / lengths[2].Length < BRAINPRUNE_RATIO_SKINNY) { #region Isosceles - skinny base if (StaticRandom.NextDouble() < BRAINPRUNE_MERGECHANCE) { // Treat the two close brains like one, and split the links evenly with the far brain return Tuple.Create(true, lengths[0].Edge); } else { // Choose one of the long links to remove if (StaticRandom.NextBool()) { return Tuple.Create(false, lengths[1].Edge); } else { return Tuple.Create(false, lengths[2].Edge); } } #endregion } else if (lengths[2].Length / (lengths[0].Length + lengths[1].Length) > BRAINPRUNE_RATIO_WIDE) { // Wide base (small angles, one huge angle) return Tuple.Create(false, lengths[2].Edge); } return null; }
private static void AddPolyPlate(ITriangleIndexed[] triangles, Model3DGroup group, Material material) { // Geometry Model GeometryModel3D geometry = new GeometryModel3D(); geometry.Material = material; geometry.BackMaterial = material; geometry.Geometry = UtilityWPF.GetMeshFromTriangles(triangles); group.Children.Add(geometry); }
public static int[] GetBlockedCells(FluidField3D field, ITriangleIndexed[] hull) { return GetBlockedCells(field, new ITriangleIndexed[][] { hull }); }
private ScreenSpaceLines3D GetModelWireframe(ITriangleIndexed[] triangles) { ScreenSpaceLines3D retVal = new ScreenSpaceLines3D(); retVal.Thickness = 1d; retVal.Color = _colors.HullWireFrame; if (triangles.Length > 0) { Point3D[] allPoints = triangles[0].AllPoints; foreach (Tuple<int, int> line in TriangleIndexed.GetUniqueLines(triangles)) { retVal.AddLine(allPoints[line.Item1], allPoints[line.Item2]); } } return retVal; }
// Asteroid public AsteroidOrMineralDefinition(PartSeparator_Part part, ITriangleIndexed[] asteroidTriangles, double asteroidRadius, Vector3D velocity, bool shouldSelfDestruct, MineralDNA[] mineralsAfterSelfDestruct) { this.IsAsteroid = true; this.Part = part; this.AsteroidTriangles = asteroidTriangles; this.AsteroidRadius = asteroidRadius; this.ShouldAsteroidSelfDestruct = shouldSelfDestruct; this.MineralsAfterSelfDestruct = mineralsAfterSelfDestruct; this.Velocity = velocity; this.MineralDefinition = null; }
/// <summary> /// Defines the child asteroid shapes, then finds positions/orientations for all child asteroids and minerals (makes /// sure nothing overlaps) /// </summary> private AsteroidOrMineralDefinition[] PositionChildAsteroidsAndMinerals(double[] asteroidVolumes, MineralDNA[] mineralDefinitions) { const double FOURTHIRDSPI = 4d / 3d * Math.PI; const double ONETHRID = 1d / 3d; double positionRange = _radiusMin * .05; #region Asteroids PartSeparator_Part[] asteroidParts = new PartSeparator_Part[asteroidVolumes.Length]; ITriangleIndexed[][] asteroidTriangles = new ITriangleIndexed[asteroidVolumes.Length][]; double[] asteroidRadii = new double[asteroidVolumes.Length]; for (int cntr = 0; cntr < asteroidVolumes.Length; cntr++) { // r^3=v/(4/3pi) asteroidRadii[cntr] = Math.Pow(asteroidVolumes[cntr] / FOURTHIRDSPI, ONETHRID); asteroidTriangles[cntr] = GetHullTriangles_Initial(asteroidRadii[cntr]); double currentMass = _getMassByRadius(asteroidRadii[cntr], asteroidTriangles[cntr]); asteroidParts[cntr] = new PartSeparator_Part(asteroidTriangles[cntr][0].AllPoints, currentMass, Math3D.GetRandomVector_Spherical(positionRange).ToPoint(), Math3D.GetRandomRotation()); } #endregion #region Minerals PartSeparator_Part[] mineralParts = new PartSeparator_Part[mineralDefinitions.Length]; for (int cntr = 0; cntr < mineralDefinitions.Length; cntr++) { //NOTE: Copied logic from Mineral that gets collision points and mass // Points Point3D[] mineralPoints = UtilityWPF.GetPointsFromMesh((MeshGeometry3D)_sharedVisuals.Value.GetMineralMesh(mineralDefinitions[cntr].MineralType)); // Mass double mass = mineralDefinitions[cntr].Density * mineralDefinitions[cntr].Volume; // Store it mineralParts[cntr] = new PartSeparator_Part(mineralPoints, mass, Math3D.GetRandomVector_Spherical(positionRange).ToPoint(), Math3D.GetRandomRotation()); } #endregion #region Pull Apart if (asteroidParts.Length + mineralParts.Length > 1) { PartSeparator_Part[] allParts = UtilityCore.ArrayAdd(asteroidParts, mineralParts); bool dummy; CollisionHull[] hulls = PartSeparator.Separate(out dummy, allParts, _world); foreach (CollisionHull hull in hulls) { hull.Dispose(); } } #endregion #region Build Return List<AsteroidOrMineralDefinition> retVal = new List<AsteroidOrMineralDefinition>(); for (int cntr = 0; cntr < asteroidParts.Length; cntr++) { retVal.Add(new AsteroidOrMineralDefinition(asteroidParts[cntr], asteroidTriangles[cntr], asteroidRadii[cntr])); } for (int cntr = 0; cntr < mineralParts.Length; cntr++) { retVal.Add(new AsteroidOrMineralDefinition(mineralParts[cntr], mineralDefinitions[cntr])); } #endregion return retVal.ToArray(); }
//NOTE: This will only return a proper polygon, or null (will return null if the triangle is only touching the hull, but not intersecting) public static Point3D[] GetIntersection_Hull_Triangle(ITriangleIndexed[] hull, ITriangle triangle) { // Think of the triangle as a plane, and get a polygon of it slicing through the hull Point3D[] planePoly = GetIntersection_Hull_Plane(hull, triangle); if (planePoly == null || planePoly.Length < 3) { return null; } // Now intersect that polygon with the triangle (could return null) return Math2D.GetIntersection_Polygon_Triangle(planePoly, triangle); }
private static Point3D[] GetIntersection_Hull_Plane(ITriangleIndexed[] hull, ITriangle plane) { // Shoot through all the triangles in the hull, and get line segment intersections List<Tuple<Point3D, Point3D>> lineSegments = null; if (hull.Length > 100) { lineSegments = hull. AsParallel(). Select(o => GetIntersection_Plane_Triangle(plane, o)). Where(o => o != null). ToList(); } else { lineSegments = hull. Select(o => GetIntersection_Plane_Triangle(plane, o)). Where(o => o != null). ToList(); } if (lineSegments.Count < 2) { // length of 0 is a clear miss, 1 is just touching //NOTE: All the points could be colinear, and just touching the hull, but deeper analysis is needed return null; } // Stitch the line segments together Point3D[] retVal = GetIntersection_Hull_PlaneSprtStitchSegments(lineSegments); if (retVal == null) { // In some cases, the above method fails, so call the more generic 2D convex hull method //NOTE: This assumes the hull is convex retVal = GetIntersection_Hull_PlaneSprtConvexHull(lineSegments); } // Exit Function return retVal; // could still be null }
private static double GetAsteroidMassByRadius(double radius, ITriangleIndexed[] triangles) { // this isn't as realistic as 4/3 pi r^3, but is more fun and stable return 4000d * Math.Pow(radius, 1.5d); }
private static Visual3D GetVisual_HullLines(ITriangleIndexed[] hull, Color color, double thickness) { Point3D[] allPoints = hull[0].AllPoints; ScreenSpaceLines3D retVal = new ScreenSpaceLines3D(); retVal.Color = color; retVal.Thickness = thickness; foreach (var line in TriangleIndexed.GetUniqueLines(hull)) { retVal.AddLine(allPoints[line.Item1], allPoints[line.Item2]); } return retVal; }
private static int[] GetInsidePoints(ITriangleIndexed[] hull, ITriangle triangle) { List<int> retVal = new List<int>(); // the values will be 0,1,2 corresponding to the point0,point1,point2 for (int cntr = 0; cntr < 3; cntr++) { if (Math3D.IsInside_ConvexHull(hull, GetTrianglePoint(triangle, cntr))) { retVal.Add(cntr); } } return retVal.ToArray(); }
public static ExplosionForceResponse ShootHull(ITriangleIndexed[] convexHull, Tuple<Point3D, Vector3D, double>[] shots, ExplosionForceOptions options = null) { options = options ?? new ExplosionForceOptions(); // Create a voronoi, and intersect with the hull var retVal = SplitHull(convexHull, shots, options); if (retVal == null) return null; else if (!retVal.Item2) return retVal.Item1; // Figure out velocities var aabb = Math3D.GetAABB(convexHull); retVal.Item1.Velocities = GetVelocities(retVal.Item1.Shards, retVal.Item1.Hits, Math.Sqrt((aabb.Item2 - aabb.Item1).Length / 2), options); return retVal.Item1; }
private static Point3D[] AllOutside(ITriangleIndexed[] hull, ITriangle triangle) { return null; }
private Tuple<Model3D, Visual3D[]> GetAsteroid(ITriangleIndexed[] hull) { Visual3D[] visuals = GetHull(hull, chkDrawFaces.IsChecked.Value, chkDrawLines.IsChecked.Value, chkDrawNormals.IsChecked.Value, chkNearlyTransparent.IsChecked.Value, chkSoftFaces.IsChecked.Value, true); return new Tuple<Model3D, Visual3D[]>(null, visuals); }
private static Point3D[] OneInside(ITriangleIndexed[] hull, ITriangle triangle, int insidePointIndex) { #region Examine Index TriangleEdge edge1, edge2, edgeOutside; // Edges that are intersecting the hull Point3D insidePoint, outsideCenterPoint; switch (insidePointIndex) { case 0: edge1 = TriangleEdge.Edge_01; edge2 = TriangleEdge.Edge_20; edgeOutside = TriangleEdge.Edge_12; insidePoint = triangle.Point0; outsideCenterPoint = triangle.GetEdgeMidpoint(edgeOutside); break; case 1: edge1 = TriangleEdge.Edge_01; edge2 = TriangleEdge.Edge_12; edgeOutside = TriangleEdge.Edge_20; insidePoint = triangle.Point1; outsideCenterPoint = triangle.GetEdgeMidpoint(edgeOutside); break; case 2: edge1 = TriangleEdge.Edge_12; edge2 = TriangleEdge.Edge_20; edgeOutside = TriangleEdge.Edge_01; insidePoint = triangle.Point2; outsideCenterPoint = triangle.GetEdgeMidpoint(edgeOutside); break; default: throw new ApplicationException("Unexpected point: " + insidePointIndex.ToString()); } // Make sure that outsideCenterPoint is outside the hull outsideCenterPoint = EnsureOutsidePointIsBeyondHull(hull[0].AllPoints, insidePoint, outsideCenterPoint); #endregion // Get edge/hull intersections var intersect1 = GetIntersectPoints(hull, triangle.GetPoint(edge1, true), triangle.GetPoint(edge1, false)); if (intersect1.Length == 0) { return null; // triangle is only touching the hull, not really intersecting } else if (intersect1.Length > 1) { throw new ApplicationException("Should never get more than one intersect point: " + intersect1.Length.ToString()); } var intersect2 = GetIntersectPoints(hull, triangle.GetPoint(edge2, true), triangle.GetPoint(edge2, false)); if (intersect2.Length == 0) { return null; } else if (intersect2.Length > 1) { throw new ApplicationException("Should never get more than one intersect point: " + intersect2.Length.ToString()); } // Now that the two intersect points are found, find connecting lines Point3D[] hullEdge = GetConnectingLines(hull, intersect1[0], intersect2[0], outsideCenterPoint); // Clip poly List<Point3D> hullPoly = new List<Point3D>(); hullPoly.Add(insidePoint); hullPoly.AddRange(hullEdge); Point3D[] retVal = Math2D.GetIntersection_Polygon_Triangle(hullPoly.ToArray(), triangle); // Exit Function return retVal; }
private Visual3D[] GetHull(ITriangleIndexed[] triangles, bool drawFaces, bool drawLines, bool drawNormals, bool nearlyTransparent, bool softFaces, bool isBrightLines) { List<Visual3D> retVal = new List<Visual3D>(); if (drawLines) { #region Lines // Draw the lines ScreenSpaceLines3D lineVisual = new ScreenSpaceLines3D(true); lineVisual.Thickness = (!drawFaces || nearlyTransparent) ? LINETHICKNESS_SCREENSPACE * .5d : LINETHICKNESS_SCREENSPACE; lineVisual.Color = isBrightLines ? _colors.PrimaryLine_Bright : _colors.PrimaryLine_Dark; Point3D[] points = triangles[0].AllPoints; foreach (var line in TriangleIndexed.GetUniqueLines(triangles)) { lineVisual.AddLine(points[line.Item1], points[line.Item2]); } //_viewport.Children.Add(lineVisual); //_visuals.Add(lineVisual); retVal.Add(lineVisual); #endregion } if (drawNormals) { #region Normals // Draw the lines ScreenSpaceLines3D lineVisual = new ScreenSpaceLines3D(true); lineVisual.Thickness = (!drawFaces || nearlyTransparent) ? LINETHICKNESS_SCREENSPACE * .5d : LINETHICKNESS_SCREENSPACE; lineVisual.Color = _colors.NormalLine; foreach (TriangleIndexed triangle in triangles) { Point3D centerPoint = triangle.GetCenterPoint(); lineVisual.AddLine(centerPoint, centerPoint + triangle.Normal); } //_viewport.Children.Add(lineVisual); //_visuals.Add(lineVisual); retVal.Add(lineVisual); #endregion } if (drawFaces) { #region Faces // Material MaterialGroup materials = new MaterialGroup(); materials.Children.Add(new DiffuseMaterial(new SolidColorBrush(nearlyTransparent ? _colors.HullFaceTransparent : _colors.HullFace))); if (nearlyTransparent) { materials.Children.Add(_colors.HullFaceSpecularTransparent); } else { if (softFaces) { materials.Children.Add(_colors.HullFaceSpecularSoft); } else { materials.Children.Add(_colors.HullFaceSpecular); } } // Geometry Mesh MeshGeometry3D mesh = null; if (softFaces) { mesh = UtilityWPF.GetMeshFromTriangles(TriangleIndexed.Clone_CondensePoints(triangles)); } else { mesh = UtilityWPF.GetMeshFromTriangles_IndependentFaces(triangles); } // Geometry Model GeometryModel3D geometry = new GeometryModel3D(); geometry.Material = materials; geometry.BackMaterial = materials; geometry.Geometry = mesh; // Model Visual ModelVisual3D visual = new ModelVisual3D(); visual.Content = geometry; //_viewport.Children.Add(visual); //_visuals.Add(visual); retVal.Add(visual); #endregion } return retVal.ToArray(); }
private static Point3D[] TwoInside(ITriangleIndexed[] hull, ITriangle triangle, int insidePointIndex1, int insidePointIndex2) { if (insidePointIndex1 > insidePointIndex2) { // Reverse them to simplify the switch statements return TwoInside(hull, triangle, insidePointIndex2, insidePointIndex1); } #region Examine Indices TriangleEdge edgeLeft, edgeRight; Point3D insidePointLeft, insidePointRight, insidePointMiddle, outsideCenterPoint; switch (insidePointIndex1) { case 0: switch (insidePointIndex2) { case 1: #region 0, 1 edgeLeft = TriangleEdge.Edge_20; edgeRight = TriangleEdge.Edge_12; insidePointLeft = triangle.Point0; insidePointRight = triangle.Point1; insidePointMiddle = triangle.GetEdgeMidpoint(TriangleEdge.Edge_01); outsideCenterPoint = triangle.Point2; #endregion break; case 2: #region 0, 2 edgeLeft = TriangleEdge.Edge_01; edgeRight = TriangleEdge.Edge_12; insidePointLeft = triangle.Point0; insidePointRight = triangle.Point2; insidePointMiddle = triangle.GetEdgeMidpoint(TriangleEdge.Edge_20); outsideCenterPoint = triangle.Point1; #endregion break; default: throw new ApplicationException("Unexpected point: " + insidePointIndex2.ToString()); } break; case 1: #region 1, 2 if (insidePointIndex2 != 2) { throw new ApplicationException("Unexpected point: " + insidePointIndex2.ToString()); } edgeLeft = TriangleEdge.Edge_01; edgeRight = TriangleEdge.Edge_20; insidePointLeft = triangle.Point1; insidePointRight = triangle.Point2; insidePointMiddle = triangle.GetEdgeMidpoint(TriangleEdge.Edge_12); outsideCenterPoint = triangle.Point0; #endregion break; default: throw new ApplicationException("Unexpected point: " + insidePointIndex2.ToString()); } // Make sure that outsideCenterPoint is outside the hull outsideCenterPoint = EnsureOutsidePointIsBeyondHull(hull[0].AllPoints, insidePointMiddle, outsideCenterPoint); #endregion // Get edge/hull intersections var intersectLeft = GetIntersectPoints(hull, triangle.GetPoint(edgeLeft, true), triangle.GetPoint(edgeLeft, false)); if (intersectLeft.Length == 0) { return null; // triangle is only touching the hull, not really intersecting } var intersectRight = GetIntersectPoints(hull, triangle.GetPoint(edgeRight, true), triangle.GetPoint(edgeRight, false)); if (intersectRight.Length == 0) { return null; } // Now that the two intersect points are found, find connecting lines Point3D[] hullEdge = GetConnectingLines(hull, intersectLeft[0], intersectRight[0], outsideCenterPoint); // Clip poly List<Point3D> hullPoly = new List<Point3D>(); hullPoly.Add(insidePointLeft); hullPoly.AddRange(hullEdge); hullPoly.Add(insidePointRight); Point3D[] retVal = Math2D.GetIntersection_Polygon_Triangle(hullPoly.ToArray(), triangle); // Exit Function return retVal; }
private static Tuple<int, Point3D>[] GetIntersectPoints(ITriangleIndexed[] hull, Point3D point1, Point3D point2) { List<Tuple<int, Point3D>> retVal = new List<Tuple<int, Point3D>>(); for (int cntr = 0; cntr < hull.Length; cntr++) { Point3D? point = Math3D.GetIntersection_Triangle_LineSegment(hull[cntr], point1, point2); if (point != null && !Math3D.IsNearValue(point.Value, point1) && !Math3D.IsNearValue(point.Value, point2)) { retVal.Add(Tuple.Create(cntr, point.Value)); } } return retVal.ToArray(); }
private static Point3D[] GetConnectingLines(ITriangleIndexed[] hull, Tuple<int, Point3D> intersect1, Tuple<int, Point3D> intersect2, Point3D outsideCenterPoint) { if (intersect1.Item1 == intersect2.Item1) { // They are intersecting the same triangle. Just return a line between them return new Point3D[] { intersect1.Item2, intersect2.Item2 }; } // Get a line from intersect1 to the edge of its triangle var edgeIntersect1 = GetPointOnTriangleEdge(hull[intersect1.Item1], intersect1.Item2, outsideCenterPoint); // Get a line from intersect2 to the edge of its triangle var edgeIntersect2 = GetPointOnTriangleEdge(hull[intersect2.Item1], intersect2.Item2, outsideCenterPoint); if (edgeIntersect1 == null && edgeIntersect2 == null) { // This should never happen. The only thing I can think of is intersect1 and 2 are intersecting 2 triangles, but right on their edge return new Point3D[] { intersect1.Item2, intersect2.Item2 }; } else if (edgeIntersect1 != null && edgeIntersect2 == null) { return new Point3D[] { intersect1.Item2, edgeIntersect1.Item2, intersect2.Item2 }; } else if (edgeIntersect1 == null && edgeIntersect2 != null) { return new Point3D[] { intersect1.Item2, edgeIntersect2.Item2, intersect2.Item2 }; } // edgeIntersect1 and 2 are both nonnull at this point if (Math3D.IsNearValue(edgeIntersect1.Item2, edgeIntersect2.Item2)) { // intersect1 and 2 are intersecting neighboring triangles, so edgeIntersect1 and 2 are the same point return new Point3D[] { intersect1.Item2, edgeIntersect1.Item2, intersect2.Item2 }; } // If execution gets here, there are triangles between the two intersected triangles passed in // Convert the hull into linked triangles TriangleIndexedLinked[] hullLinked = hull.Select(o => new TriangleIndexedLinked(o.Index0, o.Index1, o.Index2, o.AllPoints)).ToArray(); TriangleIndexedLinked.LinkTriangles_Edges(hullLinked.ToList(), true); return GetConnectingLinesSprtBetween(hullLinked, intersect1, intersect2, edgeIntersect1, edgeIntersect2); }
private void DrawPolyPlate(ITriangleIndexed[] triangles, Color color, bool isShiny) { // Material MaterialGroup materials = new MaterialGroup(); materials.Children.Add(new DiffuseMaterial(new SolidColorBrush(color))); if (isShiny) { materials.Children.Add(new SpecularMaterial(new SolidColorBrush(UtilityWPF.AlphaBlend(color, Colors.White, .5d)), 5d)); } else { Color derivedColor = UtilityWPF.AlphaBlend(color, Colors.White, .8d); materials.Children.Add(new SpecularMaterial(new SolidColorBrush(Color.FromArgb(128, derivedColor.R, derivedColor.G, derivedColor.B)), 2d)); } // Geometry Model GeometryModel3D geometry = new GeometryModel3D(); geometry.Material = materials; geometry.BackMaterial = materials; geometry.Geometry = UtilityWPF.GetMeshFromTriangles(triangles); // Model Visual ModelVisual3D model = new ModelVisual3D(); model.Content = geometry; // Temporarily add to the viewport _viewport.Children.Add(model); _visuals.Add(model); }
public static ITriangleIndexed[] SmoothTriangles(ITriangleIndexed[] hull) { const double RATIO = .93; // This holds the mesh so far before an exception occurs ITriangleIndexed[] retVal = hull; // Math3D.SliceLargeTriangles_Smooth + Math3D.RemoveThinTriangles sometimes throws an exception. So try a few times before giving up try { retVal = hull; // Remove excessively thin triangles ITriangleIndexed[] slicedHull = Math3D.RemoveThinTriangles(hull, RATIO); retVal = slicedHull; // Now round it out (which adds triangles) Point3D[] hullPoints = slicedHull[0].AllPoints; double[] lengths = TriangleIndexed.GetUniqueLines(slicedHull). Select(o => (hullPoints[o.Item1] - hullPoints[o.Item2]).Length). ToArray(); double avgLen = lengths.Average(); double maxLen = lengths.Max(); double sliceLen = UtilityCore.GetScaledValue(avgLen, maxLen, 0, 1, .333); ITriangleIndexed[] smoothHull = Math3D.SliceLargeTriangles_Smooth(slicedHull, sliceLen); retVal = smoothHull; // Remove again ITriangleIndexed[] secondSlicedHull = Math3D.RemoveThinTriangles(smoothHull, RATIO); retVal = secondSlicedHull; // Sometimes there were holes, so call convex to fix that ITriangleIndexed[] convex = Math3D.GetConvexHull(Triangle.GetUniquePoints(secondSlicedHull)); return convex; } catch (Exception) { } if (retVal == null) { throw new ApplicationException("Couldn't create a hull"); } else { return retVal; } }