/*
 * Real3D FlipBook [https://real3dflipbook.com]
 * @author creativeinteractivemedia [https://codecanyon.net/user/creativeinteractivemedia/portfolio]
 * @version 4.20
 * @date 2026-03-18
 */
/**
*   MOD3 3D Modifier Library for JavaScript
*   port of AS3DMod ActionScript3 library (http://code.google.com/p/as3dmod/)
*
*   @version 1.0.0 (2023-01-03 16:08:34)
*   https://github.com/foo123/MOD3
*
**//**
*   MOD3 3D Modifier Library for JavaScript
*   port of AS3DMod ActionScript3 library (http://code.google.com/p/as3dmod/)
*
*   @version 1.0.0 (2023-01-03 16:08:34)
*   https://github.com/foo123/MOD3
*
**/
!function(root, name, factory) {
"use strict";
if (('object' === typeof module) && module.exports) /* CommonJS */
    (module.$deps = module.$deps||{}) && (module.exports = module.$deps[name] = factory.call(root));
else if (('function' === typeof define) && define.amd && ('function' === typeof require) && ('function' === typeof require.specified) && require.specified(name) /*&& !require.defined(name)*/) /* AMD */
    define(name, ['module'], function(module) {factory.moduleUri = module.uri; return factory.call(root);});
else if (!(name in root)) /* Browser/WebWorker/.. */
    (root[name] = factory.call(root)||1) && ('function' === typeof(define)) && define.amd && define(function() {return root[name];});
}(  /* current root */          'undefined' !== typeof self ? self : this,
    /* module name */           "MOD3",
    /* module factory */        function ModuleFactory__MOD3(undef) {
"use strict";

var HAS = Object.prototype.hasOwnProperty,
    toString = Object.prototype.toString,
    def = Object.defineProperty,
    stdMath = Math, PI = stdMath.PI,
    TWO_PI = 2*PI, HALF_PI = PI/2, INV_PI = 1/PI,
    EMPTY_ARR = [], EMPTY_OBJ = {}, NOP = function() {},
    isNode = ("undefined" !== typeof global) && ("[object global]" === toString.call(global)),
    isBrowser = ("undefined" !== typeof window) && ("[object Window]" === toString.call(window))
;

// basic backwards-compatible "class" construction
function makeSuper(superklass)
{
    var called = {};
    return function $super(method, args) {
        var self = this, m = ':'+method, ret;
        if (1 === called[m]) return (superklass.prototype.$super || NOP).call(self, method, args);
        called[m] = 1;
        ret = ('constructor' === method ? superklass : (superklass.prototype[method] || NOP)).apply(self, args || []);
        called[m] = 0;
        return ret;
    };
}
function makeClass(superklass, klass, statik)
{
    if (arguments.length < 2)
    {
        klass = superklass;
        superklass = null;
    }
    var C = HAS.call(klass, 'constructor') ? klass.constructor : function() {}, p;
    if (superklass)
    {
        C.prototype = Object.create(superklass.prototype);
        C.prototype.$super = makeSuper(superklass);
    }
    else
    {
        C.prototype.$super = NOP;
    }
    C.prototype.constructor = C;
    for (p in klass)
    {
        if (HAS.call(klass, p) && ('constructor' !== p))
        {
            C.prototype[p] = klass[p];
        }
    }
    if (statik)
    {
        for (p in statik)
        {
            if (HAS.call(statik, p))
            {
                C[p] = statik[p];
            }
        }
    }
    return C;
}
var MOD3 = {
    VERSION: "1.0.0",
    Class: makeClass
};
/**
* MOD3  Constants and Auxilliary methods
**/
MOD3.Constants = {
    // cache math constants for reference and optimization
    PI: PI,
    invPI: INV_PI,
    halfPI: HALF_PI,
    doublePI: TWO_PI,
    toRad: PI/180,
    toDeg: 180/PI
};
MOD3.ModConstant = {
    NONE: 0,
    LEFT: -1,
    RIGHT: 1,

    X: 1,
    Y: 2,
    Z: 4,

    Xi: 0,
    Yi: 1,
    Zi: 2
};
MOD3.XYZi = [
    null,
    0,
    1,
    null,
    2
];
MOD3.iXYZ = [
    1,
    2,
    4
];
MOD3.xyz = [
    "x",
    "y",
    "z"
];
MOD3.XYZ = [
    "X",
    "Y",
    "Z"
];

// Typed Arrays Substitutes
MOD3.Array32F = typeof Float32Array !== "undefined" ? Float32Array : Array;
MOD3.Array64F = typeof Float64Array !== "undefined" ? Float64Array : Array;
MOD3.Array8I = typeof Int8Array !== "undefined" ? Int8Array : Array;
MOD3.Array16I = typeof Int16Array !== "undefined" ? Int16Array : Array;
MOD3.Array32I = typeof Int32Array !== "undefined" ? Int32Array : Array;
MOD3.Array8U = typeof Uint8Array !== "undefined" ? Uint8Array : Array;
MOD3.Array16U = typeof Uint16Array !== "undefined" ? Uint16Array : Array;
MOD3.Array32U = typeof Uint32Array !== "undefined" ? Uint32Array : Array;
// vector typed-array
MOD3.VecArray = MOD3.Array32F;
/**
* MOD3  Math Utilities Class
**/
MOD3.XMath = {
    normalize: function(start, end, val) {
        var range = end - start;
        return 0 === range ? 1 : MOD3.XMath.trim(0, 1, (val - start)/end);
    },

    toRange: function(start, end, normalized) {
        var range = end - start;
        return 0 === range ? 0 : (start + range*normalized);
    },

    inRange: function(start, end, value, excluding) {
        return false !== excluding ? (value >= start && value <= end) : (value > start && value < end);
    },

    sign: function(val, ifZero) {
        return 0 === val ? (ifZero || 0) : (val > 0 ? 1 : -1);
    },

    trim: function(start, end, value) {
        return value < start ? start : (value > end ? end : value);
    },

    wrap: function(start, end, value) {
        var r = end - start;
        return value < start ? (value + r) : (value >= end ? value - r : value);
    },

    degToRad: function(deg) {
        return deg/180*PI;
    },

    radToDeg: function(rad)  {
        return rad/PI*180;
    },

    presicion: function(number, precision) {
        var r = stdMath.pow(10, precision);
        return stdMath.round(number*r)/r;
    },

    uceil: function(val) {
        return val < 0 ? stdMath.floor(val) : stdMath.ceil(val);
    }
};
// alias
MOD3.XMath.clamp = MOD3.XMath.trim;
/**
* MOD3  Range Auxilliary Class
**/
MOD3.Range = MOD3.Class({
    constructor: function Range(s, e)  {
        var self = this;
        if (!(self instanceof Range)) return new Range(s, e);
        self.start = null != s ? s : 0;
        self.end = null != e ? e : 1;
    },

    name: "Range",
    start: 0,
    end: 1,

    dispose: function() {
        var self = this;
        self.start = null;
        self.end = null;
        return self;
    },

    getSize: function()  {
        return this.end - this.start;
    },

    move: function(amount)  {
        this.start += amount;
        this.end += amount;
    },

    isIn: function(n) {
        return (n >= this.start && n <= this.end);
    },

    normalize: function(n) {
        return MOD3.XMath.normalize(this.start, this.end, n);
    },

    toRange: function(n) {
        return MOD3.XMath.toRange(this.start, this.end, n);
    },

    trim: function(n) {
        return MOD3.XMath.trim(this.start, this.end, n);
    },

    interpolate: function(n, r) {
        return MOD3.XMath.toRange(this.start, this.end, r.normalize(n));
    },

    toString: function() {
        return "[" + this.start + " - " + this.end + "]";
    }
});
/**
* MOD3  Phase Auxilliary Class
**/
MOD3.Phase = MOD3.Class({
    constructor: function Phase(v) {
        var self = this;
        if (!(self instanceof Phase)) return new Phase(v);
        self.value = v || 0;
    },

    name: "Phase",
    value: 0,

    dispose: function() {
        this.value = null;
        return this;
    },

    getPhasedValue: function()  {
        return stdMath.sin(this.value);
    },

    getAbsPhasedValue: function()  {
        return stdMath.abs(stdMath.sin(this.value));
    },

    getNormValue: function() {
        return (stdMath.sin(this.value) + 1)*0.5;
    }
});
/**
* MOD3  2D Point Class
**/
MOD3.Point = MOD3.Class({
    constructor: function Point(x, y) {
        var self = this;
        if (!(self instanceof Point)) return new Point(x, y);
        self.x = x || 0;
        self.y = y || 0;
    },

    name: "Point",
    x: 0,
    y: 0,

    dispose: function() {
        var self = this;
        self.x = null;
        self.y = null;
        return self;
    },

    clone: function() {
        return new MOD3.Point(this.x, this.y);
    }
});
/**
* MOD3  2D Transform Matrix Class
**/
MOD3.Matrix = MOD3.Class(null, {
    constructor: function Matrix(m11, m12,
                                 m21, m22)
   {
        var self = this;
        if (!(self instanceof Matrix)) return new Matrix(m11, m12,
                                                         m21, m22);
        self.m = new MOD3.VecArray([
            m11 == null ? 1 : m11,
            m12 == null ? 0 : m12,
            m21 == null ? 0 : m21,
            m22 == null ? 1 : m22
        ]);
    },

    name: "Matrix",
    m: null,

    dispose: function() {
        this.m = null;
        return this;
    },

    reset: function() {
        var m = this.m;
        m[0] = 1; m[1] = 0;
        m[2] = 0; m[3] = 1;
        return this;
    },

    rotate: function(angle)  {
        var m = this.m, c = stdMath.cos(angle), s = stdMath.sin(angle);
        m[0] = c; m[1] = -s;
        m[2] = s; m[3] = c;
        return this;
    },

    scale: function(sx, sy) {
        var m = this.m;
        m[0] = 1; m[1] = 0;
        m[2] = 0; m[3] = 1;
        if (sx != null)
        {
            m[0] = sx;
            m[3] = sx;
        }
        if (sy != null)
        {
            m[3] = sy;
        }
        return this;
    },

    multiply: function(b) {
        return MOD3.Matrix.mult(this, b);
    },

    transformPoint: function(p) {
        var xy = MOD3.Matrix.transform(this, [p.x, p.y]);
        return new MOD3.Point(xy[0], xy[1]);
    },

    transformPointSelf: function(p) {
        var xy = MOD3.Matrix.transform(this, [p.x, p.y]);
        p.x = xy[0]; p.y = xy[1];
        return p;
    },

    clone: function() {
        var m = this.m;
        return new MOD3.Matrix(m[0], m[1],
                          m[2], m[3]);
    }
}, {
    transform: function(m2, xy) {
        var m = m2.m, x = xy[0], y = xy[1];
        xy[0] = m[0]*x + m[1]*y;
        xy[1] = m[2]*x + m[3]*y;
        return xy;
    },

    mult: function(m1, m2) {
        var a = m1.m, b = m2.m, a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3];
        a[0] = a0*b[0] + a1*b[2];
        a[1] = a0*b[1] + a1*b[3];
        a[2] = a2*b[0] + a3*b[2];
        a[3] = a2*b[1] + a3*b[3];
        return m1;
    }
});
/**
* MOD3  Vector3 Class
**/
MOD3.Vector3 = MOD3.Class(null, {
    constructor: function Vector3(x, y, z) {
        var self = this;
        if (!(self instanceof Vector3)) return new Vector3(x, y, z);

        // use an internal typed-array for speed
        var v = new MOD3.VecArray(3);
        if (x && (3 === x.length))
        {
            // array passed
            v[0] = x[0] || 0;
            v[1] = x[1] || 0;
            v[2] = x[2] || 0;
        }
        else
        {
            // numbers passed
            v[0] = x || 0;
            v[1] = y || 0;
            v[2] = z || 0;
        }
        self.xyz = v;
    },

    name: "Vector3",
    xyz: null,

    dispose: function() {
        this.xyz = null;
        return this;
    },

    getXYZ: function() {
       // copy it
       return new MOD3.VecArray(this.xyz);
    },

    getXYZRef: function() {
       return this.xyz;
    },

    setXYZ: function(w) {
       var v = this.xyz;
       v[0] = w[0];
       v[1] = w[1];
       v[2] = w[2];
       return this;
    },

    setXYZRef: function(xyz) {
       this.xyz = xyz;
       return this;
    },

    clone: function() {
        return new MOD3.Vector3(this.xyz);
    },

    equalsSelf: function(b) {
        var v = this.xyz, w = b.xyz;
        return (v[0] === w[0]) && (v[1] === w[1]) && (v[2] === w[2]);
    },

    zeroSelf: function() {
        var v = this.xyz;
        v[0] = 0; v[1] = 0; v[2] = 0;
        return this;
    },

    negate: function() {
        var v = this.xyz;
        return new MOD3.Vector3(-v[0], -v[1], -v[2]);
    },

    negateSelf: function() {
        var v = this.xyz;
        v[0] = -v[0]; v[1] = -v[1]; v[2] = -v[2];
        return this;
    },

    add: function(b) {
        var v = this.xyz, w = b.xyz;
        return new MOD3.Vector3(v[0] + w[0], v[1] + w[1], v[2] + w[2]);
    },

    addSelf: function(b) {
        var v = this.xyz, w = b.xyz;
        v[0] += w[0]; v[1] += w[1]; v[2] += w[2];
        return this;
    },

    subtract: function(b) {
        var v = this.xyz, w = b.xyz;
        return new MOD3.Vector3(v[0] - w[0], v[1] - w[1], v[2] - w[2]);
    },

    subtractSelf: function(b) {
        var v = this.xyz, w = b.xyz;
        v[0] -= w[0]; v[1] -= w[1]; v[2] -= w[2];
        return this;
    },

    multiplyScalar: function(s) {
        var v = this.xyz;
        return new MOD3.Vector3(v[0]*s, v[1]*s, v[2]*s);
    },

    multiplyScalarSelf: function(s) {
        var v = this.xyz;
        v[0] *= s; v[1] *= s; v[2] *= s;
        return this;
    },

    multiply: function(b) {
        var v = this.xyz, w = b.xyz;
        return new MOD3.Vector3(v[0] * w[0], v[1] * w[1], v[2] * w[2]);
    },

    multiplySelf: function(b) {
        var v = this.xyz, w = b.xyz;
        v[0] *= w[0]; v[1] *= w[1]; v[2] *= w[2];
        return this;
    },

    divide: function(s) {
        var v = this.xyz;
        return new MOD3.Vector3(v[0] / s, v[1] / s, v[2] / s);
    },

    divideSelf: function(s) {
        var v = this.xyz;
        v[0] /= s; v[1] /= s; v[2] /= s;
        return this;
    },

    normalize: function() {
        var v = this.xyz,
            x = v[0], y = v[1], z = v[2],
            m = x * x + y * y + z * z, n;
        if (0 < m)
        {
            n = stdMath.sqrt(m);
            x /= n;
            y /= n;
            z /= n;
        }
        return new MOD3.Vector3(x, y, z);
    },

    normalizeSelf: function() {
        var v = this.xyz,
            x = v[0], y = v[1], z = v[2],
            m = x * x + y * y + z * z, n;
        if (0 < m)
        {
            n = stdMath.sqrt(m);
            x /= n;
            y /= n;
            z /= n;
        }
        v[0] = x; v[1] = y; v[2] = z;
        return this;
    },

    getMagnitude: function() {
        var v = this.xyz, x = v[0], y = v[1], z = v[2];
        return stdMath.sqrt(x*x + y*y + z*z);
    },

    setMagnitude: function(m) {
        this.normalizeSelf();
        var v = this.xyz;
        v[0] *= m; v[1] *= m; v[2] *= m;
        return this;
    },

    dot: function(b) {
        var v = this.xyz, w = b.xyz;
        return v[0]*w[0] + v[1]*w[1] + v[2]*w[2];
    },

    cross: function(b) {
        var v = this.xyz, w = b.xyz,
            x1 = v[0], y1 = v[1], z1 = v[2],
            x2 = w[0], y2 = w[1], z2 = w[2];
        return new MOD3.Vector3(y1 * z2 - z1 * y2, z1 * x2 - x1 * z2, x1 * y2 - y1 * x2);
    },

    crossSelf: function(v) {
        var v = this.xyz, w = b.xyz,
            x1 = v[0], y1 = v[1], z1 = v[2],
            x2 = w[0], y2 = w[1], z2 = w[2];
        v[0] = y1 * z2 - z1 * y2;
        v[1] = z1 * x2 - x1 * z2;
        v[2] = x1 * y2 - y1 * x2;
        return this;
    },

    distance: function(b) {
        var v = this.xyz, w = b.xyz,
            dx = v[0] - w[0],
            dy = v[1] - w[1],
            dz = v[2] - w[2];
        return stdMath.sqrt(dx*dx + dy*dy + dz*dz);
    },

    toString: function() {
        var v = this.xyz;
        return "[" + v[0] + " , " + v[1] + " , " + v[2] + "]";
    }
}, {
    ZERO: function() {
        return new MOD3.Vector3(0, 0, 0);
    },

    X: function(direct_or_complement) {
        return false === direct_or_complement ? new MOD3.Vector3(0, 1, 1) : new MOD3.Vector3(1, 0, 0);
    },

    Y: function(direct_or_complement) {
        return false === direct_or_complement ? new MOD3.Vector3(1, 0, 1) : new MOD3.Vector3(0, 1, 0);
    },

    Z: function(direct_or_complement) {
        return false === direct_or_complement ? new MOD3.Vector3(1, 1, 0) : new MOD3.Vector3(0, 0, 1);
    },

    dot: function(v, w) {
        return v[0]*w[0] + v[1]*w[1] + v[2]*w[2];
    },

    equals: function(v, w) {
        return (v[0] === w[0]) && (v[1] === w[1]) && (v[2] === w[2]);
    },

    cross: function(v, w) {
        var vw = new MOD3.VecArray(3);
        vw[0] = v[1] * w[2] - v[2] * w[1];
        vw[1] = v[2] * w[0] - v[0] * w[2];
        vw[2] = v[0] * w[1] - v[1] * w[0];
        return vw;
    },

    mod: function(v) {
        var x = v[0], y = v[1], z = v[2];
        return stdMath.sqrt(x*x + y*y + z*z);
    },

    dist: function(v, w) {
        var dx = v[0] - w[0],
            dy = v[1] - w[1],
            dz = v[2] - w[2];
        return stdMath.sqrt(dx*dx + dy*dy + dz*dz);
    },

    add: function(v, w) {
        v[0] += w[0];
        v[1] += w[1];
        v[2] += w[2];
        return v;
    },

    sub: function(v, w) {
        v[0] -= w[0];
        v[1] -= w[1];
        v[2] -= w[2];
        return v;
    },

    mul: function(v, w) {
        v[0] *= w[0];
        v[1] *= w[1];
        v[2] *= w[2];
        return v;
    },

    muls: function(v, m) {
        v[0] *= m;
        v[1] *= m;
        v[2] *= m;
        return v;
    },

    norm: function(v) {
        var x = v[0], y = v[1], z = v[2],
            m = x*x + y*y + z*z, n;
        if (0 < m)
        {
            n = stdMath.sqrt(m);
            x /= n;
            y /= n;
            z /= n;
        }
        v[0] = x; v[1] = y; v[2] = z;
        return v;
    }
});
// alaises
MOD3.Vector3.modulo = MOD3.Vector3.mod;
MOD3.Vector3.distance = MOD3.Vector3.dist;
MOD3.Vector3.prototype.dotSelf = MOD3.Vector3.prototype.dot;
MOD3.Vector3.prototype.distanceSelf = MOD3.Vector3.prototype.distance;
/**
* MOD3  3D Transform Matrix Class
**/
MOD3.Matrix4 = MOD3.Class(null, {
    constructor: function Matrix4(n11, n12, n13, n14,
                                   n21, n22, n23, n24,
                                   n31, n32, n33, n34,
                                   n41, n42, n43, n44)
    {
        var self = this;
        if (!(self instanceof Matrix4)) return new Matrix4(n11, n12, n13, n14,
                                                           n21, n22, n23, n24,
                                                           n31, n32, n33, n34,
                                                           n41, n42, n43, n44);
        self.m = new MOD3.VecArray([
            n11 == null ? 1 : n11,
            n12 == null ? 0 : n12,
            n13 == null ? 0 : n13,
            n14 == null ? 0 : n14,

            n21 == null ? 0 : n21,
            n22 == null ? 1 : n22,
            n23 == null ? 0 : n23,
            n24 == null ? 0 : n24,

            n31 == null ? 0 : n31,
            n32 == null ? 0 : n32,
            n33 == null ? 1 : n33,
            n34 == null ? 0 : n34,

            n41 == null ? 0 : n41,
            n42 == null ? 0 : n42,
            n43 == null ? 0 : n43,
            n44 == null ? 1 : n44
        ]);
    },

    name: "Matrix4",
    m: null,

    dispose: function() {
        this.m = null;
        return this;
    },

    reset: function() {
        var m = this.m;
        m[0 ] = 1; m[1 ] = 0; m[2 ] = 0; m[3 ] = 0;
        m[4 ] = 0; m[5 ] = 1; m[6 ] = 0; m[7 ] = 0;
        m[8 ] = 0; m[9 ] = 0; m[10] = 1; m[11] = 0;
        m[12] = 0; m[13] = 0; m[14] = 0; m[15] = 1;
        return this;
    },

    translate: function(tx, ty, tz, reset) {
        var m = this.m;
        if (true === reset) this.reset();
        m[3 ] = tx;
        m[7 ] = ty;
        m[11] = tz;
        return this;
    },

    scale: function(sx, sy, sz, reset) {
        var m = this.m;
        if (true === reset) this.reset();
        m[0 ] = sx;
        m[5 ] = sy;
        m[10] = sz;
        return this;
    },

    rotate: function(rx, ry, rz, theta, reset)  {
        var m = this.m,
            nCos = stdMath.cos(theta), nSin = stdMath.sin(theta), scos = 1 - nCos,
            sxy = rx*ry*scos, syz = ry*rz*scos, sxz = rx*rz*scos,
            sz = nSin*rz, sy = nSin*ry, sx = nSin*rx
        ;
        if (true === reset) this.reset();
        m[0 ] = nCos + rx*rx*scos;
        m[1 ] = -sz + sxy;
        m[2 ] = sy + sxz;
        m[3 ] = 0;

        m[4 ] = sz + sxy;
        m[5 ] = nCos + ry*ry*scos;
        m[6 ] = -sx + syz;
        m[7 ] = 0;

        m[8 ] = -sy + sxz;
        m[9 ] = sx + syz;
        m[10] = nCos + rz*rz*scos;
        m[11] = 0;
        return this;
    },

    translateFromVector: function(v, reset) {
        return this.translate(v.xyz[0], v.xyz[1], v.xyz[2], reset);
    },

    scaleFromVector: function(v, reset) {
        return this.scale(v.xyz[0], v.xyz[1], v.xyz[2], reset);
    },

    rotateFromVector: function(v, theta, reset)  {
        return this.rotate(v.xyz[0], v.xyz[1], v.xyz[2], theta, reset);
    },

    multiply: function(b) {
        return MOD3.Matrix4.mult(this, b);
    },

    multiplyVector: function(v) {
        MOD3.Matrix4.multXYZ(this, v.xyz);
        return v;
    }
}, {
    multXYZ: function(m4, v) {
        var m = m4.m, x = v[0], y = v[1], z = v[2];
        v[0] = x*m[0 ] + y*m[1 ] + z*m[2 ] + m[3 ];
        v[1] = x*m[4 ] + y*m[5 ] + z*m[6 ] + m[7 ];
        v[2] = x*m[8 ] + y*m[9 ] + z*m[10] + m[11];
        return v;
    },

    mult: function(m1, m2) {
        var a = m1.m, b = m2.m,
            a11 = a[0 ], b11 = b[0 ],
            a21 = a[4 ], b21 = b[4 ],
            a31 = a[8 ], b31 = b[8 ],
            a12 = a[1 ], b12 = b[1 ],
            a22 = a[5 ], b22 = b[5 ],
            a32 = a[9 ], b32 = b[9 ],
            a13 = a[2 ], b13 = b[2 ],
            a23 = a[6 ], b23 = b[6 ],
            a33 = a[10], b33 = b[10],
            a14 = a[3 ], b14 = b[3 ],
            a24 = a[7 ], b24 = b[7 ],
            a34 = a[11], b34 = b[11];

        a[0 ] = a11*b11 + a12*b21 + a13*b31;
        a[1 ] = a11*b12 + a12*b22 + a13*b32;
        a[2 ] = a11*b13 + a12*b23 + a13*b33;
        a[3 ] = a11*b14 + a12*b24 + a13*b34 + a14;

        a[4 ] = a21*b11 + a22*b21 + a23*b31;
        a[5 ] = a21*b12 + a22*b22 + a23*b32;
        a[6 ] = a21*b13 + a22*b23 + a23*b33;
        a[7 ] = a21*b14 + a22*b24 + a23*b34 + a24;

        a[8 ] = a31*b11 + a32*b21 + a33*b31;
        a[9 ] = a31*b12 + a32*b22 + a33*b32;
        a[10] = a31*b13 + a32*b23 + a33*b33;
        a[11] = a31*b14 + a32*b24 + a33*b34 + a34;
        return m1;
    }
});
// aliases
MOD3.Matrix4.prototype.translationMatrix = MOD3.Matrix4.prototype.translate;
MOD3.Matrix4.prototype.scaleMatrix = MOD3.Matrix4.prototype.scale;
MOD3.Matrix4.prototype.rotationMatrix = MOD3.Matrix4.prototype.rotate;
MOD3.Matrix4.prototype.translationMatrixFromVector = MOD3.Matrix4.prototype.translateFromVector;
MOD3.Matrix4.prototype.scaleMatrixFromVector = MOD3.Matrix4.prototype.scaleFromVector;
MOD3.Matrix4.prototype.rotationMatrixFromVector = MOD3.Matrix4.prototype.rotateFromVector;
// fast list utilities
MOD3.List = {
     operate: function operate(x, F, F0, i0, i1, reverse) {
        var len = x.length;
        if (arguments.length < 5) i1 = len-1;
        if (0 > i1) i1 += len;
        if (arguments.length < 4) i0 = 0;
        if (i0 > i1) return F0;
        if (true === reverse)
        {
        var i, k, l=i1-i0+1, l1=l-1, r=l&15, q=r&1, lr=l1-r, Fv=q?F(F0,x[i1],i1):F0;
        for (i=l1-q; i>lr; i-=2) { k = i0+i; Fv = F(F(Fv,x[k],k),x[k-1],k-1); }
        for (i=lr; i>=0; i-=16)  { k = i0+i; Fv = F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(Fv,x[k],k),x[k-1],k-1),x[k-2],k-2),x[k-3],k-3),x[k-4],k-4),x[k-5],k-5),x[k-6],k-6),x[k-7],k-7),x[k-8],k-8),x[k-9],k-9),x[k-10],k-10),x[k-11],k-11),x[k-12],k-12),x[k-13],k-13),x[k-14],k-14),x[k-15],k-15); }
        }
        else
        {
        var i, k, l=i1-i0+1, r=l&15, q=r&1, Fv=q?F(F0,x[i0],i0):F0;
        for (i=q; i<r; i+=2)  { k = i0+i; Fv = F(F(Fv,x[k],k),x[k+1],k+1); }
        for (i=r; i<l; i+=16) { k = i0+i; Fv = F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(F(Fv,x[k],k),x[k+1],k+1),x[k+2],k+2),x[k+3],k+3),x[k+4],k+4),x[k+5],k+5),x[k+6],k+6),x[k+7],k+7),x[k+8],k+8),x[k+9],k+9),x[k+10],k+10),x[k+11],k+11),x[k+12],k+12),x[k+13],k+13),x[k+14],k+14),x[k+15],k+15); }
        }
        return Fv;
    }

    ,each: function each(x, F, i0, i1, reverse) {
        if (null == x || !x.length) return x;
        var len = x.length;
        if (arguments.length < 4) i1 = len-1;
        if (0 > i1) i1 += len;
        if (arguments.length < 3) i0 = 0;
        if (i0 > i1) return x;
        var i, k, l=i1-i0+1, l1, lr, r, q;
        if (true === reverse)
        {
            l1=l-1; r=l&15; q=r&1; lr=l1-r;
            if (q) F(x[i1]);
            for (i=l1-q; i>lr; i-=2)
            {
                k = i0+i;
                F(x[k  ]);
                F(x[k-1]);
            }
            for (i=lr; i>=0; i-=16)
            {
                k = i0+i;
                F(x[k  ] );
                F(x[k-1] );
                F(x[k-2] );
                F(x[k-3] );
                F(x[k-4] );
                F(x[k-5] );
                F(x[k-6] );
                F(x[k-7] );
                F(x[k-8] );
                F(x[k-9] );
                F(x[k-10]);
                F(x[k-11]);
                F(x[k-12]);
                F(x[k-13]);
                F(x[k-14]);
                F(x[k-15]);
            }
        }
        else
        {
            r=l&15; q=r&1;
            if (q) F(x[i0]);
            for (i=q; i<r; i+=2)
            {
                k = i0+i;
                F(x[k  ]);
                F(x[k+1]);
            }
            for (i=r; i<l; i+=16)
            {
                k = i0+i;
                F(x[k  ] );
                F(x[k+1] );
                F(x[k+2] );
                F(x[k+3] );
                F(x[k+4] );
                F(x[k+5] );
                F(x[k+6] );
                F(x[k+7] );
                F(x[k+8] );
                F(x[k+9] );
                F(x[k+10]);
                F(x[k+11]);
                F(x[k+12]);
                F(x[k+13]);
                F(x[k+14]);
                F(x[k+15]);
            }
        }
        return x;
    }
};
/**
* MOD3  MeshProxy Super Class
**/
function dispose(o)
{
    o.dispose();
}
function reset(o)
{
    o.reset();
}
function collapse(o)
{
    o.collapse();
}

MOD3.FaceProxy = MOD3.Class(null, {
    constructor: function FaceProxy() {
        this.vertices = [];
    },

    name: "FaceProxy",
    vertices: null,

    dispose: function() {
        var self = this;
        self.vertices = null;
        return self;
    },

    addVertex: function(v)  {
        this.vertices.push(v);
    },

    getVertices: function() {
        return this.vertices;
    }
});

MOD3.VertexProxy = MOD3.Class(null, {
    constructor: function VertexProxy(vertex, mesh) {
        var self = this;
        self.mesh = mesh || null;
        // use internal typed-arrays for speed
        self.original = new MOD3.VecArray([0,0,0]);
        self.ratio = new MOD3.VecArray([0,0,0]);
        // vertex can be zero
        if (null != vertex) self.setVertex(vertex);
    },

    name: "VertexProxy",
    mesh: null,
    vertex: null,
    original: null,
    ratio: null,

    dispose: function() {
        var self = this;
        self.mesh = null;
        self.vertex = null;
        self.original = null;
        self.ratio = null;
        return self;
    },

    setVertex: function(vt)  {
        // override
        var self = this;
        self.vertex = vt;
        return self;
    },

    getRatioVector: function() {
        var r = this.ratio, rv = new MOD3.VecArray(3);
        rv[0] = r[0]; rv[1] = r[1]; rv[2] = r[2];
        return rv;
    },

    getRatio: function(axis) {
        return this.ratio[MOD3.XYZi[axis]] || 0;
    },

    setRatios: function(rx, ry, rz) {
        var r = this.ratio;
        r[0] = rx || 0;
        r[1] = ry || 0;
        r[2] = rz || 0;
        return this;
    },

    getOriginalValue: function(axis) {
        return this.original[MOD3.XYZi[axis]] || 0;
    },

    setOriginalPosition: function(ox, oy, oz) {
        var o = this.original;
        o[0] = ox || 0;
        o[1] = oy || 0;
        o[2] = oz || 0;
        return this;
    },

    getXYZ: function() {
        // override
        return new MOD3.VecArray([0,0,0]);
    },

    getX: function() {
        // override
        return 0;
    },

    getY: function() {
        // override
        return 0;
    },

    getZ: function() {
        // override
        return 0;
    },

    getValue: function(axis)  {
        var self = this;
        // override
        return MOD3.ModConstant.X === axis
            ? self.getX()
            : (MOD3.ModConstant.Y === axis
            ? self.getY()
            : (MOD3.ModConstant.Z === axis
            ? self.getZ()
            : 0))
        ;
    },

    setXYZ: function(xyz) {
        // override
        return this;
    },

    setX: function(vo) {
        // override
        return this;
    },

    setY: function(vo) {
        // override
        return this;
    },

    setZ: function(vo) {
        // override
        return this;
    },

    setValue: function(axis, vo) {
        var self = this;
        // override
        if (MOD3.ModConstant.X === axis)       self.setX(vo);
        else if (MOD3.ModConstant.Y === axis)  self.setY(vo);
        else if (MOD3.ModConstant.Z === axis)  self.setZ(vo);
        return self;
    },

    reset: function() {
        // override
        var self = this;
        self.setXYZ(self.original);
        return self;
    },

    collapse: function() {
        // override
        var self = this, xyz = self.getXYZ(), o = self.original;
        o[0] = xyz[0]; o[1] = xyz[1]; o[2] = xyz[2];
        return self;
    }
});

MOD3.MeshProxy = MOD3.Class(null, {
    constructor: function MeshProxy(mesh) {
        var self = this;
        self.maxX = 0;
        self.maxY = 0;
        self.maxZ = 0;

        self.minX = 0;
        self.minY = 0;
        self.minZ = 0;

        self.maxAxis = 0;
        self.midAxis = 0;
        self.minAxis = 0;

        self.width = 0;
        self.height = 0;
        self.depth = 0;

        self.vertices = null;
        self.faces = null;
        self.mesh = null;

        if (null != mesh) self.setMesh(mesh);
    },

    name: "MeshProxy",

    maxX: 0,
    maxY: 0,
    maxZ: 0,
    minX: 0,
    minY: 0,
    minZ: 0,

    maxAxis: 0,
    midAxis: 0,
    minAxis: 0,

    width: 0,
    height: 0,
    depth: 0,

    vertices : null,
    faces : null,
    mesh : null,
    v: null,

    dispose: function() {
        var self = this;
        self.maxX = null;
        self.maxY = null;
        self.maxZ = null;
        self.minX = null;
        self.minY = null;
        self.minZ = null;

        self.maxAxis = null;
        self.midAxis = null;
        self.minAxis = null;

        self.width = null;
        self.height = null;
        self.depth = null;

        self.disposeFaces();
        self.disposeVertices();
        self.mesh = null;
        self.v = null;
        return self;
    },

    disposeVertices: function() {
        var self = this;
        if (self.vertices) MOD3.List.each(self.vertices, dispose);
        self.vertices = null;
        return self;
    },

    disposeFaces: function() {
        var self = this;
        if (self.faces) MOD3.List.each(self.faces, dispose);
        self.faces = null;
        return self;
    },

    init: function(mesh) {
        var self = this;
        self.mesh = mesh;
        //self.vertices = [];
        // not used
        //self.faces = [];
        return self;
    },

    setMesh: function(mesh) {
        var self = this;
        self.init(mesh);
        self.preApply();
        self.analyzeGeometry()
        self.postApply();
        return self;
    },

    getVertices: function() {
        return this.vertices;
    },

    getFaces: function() {
        return this.faces;
    },

    applyModifiers: function(modStack) {
        var self = this, sl, i;
        for (i=0,sl=modStack.length; i<sl; ++i)
        {
            modStack[i].enabled && modStack[i].apply(self);
        }
        return self;
    },

    analyzeGeometry: function() {
        var self = this,
            vertices = self.vertices,
            minX = 0, minY = 0,  minZ = 0,
            maxX = 0, maxY = 0, maxZ = 0,
            width = 0, height = 0, depth = 0,
            maxe, mine, w
        ;
        if (!vertices || !vertices.length) return self;

        w = vertices[0].getXYZ();
        minX = w[0];
        minY = w[1];
        minZ = w[2];

        maxX = w[0];
        maxY = w[1];
        maxZ = w[2];

        MOD3.List.each(vertices, function(v) {
            var xyz = v.getXYZ(), x = xyz[0], y = xyz[1], z = xyz[2];
            minX = stdMath.min(minX, x);
            minY = stdMath.min(minY, y);
            minZ = stdMath.min(minZ, z);

            maxX = stdMath.max(maxX, x);
            maxY = stdMath.max(maxY, y);
            maxZ = stdMath.max(maxZ, z);
            v.setOriginalPosition(x, y, z);
        });

        width = maxX - minX;
        height = maxY - minY;
        depth = maxZ - minZ;

        self.width = width;
        self.height = height;
        self.depth = depth;
        self.minX = minX;
        self.maxX = maxX;
        self.minY = minY;
        self.maxY = maxY;
        self.minZ = minZ;
        self.maxZ = maxZ;

        maxe = stdMath.max(width, height, depth);
        mine = stdMath.min(width, height, depth);

        if ((maxe === width) && (mine === height))
        {
            self.minAxis = MOD3.ModConstant.Y;
            self.midAxis = MOD3.ModConstant.Z;
            self.maxAxis = MOD3.ModConstant.X;
        }
        else if ((maxe === width) && (mine === depth))
        {
            self.minAxis = MOD3.ModConstant.Z;
            self.midAxis = MOD3.ModConstant.Y;
            self.maxAxis = MOD3.ModConstant.X;
        }
        else if ((maxe === height) && (mine === width))
        {
            self.minAxis = MOD3.ModConstant.X;
            self.midAxis = MOD3.ModConstant.Z;
            self.maxAxis = MOD3.ModConstant.Y;
        }
        else if ((maxe === height) && (mine === depth))
        {
            self.minAxis = MOD3.ModConstant.Z;
            self.midAxis = MOD3.ModConstant.X;
            self.maxAxis = MOD3.ModConstant.Y;
        }
        else if ((maxe === depth) && (mine === width))
        {
            self.minAxis = MOD3.ModConstant.X;
            self.midAxis = MOD3.ModConstant.Y;
            self.maxAxis = MOD3.ModConstant.Z;
        }
        else if ((maxe === depth) && (mine === height))
        {
            self.minAxis = MOD3.ModConstant.Y;
            self.midAxis = MOD3.ModConstant.X;
            self.maxAxis = MOD3.ModConstant.Z;
        }

        MOD3.List.each(vertices, function(v) {
            var xyz = v.getXYZ();
            v.setRatios(width > 0 ? (xyz[0] - minX) / width : 0, height > 0 ? (xyz[1] - minY) / height : 0, depth > 0 ? (xyz[2] - minZ) / depth : 0);
        });
        return self;
    },

    resetGeometry: function() {
        var self = this;
        MOD3.List.each(self.vertices, reset);
        return self;
    },

    collapseGeometry: function() {
        var self = this;
        MOD3.List.each(self.vertices, collapse);
        self.analyzeGeometry();
        return self;
    },

    getMin: function(axis) {
        var self = this;
        return MOD3.ModConstant.X === axis
            ? self.minX
            : (MOD3.ModConstant.Y === axis
            ? self.minY
            : (MOD3.ModConstant.Z === axis
            ? self.minZ
            : -1))
        ;
    },

    getMax: function(axis) {
        var self = this;
        return MOD3.ModConstant.X === axis
            ? self.maxX
            : (MOD3.ModConstant.Y === axis
            ? self.maxY
            : (MOD3.ModConstant.Z === axis
            ? self.maxZ
            : -1))
        ;
    },

    getSize: function(axis) {
        var self = this;
        return MOD3.ModConstant.X === axis
            ? self.width
            : (MOD3.ModConstant.Y === axis
            ? self.height
            : (MOD3.ModConstant.Z === axis
            ? self.depth
            : -1))
        ;
    },

    update: function()  {
        // do nothing
        return this;
    },

    preApply: function()  {
        // do nothing
        return this;
    },

    postApply: function()  {
        // do nothing
        return this;
    },

    updateMeshPosition: function(p) {
        // do nothing
        return this;
    }
});

MOD3.Library3d = {
    id      : "Library3d",
    Mesh    : MOD3.MeshProxy,
    Vertex  : MOD3.VertexProxy
};

MOD3.Factory = {
    getLibrary: function(json) {
        if (json && json.library && MOD3[json.library]) return MOD3[json.library];
        // dummy, default
        return MOD3.Library3d;
    }

    ,getMeshProxy: function(lib3D) {
        if (arguments.length) return lib3D.Mesh ? new lib3D.Mesh() : null;
        return null;
    }

    ,getModifier: function(json) {
        if (json && json.modifier && MOD3[json.modifier]) return new MOD3[json.modifier]();
        return null;
    }

    /*
    ,getMesh: function(json) {
        if (json && json.mesh && MOD3[json.mesh] ) return new MOD3.MeshProxy().unserialize(json);
        // dummy, default
        return new MOD3.MeshProxy();
    }

    ,getVertex: function(json) {
        if (json && json.vertex && MOD3[json.vertex]) return new MOD3.VertexProxy().unserialize(json);
        // dummy, default
        return new MOD3.VertexProxy();
    }*/
};
/**
* MOD3  Modifier & ModifierStack Classes
**/
var _modCount = 0;

MOD3.Modifier = MOD3.Class({
    constructor: function Modifier() {
        var self = this;
        self.id = ++_modCount;
        self.name = 'Modifier';
        self.axes = MOD3.ModConstant.NONE;
        self.constraint = MOD3.ModConstant.NONE;
        self.enabled = true;
    },

    id: null,
    name: 'Modifier',
    axes: null,
    constraint: null,
    enabled: true,

    dispose: function() {
        var self = this;
        self.name = null;
        self.axes = null;
        self.constraint = null;
        return self;
    },

    enable: function(enabled) {
        if (arguments.length)
        {
            this.enabled = !!enabled;
            return this;
        }
        return this.enabled;
    },

    constraintAxes: function(axes) {
        this.axes = axes || MOD3.ModConstant.NONE;
        return this;
    },

    setConstraint: function(c) {
        this.constraint = c || MOD3.ModConstant.NONE;
        return this;
    },

    // override
    apply: function(modifiable) {
        return this;
    },

    toString: function() {
        return '[Modifier '+this.name+']';
    }
});

MOD3.ModifierStack = MOD3.Class({
    constructor: function ModifierStack(lib3d, mesh) {
        var self = this;
        if (!(self instanceof ModifierStack)) return new ModifierStack(lib3d, mesh);
        self.stack = [];
        self.setModifiable(MOD3.Factory.getMeshProxy(lib3d), mesh);
    },

    name: "ModifierStack",
    modifiable: null,
    stack: null,

    dispose: function(withModifiers) {
        var self = this;
        if (withModifiers && self.stack) while (self.stack.length) self.stack.pop().dispose();
        if (self.modifiable) self.modifiable.dispose();
        self.stack = null;
        self.modifiable = null;
        return self;
    },

    getModifiable: function() {
        return this.modifiable;
    },

    setModifiable: function(modifiable, mesh) {
        var self = this;
        self.modifiable = modifiable;
        if (mesh) self.modifiable.setMesh(mesh);
        return self;
    },

    add: function(modifier) {
        var self = this;
        if (modifier) self.stack.push(modifier);
        return self;
    },

    apply: function() {
        var self = this, modifiable = self.modifiable, stack = self.stack;
        if (modifiable && stack && stack.length)
            modifiable
                .preApply()
                .resetGeometry()
                .applyModifiers(stack)
                .postApply()
                .update()
            ;
        return self;
    },

    collapse: function() {
        var self = this, modifiable = self.modifiable, stack = self.stack;
        if (modifiable && stack && stack.length)
        {
            modifiable
                .preApply()
                .resetGeometry()
                .applyModifiers(stack)
                .collapseGeometry()
                .postApply()
                .update()
            ;
            stack.length = 0;
        }
        return self;
    },

    clear: function() {
        var self = this;
        if (self.stack) self.stack.length = 0;
        return self;
    }
});
// aliases
MOD3.ModifierStack.prototype.getMeshInfo = MOD3.ModifierStack.prototype.getModifiable;
MOD3.ModifierStack.prototype.addModifier = MOD3.ModifierStack.prototype.add;
!function(MOD3) {
"use strict";
/**
* MOD3  Pivot Modifier
**/

/**[DOC_MD]
 * ### Pivot modifier
 *
 * Allows to move the pivot point of a 3D mesh.
 *
 * @author Bartek Drozdz
 *
[/DOC_MD]**/
MOD3.Pivot = MOD3.Class(MOD3.Modifier, {
    constructor: function Pivot(x, y, z) {
        var self = this;
        if (!(self instanceof Pivot)) return new Pivot(x, y, z);
        self.$super('constructor');
        self.name = 'Pivot';
        self.vector = new MOD3.Vector3(x||0, y||0, z||0);
    },

    vector: null,

    dispose: function() {
        var self = this;
        self.vector.dispose();
        self.vector = null;
        self.$super('dispose');
        return self;
    },

    setMeshCenter: function(modifiable) {
        var self = this;
        self.vector = new MOD3.Vector3(
        -(modifiable.minX + 0.5*modifiable.width),
        -(modifiable.minY + 0.5*modifiable.height),
        -(modifiable.minZ + 0.5*modifiable.depth)
        );
        return self;
    },

    apply: function(modifiable) {
        var self = this, pivot = self.vector, pv = pivot.xyz;

        MOD3.List.each(modifiable.vertices, function(v) {
            v.setXYZ(MOD3.Vector3.add(v.getXYZ(), pv));
        });
        modifiable.updateMeshPosition(pivot.negate());
        return self;
    }
});
}(MOD3);!function(MOD3) {
"use strict";
/**
* MOD3  Bend Modifier
**/

/**[DOC_MD]
 * ### Bend modifier 
 *
 * Bends an object along an axis. 
 *
 * @author Bartek Drozdz
 *  
[/DOC_MD]**/
var stdMath = Math, PI = stdMath.PI,
    TWO_PI = 2*PI, HALF_PI = PI/2;

MOD3.Bend = MOD3.Class(MOD3.Modifier, {
    constructor: function Bend(force, offset, angle) {
        var self = this;
        if (!(self instanceof Bend)) return new Bend(force, offset, angle);
        self.$super('constructor');
        self.name = 'Bend';
        self.constraint = MOD3.ModConstant.NONE;
        self.switchAxes = false;
        self.force = force || 0;
        self.offset = offset || 0;
        self.angle = angle || 0;
    },

    force: 0,
    offset: 0,
    angle: 0,
    switchAxes: false,
    
    dispose: function() {
        var self = this;
        self.force = null;
        self.offset = null;
        self.angle = null;
        self.switchAxes = null;
        self.$super('dispose');
        return self;
    },
    
    apply: function(modifiable) {   
        var self = this;
        
        if (0 === self.force) return self;
        
        var constraint = self.constraint, switchAxes = self.switchAxes,
            force = self.force, offset = stdMath.min(1, stdMath.max(0, self.offset)), a = self.angle,
            max = switchAxes ? modifiable.midAxis : modifiable.maxAxis,
            min = modifiable.minAxis,
            mid = switchAxes ? modifiable.maxAxis : modifiable.midAxis,
            width = modifiable.getSize(max),
            height = modifiable.getSize(mid),
            origin = modifiable.getMin(max),
            //diagAngle = stdMath.atan2(height, width),
            m1 = new MOD3.Matrix().rotate(a),
            m2 = new MOD3.Matrix().rotate(-a),
            distance = origin + width * offset,
            radius = width / PI / force,
            bendAngle = TWO_PI * (width / (radius * TWO_PI))
        ;
        
        MOD3.List.each(modifiable.vertices, function(v) {
            var xyz = v.getXYZ(),
                vmax = xyz[MOD3.XYZi[max]],
                vmid = xyz[MOD3.XYZi[mid]],
                vmin = xyz[MOD3.XYZi[min]],
                np = MOD3.Matrix.transform(m1, [vmax, vmid]),
                p, fa, op, ow, np2
            ;
            vmax = np[0]; vmid = np[1];

            p = (vmax - origin) / width;

            if (
                ((MOD3.ModConstant.LEFT === constraint) && (p <= offset)) || 
                ((MOD3.ModConstant.RIGHT === constraint) && (p >= offset))
            ) 
            {  
                /* do nothing */ 
            } 
            else 
            {
                fa = (HALF_PI - bendAngle * offset) + (bendAngle * p);
                op = stdMath.sin(fa) * (radius + vmin);
                ow = stdMath.cos(fa) * (radius + vmin);
                vmin = op - radius;
                vmax = distance - ow;
            }

            np2 = MOD3.Matrix.transform(m2, [vmax, vmid]);
            vmax = np2[0]; vmid = np2[1];
            xyz[MOD3.XYZi[max]] = vmax;
            xyz[MOD3.XYZi[mid]] = vmid;
            xyz[MOD3.XYZi[min]] = vmin;
            v.setXYZ(xyz);
        });
        return self;
    }
});
}(MOD3);!function(MOD3) {
"use strict";
/**
* MOD3  Bloat Modifier
**/

/**[DOC_MD]
 * ### Bloat modifier
 *
 * Bloats a mesh by forcing vertices out of specified sphere
 *
 * @author makc
 *
[/DOC_MD]**/
var stdMath = Math;

MOD3.Bloat = MOD3.Class(MOD3.Modifier, {
    constructor: function Bloat(radius, a, center) {
        var self = this;
        if (!(self instanceof Bloat)) return new Bloat(radius, a, center);
        self.$super('constructor');
        self.name = 'Bloat';
        self.radius = radius || 0;
        self.a = null == a ? 0.01 : a;
        self.center = center || MOD3.Vector3.ZERO();
        //self.u = MOD3.Vector3.ZERO();
    },

    radius: 0,
    a: 0.01,
    center: null,
    //u: null,

    dispose: function() {
        var self = this;
        self.center.dispose();
        self.center = null;
        self.radius = null;
        self.a = null;
        self.$super('dispose');
        return self;
    },

    apply: function(modifiable)  {
        var self = this, center = self.center.xyz,
            radius = stdMath.max(0, self.radius), a = stdMath.max(0, self.a);

        MOD3.List.each(modifiable.vertices, function(v) {
            // get a vector towards vertex
            // change norm to norm + r * exp (-a * norm)
            var uu = MOD3.Vector3.sub(v.getXYZ(), center), magn = MOD3.Vector3.mod(uu);
            MOD3.Vector3.muls(MOD3.Vector3.norm(uu), magn + radius * stdMath.exp(- magn * a));
            // move vertex accordingly
            v.setXYZ(MOD3.Vector3.add(uu, center));
            // ?? needed??
            //self.u=uu;
        });
        return self;
    }
});
}(MOD3);!function(MOD3) {
"use strict";
/**
* MOD3  Twist Modifier
**/

/**[DOC_MD]
 * ### Twist modifier
 *
 * Twist mesh along an axis
 * Adapted from the Twist modifier for PV3D
 *
[/DOC_MD]**/
MOD3.Twist = MOD3.Class(MOD3.Modifier, {
    constructor: function Twist(angle, vector, center) {
        var self = this;
        if (!(self instanceof Twist)) return new Twist(angle, vector, center);
        self.$super('constructor');
        self.name = 'Twist';
        self.angle = angle || 0;
        self.vector = vector || MOD3.Vector3.Y();
        self.center = center || MOD3.Vector3.ZERO();
    },

    angle: 0,
    vector: null,
    center: null,

    dispose: function() {
        var self = this;
        self.vector.dispose();
        self.vector = null;
        self.angle = null;
        self.center.dispose();
        self.center = null;
        self.$super('dispose');
        return self;
    },

    apply: function(modifiable) {
        var self = this,
            tvec = self.vector.normalizeSelf().xyz, angle = self.angle, center = self.center.xyz,
            modulo = MOD3.Vector3.mod([0.5*modifiable.maxX, 0.5*modifiable.maxY, 0.5*modifiable.maxZ]),
            d = -MOD3.Vector3.dot(tvec, center),
            m1 = new MOD3.Matrix4(), m2 = new MOD3.Matrix4()
        ;

        MOD3.List.each(modifiable.vertices, function(v) {
            var xyz = v.getXYZ(),
                a = (MOD3.Vector3.dot(xyz, tvec) + d) * angle / modulo,
                m = MOD3.Matrix4.mult(
                    m2.rotate(tvec[0], tvec[1], tvec[2], a, true),
                    m1.translate(xyz[0], xyz[1], xyz[2], true)
                )
            ;
            v.setXYZ([m.m[3], m.m[7], m.m[11]]);
        });
        return self;
    }
});
}(MOD3);!function(MOD3) {
"use strict";
/**
* MOD3  Skew Modifier
**/

/**[DOC_MD]
 * ### Skew modifier
 *
 * Skew mesh along an axis
 *
 * @author Bartek Drozdz
 *
[/DOC_MD]**/
var stdMath = Math;

MOD3.Skew = MOD3.Class(MOD3.Modifier, {
    constructor: function Skew(force, offset, power, falloff) {
        var self = this;
        if (!(self instanceof Skew)) return new Skew(force, offset, power, falloff);
        self.$super('constructor');
        self.name = 'Skew';
        self.constraint = MOD3.ModConstant.NONE;
        self.force = force != null ? force : 0;
        self.offset = offset != null ? offset : 0.5;
        self.power = power != null ? power : 1;
        self.falloff = falloff != null ? falloff : 1;
        self.inverseFalloff = false;
        self.oneSide = false;
        self.swapAxes = false;
        self.skewAxis = 0;
    },

    force: 0,
    skewAxis: 0,
    offset: 0.5,
    power: 1,
    falloff:  1,
    inverseFalloff: false,
    oneSide: false,
    swapAxes: false,

    dispose: function() {
        var self = this;
        self.force = null;
        self.skewAxis = null;
        self.offset = null;
        self.power = null;
        self.falloff = null;
        self.inverseFalloff = null;
        self.oneSide = null;
        self.swapAxes = null;
        self.$super('dispose');
        return self;
    },

    apply: function(modifiable) {
        var self = this,
            constraint = self.constraint,
            skewAxis = self.skewAxis || modifiable.maxAxis,
            swapAxes = self.swapAxes,
            offset = stdMath.min(1, stdMath.max(0, self.offset)),
            oneSide = self.oneSide,
            inverseFalloff = !!self.inverseFalloff,
            falloff = stdMath.min(1, stdMath.max(0, self.falloff)),
            mirrorfalloff = 1-falloff,
            power = self.power,
            force = self.force,
            displaceAxis = MOD3.ModConstant.X === skewAxis
                ? (swapAxes ? MOD3.ModConstant.Z : MOD3.ModConstant.Y)
                : (MOD3.ModConstant.Y === skewAxis
                ? (swapAxes ? MOD3.ModConstant.Z : MOD3.ModConstant.X)
                : (MOD3.ModConstant.Z === skewAxis
                ? (swapAxes ? MOD3.ModConstant.Y : MOD3.ModConstant.X)
                : 0))
        ;

        MOD3.List.each(modifiable.vertices, function(v) {
            var r, dr, f, p, vRatio;
            vRatio = v.getRatio(skewAxis);
            if ((MOD3.ModConstant.LEFT === constraint) && (vRatio <= offset)) return;
            if ((MOD3.ModConstant.RIGHT === constraint) && (vRatio > offset)) return;

            r = vRatio - offset;
            if (oneSide && (0 > r)) r = -r;

            dr = v.getRatio(displaceAxis);
            if (inverseFalloff) dr = 1 - dr;

            f = falloff + dr * mirrorfalloff;
            p = (0 > r ? -1 : 1) * stdMath.pow(stdMath.abs(r), power);
            v.setValue(displaceAxis, v.getValue(displaceAxis) + force * p * f);
        });
        return self;
    },
});
}(MOD3);!function(MOD3) {
"use strict";
/**
* MOD3  Taper Modifier
**/

/**[DOC_MD]
 * ### Taper modifier
 *
 * The taper modifier displaces the vertices on two axes proportionally to their position on the third axis.
 *
 * @author Bartek Drozdz
 *
[/DOC_MD]**/
var stdMath = Math;

MOD3.Taper = MOD3.Class(MOD3.Modifier, {
    constructor: function Taper(force, power, v1, v2)  {
        var self = this;
        if (!(self instanceof Taper)) return new Taper(force, power, v1, v2);
        self.$super('constructor');
        self.name = 'Taper';
        /*self.start = 0;
        self.end = 1;*/
        self.force = force != null ? force : 0;
        self.power = power != null ? power : 1;
        self.vector = v1 || MOD3.Vector3.Y(false);
        self.vector2 = v2 || MOD3.Vector3.Y();
    },

    force: 0,
    power: 1,
    /*start: 0,
    end: 1,*/
    vector: null,
    vector2: null,

    /*setFalloff : function(start, end)  {
        this.start = (start!==undef) ? start : 0;
        this.end = (end!==undef) ? end : 1;

        return this;
    },*/

    dispose: function() {
        var self = this;
        self.vector.dispose();
        self.vector2.dispose();
        self.vector = null;
        self.vector2 = null;
        self.force = null;
        self.power = null;
        self.$super('dispose');
        return self;
    },

    apply: function(modifiable) {
        var self = this,
            vec = self.vector.xyz, vec2 = self.vector2.xyz,
            force = self.force, power = self.power, m = new MOD3.Matrix4();

        MOD3.List.each(modifiable.vertices, 1 !== power
            ? function(v) {
                var ar = MOD3.Vector3.mod(MOD3.Vector3.mul(v.getRatioVector(), vec2)), sc = force * stdMath.pow(ar, power);
                v.setXYZ(MOD3.Matrix4.multXYZ(m.scale(1 + sc * vec[0], 1 + sc * vec[1], 1 + sc * vec[2]), v.getXYZ()));
            }
            : function(v) {
                var ar = MOD3.Vector3.mod(MOD3.Vector3.mul(v.getRatioVector(), vec2)), sc = force * ar;
                v.setXYZ(MOD3.Matrix4.multXYZ(m.scale(1 + sc * vec[0], 1 + sc * vec[1], 1 + sc * vec[2]), v.getXYZ()));
            }
        );
        return self;
    }
});
}(MOD3);!function(MOD3) {
"use strict";
/**
* MOD3  Wheel Modifier
**/

/**[DOC_MD]
 * ### Wheel modifier
 *
 * Use it with vehicle models for wheels.
 *
 * The usual problem with a 3d wheel in a vahicle is that it is
 * supposed to turn (steer) and roll in the same time.
 * So, this code:
 *
 * ```javascript
 * wheel.rotationY = 10; // Steer 10deg to the left
 * wheel.rotationZ += 5; // Roll with a speed of 5
 * ```
 * This will make the wheel roll incorectly.
 *
 * A usual way to solve this problem is to put the wheel in another DisplayObject3D/Mesh,
 * turn the parent and roll the child, like that:
 * ```javascript
 * steer.rotationY = 10; // Steer 10deg to the left
 * steer.wheel.rotationZ += 5; // Roll with a speed of 5
 * ```
 * That will make the wheel behave correctly. But it can be uncomfortanble to apply, especially
 * to imported complex Collada models.
 *
 * The Wheel modifier elegantly solves this problem by doind the proper math in order to steer and roll
 * a single mesh at the same time. The only thing you need to do is to specify a steer vector and
 * roll vector - usually it will be 2 of the cardinal axes. The default value is:
 *
 * * steer - along the Y axis / new Vector3(0, 1, 0)
 * * roll - along the Z axis / new Vector3(0, 0, 1)
 *
 *
 * It should work with most car models imported from 3D editors as this is the natural position of a wheel.
 *
 * *Please note, that Papervision primitive cylinder, which may also be used as wheel, will require different axes
 * (Y for roll and Z or X for steer).*
 *
 * @author Bartek Drozdz
 *
 [/DOC_MD]**/
MOD3.Wheel = MOD3.Class(MOD3.Modifier, {
   constructor: function Wheel(speed, turn, roll, steerVector, rollVector) {
        var self = this;
        if (!(self instanceof Wheel)) return new Wheel(speed, turn, roll, steerVector, rollVector);
        self.$super('constructor');
        self.name = 'Wheel';
        self.speed = speed || 0;
        self.turn = turn || 0;
        self.roll = roll || 0;
        self.steerVector = steerVector || MOD3.Vector3.Y();
        self.rollVector = rollVector || MOD3.Vector3.Z();
    },

    speed:  0,
    turn: 0,
    roll: 0,
    steerVector: null,
    rollVector: null,

    dispose: function() {
        var self = this;
        self.speed = null;
        self.turn = null;
        self.roll = null;
        self.steerVector.dispose();
        self.rollVector.dispose();
        self.steerVector = null;
        self.rollVector = null;
        self.$super('dispose');

        return self;
    },

    apply: function(modifiable) {
        var self = this,
            steerVector = self.steerVector.normalizeSelf(),
            rollVector = self.rollVector.normalizeSelf(),
            turn = self.turn, roll = self.roll,
            //radius = 0.5*modifiable.width,
            //step = radius * self.speed / PI,
            //perimeter = radius * TWO_PI,
            ms = null, mt = null
        ;

        self.roll += self.speed;

        if (turn)
        {
            mt = new MOD3.Matrix4().rotateFromVector(steerVector, turn);
            ms = new MOD3.Matrix4().rotateFromVector(mt.multiplyVector(rollVector.clone()), roll);
        }
        else
        {
            ms = new MOD3.Matrix4().rotateFromVector(rollVector, roll);
        }

        MOD3.List.each(modifiable.vertices, mt
            ? function(v) {
                v.setXYZ(MOD3.Matrix4.multXYZ(ms, MOD3.Matrix4.multXYZ(mt, v.getXYZ())));
            }
            : function(v) {
                v.setXYZ(MOD3.Matrix4.multXYZ(ms, v.getXYZ()));
            }
        );
        return self;
    }
});
}(MOD3);!function(MOD3) {
"use strict";
/**
* MOD3  Break Modifier
**/

/**[DOC_MD]
 * ### Break modifier
 *
 * Allow to break a mesh
 *
 * @author Bartek Drozdz
 *
[/DOC_MD]**/
var stdMath = Math;

MOD3.Break = MOD3.Class(MOD3.Modifier, {
    constructor: function Break(offset, angle, vector) {
        var self = this;
        if (!(self instanceof Break)) return new Break(offset, angle, vector);
        self.$super('constructor');
        self.name = 'Break';
        self.offset = offset || 0;
        self.angle = angle || 0;
        self.vector = vector || MOD3.Vector3.Y();
        self.range = new MOD3.Range(0, 1);
    },

    offset: 0,
    angle: 0,
    vector: null,
    range: null,

    dispose: function() {
        var self = this;
        self.vector.dispose();
        self.range.dispose();
        self.vector = null;
        self.range = null;
        self.offset = null;
        self.angle = null;
        self.$super('dispose');
        return self;
    },

    apply: function(modifiable) {
        var self = this,
            offset = stdMath.min(1, stdMath.max(0, self.offset)), range = self.range, angle = self.angle,
            bv = self.vector.normalizeSelf().xyz, pv, rm;

        pv = modifiable.minZ + modifiable.depth*offset;
        rm = new MOD3.Matrix4().rotate(bv[0], bv[1], bv[2], angle);

        MOD3.List.each(modifiable.vertices, function(v) {
            var c = v.getXYZ();
            c[2] -= pv;
            if ((0 <= c[2]) && range.isIn(v.ratio[1])) MOD3.Matrix4.multXYZ(rm, c);
            c[2] += pv;
            v.setXYZ(c);
        });
        return self;
   }
});
}(MOD3);!function(MOD3) {
"use strict";
/**
* MOD3  Noise Modifier
**/

/**[DOC_MD]
 * ### Noise modifier
 *
 * Randomly displaces each vertex in all 3 axes
 *
 *
[/DOC_MD]**/
var stdMath = Math;

MOD3.Noise = MOD3.Class(MOD3.Modifier, {
    constructor: function Noise(force) {
        var self = this;
        if (!(self instanceof Noise)) return new Noise(force);
        self.$super('constructor');
        self.name = 'Noise';
        self.force = force || 0;
        self.start = 0;
        self.end = 1;
        self.axes = MOD3.ModConstant.X | MOD3.ModConstant.Y | MOD3.ModConstant.Z;
    },

    force: 0,
    start: 0,
    end: 1,

    dispose: function() {
        var self = this;
        self.force = null;
        self.start = null;
        self.end = null;
        self.$super('dispose');
        return self;
    },

    setFalloff: function(start, end) {
        var self = this;
        self.start = start != null ? start : 0;
        self.end = end != null ? end : 1;
        return self;
    },

    apply: function(modifiable) {
        var self = this,
            axes = self.axes, start = self.start, end = self.end,
            force = self.force, halfforce = 0.5*force,
            maxAxis = modifiable.maxAxis;

        if ((0 == axes) || (0 == force)) return self;

        MOD3.List.each(modifiable.vertices, function(v) {
            var r = stdMath.random() * force - halfforce,
                p = v.getRatio(maxAxis), rp, xyz;
            if (start < end)
            {
                if (p < start) p = 0;
                else if (p > end) p = 1;
            }
            else if (start > end)
            {
                p = 1 - p;
                if (p > start) p = 0;
                else if (p < end) p = 1;
            }
            else
            {
                p = 1;
            }

            rp = r * p;
            xyz = v.getXYZ();
            v.setXYZ([
                xyz[0] + (axes & MOD3.ModConstant.X ? rp : 0),
                xyz[1] + (axes & MOD3.ModConstant.Y ? rp : 0),
                xyz[2] + (axes & MOD3.ModConstant.Z ? rp : 0)
            ]);
        });
        return self;
    }
});
}(MOD3);!function(MOD3) {
"use strict";
/**
* MOD3  DisplaceMap (BitmapDisplacement) Modifier
**/

/**[DOC_MD]
 * ### DisplaceMap (BitmapDisplacement) Modifier
 *
 * Displaces vertices based on RGB values of bitmapData pixels.
 *
 * BitmapDisplacement is inspired by both the AS3 built-in DisplacementMapFilter. It allows
 * to use color values for each channels of a bitmap to modify the position of vertices in a mesh.
 *
 * The displacement takes place along the cardinal axes, and each axis is mapped to a
 * channel in the bitmap: X for Red, Y for Green and Z for Blue.
 *
 * @author Bartek Drozdz
 *
[/DOC_MD]**/
MOD3.DisplaceMap = MOD3.Class(MOD3.Modifier, {
    constructor: function DisplaceMap(bmp, force, offset) {
        var self = this;
        if (!(self instanceof DisplaceMap)) return new DisplaceMap(bmp, force, offset);
        self.$super('constructor');
        self.name = 'DisplaceMap';
        if (+bmp === bmp) // number
        {
            self.force = bmp || 1;
            self.offset = null == force ? 127 : force;// 0x7F;
        }
        else
        {
            self.setBitmap(bmp);
            self.force = force || 1;
            self.offset = null == offset ? 127 : offset;// 0x7F;
        }
        self.axes = MOD3.ModConstant.X | MOD3.ModConstant.Y | MOD3.ModConstant.Z;
    },

    width: null,
    height: null,
    bmpData: null,
    force: 1,
    offset: 127,

    dispose: function() {
        var self = this;
        self.bmpData = null;
        self.width = null;
        self.height = null;
        self.force = null;
        self.offset = null;
        self.$super('dispose');
        return self;
    },

    setBitmap: function(bmpData) {
        var self = this;
        self.bmpData = bmpData ? bmpData.data : null;
        self.width = bmpData ? bmpData.width : 0;
        self.height = bmpData ? bmpData.height : 0;
        return self;
    },

    apply: function(modifiable) {
        var self = this,
            axes = self.axes,
            w = self.width, h = self.height, bmp = self.bmpData,
            force = self.force, offset = self.offset;

        if (!axes || !bmp) return self;

        MOD3.List.each(modifiable.vertices, function(v) {
            var uv, uu, vv, xyz = v.getXYZ();

            uu = ~~((w - 1) * v.ratio[0]/* X */);
            vv = ~~((h - 1) * v.ratio[2]/* Z */);
            uv = (vv * w + uu) << 2;

            v.setXYZ([
                xyz[0] + (axes & MOD3.ModConstant.X ? ((bmp[uv] & 0xff) - offset) * force : 0),
                xyz[1] + (axes & MOD3.ModConstant.Y ? ((bmp[uv+1] & 0xff) - offset) * force : 0),
                xyz[2] + (axes & MOD3.ModConstant.Z ? ((bmp[uv+2] & 0xff) - offset) * force : 0)
            ]);
        });
        return self;
    }
});
}(MOD3);!function(MOD3) {
"use strict";
/**
* MOD3  Perlin/Simplex Noise Modifier
**/

/**[DOC_MD]
 * ### Perlin modifier
 *
 *  Displaces vertices based on a perlin/simplex noise source.
 *
 *  Accepts a perlin/simplex noise data (with width and height information) and displaces vertices
 *  based on the value of each point of the noise map.
 *
 *  @author Bartek Drozdz
 *
 *  @uses: https://github.com/josephg/noisejs for JavaScript
 *
[/DOC_MD]**/
function cyclic_shift(a, w, h, dX, dY)
{
    var size = w*h, b = new MOD3.VecArray(size), i, j, i2, j2, index;
    if (dX < 0) dX += w;
    if (dY < 0) dY += h;
    dX = ~~dX; dY = ~~dY;
    for (i=0,j=0,index=0; index<size; ++index,++i)
    {
        if (i >= w) {i = 0; ++j;}
        i2 = (i + dX) % w; j2 = (j + dY) % h;
        b[index] = a[i2 + j2 * w];
    }
    return b;
}
/*function generate2d(perlinNoise2d, w, h)
{
    var size = w*h, a = new MOD3.VecArray(size), i, j, index;
    for (i=0,j=0,index=0; index<size; ++index,++i)
    {
        if (i >= w) {i = 0; ++j;}
        a[index] = perlinNoise2d(i/w, j/h);
    }
    return a;
}*/
MOD3.Perlin = MOD3.Class(MOD3.Modifier, {
    constructor: function Perlin(force, noise, autoRun) {
        var self = this;
        if (!(self instanceof Perlin)) return new Perlin(force, noise, autoRun);
        self.$super('constructor');
        self.name = 'Perlin';
        self.force = null != force ? force : 1;
        self.perlin = noise;
        self.autoRun = null != autoRun ? !!autoRun : true;
        self.axes = MOD3.ModConstant.X | MOD3.ModConstant.Y | MOD3.ModConstant.Z;
    },

    speedX: 1,
    speedY: 1,
    perlin: null,
    force: 1,
    offset: 0,
    autoRun: true,

    dispose: function() {
        var self = this;
        self.perlin = null;
        self.speedX = null;
        self.speedY = null;
        self.force = null;
        self.offset = null;
        self.autoRun = null;
        self.$super('dispose');

        return self;
    },

    setSpeed: function(dX, dY) {
        var self = this;
        self.speedX = dX;
        self.speedY = dY;
        return self;
    },

    apply: function(modifiable) {
        var self = this,
            axes = self.axes, force = self.force,
            offset = self.offset, pn = self.perlin,
            w, h;

        if (!axes || !pn) return self;
        w = pn.width; h = pn.height;
        if (self.autoRun)
        {
            pn = self.perlin = cyclic_shift(pn, w, h, self.speedX, self.speedY);
            pn.width = w; pn.height = h;
        }

        MOD3.List.each(modifiable.vertices, function(v) {
            var xyz = v.getXYZ(),
                uu = ~~((w - 1) * v.ratio[0]/* u */),
                vv = ~~((h - 1) * v.ratio[2]/* v */),
                uv = uu + vv * w;

            v.setXYZ([
                xyz[0] + (axes & MOD3.ModConstant.X ? (pn[uv] - offset) * force : 0),
                xyz[1] + (axes & MOD3.ModConstant.Y ? (pn[uv/*+1*/] - offset) * force : 0),
                xyz[2] + (axes & MOD3.ModConstant.Z ? (pn[uv/*+2*/] - offset) * force : 0)
            ]);
        });
        return self;
    }
});
}(MOD3);// export it
return MOD3;
});
