Parse

File Parse constrain-distance.js

This tree is parsed live from the source file.

Classes

  • {{ item.name }}

    • {{ key }}

Not Classes

{{ getTree() }}

Comments

{{ getTreeComments() }}

Source

            
const constraints = {
    lte: (a, b) => a <= b
    , gt: (a, b) => a > b
    , any: (a, b) => true

    , _ifDistance: function(pointA, pointB, maxDistance, action=gt) {
        const d2d = pointA.distance2D(pointB)
            , distance = d2d.distance
            , r = action(distance, maxDistance)
            ;

        if(r) {
            /* Same as:

            const ratio = maxDistance / distance;
                pointB.x = pointA.x - d2d.x * ratio;
                pointB.y = pointA.y - d2d.y * ratio;
            */
            let t = pointA.subtract(
                    Point.from(d2d).multiply(maxDistance / distance)
                )
            pointB.copy(t)
        };
        return r
    }

    , distance(pointA, pointB, maxDistance) {
        return this._ifDistance(pointA, pointB, maxDistance, this.any)
    }

    , within(pointA, pointB, maxDistance) {
        return this._ifDistance(pointA, pointB, maxDistance, this.gt)
    }

    , inverse(pointA, pointB, maxDistance) {
        /*
            If the distance between A and B is greater than the given max distance,
            move point B away from point A.

            Or; Point B _avoids_ point A.
         */
        return this._ifDistance(pointA, pointB, maxDistance, this.lte)
    }

}


const stringAttach = function(followPoint, originPoint, settings) {
    // Apply gravity to the follow point's vertical velocity
    // Calculate the vector from the originPoint to the follow point
    // let dx = followPoint.x - originPoint.x ;
    // let dy = followPoint.y - originPoint.y ;
    const defaultSettings = {
        gravity: {x:0, y:1}
        , damping:.95
        , dotDamping:.2
        , forceMultiplier:.1
        , forceValue:undefined
        , distance: 100
    }

    const conf = Object.assign(defaultSettings, settings);

    const gravity = conf.gravity
        , damping = conf.damping
        , dotDamping = conf.dotDamping
        , forceMultiplier = conf.forceMultiplier
        , forceValue = conf.forceValue
        , stringLength = conf.distance
        ;

    let dx = originPoint.x - followPoint.x;
    let dy = originPoint.y - followPoint.y;

    // Calculate the current distance between the follow point and the originPoint
    let distance = Math.sqrt(dx * dx + dy * dy);

    // Set the follow point's position to be exactly on the circumference of the string length
    // const force = (distance - stringLength) * 0.01; // Tweak this factor as needed

    // If the distance exceeds the string length, we need to constrain the follow point
    if (distance > stringLength) {

        // Normalize the direction vector
        dx /= distance;
        dy /= distance;

        if(dotDamping!==false) {
            // Adjust the velocity so that it reflects the string tension
            let dotProduct = (followPoint.vx * dx + followPoint.vy * dy) * dotDamping;
            followPoint.vx -= dotProduct * dx;
            followPoint.vy -= dotProduct * dy;
        }

        if(forceMultiplier!==false){
            const force = forceValue? forceValue: (distance - stringLength) * forceMultiplier; // Tweak this factor as needed
            followPoint.vx += force * dx;
            followPoint.vy += force * dy;
        }

    }

    // Apply gravity
    if(gravity){
        followPoint.vy += gravity.y;
        followPoint.vx += gravity.x;
    }

    // Update the follow point's position based on its velocity
    followPoint.x += followPoint.vx;
    followPoint.y += followPoint.vy;

    if(damping) {
        // Apply damping continuously to smooth the motion
        followPoint.vx *= damping;
        followPoint.vy *= damping;
    }
};


class PointConstraints {


    constructor(point) {
        this.parent = point
        /* string stuff */
        this.gravity = {x:0, y:1}
        this.damping =.95
        this.dotDamping =.2
        this.forceMultiplier =.1
        this.forceValue =undefined
        this.distance = 100
    }

    /* Track another point using IK - this point follows the _other_ at a
    set distance. */
    track(other, settings) {
        // return followPoint(other, this, settings)
        return constraints.distance(other, this.parent, settings)
    }

    /* Track another point using constraints. This point follows the other
    point at a distance or less. */
    leash(other, settings) {
        return constraints.within(other, this.parent, settings)
    }

    /* Ensure this point does not overlap the _other_ point. If an overlap
    occurs, this point is moved. Fundamentally this is the antethsis of leash().*/
    avoid(other, settings) {
        return constraints.inverse(other, this.parent, settings)
    }

    elbow(other, settings){
        /* Connect this point and another point by their _edges_. Similar to
        _track_ at a distance, but accounting for radius. */
        let point = this.parent;
        /* Ensure a point stays within a distance. */
        this.leash(other, (other.radius + point.radius) - .01)
        /* Ensure the point binds to the edge of the target. */
        this.avoid(other, Math.abs(other.radius - point.radius) + .01)
    }

    string(other, settings){
        // Manipulate the _other_; with this entity as the origin owner.
        const c = Object.assign({
            gravity: this.gravity
            , damping: this.damping
            , dotDamping: this.dotDamping
            , forceMultiplier: this.forceMultiplier
            , forceValue: this.forceValue
            , distance: this.distance
        }, settings)
        return stringAttach(other, this.parent, c)
    }
}

Polypoint.head.deferredProp('Point',
    function constraint() {
        return new PointConstraints(this)
    }
);


Polypoint.head.installFunctions('Point', {
    /* Track another point using IK - this point follows the _other_ at a
    set distance. */
    track(other, settings) {
        // return followPoint(other, this, settings)
        return constraints.distance(other, this, settings)
    }

    /* Track another point using constraints. This point follows the other
    point at a distance or less. */
    , leash(other, settings) {
        return constraints.within(other, this, settings)
    }

    /* Ensure this point does not overlap the _other_ point. If an overlap
    occurs, this point is moved. Fundamentally this is the antethsis of leash().*/
    , avoid(other, settings) {
        return constraints.inverse(other, this, settings)
    }
})


// const _constrainDistance = function(pointA, pointB, maxDistance) {
//     // Calculate the distance between pointA and pointB
//     // const dx = pointA.x - pointB.x;
//     // const dy = pointA.y - pointB.y;
//     // const distance = Math.sqrt(dx * dx + dy * dy);
//     const distance = pointA.distanceTo(pointB)
//     const distance2D = pointA.distance2D(pointB)

//     if(gt(distance, maxDistance)) {
//         // const constrainedX = pointA.x - dx * ratio;
//         // const constrainedY = pointA.y - dy * ratio;
//         const ratio = maxDistance / distance;
//         const constrainedX = pointA.x - distance2D.x * ratio;
//         const constrainedY = pointA.y - distance2D.y * ratio;
//         pointB.x = constrainedX;
//         pointB.y = constrainedY;
//     }
// }


// const _inverseConstrainDistance = function(pointA, pointB, maxDistance) {
//     // Calculate the distance between pointA and pointB
//     const dx = pointA.x - pointB.x;
//     const dy = pointA.y - pointB.y;
//     // const distance = Math.sqrt(dx * dx + dy * dy);
//     const distance = pointA.distanceTo(pointB)
//     const distance2D = pointA.distance2D(pointB)

//     // If the distance is greater than the maxDistance, move pointB closer
//     if(lte(distance, maxDistance) ) {
//         const ratio = maxDistance / distance;
//         const constrainedX = pointA.x - distance2D.x * ratio;
//         const constrainedY = pointA.y - distance2D.y * ratio;
//         pointB.x = constrainedX;
//         pointB.y = constrainedY;
//     }
// }

copy