UNPKG

bytev-charts

Version:

基于echarts和JavaScript及ES6封装的一个可以直接调用的图表组件库,内置主题设计,简单快捷,且支持用户自定义配置; npm 安装方式: npm install bytev-charts 若启动提示还需额外install插件,则运行 npm install @babel/runtime-corejs2 即可;

1,115 lines (932 loc) 45.5 kB
import "core-js/modules/es.string.sub.js"; import "core-js/modules/es.function.name.js"; import "core-js/modules/es.array.index-of.js"; import "core-js/modules/es.regexp.exec.js"; import "core-js/modules/es.string.search.js"; import "core-js/modules/es.array.concat.js"; import "core-js/modules/es.array.some.js"; import "core-js/modules/es.object.to-string.js"; import "core-js/modules/es.string.split.js"; import _Object$defineProperty from "@babel/runtime-corejs2/core-js/object/define-property"; import _Object$assign from "@babel/runtime-corejs2/core-js/object/assign"; import _Object$create from "@babel/runtime-corejs2/core-js/object/create"; import { BoxBufferGeometry, BufferGeometry, Color, CylinderBufferGeometry, DoubleSide, Euler, Float32BufferAttribute, Line, LineBasicMaterial, Matrix4, Mesh, MeshBasicMaterial, Object3D, OctahedronBufferGeometry, PlaneBufferGeometry, Quaternion, Raycaster, SphereBufferGeometry, TorusBufferGeometry, Vector3 } from "../../../build/three.module.js"; var TransformControls = function TransformControls(camera, domElement) { if (domElement === undefined) { console.warn('THREE.TransformControls: The second parameter "domElement" is now mandatory.'); domElement = document; } Object3D.call(this); this.visible = false; this.domElement = domElement; var _gizmo = new TransformControlsGizmo(); this.add(_gizmo); var _plane = new TransformControlsPlane(); this.add(_plane); var scope = this; // Define properties with getters/setter // Setting the defined property will automatically trigger change event // Defined properties are passed down to gizmo and plane defineProperty("camera", camera); defineProperty("object", undefined); defineProperty("enabled", true); defineProperty("axis", null); defineProperty("mode", "translate"); defineProperty("translationSnap", null); defineProperty("rotationSnap", null); defineProperty("scaleSnap", null); defineProperty("space", "world"); defineProperty("size", 1); defineProperty("dragging", false); defineProperty("showX", true); defineProperty("showY", true); defineProperty("showZ", true); var changeEvent = { type: "change" }; var mouseDownEvent = { type: "mouseDown" }; var mouseUpEvent = { type: "mouseUp", mode: scope.mode }; var objectChangeEvent = { type: "objectChange" }; // Reusable utility variables var raycaster = new Raycaster(); function intersectObjectWithRay(object, raycaster, includeInvisible) { var allIntersections = raycaster.intersectObject(object, true); for (var i = 0; i < allIntersections.length; i++) { if (allIntersections[i].object.visible || includeInvisible) { return allIntersections[i]; } } return false; } var _tempVector = new Vector3(); var _tempVector2 = new Vector3(); var _tempQuaternion = new Quaternion(); var _unit = { X: new Vector3(1, 0, 0), Y: new Vector3(0, 1, 0), Z: new Vector3(0, 0, 1) }; var pointStart = new Vector3(); var pointEnd = new Vector3(); var offset = new Vector3(); var rotationAxis = new Vector3(); var startNorm = new Vector3(); var endNorm = new Vector3(); var rotationAngle = 0; var cameraPosition = new Vector3(); var cameraQuaternion = new Quaternion(); var cameraScale = new Vector3(); var parentPosition = new Vector3(); var parentQuaternion = new Quaternion(); var parentQuaternionInv = new Quaternion(); var parentScale = new Vector3(); var worldPositionStart = new Vector3(); var worldQuaternionStart = new Quaternion(); var worldScaleStart = new Vector3(); var worldPosition = new Vector3(); var worldQuaternion = new Quaternion(); var worldQuaternionInv = new Quaternion(); var worldScale = new Vector3(); var eye = new Vector3(); var positionStart = new Vector3(); var quaternionStart = new Quaternion(); var scaleStart = new Vector3(); // TODO: remove properties unused in plane and gizmo defineProperty("worldPosition", worldPosition); defineProperty("worldPositionStart", worldPositionStart); defineProperty("worldQuaternion", worldQuaternion); defineProperty("worldQuaternionStart", worldQuaternionStart); defineProperty("cameraPosition", cameraPosition); defineProperty("cameraQuaternion", cameraQuaternion); defineProperty("pointStart", pointStart); defineProperty("pointEnd", pointEnd); defineProperty("rotationAxis", rotationAxis); defineProperty("rotationAngle", rotationAngle); defineProperty("eye", eye); { domElement.addEventListener("mousedown", onPointerDown, false); domElement.addEventListener("touchstart", onPointerDown, false); domElement.addEventListener("mousemove", onPointerHover, false); domElement.addEventListener("touchmove", onPointerHover, false); domElement.addEventListener("touchmove", onPointerMove, false); scope.domElement.ownerDocument.addEventListener("mouseup", onPointerUp, false); domElement.addEventListener("touchend", onPointerUp, false); domElement.addEventListener("touchcancel", onPointerUp, false); domElement.addEventListener("touchleave", onPointerUp, false); } this.dispose = function () { domElement.removeEventListener("mousedown", onPointerDown); domElement.removeEventListener("touchstart", onPointerDown); domElement.removeEventListener("mousemove", onPointerHover); scope.domElement.ownerDocument.removeEventListener("mousemove", onPointerMove); domElement.removeEventListener("touchmove", onPointerHover); domElement.removeEventListener("touchmove", onPointerMove); scope.domElement.ownerDocument.removeEventListener("mouseup", onPointerUp); domElement.removeEventListener("touchend", onPointerUp); domElement.removeEventListener("touchcancel", onPointerUp); domElement.removeEventListener("touchleave", onPointerUp); this.traverse(function (child) { if (child.geometry) child.geometry.dispose(); if (child.material) child.material.dispose(); }); }; // Set current object this.attach = function (object) { this.object = object; this.visible = true; return this; }; // Detatch from object this.detach = function () { this.object = undefined; this.visible = false; this.axis = null; return this; }; // Defined getter, setter and store for a property function defineProperty(propName, defaultValue) { var propValue = defaultValue; _Object$defineProperty(scope, propName, { get: function get() { return propValue !== undefined ? propValue : defaultValue; }, set: function set(value) { if (propValue !== value) { propValue = value; _plane[propName] = value; _gizmo[propName] = value; scope.dispatchEvent({ type: propName + "-changed", value: value }); scope.dispatchEvent(changeEvent); } } }); scope[propName] = defaultValue; _plane[propName] = defaultValue; _gizmo[propName] = defaultValue; } // updateMatrixWorld updates key transformation variables this.updateMatrixWorld = function () { if (this.object !== undefined) { this.object.updateMatrixWorld(); if (this.object.parent === null) { console.error('TransformControls: The attached 3D object must be a part of the scene graph.'); } else { this.object.parent.matrixWorld.decompose(parentPosition, parentQuaternion, parentScale); } this.object.matrixWorld.decompose(worldPosition, worldQuaternion, worldScale); parentQuaternionInv.copy(parentQuaternion).inverse(); worldQuaternionInv.copy(worldQuaternion).inverse(); } this.camera.updateMatrixWorld(); this.camera.matrixWorld.decompose(cameraPosition, cameraQuaternion, cameraScale); eye.copy(cameraPosition).sub(worldPosition).normalize(); Object3D.prototype.updateMatrixWorld.call(this); }; this.pointerHover = function (pointer) { if (this.object === undefined || this.dragging === true || pointer.button !== undefined && pointer.button !== 0) return; raycaster.setFromCamera(pointer, this.camera); var intersect = intersectObjectWithRay(_gizmo.picker[this.mode], raycaster); if (intersect) { this.axis = intersect.object.name; } else { this.axis = null; } }; this.pointerDown = function (pointer) { if (this.object === undefined || this.dragging === true || pointer.button !== undefined && pointer.button !== 0) return; if ((pointer.button === 0 || pointer.button === undefined) && this.axis !== null) { raycaster.setFromCamera(pointer, this.camera); var planeIntersect = intersectObjectWithRay(_plane, raycaster, true); if (planeIntersect) { var space = this.space; if (this.mode === 'scale') { space = 'local'; } else if (this.axis === 'E' || this.axis === 'XYZE' || this.axis === 'XYZ') { space = 'world'; } if (space === 'local' && this.mode === 'rotate') { var snap = this.rotationSnap; if (this.axis === 'X' && snap) this.object.rotation.x = Math.round(this.object.rotation.x / snap) * snap; if (this.axis === 'Y' && snap) this.object.rotation.y = Math.round(this.object.rotation.y / snap) * snap; if (this.axis === 'Z' && snap) this.object.rotation.z = Math.round(this.object.rotation.z / snap) * snap; } this.object.updateMatrixWorld(); this.object.parent.updateMatrixWorld(); positionStart.copy(this.object.position); quaternionStart.copy(this.object.quaternion); scaleStart.copy(this.object.scale); this.object.matrixWorld.decompose(worldPositionStart, worldQuaternionStart, worldScaleStart); pointStart.copy(planeIntersect.point).sub(worldPositionStart); } this.dragging = true; mouseDownEvent.mode = this.mode; this.dispatchEvent(mouseDownEvent); } }; this.pointerMove = function (pointer) { var axis = this.axis; var mode = this.mode; var object = this.object; var space = this.space; if (mode === 'scale') { space = 'local'; } else if (axis === 'E' || axis === 'XYZE' || axis === 'XYZ') { space = 'world'; } if (object === undefined || axis === null || this.dragging === false || pointer.button !== undefined && pointer.button !== 0) return; raycaster.setFromCamera(pointer, this.camera); var planeIntersect = intersectObjectWithRay(_plane, raycaster, true); if (!planeIntersect) return; pointEnd.copy(planeIntersect.point).sub(worldPositionStart); if (mode === 'translate') { // Apply translate offset.copy(pointEnd).sub(pointStart); if (space === 'local' && axis !== 'XYZ') { offset.applyQuaternion(worldQuaternionInv); } if (axis.indexOf('X') === -1) offset.x = 0; if (axis.indexOf('Y') === -1) offset.y = 0; if (axis.indexOf('Z') === -1) offset.z = 0; if (space === 'local' && axis !== 'XYZ') { offset.applyQuaternion(quaternionStart).divide(parentScale); } else { offset.applyQuaternion(parentQuaternionInv).divide(parentScale); } object.position.copy(offset).add(positionStart); // Apply translation snap if (this.translationSnap) { if (space === 'local') { object.position.applyQuaternion(_tempQuaternion.copy(quaternionStart).inverse()); if (axis.search('X') !== -1) { object.position.x = Math.round(object.position.x / this.translationSnap) * this.translationSnap; } if (axis.search('Y') !== -1) { object.position.y = Math.round(object.position.y / this.translationSnap) * this.translationSnap; } if (axis.search('Z') !== -1) { object.position.z = Math.round(object.position.z / this.translationSnap) * this.translationSnap; } object.position.applyQuaternion(quaternionStart); } if (space === 'world') { if (object.parent) { object.position.add(_tempVector.setFromMatrixPosition(object.parent.matrixWorld)); } if (axis.search('X') !== -1) { object.position.x = Math.round(object.position.x / this.translationSnap) * this.translationSnap; } if (axis.search('Y') !== -1) { object.position.y = Math.round(object.position.y / this.translationSnap) * this.translationSnap; } if (axis.search('Z') !== -1) { object.position.z = Math.round(object.position.z / this.translationSnap) * this.translationSnap; } if (object.parent) { object.position.sub(_tempVector.setFromMatrixPosition(object.parent.matrixWorld)); } } } } else if (mode === 'scale') { if (axis.search('XYZ') !== -1) { var d = pointEnd.length() / pointStart.length(); if (pointEnd.dot(pointStart) < 0) d *= -1; _tempVector2.set(d, d, d); } else { _tempVector.copy(pointStart); _tempVector2.copy(pointEnd); _tempVector.applyQuaternion(worldQuaternionInv); _tempVector2.applyQuaternion(worldQuaternionInv); _tempVector2.divide(_tempVector); if (axis.search('X') === -1) { _tempVector2.x = 1; } if (axis.search('Y') === -1) { _tempVector2.y = 1; } if (axis.search('Z') === -1) { _tempVector2.z = 1; } } // Apply scale object.scale.copy(scaleStart).multiply(_tempVector2); if (this.scaleSnap) { if (axis.search('X') !== -1) { object.scale.x = Math.round(object.scale.x / this.scaleSnap) * this.scaleSnap || this.scaleSnap; } if (axis.search('Y') !== -1) { object.scale.y = Math.round(object.scale.y / this.scaleSnap) * this.scaleSnap || this.scaleSnap; } if (axis.search('Z') !== -1) { object.scale.z = Math.round(object.scale.z / this.scaleSnap) * this.scaleSnap || this.scaleSnap; } } } else if (mode === 'rotate') { offset.copy(pointEnd).sub(pointStart); var ROTATION_SPEED = 20 / worldPosition.distanceTo(_tempVector.setFromMatrixPosition(this.camera.matrixWorld)); if (axis === 'E') { rotationAxis.copy(eye); rotationAngle = pointEnd.angleTo(pointStart); startNorm.copy(pointStart).normalize(); endNorm.copy(pointEnd).normalize(); rotationAngle *= endNorm.cross(startNorm).dot(eye) < 0 ? 1 : -1; } else if (axis === 'XYZE') { rotationAxis.copy(offset).cross(eye).normalize(); rotationAngle = offset.dot(_tempVector.copy(rotationAxis).cross(this.eye)) * ROTATION_SPEED; } else if (axis === 'X' || axis === 'Y' || axis === 'Z') { rotationAxis.copy(_unit[axis]); _tempVector.copy(_unit[axis]); if (space === 'local') { _tempVector.applyQuaternion(worldQuaternion); } rotationAngle = offset.dot(_tempVector.cross(eye).normalize()) * ROTATION_SPEED; } // Apply rotation snap if (this.rotationSnap) rotationAngle = Math.round(rotationAngle / this.rotationSnap) * this.rotationSnap; this.rotationAngle = rotationAngle; // Apply rotate if (space === 'local' && axis !== 'E' && axis !== 'XYZE') { object.quaternion.copy(quaternionStart); object.quaternion.multiply(_tempQuaternion.setFromAxisAngle(rotationAxis, rotationAngle)).normalize(); } else { rotationAxis.applyQuaternion(parentQuaternionInv); object.quaternion.copy(_tempQuaternion.setFromAxisAngle(rotationAxis, rotationAngle)); object.quaternion.multiply(quaternionStart).normalize(); } } this.dispatchEvent(changeEvent); this.dispatchEvent(objectChangeEvent); }; this.pointerUp = function (pointer) { if (pointer.button !== undefined && pointer.button !== 0) return; if (this.dragging && this.axis !== null) { mouseUpEvent.mode = this.mode; this.dispatchEvent(mouseUpEvent); } this.dragging = false; if (pointer.button === undefined) this.axis = null; }; // normalize mouse / touch pointer and remap {x,y} to view space. function getPointer(event) { if (scope.domElement.ownerDocument.pointerLockElement) { return { x: 0, y: 0, button: event.button }; } else { var pointer = event.changedTouches ? event.changedTouches[0] : event; var rect = domElement.getBoundingClientRect(); return { x: (pointer.clientX - rect.left) / rect.width * 2 - 1, y: -(pointer.clientY - rect.top) / rect.height * 2 + 1, button: event.button }; } } // mouse / touch event handlers function onPointerHover(event) { if (!scope.enabled) return; scope.pointerHover(getPointer(event)); } function onPointerDown(event) { if (!scope.enabled) return; scope.domElement.ownerDocument.addEventListener("mousemove", onPointerMove, false); scope.pointerHover(getPointer(event)); scope.pointerDown(getPointer(event)); } function onPointerMove(event) { if (!scope.enabled) return; scope.pointerMove(getPointer(event)); } function onPointerUp(event) { if (!scope.enabled) return; scope.domElement.ownerDocument.removeEventListener("mousemove", onPointerMove, false); scope.pointerUp(getPointer(event)); } // TODO: deprecate this.getMode = function () { return scope.mode; }; this.setMode = function (mode) { scope.mode = mode; }; this.setTranslationSnap = function (translationSnap) { scope.translationSnap = translationSnap; }; this.setRotationSnap = function (rotationSnap) { scope.rotationSnap = rotationSnap; }; this.setScaleSnap = function (scaleSnap) { scope.scaleSnap = scaleSnap; }; this.setSize = function (size) { scope.size = size; }; this.setSpace = function (space) { scope.space = space; }; this.update = function () { console.warn('THREE.TransformControls: update function has no more functionality and therefore has been deprecated.'); }; }; TransformControls.prototype = _Object$assign(_Object$create(Object3D.prototype), { constructor: TransformControls, isTransformControls: true }); var TransformControlsGizmo = function TransformControlsGizmo() { 'use strict'; Object3D.call(this); this.type = 'TransformControlsGizmo'; // shared materials var gizmoMaterial = new MeshBasicMaterial({ depthTest: false, depthWrite: false, transparent: true, side: DoubleSide, fog: false, toneMapped: false }); var gizmoLineMaterial = new LineBasicMaterial({ depthTest: false, depthWrite: false, transparent: true, linewidth: 1, fog: false, toneMapped: false }); // Make unique material for each axis/color var matInvisible = gizmoMaterial.clone(); matInvisible.opacity = 0.15; var matHelper = gizmoMaterial.clone(); matHelper.opacity = 0.33; var matRed = gizmoMaterial.clone(); matRed.color.set(0xff0000); var matGreen = gizmoMaterial.clone(); matGreen.color.set(0x00ff00); var matBlue = gizmoMaterial.clone(); matBlue.color.set(0x0000ff); var matWhiteTransparent = gizmoMaterial.clone(); matWhiteTransparent.opacity = 0.25; var matYellowTransparent = matWhiteTransparent.clone(); matYellowTransparent.color.set(0xffff00); var matCyanTransparent = matWhiteTransparent.clone(); matCyanTransparent.color.set(0x00ffff); var matMagentaTransparent = matWhiteTransparent.clone(); matMagentaTransparent.color.set(0xff00ff); var matYellow = gizmoMaterial.clone(); matYellow.color.set(0xffff00); var matLineRed = gizmoLineMaterial.clone(); matLineRed.color.set(0xff0000); var matLineGreen = gizmoLineMaterial.clone(); matLineGreen.color.set(0x00ff00); var matLineBlue = gizmoLineMaterial.clone(); matLineBlue.color.set(0x0000ff); var matLineCyan = gizmoLineMaterial.clone(); matLineCyan.color.set(0x00ffff); var matLineMagenta = gizmoLineMaterial.clone(); matLineMagenta.color.set(0xff00ff); var matLineYellow = gizmoLineMaterial.clone(); matLineYellow.color.set(0xffff00); var matLineGray = gizmoLineMaterial.clone(); matLineGray.color.set(0x787878); var matLineYellowTransparent = matLineYellow.clone(); matLineYellowTransparent.opacity = 0.25; // reusable geometry var arrowGeometry = new CylinderBufferGeometry(0, 0.05, 0.2, 12, 1, false); var scaleHandleGeometry = new BoxBufferGeometry(0.125, 0.125, 0.125); var lineGeometry = new BufferGeometry(); lineGeometry.setAttribute('position', new Float32BufferAttribute([0, 0, 0, 1, 0, 0], 3)); var CircleGeometry = function CircleGeometry(radius, arc) { var geometry = new BufferGeometry(); var vertices = []; for (var i = 0; i <= 64 * arc; ++i) { vertices.push(0, Math.cos(i / 32 * Math.PI) * radius, Math.sin(i / 32 * Math.PI) * radius); } geometry.setAttribute('position', new Float32BufferAttribute(vertices, 3)); return geometry; }; // Special geometry for transform helper. If scaled with position vector it spans from [0,0,0] to position var TranslateHelperGeometry = function TranslateHelperGeometry() { var geometry = new BufferGeometry(); geometry.setAttribute('position', new Float32BufferAttribute([0, 0, 0, 1, 1, 1], 3)); return geometry; }; // Gizmo definitions - custom hierarchy definitions for setupGizmo() function var gizmoTranslate = { X: [[new Mesh(arrowGeometry, matRed), [1, 0, 0], [0, 0, -Math.PI / 2], null, 'fwd'], [new Mesh(arrowGeometry, matRed), [1, 0, 0], [0, 0, Math.PI / 2], null, 'bwd'], [new Line(lineGeometry, matLineRed)]], Y: [[new Mesh(arrowGeometry, matGreen), [0, 1, 0], null, null, 'fwd'], [new Mesh(arrowGeometry, matGreen), [0, 1, 0], [Math.PI, 0, 0], null, 'bwd'], [new Line(lineGeometry, matLineGreen), null, [0, 0, Math.PI / 2]]], Z: [[new Mesh(arrowGeometry, matBlue), [0, 0, 1], [Math.PI / 2, 0, 0], null, 'fwd'], [new Mesh(arrowGeometry, matBlue), [0, 0, 1], [-Math.PI / 2, 0, 0], null, 'bwd'], [new Line(lineGeometry, matLineBlue), null, [0, -Math.PI / 2, 0]]], XYZ: [[new Mesh(new OctahedronBufferGeometry(0.1, 0), matWhiteTransparent.clone()), [0, 0, 0], [0, 0, 0]]], XY: [[new Mesh(new PlaneBufferGeometry(0.295, 0.295), matYellowTransparent.clone()), [0.15, 0.15, 0]], [new Line(lineGeometry, matLineYellow), [0.18, 0.3, 0], null, [0.125, 1, 1]], [new Line(lineGeometry, matLineYellow), [0.3, 0.18, 0], [0, 0, Math.PI / 2], [0.125, 1, 1]]], YZ: [[new Mesh(new PlaneBufferGeometry(0.295, 0.295), matCyanTransparent.clone()), [0, 0.15, 0.15], [0, Math.PI / 2, 0]], [new Line(lineGeometry, matLineCyan), [0, 0.18, 0.3], [0, 0, Math.PI / 2], [0.125, 1, 1]], [new Line(lineGeometry, matLineCyan), [0, 0.3, 0.18], [0, -Math.PI / 2, 0], [0.125, 1, 1]]], XZ: [[new Mesh(new PlaneBufferGeometry(0.295, 0.295), matMagentaTransparent.clone()), [0.15, 0, 0.15], [-Math.PI / 2, 0, 0]], [new Line(lineGeometry, matLineMagenta), [0.18, 0, 0.3], null, [0.125, 1, 1]], [new Line(lineGeometry, matLineMagenta), [0.3, 0, 0.18], [0, -Math.PI / 2, 0], [0.125, 1, 1]]] }; var pickerTranslate = { X: [[new Mesh(new CylinderBufferGeometry(0.2, 0, 1, 4, 1, false), matInvisible), [0.6, 0, 0], [0, 0, -Math.PI / 2]]], Y: [[new Mesh(new CylinderBufferGeometry(0.2, 0, 1, 4, 1, false), matInvisible), [0, 0.6, 0]]], Z: [[new Mesh(new CylinderBufferGeometry(0.2, 0, 1, 4, 1, false), matInvisible), [0, 0, 0.6], [Math.PI / 2, 0, 0]]], XYZ: [[new Mesh(new OctahedronBufferGeometry(0.2, 0), matInvisible)]], XY: [[new Mesh(new PlaneBufferGeometry(0.4, 0.4), matInvisible), [0.2, 0.2, 0]]], YZ: [[new Mesh(new PlaneBufferGeometry(0.4, 0.4), matInvisible), [0, 0.2, 0.2], [0, Math.PI / 2, 0]]], XZ: [[new Mesh(new PlaneBufferGeometry(0.4, 0.4), matInvisible), [0.2, 0, 0.2], [-Math.PI / 2, 0, 0]]] }; var helperTranslate = { START: [[new Mesh(new OctahedronBufferGeometry(0.01, 2), matHelper), null, null, null, 'helper']], END: [[new Mesh(new OctahedronBufferGeometry(0.01, 2), matHelper), null, null, null, 'helper']], DELTA: [[new Line(TranslateHelperGeometry(), matHelper), null, null, null, 'helper']], X: [[new Line(lineGeometry, matHelper.clone()), [-1e3, 0, 0], null, [1e6, 1, 1], 'helper']], Y: [[new Line(lineGeometry, matHelper.clone()), [0, -1e3, 0], [0, 0, Math.PI / 2], [1e6, 1, 1], 'helper']], Z: [[new Line(lineGeometry, matHelper.clone()), [0, 0, -1e3], [0, -Math.PI / 2, 0], [1e6, 1, 1], 'helper']] }; var gizmoRotate = { X: [[new Line(CircleGeometry(1, 0.5), matLineRed)], [new Mesh(new OctahedronBufferGeometry(0.04, 0), matRed), [0, 0, 0.99], null, [1, 3, 1]]], Y: [[new Line(CircleGeometry(1, 0.5), matLineGreen), null, [0, 0, -Math.PI / 2]], [new Mesh(new OctahedronBufferGeometry(0.04, 0), matGreen), [0, 0, 0.99], null, [3, 1, 1]]], Z: [[new Line(CircleGeometry(1, 0.5), matLineBlue), null, [0, Math.PI / 2, 0]], [new Mesh(new OctahedronBufferGeometry(0.04, 0), matBlue), [0.99, 0, 0], null, [1, 3, 1]]], E: [[new Line(CircleGeometry(1.25, 1), matLineYellowTransparent), null, [0, Math.PI / 2, 0]], [new Mesh(new CylinderBufferGeometry(0.03, 0, 0.15, 4, 1, false), matLineYellowTransparent), [1.17, 0, 0], [0, 0, -Math.PI / 2], [1, 1, 0.001]], [new Mesh(new CylinderBufferGeometry(0.03, 0, 0.15, 4, 1, false), matLineYellowTransparent), [-1.17, 0, 0], [0, 0, Math.PI / 2], [1, 1, 0.001]], [new Mesh(new CylinderBufferGeometry(0.03, 0, 0.15, 4, 1, false), matLineYellowTransparent), [0, -1.17, 0], [Math.PI, 0, 0], [1, 1, 0.001]], [new Mesh(new CylinderBufferGeometry(0.03, 0, 0.15, 4, 1, false), matLineYellowTransparent), [0, 1.17, 0], [0, 0, 0], [1, 1, 0.001]]], XYZE: [[new Line(CircleGeometry(1, 1), matLineGray), null, [0, Math.PI / 2, 0]]] }; var helperRotate = { AXIS: [[new Line(lineGeometry, matHelper.clone()), [-1e3, 0, 0], null, [1e6, 1, 1], 'helper']] }; var pickerRotate = { X: [[new Mesh(new TorusBufferGeometry(1, 0.1, 4, 24), matInvisible), [0, 0, 0], [0, -Math.PI / 2, -Math.PI / 2]]], Y: [[new Mesh(new TorusBufferGeometry(1, 0.1, 4, 24), matInvisible), [0, 0, 0], [Math.PI / 2, 0, 0]]], Z: [[new Mesh(new TorusBufferGeometry(1, 0.1, 4, 24), matInvisible), [0, 0, 0], [0, 0, -Math.PI / 2]]], E: [[new Mesh(new TorusBufferGeometry(1.25, 0.1, 2, 24), matInvisible)]], XYZE: [[new Mesh(new SphereBufferGeometry(0.7, 10, 8), matInvisible)]] }; var gizmoScale = { X: [[new Mesh(scaleHandleGeometry, matRed), [0.8, 0, 0], [0, 0, -Math.PI / 2]], [new Line(lineGeometry, matLineRed), null, null, [0.8, 1, 1]]], Y: [[new Mesh(scaleHandleGeometry, matGreen), [0, 0.8, 0]], [new Line(lineGeometry, matLineGreen), null, [0, 0, Math.PI / 2], [0.8, 1, 1]]], Z: [[new Mesh(scaleHandleGeometry, matBlue), [0, 0, 0.8], [Math.PI / 2, 0, 0]], [new Line(lineGeometry, matLineBlue), null, [0, -Math.PI / 2, 0], [0.8, 1, 1]]], XY: [[new Mesh(scaleHandleGeometry, matYellowTransparent), [0.85, 0.85, 0], null, [2, 2, 0.2]], [new Line(lineGeometry, matLineYellow), [0.855, 0.98, 0], null, [0.125, 1, 1]], [new Line(lineGeometry, matLineYellow), [0.98, 0.855, 0], [0, 0, Math.PI / 2], [0.125, 1, 1]]], YZ: [[new Mesh(scaleHandleGeometry, matCyanTransparent), [0, 0.85, 0.85], null, [0.2, 2, 2]], [new Line(lineGeometry, matLineCyan), [0, 0.855, 0.98], [0, 0, Math.PI / 2], [0.125, 1, 1]], [new Line(lineGeometry, matLineCyan), [0, 0.98, 0.855], [0, -Math.PI / 2, 0], [0.125, 1, 1]]], XZ: [[new Mesh(scaleHandleGeometry, matMagentaTransparent), [0.85, 0, 0.85], null, [2, 0.2, 2]], [new Line(lineGeometry, matLineMagenta), [0.855, 0, 0.98], null, [0.125, 1, 1]], [new Line(lineGeometry, matLineMagenta), [0.98, 0, 0.855], [0, -Math.PI / 2, 0], [0.125, 1, 1]]], XYZX: [[new Mesh(new BoxBufferGeometry(0.125, 0.125, 0.125), matWhiteTransparent.clone()), [1.1, 0, 0]]], XYZY: [[new Mesh(new BoxBufferGeometry(0.125, 0.125, 0.125), matWhiteTransparent.clone()), [0, 1.1, 0]]], XYZZ: [[new Mesh(new BoxBufferGeometry(0.125, 0.125, 0.125), matWhiteTransparent.clone()), [0, 0, 1.1]]] }; var pickerScale = { X: [[new Mesh(new CylinderBufferGeometry(0.2, 0, 0.8, 4, 1, false), matInvisible), [0.5, 0, 0], [0, 0, -Math.PI / 2]]], Y: [[new Mesh(new CylinderBufferGeometry(0.2, 0, 0.8, 4, 1, false), matInvisible), [0, 0.5, 0]]], Z: [[new Mesh(new CylinderBufferGeometry(0.2, 0, 0.8, 4, 1, false), matInvisible), [0, 0, 0.5], [Math.PI / 2, 0, 0]]], XY: [[new Mesh(scaleHandleGeometry, matInvisible), [0.85, 0.85, 0], null, [3, 3, 0.2]]], YZ: [[new Mesh(scaleHandleGeometry, matInvisible), [0, 0.85, 0.85], null, [0.2, 3, 3]]], XZ: [[new Mesh(scaleHandleGeometry, matInvisible), [0.85, 0, 0.85], null, [3, 0.2, 3]]], XYZX: [[new Mesh(new BoxBufferGeometry(0.2, 0.2, 0.2), matInvisible), [1.1, 0, 0]]], XYZY: [[new Mesh(new BoxBufferGeometry(0.2, 0.2, 0.2), matInvisible), [0, 1.1, 0]]], XYZZ: [[new Mesh(new BoxBufferGeometry(0.2, 0.2, 0.2), matInvisible), [0, 0, 1.1]]] }; var helperScale = { X: [[new Line(lineGeometry, matHelper.clone()), [-1e3, 0, 0], null, [1e6, 1, 1], 'helper']], Y: [[new Line(lineGeometry, matHelper.clone()), [0, -1e3, 0], [0, 0, Math.PI / 2], [1e6, 1, 1], 'helper']], Z: [[new Line(lineGeometry, matHelper.clone()), [0, 0, -1e3], [0, -Math.PI / 2, 0], [1e6, 1, 1], 'helper']] }; // Creates an Object3D with gizmos described in custom hierarchy definition. var setupGizmo = function setupGizmo(gizmoMap) { var gizmo = new Object3D(); for (var name in gizmoMap) { for (var i = gizmoMap[name].length; i--;) { var object = gizmoMap[name][i][0].clone(); var position = gizmoMap[name][i][1]; var rotation = gizmoMap[name][i][2]; var scale = gizmoMap[name][i][3]; var tag = gizmoMap[name][i][4]; // name and tag properties are essential for picking and updating logic. object.name = name; object.tag = tag; if (position) { object.position.set(position[0], position[1], position[2]); } if (rotation) { object.rotation.set(rotation[0], rotation[1], rotation[2]); } if (scale) { object.scale.set(scale[0], scale[1], scale[2]); } object.updateMatrix(); var tempGeometry = object.geometry.clone(); tempGeometry.applyMatrix4(object.matrix); object.geometry = tempGeometry; object.renderOrder = Infinity; object.position.set(0, 0, 0); object.rotation.set(0, 0, 0); object.scale.set(1, 1, 1); gizmo.add(object); } } return gizmo; }; // Reusable utility variables var tempVector = new Vector3(0, 0, 0); var tempEuler = new Euler(); var alignVector = new Vector3(0, 1, 0); var zeroVector = new Vector3(0, 0, 0); var lookAtMatrix = new Matrix4(); var tempQuaternion = new Quaternion(); var tempQuaternion2 = new Quaternion(); var identityQuaternion = new Quaternion(); var unitX = new Vector3(1, 0, 0); var unitY = new Vector3(0, 1, 0); var unitZ = new Vector3(0, 0, 1); // Gizmo creation this.gizmo = {}; this.picker = {}; this.helper = {}; this.add(this.gizmo["translate"] = setupGizmo(gizmoTranslate)); this.add(this.gizmo["rotate"] = setupGizmo(gizmoRotate)); this.add(this.gizmo["scale"] = setupGizmo(gizmoScale)); this.add(this.picker["translate"] = setupGizmo(pickerTranslate)); this.add(this.picker["rotate"] = setupGizmo(pickerRotate)); this.add(this.picker["scale"] = setupGizmo(pickerScale)); this.add(this.helper["translate"] = setupGizmo(helperTranslate)); this.add(this.helper["rotate"] = setupGizmo(helperRotate)); this.add(this.helper["scale"] = setupGizmo(helperScale)); // Pickers should be hidden always this.picker["translate"].visible = false; this.picker["rotate"].visible = false; this.picker["scale"].visible = false; // updateMatrixWorld will update transformations and appearance of individual handles this.updateMatrixWorld = function () { var space = this.space; if (this.mode === 'scale') space = 'local'; // scale always oriented to local rotation var quaternion = space === "local" ? this.worldQuaternion : identityQuaternion; // Show only gizmos for current transform mode this.gizmo["translate"].visible = this.mode === "translate"; this.gizmo["rotate"].visible = this.mode === "rotate"; this.gizmo["scale"].visible = this.mode === "scale"; this.helper["translate"].visible = this.mode === "translate"; this.helper["rotate"].visible = this.mode === "rotate"; this.helper["scale"].visible = this.mode === "scale"; var handles = []; handles = handles.concat(this.picker[this.mode].children); handles = handles.concat(this.gizmo[this.mode].children); handles = handles.concat(this.helper[this.mode].children); for (var i = 0; i < handles.length; i++) { var handle = handles[i]; // hide aligned to camera handle.visible = true; handle.rotation.set(0, 0, 0); handle.position.copy(this.worldPosition); var factor; if (this.camera.isOrthographicCamera) { factor = (this.camera.top - this.camera.bottom) / this.camera.zoom; } else { factor = this.worldPosition.distanceTo(this.cameraPosition) * Math.min(1.9 * Math.tan(Math.PI * this.camera.fov / 360) / this.camera.zoom, 7); } handle.scale.set(1, 1, 1).multiplyScalar(factor * this.size / 7); // TODO: simplify helpers and consider decoupling from gizmo if (handle.tag === 'helper') { handle.visible = false; if (handle.name === 'AXIS') { handle.position.copy(this.worldPositionStart); handle.visible = !!this.axis; if (this.axis === 'X') { tempQuaternion.setFromEuler(tempEuler.set(0, 0, 0)); handle.quaternion.copy(quaternion).multiply(tempQuaternion); if (Math.abs(alignVector.copy(unitX).applyQuaternion(quaternion).dot(this.eye)) > 0.9) { handle.visible = false; } } if (this.axis === 'Y') { tempQuaternion.setFromEuler(tempEuler.set(0, 0, Math.PI / 2)); handle.quaternion.copy(quaternion).multiply(tempQuaternion); if (Math.abs(alignVector.copy(unitY).applyQuaternion(quaternion).dot(this.eye)) > 0.9) { handle.visible = false; } } if (this.axis === 'Z') { tempQuaternion.setFromEuler(tempEuler.set(0, Math.PI / 2, 0)); handle.quaternion.copy(quaternion).multiply(tempQuaternion); if (Math.abs(alignVector.copy(unitZ).applyQuaternion(quaternion).dot(this.eye)) > 0.9) { handle.visible = false; } } if (this.axis === 'XYZE') { tempQuaternion.setFromEuler(tempEuler.set(0, Math.PI / 2, 0)); alignVector.copy(this.rotationAxis); handle.quaternion.setFromRotationMatrix(lookAtMatrix.lookAt(zeroVector, alignVector, unitY)); handle.quaternion.multiply(tempQuaternion); handle.visible = this.dragging; } if (this.axis === 'E') { handle.visible = false; } } else if (handle.name === 'START') { handle.position.copy(this.worldPositionStart); handle.visible = this.dragging; } else if (handle.name === 'END') { handle.position.copy(this.worldPosition); handle.visible = this.dragging; } else if (handle.name === 'DELTA') { handle.position.copy(this.worldPositionStart); handle.quaternion.copy(this.worldQuaternionStart); tempVector.set(1e-10, 1e-10, 1e-10).add(this.worldPositionStart).sub(this.worldPosition).multiplyScalar(-1); tempVector.applyQuaternion(this.worldQuaternionStart.clone().inverse()); handle.scale.copy(tempVector); handle.visible = this.dragging; } else { handle.quaternion.copy(quaternion); if (this.dragging) { handle.position.copy(this.worldPositionStart); } else { handle.position.copy(this.worldPosition); } if (this.axis) { handle.visible = this.axis.search(handle.name) !== -1; } } // If updating helper, skip rest of the loop continue; } // Align handles to current local or world rotation handle.quaternion.copy(quaternion); if (this.mode === 'translate' || this.mode === 'scale') { // Hide translate and scale axis facing the camera var AXIS_HIDE_TRESHOLD = 0.99; var PLANE_HIDE_TRESHOLD = 0.2; var AXIS_FLIP_TRESHOLD = 0.0; if (handle.name === 'X' || handle.name === 'XYZX') { if (Math.abs(alignVector.copy(unitX).applyQuaternion(quaternion).dot(this.eye)) > AXIS_HIDE_TRESHOLD) { handle.scale.set(1e-10, 1e-10, 1e-10); handle.visible = false; } } if (handle.name === 'Y' || handle.name === 'XYZY') { if (Math.abs(alignVector.copy(unitY).applyQuaternion(quaternion).dot(this.eye)) > AXIS_HIDE_TRESHOLD) { handle.scale.set(1e-10, 1e-10, 1e-10); handle.visible = false; } } if (handle.name === 'Z' || handle.name === 'XYZZ') { if (Math.abs(alignVector.copy(unitZ).applyQuaternion(quaternion).dot(this.eye)) > AXIS_HIDE_TRESHOLD) { handle.scale.set(1e-10, 1e-10, 1e-10); handle.visible = false; } } if (handle.name === 'XY') { if (Math.abs(alignVector.copy(unitZ).applyQuaternion(quaternion).dot(this.eye)) < PLANE_HIDE_TRESHOLD) { handle.scale.set(1e-10, 1e-10, 1e-10); handle.visible = false; } } if (handle.name === 'YZ') { if (Math.abs(alignVector.copy(unitX).applyQuaternion(quaternion).dot(this.eye)) < PLANE_HIDE_TRESHOLD) { handle.scale.set(1e-10, 1e-10, 1e-10); handle.visible = false; } } if (handle.name === 'XZ') { if (Math.abs(alignVector.copy(unitY).applyQuaternion(quaternion).dot(this.eye)) < PLANE_HIDE_TRESHOLD) { handle.scale.set(1e-10, 1e-10, 1e-10); handle.visible = false; } } // Flip translate and scale axis ocluded behind another axis if (handle.name.search('X') !== -1) { if (alignVector.copy(unitX).applyQuaternion(quaternion).dot(this.eye) < AXIS_FLIP_TRESHOLD) { if (handle.tag === 'fwd') { handle.visible = false; } else { handle.scale.x *= -1; } } else if (handle.tag === 'bwd') { handle.visible = false; } } if (handle.name.search('Y') !== -1) { if (alignVector.copy(unitY).applyQuaternion(quaternion).dot(this.eye) < AXIS_FLIP_TRESHOLD) { if (handle.tag === 'fwd') { handle.visible = false; } else { handle.scale.y *= -1; } } else if (handle.tag === 'bwd') { handle.visible = false; } } if (handle.name.search('Z') !== -1) { if (alignVector.copy(unitZ).applyQuaternion(quaternion).dot(this.eye) < AXIS_FLIP_TRESHOLD) { if (handle.tag === 'fwd') { handle.visible = false; } else { handle.scale.z *= -1; } } else if (handle.tag === 'bwd') { handle.visible = false; } } } else if (this.mode === 'rotate') { // Align handles to current local or world rotation tempQuaternion2.copy(quaternion); alignVector.copy(this.eye).applyQuaternion(tempQuaternion.copy(quaternion).inverse()); if (handle.name.search("E") !== -1) { handle.quaternion.setFromRotationMatrix(lookAtMatrix.lookAt(this.eye, zeroVector, unitY)); } if (handle.name === 'X') { tempQuaternion.setFromAxisAngle(unitX, Math.atan2(-alignVector.y, alignVector.z)); tempQuaternion.multiplyQuaternions(tempQuaternion2, tempQuaternion); handle.quaternion.copy(tempQuaternion); } if (handle.name === 'Y') { tempQuaternion.setFromAxisAngle(unitY, Math.atan2(alignVector.x, alignVector.z)); tempQuaternion.multiplyQuaternions(tempQuaternion2, tempQuaternion); handle.quaternion.copy(tempQuaternion); } if (handle.name === 'Z') { tempQuaternion.setFromAxisAngle(unitZ, Math.atan2(alignVector.y, alignVector.x)); tempQuaternion.multiplyQuaternions(tempQuaternion2, tempQuaternion); handle.quaternion.copy(tempQuaternion); } } // Hide disabled axes handle.visible = handle.visible && (handle.name.indexOf("X") === -1 || this.showX); handle.visible = handle.visible && (handle.name.indexOf("Y") === -1 || this.showY); handle.visible = handle.visible && (handle.name.indexOf("Z") === -1 || this.showZ); handle.visible = handle.visible && (handle.name.indexOf("E") === -1 || this.showX && this.showY && this.showZ); // highlight selected axis handle.material._opacity = handle.material._opacity || handle.material.opacity; handle.material._color = handle.material._color || handle.material.color.clone(); handle.material.color.copy(handle.material._color); handle.material.opacity = handle.material._opacity; if (!this.enabled) { handle.material.opacity *= 0.5; handle.material.color.lerp(new Color(1, 1, 1), 0.5); } else if (this.axis) { if (handle.name === this.axis) { handle.material.opacity = 1.0; handle.material.color.lerp(new Color(1, 1, 1), 0.5); } else if (this.axis.split('').some(function (a) { return handle.name === a; })) { handle.material.opacity = 1.0; handle.material.color.lerp(new Color(1, 1, 1), 0.5); } else { handle.material.opacity *= 0.25; handle.material.color.lerp(new Color(1, 1, 1), 0.5); } } } Object3D.prototype.updateMatrixWorld.call(this); }; }; TransformControlsGizmo.prototype = _Object$assign(_Object$create(Object3D.prototype), { constructor: TransformControlsGizmo, isTransformControlsGizmo: true }); var TransformControlsPlane = function TransformControlsPlane() { 'use strict'; Mesh.call(this, new PlaneBufferGeometry(100000, 100000, 2, 2), new MeshBasicMaterial({ visible: false, wireframe: true, side: DoubleSide, transparent: true, opacity: 0.1, toneMapped: false })); this.type = 'TransformControlsPlane'; var unitX = new Vector3(1, 0, 0); var unitY = new Vector3(0, 1, 0); var unitZ = new Vector3(0, 0, 1); var tempVector = new Vector3(); var dirVector = new Vector3(); var alignVector = new Vector3(); var tempMatrix = new Matrix4(); var identityQuaternion = new Quaternion(); this.updateMatrixWorld = function () { var space = this.space; this.position.copy(this.worldPosition); if (this.mode === 'scale') space = 'local'; // scale always oriented to local rotation unitX.set(1, 0, 0).applyQuaternion(space === "local" ? this.worldQuaternion : identityQuaternion); unitY.set(0, 1, 0).applyQuaternion(space === "local" ? this.worldQuaternion : identityQuaternion); unitZ.set(0, 0, 1).applyQuaternion(space === "local" ? this.worldQuaternion : identityQuaternion); // Align the plane for current transform mode, axis and space. alignVector.copy(unitY); switch (this.mode) { case 'translate': case 'scale': switch (this.axis) { case 'X': alignVector.copy(this.eye).cross(unitX); dirVector.copy(unitX).cross(alignVector); break; case 'Y': alignVector.copy(this.eye).cross(unitY); dirVector.copy(unitY).cross(alignVector); break; case 'Z': alignVector.copy(this.eye).cross(unitZ); dirVector.copy(unitZ).cross(alignVector); break; case 'XY': dirVector.copy(unitZ); break; case 'YZ': dirVector.copy(unitX); break; case 'XZ': alignVector.copy(unitZ); dirVector.copy(unitY); break; case 'XYZ': case 'E': dirVector.set(0, 0, 0); break; } break; case 'rotate': default: // special case for rotate dirVector.set(0, 0, 0); } if (dirVector.length() === 0) { // If in rotate mode, make the plane parallel to camera this.quaternion.copy(this.cameraQuaternion); } else { tempMatrix.lookAt(tempVector.set(0, 0, 0), dirVector, alignVector); this.quaternion.setFromRotationMatrix(tempMatrix); } Object3D.prototype.updateMatrixWorld.call(this); }; }; TransformControlsPlane.prototype = _Object$assign(_Object$create(Mesh.prototype), { constructor: TransformControlsPlane, isTransformControlsPlane: true }); export { TransformControls, TransformControlsGizmo, TransformControlsPlane };