• Jump To … +
    ./source/asset-management/image-asset.js ./source/asset-management/noise-asset.js ./source/asset-management/raw-asset.js ./source/asset-management/reaction-diffusion-asset.js ./source/asset-management/sprite-asset.js ./source/asset-management/video-asset.js ./source/core/animation-loop.js ./source/core/display-cycle.js ./source/core/document.js ./source/core/events.js ./source/core/init.js ./source/core/library.js ./source/core/snippets.js ./source/core/user-interaction.js ./source/factory/action.js ./source/factory/anchor.js ./source/factory/animation.js ./source/factory/bezier.js ./source/factory/block.js ./source/factory/button.js ./source/factory/canvas.js ./source/factory/cell.js ./source/factory/cog.js ./source/factory/color.js ./source/factory/conic-gradient.js ./source/factory/crescent.js ./source/factory/element.js ./source/factory/emitter.js ./source/factory/enhanced-label.js ./source/factory/filter.js ./source/factory/gradient.js ./source/factory/grid.js ./source/factory/group.js ./source/factory/label.js ./source/factory/line-spiral.js ./source/factory/line.js ./source/factory/loom.js ./source/factory/mesh.js ./source/factory/net.js ./source/factory/oval.js ./source/factory/particle-force.js ./source/factory/particle-spring.js ./source/factory/particle-world.js ./source/factory/particle.js ./source/factory/pattern.js ./source/factory/picture.js ./source/factory/polygon.js ./source/factory/polyline.js ./source/factory/quadratic.js ./source/factory/radial-gradient.js ./source/factory/rectangle.js ./source/factory/render-animation.js ./source/factory/shape.js ./source/factory/spiral.js ./source/factory/stack.js ./source/factory/star.js ./source/factory/tetragon.js ./source/factory/ticker.js ./source/factory/tracer.js ./source/factory/tween.js ./source/factory/unstacked-element.js ./source/factory/wheel.js ./source/helper/array-pool.js ./source/helper/color-engine.js ./source/helper/document-root-elements.js ./source/helper/filter-engine-bluenoise-data.js ./source/helper/filter-engine.js ./source/helper/random-seed.js ./source/helper/shape-path-calculation.js ./source/helper/shared-vars.js ./source/helper/system-flags.js ./source/helper/utilities.js ./source/helper/workstore.js ./source/mixin/anchor.js ./source/mixin/asset-advanced-functionality.js ./source/mixin/asset-consumer.js ./source/mixin/asset.js ./source/mixin/base.js ./source/mixin/button.js ./source/mixin/cascade.js ./source/mixin/cell-key-functions.js ./source/mixin/delta.js ./source/mixin/display-shape.js ./source/mixin/dom.js ./source/mixin/entity.js ./source/mixin/filter.js ./source/mixin/hidden-dom-elements.js ./source/mixin/mimic.js ./source/mixin/path.js ./source/mixin/pattern.js ./source/mixin/pivot.js ./source/mixin/position.js ./source/mixin/shape-basic.js ./source/mixin/shape-curve.js ./source/mixin/styles.js ./source/mixin/text.js ./source/mixin/tween.js ./source/scrawl.js ./source/untracked-factory/cell-fragment.js ./source/untracked-factory/coordinate.js ./source/untracked-factory/drag-zone.js ./source/untracked-factory/keyboard-zone.js ./source/untracked-factory/observe-update.js ./source/untracked-factory/palette.js ./source/untracked-factory/particle-history.js ./source/untracked-factory/quaternion.js ./source/untracked-factory/state.js ./source/untracked-factory/text-style.js ./source/untracked-factory/vector.js
  • §

    Quaternion factory

    Scrawl-canvas uses quaternion objects for some of its calculations - in particular for calculating DOM element 3D rotation values. These objects are not stored in the library; rather, they are kept in a quaternion pool and pulled from it when required.

  • §

    Imports

    import { constructors } from '../core/library.js';
    
    import { correctForZero, doCreate, isa_number, isa_quaternion, xt, xto, Ωempty } from '../helper/utilities.js';
    
    import { makeVector } from './vector.js';
  • §

    Shared constants

    import { _cos, _radian, _sin, _sqrt, T_QUATERNION } from '../helper/shared-vars.js';
  • §

    Local constants

    const _acos = Math.acos;
  • §

    Quaternion constructor

    const Quaternion = function (items = Ωempty) {
    
        this.n = isa_number(items.n) ? items.n : 1;
        this.v = makeVector();
    
        this.set(items);
    
        return this;
    };
  • §

    Quaternion prototype

    const P = Quaternion.prototype = doCreate();
    P.type = T_QUATERNION;
  • §

    Mixins

    Quaternion objects do not use mixins - they are regular Javascript objects. As such, they do not possess packet, clone or kill functionality.

  • §

    Quaternion attributes

    The name, n and v (Vector object) attributes are set in the Quaternion constructor - the Quaternion object does not have a defs object. The Vector attributes v.x, v.y, v.z can also be set directly.

    We strongly advise against setting Quaternion object attributes directly! Quaternions are complex beasts and their n and v attributes are linked in mysterious (and possibly debauched) ways. Set and manipulate your Quaternion object’s attributes using the functions below.

  • §

    Get, Set, deltaSet

  • §

    Quaternion attributes can be retrieved directly - myquaternion.n, myquaternion.v. The v attribute is a Vector object, thus its attributes can be retrieved using that object’s convenience functions myquaternion.v.getXYCoordinate and myquaternion.v.getXYZCoordinate - these functions will return Arrays.

  • §

    The Quaternion set function is overloaded. It can accept the following arguments:

    • set(Quaternion) - a Quaternion object
    • set(Vector) - a Vector object
    • set({x:Number, y:Number, z:Number}) - a Javascript object containing one or more attributes from: x, y, z - with values in degrees (not radians)
    • set({pitch:Number, yaw:Number, roll:Number}) - a Javascript object containing one or more attributes: pitch (for x-axis rotations); yaw (for y-axis rotations); roll (for z-axis rotations) - with values in degrees (not radians)
    P.set = function (obj = Ωempty) {
    
        if (isa_quaternion(obj)) return this.setFromQuaternion(obj);
    
        if (xto(obj.pitch, obj.yaw, obj.roll) || xto(obj.x, obj.y, obj.z)) return this.setFromEuler(obj);
    
        const tv = this.v;
        const hasVec = xt(obj.vector) || xt(obj.v);
        const v = hasVec ? (obj.vector || obj.v) : Ωempty;
  • §

    scalar (n)

        if (xto(obj.scalar, obj.n)) {
    
            const nval = xt(obj.scalar) ? obj.scalar : obj.n;
            if (isa_number(nval)) this.n = nval;
        }
  • §

    vector components (set only if provided; zero is valid)

        if (xto(v.x, obj.x)) {
    
            const x = hasVec ? v.x : obj.x;
            if (isa_number(x)) tv.x = x;
        }
    
        if (xto(v.y, obj.y)) {
    
            const y = hasVec ? v.y : obj.y;
            if (isa_number(y)) tv.y = y;
        }
    
        if (xto(v.z, obj.z)) {
    
            const z = hasVec ? v.z : obj.z;
            if (isa_number(z)) tv.z = z;
        }
    
        return this;
    };
    
    P.setFromQuaternion = function (item) {
    
        if (isa_quaternion(item)) {
    
            const tv = this.v,
                iv = item.v;
    
            this.n = item.n;
            tv.x = iv.x;
            tv.y = iv.y;
            tv.z = iv.z;
        }
        return this;
    };
    
    P.setFromEuler = function (items = Ωempty) {
    
        const tv = this.v;
    
        const pitch = (items.pitch || items.x || 0) * _radian,
            yaw = (items.yaw || items.y || 0) * _radian,
            roll = (items.roll || items.z || 0) * _radian;
    
        const c1 = _cos( pitch / 2 ),
            c2 = _cos( yaw / 2 ),
            c3 = _cos( roll / 2 );
    
        const s1 = _sin( pitch / 2 ),
            s2 = _sin( yaw / 2 ),
            s3 = _sin( roll / 2 );
  • §

    Standard intrinsic XYZ (pitch = x, yaw = y, roll = z)

        tv.x =  s1 * c2 * c3 + c1 * s2 * s3;
        tv.y =  c1 * s2 * c3 - s1 * c2 * s3;
        tv.z =  c1 * c2 * s3 + s1 * s2 * c3;
    
        this.n = c1 * c2 * c3 - s1 * s2 * s3;
    
        return this.normalize();
    };
  • §

    Prototype functions

  • §

    Set the Quaternion attributes to their default values

    P.zero = function () {
    
        const v = this.v;
    
        this.n = 1;
        v.x = 0;
        v.y = 0;
        v.z = 0;
    
        return this;
    };
  • §

    Get the Quaternion’s magnitude value

    P.getMagnitude = function () {
    
        const v = this.v;
    
        return _sqrt((this.n * this.n) + (v.x * v.x) + (v.y * v.y) + (v.z * v.z));
    };
  • §

    Normalize the Quaternion

    P.normalize = function () {
    
        const mag = this.getMagnitude(),
            v = this.v;
    
        if (mag) {
    
            this.n = correctForZero(this.n / mag);
            v.x = correctForZero(v.x / mag);
            v.y = correctForZero(v.y / mag);
            v.z = correctForZero(v.z / mag);
        }
        return this;
    };
  • §

    Multiply the Quaternion by another Quaternion

    P.quaternionMultiply = function (item) {
    
        if (isa_quaternion(item)) {
    
            const tv = this.v,
                iv = item.v,
    
                n1 = this.n,
                x1 = tv.x,
                y1 = tv.y,
                z1 = tv.z,
    
                n2 = item.n,
                x2 = iv.x,
                y2 = iv.y,
                z2 = iv.z;
    
            this.n = (n1 * n2) - (x1 * x2) - (y1 * y2) - (z1 * z2);
    
            tv.x = (n1 * x2) + (x1 * n2) + (y1 * z2) - (z1 * y2);
            tv.y = (n1 * y2) + (y1 * n2) + (z1 * x2) - (x1 * z2);
            tv.z = (n1 * z2) + (z1 * n2) + (x1 * y2) - (y1 * x2);
        }
        return this;
    };
  • §

    Retrieve the Quaternion’s current angle. Returns radians by default.

    • Pass true to get degrees.
    P.getAngle = function (degree) {
  • §

    Clamp to handle tiny FP drift; normalize not required but helps.

        const w = Math.min(1, Math.max(-1, this.n));
    
        let result = 2 * _acos(w);
    
        const asDegrees = degree === true;
    
        if (asDegrees) result *= (1 / _radian);
    
        return correctForZero(result);
    };
  • §

    Rotate the Quaternion using another Quaternion’s values

    P.quaternionRotate = function (item) {
    
        if (isa_quaternion(item)) {
    
            const q4 = requestQuaternion(item),
                q5 = requestQuaternion(this);
    
            this.setFromQuaternion(q4.quaternionMultiply(q5));
    
            releaseQuaternion(q4, q5);
    
            this.normalize();
        }
        return this
    };
  • §

    Quaternion pool

    An attempt to reuse quaternions rather than constantly creating and deleting them

    const quaternionPool = [];
  • §

    exported function - retrieve a Quaternion from the quaternion pool

    export const requestQuaternion = function (items) {
    
        if (!quaternionPool.length) quaternionPool.push(makeQuaternion());
    
        const q = quaternionPool.pop();
    
        q.zero().set(items);
    
        return q
    };
  • §

    exported function - return a Quaternion to the quaternion pool. Failing to return Quaternion to the pool may lead to more inefficient code and possible memory leaks.

    export const releaseQuaternion = function (...args) {
    
        args.forEach(q => {
    
            if (q && q.type === T_QUATERNION) quaternionPool.push(q);
        });
    };
  • §

    Factory

    export const makeQuaternion = function (items) {
    
        return new Quaternion(items);
    };
    
    constructors.Quaternion = Quaternion;