void PushToHeap(int nodeIndex, float3 tempClosestPoint, float3 queryPosition, ref KnnQueryTemp temp)
        {
            float sqrDist = math.lengthsq(tempClosestPoint - queryPosition);

            KdQueryNode queryNode = new KdQueryNode {
                NodeIndex        = nodeIndex,
                TempClosestPoint = tempClosestPoint,
                Distance         = sqrDist
            };

            temp.MinHeap.PushObj(queryNode, sqrDist);
        }
        internal unsafe void KNearest(float3 queryPosition, NativeSlice <int> result, ref KnnQueryTemp temp)
        {
            int k = result.Length;

            temp.Heap.Clear();

            var points      = Points;
            var permutation = m_permutation;
            var rootNode    = RootNode;
            var nodePtr     = m_nodes.GetUnsafePtr();

            // Biggest Smallest Squared Radius
            float  bssr             = float.PositiveInfinity;
            float3 rootClosestPoint = rootNode.Bounds.ClosestPoint(queryPosition);

            PushToHeap(m_rootNodeIndex[0], rootClosestPoint, queryPosition, ref temp);

            // searching
            while (temp.MinHeap.Count > 0)
            {
                KdQueryNode queryNode = temp.MinHeap.PopObj();

                if (queryNode.Distance > bssr)
                {
                    continue;
                }

                ref KdNode node = ref UnsafeUtilityEx.ArrayElementAsRef <KdNode>(nodePtr, queryNode.NodeIndex);

                if (!node.Leaf)
                {
                    int    partitionAxis    = node.PartitionAxis;
                    float  partitionCoord   = node.PartitionCoordinate;
                    float3 tempClosestPoint = queryNode.TempClosestPoint;

                    if (tempClosestPoint[partitionAxis] - partitionCoord < 0)
                    {
                        // we already know we are on the side of negative bound/node,
                        // so we don't need to test for distance
                        // push to stack for later querying
                        PushToHeap(node.NegativeChildIndex, tempClosestPoint, queryPosition, ref temp);

                        // project the tempClosestPoint to other bound
                        tempClosestPoint[partitionAxis] = partitionCoord;

                        if (node.Count != 0)
                        {
                            PushToHeap(node.PositiveChildIndex, tempClosestPoint, queryPosition, ref temp);
                        }
                    }
                    else
                    {
                        // we already know we are on the side of positive bound/node,
                        // so we don't need to test for distance
                        // push to stack for later querying
                        PushToHeap(node.PositiveChildIndex, tempClosestPoint, queryPosition, ref temp);

                        // project the tempClosestPoint to other bound
                        tempClosestPoint[partitionAxis] = partitionCoord;

                        if (node.Count != 0)
                        {
                            PushToHeap(node.NegativeChildIndex, tempClosestPoint, queryPosition, ref temp);
                        }
                    }
                }
                else
                {
                    for (int i = node.Start; i < node.End; i++)
                    {
                        int   index   = permutation[i];
                        float sqrDist = math.lengthsq(points[index] - queryPosition);

                        if (sqrDist <= bssr)
                        {
                            temp.Heap.PushObj(index, sqrDist);

                            if (temp.Heap.Count == k)
                            {
                                bssr = temp.Heap.HeadValue;
                            }
                        }
                    }
                }
            }