transformcomposer.js 5.98 KB
import ARCS from '../engine/arcs.js';
import * as THREE from '../deps/three.js/index.js';

let TransformComposer = ARCS.Component.create(
    function(config) {
        const self = this;
        config ??= {};
        config.poses ??= {};
        config.operations ??= {};
        
        let poses = {};
        
        // operand list
        // multiply, premultiply, invert, compose
        // structure for the operation
        
        /*
         * { 
         *    poses : {
         *      pose_name : { type : "", values: [opt] }
         *    },
         *    operations : {
         *      op_name : { func: f_name, params : [] }
         *    }
         * }
         */
        
        let fTypes = {
            'mat' : function(m) { 
                return m.clone();
            },
            'arr' : function(arr) {
                return (new THREE.Matrix4()).fromArray(arr);
            },
            'pos_quat' : function(pos,quat) {
                let m =  new THREE.Matrix4()
                m.setPosition((new THREE.Vector3()).fromArray(pos));
                m.makeRotationFromQuaternion((new THREE.Quaternion()).fromArray(quat));              
            },
            'pos': function(pos) {
                return (new THREE.Matrix4()).setPosition((new THREE.Vector3()).fromArray(pos));            
            },
            'quat': function(pos) {
                return (new THREE.Matrix4()).makeRotationFromQuaternion((new THREE.Quaternion()).fromArray(quat));            
            },
        };
        
        let fOperators = {
            '-1' : function(m) { 
                return m.clone().invert(); 
            },
            'T': function(m) { 
                return m.clone().transpose(); 
            },
            '*>' : function() {
                if( arguments.length === 0)
                    return new THREE.Matrix4();

                let mat = arguments[0].clone();
                if (arguments.length > 1) {
                    for (let i=1; i< arguments.length; i++) {
                        mat.multiply(arguments[i]);
                    }
                }
                return mat;
            },
            '<*' : function() {
                if( arguments.length === 0)
                    return new THREE.Matrix4();

                let mat = arguments[0].clone(); 
                if (arguments.length > 1) {
                    for (let i=1; i< arguments.length; i++) {
                        mat.premultiply(arguments[i]);
                    }
                }
                return mat;
            },
            't': function(a,b) {
                return a.clone().copyPosition(b);
            },
            'R': function(a, b) {
                return a.clone().extractRotation(b);
            },
            '|': function(m) {
                let mat = m.elements;
                const mX = new THREE.Vector3(mat[0],0,mat[2]);
                const mY = new THREE.Vector3(0,1,0);
                const mZ = new THREE.Vector3(mat[8],0,mat[10]);

                const lm = new THREE.Vector3();
                lm.addVectors(mX,mZ);
                const lX = lm.clone().normalize().applyAxisAngle(mY,Math.PI/4);
                const lZ = lm.clone().normalize().applyAxisAngle(mY,-Math.PI/4);
                // potentially, the acquired point is a little bit lower 
                // so, a correction should also be applied to this one.
                
                const rY = new THREE.Vector3(mat[4],mat[5],mat[6]);
                const rZ = mY.clone().cross(rY);
                const aZ = rY.angleTo(mY);

                const t = new THREE.Vector3(mat[12], mat[13], mat[14]);
                t.applyAxisAngle(rZ,-aZ);
        
                return (new THREE.Matrix4()).fromArray([
                    lX.x, lX.y, lX.z, 0,
                    mY.x, mY.y, mY.z, 0, 
                    lZ.x, lZ.y, lZ.z, 0,
                    t.x, t.y, t.z, 1
                ]);                
            }
        };
        
        
        let computeMatrix = function(obj) {
            if (typeof obj === "string") {
                console.log(obj,poses[obj]);
                return poses[obj];
            } else {
                for (let p in obj) {
                    let mat = fOperators[p].apply(
                        null, obj[p].map( (v) => { return computeMatrix(v);} )
                    );
                    console.log(JSON.stringify(obj), mat);
                    return mat;
                }
            }            
        };
                
        let computePose = function(name) {
            let obj = config.operations[name];
            if (obj === undefined) return;            
            let m = computeMatrix(obj);
            console.log(name, m);
            self.emit(name, m);            
        };
                      
        for(let p in config.poses) {
            if (config.poses.hasOwnProperty(p)) {
                self.slot(p, function() { poses[p] = fTypes[config.poses[p].type].apply(null,arguments);});
                if (config.poses[p].values !== undefined) {
                  /*poses[p] =*/ self[p].apply(null, config.poses[p].values);
                } else {
                  poses[p] = new THREE.Matrix4();
                }
            }
        }
      
        for (let o in config.operations) {
            // create operations that correspond to a slot and signals
            self.signal(o);
            self.slot(o, ((name) => { return function() { computePose(name);};})(o));
        }
        
        //console.log("transform composer", self, poses);
    }
);

export default { TransformComposer: TransformComposer};

/*
ARCS.__lib__`
{
    "components": {
        "TransformComposer": {
            "description": "A component that computes a transform matrix from a set of poses and operations",
            "keywords": ["transform", "matrix", "pose"]
        }
    },
    "dependencies": [ "three" ],
    "recipes": {
        "from": "node_modules/three/build/three.module.js",
        "to": "deps/three.js/index.js"
    }
}
`;
*/