Parse

File Parse pointlist.js

This tree is parsed live from the source file.

Classes

  • {{ item.name }}

    • {{ key }}

Not Classes

{{ getTree() }}

Comments

{{ getTreeComments() }}

Source

            /*
files:
    pointlistdraw.js
    pointlistgradient.js
    pointlistshape.js
    pointlistgenerator.js
    unpack.js
 */

const pointArray = function(count=5, distance=10) {
    let res = new PointList
    let f = (i) => point(0, distance*i);
    if(typeof(distance) == 'function') {
        f = distance
    }

    for(let i = 0; i<=count-1; i++){
        let p = f(i)
        res.push(p)
    }

    return res
}


const asPoints = function(items) {
    let res = new PointList;
    for(let item in items) {
        res.push(new Point(items[item]))
    }
    return res
}


function originRotate(target, origin, theta) {
    /*
    Example usage:

        const a = [100, 200];
        const b = [300, 200];
        const theta90 = 90;
        const theta50 = 50;

        const newA90 = originRotate(a, b, theta90);
        const newA50 = originRotate(a, b, theta50);

        console.log('New position for 90 degrees:', newA90);
        console.log('New position for 50 degrees:', newA50);
    */

    // Convert theta to radians
    const thetaRad = theta * (Math.PI / 180);

    // Translate point a to the origin with respect to point origin
    const aPrime = [target[0] - origin[0], target[1] - origin[1]];

    // Apply the rotation matrix
    const aPrimeRotated = [
        aPrime[0] * Math.cos(thetaRad) - aPrime[1] * Math.sin(thetaRad),
        aPrime[0] * Math.sin(thetaRad) + aPrime[1] * Math.cos(thetaRad)
    ];

    // Translate the point back
    const aDoublePrime = [aPrimeRotated[0] + origin[0], aPrimeRotated[1] + origin[1]];

    return aDoublePrime;
}



class LazyAccessArray extends Array {

    // drawClass = undefined
    // penClass = undefined
    // gradientClass = undefined
    // generatorClass = undefined
    // shapeClass = undefined

    get draw() {
        const C = (this.drawClass || PointListDraw)
        return (this._draw || (this._draw = new C(this)))

    }

    get pen() {

        const C = (this.penClass || PointListPen)
        if(this._pen == undefined) {
            Object.defineProperty(this, '_pen', { value: new C(this) })
        }
        return this._pen
        // return (this._pen || (this._pen = new C(this)))
    }

    get gradient() {
        const C = (this.gradientClass || PointListGradient)
        if(this._gradient == undefined) {
            Object.defineProperty(this, '_gradient', { value: new C(this) })
        }
        return this._gradient
    }

    get generate() {
        const C = (this.generatorClass || PointListGenerator)
        if(this._generator == undefined) {
            Object.defineProperty(this, '_generator', { value: new C(this) })
        }
        return this._generator
    }

    get shape() {
        const C = (this.shapeClass || PointListShape)
        if(this._shape == undefined) {
            Object.defineProperty(this, '_shape', { value: new C(this) })
        }
        return this._shape
    }

    siblings(close=false) {
        /* Return in sibling pairs, where each node is paired with its neighbour
        for every item. Each node appears twice:

            [a,b,c,d,e,f,g]
            pairs()
            a,b
            b,c
            c,d
            d,e
            e,f
            f,g

        example:

            myitems.siblings().map(pair=>[pair[0].uuid, pair[1].uuid]);
        */
        let r = []
            , l = this.length;

        for (var i = 0; i < l; i++) {
            let to = this[i+1]
            if(to==undefined) {continue}
            let v = new PointList(this[i], to)
            r.push(v)
        }

        if(close) {
            let v = new PointList(this[l-1], this[0])
            r.push(v)
        }

        return r
    }

    pairs(close=false) {
        /* Return in pairs, where each node is paired with its next neighour once.
        Each node appears once:

            [a,b,c,d,e,f,g]
            pairs()
            a,b
            c,d
            e,f

        example:

            myitems.pairs().map((b)=>b[0].uuid);

        */
        let r = []
            , l = this.length;

        for (var i = 0; i < l-1; i+=2) {
            let v = new PointList(this[i], this[i+1])
            r.push(v)
        }

        if(close) {
            let v = new PointList(this[l-1], this[0])
            r.push(v)
        }
        return r
    }

    triples() {
        /*
        Return a list with points in triplets, where each entry has 3 points

            [a,b,c,d,e,f,g]
            triples()
            a,b,c
            b,c,d
            c,d,e
            d,e,f
            e,f,g
            f,g,a
            g,a,b
         */
        const triples = [];
        const len = this.length;
        for (let i = 0; i < len; i++) {
            triples.push(new PointList(
                        this[i % len]
                        , this[(i + 1) % len]
                        , this[(i + 2) % len]
                    ));
        }
        return triples;
    }

    get each() {
        /*
            The _each_ unit is a special accessor for the points, allowing
            the generic editing of points for all items in the array.

            Access a function on every point:

                points.each.rotate(10)
                points.each.rotate(10)

            set a value on every point

                points.each.rotation = 50

         */
        const target = this;


        const handler = {
            set(headTarget, innerProp, value) {
                // console.log('Set', innerProp, value)
                let innerV = value
                if(!isFunction(innerV)) {
                    innerV = ()=> value
                }

                target.forEach((p, i, a)=>{
                    p[innerProp] = innerV.apply(p, [p, i, a])
                })

                return true
            }

            , get(target, prop, receiver) {

                const caller = function eachCaller(values) {
                    // console.log('Called', this, this.prop, values)
                    let r = []
                    let previouslyCalled = undefined;
                    let isCaller = function(v){
                        if(previouslyCalled) {
                            return previouslyCalled
                        }
                        previouslyCalled = isFunction(v)
                    }

                    this.target.forEach((p)=>{

                        let v = p[this.prop]
                        if(isCaller(v)){
                            v = v.apply(p, arguments)
                        }
                        r.push(v)
                    })
                    return r;
                }

                let head = caller.bind({ target, prop })

                const headHandler = {

                    set(headTarget, innerProp, value) {
                        console.log('Set', innerProp, value)
                        target.forEach((p)=>{
                            p[innerProp] = value // .apply(p, arguments)
                        })
                    }

                    , get(headTarget, innerProp, _proxy) {
                        console.log('Get', innerProp, _proxy)

                        let fs = {
                            array: ()=> {
                                let r = []
                                target.forEach((p)=>{ r.push(p[prop]) })
                                return r
                            }
                        }

                        return fs[innerProp]//() // _proxy
                    }
                    , next(){
                        console.log('next')
                    }
                    , [Symbol.iterator] () {
                        console.log('iterator')
                        return stage.points
                    }

                    , apply(target, thisArg, argsList) {
                        /*
                            target: the head (A bound eachCaller)
                            thisArg: _this_, being the parent proxy
                            argsList: given argument.
                            ---
                            this: the head headHandler
                        */
                        // console.log('Call to head', this)
                                // head
                        return target.apply(thisArg, argsList)
                    }
                }


                const proxy = new Proxy(head, headHandler);
                // return Reflect.get(...arguments)
                return proxy
            }
        }

        const proxy = new Proxy(target, handler);
        return proxy;
    }
}


class PointList extends LazyAccessArray {

    first() {
        return this[0]
    }

    last() {
        return this[this.length-1]
    }

    copy(deep=false) {
        /* Copy:

            stage.points.copy() == stage.points
            false
            stage.points.copy()[0] == stage.points[0]
            true

        Deep Copy:

            stage.points.copy(true) == stage.points
            false
            stage.points.copy(true)[0] == stage.points[0]
            false
        */
        let pl = new PointList;
        if (deep == true) {
            this.forEach(p=>{
                pl.push(p.copy())
            })
            return pl
        }

        return  pl.concat(this)
    }

    getBoundingClientRect() {
        /* return thr bounding box of the point.*/
        return DOMRect.fromRect(this.getSize())
    }

    getSize() {
        /* Return the width/height of the pointlist, discovering the _min_
        and _max_ points to determin a rectangle */

        let x = 0
            , y = 0
            , min = this[0]
            , max = {x,y}
            ;

        this.forEach((p)=>{

            if(p.x < min.x) { min.x = p.x };
            if(p.y < min.y) { min.y = p.y };

            if(p.x > max.x) { max.x = p.x };
            if(p.y > max.y) { max.y = p.y };
        })

        return {
            x: min.x
            , y: min.y
            , width: max.x - min.x
            , height: max.y - min.y
            , min, max
        }
    }

    cast(type=Point, func) {
        /* mutate each point with the given.
        Similar to map(p=>new Cast(p))
        */
        if(func == undefined)  {
            func = function(p){
                return new type(p)
            }
        }
        return this.map(p=>func(p))
    }

    centerOfMass(type='simple', origin) {
        return centerOfMass[type](this, origin)
    }

    get center() {
        let size = this.getSize()
        let x = (size.width * .5)
        let y = (size.height * .5)
        // let z = size.z * .5
        return new Point(size.x + x, size.y + y)
    }

    setX(value, key='x') {
        return this.setMany(value, key)
    }

    setY(value, key='y') {
        return this.setMany(value, key)
    }

    setData(data) {
        for(let k in data) {
            this.setMany(data[k], k)
        }
    }

    getById(id){
        for(let p of this) {
            if(p._id == id) {
                return p
            }
        }
    }

    getByName(name) {
        return this.getByKey('name', name)
    }

    getByKey(key, value) {
        for(let x of this){
            if(x[key] == value){
                return x
            }
        }
    }

    update(data) {
        this.forEach((e,i,a)=> {
            return e.update(data)
        })
    }

    setMany(value, key) {
        /* Set the X value for all points within the list
        accepts undefined, number, point[x], function */
        if(isPoint(value)) {
            value = point[key]
        }

        let f = value;

        if(!isFunction(value)) {
            f = (e,i,a) => {
                    e[key] = value
                }
        }

        this.forEach((e,i,a)=> {
            return f(e,i,a)
        })
    }

    keyMany(key, value) {
        /* Set the X value for all points within the list
        accepts undefined, number, point[x], function */
        let orig = value
        /* copy the key value from the other*/
        if(isPoint(value)) {
            value = orig[key]
        }

        let f = value;

        if(!isFunction(value)) {
            f = function(e,i,a){
                    return value
                }
        }

        this.forEach((e,i,a)=> {
            return e[key] = f(e,i,a)
        })
    }

    cleanArray(fix=true) {
        let r = []
        this.forEach((x)=>r.push(x.asArray(fix)))
        return r
    }

    offset(value) {
        this.forEach(p=>{
            let va = p.add(value)
            p.x = va.x
            p.y = va.y
            // p.copy(p.add(value))
            // p.rotate(rot)
        })
    }

    rotate(value, point) {
        /* Spin the cluster by a given rotation, around an optional anchor
        If given a single point, the rotation is used.*/
        if(point == undefined && isPoint(value)) {
            this.handleRotate(value)
        }

        let rot = isPoint(value)? value.rotation: value
        // console.log('rotation to', rot)
        if(point == undefined) {
            point = this.centerOfMass()
        }

        this.forEach(p=>{
            let target = p
            const res = originRotate(target, point, rot);
            target.x = res[0]
            target.y = res[1]
            // p.rotate(rot)
        })
        // return p
    }

    lastDiff = 0
    handleRotate(handlePoint) {
        let rot = handlePoint.radians
        // this.lastDiff = handlePoint.radians
        this.forEach(target=> {
            let diff = radiansDiff2(handlePoint.radians, this.lastDiff)
            const res = originRotate(target, handlePoint, radiansToDegrees(diff))
            // const res = originRotate(target, handlePoint, rot);
            target.x = res[0]
            target.y = res[1]
            // p.rotate(rot)
        })
        this.lastDiff = handlePoint.radians
    }


    everyEvery(func) {
        /*
        Iterate every point against every point.
        This is useful for point to point connection

            points.everyEvery(function(a,b){
            })
        */
        const points = this;
        // let points = Array.from(pointMap.values())
        let complete = new Set();

        points.forEach((e,i,a)=>{
            points.forEach((f,j)=> {
                if(e.uuid == f.uuid) { return }
                let v = e.iid + f.iid
                if(complete.has(v)) {
                    return
                }
                complete.add(v)
                // e.pen.line(ctx, f)
                func(e,f)
            })
        })
    }

    lookAt(other) {
        this.forEach(p=>{ p.lookAt(other)})
    }

    grow(point=undefined) {
        /* shift the points within the list as per a growth method.
        If is a list of 2, the line will extend,
        If more than 2, the points will push away from the origin point.
        If the origin is undefined, the COM is used.*/
    }

    sortByRadius(){
        this.sort((a,b)=>b.radius-a.radius)
    }

    sortByZ(){
        this.sort((a,b)=>b.z-a.z)
    }

    remove(item) {
        /* Remove an item. but this does perform a splice
        so it may be costly.*/
        let i = this.indexOf(item)
        if(i>-1){
            return this.splice(i, 1)
        }

    }
}


Polypoint.head.install(PointList)


PointList.generate = new PointListGenerator();

copy