Exemple #1
0
        private static Vector3D[] GetNeuronPositionsInitial(out Vector3D[] staticPoints, out double[] staticRepulseMult, Point3D[] dnaPositions, Point3D[] existingStaticPositions, double staticMultValue, int count, double radius, Func<double, Vector3D> getNewPoint)
        {
            //TODO: When reducing/increasing, it is currently just being random.  It may be more realistic to take proximity into account.  Play
            // a variant of conway's game of life or something

            Vector3D[] retVal;

            if (dnaPositions == null)
            {
                #region Create new

                retVal = new Vector3D[count];
                for (int cntr = 0; cntr < count; cntr++)
                {
                    //retVal[cntr] = Math3D.GetRandomVectorSpherical(radius);
                    retVal[cntr] = getNewPoint(radius);     // using a delegate instead
                }

                #endregion
            }
            else if (dnaPositions.Length > count)
            {
                #region Reduce

                List<Vector3D> posList = dnaPositions.Select(o => o.ToVector()).ToList();

                int reduceCount = dnaPositions.Length - count;

                for (int cntr = 0; cntr < reduceCount; cntr++)
                {
                    posList.RemoveAt(StaticRandom.Next(posList.Count));
                }

                retVal = posList.ToArray();

                #endregion
            }
            else if (dnaPositions.Length < count)
            {
                #region Increase

                List<Vector3D> posList = dnaPositions.Select(o => o.ToVector()).ToList();

                int increaseCount = count - dnaPositions.Length;

                for (int cntr = 0; cntr < increaseCount; cntr++)
                {
                    //posList.Add(Math3D.GetRandomVectorSpherical2D(radius));
                    posList.Add(getNewPoint(radius));       // using a delegate instead
                }

                retVal = posList.ToArray();

                #endregion
            }
            else
            {
                #region Copy as is

                retVal = dnaPositions.Select(o => o.ToVector()).ToArray();

                #endregion
            }

            // Prep the static point arrays
            staticPoints = null;
            staticRepulseMult = null;
            if (existingStaticPositions != null)
            {
                staticPoints = existingStaticPositions.Select(o => o.ToVector()).ToArray();
                // This will force more than normal distance between brain chemical neurons and standard neurons.  The reason I'm doing this
                // is because the brain chemical neurons can have some powerful effects on the brain, and when an offspring is made, the links
                // in the dna only hold neuron position.  By having a larger gap, there is less chance of accidental miswiring
                staticRepulseMult = Enumerable.Range(0, staticPoints.Length).Select(o => staticMultValue).ToArray();
            }

            // Exit Function
            return retVal;
        }
        private static int GetDelaunayResults_SegmentCount(Point3D[] brains, Point3D[] io)
        {
            LinkItem[] brainItems = brains.
                Select(o => new LinkItem(o, 1d)).
                ToArray();

            LinkItem[] ioItems = io.
                Select(o => new LinkItem(o, 1d)).
                ToArray();

            // Self
            SortedList<Tuple<int, int>, double> all;
            LinkSetPair[] final;
            ItemLinker.Link_Self(out all, out final, brainItems);

            // Brain-IO
            Tuple<int, int>[] oneTwo = ItemLinker.Link_1_2(brainItems, ioItems);

            return final.Length + oneTwo.Length;
        }
 public static Tuple<Point3D, Vector3D>[] BuildStroke(Point3D[] points, double smallestSubstrokeSize)
 {
     return BuildStroke(points.
         Select(o => new Tuple<Point3D, Vector3D?>(o, null)).ToArray(),
         smallestSubstrokeSize);
 }
        public static TruncatedIcosidodecahedron GetTruncatedIcosidodecahedron(double radius, Transform3D transform = null)
        {
            //TODO: Currently, the points are hardcoded.  All the polygons are regular.  Take in a ratio for the length of side of the decagons.
            //  0 would make the decagons disappar, and the squares and hexagons would be it
            //  1 would make the squares disappear and the hexagons would become triangles
            //
            //The squares are the cornerstones.  When calculating points, figure out what the centers of the various polygons are.  Then adjust the
            //sizes of the squares.  From that, find the rest of the points using the decagons.  No need to find points for the hexagons, they have
            //no unique points

            #region Points

            double t = (1d + Math.Sqrt(5d)) / 2d;       // φ
            double tS = t * t;      // φ^2
            double tI1 = 1d / t;        // 1/φ
            double tI2 = 2d / t;        // 2/φ
            double t2 = 2d * t;     // 2φ
            double tA = 1d + (2d * t);      // 1+2φ
            double tB = 2d + t;     // 2+φ
            double tC = 3d + t;     // 3+φ
            double tN1 = -1d + (3d * t);        // -1+3φ
            double tN2 = -1d + (2d * t);        // -1+2φ

            Point3D[] points = new Point3D[]
            {
                //(±1/φ, ±1/φ, ±(3+φ))
                new Point3D(tI1, tI1, tC),
                new Point3D(tI1, tI1, -tC),
                new Point3D(tI1, -tI1, tC),
                new Point3D(tI1, -tI1, -tC),
                new Point3D(-tI1, tI1, tC),
                new Point3D(-tI1, tI1, -tC),
                new Point3D(-tI1, -tI1, tC),
                new Point3D(-tI1, -tI1, -tC),

                //(±2/φ, ±φ, ±(1+2φ))
                new Point3D(tI2, t, tA),
                new Point3D(tI2, t, -tA),
                new Point3D(tI2, -t, tA),
                new Point3D(tI2, -t, -tA),
                new Point3D(-tI2, t, tA),
                new Point3D(-tI2, t, -tA),
                new Point3D(-tI2, -t, tA),
                new Point3D(-tI2, -t, -tA),

                //(±1/φ, ±φ^2, ±(-1+3φ))
                new Point3D(tI1, tS, tN1),
                new Point3D(tI1, tS, -tN1),
                new Point3D(tI1, -tS, tN1),
                new Point3D(tI1, -tS, -tN1),
                new Point3D(-tI1, tS, tN1),
                new Point3D(-tI1, tS, -tN1),
                new Point3D(-tI1, -tS, tN1),
                new Point3D(-tI1, -tS, -tN1),

                //(±(-1+2φ), ±2, ±(2+φ))
                new Point3D(tN2, 2, tB),
                new Point3D(tN2, 2, -tB),
                new Point3D(tN2, -2, tB),
                new Point3D(tN2, -2, -tB),
                new Point3D(-tN2, 2, tB),
                new Point3D(-tN2, 2, -tB),
                new Point3D(-tN2, -2, tB),
                new Point3D(-tN2, -2, -tB),

                //(±φ, ±3, ±2φ),
                new Point3D(t, 3, t2),
                new Point3D(t, 3, -t2),
                new Point3D(t, -3, t2),
                new Point3D(t, -3, -t2),
                new Point3D(-t, 3, t2),
                new Point3D(-t, 3, -t2),
                new Point3D(-t, -3, t2),
                new Point3D(-t, -3, -t2),
            };

            points = points.Select(o => new Point3D[]
            { 
                o,      // orig
                new Point3D(o.Y, o.Z, o.X),      // shift left
                new Point3D(o.Z, o.X, o.Y)       // shift left twice
            }).
            SelectMany(o => o).
            ToArray();

            double maxLength = points[0].ToVector().Length;
            double ratio = radius / maxLength;
            points = points.Select(o => (o.ToVector() * ratio).ToPoint()).ToArray();

            if (transform != null)
            {
                points = points.Select(o => transform.Transform(o)).ToArray();
            }

            #endregion

            int[][] decagonPolys = new int[][]
            {
                new int [] { 0, 6, 30, 78, 110, 62, 50, 98, 72, 24 },
                new int [] { 14, 38, 86, 100, 52, 64, 112, 92, 44, 20 },
                new int [] { 3, 27, 75, 104, 56, 68, 116, 81, 33, 9 },
                new int [] { 15, 21, 45, 93, 119, 71, 59, 107, 87, 39 },
                new int [] { 2, 8, 32, 80, 109, 61, 49, 97, 74, 26 },
                new int [] { 1, 7, 31, 79, 108, 60, 48, 96, 73, 25 },
                new int [] { 12, 36, 84, 101, 53, 65, 113, 90, 42, 18 },
                new int [] { 4, 28, 76, 102, 54, 66, 114, 82, 34, 10 },
                new int [] { 16, 22, 46, 94, 117, 69, 57, 105, 88, 40 },
                new int [] { 17, 23, 47, 95, 118, 70, 58, 106, 89, 41 },
                new int [] { 5, 29, 77, 103, 55, 67, 115, 83, 35, 11 },
                new int [] { 13, 37, 85, 99, 51, 63, 111, 91, 43, 19 },
            };

            int[][] hexagonPolys = new int[][]
            {
                new int [] { 6, 18, 42, 66, 54, 30 },
                new int [] { 82, 114, 90, 113, 89, 106 },
                new int [] { 5, 17, 41, 65, 53, 29 },
                new int [] { 77, 101, 84, 108, 79, 103 },
                new int [] { 7, 19, 43, 67, 55, 31 },
                new int [] { 83, 115, 91, 111, 87, 107 },
                new int [] { 3, 15, 39, 63, 51, 27 },
                new int [] { 0, 24, 48, 60, 36, 12 },
                new int [] { 72, 98, 74, 97, 73, 96 },
                new int [] { 1, 25, 49, 61, 37, 13 },
                new int [] { 75, 99, 85, 109, 80, 104 },
                new int [] { 76, 100, 86, 110, 78, 102 },
                new int [] { 2, 26, 50, 62, 38, 14 },
                new int [] { 8, 20, 44, 68, 56, 32 },
                new int [] { 81, 116, 92, 112, 88, 105 },
                new int [] { 9, 33, 57, 69, 45, 21 },
                new int [] { 11, 35, 59, 71, 47, 23 },
                new int [] { 93, 117, 94, 118, 95, 119 },
                new int [] { 10, 34, 58, 70, 46, 22 },
                new int [] { 4, 16, 40, 64, 52, 28 },
            };

            int[][] squarePolys = new int[][]
            {
                new int [] { 0, 12, 18, 6 },
                new int [] { 36, 60, 108, 84 },
                new int [] { 31, 55, 103, 79 },
                new int [] { 43, 91, 115, 67 },
                new int [] { 39, 87, 111, 63 },
                new int [] { 3, 9, 21, 15 },
                new int [] { 24, 72, 96, 48 },
                new int [] { 25, 73, 97, 49 },
                new int [] { 1, 13, 19, 7 },
                new int [] { 37, 61, 109, 85 },
                new int [] { 27, 51, 99, 75 },
                new int [] { 33, 81, 105, 57 },
                new int [] { 42, 90, 114, 66 },
                new int [] { 41, 89, 113, 65 },
                new int [] { 5, 11, 23, 17 },
                new int [] { 35, 83, 107, 59 },
                new int [] { 45, 69, 117, 93 },
                new int [] { 29, 53, 101, 77 },
                new int [] { 30, 54, 102, 78 },
                new int [] { 28, 52, 100, 76 },
                new int [] { 38, 62, 110, 86 },
                new int [] { 26, 74, 98, 50 },
                new int [] { 2, 14, 20, 8 },
                new int [] { 32, 56, 104, 80 },
                new int [] { 34, 82, 106, 58 },
                new int [] { 46, 70, 118, 94 },
                new int [] { 47, 71, 119, 95 },
                new int [] { 4, 10, 22, 16 },
                new int [] { 40, 88, 112, 64 },
                new int [] { 44, 92, 116, 68 },
            };

            return new TruncatedIcosidodecahedron(decagonPolys, hexagonPolys, squarePolys, points);
        }
Exemple #5
0
        /// <summary>
        /// This returns the index and distance of the nodes that are close to search
        /// </summary>
        private static List<Tuple<int, double>> GetNearNodes(Point3D search, Point3D[] points, double searchRadiusMultiplier)
        {
            // Get the distances to each point
            double[] distSquared = points.Select(o => (o - search).LengthSquared).ToArray();

            // Find the smallest distance
            int smallestIndex = 0;
            for (int cntr = 1; cntr < distSquared.Length; cntr++)
            {
                if (distSquared[cntr] < distSquared[smallestIndex])
                {
                    smallestIndex = cntr;
                }
            }

            // Figure out how far out to allow
            double min = Math.Sqrt(distSquared[smallestIndex]);
            double maxSquared = Math.Pow(min * searchRadiusMultiplier, 2d);

            // Find all the points in range
            List<Tuple<int, double>> retVal = new List<Tuple<int, double>>();

            // This one is obviously in range (adding it now to avoid an unnessary sqrt)
            retVal.Add(new Tuple<int, double>(smallestIndex, min));

            for (int cntr = 0; cntr < distSquared.Length; cntr++)
            {
                if (cntr == smallestIndex)
                {
                    continue;
                }

                if (distSquared[cntr] < maxSquared)
                {
                    retVal.Add(new Tuple<int, double>(cntr, Math.Sqrt(distSquared[cntr])));
                }
            }

            // Exit Function
            return retVal;
        }
        public static Dodecahedron GetDodecahedron(double radius, Transform3D transform = null)
        {
            // This is 12 pentagons

            #region Points

            double t = (1d + Math.Sqrt(5d)) / 2d;
            double t1 = 1d / t;

            Point3D[] points = new Point3D[]
            {
                //(±1, ±1, ±1)
                new Point3D(1, 1, 1),       // 0
                new Point3D(1, 1, -1),      // 1
                new Point3D(1, -1, 1),      // 2
                new Point3D(1, -1, -1),     // 3
                new Point3D(-1, 1, 1),      // 4
                new Point3D(-1, 1, -1),     // 5
                new Point3D(-1, -1, 1),     // 6
                new Point3D(-1, -1, -1),        // 7

                //(0, ±1/φ, ±φ)
                new Point3D(0, t1, t),      // 8
                new Point3D(0, t1, -t),     // 9
                new Point3D(0, -t1, t),     // 10
                new Point3D(0, -t1, -t),        // 11

                //(±1/φ, ±φ, 0)
                new Point3D(t1, t, 0),      // 12
                new Point3D(t1, -t, 0),     // 13
                new Point3D(-t1, t, 0),     // 14
                new Point3D(-t1, -t, 0),        // 15

                //(±φ, 0, ±1/φ)
                new Point3D(t, 0, t1),      // 16
                new Point3D(t, 0, -t1),     // 17
                new Point3D(-t, 0, t1),     // 18
                new Point3D(-t, 0, -t1),        // 19
            };

            double maxLength = points[8].ToVector().Length;     // this represents the longest vector
            double ratio = radius / maxLength;
            points = points.Select(o => (o.ToVector() * ratio).ToPoint()).ToArray();

            if (transform != null)
            {
                points = points.Select(o => transform.Transform(o)).ToArray();
            }

            #endregion

            int[][] pentagonPolys = new int[][]
            {
                new int [] { 2, 10, 6, 15, 13 },
                new int [] { 0, 8, 10, 2, 16 },
                new int [] { 0, 12, 14, 4, 8 },
                new int [] { 1, 9, 5, 14, 12 },
                new int [] { 1, 17, 3, 11, 9 },
                new int [] { 2, 13, 3, 17, 16 },
                new int [] { 3, 13, 15, 7, 11 },
                new int [] { 6, 18, 19, 7, 15 },
                new int [] { 4, 18, 6, 10, 8 },
                new int [] { 4, 14, 5, 19, 18 },
                new int [] { 5, 9, 11, 7, 19 },
                new int [] { 0, 16, 17, 1, 12 },
            };

            return new Dodecahedron(pentagonPolys, points);
        }
        public static TruncatedIcosahedron GetTruncatedIcosahedron(double radius, Transform3D transform = null)
        {
            #region Points

            double t = (1d + Math.Sqrt(5d)) / 2d;
            double t2 = t * 2d;
            double t3 = t * 3d;
            double tA = 1d + (2d * t);
            double tB = 2d + t;

            // Length compare:
            //  t3  4.8541019662496847
            //  tA  4.23606797749979
            //  tB  3.6180339887498949
            //  t2  3.23606797749979
            //  2   2
            //  t   1.6180339887498949
            //  1   1
            //  0   0

            Point3D[] points = new Point3D[]
            {
                //------------------------------------------------- X axis
                //(±3φ, 0, ±1)
                new Point3D(t3, 0, 1),      // 0
                new Point3D(-t3, 0, 1),
                new Point3D(t3, 0, -1),
                new Point3D(-t3, 0, -1),        // 3

                //(±(1+2φ), ±φ, ±2)
                new Point3D(tA, t, 2),      // 4
                new Point3D(tA, -t, 2),
                new Point3D(-tA, t, 2),
                new Point3D(-tA, -t, 2),
                new Point3D(tA, t, -2),
                new Point3D(tA, -t, -2),
                new Point3D(-tA, t, -2),
                new Point3D(-tA, -t, -2),       // 11

                //(±(2+φ), ±2φ, ±1)
                new Point3D(tB, t2, 1),     // 12
                new Point3D(tB, t2, -1),
                new Point3D(tB, -t2, 1),
                new Point3D(tB, -t2, -1),
                new Point3D(-tB, t2, 1),
                new Point3D(-tB, t2, -1),
                new Point3D(-tB, -t2, 1),
                new Point3D(-tB, -t2, -1),      // 19

                //------------------------------------------------- Y axis
                //(±1, ±3φ, 0)
                new Point3D(1, t3, 0),      // 20
                new Point3D(1, -t3, 0),
                new Point3D(-1, t3, 0),
                new Point3D(-1, -t3, 0),        // 23

                //(±2, ±(1+2φ), ±φ)
                new Point3D(2, tA, t),      // 24
                new Point3D(2, tA, -t),
                new Point3D(2, -tA, t),
                new Point3D(2, -tA, -t),
                new Point3D(-2, tA, t),
                new Point3D(-2, tA, -t),
                new Point3D(-2, -tA, t),
                new Point3D(-2, -tA, -t),       // 31

                //(±1, ±(2+φ), ±2φ)
                new Point3D(1, tB, t2),     // 32
                new Point3D(1, tB, -t2),
                new Point3D(1, -tB, t2),
                new Point3D(1, -tB, -t2),
                new Point3D(-1, tB, t2),
                new Point3D(-1, tB, -t2),
                new Point3D(-1, -tB, t2),
                new Point3D(-1, -tB, -t2),      // 39

                //------------------------------------------------- Z axis
                //(0, ±1, ±3φ)
                new Point3D(0, 1, t3),      // 40
                new Point3D(0, 1, -t3),
                new Point3D(0, -1, t3),
                new Point3D(0, -1, -t3),        // 43

                //(±φ, ±2, ±(1+2φ))
                new Point3D(t, 2, tA),      // 44
                new Point3D(-t, 2, tA),
                new Point3D(t, 2, -tA),
                new Point3D(-t, 2, -tA),
                new Point3D(t, -2, tA),
                new Point3D(-t, -2, tA),
                new Point3D(t, -2, -tA),
                new Point3D(-t, -2, -tA),       // 51

                //(±2φ, ±1, ±(2+φ))
                new Point3D(t2, 1, tB),     // 52
                new Point3D(t2, 1, -tB),
                new Point3D(t2, -1, tB),
                new Point3D(t2, -1, -tB),
                new Point3D(-t2, 1, tB),
                new Point3D(-t2, 1, -tB),
                new Point3D(-t2, -1, tB),
                new Point3D(-t2, -1, -tB),      // 59
            };

            double maxLength = points[0].ToVector().Length;
            double ratio = radius / maxLength;
            points = points.Select(o => (o.ToVector() * ratio).ToPoint()).ToArray();

            if (transform != null)
            {
                points = points.Select(o => transform.Transform(o)).ToArray();
            }

            #endregion

            // Pentagons
            int[][] pentagonPolys = new int[][]
            {
                new int [] { 40, 44, 32, 36, 45 },
                new int [] { 42, 49, 38, 34, 48 },
                new int [] { 41, 47, 37, 33, 46 },
                new int [] { 43, 50, 35, 39, 51 },
                new int [] { 1, 7, 58, 56, 6 },
                new int [] { 0, 4, 52, 54, 5 },
                new int [] { 3, 10, 57, 59, 11 },
                new int [] { 2, 9, 55, 53, 8 },
                new int [] { 18, 19, 31, 23, 30 },
                new int [] { 14, 26, 21, 27, 15 },
                new int [] { 12, 13, 25, 20, 24 },
                new int [] { 16, 28, 22, 29, 17 },
            };

            // Hexagons
            int[][] hexagonPolys = new int[][]
            {
                new int [] { 40, 45, 56, 58, 49, 42 },
                new int [] { 40, 42, 48, 54, 52, 44 },
                new int [] { 41, 43, 51, 59, 57, 47 },
                new int [] { 41, 46, 53, 55, 50, 43 },
                new int [] { 1, 6, 16, 17, 10, 3 },
                new int [] { 1, 3, 11, 19, 18, 7 },
                new int [] { 0, 2, 8, 13, 12, 4 },
                new int [] { 0, 5, 14, 15, 9, 2 },
                new int [] { 34, 26, 14, 5, 54, 48 },
                new int [] { 32, 44, 52, 4, 12, 24 },
                new int [] { 38, 49, 58, 7, 18, 30 },
                new int [] { 33, 25, 13, 8, 53, 46 },
                new int [] { 35, 50, 55, 9, 15, 27 },
                new int [] { 36, 28, 16, 6, 56, 45 },
                new int [] { 39, 31, 19, 11, 59, 51 },
                new int [] { 37, 47, 57, 10, 17, 29 },
                new int [] { 20, 25, 33, 37, 29, 22 },
                new int [] { 20, 22, 28, 36, 32, 24 },
                new int [] { 21, 26, 34, 38, 30, 23 },
                new int [] { 21, 23, 31, 39, 35, 27 },
            };

            return new TruncatedIcosahedron(pentagonPolys, hexagonPolys, points);
        }
        public static Rhombicuboctahedron GetRhombicuboctahedron(double sizeX, double sizeY, double sizeZ, Transform3D transform = null)
        {
            #region Points

            double hX = sizeX / 2d;
            double hY = sizeY / 2d;
            double hZ = sizeZ / 2d;

            double sqrt2_1 = Math.Sqrt(2) + 1d;
            double sX = (sizeX / sqrt2_1) / 2d;     // sX is half the width of one of the faces (the faces form an octogon)
            double sY = (sizeY / sqrt2_1) / 2d;
            double sZ = (sizeZ / sqrt2_1) / 2d;

            // Points
            Point3D[] points = new Point3D[]
            {
                // Top 4
                new Point3D(sX, sY, hZ),        // 0
                new Point3D(sX, -sY, hZ),       // 1
                new Point3D(-sX, -sY, hZ),      // 2
                new Point3D(-sX, sY, hZ),      // 3

                // Top 8
                new Point3D(hX, sY, sZ),        // 4
                new Point3D(hX, -sY, sZ),       // 5
                new Point3D(sX, -hY, sZ),       // 6
                new Point3D(-sX, -hY, sZ),      // 7
                new Point3D(-hX, -sY, sZ),      // 8
                new Point3D(-hX, sY, sZ),       // 9
                new Point3D(-sX, hY, sZ),       // 10
                new Point3D(sX, hY, sZ),        // 11

                // Bottom 8
                new Point3D(hX, sY, -sZ),       // 12
                new Point3D(hX, -sY, -sZ),      // 13
                new Point3D(sX, -hY, -sZ),      // 14
                new Point3D(-sX, -hY, -sZ),     // 15
                new Point3D(-hX, -sY, -sZ),     // 16
                new Point3D(-hX, sY, -sZ),      // 17
                new Point3D(-sX, hY, -sZ),      // 18
                new Point3D(sX, hY, -sZ),       // 19

                // Bottom 4
                new Point3D(sX, sY, -hZ),       // 20
                new Point3D(sX, -sY, -hZ),      // 21
                new Point3D(-sX, -sY, -hZ),     // 22
                new Point3D(-sX, sY, -hZ),     // 23
            };

            if (transform != null)
            {
                points = points.Select(o => transform.Transform(o)).ToArray();
            }

            #endregion

            int[][] squarePolys_Orth = new int[][]
            {
                new int[] { 0, 3, 2, 1 },       // Top
                new int[] { 4, 5, 13, 12 },     // Right
                new int[] { 6, 7, 15, 14 },     // Front
                new int[] { 8, 9, 17, 16 },     // Left
                new int[] { 10, 11, 19, 18 },       // Back
                new int[] { 20, 21, 22, 23 },       // Bottom
            };

            int[][] squarePolys_Diag = new int[][]
            {
                // Top 4 angled
                new int[] {0, 1, 5, 4 },
                new int[] { 1, 2, 7, 6 },
                new int[] { 2, 3, 9, 8 },
                new int[] { 0, 11, 10, 3 },

                // Middle 4 angled
                new int[] { 4, 12, 19, 11 },
                new int[] { 5, 6, 14, 13 },
                new int[] { 7, 8, 16, 15 },
                new int[] { 9, 10, 18, 17 },

                // Bottom 4 angled
                new int[] { 12, 13, 21, 20 },
                new int[] { 14, 15, 22, 21 },
                new int[] { 16, 17, 23, 22 },
                new int[] { 18, 19, 20, 23 },
            };

            TriangleIndexed[] triangles = new TriangleIndexed[]
            {
                // Top 4
                new TriangleIndexed(0, 4, 11, points),
                new TriangleIndexed(1, 6, 5, points),
                new TriangleIndexed(2, 8, 7, points),
                new TriangleIndexed(3, 10, 9, points),

                // Bottom 4
                new TriangleIndexed(12, 20, 19, points),
                new TriangleIndexed(13, 14, 21, points),
                new TriangleIndexed(15, 16, 22, points),
                new TriangleIndexed(17, 18, 23, points),
            };

            return new Rhombicuboctahedron(squarePolys_Orth, squarePolys_Diag, triangles, points);
        }
        public static Icosidodecahedron GetIcosidodecahedron(double radius, Transform3D transform = null)
        {
            //NOTE: Don't confuse this with an icosahedron.  That is the more common object (made of equilateral triangles).
            //This object is made of pentagons and triangles.  I'm just making this because it looks cool

            #region Points

            double t = (1d + Math.Sqrt(5d)) / 2d;
            double t2 = t / 2d;
            double t3 = (1d + t) / 2d;

            Point3D[] points = new Point3D[]
            {
                //(0,0,±φ)
                new Point3D(0, 0, t),       // 0
                new Point3D(0, 0, -t),      // 1

                //(0,±φ,0)
                new Point3D(0, t, 0),       // 2
                new Point3D(0, -t, 0),      // 3

                //(±φ,0,0)
                new Point3D(t, 0, 0),       // 4
                new Point3D(-t, 0, 0),      // 5

                //(±1/2, ±φ/2, ±(1+φ)/2)
                new Point3D(.5, t2, t3),        // 6
                new Point3D(.5, t2, -t3),       // 7
                new Point3D(.5, -t2, t3),       // 8
                new Point3D(.5, -t2, -t3),      // 9
                new Point3D(-.5, t2, t3),       // 10
                new Point3D(-.5, t2, -t3),      // 11
                new Point3D(-.5, -t2, t3),      // 12
                new Point3D(-.5, -t2, -t3),     // 13

                //(±φ/2, ±(1+φ)/2, ±1/2)
                new Point3D(t2, t3, .5),        // 14
                new Point3D(t2, t3, -.5),       // 15
                new Point3D(t2, -t3, .5),       // 16
                new Point3D(t2, -t3, -.5),      // 17
                new Point3D(-t2, t3, .5),       // 18
                new Point3D(-t2, t3, -.5),      // 19
                new Point3D(-t2, -t3, .5),      // 20
                new Point3D(-t2, -t3, -.5),     // 21

                //(±(1+φ)/2, ±1/2, ±φ/2)
                new Point3D(t3, .5, t2),        // 22
                new Point3D(t3, .5, -t2),       // 23
                new Point3D(t3, -.5, t2),       // 24
                new Point3D(t3, -.5, -t2),      // 25
                new Point3D(-t3, .5, t2),       // 26
                new Point3D(-t3, .5, -t2),      // 27
                new Point3D(-t3, -.5, t2),      // 28
                new Point3D(-t3, -.5, -t2),     // 29
            };

            double maxLength = points[6].ToVector().Length;     // this represents the longest vector
            double ratio = radius / maxLength;
            points = points.Select(o => (o.ToVector() * ratio).ToPoint()).ToArray();

            if (transform != null)
            {
                points = points.Select(o => transform.Transform(o)).ToArray();
            }

            #endregion

            int[][] pentagonPolys = new int[][]
            {
                new int [] { 0, 10, 26, 28, 12 },
                new int [] { 26, 18, 19, 27, 5 },
                new int [] { 5, 29, 21, 20, 28 },
                new int [] { 10, 6, 14, 2, 18 },
                new int [] { 3, 16, 8, 12, 20 },
                new int [] { 0, 8, 24, 22, 6 },
                new int [] { 9, 17, 3, 21, 13 },
                new int [] { 27, 11, 1, 13, 29 },
                new int [] { 4, 24, 16, 17, 25 },
                new int [] { 1, 7, 23, 25, 9 },
                new int [] { 4, 23, 15, 14, 22 },
                new int [] { 2, 15, 7, 11, 19 },
            };

            TriangleIndexed[] triangles = new TriangleIndexed[]
            {
                new TriangleIndexed(0, 12, 8, points),
                new TriangleIndexed(0, 6, 10, points),
                new TriangleIndexed(10, 18, 26, points),
                new TriangleIndexed(5, 28, 26, points),
                new TriangleIndexed(12, 28, 20, points),
                new TriangleIndexed(3, 20, 21, points),
                new TriangleIndexed(8, 16, 24, points),
                new TriangleIndexed(3, 17, 16, points),
                new TriangleIndexed(9, 25, 17, points),
                new TriangleIndexed(4, 25, 23, points),
                new TriangleIndexed(4, 22, 24, points),
                new TriangleIndexed(13, 21, 29, points),
                new TriangleIndexed(1, 9, 13, points),
                new TriangleIndexed(1, 11, 7, points),
                new TriangleIndexed(11, 27, 19, points),
                new TriangleIndexed(5, 27, 29, points),
                new TriangleIndexed(6, 22, 14, points),
                new TriangleIndexed(2, 14, 15, points),
                new TriangleIndexed(2, 19, 18, points),
                new TriangleIndexed(7, 15, 23, points),
            };

            return new Icosidodecahedron(pentagonPolys, triangles, points);
        }
        private static Vector3D[] DistributeForces_UNECESSARRILYCOMLEX(Point3D[] bodyCenters, Point3D hitStart, Vector3D hitDirection, double mainBodyRadius, bool useDistDot = true, double distDotPow = .4)
        {
            Vector3D hitDirectionUnit = hitDirection.ToUnit();
            double hitForceBase = hitDirection.Length / mainBodyRadius;

            var vectors = bodyCenters.
                Select(o =>
                {
                    Vector3D toPoint = o - hitStart;
                    Vector3D toPointUnit = toPoint.ToUnit();

                    double dot = Vector3D.DotProduct(hitDirectionUnit, toPointUnit);
                    double linearDot = Math3D.GetLinearDotProduct(hitDirectionUnit, toPointUnit);

                    Vector3D along = toPoint.GetProjectedVector(hitDirection).ToUnit(false);
                    Vector3D orth = (o - Math3D.GetClosestPoint_Line_Point(hitStart, hitDirection, o)).ToUnit(false);

                    along *= linearDot;
                    orth *= (1 - linearDot);

                    double distance = toPoint.Length;

                    // Exaggerate the distance based on dot product.  That way, points in line with the hit will get more of the impact
                    double scale = (1 - Math.Abs(linearDot));
                    scale = Math.Pow(scale, distDotPow);
                    double scaledDistance = scale * distance;

                    return new
                    {
                        ForcePoint = o,
                        Distance = distance,
                        ScaledDistance = scaledDistance,
                        Along = along,
                        Orth = orth,
                    };
                }).
                ToArray();

            double[] distances = useDistDot ?
                vectors.Select(o => o.ScaledDistance).ToArray() :
                vectors.Select(o => o.Distance).ToArray();

            double[] percents = ExplosionForceWorker.GetPercentOfProjForce(distances);

            Vector3D[] retVal = new Vector3D[vectors.Length];

            for (int cntr = 0; cntr < vectors.Length; cntr++)
            {
                Vector3D along = vectors[cntr].Along * (hitForceBase * percents[cntr]);
                Vector3D orth = vectors[cntr].Orth * (hitForceBase * percents[cntr]);

                retVal[cntr] = along + orth;
            }

            return retVal;
        }
        /// <summary>
        /// This will figure how much velocity to apply to each bodyCenter
        /// TODO: Also calculate angular velocities
        /// </summary>
        /// <param name="bodyCenters">The bodies to apply velocity to</param>
        /// <param name="hitStart">The point of impact</param>
        /// <param name="hitDirection">The direction and force of the impact</param>
        /// <param name="mainBodyRadius">The avg radius of the body that is getting blown apart</param>
        private static Vector3D[] DistributeForces(Point3D[] bodyCenters, Point3D hitStart, Vector3D hitDirection, double mainBodyRadius, HullVoronoiExploder_Options options)
        {
            Vector3D hitDirectionUnit = hitDirection.ToUnit();
            double hitForceBase = hitDirection.Length / mainBodyRadius;

            var vectors = bodyCenters.
                Select(o =>
                {
                    Vector3D direction = o - hitStart;
                    Vector3D directionUnit = direction.ToUnit();

                    double distance = direction.Length;
                    double scaledDistance = distance;
                    if (options.DistanceDot_Power != null)
                    {
                        double linearDot = Math3D.GetLinearDotProduct(hitDirectionUnit, directionUnit);     // making it linear so the power function is more predictable

                        // Exaggerate the distance based on dot product.  That way, points in line with the hit will get more of the impact
                        double scale = (1 - Math.Abs(linearDot));
                        scale = Math.Pow(scale, options.DistanceDot_Power.Value);
                        scaledDistance = scale * distance;
                    }

                    return new
                    {
                        ForcePoint = o,
                        Distance = distance,
                        ScaledDistance = scaledDistance,
                        DirectionUnit = directionUnit,
                    };
                }).
                ToArray();

            double[] percents = GetPercentOfProjForce(vectors.Select(o => o.ScaledDistance).ToArray());

            Vector3D[] retVal = new Vector3D[vectors.Length];

            for (int cntr = 0; cntr < vectors.Length; cntr++)
            {
                retVal[cntr] = vectors[cntr].DirectionUnit * (hitForceBase * percents[cntr]);
            }

            return retVal;
        }
        private static Point3D[] GetPlacements(int count, double minDistanceSquared, Point3D[] existing, Func<Vector3D> getRandVector)
        {
            Vector3D[] existingCast = existing == null ? new Vector3D[0] : existing.Select(o => o.ToVector()).ToArray();

            List<Vector3D> retVal = new List<Vector3D>();

            for (int cntr = 0; cntr < count; cntr++)
            {
                while (true)
                {
                    // Ask the delegate for a random vector
                    Vector3D test = getRandVector();

                    // Make sure this isn't too close to the other items
                    bool wasTooClose = false;

                    foreach (Vector3D prev in UtilityCore.Iterate(existingCast, retVal))
                    {
                        double distance = (test - prev).LengthSquared;
                        if (distance < minDistanceSquared)
                        {
                            wasTooClose = true;
                            break;
                        }
                    }

                    if (!wasTooClose)
                    {
                        retVal.Add(test);
                        break;
                    }
                }
            }

            // Exit Function
            return retVal.Select(o => o.ToPoint()).ToArray();
        }
        private static Point3D[] GetSpherePlacement(int count, double radius, double factor, Point3D[] existing)
        {
            // Calculate min distance
            double fourThirds = 4d / 3d;
            double volume = fourThirds * Math.PI * radius * radius * radius;
            volume *= factor;		// give some slack (factor needs to be less than 1, the smaller it is, the closer they're allowed to be together)
            volume /= count + (existing == null ? 0 : existing.Length);		// the sum of the little volumes can't exceed the total volume

            double minDistance = Math.Pow(volume / (fourThirds * Math.PI), 1d / 3d);		// run the volume equation backward to get the radius

            // Convert to a vector array
            Vector3D[] existingVectors = null;
            if (existing != null)
            {
                existingVectors = existing.Select(o => o.ToVector()).ToArray();
            }

            // Use this method instead.  It should be a bit more efficient
            return Math3D.GetRandomVectors_Spherical_ClusteredMinDist(count, radius, minDistance, .001d, 1000, null, existingVectors, null).Select(o => o.ToPoint()).ToArray();
        }
        private double GetPercentVisible(Point3D[] polygon, Point3D[][] clipPolygons)
        {
            var transforms = Math2D.GetTransformTo2D(polygon);

            Point[][] clip2D = clipPolygons.Select(o => o.Select(p => transforms.Item1.Transform(p).ToPoint2D()).ToArray()).ToArray();

            // Merge the polygons
            Polygon2D[] unions = null;
            try
            {
                unions = GetPolyUnion(clip2D);
            }
            catch (Exception ex)
            {
                throw;
            }

            // Get the area of the outer polygon
            double outerArea = Math2D.GetAreaPolygon(polygon.Select(o => transforms.Item1.Transform(o)).ToArray());

            // Get the area of the inner polygons
            double innerArea = 0d;
            foreach (Polygon2D inner in unions)
            {
                double innerPoly = Math2D.GetAreaPolygon(inner.Polygon);
                double innerHoles = inner.Holes.Sum(o => Math2D.GetAreaPolygon(o));

                // Add to the total
                innerArea += innerPoly - innerHoles;
            }

            return (outerArea - innerArea) / outerArea;
        }
        private static Visual3D GetVisual_PolygonHull(Point3D[] polygon, Color color, double thickness)
        {
            if (polygon.Length < 3)
            {
                throw new ApplicationException("Need at least 3 points for a polygon");
            }

            Vector3D normal = Math2D.GetPolygonNormal(polygon, PolygonNormalLength.Unit) * (thickness * .5d);

            Point3D[] top = polygon.Select(o => o + normal).ToArray();
            Point3D[] bottom = polygon.Select(o => o - normal).ToArray();

            TriangleIndexed[] topTriangles = Math2D.GetTrianglesFromConvexPoly(top);
            TriangleIndexed[] bottomTriangles = Math2D.GetTrianglesFromConvexPoly(bottom);

            //TODO: make triangles out of the sides


            // Material
            MaterialGroup materials = new MaterialGroup();
            materials.Children.Add(new DiffuseMaterial(new SolidColorBrush(color)));
            materials.Children.Add(new SpecularMaterial(new SolidColorBrush(UtilityWPF.AlphaBlend(Colors.White, color, .5d)), 30d));

            Model3DGroup geometries = new Model3DGroup();

            // Geometry Model
            GeometryModel3D geometry = new GeometryModel3D();
            geometry.Material = materials;
            geometry.BackMaterial = materials;
            geometry.Geometry = UtilityWPF.GetMeshFromTriangles_IndependentFaces(topTriangles);

            geometries.Children.Add(geometry);

            geometry = new GeometryModel3D();
            geometry.Material = materials;
            geometry.BackMaterial = materials;
            geometry.Geometry = UtilityWPF.GetMeshFromTriangles_IndependentFaces(bottomTriangles);

            geometries.Children.Add(geometry);

            // Visual
            ModelVisual3D retVal = new ModelVisual3D();
            retVal.Content = geometries;

            return retVal;
        }
        private static Item3D DrawBrainGroup(Viewport3D viewport, Point3D[] points, Point3D center, Color color)
        {
            var lines = points.
                Select(o => Tuple.Create(o, center));

            Visual3D visual = AddLines(viewport, lines, color, .09);

            return new Item3D(visual, 0, center);
        }