/// <summary>
            /// This returns all the unique edge triangles of the cells passed in (each face of the cell cube has two triangles), and which
            /// cells share each of those triangles
            /// </summary>
            private static Tuple<ITriangle, int[]>[] GetBlockedCellsSprtEdgeMap(Rectangle3DIndexedMapped[] cells)
            {
                if (cells.Length == 0)
                {
                    return new Tuple<ITriangle, int[]>[0];
                }

                // Get the triangles for each cell
                List<Tuple<int, Tuple<int, int, int>>> trianglesPerCell = new List<Tuple<int, Tuple<int, int, int>>>();

                for (int cntr = 0; cntr < cells.Length; cntr++)
                {
                    Tuple<int, int, int>[] trianglesOrdered = cells[cntr].GetEdgeTriangles().Select(o =>
                    {
                        int[] indices = o.IndexArray.OrderBy(p => p).ToArray();     // sorting them so they can be easily compared to other cell's triangles
                        return Tuple.Create(indices[0], indices[1], indices[2]);
                    }).
                    ToArray();

                    trianglesPerCell.AddRange(trianglesOrdered.Select(o => Tuple.Create(cntr, o)));
                }

                Point3D[] points = cells[0].AllPoints;

                // Now group by triangle
                var retVal = trianglesPerCell.GroupBy(o => o.Item2).
                    Select(o =>
                    {
                        ITriangle triangle = new TriangleIndexed(o.Key.Item1, o.Key.Item2, o.Key.Item3, points);

                        int[] correspondingCells = o.Select(p => p.Item1).Distinct().ToArray();

                        return Tuple.Create(triangle, correspondingCells);
                    }).
                    ToArray();

                // Exit Function
                return retVal;
            }
        public Rectangle3DIndexedMapped[] GetCells(double rectangleSize, bool shouldTransform = false)
        {
            double cellSize = rectangleSize / _size;
            double halfSize = rectangleSize / 2d;

            Transform3DGroup transform = null;
            if (shouldTransform)
            {
                transform = new Transform3DGroup();
                transform.Children.Add(new RotateTransform3D(new QuaternionRotation3D(this.RotationWorld)));
                transform.Children.Add(new TranslateTransform3D(this.PositionWorld.ToVector()));
            }

            // Corner Points
            int cornerSize = _size + 1;
            Point3D[] cornerPoints = new Point3D[cornerSize * cornerSize * cornerSize];

            for (int x = 0; x < cornerSize; x++)
            {
                for (int y = 0; y < cornerSize; y++)
                {
                    for (int z = 0; z < cornerSize; z++)
                    {
                        Point3D point = new Point3D(-halfSize + (x * cellSize), -halfSize + (y * cellSize), -halfSize + (z * cellSize));
                        cornerPoints[FluidField3D.Get1DIndex(x, y, z, cornerSize)] = shouldTransform ? transform.Transform(point) : point;
                    }
                }
            }

            // Cells
            Rectangle3DIndexedMapped[] retVal = new Rectangle3DIndexedMapped[_size1D];

            for (int x = 0; x < _size; x++)
            {
                for (int y = 0; y < _size; y++)
                {
                    for (int z = 0; z < _size; z++)
                    {
                        int index = Get1DIndex(x, y, z);
                        retVal[index] = new Rectangle3DIndexedMapped(new Mapping_3D_1D(x, y, z, index), cornerPoints, new int[] 
                            {
                                FluidField3D.Get1DIndex(x, y, z, cornerSize),
                                FluidField3D.Get1DIndex(x, y, z + 1, cornerSize),
                                FluidField3D.Get1DIndex(x, y + 1, z, cornerSize),
                                FluidField3D.Get1DIndex(x, y + 1, z + 1, cornerSize),
                                FluidField3D.Get1DIndex(x + 1, y, z, cornerSize),
                                FluidField3D.Get1DIndex(x + 1, y, z + 1, cornerSize),
                                FluidField3D.Get1DIndex(x + 1, y + 1, z, cornerSize),
                                FluidField3D.Get1DIndex(x + 1, y + 1, z + 1, cornerSize)
                            });
                    }
                }
            }

            return retVal;
        }
            /// <summary>
            /// This returns all the unique corner points of the cells passed in, and which cells share each of those points
            /// </summary>
            private static Tuple<Point3D, int[]>[] GetBlockedCellsSprtPointMap(Rectangle3DIndexedMapped[] cells)
            {
                if (cells.Length == 0)
                {
                    return new Tuple<Point3D, int[]>[0];
                }

                // Build an intermediate map
                SortedList<int, List<int>> points_cells = new SortedList<int, List<int>>();

                for (int cntr = 0; cntr < cells.Length; cntr++)
                {
                    foreach (int index in cells[cntr].Indices)
                    {
                        if (!points_cells.ContainsKey(index))
                        {
                            points_cells.Add(index, new List<int>());
                        }

                        points_cells[index].Add(cntr);
                    }
                }

                Point3D[] allPoints = cells[0].AllPoints;

                // Build the final map
                List<Tuple<Point3D, int[]>> retVal = new List<Tuple<Point3D, int[]>>();

                foreach (int pointIndex in points_cells.Keys)
                {
                    retVal.Add(Tuple.Create(allPoints[pointIndex], points_cells[pointIndex].ToArray()));
                }

                // Exit Function
                return retVal.ToArray();
            }