Parse

File Parse emitter.js

This tree is parsed live from the source file.

Classes

  • {{ item.name }}

    • {{ key }}

Not Classes

{{ getTree() }}

Comments

{{ getTreeComments() }}

Source

            /* An 'emitter' generates points with a velocity
This requires some sort of reactor to push the particles.

A single point can emit through an one direction, arc, or full pi
a line emits along it.

Each can be performed with split lerp.

    e = new Emitter(200, 300) //point
    e = new Emitter(Line) // alone a spline.
    newPoints = e.step()
    allPoints = e.points

*/


class Emitter extends Point {

    /* Every step performs a tick, used as the internal clock for
    generate and stepping */
    tick = 0

    // Every modulo to run an emission
    tickModulo = 20

    /* Speed of a single particle (+vx, +vy)*/
    particleSpeed = 2

    /*birthrate = how many particles over X*/
    birthrate = 1

    // speed = XY velocities of particles.
    // lifetime = age of particle before force death
    //
    // birth position = where across the line or point to render.
    //
    // Then we need point motion;
    //  rotation over time.
    //  some mutator (Value(smooth))

    /* Lifetime of a particle. If undefined, it's 50% the radius.*/
    lifetime = undefined //300

    // Emit from the center or the radius.
    fromEdge = false

    /* Which direction should a point emit, e.g. _forward_ (1,0) relative
    of the indicator.*/
    direction = {x:1, y:0}

    pointLimit = 2000

    wake() {
        this.points = new PointList
        // this.direction = new Point(this.direction)
    }

    getDirection(){

        return new Point(isFunction(this.direction)? this.direction(): this.direction)
    }

    step() {
        /* perform a tick for every interval, creating points, and pushing
        existing points */
        this.tick += 1

        if(this.tick % this.tickModulo == 0) {
            this.cycle()
        }

        this.points = PointList.from(this.points.filter((p)=> p.age < p.lifetime))
        this.points.forEach((p)=>{
            p.x += p.vx
            p.y += p.vy
            p.age += 1
        })
    }

    cycle() {
        /* Perform a task of one step, involving birthing, and killing
        points, selecting and rotation lerp points.*/
        let l = this.points.length
        if(l > this.pointLimit) { return }
        let birthrate = this.birthrate
        for (var i = 0; i < birthrate; i++) {
            let p = this.newPoint(i/birthrate)
            this.pump(p, i, birthrate)
            this.offsetSpawnedPoint(p, i, birthrate)
            this.points.push(p)
        }

        l = this.points.length
        if(this.lastCount != l) {
            // console.log('Point length', l)
            this.length = l
        }

        this.lastCount = l
    }

    pump(p, birthIndex, birthrate) {
    }

    newPoint(birthPartial) {
        /* Return a _ready to go_ point, using the local attributes
        as a template to produce the new point.

        This should include speed and any variants. */
        let fromEdge = this.fromEdge;
        let p = (fromEdge?this.project():this).copy()
        let v = this.particleSpeed //* (3 * Math.random())
        if(isFunction(v)) {v = v()}

        // let FORWARD = {x: random.float(-1,1) ,y:0} // this.direction
        // let FORWARD =  this.direction
        let FORWARD = this.getDirection()
        // create a vector in the direction of the nose
        // x=1 as forward is across to the right (0deg) by default.
        const rotatedDir = impartOnRads(this.radians, FORWARD)

        let lifetime = this.lifetime;
        if(lifetime == undefined) {
            lifetime = this.radius * .5;
        }

        p.update({
            age: 0
            , radius: 5// 1 + Math.random() * 10
            , lifetime: isFunction(lifetime)?lifetime():lifetime
            // , lifetime: lifetime// * Math.random()
            , vx: rotatedDir.x * v
            , vy: rotatedDir.y * v
        })

        return p
    }

    offsetSpawnedPoint(p){
        if(this.spawnOffset) {
            // debugger
            const _moved = impartOnRads(p.radians, new Point({x: -1, y:0}))
            p.x += _moved.x * p.radius - (p.vx * this.particleSpeed)
            p.y += _moved.y * p.radius - (p.vy * this.particleSpeed)
        }
    }
}


class LineEmitter extends Emitter {

    radiusVariant = 2
    minSize = 5
    maxSize = 10
    directionVariant = 5
    baseRadius = 5
    baseSpeed = 1
    lifetimeVariant = 2

    wake() {
        this.index = 0
        console.log('Wake')
        super.wake()
    }

    cachePoints(line, divider=1, pointing=0) {
        // console.log('Cache Points')
        this.positions = line.split(line.length * divider, pointing)
        this.lineLength = line.length
    }

    newPoint(birthPartial) {
        let choice = this.getChoice()

        let template = this.positions[choice]
        if(!template) {debugger}
        let p = template.copy()
        let v = this.particleSpeed //* (3 * Math.random())

        let FORWARD = new Point(random.choice([-1, 1]), 0)// this.direction
        // create a vector in the direction of the nose
        // x=1 as forward is across to the right (0deg) by default.
        const rotatedDir = impartOnRads(p.radians, FORWARD)

        let lifetime = this.lifetime;
        if(lifetime == undefined) {
            lifetime = this.lineLength * .5;
        }

        p.update({
            age: 0
            , radius: this.baseRadius// 1 + Math.random() * 10
            , lifetime: lifetime// * Math.random()
            , vx: rotatedDir.x * v
            , vy: rotatedDir.y * v
        })

        return p
    }

    getChoice(){
        // let choice = (this.index++) % this.positions.length
        let l = this.positions.length
        let choice = (Math.random() * l).toFixed(0)
        if(choice >= l) { choice = l - 1}
        if(choice < 0) { choice = 0}
        return choice
    }

    pump(point, birthIndex, birthrate) {
        let partial = birthIndex / birthrate
        let v = ((1+ partial) * (1+birthIndex)  * Math.random()) * this.directionVariant
        let s = ((1+ partial) * (1+birthIndex)  * Math.random()) * this.baseSpeed
        point.rotation += v * (Math.random()>.488? -1: 1)


        // let FORWARD = this.direction
        let FORWARD = this.getDirection()

        // create a vector in the direction of the nose
        // x=1 as forward is across to the right (0deg) by default.
        const rotatedDir = impartOnRads(point.radians, FORWARD)

        point.update({
            radius: clamp(
                        random.int(this.radius * this.radiusVariant)
                        , this.minSize
                        , this.maxSize
                    )
            , vx: rotatedDir.x * s
            , vy: rotatedDir.y * s
            , lifetime: point.lifetime * (Math.random() * this.lifetimeVariant)
        })

        return point
    }

}


class RandomPointEmitter extends Emitter {

    /* Per birth, how much should the radius vary for each point.
    from the minSize to the max (point radius) */
    radiusVariant = .1
    /* A rotation variant for the emitter per birth. between 0 and full rotation 360 */
    directionVariant = 1
    /* minimum particle size */
    minSize = 2

    pump(point, birthIndex, birthrate) {
        let partial = birthIndex / birthrate
        let v = ((1+ partial) * (1+birthIndex)  * Math.random()) * this.directionVariant
        this.rotation += v
        point.radius = clamp(random.int(this.radius * this.radiusVariant), this.minSize, this.radius)
    }

}


class PumpRandomPointEmitter extends RandomPointEmitter {
    fromEdge = true
    tickModulo = 90
    birthrate = 50
    lifetime = 300
    radiusVariant = .1
    directionVariant = 360
    minSize = 2
}


class DirectionalPointEmitter extends RandomPointEmitter {
    directionVariant = .05
    particleSpeed = .6
    lifetime = 200
    fromEdge = true
    tickModulo = 5
    //  speed = 100
    birthrate = .1
    pointLimit = 1000

    invert = false
    stepDirection(multplier){
        return this.speed2D.direction(multplier)
    }

    step(direction, speedFloat, point){
        super.step()
        let invert = 1 + (-2 * +this.invert)
        direction = direction == undefined? this.stepDirection(invert): direction
        speedFloat = speedFloat == undefined? this.speed2D.absFloat(): speedFloat
        this.rotation = 90 - radiansToDegrees(direction)

        let v = speedFloat == undefined ? Math.abs(direction[0])+Math.abs(direction[1]): speedFloat
        let vh = (v * .5)
        this.birthrate = v < 1? 0: .1
        // this.birthrate = vh * .3
        this.particleSpeed = 1 + (vh * .1)
        this.radiusVariant = .1 + (vh * .02)

        if(point) {
            this.lifetime = (this.distanceTo(point) - vh) * .4
        }
    }
}


class TrailPointEmitter extends DirectionalPointEmitter {
    invert = true
    // tickModulo = 5
}


copy