// compute FWN cache for all points underneath this box
        protected void make_box_fast_winding_cache(int iBox, IEnumerable <int> pointIndices)
        {
            Util.gDevAssert(FastWindingCache.ContainsKey(iBox) == false);

            // construct cache
            var cacheInfo = new FWNInfo();

            FastPointWinding.ComputeCoeffs(points, pointIndices, FastWindingAreaCache,
                                           ref cacheInfo.Center, ref cacheInfo.R, ref cacheInfo.Order1Vec, ref cacheInfo.Order2Mat);

            FastWindingCache[iBox] = cacheInfo;
        }
        // evaluate the FWN cache for iBox
        protected double evaluate_box_fast_winding_cache(int iBox, ref Vector3d q)
        {
            FWNInfo cacheInfo = FastWindingCache[iBox];

            if (FWNApproxOrder == 2)
            {
                return(FastPointWinding.EvaluateOrder2Approx(ref cacheInfo.Center, ref cacheInfo.Order1Vec, ref cacheInfo.Order2Mat, ref q));
            }
            else
            {
                return(FastPointWinding.EvaluateOrder1Approx(ref cacheInfo.Center, ref cacheInfo.Order1Vec, ref q));
            }
        }
        // evaluate winding number contribution for all points below iBox
        protected double branch_fast_winding_num(int iBox, Vector3d p)
        {
            double branch_sum = 0;

            int idx = box_to_index[iBox];

            if (idx < points_end)
            {                        // point-list case, array is [N t1 t2 ... tN]
                int num_pts = index_list[idx];
                for (int i = 1; i <= num_pts; ++i)
                {
                    int      pi = index_list[idx + i];
                    Vector3d v  = Points.GetVertex(pi);
                    Vector3d n  = Points.GetVertexNormal(pi);
                    double   a  = FastWindingAreaCache[pi];
                    branch_sum += FastPointWinding.ExactEval(ref v, ref n, a, ref p);
                }
            }
            else
            {                                            // internal node, either 1 or 2 child boxes
                int iChild1 = index_list[idx];
                if (iChild1 < 0)
                {                                 // 1 child, descend if nearer than cur min-dist
                    iChild1 = (-iChild1) - 1;

                    // if we have winding cache, we can more efficiently compute contribution of all points
                    // below this box. Otherwise, recursively descend tree.
                    bool contained = box_contains(iChild1, p);
                    if (contained == false && can_use_fast_winding_cache(iChild1, ref p))
                    {
                        branch_sum += evaluate_box_fast_winding_cache(iChild1, ref p);
                    }
                    else
                    {
                        branch_sum += branch_fast_winding_num(iChild1, p);
                    }
                }
                else
                {                                            // 2 children, descend closest first
                    iChild1 = iChild1 - 1;
                    int iChild2 = index_list[idx + 1] - 1;

                    bool contained1 = box_contains(iChild1, p);
                    if (contained1 == false && can_use_fast_winding_cache(iChild1, ref p))
                    {
                        branch_sum += evaluate_box_fast_winding_cache(iChild1, ref p);
                    }
                    else
                    {
                        branch_sum += branch_fast_winding_num(iChild1, p);
                    }

                    bool contained2 = box_contains(iChild2, p);
                    if (contained2 == false && can_use_fast_winding_cache(iChild2, ref p))
                    {
                        branch_sum += evaluate_box_fast_winding_cache(iChild2, ref p);
                    }
                    else
                    {
                        branch_sum += branch_fast_winding_num(iChild2, p);
                    }
                }
            }

            return(branch_sum);
        }