diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f419a5f..6e19709d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,17 @@ CHANGELOG =========== +## 2.3.3 ( [see diff](https://github.com/toxicFork/react-three-renderer/compare/v2.3.2...v2.3.3) ) + +### Components +- Added `` ( #114 ) +- Fixed a bug which would cause a crash when you changed some properties on components + whose children used ref functions +- Added `` ( #105 #119 ) + +### Core +- Fixed bug which would not allow you to get a canvas reference through the `canvasRef` property in some conditions ( #115 ) + ## 2.3.2 ( [see diff](https://github.com/toxicFork/react-three-renderer/compare/v2.3.1...v2.3.2) ) ### Docs diff --git a/README.md b/README.md index 754ad4c5..2784056d 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,8 @@ Would you like to know more? [See the wiki](https://github.com/toxicFork/react-t > Currently supported react version: `15.3.1` ( things break fast when you fly this close to the sun ) +> Currently supported three version: `0.79.0` ( need to update babel config to work better with rollup ) + [![Join the chat at https://gitter.im/toxicFork/react-three-renderer](https://badges.gitter.im/toxicFork/react-three-renderer.svg)](https://gitter.im/toxicFork/react-three-renderer?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Build Status](https://travis-ci.org/toxicFork/react-three-renderer.svg)](https://travis-ci.org/toxicFork/react-three-renderer) [![npm](https://nodei.co/npm/react-three-renderer.svg?downloads=true)](https://nodei.co/npm/react-three-renderer/) @@ -19,7 +21,7 @@ Installation ============ ``` -npm install --save react react-dom three +npm install --save react@15.3.1 react-dom@15.3.1 three@0.79.0 npm install --save react-three-renderer ``` diff --git a/docs/src/Generator.js b/docs/src/Generator.js index 24e76fd7..470d0793 100644 --- a/docs/src/Generator.js +++ b/docs/src/Generator.js @@ -66,7 +66,7 @@ function mockPropTypes() { constructor(type, isRequired = false) { this._type = type; - this._isRequired = !!isRequired; + this._isRequired = isRequired; if (!isRequired) { this.isRequired = new PropType(type, true); } diff --git a/docs/src/internalComponentCategories.js b/docs/src/internalComponentCategories.js index 2aa0c162..c9612414 100644 --- a/docs/src/internalComponentCategories.js +++ b/docs/src/internalComponentCategories.js @@ -181,6 +181,9 @@ module.exports = { geometry: { isComponent: true, }, + bufferGeometry: { + isComponent: true, + }, boxGeometry: { isComponent: true, }, @@ -241,10 +244,12 @@ module.exports = { textGeometry: { isComponent: true, }, + shapeGeometry: { + isComponent: true, + }, }, TODO: [ 'CubeGeometry', // BoxGeometry - 'ShapeGeometry', // hmmm ], }, Shapes: { diff --git a/docs/src/internalComponents/bufferGeometry.js b/docs/src/internalComponents/bufferGeometry.js new file mode 100644 index 00000000..2a8a8050 --- /dev/null +++ b/docs/src/internalComponents/bufferGeometry.js @@ -0,0 +1,24 @@ +import DocInfo from '../DocInfo'; + +class bufferGeometry extends DocInfo { + getIntro() { + return 'Creates a [THREE.BufferGeometry](http://threejs.org/docs/#Reference/Core/BufferGeometry)'; + } + + getDescription() { + return ''; + } + + getAttributesText() { + return { + name: '', + resourceId: '', + position: '', + normal: '', + color: '', + index: '', + }; + } +} + +module.exports = bufferGeometry; diff --git a/docs/src/internalComponents/gridHelper.js b/docs/src/internalComponents/gridHelper.js index 92e46e41..f1d9e399 100644 --- a/docs/src/internalComponents/gridHelper.js +++ b/docs/src/internalComponents/gridHelper.js @@ -1,8 +1,8 @@ import object3D from './object3D'; class gridHelper extends object3D { - getDescription() { - return 'Creates a [THREE.AxisHelper](https://threejs.org/docs/index.html#Reference/Extras.Helpers/GridHelper)'; + getIntro() { + return 'Creates a [THREE.GridHelper](https://threejs.org/docs/index.html#Reference/Extras.Helpers/GridHelper)'; } getAttributesText() { diff --git a/docs/src/internalComponents/shape.js b/docs/src/internalComponents/shape.js index 4098003f..214eed52 100644 --- a/docs/src/internalComponents/shape.js +++ b/docs/src/internalComponents/shape.js @@ -6,8 +6,9 @@ class shape extends DocInfo { } getDescription() { - return `Place this within [[<extrudeGeometry>|extrudeGeometry]] - or [<resources>](resources).`; + return `Place this within [[<extrudeGeometry>|extrudeGeometry]], + [[<shapeGeometry>|shapeGeometry]], + or [[<resources>|resources]].`; } getAttributesText() { diff --git a/docs/src/internalComponents/shapeGeometry.js b/docs/src/internalComponents/shapeGeometry.js new file mode 100644 index 00000000..9e4ec494 --- /dev/null +++ b/docs/src/internalComponents/shapeGeometry.js @@ -0,0 +1,20 @@ +import geometry from './geometry'; + +class shapeGeometry extends geometry { + getIntro() { + return 'Creates a [THREE.ShapeGeometry](https://threejs.org/docs/#Reference/Geometries/ShapeGeometry)'; + } + + getAttributesText() { + return { + ...super.getAttributesText(), + + shapes: 'Array of shapes, or a single shape THREE.Shape', + curveSegments: 'Default is 12 (not used in three.js at the moment)', + material: 'Index of the material in a material list', + UVGenerator: 'A UV generator, defaults to ExtrudeGeometry\'s WorldUVGenerator', + }; + } +} + +module.exports = shapeGeometry; diff --git a/src/lib/ElementDescriptorContainer.js b/src/lib/ElementDescriptorContainer.js index 27d3d4c4..4db39145 100644 --- a/src/lib/ElementDescriptorContainer.js +++ b/src/lib/ElementDescriptorContainer.js @@ -31,6 +31,7 @@ import TextureResourceDescriptor from './descriptors/Resource/TextureResourceDes import ShapeResourceDescriptor from './descriptors/Resource/ShapeResourceDescriptor'; import GeometryDescriptor from './descriptors/Geometry/GeometryDescriptor'; +import BufferGeometryDescriptor from './descriptors/Geometry/BufferGeometryDescriptor'; import BoxGeometryDescriptor from './descriptors/Geometry/BoxGeometryDescriptor'; import SphereGeometryDescriptor from './descriptors/Geometry/SphereGeometryDescriptor'; import ParametricGeometryDescriptor from './descriptors/Geometry/ParametricGeometryDescriptor'; @@ -51,6 +52,7 @@ import ExtrudeGeometryDescriptor from './descriptors/Geometry/ExtrudeGeometryDes import TubeGeometryDescriptor from './descriptors/Geometry/TubeGeometryDescriptor'; import DodecahedronGeometryDescriptor from './descriptors/Geometry/DodecahedronGeometryDescriptor'; import TextGeometryDescriptor from './descriptors/Geometry/TextGeometryDescriptor'; +import ShapeGeometryDescriptor from './descriptors/Geometry/ShapeGeometryDescriptor'; import ShapeDescriptor from './descriptors/Geometry/Shapes/ShapeDescriptor'; import MoveToDescriptor from './descriptors/Geometry/Shapes/MoveToDescriptor'; @@ -126,6 +128,7 @@ class ElementDescriptorContainer { texture: new TextureDescriptor(react3RendererInstance), geometry: new GeometryDescriptor(react3RendererInstance), + bufferGeometry: new BufferGeometryDescriptor(react3RendererInstance), boxGeometry: new BoxGeometryDescriptor(react3RendererInstance), sphereGeometry: new SphereGeometryDescriptor(react3RendererInstance), parametricGeometry: new ParametricGeometryDescriptor(react3RendererInstance), @@ -146,6 +149,7 @@ class ElementDescriptorContainer { tubeGeometry: new TubeGeometryDescriptor(react3RendererInstance), dodecahedronGeometry: new DodecahedronGeometryDescriptor(react3RendererInstance), textGeometry: new TextGeometryDescriptor(react3RendererInstance), + shapeGeometry: new ShapeGeometryDescriptor(react3RendererInstance), shape: new ShapeDescriptor(react3RendererInstance), moveTo: new MoveToDescriptor(react3RendererInstance), diff --git a/src/lib/InternalComponent.js b/src/lib/InternalComponent.js index bb38c92a..46322bca 100644 --- a/src/lib/InternalComponent.js +++ b/src/lib/InternalComponent.js @@ -7,6 +7,10 @@ import emptyFunction from 'fbjs/lib/emptyFunction'; import flattenChildren from 'react/lib/flattenChildren'; import ReactCurrentOwner from 'react/lib/ReactCurrentOwner'; import ReactInstrumentation from 'react/lib/ReactInstrumentation'; + +import ReactElement from 'react/lib/ReactElement'; +import ReactRef from 'react/lib/ReactRef'; + import Flags from './React3ComponentFlags'; import ID_PROPERTY_NAME from './utils/idPropertyName'; @@ -574,6 +578,30 @@ class InternalComponent { this.threeElementDescriptor.completePropertyUpdates(this._threeObject); } + _removeAllChildRefs() { + const renderedChildren = this._renderedChildren; + + if (renderedChildren) { + const renderedChildrenKeys = Object.keys(renderedChildren); + + for (let i = 0; i < renderedChildrenKeys.length; ++i) { + const name = renderedChildrenKeys[i]; + + const renderedChild = renderedChildren[name]; + + if (renderedChild && renderedChild._currentElement && renderedChild._currentElement.ref) { + ReactRef.detachRefs(renderedChild, renderedChild._currentElement); + + renderedChild._currentElement = ReactElement.cloneElement(renderedChild._currentElement, { + ref: null, + }); + } + + renderedChild._removeAllChildRefs(); + } + } + } + /** * @see ReactDOMComponent.Mixin.unmountComponent */ @@ -582,6 +610,10 @@ class InternalComponent { this.threeElementDescriptor.componentWillUnmount(this._threeObject); } + if (this._forceRemountOfComponent) { + this._removeAllChildRefs(); // prevent attaching of refs to children + } + this.unmountChildren(safely); React3ComponentTree.uncacheMarkup(this); diff --git a/src/lib/React3.jsx b/src/lib/React3.jsx index 70bd08e2..29de50e3 100644 --- a/src/lib/React3.jsx +++ b/src/lib/React3.jsx @@ -66,6 +66,20 @@ class React3 extends React.Component { this._render(); } + componentWillReceiveProps(newProps) { + const lastProps = this.props; + + if (lastProps.canvasRef !== newProps.canvasRef) { + if (lastProps.canvasRef) { + lastProps.canvasRef(null); + } + + if (newProps.canvasRef) { + newProps.canvasRef(this._canvas); + } + } + } + shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate; componentDidUpdate() { diff --git a/src/lib/React3Instance.js b/src/lib/React3Instance.js index 0a2d033a..2fb963c1 100644 --- a/src/lib/React3Instance.js +++ b/src/lib/React3Instance.js @@ -785,13 +785,10 @@ class React3DInstance { } if (contextLossExtension && this._canvas) { - this._canvas.addEventListener('webglcontextlost', () => { - // this should recreate the canvas - this._recreateCanvasCallback(); - }, false); - // noinspection JSUnresolvedFunction contextLossExtension.loseContext(); + + this._recreateCanvasCallback(); } else { this._recreateCanvasCallback(); } diff --git a/src/lib/descriptors/Geometry/BufferGeometryDescriptor.js b/src/lib/descriptors/Geometry/BufferGeometryDescriptor.js new file mode 100644 index 00000000..dd638564 --- /dev/null +++ b/src/lib/descriptors/Geometry/BufferGeometryDescriptor.js @@ -0,0 +1,61 @@ +import THREE from 'three'; +import PropTypes from 'react/lib/ReactPropTypes'; + +import GeometryDescriptorBase from './GeometryDescriptorBase'; +import propTypeInstanceOf from '../../utils/propTypeInstanceOf'; + +class BufferGeometryDescriptor extends GeometryDescriptorBase { + constructor(react3RendererInstance) { + super(react3RendererInstance); + + [ + 'vertices', + 'colors', + 'faceVertexUvs', + 'faces', + 'dynamic', + ].forEach(propName => { + this.removeProp(propName); + }); + + [ + 'position', + 'normal', + 'color', + ].forEach(attributeName => { + this.hasProp(attributeName, { + type: PropTypes.oneOfType([ + propTypeInstanceOf(THREE.BufferAttribute), + propTypeInstanceOf(THREE.InterleavedBufferAttribute), + ]), + update(threeObject, attributeValue) { + if (attributeValue) { + threeObject.addAttribute(attributeName, attributeValue); + } else { + threeObject.removeAttribute(attributeName); + } + }, + updateInitial: true, + default: undefined, + }); + }); + + this.hasProp('index', { + type: PropTypes.oneOfType([ + propTypeInstanceOf(THREE.BufferAttribute), + propTypeInstanceOf(THREE.InterleavedBufferAttribute), + ]), + update(threeObject, attributeValue) { + threeObject.setIndex(attributeValue); + }, + updateInitial: true, + default: undefined, + }); + } + + construct() { + return new THREE.BufferGeometry(); + } +} + +module.exports = BufferGeometryDescriptor; diff --git a/src/lib/descriptors/Geometry/ExtrudeGeometryDescriptor.js b/src/lib/descriptors/Geometry/ExtrudeGeometryDescriptor.js index 298228c4..ac38ab89 100644 --- a/src/lib/descriptors/Geometry/ExtrudeGeometryDescriptor.js +++ b/src/lib/descriptors/Geometry/ExtrudeGeometryDescriptor.js @@ -1,30 +1,14 @@ import THREE from 'three'; import PropTypes from 'react/lib/ReactPropTypes'; -import invariant from 'fbjs/lib/invariant'; -import GeometryDescriptorBase from './GeometryDescriptorBase'; -import ShapeResourceReference from '../../Resources/ShapeResourceReference'; +import GeometryWithShapesDescriptor from './GeometryWithShapesDescriptor'; import propTypeInstanceOf from '../../utils/propTypeInstanceOf'; -class ExtrudeGeometryDescriptor extends GeometryDescriptorBase { +class ExtrudeGeometryDescriptor extends GeometryWithShapesDescriptor { constructor(react3RendererInstance) { super(react3RendererInstance); - this.hasProp('shapes', { - type: PropTypes.arrayOf(propTypeInstanceOf(THREE.Shape)), - updateInitial: true, - update: (threeObject, shapes) => { - threeObject.userData._shapesFromProps = shapes || []; - - // if the root instance exists, then it can be refreshed - if (threeObject.userData._rootInstance) { - this._refreshGeometry(threeObject); - } - }, - default: [], - }); - this.hasProp('settings', { type: PropTypes.any, update(threeObject, settings) { @@ -35,19 +19,15 @@ class ExtrudeGeometryDescriptor extends GeometryDescriptorBase { }); [ + 'steps', 'amount', 'bevelThickness', 'bevelSize', 'bevelSegments', - 'bevelEnabled', - 'curveSegments', - 'steps', - 'extrudePath', - 'UVGenerator', - 'frames', + 'extrudeMaterial', ].forEach(propName => { this.hasProp(propName, { - type: PropTypes.any, + type: PropTypes.number, update: (threeObject, value) => { if (value === undefined) { delete threeObject.userData._options[propName]; @@ -55,53 +35,43 @@ class ExtrudeGeometryDescriptor extends GeometryDescriptorBase { threeObject.userData._options[propName] = value; } - this._refreshGeometry(threeObject); + threeObject.userData._needsToRefreshGeometry = true; }, default: undefined, }); }); - } - - construct() { - return new THREE.BufferGeometry(); - } - - applyInitialProps(threeObject, props) { - super.applyInitialProps(threeObject, props); - - const options = {}; - [ - 'amount', - 'bevelThickness', - 'bevelSize', - 'bevelSegments', + const extraNames = [ 'bevelEnabled', - 'curveSegments', - 'steps', 'extrudePath', - 'UVGenerator', 'frames', - ].forEach(propName => { - if (props.hasOwnProperty(propName)) { - options[propName] = props[propName]; - } - }); + ]; - threeObject.userData._shapeCache = []; - threeObject.userData._options = options; - threeObject.userData._resourceListenerCleanupFunctions = []; - - this._refreshGeometry(threeObject); - } + const extraTypes = [ + PropTypes.bool, // bevelEnabled + propTypeInstanceOf(THREE.CurvePath), // extrudePath + propTypeInstanceOf(THREE.TubeGeometry.FrenetFrames), // frames + ]; - _onShapeResourceUpdate(threeObject, shapeIndex, shape) { - threeObject.userData._shapeCache[shapeIndex] = shape; + extraNames.forEach((propName, i) => { + this.hasProp(propName, { + type: extraTypes[i], + update: (threeObject, value) => { + if (value === undefined) { + delete threeObject.userData._options[propName]; + } else { + threeObject.userData._options[propName] = value; + } - this._refreshGeometry(threeObject); + threeObject.userData._needsToRefreshGeometry = true; + }, + default: undefined, + }); + }); } - _refreshGeometry(threeObject) { + // noinspection JSMethodCanBeStatic + refreshGeometry(threeObject) { const shapes = threeObject.userData._shapeCache.filter(shape => !!shape) .concat(threeObject.userData._shapesFromProps); @@ -111,85 +81,26 @@ class ExtrudeGeometryDescriptor extends GeometryDescriptorBase { })); } - addChildren(threeObject, children) { - // TODO: add shapes here! - - if (process.env.NODE_ENV !== 'production') { - invariant(children.filter(this._invalidChild).length === 0, 'Extrude geometry children' + - ' can only be shapes!'); - } else { - invariant(children.filter(this._invalidChild).length === 0, false); - } - - const shapeCache = []; - - children.forEach(child => { - if (child instanceof ShapeResourceReference) { - const shapeIndex = shapeCache.length; - - const resourceListener = this._onShapeResourceUpdate.bind(this, threeObject, shapeIndex); - - resourceListener.target = child; - - const cleanupFunction = () => { - child.userData.events.removeListener('resource.set', resourceListener); - - threeObject.userData._resourceListenerCleanupFunctions - .splice(threeObject.userData - ._resourceListenerCleanupFunctions.indexOf(cleanupFunction), 1); - }; - - threeObject.userData._resourceListenerCleanupFunctions.push(cleanupFunction); + getOptions(props) { + const options = super.getOptions(props); - child.userData.events.on('resource.set', resourceListener); - child.userData.events.once('dispose', () => { - cleanupFunction(); - }); - - shapeCache.push(null); - } else { - shapeCache.push(child); + [ + 'steps', + 'amount', + 'bevelEnabled', + 'bevelThickness', + 'bevelSize', + 'bevelSegments', + 'extrudePath', + 'frames', + 'extrudeMaterial', + ].forEach(propName => { + if (props.hasOwnProperty(propName)) { + options[propName] = props[propName]; } }); - threeObject.userData._shapeCache = shapeCache; - - this._refreshGeometry(threeObject); - } - - addChild(threeObject) { - // new shape was added - // TODO optimize - - this.triggerRemount(threeObject); - } - - removeChild(threeObject) { - // shape was removed - // TODO optimize - - this.triggerRemount(threeObject); - } - - _invalidChild = child => { - const invalid = !( - child instanceof THREE.Shape || - child instanceof ShapeResourceReference - ); - - return invalid; - }; - - unmount(geometry) { - geometry.userData._resourceListenerCleanupFunctions.forEach(listener => { - listener(); - }); - - delete geometry.userData._resourceListenerCleanupFunctions; - delete geometry.userData._options; - delete geometry.userData._shapesFromProps; - - return super.unmount(geometry); + return options; } } diff --git a/src/lib/descriptors/Geometry/GeometryWithShapesDescriptor.js b/src/lib/descriptors/Geometry/GeometryWithShapesDescriptor.js new file mode 100644 index 00000000..72d1b22c --- /dev/null +++ b/src/lib/descriptors/Geometry/GeometryWithShapesDescriptor.js @@ -0,0 +1,186 @@ +import THREE from 'three'; + +import PropTypes from 'react/lib/ReactPropTypes'; +import invariant from 'fbjs/lib/invariant'; + +import GeometryDescriptorBase from './GeometryDescriptorBase'; +import ShapeResourceReference from '../../Resources/ShapeResourceReference'; +import propTypeInstanceOf from '../../utils/propTypeInstanceOf'; + +class GeometryWithShapesDescriptor extends GeometryDescriptorBase { + constructor(react3RendererInstance) { + super(react3RendererInstance); + + this.hasProp('shapes', { + type: PropTypes.arrayOf(propTypeInstanceOf(THREE.Shape)), + updateInitial: true, + update: (threeObject, shapes) => { + threeObject.userData._shapesFromProps = shapes || []; + + threeObject.userData._needsToRefreshGeometry = true; + }, + default: [], + }); + + const optionNames = [ + 'curveSegments', + 'material', + 'UVGenerator', + ]; + + const optionTypes = [ + PropTypes.number, + PropTypes.number, + PropTypes.shape({ + generateTopUV: PropTypes.func, + generateSideWallUV: PropTypes.func, + }), + ]; + + optionNames.forEach((propName, i) => { + this.hasProp(propName, { + type: optionTypes[i], + update: (threeObject, value) => { + if (value === undefined) { + delete threeObject.userData._options[propName]; + } else { + threeObject.userData._options[propName] = value; + } + + threeObject.userData._needsToRefreshGeometry = true; + }, + default: undefined, + }); + }); + } + + completePropertyUpdates(threeObject) { + if (threeObject.userData._needsToRefreshGeometry) { + this.refreshGeometry(threeObject); + + threeObject.userData._needsToRefreshGeometry = false; + } + } + + construct() { + return new THREE.BufferGeometry(); + } + + getOptions(props) { + const options = {}; + + [ + 'curveSegments', + 'material', + 'UVGenerator', + ].forEach(propName => { + if (props.hasOwnProperty(propName)) { + options[propName] = props[propName]; + } + }); + + return options; + } + + applyInitialProps(threeObject, props) { + super.applyInitialProps(threeObject, props); + + threeObject.userData._shapeCache = []; + threeObject.userData._options = this.getOptions(props); + threeObject.userData._resourceListenerCleanupFunctions = []; + threeObject.userData._needsToRefreshGeometry = false; + + if (!props.children) { + // will use shapes only from props + this.refreshGeometry(threeObject); + } + } + + addChildren(threeObject, children) { + if (process.env.NODE_ENV !== 'production') { + invariant(children.filter(this._invalidChild).length === 0, 'shape-based geometry children' + + ' can only be shapes!'); + } else { + invariant(children.filter(this._invalidChild).length === 0, false); + } + + const shapeCache = []; + + children.forEach(child => { + if (child instanceof ShapeResourceReference) { + const shapeIndex = shapeCache.length; + + const resourceListener = (shape) => { + threeObject.userData._shapeCache[shapeIndex] = shape; + + this.refreshGeometry(threeObject); + }; + + resourceListener.target = child; + + const cleanupFunction = () => { + child.userData.events.removeListener('resource.set', resourceListener); + + threeObject.userData._resourceListenerCleanupFunctions + .splice(threeObject.userData + ._resourceListenerCleanupFunctions.indexOf(cleanupFunction), 1); + }; + + threeObject.userData._resourceListenerCleanupFunctions.push(cleanupFunction); + + child.userData.events.on('resource.set', resourceListener); + child.userData.events.once('dispose', () => { + cleanupFunction(); + }); + + shapeCache.push(null); + } else { + shapeCache.push(child); + } + }); + + threeObject.userData._shapeCache = shapeCache; + + this.refreshGeometry(threeObject); + } + + addChild(threeObject) { + // new shape was added + // TODO optimize + + this.triggerRemount(threeObject); + } + + moveChild(threeObject) { + // a shape was moved + // TODO optimize + + this.triggerRemount(threeObject); + } + + removeChild(threeObject) { + // shape was removed + // TODO optimize + + this.triggerRemount(threeObject); + } + + _invalidChild = child => !( + child instanceof THREE.Shape || + child instanceof ShapeResourceReference + ); + + unmount(geometry) { + geometry.userData._resourceListenerCleanupFunctions.forEach(listener => { + listener(); + }); + + delete geometry.userData._options; + delete geometry.userData._resourceListenerCleanupFunctions; + delete geometry.userData._shapesFromProps; + + return super.unmount(geometry); + } +} + +module.exports = GeometryWithShapesDescriptor; diff --git a/src/lib/descriptors/Geometry/ShapeGeometryDescriptor.js b/src/lib/descriptors/Geometry/ShapeGeometryDescriptor.js new file mode 100644 index 00000000..bb127232 --- /dev/null +++ b/src/lib/descriptors/Geometry/ShapeGeometryDescriptor.js @@ -0,0 +1,17 @@ +import THREE from 'three'; + +import GeometryWithShapesDescriptor from './GeometryWithShapesDescriptor'; + +class ShapeGeometryDescriptor extends GeometryWithShapesDescriptor { + // noinspection JSMethodCanBeStatic + refreshGeometry(threeObject) { + const shapes = threeObject.userData._shapeCache.filter(shape => !!shape) + .concat(threeObject.userData._shapesFromProps); + + threeObject.fromGeometry(new THREE.ShapeGeometry(shapes, { + ...threeObject.userData._options, + })); + } +} + +module.exports = ShapeGeometryDescriptor; diff --git a/tests/src/core/React3/Updates.jsx b/tests/src/core/React3/Updates.jsx index 92aad733..b9d306d6 100644 --- a/tests/src/core/React3/Updates.jsx +++ b/tests/src/core/React3/Updates.jsx @@ -474,7 +474,8 @@ module.exports = type => { meshRef.getCall(0).args[0]); mockConsole.expectDev('Warning: updating prop ' + - 'onMouseEnter ( function onMouseEnter() {} ) for MeshDescriptor'); + `onMouseEnter ( ${(function onMouseEnter() { + }).toString()} ) for MeshDescriptor`); const meshRef2 = sinon.spy(); @@ -516,7 +517,8 @@ module.exports = type => { meshRef.lastCall.args[0]); mockConsole.expectDev('Warning: updating prop ' + - 'onMouseEnter ( function onMouseEnter() {} ) for MeshDescriptor'); + `onMouseEnter ( ${(function onMouseEnter() { + }).toString()} ) for MeshDescriptor`); }); it('updates state of components', () => { @@ -590,4 +592,86 @@ module.exports = type => { 'The state should have changed correct number of times' ).to.equal(9); }); + + it('Calls canvasRef when the props update', () => { + const canvasRef = sinon.spy(); + + ReactDOM.render(, testDiv); + + mockConsole.expectThreeLog(); + + const canvas = canvasRef.lastCall.args[0]; + expect(canvas).to.equal(testDiv.firstChild); + + const secondCanvasRef = sinon.spy(); + + ReactDOM.render(, testDiv); + + expect(canvasRef.callCount).to.equal(2); + expect(canvasRef.lastCall.args[0]).to.be.null(); // ref removed + + expect(secondCanvasRef.callCount).to.equal(1); + expect(secondCanvasRef.lastCall.args[0]).to.equal(testDiv.firstChild); // call on the newer one + }); + + it('Does not call canvasRef again when changing simple properties', () => { + const canvasRef = sinon.spy(); + + ReactDOM.render(, testDiv); + + mockConsole.expectThreeLog(); + + expect(canvasRef.lastCall.args[0]).to.equal(testDiv.firstChild); + + ReactDOM.render(, testDiv); + + expect(canvasRef.callCount).to.equal(1); + }); + + it('Calls canvasRef when the canvas remounts', () => { + const canvasRef = sinon.spy(); + + ReactDOM.render(, testDiv); + + mockConsole.expectThreeLog(); + + expect(canvasRef.lastCall.args[0]).to.equal(testDiv.firstChild); + + const oldCanvas = canvasRef.lastCall.args[0]; + + ReactDOM.render(, testDiv); + + mockConsole.expectThreeLog(); + + // canvas should have remounted + expect(oldCanvas).not.to.equal(testDiv.firstChild); + + expect(canvasRef.callCount).to.equal(3); // unmount and remount! + expect(canvasRef.lastCall.args[0]).to.equal(testDiv.firstChild); + }); }; diff --git a/tests/src/descriptors/Shapes.jsx b/tests/src/descriptors/Shapes.jsx new file mode 100644 index 00000000..75b783d4 --- /dev/null +++ b/tests/src/descriptors/Shapes.jsx @@ -0,0 +1,468 @@ +import React from 'react'; +import THREE from 'three'; +import ReactDOM from 'react-dom'; +import sinon from 'sinon'; +import chai from 'chai'; + +const { expect } = chai; + +module.exports = type => { + describe('Shapes', () => { + const { testDiv, React3, mockConsole } = require('../utils/initContainer')(type); + + let shapeGeometryStub = null; + + afterEach(() => { + if (shapeGeometryStub) { + shapeGeometryStub.restore(); + shapeGeometryStub = null; + } + }); + + it('Should set shapes from props', () => { + const rectLength = 120; + const rectWidth = 40; + + const rectShape = new THREE.Shape(); + rectShape.moveTo(0, 0); + rectShape.lineTo(0, rectWidth); + rectShape.lineTo(rectLength, rectWidth); + rectShape.lineTo(rectLength, 0); + rectShape.lineTo(0, 0); + + const shapes = [ + rectShape, + ]; + + const OriginalShapeGeometry = THREE.ShapeGeometry; + + const spy = sinon.spy(); + + class ShapeGeometryMock extends OriginalShapeGeometry { + constructor(inputShapes, options) { + super(inputShapes, options); + + spy(inputShapes, options); + } + } + + shapeGeometryStub = sinon.stub(THREE, 'ShapeGeometry', ShapeGeometryMock); + mockConsole.expectThreeLog(); + + ReactDOM.render(( { + }} + > + + + + + + + ), testDiv); + + sinon.assert.calledOnce(spy); + + expect(spy.lastCall.args[0][0], + 'Shapes should be passed to the constructor of ShapeGeometry').to.equal(shapes[0]); + }); + + it('Should set shapes from children', () => { + const rectLength = 120; + const rectWidth = 40; + + const OriginalShapeGeometry = THREE.ShapeGeometry; + + const shapeGeometrySpy = sinon.spy(); + const shapeRef = sinon.spy(); + + class ShapeGeometryMock extends OriginalShapeGeometry { + constructor(shapes, options) { + super(shapes, options); + + shapeGeometrySpy(shapes, options); + } + } + + shapeGeometryStub = sinon.stub(THREE, 'ShapeGeometry', ShapeGeometryMock); + mockConsole.expectThreeLog(); + + ReactDOM.render(( { + }} + > + + + + + + + + + + + + + + + ), testDiv); + + // the constructor gets called only once and should have the shape in it + sinon.assert.calledOnce(shapeGeometrySpy); + + expect(shapeGeometrySpy.firstCall.args[0].length).to.equal(1); + + const shape = shapeGeometrySpy.firstCall.args[0][0]; + + expect(shapeRef.lastCall.args[0]).to.equal(shape); + + expect(shape.curves.length).to.equal(4); + + expect(shape.curves[0].v1.x).to.equal(0); + expect(shape.curves[0].v1.y).to.equal(0); + + expect(shape.curves[0].v2.x).to.equal(0); + expect(shape.curves[0].v2.y).to.equal(rectWidth); + + + expect(shape.curves[1].v1.x).to.equal(0); + expect(shape.curves[1].v1.y).to.equal(rectWidth); + + expect(shape.curves[1].v2.x).to.equal(rectLength); + expect(shape.curves[1].v2.y).to.equal(rectWidth); + + + expect(shape.curves[2].v1.x).to.equal(rectLength); + expect(shape.curves[2].v1.y).to.equal(rectWidth); + + expect(shape.curves[2].v2.x).to.equal(rectLength); + expect(shape.curves[2].v2.y).to.equal(0); + + + expect(shape.curves[3].v1.x).to.equal(rectLength); + expect(shape.curves[3].v1.y).to.equal(0); + + expect(shape.curves[3].v2.x).to.equal(0); + expect(shape.curves[3].v2.y).to.equal(0); + }); + + it('Should set options from props', () => { + const OriginalShapeGeometry = THREE.ShapeGeometry; + + const spy = sinon.spy(); + + class ShapeGeometryMock extends OriginalShapeGeometry { + constructor(shapes, options) { + super(shapes, options); + + spy(shapes, options); + } + } + + shapeGeometryStub = sinon.stub(THREE, 'ShapeGeometry', ShapeGeometryMock); + mockConsole.expectThreeLog(); + + ReactDOM.render(( { + }} + > + + + + + + + ), testDiv); + + sinon.assert.calledOnce(spy); + + expect(spy.firstCall.args[1].curveSegments).to.equal(1); + expect(spy.firstCall.args[1].material).to.equal(2); + + ReactDOM.render(( { + }} + > + + + + + + + ), testDiv); + + sinon.assert.calledTwice(spy); + + expect(spy.lastCall.args[1].curveSegments).to.equal(3); + expect(spy.lastCall.args[1].material).to.equal(4); + }); + + it('Should update shapes from children', () => { + const rectLength = 120; + const rectWidth = 40; + + const OriginalShapeGeometry = THREE.ShapeGeometry; + + const shapeGeometrySpy = sinon.spy(); + const shapeRef = sinon.spy(); + const secondShapeRef = sinon.spy(); + + class ShapeGeometryMock extends OriginalShapeGeometry { + constructor(shapes, options) { + super(shapes, options); + + shapeGeometrySpy(shapes, options); + } + } + + shapeGeometryStub = sinon.stub(THREE, 'ShapeGeometry', ShapeGeometryMock); + mockConsole.expectThreeLog(); + + ReactDOM.render(( { + }} + > + + + + + + + + + + + + + + + ), testDiv); + + // the constructor gets called only once and should have the shape in it + sinon.assert.calledOnce(shapeGeometrySpy); + + expect(shapeGeometrySpy.firstCall.args[0].length).to.equal(1); + + let shape = shapeGeometrySpy.firstCall.args[0][0]; + + expect(shapeRef.lastCall.args[0]).to.equal(shape); + + ReactDOM.render(( { + }} + > + + + + + + + + + + + + + + + + + + + + ), testDiv); + + expect(shapeRef.callCount).to.equal(3); // mount, unmount, ref cleanup, and remount + expect(shapeRef.getCall(0).args[0]).not.to.be.null(); + expect(shapeRef.getCall(1).args[0]).to.be.null(); + + shape = shapeRef.getCall(2).args[0]; + + expect(shape).not.to.be.null(); + expect(shape.curves.length).to.equal(2); + + expect(shape.curves[0].v1.x).to.equal(0); + expect(shape.curves[0].v1.y).to.equal(0); + + expect(shape.curves[0].v2.x).to.equal(10); + expect(shape.curves[0].v2.y).to.equal(10 + (rectWidth * 5)); + + expect(secondShapeRef.callCount).to.equal(2); // mount, remount + + ReactDOM.render(( { + }} + > + + + + + + + + + + + + + + + + + + + + + + ), testDiv); + + expect(shapeRef.callCount).to.equal(5); // ref cleanup, remount + expect(shapeRef.getCall(3).args[0]).to.be.null(); + expect(shapeRef.getCall(4).args[0]).not.to.be.null(); + + shape = shapeRef.getCall(4).args[0]; + + expect(shape).not.to.be.null(); + expect(shape.curves.length).to.equal(4); + + expect(shape.curves[0].v1.x).to.equal(0); + expect(shape.curves[0].v1.y).to.equal(0); + + expect(shape.curves[0].v2.x).to.equal(0); + expect(shape.curves[0].v2.y).to.equal(rectWidth * 5); + + expect(shape.curves[1].v2.x).to.equal(rectLength * 10); + expect(shape.curves[1].v2.y).to.equal(rectWidth); + + expect(shape.curves[2].v2.x).to.equal(rectLength); + expect(shape.curves[2].v2.y).to.equal(0); + + expect(shape.curves[3].v2.x).to.equal(0); + expect(shape.curves[3].v2.y).to.equal(0); + + expect(secondShapeRef.callCount).to.equal(4); // ref cleanup, remount + expect(secondShapeRef.getCall(2).args[0]).to.be.null(); + expect(secondShapeRef.getCall(3).args[0]).not.to.be.null(); + }); + }); +}; diff --git a/tests/src/descriptors/index.js b/tests/src/descriptors/index.js index d6dc4bd4..cbf5c55e 100644 --- a/tests/src/descriptors/index.js +++ b/tests/src/descriptors/index.js @@ -5,4 +5,5 @@ module.exports = (type) => { require('./DirectionalLight')(type); require('./PointLight')(type); require('./ArrowHelper')(type); + require('./Shapes')(type); }; diff --git a/tests/src/meta/MockConsole.js b/tests/src/meta/MockConsole.js index 388386ca..1e238ea7 100644 --- a/tests/src/meta/MockConsole.js +++ b/tests/src/meta/MockConsole.js @@ -7,7 +7,9 @@ import MockConsole from '../utils/MockConsole'; if (process.env.KARMA_TDD) { sourceMapSupport.install({ // eslint-disable-line no-undef - handleUncaughtExceptions: false, + handleUncaughtExceptions: true, + environment: 'node', + hookRequire: true, }); } diff --git a/tests/src/start-testing.js b/tests/src/start-testing.js index 1efae351..76e50f49 100644 --- a/tests/src/start-testing.js +++ b/tests/src/start-testing.js @@ -4,7 +4,9 @@ import dirtyChai from 'dirty-chai'; if (process.env.KARMA_TDD) { sourceMapSupport.install({ // eslint-disable-line no-undef - handleUncaughtExceptions: false, + handleUncaughtExceptions: true, + environment: 'node', + hookRequire: true, }); } diff --git a/wiki/API-Reference.md b/wiki/API-Reference.md index 8fd70024..6ac6603d 100644 --- a/wiki/API-Reference.md +++ b/wiki/API-Reference.md @@ -41,7 +41,7 @@ * [[cameraHelper]]: Creates a [THREE.CameraHelper](http://threejs.org/docs/#Reference/Extras.Helpers/CameraHelper) * [[axisHelper]]: Creates a [THREE.AxisHelper](http://threejs.org/docs/#Reference/Extras.Helpers/AxisHelper) * [[arrowHelper]]: Creates a [THREE.ArrowHelper](http://threejs.org/docs/#Reference/Extras.Helpers/ArrowHelper) - * [[gridHelper]]: Creates a [THREE.Object3D](http://threejs.org/docs/#Reference/Core/Object3D) + * [[gridHelper]]: Creates a [THREE.GridHelper](https://threejs.org/docs/index.html#Reference/Extras.Helpers/GridHelper) * TODO: * BoundingBoxHelper * BoxHelper @@ -81,6 +81,7 @@ * DataTexture * [[Geometries]]: * [[geometry]]: Creates a [THREE.Geometry](http://threejs.org/docs/#Reference/Extras.Geometries/Geometry) + * [[bufferGeometry]]: Creates a [THREE.BufferGeometry](http://threejs.org/docs/#Reference/Core/BufferGeometry) * [[boxGeometry]]: Creates a [THREE.BoxGeometry](http://threejs.org/docs/#Reference/Extras.Geometries/BoxGeometry) * [[sphereGeometry]]: Creates a [THREE.SphereGeometry](http://threejs.org/docs/#Reference/Extras.Geometries/SphereGeometry) * [[parametricGeometry]]: Creates a [THREE.ParametricGeometry](http://threejs.org/docs/#Reference/Extras.Geometries/ParametricGeometry) @@ -101,9 +102,9 @@ * [[tubeGeometry]]: Creates a [THREE.TubeGeometry](http://threejs.org/docs/#Reference/Extras.Geometries/TubeGeometry) * [[dodecahedronGeometry]]: Creates a [THREE.DodecahedronGeometry](http://threejs.org/docs/index.html#Reference/Extras.Geometries/DodecahedronGeometry) * [[textGeometry]]: Creates a [THREE.TextGeometry](http://threejs.org/docs/#Reference/Extras.Geometries/TextGeometry) + * [[shapeGeometry]]: Creates a [THREE.ShapeGeometry](https://threejs.org/docs/#Reference/Geometries/ShapeGeometry) * TODO: * CubeGeometry - * ShapeGeometry * [[Shapes]]: * [[shape]]: Creates a [THREE.Shape](http://threejs.org/docs/#Reference/Extras.Core/Shape) * [[moveTo]]: Calls [THREE.Path#moveTo](http://threejs.org/docs/#Reference/Extras.Core/Path.moveTo) on the parent shape diff --git a/wiki/Geometries.md b/wiki/Geometries.md index c8a030bc..10622866 100644 --- a/wiki/Geometries.md +++ b/wiki/Geometries.md @@ -5,6 +5,7 @@ ## Components * [[geometry]]: Creates a [THREE.Geometry](http://threejs.org/docs/#Reference/Extras.Geometries/Geometry) +* [[bufferGeometry]]: Creates a [THREE.BufferGeometry](http://threejs.org/docs/#Reference/Core/BufferGeometry) * [[boxGeometry]]: Creates a [THREE.BoxGeometry](http://threejs.org/docs/#Reference/Extras.Geometries/BoxGeometry) * [[sphereGeometry]]: Creates a [THREE.SphereGeometry](http://threejs.org/docs/#Reference/Extras.Geometries/SphereGeometry) * [[parametricGeometry]]: Creates a [THREE.ParametricGeometry](http://threejs.org/docs/#Reference/Extras.Geometries/ParametricGeometry) @@ -25,6 +26,6 @@ * [[tubeGeometry]]: Creates a [THREE.TubeGeometry](http://threejs.org/docs/#Reference/Extras.Geometries/TubeGeometry) * [[dodecahedronGeometry]]: Creates a [THREE.DodecahedronGeometry](http://threejs.org/docs/index.html#Reference/Extras.Geometries/DodecahedronGeometry) * [[textGeometry]]: Creates a [THREE.TextGeometry](http://threejs.org/docs/#Reference/Extras.Geometries/TextGeometry) +* [[shapeGeometry]]: Creates a [THREE.ShapeGeometry](https://threejs.org/docs/#Reference/Geometries/ShapeGeometry) * TODO: * CubeGeometry - * ShapeGeometry diff --git a/wiki/Helpers.md b/wiki/Helpers.md index c6f245ef..27cf7509 100644 --- a/wiki/Helpers.md +++ b/wiki/Helpers.md @@ -7,7 +7,7 @@ * [[cameraHelper]]: Creates a [THREE.CameraHelper](http://threejs.org/docs/#Reference/Extras.Helpers/CameraHelper) * [[axisHelper]]: Creates a [THREE.AxisHelper](http://threejs.org/docs/#Reference/Extras.Helpers/AxisHelper) * [[arrowHelper]]: Creates a [THREE.ArrowHelper](http://threejs.org/docs/#Reference/Extras.Helpers/ArrowHelper) -* [[gridHelper]]: Creates a [THREE.Object3D](http://threejs.org/docs/#Reference/Core/Object3D) +* [[gridHelper]]: Creates a [THREE.GridHelper](https://threejs.org/docs/index.html#Reference/Extras.Helpers/GridHelper) * TODO: * BoundingBoxHelper * BoxHelper diff --git a/wiki/Internal-Components.md b/wiki/Internal-Components.md index 64228a73..edf18ef9 100644 --- a/wiki/Internal-Components.md +++ b/wiki/Internal-Components.md @@ -58,7 +58,7 @@ class SomeClass extends React.Component{ * [[cameraHelper]]: Creates a [THREE.CameraHelper](http://threejs.org/docs/#Reference/Extras.Helpers/CameraHelper) * [[axisHelper]]: Creates a [THREE.AxisHelper](http://threejs.org/docs/#Reference/Extras.Helpers/AxisHelper) * [[arrowHelper]]: Creates a [THREE.ArrowHelper](http://threejs.org/docs/#Reference/Extras.Helpers/ArrowHelper) - * [[gridHelper]]: Creates a [THREE.Object3D](http://threejs.org/docs/#Reference/Core/Object3D) + * [[gridHelper]]: Creates a [THREE.GridHelper](https://threejs.org/docs/index.html#Reference/Extras.Helpers/GridHelper) * TODO: * BoundingBoxHelper * BoxHelper @@ -98,6 +98,7 @@ class SomeClass extends React.Component{ * DataTexture * [[Geometries]]: * [[geometry]]: Creates a [THREE.Geometry](http://threejs.org/docs/#Reference/Extras.Geometries/Geometry) + * [[bufferGeometry]]: Creates a [THREE.BufferGeometry](http://threejs.org/docs/#Reference/Core/BufferGeometry) * [[boxGeometry]]: Creates a [THREE.BoxGeometry](http://threejs.org/docs/#Reference/Extras.Geometries/BoxGeometry) * [[sphereGeometry]]: Creates a [THREE.SphereGeometry](http://threejs.org/docs/#Reference/Extras.Geometries/SphereGeometry) * [[parametricGeometry]]: Creates a [THREE.ParametricGeometry](http://threejs.org/docs/#Reference/Extras.Geometries/ParametricGeometry) @@ -118,9 +119,9 @@ class SomeClass extends React.Component{ * [[tubeGeometry]]: Creates a [THREE.TubeGeometry](http://threejs.org/docs/#Reference/Extras.Geometries/TubeGeometry) * [[dodecahedronGeometry]]: Creates a [THREE.DodecahedronGeometry](http://threejs.org/docs/index.html#Reference/Extras.Geometries/DodecahedronGeometry) * [[textGeometry]]: Creates a [THREE.TextGeometry](http://threejs.org/docs/#Reference/Extras.Geometries/TextGeometry) + * [[shapeGeometry]]: Creates a [THREE.ShapeGeometry](https://threejs.org/docs/#Reference/Geometries/ShapeGeometry) * TODO: * CubeGeometry - * ShapeGeometry * [[Shapes]]: * [[shape]]: Creates a [THREE.Shape](http://threejs.org/docs/#Reference/Extras.Core/Shape) * [[moveTo]]: Calls [THREE.Path#moveTo](http://threejs.org/docs/#Reference/Extras.Core/Path.moveTo) on the parent shape diff --git a/wiki/Objects.md b/wiki/Objects.md index 5f73c982..310954ea 100644 --- a/wiki/Objects.md +++ b/wiki/Objects.md @@ -38,7 +38,7 @@ Entities that can be added into a [[scene]] * [[cameraHelper]]: Creates a [THREE.CameraHelper](http://threejs.org/docs/#Reference/Extras.Helpers/CameraHelper) * [[axisHelper]]: Creates a [THREE.AxisHelper](http://threejs.org/docs/#Reference/Extras.Helpers/AxisHelper) * [[arrowHelper]]: Creates a [THREE.ArrowHelper](http://threejs.org/docs/#Reference/Extras.Helpers/ArrowHelper) - * [[gridHelper]]: Creates a [THREE.Object3D](http://threejs.org/docs/#Reference/Core/Object3D) + * [[gridHelper]]: Creates a [THREE.GridHelper](https://threejs.org/docs/index.html#Reference/Extras.Helpers/GridHelper) * TODO: * BoundingBoxHelper * BoxHelper diff --git a/wiki/Resource-Types.md b/wiki/Resource-Types.md index 1af4372e..8c6ae172 100644 --- a/wiki/Resource-Types.md +++ b/wiki/Resource-Types.md @@ -31,6 +31,7 @@ Then you can place these components inside to create and assign resources, * [[Shapes]]: * [[shape]]: Creates a [THREE.Shape](http://threejs.org/docs/#Reference/Extras.Core/Shape) * [[Geometries]]: + * [[shapeGeometry]]: Creates a [THREE.ShapeGeometry](https://threejs.org/docs/#Reference/Geometries/ShapeGeometry) * [[textGeometry]]: Creates a [THREE.TextGeometry](http://threejs.org/docs/#Reference/Extras.Geometries/TextGeometry) * [[dodecahedronGeometry]]: Creates a [THREE.DodecahedronGeometry](http://threejs.org/docs/index.html#Reference/Extras.Geometries/DodecahedronGeometry) * [[tubeGeometry]]: Creates a [THREE.TubeGeometry](http://threejs.org/docs/#Reference/Extras.Geometries/TubeGeometry) @@ -51,6 +52,7 @@ Then you can place these components inside to create and assign resources, * [[parametricGeometry]]: Creates a [THREE.ParametricGeometry](http://threejs.org/docs/#Reference/Extras.Geometries/ParametricGeometry) * [[sphereGeometry]]: Creates a [THREE.SphereGeometry](http://threejs.org/docs/#Reference/Extras.Geometries/SphereGeometry) * [[boxGeometry]]: Creates a [THREE.BoxGeometry](http://threejs.org/docs/#Reference/Extras.Geometries/BoxGeometry) + * [[bufferGeometry]]: Creates a [THREE.BufferGeometry](http://threejs.org/docs/#Reference/Core/BufferGeometry) * [[geometry]]: Creates a [THREE.Geometry](http://threejs.org/docs/#Reference/Extras.Geometries/Geometry) * [[Textures]]: * [[texture]]: Creates a [THREE.Texture](http://threejs.org/docs/#Reference/Textures/Texture) diff --git a/wiki/bufferGeometry.md b/wiki/bufferGeometry.md new file mode 100644 index 00000000..f8f70eab --- /dev/null +++ b/wiki/bufferGeometry.md @@ -0,0 +1,38 @@ +> [Wiki](Home) » [[Internal Components]] » [[Geometries]] » **bufferGeometry** + +# bufferGeometry + +Creates a [THREE.BufferGeometry](http://threejs.org/docs/#Reference/Core/BufferGeometry) + +## Attributes + +### name +``` string ``` + +**Default**: `''` + +### position +``` one of types [THREE.BufferAttribute, THREE.InterleavedBufferAttribute] ``` + +### normal +``` one of types [THREE.BufferAttribute, THREE.InterleavedBufferAttribute] ``` + +### color +``` one of types [THREE.BufferAttribute, THREE.InterleavedBufferAttribute] ``` + +### index +``` one of types [THREE.BufferAttribute, THREE.InterleavedBufferAttribute] ``` + +### resourceId +``` string ```: The resource id of this object, only used if it is placed into [[resources]]. + +**Default**: `''` + +## Notes: + +This component can be added into [<resources/>](resources)! See [[Resource Types]] for more information. + +=== + +|**[View Source](../blob/master/src/lib/descriptors/Geometry/BufferGeometryDescriptor.js)**| + ---| diff --git a/wiki/extrudeGeometry.md b/wiki/extrudeGeometry.md index 7d8ea2f8..5348867a 100644 --- a/wiki/extrudeGeometry.md +++ b/wiki/extrudeGeometry.md @@ -55,38 +55,193 @@ once sent to GPU. **Default**: `[]` +### curveSegments +``` number ``` + +### material +``` number ``` + +### UVGenerator +``` shape of {"generateTopUV":{"_type":"function","_isRequired":false,"isRequired":{"_type":"function","_isRequired":true}},"generateSideWallUV":{"_type":"function","_isRequired":false,"isRequired":{"_type":"function","_isRequired":true}}} ``` + ### settings ``` any ``` +### steps +``` number ``` + ### amount -``` any ``` +``` number ``` ### bevelThickness -``` any ``` +``` number ``` ### bevelSize -``` any ``` +``` number ``` ### bevelSegments -``` any ``` +``` number ``` + +### extrudeMaterial +``` number ``` ### bevelEnabled -``` any ``` +``` bool ``` -### curveSegments -``` any ``` +### extrudePath +``` THREE.CurvePath ``` -### steps -``` any ``` +### frames +``` function ( path, segments, closed ) { -### extrudePath -``` any ``` + var normal = new THREE.Vector3(), -### UVGenerator -``` any ``` + tangents = [], + normals = [], + binormals = [], -### frames -``` any ``` + vec = new THREE.Vector3(), + mat = new THREE.Matrix4(), + + numpoints = segments + 1, + theta, + smallest, + + tx, ty, tz, + i, u; + + + // expose internals + this.tangents = tangents; + this.normals = normals; + this.binormals = binormals; + + // compute the tangent vectors for each segment on the path + + for ( i = 0; i < numpoints; i ++ ) { + + u = i / ( numpoints - 1 ); + + tangents[ i ] = path.getTangentAt( u ); + tangents[ i ].normalize(); + + } + + initialNormal3(); + + /* + function initialNormal1(lastBinormal) { + // fixed start binormal. Has dangers of 0 vectors + normals[ 0 ] = new THREE.Vector3(); + binormals[ 0 ] = new THREE.Vector3(); + if (lastBinormal===undefined) lastBinormal = new THREE.Vector3( 0, 0, 1 ); + normals[ 0 ].crossVectors( lastBinormal, tangents[ 0 ] ).normalize(); + binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ).normalize(); + } + + function initialNormal2() { + + // This uses the Frenet-Serret formula for deriving binormal + var t2 = path.getTangentAt( epsilon ); + + normals[ 0 ] = new THREE.Vector3().subVectors( t2, tangents[ 0 ] ).normalize(); + binormals[ 0 ] = new THREE.Vector3().crossVectors( tangents[ 0 ], normals[ 0 ] ); + + normals[ 0 ].crossVectors( binormals[ 0 ], tangents[ 0 ] ).normalize(); // last binormal x tangent + binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ).normalize(); + + } + */ + + function initialNormal3() { + + // select an initial normal vector perpendicular to the first tangent vector, + // and in the direction of the smallest tangent xyz component + + normals[ 0 ] = new THREE.Vector3(); + binormals[ 0 ] = new THREE.Vector3(); + smallest = Number.MAX_VALUE; + tx = Math.abs( tangents[ 0 ].x ); + ty = Math.abs( tangents[ 0 ].y ); + tz = Math.abs( tangents[ 0 ].z ); + + if ( tx <= smallest ) { + + smallest = tx; + normal.set( 1, 0, 0 ); + + } + + if ( ty <= smallest ) { + + smallest = ty; + normal.set( 0, 1, 0 ); + + } + + if ( tz <= smallest ) { + + normal.set( 0, 0, 1 ); + + } + + vec.crossVectors( tangents[ 0 ], normal ).normalize(); + + normals[ 0 ].crossVectors( tangents[ 0 ], vec ); + binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ); + + } + + + // compute the slowly-varying normal and binormal vectors for each segment on the path + + for ( i = 1; i < numpoints; i ++ ) { + + normals[ i ] = normals[ i - 1 ].clone(); + + binormals[ i ] = binormals[ i - 1 ].clone(); + + vec.crossVectors( tangents[ i - 1 ], tangents[ i ] ); + + if ( vec.length() > Number.EPSILON ) { + + vec.normalize(); + + theta = Math.acos( THREE.Math.clamp( tangents[ i - 1 ].dot( tangents[ i ] ), - 1, 1 ) ); // clamp for floating pt errors + + normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) ); + + } + + binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); + + } + + + // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same + + if ( closed ) { + + theta = Math.acos( THREE.Math.clamp( normals[ 0 ].dot( normals[ numpoints - 1 ] ), - 1, 1 ) ); + theta /= ( numpoints - 1 ); + + if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ numpoints - 1 ] ) ) > 0 ) { + + theta = - theta; + + } + + for ( i = 1; i < numpoints; i ++ ) { + + // twist a little... + normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) ); + binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); + + } + + } + +} ``` ### resourceId ``` string ```: The resource id of this object, only used if it is placed into [[resources]]. diff --git a/wiki/gridHelper.md b/wiki/gridHelper.md index 3d42ae37..6469d579 100644 --- a/wiki/gridHelper.md +++ b/wiki/gridHelper.md @@ -2,9 +2,7 @@ # gridHelper -Creates a [THREE.Object3D](http://threejs.org/docs/#Reference/Core/Object3D) - -Creates a [THREE.AxisHelper](https://threejs.org/docs/index.html#Reference/Extras.Helpers/GridHelper) +Creates a [THREE.GridHelper](https://threejs.org/docs/index.html#Reference/Extras.Helpers/GridHelper) ## Attributes diff --git a/wiki/shape.md b/wiki/shape.md index 05f953f7..215b7dd6 100644 --- a/wiki/shape.md +++ b/wiki/shape.md @@ -4,8 +4,9 @@ Creates a [THREE.Shape](http://threejs.org/docs/#Reference/Extras.Core/Shape) -Place this within [[<extrudeGeometry>|extrudeGeometry]] - or [<resources>](resources). +Place this within [[<extrudeGeometry>|extrudeGeometry]], + [[<shapeGeometry>|shapeGeometry]], + or [[<resources>|resources]]. ## Attributes diff --git a/wiki/shapeGeometry.md b/wiki/shapeGeometry.md new file mode 100644 index 00000000..23f0b769 --- /dev/null +++ b/wiki/shapeGeometry.md @@ -0,0 +1,70 @@ +> [Wiki](Home) » [[Internal Components]] » [[Geometries]] » **shapeGeometry** + +# shapeGeometry + +Creates a [THREE.ShapeGeometry](https://threejs.org/docs/#Reference/Geometries/ShapeGeometry) + +## Attributes + +### name +``` string ```: Name for this geometry. + +**Default**: `''` + +### vertices +``` array of THREE.Vector3 ```: See [THREE.Geometry#vertices](http://threejs.org/docs/#Reference/Core/Geometry.vertices). + +**Default**: `[]` + +### colors +``` array of THREE.Color ```: See [THREE.Geometry#colors](http://threejs.org/docs/#Reference/Core/Geometry.colors). + +**Default**: `[]` + +### faceVertexUvs +``` array of (array of (array of THREE.Vector2)) ```: See [THREE.Geometry#faceVertexUvs](http://threejs.org/docs/#Reference/Core/Geometry.faceVertexUvs). + +**Default**: `[]` + +### faces +``` array of THREE.Face3 ```: See [THREE.Geometry#faces](http://threejs.org/docs/#Reference/Core/Geometry.faces). + +**Default**: `[]` + +### dynamic +``` bool ```: See [THREE.Geometry#dynamic](http://threejs.org/docs/#Reference/Core/Geometry.dynamic). + +Set to true if attribute buffers will need to change in runtime (using "dirty" flags). + +Unless set to true internal typed arrays corresponding to buffers will be deleted +once sent to GPU. + +**Default**: `false` + +### shapes +``` array of THREE.Shape ```: Array of shapes, or a single shape THREE.Shape + +**Default**: `[]` + +### curveSegments +``` number ```: Default is 12 (not used in three.js at the moment) + +### material +``` number ```: Index of the material in a material list + +### UVGenerator +``` shape of {"generateTopUV":{"_type":"function","_isRequired":false,"isRequired":{"_type":"function","_isRequired":true}},"generateSideWallUV":{"_type":"function","_isRequired":false,"isRequired":{"_type":"function","_isRequired":true}}} ```: A UV generator, defaults to ExtrudeGeometry's WorldUVGenerator + +### resourceId +``` string ```: The resource id of this object, only used if it is placed into [[resources]]. + +**Default**: `''` + +## Notes: + +This component can be added into [<resources/>](resources)! See [[Resource Types]] for more information. + +=== + +|**[View Source](../blob/master/src/lib/descriptors/Geometry/ShapeGeometryDescriptor.js)**| + ---|