/// <summary>
 /// Add a mesh to the scalar grid
 /// </summary>
 /// <param name="m">Volumetric mesh</param>
 /// <param name="exp">Falloff Exponent</param>
 /// <param name="mass">Mass m</param>
 /// <param name="sg">Scalar grid</param>
 private void AddMesh(Mesh m, double exp, double mass, ref ScalarGrid3D sg)
 {
     if (m.IsClosed)
     {
         // add closed mesh
         var bb     = m.GetBoundingBox(true);
         var length = bb.Diagonal.Length * 1.1;
         for (var i = 0; i < sg.Count; i++)
         {
             var pt       = sg.EvaluatePoint(i);
             var isInside = false;
             Intersection.MeshLine(m, new Line(pt, Vector3d.XAxis, length), out var faces);
             if (faces != null)
             {
                 isInside = faces.Length % 2 == 1;
             }
             var cp = m.ClosestPoint(pt);
             sg[i] += CalculateMass(cp.DistanceTo(pt), mass, exp, isInside);
         }
     }
     else
     {
         // add open mesh
         for (var i = 0; i < sg.Count; i++)
         {
             var pt         = sg.EvaluatePoint(i);
             var foundPoint = m.ClosestPoint(pt);
             var dist       = foundPoint.DistanceTo(pt);
             sg[i] += CalculateMass(dist, mass, exp, false);
         }
     }
 }
 /// <summary>
 /// Add a point to the scalar grid. Currently an expensive operation
 /// </summary>
 /// <param name="pt"></param>
 /// <param name="exp"></param>
 /// <param name="mass"></param>
 /// <param name="vg"></param>
 public void AddPt(Point3d pt, double exp, double mass, ref ScalarGrid3D vg)
 {
     for (var i = 0; i < vg.Count; i++)
     {
         var dist = vg.EvaluatePoint(i).DistanceTo(pt);
         vg[i] += CalculateMass(dist, mass, exp);
     }
     //
 }
 /// <summary>
 /// Add an open brep (non inside detection)
 /// </summary>
 /// <param name="b"></param>
 /// <param name="exp"></param>
 /// <param name="mass"></param>
 /// <param name="vg"></param>
 public void AddOpenBrep(Brep b, double exp, double mass, ref ScalarGrid3D vg)
 {
     for (var i = 0; i < vg.Count; i++)
     {
         var pt   = vg.EvaluatePoint(i);
         var cp   = b.ClosestPoint(pt);
         var dist = pt.DistanceTo(cp);
         vg[i] += CalculateMass(dist, mass, exp);
     }
 }
        /// <summary>
        /// Add a curve to the scalar grid
        /// </summary>
        /// <param name="curve"></param>
        /// <param name="exp"></param>
        /// <param name="mass"></param>
        /// <param name="sg"></param>
        public void AddCrv(Curve curve, double exp, double mass, ref ScalarGrid3D sg)
        {
            var bb = curve.GetBoundingBox(true);

            bb.Inflate(sg.VoxelSize.X * 2, sg.VoxelSize.Y * 2, sg.VoxelSize.Z * 2);
            var maxDistance = Math.Sqrt(Math.Pow(sg.VoxelSize.X, 2) + Math.Pow(sg.VoxelSize.Y, 2) + Math.Pow(sg.VoxelSize.Z, 2));

            for (var i = 0; i < sg.Count; i++)
            {
                var pt = sg.EvaluatePoint(i);
                curve.ClosestPoint(pt, out var t);
                var cp   = curve.PointAt(t);
                var dist = cp.DistanceTo(pt);
                sg[i] += CalculateMass(dist, mass, exp);
            }
        }
        /// <summary>
        /// Add a box to the scalar grid
        /// </summary>
        /// <param name="oBox"></param>
        /// <param name="exp"></param>
        /// <param name="mass"></param>
        /// <param name="sg"></param>
        private void AddBox(Box oBox, double exp, double mass, ref ScalarGrid3D sg)
        {
            for (var i = 0; i < sg.Count; i++)
            {
                var pt       = sg.EvaluatePoint(i);
                var contains = oBox.Contains(pt, false);
                var cp       = oBox.ClosestPoint(pt);
                var distance = pt.DistanceTo(cp);

                if (contains)
                {
                    distance *= -1;
                }
                sg[i] = +CalculateMass(Convert.ToSingle(distance), mass, exp);
            }
        }
        /// <summary>
        /// Add a brep to the scalar grid
        /// </summary>
        /// <param name="b"></param>
        /// <param name="exp"></param>
        /// <param name="mass"></param>
        /// <param name="vg"></param>
        public void AddBrep(Brep b, double exp, double mass, ref ScalarGrid3D vg)
        {
            if (b == null || (b).Equals(default(Brep)) || !b.IsValid)
            {
                return;
            }

            if (!b.IsSolid)
            {
                AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Brep is not closed, brep will be treated as a surface");
                AddOpenBrep(b, exp, mass, ref vg);
            }
            else
            {
                AddSolidBrep(b, exp, mass, ref vg);
            }
        }
        /// <summary>
        /// Add a solid brep
        /// </summary>
        /// <param name="b"></param>
        /// <param name="exp"></param>
        /// <param name="mass"></param>
        /// <param name="vg"></param>
        public void AddSolidBrep(Brep b, double exp, double mass, ref ScalarGrid3D vg)
        {
            // get the axis direction for each point
            var pt1 = vg.EvaluatePoint(new Point3i(0, 0, 0));
            var pt2 = vg.EvaluatePoint(new Point3i(1, 0, 0));
            var pln = new Plane(pt1, (pt2 - pt1));

            for (var x = 0; x < vg.SizeUVW.X; x++)
            {
                pln.Origin = vg.EvaluatePoint(new Point3i(x, 0, 0));
                Intersection.BrepPlane(b, pln, DocumentHelper.GetModelTolerance(), out var sections, out var pts);
                var surfaces = Brep.CreatePlanarBreps(sections);


                // perhaps check first if the points are inside the bounding box of the surface.
                if (surfaces == null)
                {
                    continue;
                }
                for (var y = 0; y < vg.SizeUVW.Y; y++)
                {
                    for (var z = 0; z < vg.SizeUVW.Z; z++)
                    {
                        var isInside = false;
                        var pti      = new Point3i(x, y, z);
                        var pt       = vg.EvaluatePoint(pti);
                        var distance = b.ClosestPoint(pt).DistanceTo(pt);
                        for (var i = 0; i < surfaces.Length; i++)
                        {
                            //BoundingBox bb = surfaces[i].GetBoundingBox(false);
                            if (surfaces[i].ClosestPoint(pt).DistanceTo(pt) < DocumentHelper.GetModelTolerance())
                            {
                                isInside = true;
                                break;
                            }
                        }

                        vg[pti] = +CalculateMass(distance, mass, exp, isInside);
                    }
                }
            }
        }
        /// <summary>
        /// This is the method that actually does the work.
        /// </summary>
        /// <param name="DA">The DA object is used to retrieve from inputs and store in outputs.</param>
        protected override void SolveInstance(IGH_DataAccess DA)
        {
            var    bb = new Box();
            double x, y, z;

            x = y = z = 0;
            DA.GetData(1, ref x);
            DA.GetData(2, ref y);
            DA.GetData(3, ref z);
            DA.GetData(0, ref bb);

            var vg = new ScalarGrid3D(bb, new Point3d(x, y, z));

            if (!vg.IsValid)
            {
                AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Result was an invalid grid");
            }

            DA.SetData(0, new GH_ScalarGrid(vg));
        }
        /// <summary>
        /// Marching cubes algorithm
        /// </summary>
        /// <param name="vg"></param>
        /// <param name="points"></param>
        /// <param name="val"></param>
        /// <param name="isolevel"></param>
        /// <param name="m"></param>
        /// <param name="cubeindex"></param>
        /// <returns></returns>
        public int Polygonise(ScalarGrid3D vg, Point3i[] points, float[] val, float isolevel, ref Mesh m, out int cubeindex)
        {
            cubeindex = 0;
            if (val[0] < isolevel)
            {
                cubeindex |= 1;
            }
            if (val[1] < isolevel)
            {
                cubeindex |= 2;
            }
            if (val[2] < isolevel)
            {
                cubeindex |= 4;
            }
            if (val[3] < isolevel)
            {
                cubeindex |= 8;
            }
            if (val[4] < isolevel)
            {
                cubeindex |= 16;
            }
            if (val[5] < isolevel)
            {
                cubeindex |= 32;
            }
            if (val[6] < isolevel)
            {
                cubeindex |= 64;
            }
            if (val[7] < isolevel)
            {
                cubeindex |= 128;
            }

            /* Cube is entirely in/out of the surface */
            if (EdgeTable[cubeindex] == 0)
            {
                return(0);
            }
            var vertlist = new Point3d[12];

            /* Find the vertices where the surface intersects the cube */
            if ((EdgeTable[cubeindex] & 1) == 1)
            {
                vertlist[0] =
                    VertexInterp(isolevel, points[0], points[1], val[0], val[1]);
            }
            if ((EdgeTable[cubeindex] & 2) == 2)
            {
                vertlist[1] =
                    VertexInterp(isolevel, points[1], points[2], val[1], val[2]);
            }
            if ((EdgeTable[cubeindex] & 4) == 4)
            {
                vertlist[2] =
                    VertexInterp(isolevel, points[2], points[3], val[2], val[3]);
            }
            if ((EdgeTable[cubeindex] & 8) == 8)
            {
                vertlist[3] =
                    VertexInterp(isolevel, points[3], points[0], val[3], val[0]);
            }
            if ((EdgeTable[cubeindex] & 16) == 16)
            {
                vertlist[4] =
                    VertexInterp(isolevel, points[4], points[5], val[4], val[5]);
            }
            if ((EdgeTable[cubeindex] & 32) == 32)
            {
                vertlist[5] =
                    VertexInterp(isolevel, points[5], points[6], val[5], val[6]);
            }
            if ((EdgeTable[cubeindex] & 64) == 64)
            {
                vertlist[6] =
                    VertexInterp(isolevel, points[6], points[7], val[6], val[7]);
            }
            if ((EdgeTable[cubeindex] & 128) == 128)
            {
                vertlist[7] =
                    VertexInterp(isolevel, points[7], points[4], val[7], val[4]);
            }
            if ((EdgeTable[cubeindex] & 256) == 256)
            {
                vertlist[8] =
                    VertexInterp(isolevel, points[0], points[4], val[0], val[4]);
            }
            if ((EdgeTable[cubeindex] & 512) == 512)
            {
                vertlist[9] =
                    VertexInterp(isolevel, points[1], points[5], val[1], val[5]);
            }
            if ((EdgeTable[cubeindex] & 1024) == 1024)
            {
                vertlist[10] =
                    VertexInterp(isolevel, points[2], points[6], val[2], val[6]);
            }
            if ((EdgeTable[cubeindex] & 2048) == 2048)
            {
                vertlist[11] =
                    VertexInterp(isolevel, points[3], points[7], val[3], val[7]);
            }
            // evaluate all points between these 8 vertices

            var z = m.Vertices.Count;


            /* Create the triangle */
            var vertexCount = m.Vertices.Count;
            var triangles   = 0;

            for (var k = 0; TriTable[cubeindex, k] != -1; k += 3)
            {
                var facePts = new Point3d[3];
                facePts[0] = vg.EvaluatePoint(vertlist[TriTable[cubeindex, k]]);
                facePts[1] = vg.EvaluatePoint(vertlist[TriTable[cubeindex, k + 1]]);
                facePts[2] = vg.EvaluatePoint(vertlist[TriTable[cubeindex, k + 2]]);
                m.Vertices.AddVertices(facePts);
                // perhaps this should be switched around for the right normal direction
                m.Faces.AddFace(vertexCount, vertexCount + 1, vertexCount + 2);
                vertexCount += 3;
                triangles++;
            }
            return(triangles);
        }