Repository: vue-gl/vue-gl
Pull request #439 (2019-02-18 15:35:02)

Toggle visibility + Quaternion Support
I have added:
* The possibility to toggle visibility of objects. This should be a more efficient way to en/disable rendering of objects, than v-if, since it does not recreate the objects.
* Support for rotation quaternions to specify rotation of objects.


Patch:
diff --git a/src/core/vgl-object3d.js b/src/core/vgl-object3d.js
index c6d06c5..e42031e 100644
--- a/src/core/vgl-object3d.js
+++ b/src/core/vgl-object3d.js
@@ -1,8 +1,9 @@
 import { Object3D } from 'three';
-import { parseVector3, parseEuler } from '../parsers';
+import { parseVector3, parseEuler, parseQuaternion } from '../parsers';
 import {
   vector3,
   euler,
+  quaternion,
   boolean,
   string,
 } from '../validators';
@@ -22,6 +23,12 @@ export default {
     position: vector3,
     /** The object's local rotation as a euler angle. */
     rotation: euler,
+    /**
+     * The object's local rotation as a quaternion (specified in x, y, z, w order).
+     * Do not use in conjunction with the rotation prop, since they both control the same property
+     * of the underlying THREE.Object3D object.
+     */
+    rotationQuaternion: quaternion,
     /** The object's local scale as a 3D vector. */
     scale: vector3,
     /** Whether the object gets rendered into shadow map. */
@@ -30,6 +37,11 @@ export default {
     receiveShadow: boolean,
     /** Optional name of the object. */
     name: string,
+    /** Whether the object is visible. */
+    visible: {
+      type: boolean,
+      default: true,
+    },
   },
   computed: {
     inst: () => new Object3D(),
@@ -57,10 +69,12 @@ export default {
         if (this.vglObject3d.inst) this.vglObject3d.inst.add(inst);
         if (this.position) inst.position.copy(parseVector3(this.position));
         if (this.rotation) inst.rotation.copy(parseEuler(this.rotation));
+        if (this.rotationQuaternion) inst.quaternion.copy(parseQuaternion(this.rotationQuaternion));
         if (this.scale) inst.scale.copy(parseVector3(this.scale));
         Object.assign(inst, {
           castShadow: this.castShadow,
           receiveShadow: this.receiveShadow,
+          visible: this.visible,
         });
         if (this.name !== undefined) this.vglNamespace.object3ds[this.name] = inst;
       },
@@ -69,6 +83,9 @@ export default {
     'vglObject3d.inst': function parentInst(inst) { inst.add(this.inst); },
     position(position) { this.inst.position.copy(parseVector3(position)); },
     rotation(rotation) { this.inst.rotation.copy(parseEuler(rotation)); },
+    rotationQuaternion(rotationQuaternion) {
+      this.inst.quaternion.copy(parseQuaternion(rotationQuaternion));
+    },
     scale(scale) { this.inst.scale.copy(parseVector3(scale)); },
     castShadow(castShadow) { this.inst.castShadow = castShadow; },
     receiveShadow(receiveShadow) { this.inst.receiveShadow = receiveShadow; },
@@ -77,6 +94,7 @@ export default {
       if (object3ds[oldName] === inst) delete object3ds[oldName];
       object3ds[name] = inst;
     },
+    visible(visible) { this.inst.visible = visible; },
   },
   render(h) { return this.$slots.default ? h('div', this.$slots.default) : undefined; },
 };
diff --git a/src/parsers.js b/src/parsers.js
index 6c63404..e81254b 100644
--- a/src/parsers.js
+++ b/src/parsers.js
@@ -3,8 +3,16 @@ import {
   Vector3,
   Euler,
   Spherical,
+  Quaternion,
 } from 'three';
 
+/**
+ * Returns a parsed quaternion object.
+ */
+export function parseQuaternion(str) {
+  return str.isQuaternion ? str : new Quaternion(...str.trim().split(/\s+/).map(elm => parseFloat(elm)));
+}
+
 /**
  * Returns a parsed vector3 object.
  */
diff --git a/src/validators.js b/src/validators.js
index 950e935..cd5cccd 100644
--- a/src/validators.js
+++ b/src/validators.js
@@ -3,6 +3,7 @@ import {
   Spherical,
   Euler,
   Vector2,
+  Quaternion,
 } from 'three';
 
 export const number = [String, Number];
@@ -12,5 +13,6 @@ export const spherical = [String, Spherical];
 export const boolean = Boolean;
 export const euler = [String, Euler];
 export const vector2 = [String, Vector2];
+export const quaternion = [String, Quaternion];
 export const floatArray = [String, Array];
 export const vector2Array = [String, Array];


Test patch:
diff --git a/test/core/vgl-object3d.spec.js b/test/core/vgl-object3d.spec.js
index 035bdf7..523122a 100644
--- a/test/core/vgl-object3d.spec.js
+++ b/test/core/vgl-object3d.spec.js
@@ -35,6 +35,22 @@ describe('VglObject3d', () => {
     expect(inst).toHaveProperty('castShadow', true);
     expect(inst).toHaveProperty('receiveShadow', true);
   });
+  test('quaternion property of the instance should correspond to rotationQuaternion prop', () => {
+    const { inst } = new (Vue.extend(VglObject3d))({
+      inject,
+      propsData: {
+        position: '8 3 -3.5',
+        rotationQuaternion: '0.671749 0.051361 0.124275 0.728472',
+      },
+    });
+    expect(inst.position).toHaveProperty('x', 8);
+    expect(inst.position).toHaveProperty('y', 3);
+    expect(inst.position).toHaveProperty('z', -3.5);
+    expect(inst.quaternion).toHaveProperty('x', 0.671749);
+    expect(inst.quaternion).toHaveProperty('y', 0.051361);
+    expect(inst.quaternion).toHaveProperty('z', 0.124275);
+    expect(inst.quaternion).toHaveProperty('w', 0.728472);
+  });
   test('the instance sould not be reinstantiated after props change', async () => {
     const vm = new (Vue.extend(VglObject3d))({ inject });
     const { inst } = vm;
@@ -67,11 +83,21 @@ describe('VglObject3d', () => {
     expect(vm.inst).toHaveProperty('castShadow', true);
     expect(vm.inst).toHaveProperty('receiveShadow', true);
   });
+  test('quaternion property of the instance should change after a change prop change', async () => {
+    const vm = new (Vue.extend(VglObject3d))({ inject });
+    vm.rotationQuaternion = '0.671749 0.051361 0.124275 0.728472';
+    await vm.$nextTick();
+    expect(vm.inst.quaternion).toHaveProperty('x', 0.671749);
+    expect(vm.inst.quaternion).toHaveProperty('y', 0.051361);
+    expect(vm.inst.quaternion).toHaveProperty('z', 0.124275);
+    expect(vm.inst.quaternion).toHaveProperty('w', 0.728472);
+  });
   test('the properties of the instance should be defaults without props', () => {
     const { inst } = new (Vue.extend(VglObject3d))({ inject });
     const {
       position,
       rotation,
+      quaternion,
       scale,
       castShadow,
       receiveShadow,
@@ -83,6 +109,10 @@ describe('VglObject3d', () => {
     expect(inst.rotation).toHaveProperty('y', rotation.y);
     expect(inst.rotation).toHaveProperty('z', rotation.z);
     expect(inst.rotation).toHaveProperty('order', rotation.order);
+    expect(inst.quaternion).toHaveProperty('x', quaternion.x);
+    expect(inst.quaternion).toHaveProperty('y', quaternion.y);
+    expect(inst.quaternion).toHaveProperty('z', quaternion.z);
+    expect(inst.quaternion).toHaveProperty('w', quaternion.w);
     expect(inst.scale).toHaveProperty('x', scale.x);
     expect(inst.scale).toHaveProperty('y', scale.y);
     expect(inst.scale).toHaveProperty('z', scale.z);