public int SampleVelocityGrid(float[] pos, float rad, float vmax, float[] vel, float[] dvel, ref float[] nvel,
                                      ObstacleAvoidanceParams param, ObstacleAvoidanceDebugData debug = null)
        {
            Prepare(pos, dvel);

            _params       = new ObstacleAvoidanceParams(param);
            _invHorizTime = 1.0f / _params.horizTime;
            _vmax         = vmax;
            _invVmax      = 1.0f / vmax;

            Helper.VSet(ref nvel, 0, 0, 0);

            if (debug != null)
            {
                debug.Reset();
            }

            float cvx  = dvel[0] * _params.velBias;
            float cvz  = dvel[2] * _params.velBias;
            float cs   = vmax * 2 * (1 - _params.velBias) / (float)(_params.gridSize - 1);
            float half = (_params.gridSize - 1) * cs * 0.5f;

            float minPenalty = float.MaxValue;
            int   ns         = 0;

            for (int y = 0; y < _params.gridSize; y++)
            {
                for (int x = 0; x < _params.gridSize; x++)
                {
                    float[] vcand = new float[3];
                    vcand[0] = cvx + x * cs - half;
                    vcand[1] = 0;
                    vcand[2] = cvz + y * cs - half;

                    if (vcand[0] * vcand[0] + vcand[2] * vcand[2] > (vmax + cs / 2) * (vmax + cs / 2))
                    {
                        continue;
                    }

                    float penalty = ProcessSample(vcand, cs, pos, rad, vel, dvel, debug);
                    ns++;
                    if (penalty < minPenalty)
                    {
                        minPenalty = penalty;
                        Helper.VCopy(ref nvel, vcand);
                    }
                }
            }
            return(ns);
        }
        public int SampleVelocityAdaptive(float[] pos, float rad, float vmax, float[] vel, float[] dvel,
                                          ref float[] nvel, ObstacleAvoidanceParams param,
                                          ObstacleAvoidanceDebugData debug = null)
        {
            Prepare(pos, dvel);

            _params       = new ObstacleAvoidanceParams(param);
            _invHorizTime = 1.0f / _params.horizTime;
            _vmax         = vmax;
            _invVmax      = 1.0f / vmax;

            Helper.VSet(ref nvel, 0, 0, 0);

            if (debug != null)
            {
                debug.Reset();
            }

            float[] pat  = new float[(MaxPatternDivs * MaxPatternRings + 1) * 2];
            int     npat = 0;

            int ndivs  = _params.adaptiveDivs;
            int nrings = _params.adaptiveRings;
            int depth  = _params.adaptiveDepth;

            int   nd   = Math.Max(1, Math.Min(MaxPatternDivs, ndivs));
            int   nr   = Math.Max(1, Math.Min(MaxPatternRings, nrings));
            float da   = (1.0f / nd) * (float)Math.PI * 2f;
            float dang = (float)Math.Atan2(dvel[2], dvel[0]);

            pat[npat * 2 + 0] = 0;
            pat[npat * 2 + 1] = 0;
            npat++;

            for (int j = 0; j < nr; j++)
            {
                float r = (nr - j) / (float)nr;
                float a = dang + (j & 1) * 0.5f * da;
                for (int i = 0; i < nd; i++)
                {
                    pat[npat * 2 + 0] = (float)Math.Cos(a) * r;
                    pat[npat * 2 + 1] = (float)Math.Sin(a) * r;
                    npat++;
                    a += da;
                }
            }

            float cr = vmax * (1.0f - _params.velBias);

            float[] res = new float[3];
            Helper.VSet(ref res, dvel[0] * _params.velBias, 0, dvel[2] * _params.velBias);
            int ns = 0;

            for (int k = 0; k < depth; k++)
            {
                float   minPenalty = float.MaxValue;
                float[] bvel       = new float[3];
                Helper.VSet(ref bvel, 0, 0, 0);

                for (int i = 0; i < npat; i++)
                {
                    float[] vcand = { res[0] + pat[i * 2 + 0] * cr, 0, res[2] + pat[i * 2 + 1] * cr };

                    if (vcand[0] * vcand[0] + vcand[2] * vcand[2] > (vmax + 0.001f) * (vmax + 0.001f))
                    {
                        continue;
                    }

                    float penalty = ProcessSample(vcand, cr / 10f, pos, rad, vel, dvel, debug);
                    ns++;
                    if (penalty < minPenalty)
                    {
                        minPenalty = penalty;
                        Helper.VCopy(ref bvel, vcand);
                    }
                }
                Helper.VCopy(ref res, bvel);

                cr *= 0.5f;
            }

            Helper.VCopy(ref nvel, res);

            return(ns);
        }