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;
        }
Esempio n. 2
0
                // 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);
 }
Esempio n. 10
0
            /// <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;
            }
Esempio n. 11
0
        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);
        }
Esempio n. 12
0
 public static int[] GetBlockedCells(FluidField3D field, ITriangleIndexed[] hull)
 {
     return GetBlockedCells(field, new ITriangleIndexed[][] { hull });
 }
Esempio n. 13
0
        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;
        }
Esempio n. 14
0
                // 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;
                }
Esempio n. 15
0
            /// <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
            }
Esempio n. 18
0
 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();
            }
Esempio n. 21
0
            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;
 }
Esempio n. 23
0
        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;
            }
Esempio n. 25
0
        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);
            }
Esempio n. 29
0
        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);
        }
Esempio n. 30
0
        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;
            }
        }