Repository: vue-gl/vue-gl
Pull request #659 (2019-10-01 10:36:12)

vgl-extrude-geometry feature
### Purpose
This PR is made for providing full feature of THREE.ExtrudeBufferGeometry to Vue-GL

It contains:
- Unit Tests
- Documentations
- Reactive props for both Shapes and Options args

### Misc
I also added the possibility to parse an Array of Number ([12, 2]) to a THREE.Vector2 by adding a specific case to the parser function.



Patch:
diff --git a/docs/property-types.md b/docs/property-types.md
index 2e74580..dfb8106 100644
--- a/docs/property-types.md
+++ b/docs/property-types.md
@@ -71,3 +71,8 @@ Following list shows all property types and parsing schemas.
   Space-separated paramaters as color, near and far, corresponding [THREE.Fog](https://threejs.org/docs/#api/en/scenes/Fog)
 - ***names***
   A string or an array of string.
+- ***shapes***
+  An array of vector2s or an array of
+  [THREE.Shape](https://threejs.org/docs/#api/en/extras/core/Shape),
+  corresponding to
+  [THREE.ExtrudeBufferGeometry](https://threejs.org/docs/#api/en/geometries/ExtrudeBufferGeometry)
diff --git a/src/geometries/vgl-extrude-geometry.js b/src/geometries/vgl-extrude-geometry.js
index 7fe769c..24106b1 100644
--- a/src/geometries/vgl-extrude-geometry.js
+++ b/src/geometries/vgl-extrude-geometry.js
@@ -1,5 +1,7 @@
 import { ExtrudeBufferGeometry } from 'three';
+import { boolean, number, shapes } from '../validators';
 import VglGeometry from '../core/vgl-geometry';
+import { parseShape } from '../parsers';
 
 /**
  * A component for creating extruded geometry from a path shape,
@@ -10,7 +12,69 @@ import VglGeometry from '../core/vgl-geometry';
 
 export default {
   mixins: [VglGeometry],
+  props: {
+    /** The Shape (or an Array of Shape)
+     * Accepts an array of points to internally create the shape.
+     * Points can either be String ("12 2"), Number ([12, 2]) or THREE.Vector2.
+     *
+     * For an array of shapes, please pass directly an Array of THREE.Shape
+     */
+    shapes,
+    /** int. Number of points on the curves */
+    curveSegments: number,
+    /** int. Number of points used for subdividing segments
+     * along the depth of the extruded spline
+     */
+    steps: number,
+    /** float. Depth to extrude the shape */
+    depth: number,
+    /** Apply beveling to the shape */
+    bevelEnabled: boolean,
+    /** float. How deep into the original shape the bevel goes */
+    bevelThickness: number,
+    /** float. Distance from the shape outline that the bevel extends */
+    bevelSize: number,
+    /** float. Distance from the shape outline that the bevel starts */
+    bevelOffset: number,
+    /** int. Number of bevel layers */
+    bevelSegments: number,
+    /** THREE.Curve. A 3D spline path along which the shape should be extruded */
+    extrudePath: Object,
+    /**  Object that provides UV generator functions */
+    UVGenerator: Object,
+  },
   computed: {
-    inst() { return new ExtrudeBufferGeometry([], {}); },
+    options() {
+      const {
+        curveSegments,
+        steps,
+        depth,
+        bevelEnabled,
+        bevelThickness,
+        bevelSize,
+        bevelOffset,
+        bevelSegments,
+        extrudePath,
+        UVGenerator,
+      } = this;
+      return {
+        ...(curveSegments != null && { curveSegments: parseInt(curveSegments, 10) }),
+        ...(steps != null && { steps: parseInt(steps, 10) }),
+        ...(depth != null && { depth: parseFloat(depth) }),
+        ...(bevelEnabled != null && { bevelEnabled }),
+        ...(bevelThickness != null && { bevelThickness: parseFloat(bevelThickness) }),
+        ...(bevelSize != null && { bevelSize: parseFloat(bevelSize) }),
+        ...(bevelOffset != null && { bevelOffset: parseFloat(bevelOffset) }),
+        ...(bevelSegments != null && { bevelSegments: parseInt(bevelSegments, 10) }),
+        ...(extrudePath != null && { extrudePath }),
+        ...(UVGenerator != null && { UVGenerator }),
+      };
+    },
+    inst() {
+      return new ExtrudeBufferGeometry(
+        parseShape(this.shapes),
+        this.options,
+      );
+    },
   },
 };
diff --git a/src/parsers.js b/src/parsers.js
index 2d5748d..efee9fb 100644
--- a/src/parsers.js
+++ b/src/parsers.js
@@ -6,6 +6,7 @@ import {
   Quaternion,
   Fog,
   Color,
+  Shape,
 } from 'three';
 
 /**
@@ -26,7 +27,11 @@ export function parseVector3(str) {
  * Returns a parsed vector2 object.
  */
 export function parseVector2(str) {
-  return str.isVector2 ? str : new Vector2(...str.trim().split(/\s+/).map((elm) => parseFloat(elm)));
+  if (str.isVector2) return str;
+  if (Array.isArray(str)) {
+    return new Vector2(...(str.map((e) => parseFloat(e))));
+  }
+  return new Vector2(...str.trim().split(/\s+/).map((elm) => parseFloat(elm)));
 }
 
 /**
@@ -70,3 +75,11 @@ export function parseFog(str) {
 export function parseColor(str) {
   return str.isColor ? str : new Color(str);
 }
+
+/**
+ * Return a parsed Shape
+ */
+export function parseShape(str) {
+  if (!str) return new Shape();
+  return (str.type && str.type === 'Shape') ? str : new Shape(parseVector2Array(str));
+}
diff --git a/src/validators.js b/src/validators.js
index cd51a00..9657b4f 100644
--- a/src/validators.js
+++ b/src/validators.js
@@ -4,7 +4,7 @@ import {
   Euler,
   Vector2,
   Quaternion,
-  Fog,
+  Fog, Shape,
 } from 'three';
 
 export const number = [String, Number];
@@ -19,3 +19,4 @@ export const floatArray = [String, Array];
 export const vector2Array = [String, Array];
 export const fog = [String, Fog];
 export const names = [String, Array];
+export const shapes = [Array, Shape];


Test patch:
diff --git a/test/geometries/vgl-extrude-geometry.spec.js b/test/geometries/vgl-extrude-geometry.spec.js
index 241c87e..af98e7b 100644
--- a/test/geometries/vgl-extrude-geometry.spec.js
+++ b/test/geometries/vgl-extrude-geometry.spec.js
@@ -1,5 +1,5 @@
 import Vue from 'vue/dist/vue';
-import { ExtrudeBufferGeometry } from 'three';
+import { ExtrudeBufferGeometry, Shape, Vector2 } from 'three';
 import { VglExtrudeGeometry, VglGeometry, VglNamespace } from '../../src';
 
 describe('VglExtrudeGeometry', () => {
@@ -17,4 +17,98 @@ describe('VglExtrudeGeometry', () => {
     expect(Object.keys(new (Vue.extend(VglExtrudeGeometry))({ inject }).$props))
       .toEqual(expect.arrayContaining(Object.keys($props)));
   });
+  test('the options of the instance should be specified by props', () => {
+    const { inst } = new (Vue.extend(VglExtrudeGeometry))({
+      inject,
+      propsData: {
+        curveSegments: '12',
+        steps: '1',
+        depth: '100',
+        bevelEnabled: true,
+        bevelThickness: '6',
+        bevelSize: '4',
+        bevelOffset: '0',
+        bevelSegments: '3',
+      },
+    });
+    const {
+      curveSegments,
+      steps,
+      depth,
+      bevelEnabled,
+      bevelThickness,
+      bevelSize,
+      bevelOffset,
+      bevelSegments,
+    } = inst.parameters.options;
+    expect(curveSegments).toStrictEqual(12);
+    expect(steps).toStrictEqual(1);
+    expect(depth).toStrictEqual(100);
+    expect(bevelEnabled).toStrictEqual(true);
+    expect(bevelThickness).toStrictEqual(6);
+    expect(bevelSize).toStrictEqual(4);
+    expect(bevelOffset).toStrictEqual(0);
+    expect(bevelSegments).toStrictEqual(3);
+  });
+  test('the shape of the instance should be specified by props', () => {
+    const points = [[350, 0], [350, 1181], [0, 1181], [0, 79], [15, 79], [15, 0]];
+    const virtualShape = new Shape(points.map((e) => new Vector2(...e)));
+    const { inst } = new (Vue.extend(VglExtrudeGeometry))({
+      inject,
+      propsData: {
+        shapes: points,
+      },
+    });
+    const { shapes } = inst.parameters;
+    expect(shapes.type).toStrictEqual('Shape');
+    expect(virtualShape.currentPoint.equals(shapes.currentPoint)).toStrictEqual(true);
+    expect(JSON.stringify(shapes.extractPoints()))
+      .toStrictEqual(JSON.stringify(virtualShape.extractPoints()));
+  });
+  test('the shapes props as an Array of string', () => {
+    const points = [[350, 0], [350, 1181], [0, 1181], [0, 79], [15, 79], [15, 0]];
+    const virtualShape = new Shape(points.map((e) => new Vector2(...e)));
+    const { inst } = new (Vue.extend(VglExtrudeGeometry))({
+      inject,
+      propsData: {
+        shapes: points.map((e) => e.join(' ')),
+      },
+    });
+    const { shapes } = inst.parameters;
+    expect(shapes.type).toStrictEqual('Shape');
+    expect(virtualShape.currentPoint.equals(shapes.currentPoint)).toStrictEqual(true);
+    expect(JSON.stringify(shapes.extractPoints()))
+      .toStrictEqual(JSON.stringify(virtualShape.extractPoints()));
+  });
+  test('the shapes props as an Array of Vector2', () => {
+    const points = [[350, 0], [350, 1181], [0, 1181], [0, 79], [15, 79], [15, 0]];
+    const virtualShape = new Shape(points.map((e) => new Vector2(...e)));
+    const { inst } = new (Vue.extend(VglExtrudeGeometry))({
+      inject,
+      propsData: {
+        shapes: points.map((e) => new Vector2(...e)),
+      },
+    });
+    const { shapes } = inst.parameters;
+    expect(shapes.type).toStrictEqual('Shape');
+    expect(virtualShape.currentPoint.equals(shapes.currentPoint)).toStrictEqual(true);
+    expect(JSON.stringify(shapes.extractPoints()))
+      .toStrictEqual(JSON.stringify(virtualShape.extractPoints()));
+  });
+  test('the shapes props as a THREE.Shape', () => {
+    const points = [[350, 0], [350, 1181], [0, 1181], [0, 79], [15, 79], [15, 0]];
+    const virtualShape = new Shape(points.map((e) => new Vector2(...e)));
+    const propShapes = virtualShape.clone();
+    const { inst } = new (Vue.extend(VglExtrudeGeometry))({
+      inject,
+      propsData: {
+        shapes: propShapes,
+      },
+    });
+    const { shapes } = inst.parameters;
+    expect(shapes.type).toStrictEqual('Shape');
+    expect(virtualShape.currentPoint.equals(shapes.currentPoint)).toStrictEqual(true);
+    expect(JSON.stringify(shapes.extractPoints()))
+      .toStrictEqual(JSON.stringify(virtualShape.extractPoints()));
+  });
 });