Parse

File Parse smooth-number.js

This tree is parsed live from the source file.

Classes

  • {{ item.name }}

    • {{ key }}

Not Classes

{{ getTree() }}

Comments

{{ getTreeComments() }}

Source

            /*
title: Smooth Number
---

> The `SmoothNumber` class receives fast number updates, and returns a _averaged_ number when queried.


        const smoothVal = new SmoothNumber()
        speedNumber.push(stage.mouse.speed())

        stage.fps.text = speedNumber

A `SmoothNumber` combines a average of many values in a list, visually
updated a (Framerate) modulo speed.


Push numbers into the list, to get an average over time allows for nicer-looking
updates on a fast changing number, such as the FPS.

## Setup

Create a new instance of the `SmoothNumber`:

    const initValue = 40
        , width = 20      // how many historical points
        , updateRate = 10 // every X frames
        , valueFix = 0    // value.toFixed

    const smoothVal = new SmoothNumber(initValue, width, updateRate, valueFix)

Then we can push a value to it:

    smoothVal.push(currentFPS)

It can be done many (many) times. When required, we read the number:

    smoothVal.get()

This secretly returns the `smoothVal.value` - but checks to ensure the value is fresh.

### Easier

To save the hassle of _pushing_ then _getting_ a number, we can do this with one call.

    fpsValue = this.smoothVal.pushGet(Math.round(currentFPS)+1)

This adheres to the update-rate, so the number `fpsValue` will change slower than
directly reading `currentFPS`.

This is nicer for humans - as most of them can't read text at 60FPS+ apparently?



*/
class SmoothNumber {
    /* A SmoothNumber provides a nicer read-back of a number over a period
    of time. Similar to the FPS counter.

    Create an instance and push a number when required.
    Read the _value_ through a few routines.

    Create:
        const initValue = 40
        width = 20      // how many historical points
        updateRate = 10 // every X frames
        valueFix = 0    // value.toFixed
        const smoothVal = new SmoothNumber(initValue, width, updateRate, valueFix)
        smoothVal.push(currentFPS)

    Read:

        smoothVal.get()

    Push + Read:

        b = this.smoothVal.pushGet(Math.round(currentFPS)+1)

     */
    // Higher is a longer delay between visual text updates.
    modulusRate = 1
    // Count of position to store historical fps counts.
    width = 20
    // decimal accuracy. Generally 0 is preferred.
    fixed = 0

    constructor(value=0, width=10, modulusRate=1, fixed=null) {

        this.width = width;
        this.value = value
        this.fixed = fixed
        this.modulusRate = modulusRate

        let a = new Array(this.width)
        a.fill(value)
        this.ticks = a
        this.inc = 0
        this.dirty = -1
    }


    pushGet(val) {
        /* Apply a value, and return the current smooth value
        This returned the cached value not a new one. */
        return this.update(this.modulusRate, val)
    }

    push(val) {
        this.incrementPush(val)
        return this.modUpdate()
    }

    get() {
        if(this.dirty) { return this.mutateCompute() }
        return this.value
    }

    [Symbol.toPrimitive](hint){

        // return this.value;

        let o = {
            'number': ()=> Number(this.value)
            , 'string': ()=> this.fixValue(Number(this.value))
            // Upon operator (+)
            , 'default': ()=> this.value
        }

        let f = o[hint]
        f = (f == undefined)? f=()=>this:f

        return f()
    }


    update(modulusRate=this.modulusRate, val=1) {
        this.modUpdate(this.inc, modulusRate)
        this.incrementPush(val)
        return this.value
    }

    modUpdate(inc=this.inc, modulusRate=this.modulusRate) {
        /* Perform an update  if the modulo matches,
        return the value */
        if(inc % modulusRate == 0) {
            // this.value = val
            /* A cleaner value output takes the average of a list of fps counts.*/
            return this.mutateCompute()
        }

        return this.value
    }

    mutateCompute(){
        this.value = this.computeValue()
        this.dirty = 0
        return this.value
    }

    computeValue(){
        let r = (this.ticks.reduce((a,b) => a+b) / this.width)
        return this.fixValue(r)
    }

    fixValue(r, fixed=this.fixed){
        return fixed==null? r: r.toFixed(fixed)
    }

    incrementPush(storeValue, tick=1){
        let i = this.inc += tick

        if(this.inc % this.width == 0) {
             i = this.inc = 0
        }

        this.ticks[i] = storeValue
        this.dirty = 1
        return i
    }

    reset(value=Number(this.value)) {
        this.ticks.fill(value)
    }
}


copy