Parse

File Parse distances.js

This tree is parsed live from the source file.

Classes

  • {{ item.name }}

    • {{ key }}

Not Classes

{{ getTree() }}

Comments

{{ getTreeComments() }}

Source

            /*
Track the distances between given points.

    const d = new Distances;
    d.addPoints(...points);

    const near = d.closest(Point.mouse.position)

---

{% load content %}
<pre>{% code_content "events/mypoint-stage.js" %}</pre>

*/
class Distances {

    constructor(){
        this.points = new Map
    }

    addPoints() {

        for(let p of Array.from(arguments)){
            try{
                this.points.set(p.uuid, p)
            } catch(e) {
                if(p === undefined) {
                    console.error('Distances.addPoints was given an undefined object.')
                }
            }
        }
    }

    removePoints() {
        let r = []
        for(let p of Array.from(arguments)){
            let u = p.uuid
            r.push(this.points.delete(u?u:p))
        }
        return r
    }

    setPoints() {
        this.points.clear()
        this.addPoints.apply(this, arguments)
    }

    closest(point, maxDistance){
        /* Return the closest point to the given point, for example the nearest
        point to the mouse.

        Return the most near point at any distance:

            > distances.closest(mouse.position)
            point

        Return the most near point, also within 100px radius distance:

            > distances.closest(mouse.position, 100)

        Of which is the same as:

            // within 100px of the mouse position
            > distances.within(100, mouse.position)

        */
        var p;
        var low = undefined;
        /* Each distance is tested against the curent _low_.
        If the distance `v` is less than `low`, store `p`.

        In the first iteration, `setterFunc`, does not do a test, and replaces
        _itself_, with a testing function.

        This ensures we don't need to check for the first `undefined` .
        */
        if(maxDistance != undefined) {
            return this.within(maxDistance, point)
        }

        var setterFunc = (v, e) => {
            low = v
            p = e
            setterFunc = (v, e) => {
                if(v < low) {
                    low = v;
                    p = e
                }
            }
        }
        this.each(function(e,i,a){
            let t = point.distanceTo(e)
            setterFunc(t, e)
        })
        return p
    }

    /*
        Check if a point _intersects_ another point, returning the interecting
        top point.

            > intersect(mouse)
            point  // a point under the mouse.

        To _pad_ the overlap distance, provide an addition padding value:

            > intersect(mouse, 10) // add 10px around the perimeter
            point

        Useful if the intersection point has a radius:

            > intersect(mouse, mouse.radius) // add 10px around the perimeter

        This is the same as calling `closest()`, with the second arg as a function
        for radius testing.
     */
    intersect(point, padding) {
        return this.closest(point, (v,p)=> v<=padding+p.radius)
    }

    /* Return a point within the max distance set

        // return the closest point within 100px (radius) from the mouse position
        > distances.within(100, this.mouse.position)
        point

    For a _list_ of points, use `near`.

        near(point, distance=point.radius)
        [point, ...]

    */
    within(maxDistance, point){

        if(isPoint(maxDistance)) {
            maxDistance = maxDistance.radius
        }

        var p;
        var low = undefined;
        /* Each distance is tested against the curent _low_.
        If the distance `v` is less than `low`, store `p`.

        In the first iteration, `setterFunc`, does not do a test, and replaces
        _itself_, with a testing function.

        This ensures we don't need to check for the first `undefined` .
        */

        let test = (v) => v < maxDistance
        if(isFunction(maxDistance)) {
            test = maxDistance
        }
        var setterFunc = (v, e) => {
            if(test(v, e)) {
                if(low == undefined || (v <= low)){
                    low = v
                    p = e
                }
            }
        }

        this.each(function(e,i,a){
            let t = point.distanceTo(e)
            setterFunc(t, e)
        })
        return p
    }

    /* Return a list of points near the the given point at a given distance.*/
    near(point, distance=point.radius){
        const ps = new Set()
        // if(distance == undefined) {
        //     distance = point.radius
        // }
        const setterFunc = (v, e) => {
            /* Add to the store, if the value is within the distance.*/
            if(v > distance) { return }
            ps.add(e)
        }

        this.each(function(e,i,a){
            let t = point.distanceTo(e)
            setterFunc(t, e)
        })
        return ps
    }

    each(caller) {
        return this.points.forEach(caller)
    }

    keep(caller) {
        let e = []
        return this.points.forEach((e,i,a)=> {
            let res = caller(e,i,a)
            if(res !== undefined) {
                r.push(e)
            }
        })

        return e
    }
};



Polypoint.head.mixin('Point', {
    isNaN: {
        value(any=false) {
            let r = 0;
            r += +isNaN(this.x)
            r += +isNaN(this.y)
            if(r==0) { return false }

            if(r > 0) {
                if(any) { return true }
                // two NaNs, is always isNaN == true
                if(r >= 2) { return true }
            }

            return false
        }
        , writable: true
    }
    , distanceTo: {
        value(other) {
            return distance(this, other)
        }
    }

    , distance2D: {
        value(other) {
            return distance2D(this, other)
        }
    }
})


// Polypoint.head.static('Point', {
//     distance: {
//         value(a, b){
//             return Math.hypot(b.x - a.x, b.y - a.y);
//         }
//     }
// })


function distance(xy1, xy2) {
  return Math.sqrt(Math.pow((xy2.x - xy1.x), 2) + Math.pow((xy2.y - xy1.y), 2));
}


function distance2D(xy1, xy2) {
    const dx = xy1.x - xy2.x;
    const dy = xy1.y - xy2.y;
    const distance = Math.sqrt(dx * dx + dy * dy);
    return { x: dx, y: dy, distance }
}


/*const*/ approx_distance = function(dx,dy ) {

   if ( dx < 0 ) dx = -dx;
   if ( dy < 0 ) dy = -dy;

    let min = dy;
    let max = dx;
    if(dx < dy) {
        min = dx;
        max = dy;
   }

   let approx = ( max * 1007 ) + ( min * 441 );
   if ( max < ( min << 4 )) {
      approx -= ( max * 40 );
   }

   // add 512 for proper rounding
   return (( approx + 512 ) >> 10 );
}



/*const*/ approx_distance2 = function(dx,dy ) {
   let min, max;

   if ( dx < 0 ) dx = -dx;
   if ( dy < 0 ) dy = -dy;

   if ( dx < dy )
   {
      min = dx;
      max = dy;
   } else {
      min = dy;
      max = dx;
   }

   // coefficients equivalent to ( 123/128 * max ) and ( 51/128 * min )
   return ((( max << 8 ) + ( max << 3 ) - ( max << 4 ) - ( max << 1 ) +
            ( min << 7 ) - ( min << 5 ) + ( min << 3 ) - ( min << 1 )) >> 8 );
}

copy