src/geometry.js
function(p, opt) {

        opt = opt || {};
        var precision = (opt.precision === undefined) ? this.PRECISION : opt.precision;
        var subdivisions = (opt.subdivisions === undefined) ? this.getSubdivisions({ precision: precision }) : opt.subdivisions;
        // does not use localOpt

        // identify the subdivision that contains the point:
        var investigatedSubdivision;
        var investigatedSubdivisionStartT; // assume that subdivisions are evenly spaced
        var investigatedSubdivisionEndT;
        var distFromStart; // distance of point from start of baseline
        var distFromEnd; // distance of point from end of baseline
        var chordLength; // distance between start and end of the subdivision
        var minSumDist; // lowest observed sum of the two distances
        var n = subdivisions.length;
        var subdivisionSize = (n ? (1 / n) : 0);
        for (var i = 0; i < n; i++) {

            var currentSubdivision = subdivisions[i];

            var startDist = currentSubdivision.start.distance(p);
            var endDist = currentSubdivision.end.distance(p);
            var sumDist = startDist + endDist;

            // check that the point is closest to current subdivision and not any other
            if (!minSumDist || (sumDist < minSumDist)) {
                investigatedSubdivision = currentSubdivision;

                investigatedSubdivisionStartT = i * subdivisionSize;
                investigatedSubdivisionEndT = (i + 1) * subdivisionSize;

                distFromStart = startDist;
                distFromEnd = endDist;

                chordLength = currentSubdivision.start.distance(currentSubdivision.end);

                minSumDist = sumDist;
            }
        }

        var precisionRatio = pow(10, -precision);

        // recursively divide investigated subdivision:
        // until distance between baselinePoint and closest path endpoint is within 10^(-precision)
        // then return the closest endpoint of that final subdivision
        while (true) {

            // check if we have reached at least one required observed precision
            // - calculated as: the difference in distances from point to start and end divided by the distance
            // - note that this function is not monotonic = it doesn't converge stably but has "teeth"
            // - the function decreases while one of the endpoints is fixed but "jumps" whenever we switch
            // - this criterion works well for points lying far away from the curve
            var startPrecisionRatio = (distFromStart ? (abs(distFromStart - distFromEnd) / distFromStart) : 0);
            var endPrecisionRatio = (distFromEnd ? (abs(distFromStart - distFromEnd) / distFromEnd) : 0);
            var hasRequiredPrecision = ((startPrecisionRatio < precisionRatio) || (endPrecisionRatio < precisionRatio));

            // check if we have reached at least one required minimal distance
            // - calculated as: the subdivision chord length multiplied by precisionRatio
            // - calculation is relative so it will work for arbitrarily large/small curves and their subdivisions
            // - this is a backup criterion that works well for points lying "almost at" the curve
            var hasMinimalStartDistance = (distFromStart ? (distFromStart < (chordLength * precisionRatio)) : true);
            var hasMinimalEndDistance = (distFromEnd ? (distFromEnd < (chordLength * precisionRatio)) : true);
            var hasMinimalDistance = (hasMinimalStartDistance || hasMinimalEndDistance);

            // do we stop now?
            if (hasRequiredPrecision || hasMinimalDistance) {
                return ((distFromStart <= distFromEnd) ? investigatedSubdivisionStartT : investigatedSubdivisionEndT);
            }

            // otherwise, set up for next iteration
            var divided = investigatedSubdivision.divide(0.5);
            subdivisionSize /= 2;

            var startDist1 = divided[0].start.distance(p);
            var endDist1 = divided[0].end.distance(p);
            var sumDist1 = startDist1 + endDist1;

            var startDist2 = divided[1].start.distance(p);
            var endDist2 = divided[1].end.distance(p);
            var sumDist2 = startDist2 + endDist2;

            if (sumDist1 <= sumDist2) {
                investigatedSubdivision = divided[0];

                investigatedSubdivisionEndT -= subdivisionSize; // subdivisionSize was already halved

                distFromStart = startDist1;
                distFromEnd = endDist1;

            } else {
                investigatedSubdivision = divided[1];

                investigatedSubdivisionStartT += subdivisionSize; // subdivisionSize was already halved

                distFromStart = startDist2;
                distFromEnd = endDist2;
            }
        }
    }
function(p, opt) {

        opt = opt || {};
        var precision = (opt.precision === undefined) ? this.PRECISION : opt.precision;
        var subdivisions = (opt.subdivisions === undefined) ? this.getSubdivisions({ precision: precision }) : opt.subdivisions;
        // does not use localOpt

        // identify the subdivision that contains the point:
        var investigatedSubdivision;
        var investigatedSubdivisionStartT; // assume that subdivisions are evenly spaced
        var investigatedSubdivisionEndT;
        var distFromStart; // distance of point from start of baseline
        var distFromEnd; // distance of point from end of baseline
        var chordLength; // distance between start and end of the subdivision
        var minSumDist; // lowest observed sum of the two distances
        var n = subdivisions.length;
        var subdivisionSize = (n ? (1 / n) : 0);
        for (var i = 0; i < n; i++) {

            var currentSubdivision = subdivisions[i];

            var startDist = currentSubdivision.start.distance(p);
            var endDist = currentSubdivision.end.distance(p);
            var sumDist = startDist + endDist;

            // check that the point is closest to current subdivision and not any other
            if (!minSumDist || (sumDist < minSumDist)) {
                investigatedSubdivision = currentSubdivision;

                investigatedSubdivisionStartT = i * subdivisionSize;
                investigatedSubdivisionEndT = (i + 1) * subdivisionSize;

                distFromStart = startDist;
                distFromEnd = endDist;

                chordLength = currentSubdivision.start.distance(currentSubdivision.end);

                minSumDist = sumDist;
            }
        }

        var precisionRatio = pow(10, -precision);

        // recursively divide investigated subdivision:
        // until distance between baselinePoint and closest path endpoint is within 10^(-precision)
        // then return the closest endpoint of that final subdivision
        while (true) {

            // check if we have reached at least one required observed precision
            // - calculated as: the difference in distances from point to start and end divided by the distance
            // - note that this function is not monotonic = it doesn't converge stably but has "teeth"
            // - the function decreases while one of the endpoints is fixed but "jumps" whenever we switch
            // - this criterion works well for points lying far away from the curve
            var startPrecisionRatio = (distFromStart ? (abs(distFromStart - distFromEnd) / distFromStart) : 0);
            var endPrecisionRatio = (distFromEnd ? (abs(distFromStart - distFromEnd) / distFromEnd) : 0);
            var hasRequiredPrecision = ((startPrecisionRatio < precisionRatio) || (endPrecisionRatio < precisionRatio));

            // check if we have reached at least one required minimal distance
            // - calculated as: the subdivision chord length multiplied by precisionRatio
            // - calculation is relative so it will work for arbitrarily large/small curves and their subdivisions
            // - this is a backup criterion that works well for points lying "almost at" the curve
            var hasMinimalStartDistance = (distFromStart ? (distFromStart < (chordLength * precisionRatio)) : true);
            var hasMinimalEndDistance = (distFromEnd ? (distFromEnd < (chordLength * precisionRatio)) : true);
            var hasMinimalDistance = (hasMinimalStartDistance || hasMinimalEndDistance);

            // do we stop now?
            if (hasRequiredPrecision || hasMinimalDistance) {
                return ((distFromStart <= distFromEnd) ? investigatedSubdivisionStartT : investigatedSubdivisionEndT);
            }

            // otherwise, set up for next iteration
            var divided = investigatedSubdivision.divide(0.5);
            subdivisionSize /= 2;

            var startDist1 = divided[0].start.distance(p);
            var endDist1 = divided[0].end.distance(p);
            var sumDist1 = startDist1 + endDist1;

            var startDist2 = divided[1].start.distance(p);
            var endDist2 = divided[1].end.distance(p);
            var sumDist2 = startDist2 + endDist2;

            if (sumDist1 <= sumDist2) {
                investigatedSubdivision = divided[0];

                investigatedSubdivisionEndT -= subdivisionSize; // subdivisionSize was already halved

                distFromStart = startDist1;
                distFromEnd = endDist1;

            } else {
                investigatedSubdivision = divided[1];

                investigatedSubdivisionStartT += subdivisionSize; // subdivisionSize was already halved

                distFromStart = startDist2;
                distFromEnd = endDist2;
            }
        }
    }
function ( p , opt ) { opt = opt || { } ; var precision = ( opt . precision === undefined ) ? this . PRECISION : opt . precision ; var subdivisions = ( opt . subdivisions === undefined ) ? this . getSubdivisions ( { precision : precision } ) : opt . subdivisions ; // does not use localOpt // identify the subdivision that contains the point: var investigatedSubdivision ; var investigatedSubdivisionStartT ; // assume that subdivisions are evenly spaced var investigatedSubdivisionEndT ; var distFromStart ; // distance of point from start of baseline var distFromEnd ; // distance of point from end of baseline var chordLength ; // distance between start and end of the subdivision var minSumDist ; // lowest observed sum of the two distances var n = subdivisions . length ; var subdivisionSize = ( n ? ( 1 / n ) : 0 ) ; for ( var i = 0 ; i < n ; i ++ ) { var currentSubdivision = subdivisions [ i ] ; var startDist = currentSubdivision . start . distance ( p ) ; var endDist = currentSubdivision . end . distance ( p ) ; var sumDist = startDist + endDist ; // check that the point is closest to current subdivision and not any other if ( ! minSumDist || ( sumDist < minSumDist ) ) { investigatedSubdivision = currentSubdivision ; investigatedSubdivisionStartT = i * subdivisionSize ; investigatedSubdivisionEndT = ( i + 1 ) * subdivisionSize ; distFromStart = startDist ; distFromEnd = endDist ; chordLength = currentSubdivision . start . distance ( currentSubdivision . end ) ; minSumDist = sumDist ; } } var precisionRatio = pow ( 10 , - precision ) ; // recursively divide investigated subdivision: // until distance between baselinePoint and closest path endpoint is within 10^(-precision) // then return the closest endpoint of that final subdivision while ( true ) { // check if we have reached at least one required observed precision // - calculated as: the difference in distances from point to start and end divided by the distance // - note that this function is not monotonic = it doesn't converge stably but has "teeth" // - the function decreases while one of the endpoints is fixed but "jumps" whenever we switch // - this criterion works well for points lying far away from the curve var startPrecisionRatio = ( distFromStart ? ( abs ( distFromStart - distFromEnd ) / distFromStart ) : 0 ) ; var endPrecisionRatio = ( distFromEnd ? ( abs ( distFromStart - distFromEnd ) / distFromEnd ) : 0 ) ; var hasRequiredPrecision = ( ( startPrecisionRatio < precisionRatio ) || ( endPrecisionRatio < precisionRatio ) ) ; // check if we have reached at least one required minimal distance // - calculated as: the subdivision chord length multiplied by precisionRatio // - calculation is relative so it will work for arbitrarily large/small curves and their subdivisions // - this is a backup criterion that works well for points lying "almost at" the curve var hasMinimalStartDistance = ( distFromStart ? ( distFromStart < ( chordLength * precisionRatio ) ) : true ) ; var hasMinimalEndDistance = ( distFromEnd ? ( distFromEnd < ( chordLength * precisionRatio ) ) : true ) ; var hasMinimalDistance = ( hasMinimalStartDistance || hasMinimalEndDistance ) ; // do we stop now? if ( hasRequiredPrecision || hasMinimalDistance ) { return ( ( distFromStart <= distFromEnd ) ? investigatedSubdivisionStartT : investigatedSubdivisionEndT ) ; } // otherwise, set up for next iteration var divided = investigatedSubdivision . divide ( 0.5 ) ; subdivisionSize /= 2 ; var startDist1 = divided [ 0 ] . start . distance ( p ) ; var endDist1 = divided [ 0 ] . end . distance ( p ) ; var sumDist1 = startDist1 + endDist1 ; var startDist2 = divided [ 1 ] . start . distance ( p ) ; var endDist2 = divided [ 1 ] . end . distance ( p ) ; var sumDist2 = startDist2 + endDist2 ; if ( sumDist1 <= sumDist2 ) { investigatedSubdivision = divided [ 0 ] ; investigatedSubdivisionEndT -= subdivisionSize ; // subdivisionSize was already halved distFromStart = startDist1 ; distFromEnd = endDist1 ; } else { investigatedSubdivision = divided [ 1 ] ; investigatedSubdivisionStartT += subdivisionSize ; // subdivisionSize was already halved distFromStart = startDist2 ; distFromEnd = endDist2 ; } } }
Returns `t` of the point on the curve closest to point `p`
Returns t of the point on the curve closest to point p