Parse

File Parse gearbox.js

This tree is parsed live from the source file.

Classes

  • {{ item.name }}

    • {{ key }}

Not Classes

{{ getTree() }}

Comments

{{ getTreeComments() }}

Source

            
class GearBox {

    constructor(points) {
        this.tick = 0
        this.doSpokes = true
        /* For xy bound wheels */
        // this.bindMap = new Map()
        // this.bindMapRev = new Map()
        this.bindMap = new XYBindMap()

        this.points = points == undefined? new PointList: points
    }

    performDraw(ctx) {
        this.drawView(ctx)
        this.performStep()
    }

    bindPinionWheels(large, pinion) {

        // pinion.parentWheel = large
        // large.childWheel = pinion
        // pinion.xy = large.xy
        // pinion.isPinion = true

        // this.bindMap.set(large, pinion)
        // this.bindMapRev.set(pinion, large)
        this.bindMap.connect(large, pinion)
    }

    ensureDoubleBound(){
        /*
            Iterate the bindmap, ensuring the XY of a pair match -
         */
        this.bindMap.step()

        return

    }

    performStep() {
        this.ensureDoubleBound()
        let tick = this.tick += 1
        let ps = this.points

        if(this.waiting) {
            // previous break-early
            let extended = this.recursiveSpinTouchMap(tick, this.waiting, ps, 3)
            if(extended) {
                console.warn('too far')
            }
        }

        let tm = this.spinGears(ps)
        this.waiting = this.recursiveSpinTouchMap(tick, tm, ps, 6)
        this.pushVelocities(ps)
    }

    pushVelocities(ps) {
        /* Iterate all the points, applying the angular velocity
        given in the compute steps.
        */
        ps.forEach((p)=> {
            // here we can _spend_ the amount of velocity.
            p.rotation += p.angularVelocity

            if(p.motor) { return }

            /* This is an issue for pinions, as the size difference
            computes differently.
            Without this, _untouched_ points continue to rotate.

            The solution is to bubble the change to the child or parent.
            However, this means a points velocity is computed twice.
            */
            p.angularVelocity *= .93
        })

    }

    recursiveSpinTouchMap(tick, touchMap, ps, maxCount=5, currentCount=0) {
        if(touchMap.size <= 0) {
            // nothing to do.
            return
        }

        if(currentCount > maxCount) {
            /* break early.*/
            // console.warn('breakEaryl', maxCount)
            return touchMap
        }

        let replacement = this.spinChildren(touchMap, ps, tick)
        return this.recursiveSpinTouchMap(tick, replacement, ps, maxCount, currentCount+1)
    }

    spinChildren(touchMap, allGears, hitTick) {
        /*
        An entry in the touch map:

            origin: [touchPoint, ...]

            the touch points is not rotated yet, and should receive the
            rotation of the origin.

            Returned is a new touchmap, where each touchPoint is the origin
            and the touches are next gears - devoid of the origin.
        */
        let nextTouchMap = new Map;

        const getTouching =  function(wheel, origin) {
            return this.getSingleTouching(
                wheel,
                allGears,
                origin, /* origin is the motor
                        - don't feed backward.*/
            )
        }.bind(this)

        const safeAppendTouch = function(parentWheel, touching){
            if(touching.length == 0) {
                return
            }

            // store down into a new touch map.
            // nextTouchMap.set(motor, touching)
            // origin: touches
            this.appendMap(nextTouchMap, parentWheel, touching)
        }.bind(this)

        const safeAppendTouchesActive = function(map, touchPoint, func){

            let boundWheels = map.get(touchPoint)
            if(boundWheels == undefined){ return }
                /* This is a reduction gear (a gear with a smaller gear
                (pinion) within. The touchPoint is bound to the `b` point.
                */

                /* Wheel wheel performs a locked (pinned) angularVelocity */
                // wheelWheel(touchPoint, boundWheels)
                // func(touchPoint, boundWheels)

                // /* Push bound point touches, */
                // let touching = getTouching(boundWheels, touchPoint)
                // safeAppendTouch(boundWheels, touching)
            activateFunction(touchPoint, boundWheels, func)
        }//.bind(this)

        const activateFunction = function(target, origins, func) {
            origins.forEach(origin=>{

                func(target, origin)
                /* Push bound point touches, */
                let touching = getTouching(origin, target)
                safeAppendTouch(origin, touching)
            })
        }

        const activateFunctionInverted = function(target, origin, func) {
            // push rotation
            func(origin, target)
            let touching = getTouching(target, origin)
            safeAppendTouch(target, touching)
        }

        const forEachEach = function(touchMap, func) {
            // Origin should be motor.
            touchMap.forEach((touchPoints, origin)=> {
                // Iterate the touchPoints array, rotating and getting hits.
                touchPoints.forEach((touchPoint) => {
                    func(origin, touchPoint, touchPoints)
                })
            })
        }

        let callback = (origin, touchPoint, touchPoints)=> {
            const doubleHit = touchPoint._hitTick == hitTick
            touchPoint.doubleHit = doubleHit

            if(doubleHit) {
                // fancy math?
                this.doubleTouchCV(origin, touchPoint)
                return
            }

            /*assign tick - for future test.*/
            touchPoint._hitTick = hitTick
            /* Note, this must go _above the bindmap touches it seems,
            else the pinion wheel _slip_ relative to their radius. */

            if(touchPoint.internal || origin.internal){
                /* Internal touches are internalWheel of which rotate
                in the same direction.*/
                activateFunctionInverted(touchPoint, origin, this.internalWheel.bind(this))
            }else {
                activateFunctionInverted(touchPoint, origin, this.gearWheel.bind(this))
            }

            /* Bubble pinion and internal wheels.*/
            safeAppendTouchesActive(this.bindMap.bindMap, touchPoint, this.wheelWheel.bind(this))
            safeAppendTouchesActive(this.bindMap.bindMapRev, touchPoint, this.wheelWheel.bind(this))
        }

        forEachEach(touchMap, callback)

        return nextTouchMap
    }

    spinGears(allGears) {
        /*Filter touching.
        store as descendant.
        Loop each descendant
        store descendant.
        If already visited - raise error
        */
        /* Naturally rotate motors.*/
        let motors = allGears.filter(this.isMotor)
        // motors.each.rotation = (p)=> p.rotation + p.motor
        motors.each.angularVelocity = (p) => p.motor;
        return this.getTouching(motors,allGears)
    }

    getTouching(motors, allGears) {
        let touchMap = new Map;
        // For each item, we store its _next_ items.
        for(let motor of motors) {
            let touching = this.getSingleTouching(motor, allGears, motor)

            if(touching.length == 0) {
                continue
            }

            this.appendMap(touchMap, motor, touching)
        }

        return touchMap
    }

    getSingleTouching(target, allGears, exceptPoint=undefined) {
        /* return a list of all touching points for this target,
        except the exceptPoint*/
        let touching = pointToManyContact(target, allGears, true, 5)
        return touching.filter(p=> p != exceptPoint);
    }

    appendMap(map, key, pointList) {
        let items = pointList
        if(map.has(key)) {
            const existing =  map.get(key)
            let newTouches = existing.concat(pointList)
            items = newTouches
        }
        map.set(key, items)
    }

    wheelWheel(parentOriginPoint, boundReceiverPoint) {
        /*
        The parentOriginPoint was rotated by the origin, the bound point should receive its rotation
        being an inner wheel of the parentOriginPoint
         */

        // (boundReceiverPoint.ratchet == 0) == both directions
        if( this.ratchetAllowed(parentOriginPoint, boundReceiverPoint) ){
            // both directions.
            boundReceiverPoint.angularVelocity = parentOriginPoint.angularVelocity;
            return
        }
    }


    doubleTouchCV(originPoint, touchPoint) {
        // Angular velocity it given by a previous, and now this.
        // B should be the max of itself or the given.

        // console.warn('double touch', hitTick)
        // Average.
        if(originPoint.angularVelocity == touchPoint.angularVelocity) {
            // Same - no change required.
            touchPoint.doubleHit = false
            return
        }

        touchPoint.doubleHit = true
        // let newA = circleA.angularVelocity
        // if(circleA.angularVelocity < 0){
        //     newA = Math.min(circleB.angularVelocity, circleA.angularVelocity)
        // }

        // if(circleA.angularVelocity > 0){
        //     newA = Math.max(circleB.angularVelocity, circleA.angularVelocity)
        // }

        // circleB.angularVelocity = newA
        // circleB.rotation += circleB.angularVelocity
    }


    isMotor(point) {
        let mv = point.motor
        if(mv === undefined || mv === false) {
            return false
        }

        return true
    }


    drawView(ctx){
        /* Draw a circle at the origin points */
        let style = { color: "#333", line: {color: 'white'}}
        let notMotorColor = '#AAA'
        let motorColor = '#EE55CC'
        let doSpokes = this.doSpokes
        this.points.forEach((p)=> {
            // spokes.
            let angle = p.internal? Math.PI: 0
            doSpokes && p.split(~~(p.radius/5), angle).pen.indicators(ctx, style)
            let color = p.motor == undefined? notMotorColor: motorColor
            let lineColor = 'green'
            if(~~(Math.abs(p.angularVelocity) * 1000) == 0) {
                lineColor = '#333'
            }
            if(p.doubleHit === true) {
                lineColor = 'red'
            }
            p.pen.indicator(ctx, { color, line: {color: lineColor}})
        });

        // let color = p.motor == undefined? '#999': '#444'
        // const c = { circle: { color, width: 1}}
        // this.points.pen.indicators(ctx, c)
        // this.others.pen.indicators(ctx, this.rawPointConf)
    }

    addGear(p, angularVelocity=1){
        if(!p.angularVelocity) {
            p.angularVelocity = angularVelocity
        }
        this.points.push(p)
    }

    addPairGear(primary, pinion) {
        /* A pair gear applies a primary wheel and a pinion wheen -
        bound together at the center. When one of the gears rotates, the other
        will also rotate at the same speed.
        */
        this.addGear(primary)
        this.addGear(pinion)
        // this.points.push(primary, pinion)
        this.bindPinionWheels(primary, pinion)
    }

    gearWheel(originPoint, receiverPoint) {
        /*
         A Gear defines a point edge meeting another point edge.
         Calculate and apply the angularVelocity using the origin and receiver radius.

         Note the originPoint.angularVelocity must exist.
        */
      // originPoint and receiverPoint each have:
      //   radius: number
      //   angularVelocity: number (radians per second or degrees per second)

      // Angular velocity of B given A:
        if( this.ratchetAllowed(originPoint, receiverPoint) ){
            receiverPoint.angularVelocity = -this.calculateReceiverVelocity(originPoint, receiverPoint)
        }
        // circleB.rotation += circleB.angularVelocity
    }

    internalWheel(originPoint, receiverPoint) {
        /*
         A Gear defines a point edge meeting another point edge.
         Calculate and apply the angularVelocity using the origin and receiver radius.

         Note the originPoint.angularVelocity must exist.
        */
      // originPoint and receiverPoint each have:
      //   radius: number
      //   angularVelocity: number (radians per second or degrees per second)

      // Angular velocity of B given A:
        if( this.ratchetAllowed(originPoint, receiverPoint) ){
            receiverPoint.angularVelocity = this.calculateReceiverVelocity(originPoint, receiverPoint)
        }
      // circleB.rotation += circleB.angularVelocity
    }

    calculateReceiverVelocity(originPoint, receiverPoint){
        return (originPoint.radius / receiverPoint.radius) * originPoint.angularVelocity;
    }

    ratchetAllowed(parentOriginPoint, boundReceiverPoint) {
        /* Ratcheting ensures the boundReceiverPoint can (and is) being spun
        in the ratchet direction `-1, 0, 1,` left, both, right respectively.

        If pass, return True:

            + If ratcheting == undefined, assume 0
            + if 0; both directions
            + if -1 ratchet and angularVelocity < 0; ratchet left
            + if 1 ratchet and angularVelocity > 0; ratchet right

        */
        let canRatchet = (
                          (boundReceiverPoint.ratchet == undefined)
                          || (boundReceiverPoint.ratchet < 0 && parentOriginPoint.angularVelocity < 0)
                          || (boundReceiverPoint.ratchet > 0 && parentOriginPoint.angularVelocity > 0)
                        )
        return canRatchet;
    }

    createGear(options) {
        let p = new Point(options)
        this.addGear(p)
        return p
    }

    createReductionGear(options) {
        let p = new Point(options)
        let pin = p.copy().update({ radius: p.radius * .5 })
        this.addPairGear(p, pin)
        return [p, pin]
    }

    createInternalGear(options) {
        let pair = this.createReductionGear(options)
        pair[1].internal = true
        return pair
    }

    createMotor(options) {
        let p = this.createGear(options)
        if(p.motor == undefined) {
            p.motor = 1
        }
        return p
    }
}


copy