Obj3 & Transformation

Component Obj3 is used for THREE.Object3D definition, declared in component/obj3.js.

Obj3.transform

Note

Obj3.transform are parameters used only for creating THREE.Object3D object. Any further updating for animation are processed via Obj3.mesh properties.

Initial object transform. (TODO re-implement rotation animation using this.)

Supported transform properties include 5 affine transformation in the exactly following sequence:

translate, rotate, reflect, shear, scale

from Wikipedia [CC BY-SA 3.0]

Shear is defined as:

{x_y, x_z, y_x, y_z, z_x, z_y}

where x_y stands for shear x with y, a.k.a x is changed wile increasing y in scale x_y, a.k.a mat4[1, 2] = x_y, a.k.a x’ = m11 * x + x_y * y.

The 3D shear matrix is defined as:

m =
11   x_y  x_z  tx
y_x  22   y_z  ty
z_x  z_y  33   tz
41   42   43   w

Using Obj3.transform

Obj3.transform setup an Object3D’s initial transformation. X-vsual supports 3 type of transforamtion that Three.js also supports:

  • rotate

e.g.:

{rotate: {deg: 90, axis: [0, 1, 0]}}
  • translate

e.g.:

{translate: [20, 0, 0]}
  • scale

e.g.:

{scale: [1.2, 1, 2]}}

Transformations can be combined into an array of Obj3’s definition:

Obj3: {
    transfrom: [{translate: [100, 0, 0]},
                {rotate: {deg: 45, axis: [1, 0, 0]}},
                {scale: [1, 0.5, 1]}]
}

Obj3.affines & combined

This 2 properties are used to control object transformation, similar to the mesh. matrix4, but created by animizer and tweened by XTtweener, other than by Three.js.

Affines is an array of affine object, where affine can be:

translate: vec3
rotate: radian
scale: number

Reflect and Shear are not supported as THREE.Matrix4 seams can only been decomposed into rotation, translation and scale. For details about this, see Three.js Matrix4 Source Code and Object3D.

Here is the code snippet of Matrix4.decompose:

Object.assign( Matrix4.prototype, {
  decompose: function ( position, quaternion, scale ) {
    var te = this.elements;
    var sx = _v1.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length();
    var sy = _v1.set( te[ 4 ], te[ 5 ], te[ 6 ] ).length();
    var sz = _v1.set( te[ 8 ], te[ 9 ], te[ 10 ] ).length();

    // if determine is negative, we need to invert one scale
    var det = this.determinant();
    if ( det < 0 ) sx = - sx;

    position.x = te[ 12 ]; position.y = te[ 13 ]; position.z = te[ 14 ];

    // scale the rotation part
    _m1.copy( this );
    var invSX = 1 / sx; var invSY = 1 / sy; var invSZ = 1 / sz;
    _m1.elements[ 0 ] *= invSX; _m1.elements[ 1 ] *= invSX; _m1.elements[ 2 ] *= invSX;
    _m1.elements[ 4 ] *= invSY; _m1.elements[ 5 ] *= invSY; _m1.elements[ 6 ] *= invSY;
    _m1.elements[ 8 ] *= invSZ; _m1.elements[ 9 ] *= invSZ; _m1.elements[ 10 ] *= invSZ;

    quaternion.setFromRotationMatrix( _m1 );
    scale.x = sx; scale.y = sy; scale.z = sz;
    return this;
  },
}

and Object3D

Object3D.prototype = Object.assign( Object.create( EventDispatcher.prototype ), {
  constructor: Object3D,
  isObject3D: true,
  onBeforeRender: function () {},
  onAfterRender: function () {},

  applyMatrix4: function ( matrix ) {
    if ( this.matrixAutoUpdate ) this.updateMatrix();
    this.matrix.premultiply( matrix );
    this.matrix.decompose( this.position, this.quaternion, this.scale );
  },
}

Obj3.Combined is an array parsed and combined operation represented as a mat4.

Combined is the XTweener’s tweening target and been set to Obj3.mesh.matrix4 directly. Not using Object3D.applyMatrix() because the mesh matrix will accumulate ratation etc. at each updating & applying matrix, making rotation steps getting increased.

User shouldn’t modify affines and combined fields.

In addition to basic affine transformation, x-visual provide some combined transformation from these basic transformations, like orbiting and interpolated translating.

See Affine Combiner Design for more details.

Issue: Quaternion Injection

Chart/D3Pie.update() use matrix decompose() and compose() to force object facing screen. Could this breaching the design doctrine?

if (e.Pie && e.Pie.lookScreen) {
    var m = e.Obj3.mesh;
    m.matrix.decompose( m.position, m.quaternion, m.scale );
    m.quaternion.copy(this.camera.quaternion);
    m.matrix.compose( m.position, m.quaternion, m.scale );
    m.matrixAutoUpdate = false;
}

Obj3 & Geometry

Most of x-visual geometry handling depends on Three.js’ geometry buffer and it’s subclasses. See Thrender.threeGeometryCase().

There are also some extensions.

Obj3Type.MapXZRoad

Generate a road polygon in y = paras.y0 plane. The generated path is scaled and stored in Obj3.datum, in {paths, dirty}.

Some animation type like AnimType.U_PATHn_MORPH will take this as it’s own’s Obj3 datum.ref object and can tweening path without noticing paths’ data changed.

See the Geometry Tests & xgeom.generateWayxz() API.

Obj3Type.Hexatile

Generate a hexagonal tiles module from json similar with geojson, except that it’s recommended using EPSG 3857 coordinates. Coordinates in EPSG 4326 can be directly applied if showing a small area and geoScale is large enough.

See the test Case: Hexatile from Geojson & xgeom.hexaprism3857() API.

Memo: hexagon tile generation