diff --git a/public/preview0.png b/public/preview0.png
new file mode 100644
index 0000000..4936177
Binary files /dev/null and b/public/preview0.png differ
diff --git a/public/preview1.png b/public/preview1.png
new file mode 100644
index 0000000..a2c1e78
Binary files /dev/null and b/public/preview1.png differ
diff --git a/public/preview2.png b/public/preview2.png
new file mode 100644
index 0000000..40be716
Binary files /dev/null and b/public/preview2.png differ
diff --git a/public/voxeltest.html b/public/voxeltest.html
deleted file mode 100644
index e98022e..0000000
--- a/public/voxeltest.html
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
+
{notification}
);
diff --git a/src/components/UI.jsx b/src/components/UI.jsx
new file mode 100644
index 0000000..653790c
--- /dev/null
+++ b/src/components/UI.jsx
@@ -0,0 +1,42 @@
+/**
+ *
+ * @flow
+ */
+
+import React from 'react';
+import { connect } from 'react-redux';
+
+import type { State } from '../reducers';
+import CoolDownBox from './CoolDownBox';
+import NotifyBox from './NotifyBox';
+import GlobeButton from './GlobeButton';
+import PalselButton from './PalselButton';
+import Palette from './Palette';
+import HistorySelect from './HistorySelect';
+
+
+const UI = ({ isHistoricalView }) => {
+ if (isHistoricalView) {
+ return
;
+ }
+ return (
+
+ );
+};
+
+function mapStateToProps(state: State) {
+ const {
+ isHistoricalView,
+ } = state.canvas;
+ return {
+ isHistoricalView,
+ };
+}
+
+export default connect(mapStateToProps)(UI);
diff --git a/src/components/VoxelButton.jsx b/src/components/VoxelButton.jsx
deleted file mode 100644
index a27182e..0000000
--- a/src/components/VoxelButton.jsx
+++ /dev/null
@@ -1,24 +0,0 @@
-/**
- *
- * @flow
- */
-
-import React from 'react';
-import { connect } from 'react-redux';
-import type { State } from '../reducers';
-
-async function switchVoxel() {
- await import(/* webpackChunkName: "voxel" */ '../voxel');
- console.log("Chunk voxel loaded");
-}
-
-
-const VoxelButton = ({
- canvasId, canvasIdent, canvasSize, view,
-}) => (
-
- ♠
-
-);
-
-export default VoxelButton;
diff --git a/src/controls/OrbitControls.js b/src/controls/OrbitControls.js
deleted file mode 100644
index 0892af1..0000000
--- a/src/controls/OrbitControls.js
+++ /dev/null
@@ -1,1164 +0,0 @@
-/**
- * @author qiao / https://github.com/qiao
- * @author mrdoob / http://mrdoob.com
- * @author alteredq / http://alteredqualia.com/
- * @author WestLangley / http://github.com/WestLangley
- * @author erich666 / http://erichaines.com
- * @author ScieCode / http://github.com/sciecode
- */
-
-import {
- EventDispatcher,
- MOUSE,
- Quaternion,
- Spherical,
- TOUCH,
- Vector2,
- Vector3
-} from "three";
-
-// This set of controls performs orbiting, dollying (zooming), and panning.
-// Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default).
-//
-// Orbit - left mouse / touch: one-finger move
-// Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish
-// Pan - right mouse, or left mouse + ctrl/meta/shiftKey, or arrow keys / touch: two-finger move
-
-var OrbitControls = function ( object, domElement ) {
-
- if ( domElement === undefined ) console.warn( 'THREE.OrbitControls: The second parameter "domElement" is now mandatory.' );
- if ( domElement === document ) console.error( 'THREE.OrbitControls: "document" should not be used as the target "domElement". Please use "renderer.domElement" instead.' );
-
- this.object = object;
- this.domElement = domElement;
-
- // Set to false to disable this control
- this.enabled = true;
-
- // "target" sets the location of focus, where the object orbits around
- this.target = new Vector3();
-
- // How far you can dolly in and out ( PerspectiveCamera only )
- this.minDistance = 0;
- this.maxDistance = Infinity;
-
- // How far you can zoom in and out ( OrthographicCamera only )
- this.minZoom = 0;
- this.maxZoom = Infinity;
-
- // How far you can orbit vertically, upper and lower limits.
- // Range is 0 to Math.PI radians.
- this.minPolarAngle = 0; // radians
- this.maxPolarAngle = Math.PI; // radians
-
- // How far you can orbit horizontally, upper and lower limits.
- // If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ].
- this.minAzimuthAngle = - Infinity; // radians
- this.maxAzimuthAngle = Infinity; // radians
-
- // Set to true to enable damping (inertia)
- // If damping is enabled, you must call controls.update() in your animation loop
- this.enableDamping = false;
- this.dampingFactor = 0.05;
-
- // This option actually enables dollying in and out; left as "zoom" for backwards compatibility.
- // Set to false to disable zooming
- this.enableZoom = true;
- this.zoomSpeed = 1.0;
-
- // Set to false to disable rotating
- this.enableRotate = true;
- this.rotateSpeed = 1.0;
-
- // Set to false to disable panning
- this.enablePan = true;
- this.panSpeed = 1.0;
- this.screenSpacePanning = true; // if true, pan in screen-space
- this.keyPanSpeed = 20.0; // pixels moved per arrow key push
-
- // Set to true to automatically rotate around the target
- // If auto-rotate is enabled, you must call controls.update() in your animation loop
- this.autoRotate = false;
- this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60
-
- // Set to false to disable use of the keys
- this.enableKeys = true;
-
- // The four arrow keys
- this.keys = { LEFT: 65, FORWARD: 87, RIGHT: 68, BACKWARD: 83 };
-
- // Mouse buttons
- this.mouseButtons = { LEFT: MOUSE.ROTATE, MIDDLE: MOUSE.DOLLY, RIGHT: MOUSE.PAN };
-
- // Touch fingers
- this.touches = { ONE: TOUCH.ROTATE, TWO: TOUCH.DOLLY_PAN };
-
- // for reset
- this.target0 = this.target.clone();
- this.position0 = this.object.position.clone();
- this.zoom0 = this.object.zoom;
-
- //
- // public methods
- //
-
- this.getPolarAngle = function () {
-
- return spherical.phi;
-
- };
-
- this.getAzimuthalAngle = function () {
-
- return spherical.theta;
-
- };
-
- this.saveState = function () {
-
- scope.target0.copy( scope.target );
- scope.position0.copy( scope.object.position );
- scope.zoom0 = scope.object.zoom;
-
- };
-
- this.reset = function () {
-
- scope.target.copy( scope.target0 );
- scope.object.position.copy( scope.position0 );
- scope.object.zoom = scope.zoom0;
-
- scope.object.updateProjectionMatrix();
- scope.dispatchEvent( changeEvent );
-
- scope.update();
-
- state = STATE.NONE;
-
- };
-
- // this method is exposed, but perhaps it would be better if we can make it private...
- this.update = function () {
-
- var offset = new Vector3();
-
- // so camera.up is the orbit axis
- var quat = new Quaternion().setFromUnitVectors( object.up, new Vector3( 0, 1, 0 ) );
- var quatInverse = quat.clone().inverse();
-
- var lastPosition = new Vector3();
- var lastQuaternion = new Quaternion();
-
- return function update() {
-
- var position = scope.object.position;
-
- offset.copy( position ).sub( scope.target );
-
- // rotate offset to "y-axis-is-up" space
- offset.applyQuaternion( quat );
-
- // angle from z-axis around y-axis
- spherical.setFromVector3( offset );
-
- if ( scope.autoRotate && state === STATE.NONE ) {
-
- rotateLeft( getAutoRotationAngle() );
-
- }
-
- if ( scope.enableDamping ) {
-
- spherical.theta += sphericalDelta.theta * scope.dampingFactor;
- spherical.phi += sphericalDelta.phi * scope.dampingFactor;
-
- } else {
-
- spherical.theta += sphericalDelta.theta;
- spherical.phi += sphericalDelta.phi;
-
- }
-
- // restrict theta to be between desired limits
- spherical.theta = Math.max( scope.minAzimuthAngle, Math.min( scope.maxAzimuthAngle, spherical.theta ) );
-
- // restrict phi to be between desired limits
- spherical.phi = Math.max( scope.minPolarAngle, Math.min( scope.maxPolarAngle, spherical.phi ) );
-
- spherical.makeSafe();
-
-
- spherical.radius *= scale;
-
- // restrict radius to be between desired limits
- spherical.radius = Math.max( scope.minDistance, Math.min( scope.maxDistance, spherical.radius ) );
-
- // move target to panned location
-
- if ( scope.enableDamping === true ) {
-
- scope.target.addScaledVector( panOffset, scope.dampingFactor );
-
- } else {
-
- scope.target.add( panOffset );
-
- }
-
- offset.setFromSpherical( spherical );
-
- // rotate offset back to "camera-up-vector-is-up" space
- offset.applyQuaternion( quatInverse );
-
- position.copy( scope.target ).add( offset );
-
- scope.object.lookAt( scope.target );
-
- if ( scope.enableDamping === true ) {
-
- sphericalDelta.theta *= ( 1 - scope.dampingFactor );
- sphericalDelta.phi *= ( 1 - scope.dampingFactor );
-
- panOffset.multiplyScalar( 1 - scope.dampingFactor );
-
- } else {
-
- sphericalDelta.set( 0, 0, 0 );
-
- panOffset.set( 0, 0, 0 );
-
- }
-
- scale = 1;
-
- // update condition is:
- // min(camera displacement, camera rotation in radians)^2 > EPS
- // using small-angle approximation cos(x/2) = 1 - x^2 / 8
-
- if ( zoomChanged ||
- lastPosition.distanceToSquared( scope.object.position ) > EPS ||
- 8 * ( 1 - lastQuaternion.dot( scope.object.quaternion ) ) > EPS ) {
-
- scope.dispatchEvent( changeEvent );
-
- lastPosition.copy( scope.object.position );
- lastQuaternion.copy( scope.object.quaternion );
- zoomChanged = false;
-
- return true;
-
- }
-
- return false;
-
- };
-
- }();
-
- this.dispose = function () {
-
- scope.domElement.removeEventListener( 'contextmenu', onContextMenu, false );
- scope.domElement.removeEventListener( 'mousedown', onMouseDown, false );
- scope.domElement.removeEventListener( 'wheel', onMouseWheel, false );
-
- scope.domElement.removeEventListener( 'touchstart', onTouchStart, false );
- scope.domElement.removeEventListener( 'touchend', onTouchEnd, false );
- scope.domElement.removeEventListener( 'touchmove', onTouchMove, false );
-
- document.removeEventListener( 'mousemove', onMouseMove, false );
- document.removeEventListener( 'mouseup', onMouseUp, false );
-
- scope.domElement.removeEventListener( 'keydown', onKeyDown, false );
-
- //scope.dispatchEvent( { type: 'dispose' } ); // should this be added here?
-
- };
-
- //
- // internals
- //
-
- var scope = this;
-
- var changeEvent = { type: 'change' };
- var startEvent = { type: 'start' };
- var endEvent = { type: 'end' };
-
- var STATE = {
- NONE: - 1,
- ROTATE: 0,
- DOLLY: 1,
- PAN: 2,
- TOUCH_ROTATE: 3,
- TOUCH_PAN: 4,
- TOUCH_DOLLY_PAN: 5,
- TOUCH_DOLLY_ROTATE: 6
- };
-
- var state = STATE.NONE;
-
- var EPS = 0.000001;
-
- // current position in spherical coordinates
- var spherical = new Spherical();
- var sphericalDelta = new Spherical();
-
- var scale = 1;
- var panOffset = new Vector3();
- var zoomChanged = false;
-
- var rotateStart = new Vector2();
- var rotateEnd = new Vector2();
- var rotateDelta = new Vector2();
-
- var panStart = new Vector2();
- var panEnd = new Vector2();
- var panDelta = new Vector2();
-
- var dollyStart = new Vector2();
- var dollyEnd = new Vector2();
- var dollyDelta = new Vector2();
-
- function getAutoRotationAngle() {
-
- return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
-
- }
-
- function getZoomScale() {
-
- return Math.pow( 0.95, scope.zoomSpeed );
-
- }
-
- function rotateLeft( angle ) {
-
- sphericalDelta.theta -= angle;
-
- }
-
- function rotateUp( angle ) {
-
- sphericalDelta.phi -= angle;
-
- }
-
- var panLeft = function () {
-
- var v = new Vector3();
-
- return function panLeft( distance, objectMatrix ) {
-
- v.setFromMatrixColumn( objectMatrix, 0 ); // get X column of objectMatrix
- v.multiplyScalar( - distance );
-
- panOffset.add( v );
-
- };
-
- }();
-
- var panUp = function () {
-
- var v = new Vector3();
-
- return function panUp( distance, objectMatrix ) {
-
- if ( scope.screenSpacePanning === true ) {
-
- v.setFromMatrixColumn( objectMatrix, 1 );
-
- } else {
-
- v.setFromMatrixColumn( objectMatrix, 0 );
- v.crossVectors( scope.object.up, v );
-
- }
-
- v.multiplyScalar( distance );
-
- //panOffset.add( v );
- scope.object.z += 50;
-
- };
-
- }();
-
- // deltaX and deltaY are in pixels; right and down are positive
- var pan = function () {
-
- var offset = new Vector3();
-
- return function pan( deltaX, deltaY ) {
-
- var element = scope.domElement;
-
- if ( scope.object.isPerspectiveCamera ) {
-
- // perspective
- var position = scope.object.position;
- offset.copy( position ).sub( scope.target );
- var targetDistance = offset.length();
-
- // half of the fov is center to top of screen
- targetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 );
-
- // we use only clientHeight here so aspect ratio does not distort speed
- panLeft( 2 * deltaX * targetDistance / element.clientHeight, scope.object.matrix );
- panUp( 2 * deltaY * targetDistance / element.clientHeight, scope.object.matrix );
-
- } else if ( scope.object.isOrthographicCamera ) {
-
- // orthographic
- panLeft( deltaX * ( scope.object.right - scope.object.left ) / scope.object.zoom / element.clientWidth, scope.object.matrix );
- panUp( deltaY * ( scope.object.top - scope.object.bottom ) / scope.object.zoom / element.clientHeight, scope.object.matrix );
-
- } else {
-
- // camera neither orthographic nor perspective
- console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' );
- scope.enablePan = false;
-
- }
-
- };
-
- }();
-
- function dollyIn( dollyScale ) {
-
- if ( scope.object.isPerspectiveCamera ) {
-
- scale /= dollyScale;
-
- } else if ( scope.object.isOrthographicCamera ) {
-
- scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom * dollyScale ) );
- scope.object.updateProjectionMatrix();
- zoomChanged = true;
-
- } else {
-
- console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' );
- scope.enableZoom = false;
-
- }
-
- }
-
- function dollyOut( dollyScale ) {
-
- if ( scope.object.isPerspectiveCamera ) {
-
- scale *= dollyScale;
-
- } else if ( scope.object.isOrthographicCamera ) {
-
- scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom / dollyScale ) );
- scope.object.updateProjectionMatrix();
- zoomChanged = true;
-
- } else {
-
- console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' );
- scope.enableZoom = false;
-
- }
-
- }
-
- //
- // event callbacks - update the object state
- //
-
- function handleMouseDownRotate( event ) {
-
- rotateStart.set( event.clientX, event.clientY );
-
- }
-
- function handleMouseDownDolly( event ) {
-
- dollyStart.set( event.clientX, event.clientY );
-
- }
-
- function handleMouseDownPan( event ) {
-
- panStart.set( event.clientX, event.clientY );
-
- }
-
- function handleMouseMoveRotate( event ) {
-
- rotateEnd.set( event.clientX, event.clientY );
-
- rotateDelta.subVectors( rotateEnd, rotateStart ).multiplyScalar( scope.rotateSpeed );
-
- var element = scope.domElement;
-
- rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientHeight ); // yes, height
-
- rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight );
-
- rotateStart.copy( rotateEnd );
-
- scope.update();
-
- }
-
- function handleMouseMoveDolly( event ) {
-
- dollyEnd.set( event.clientX, event.clientY );
-
- dollyDelta.subVectors( dollyEnd, dollyStart );
-
- if ( dollyDelta.y > 0 ) {
-
- dollyIn( getZoomScale() );
-
- } else if ( dollyDelta.y < 0 ) {
-
- dollyOut( getZoomScale() );
-
- }
-
- dollyStart.copy( dollyEnd );
-
- scope.update();
-
- }
-
- function handleMouseMovePan( event ) {
-
- panEnd.set( event.clientX, event.clientY );
-
- panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed );
-
- pan( panDelta.x, panDelta.y );
-
- panStart.copy( panEnd );
-
- scope.update();
-
- }
-
- function handleMouseUp( /*event*/ ) {
-
- // no-op
-
- }
-
- function handleMouseWheel( event ) {
-
- if ( event.deltaY < 0 ) {
-
- dollyOut( getZoomScale() );
-
- } else if ( event.deltaY > 0 ) {
-
- dollyIn( getZoomScale() );
-
- }
-
- scope.update();
-
- }
-
- function handleKeyDown( event ) {
-
- var needsUpdate = false;
- console.log(event.keyCode);
-
- switch ( event.keyCode ) {
-
- case scope.keys.FORWARD:
- pan( 0, scope.keyPanSpeed );
- needsUpdate = true;
- break;
-
- case scope.keys.BACKWARD:
- pan( 0, - scope.keyPanSpeed );
- needsUpdate = true;
- break;
-
- case scope.keys.LEFT:
- pan( scope.keyPanSpeed, 0 );
- needsUpdate = true;
- break;
-
- case scope.keys.RIGHT:
- pan( - scope.keyPanSpeed, 0 );
- needsUpdate = true;
- break;
-
- }
-
- if ( needsUpdate ) {
-
- // prevent the browser from scrolling on cursor keys
- event.preventDefault();
-
- scope.update();
-
- }
-
-
- }
-
- function handleTouchStartRotate( event ) {
-
- if ( event.touches.length == 1 ) {
-
- rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
-
- } else {
-
- var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX );
- var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY );
-
- rotateStart.set( x, y );
-
- }
-
- }
-
- function handleTouchStartPan( event ) {
-
- if ( event.touches.length == 1 ) {
-
- panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
-
- } else {
-
- var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX );
- var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY );
-
- panStart.set( x, y );
-
- }
-
- }
-
- function handleTouchStartDolly( event ) {
-
- var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
- var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
-
- var distance = Math.sqrt( dx * dx + dy * dy );
-
- dollyStart.set( 0, distance );
-
- }
-
- function handleTouchStartDollyPan( event ) {
-
- if ( scope.enableZoom ) handleTouchStartDolly( event );
-
- if ( scope.enablePan ) handleTouchStartPan( event );
-
- }
-
- function handleTouchStartDollyRotate( event ) {
-
- if ( scope.enableZoom ) handleTouchStartDolly( event );
-
- if ( scope.enableRotate ) handleTouchStartRotate( event );
-
- }
-
- function handleTouchMoveRotate( event ) {
-
- if ( event.touches.length == 1 ) {
-
- rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
-
- } else {
-
- var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX );
- var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY );
-
- rotateEnd.set( x, y );
-
- }
-
- rotateDelta.subVectors( rotateEnd, rotateStart ).multiplyScalar( scope.rotateSpeed );
-
- var element = scope.domElement;
-
- rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientHeight ); // yes, height
-
- rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight );
-
- rotateStart.copy( rotateEnd );
-
- }
-
- function handleTouchMovePan( event ) {
-
- if ( event.touches.length == 1 ) {
-
- panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
-
- } else {
-
- var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX );
- var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY );
-
- panEnd.set( x, y );
-
- }
-
- panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed );
-
- pan( panDelta.x, panDelta.y );
-
- panStart.copy( panEnd );
-
- }
-
- function handleTouchMoveDolly( event ) {
-
- var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
- var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
-
- var distance = Math.sqrt( dx * dx + dy * dy );
-
- dollyEnd.set( 0, distance );
-
- dollyDelta.set( 0, Math.pow( dollyEnd.y / dollyStart.y, scope.zoomSpeed ) );
-
- dollyIn( dollyDelta.y );
-
- dollyStart.copy( dollyEnd );
-
- }
-
- function handleTouchMoveDollyPan( event ) {
-
- if ( scope.enableZoom ) handleTouchMoveDolly( event );
-
- if ( scope.enablePan ) handleTouchMovePan( event );
-
- }
-
- function handleTouchMoveDollyRotate( event ) {
-
- if ( scope.enableZoom ) handleTouchMoveDolly( event );
-
- if ( scope.enableRotate ) handleTouchMoveRotate( event );
-
- }
-
- function handleTouchEnd( /*event*/ ) {
-
- // no-op
-
- }
-
- //
- // event handlers - FSM: listen for events and reset state
- //
-
- function onMouseDown( event ) {
-
- if ( scope.enabled === false ) return;
-
- // Prevent the browser from scrolling.
- event.preventDefault();
-
- // Manually set the focus since calling preventDefault above
- // prevents the browser from setting it automatically.
-
- scope.domElement.focus ? scope.domElement.focus() : window.focus();
-
- var mouseAction;
-
- switch ( event.button ) {
-
- case 0:
-
- mouseAction = scope.mouseButtons.LEFT;
- break;
-
- case 1:
-
- mouseAction = scope.mouseButtons.MIDDLE;
- break;
-
- case 2:
-
- mouseAction = scope.mouseButtons.RIGHT;
- break;
-
- default:
-
- mouseAction = - 1;
-
- }
-
- switch ( mouseAction ) {
-
- case MOUSE.DOLLY:
-
- if ( scope.enableZoom === false ) return;
-
- handleMouseDownDolly( event );
-
- state = STATE.DOLLY;
-
- break;
-
- case MOUSE.ROTATE:
-
- if ( event.ctrlKey || event.metaKey || event.shiftKey ) {
-
- if ( scope.enablePan === false ) return;
-
- handleMouseDownPan( event );
-
- state = STATE.PAN;
-
- } else {
-
- if ( scope.enableRotate === false ) return;
-
- handleMouseDownRotate( event );
-
- state = STATE.ROTATE;
-
- }
-
- break;
-
- case MOUSE.PAN:
-
- if ( event.ctrlKey || event.metaKey || event.shiftKey ) {
-
- if ( scope.enableRotate === false ) return;
-
- handleMouseDownRotate( event );
-
- state = STATE.ROTATE;
-
- } else {
-
- if ( scope.enablePan === false ) return;
-
- handleMouseDownPan( event );
-
- state = STATE.PAN;
-
- }
-
- break;
-
- default:
-
- state = STATE.NONE;
-
- }
-
- if ( state !== STATE.NONE ) {
-
- document.addEventListener( 'mousemove', onMouseMove, false );
- document.addEventListener( 'mouseup', onMouseUp, false );
-
- scope.dispatchEvent( startEvent );
-
- }
-
- }
-
- function onMouseMove( event ) {
-
- if ( scope.enabled === false ) return;
-
- event.preventDefault();
-
- switch ( state ) {
-
- case STATE.ROTATE:
-
- if ( scope.enableRotate === false ) return;
-
- handleMouseMoveRotate( event );
-
- break;
-
- case STATE.DOLLY:
-
- if ( scope.enableZoom === false ) return;
-
- handleMouseMoveDolly( event );
-
- break;
-
- case STATE.PAN:
-
- if ( scope.enablePan === false ) return;
-
- handleMouseMovePan( event );
-
- break;
-
- }
-
- }
-
- function onMouseUp( event ) {
-
- if ( scope.enabled === false ) return;
-
- handleMouseUp( event );
-
- document.removeEventListener( 'mousemove', onMouseMove, false );
- document.removeEventListener( 'mouseup', onMouseUp, false );
-
- scope.dispatchEvent( endEvent );
-
- state = STATE.NONE;
-
- }
-
- function onMouseWheel( event ) {
-
- if ( scope.enabled === false || scope.enableZoom === false || ( state !== STATE.NONE && state !== STATE.ROTATE ) ) return;
-
- event.preventDefault();
- event.stopPropagation();
-
- scope.dispatchEvent( startEvent );
-
- handleMouseWheel( event );
-
- scope.dispatchEvent( endEvent );
-
- }
-
- function onKeyDown( event ) {
-
- if ( scope.enabled === false || scope.enableKeys === false || scope.enablePan === false ) return;
-
- handleKeyDown( event );
-
- }
-
- function onTouchStart( event ) {
-
- if ( scope.enabled === false ) return;
-
- event.preventDefault();
-
- switch ( event.touches.length ) {
-
- case 1:
-
- switch ( scope.touches.ONE ) {
-
- case TOUCH.ROTATE:
-
- if ( scope.enableRotate === false ) return;
-
- handleTouchStartRotate( event );
-
- state = STATE.TOUCH_ROTATE;
-
- break;
-
- case TOUCH.PAN:
-
- if ( scope.enablePan === false ) return;
-
- handleTouchStartPan( event );
-
- state = STATE.TOUCH_PAN;
-
- break;
-
- default:
-
- state = STATE.NONE;
-
- }
-
- break;
-
- case 2:
-
- switch ( scope.touches.TWO ) {
-
- case TOUCH.DOLLY_PAN:
-
- if ( scope.enableZoom === false && scope.enablePan === false ) return;
-
- handleTouchStartDollyPan( event );
-
- state = STATE.TOUCH_DOLLY_PAN;
-
- break;
-
- case TOUCH.DOLLY_ROTATE:
-
- if ( scope.enableZoom === false && scope.enableRotate === false ) return;
-
- handleTouchStartDollyRotate( event );
-
- state = STATE.TOUCH_DOLLY_ROTATE;
-
- break;
-
- default:
-
- state = STATE.NONE;
-
- }
-
- break;
-
- default:
-
- state = STATE.NONE;
-
- }
-
- if ( state !== STATE.NONE ) {
-
- scope.dispatchEvent( startEvent );
-
- }
-
- }
-
- function onTouchMove( event ) {
-
- if ( scope.enabled === false ) return;
-
- event.preventDefault();
- event.stopPropagation();
-
- switch ( state ) {
-
- case STATE.TOUCH_ROTATE:
-
- if ( scope.enableRotate === false ) return;
-
- handleTouchMoveRotate( event );
-
- scope.update();
-
- break;
-
- case STATE.TOUCH_PAN:
-
- if ( scope.enablePan === false ) return;
-
- handleTouchMovePan( event );
-
- scope.update();
-
- break;
-
- case STATE.TOUCH_DOLLY_PAN:
-
- if ( scope.enableZoom === false && scope.enablePan === false ) return;
-
- handleTouchMoveDollyPan( event );
-
- scope.update();
-
- break;
-
- case STATE.TOUCH_DOLLY_ROTATE:
-
- if ( scope.enableZoom === false && scope.enableRotate === false ) return;
-
- handleTouchMoveDollyRotate( event );
-
- scope.update();
-
- break;
-
- default:
-
- state = STATE.NONE;
-
- }
-
- }
-
- function onTouchEnd( event ) {
-
- if ( scope.enabled === false ) return;
-
- handleTouchEnd( event );
-
- scope.dispatchEvent( endEvent );
-
- state = STATE.NONE;
-
- }
-
- function onContextMenu( event ) {
-
- if ( scope.enabled === false ) return;
-
- event.preventDefault();
-
- }
-
- //
-
- scope.domElement.addEventListener( 'contextmenu', onContextMenu, false );
-
- scope.domElement.addEventListener( 'mousedown', onMouseDown, false );
- scope.domElement.addEventListener( 'wheel', onMouseWheel, false );
-
- scope.domElement.addEventListener( 'touchstart', onTouchStart, false );
- scope.domElement.addEventListener( 'touchend', onTouchEnd, false );
- scope.domElement.addEventListener( 'touchmove', onTouchMove, false );
-
- scope.domElement.addEventListener( 'keydown', onKeyDown, false );
-
- // make sure element can receive keys.
-
- if ( scope.domElement.tabIndex === - 1 ) {
-
- scope.domElement.tabIndex = 0;
-
- }
-
- // force an update at start
-
- this.update();
-
-};
-
-OrbitControls.prototype = Object.create( EventDispatcher.prototype );
-OrbitControls.prototype.constructor = OrbitControls;
-
-
-// This set of controls performs orbiting, dollying (zooming), and panning.
-// Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default).
-// This is very similar to OrbitControls, another set of touch behavior
-//
-// Orbit - right mouse, or left mouse + ctrl/meta/shiftKey / touch: two-finger rotate
-// Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish
-// Pan - left mouse, or arrow keys / touch: one-finger move
-
-var MapControls = function ( object, domElement ) {
-
- OrbitControls.call( this, object, domElement );
-
- this.mouseButtons.LEFT = MOUSE.PAN;
- this.mouseButtons.RIGHT = MOUSE.ROTATE;
-
- this.touches.ONE = TOUCH.PAN;
- this.touches.TWO = TOUCH.DOLLY_ROTATE;
-
-};
-
-MapControls.prototype = Object.create( EventDispatcher.prototype );
-MapControls.prototype.constructor = MapControls;
-
-export { OrbitControls, MapControls };
diff --git a/src/controls/PixelPainterControls.js b/src/controls/PixelPainterControls.js
new file mode 100644
index 0000000..8616f67
--- /dev/null
+++ b/src/controls/PixelPainterControls.js
@@ -0,0 +1,210 @@
+/*
+ * Creates Viewport for 2D Canvas
+ *
+ * @flow
+ */
+
+import Hammer from 'hammerjs';
+import keycode from 'keycode';
+
+import {
+ tryPlacePixel,
+ setHover,
+ unsetHover,
+ setViewCoordinates,
+ setScale,
+ zoomIn,
+ zoomOut,
+ selectColor,
+ moveNorth,
+ moveWest,
+ moveSouth,
+ moveEast,
+ onViewFinishChange,
+} from '../actions';
+import {
+ screenToWorld,
+} from '../core/utils';
+
+let store = null;
+
+function onKeyPress(event: KeyboardEvent) {
+ // ignore key presses if modal is open or chat is used
+ if (event.target.nodeName === 'INPUT'
+ || event.target.nodeName === 'TEXTAREA'
+ ) {
+ return;
+ }
+
+ switch (keycode(event)) {
+ case 'up':
+ case 'w':
+ store.dispatch(moveNorth());
+ break;
+ case 'left':
+ case 'a':
+ store.dispatch(moveWest());
+ break;
+ case 'down':
+ case 's':
+ store.dispatch(moveSouth());
+ break;
+ case 'right':
+ case 'd':
+ store.dispatch(moveEast());
+ break;
+ /*
+ case 'space':
+ if ($viewport) $viewport.click();
+ return;
+ */
+ case '+':
+ case 'e':
+ store.dispatch(zoomIn());
+ break;
+ case '-':
+ case 'q':
+ store.dispatch(zoomOut());
+ break;
+ default:
+ }
+}
+
+export function initControls(renderer, viewport: HTMLCanvasElement, curStore) {
+ store = curStore;
+ viewport.onmousemove = ({ clientX, clientY }: MouseEvent) => {
+ const state = store.getState();
+ const screenCoor = screenToWorld(state, viewport, [clientX, clientY]);
+ store.dispatch(setHover(screenCoor));
+ };
+ viewport.onmouseout = () => {
+ store.dispatch(unsetHover());
+ };
+ viewport.onwheel = ({ deltaY }: WheelEvent) => {
+ const state = store.getState();
+ const { hover } = state.gui;
+ let zoompoint = null;
+ if (hover) {
+ zoompoint = hover;
+ }
+ if (deltaY < 0) {
+ store.dispatch(zoomIn(zoompoint));
+ }
+ if (deltaY > 0) {
+ store.dispatch(zoomOut(zoompoint));
+ }
+ store.dispatch(onViewFinishChange());
+ };
+ viewport.onauxclick = ({ which, clientX, clientY }: MouseEvent) => {
+ // middle mouse button
+ if (which !== 2) {
+ return;
+ }
+ const state = store.getState();
+ if (state.canvas.scale < 3) {
+ return;
+ }
+ const coords = screenToWorld(state, viewport, [clientX, clientY]);
+ const clrIndex = renderer.getColorIndexOfPixel(...coords);
+ if (clrIndex === null) {
+ return;
+ }
+ store.dispatch(selectColor(clrIndex));
+ };
+
+ // fingers controls on touch
+ const hammertime = new Hammer(viewport);
+ hammertime.get('pan').set({ direction: Hammer.DIRECTION_ALL });
+ hammertime.get('swipe').set({ direction: Hammer.DIRECTION_ALL });
+ // Zoom-in Zoom-out in touch devices
+ hammertime.get('pinch').set({ enable: true });
+
+ hammertime.on('tap', ({ center }) => {
+ const state = store.getState();
+ const { autoZoomIn, selectedColor } = state.gui;
+ const { placeAllowed } = state.user;
+
+ const {
+ scale,
+ isHistoricalView,
+ } = state.canvas;
+ if (isHistoricalView) return;
+
+ const { x, y } = center;
+ const cell = screenToWorld(state, viewport, [x, y]);
+
+ if (autoZoomIn && scale < 8) {
+ store.dispatch(setViewCoordinates(cell));
+ store.dispatch(setScale(12));
+ return;
+ }
+
+ // don't allow placing of pixel just on low zoomlevels
+ if (scale < 3) return;
+
+ if (!placeAllowed) return;
+
+ // dirty trick: to fetch only before multiple 3 AND on user action
+ // if (pixelsPlaced % 3 === 0) requestAds();
+
+ if (selectedColor !== renderer.getColorIndexOfPixel(...cell)) {
+ store.dispatch(tryPlacePixel(cell));
+ }
+ });
+
+ const initialState: State = store.getState();
+ [window.lastPosX, window.lastPosY] = initialState.canvas.view;
+ let lastScale = initialState.canvas.scale;
+ hammertime.on(
+ 'panstart pinchstart pan pinch panend pinchend',
+ ({
+ type, deltaX, deltaY, scale,
+ }) => {
+ viewport.style.cursor = 'move'; // like google maps
+ const { scale: viewportScale } = store.getState().canvas;
+
+ // pinch start
+ if (type === 'pinchstart') {
+ store.dispatch(unsetHover());
+ lastScale = viewportScale;
+ }
+
+ // panstart
+ if (type === 'panstart') {
+ store.dispatch(unsetHover());
+ const { view: initView } = store.getState().canvas;
+ [window.lastPosX, window.lastPosY] = initView;
+ }
+
+ // pinch
+ if (type === 'pinch') {
+ store.dispatch(setScale(lastScale * scale));
+ }
+
+ // pan
+ store.dispatch(setViewCoordinates([
+ window.lastPosX - (deltaX / viewportScale),
+ window.lastPosY - (deltaY / viewportScale),
+ ]));
+
+ // pinch end
+ if (type === 'pinchend') {
+ lastScale = viewportScale;
+ }
+
+ // panend
+ if (type === 'panend') {
+ store.dispatch(onViewFinishChange());
+ const { view } = store.getState().canvas;
+ [window.lastPosX, window.lastPosY] = view;
+ viewport.style.cursor = 'auto';
+ }
+ },
+ );
+
+ document.addEventListener('keydown', onKeyPress, false);
+}
+
+export function removeControls() {
+ document.removeEventListener('keydown', onKeyPress, false);
+}
diff --git a/src/controls/VoxelPainterControls.js b/src/controls/VoxelPainterControls.js
index f127961..ee7f189 100644
--- a/src/controls/VoxelPainterControls.js
+++ b/src/controls/VoxelPainterControls.js
@@ -1,1249 +1,991 @@
/**
+ * Original from OrbitControl of the three.js package from
+ *
* @author qiao / https://github.com/qiao
* @author mrdoob / http://mrdoob.com
* @author alteredq / http://alteredqualia.com/
* @author WestLangley / http://github.com/WestLangley
* @author erich666 / http://erichaines.com
* @author ScieCode / http://github.com/sciecode
+ *
+ * Changes for smooth key movement from
+ *
+ * @author hf / http://github.com/pixelplanetdev
*/
-import {
- EventDispatcher,
- MOUSE,
- Quaternion,
- Spherical,
- TOUCH,
- Vector2,
- Vector3,
-} from "three";
+/* eslint-disable no-console */
-// This set of controls performs orbiting, dollying (zooming), and panning.
-// Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default).
+import {
+ EventDispatcher,
+ MOUSE,
+ Quaternion,
+ Spherical,
+ TOUCH,
+ Vector2,
+ Vector3,
+} from 'three';
+
+// This set of controls performs orbiting, dollying (zooming),
+// and panning and smooth moving by keys.
//
// Orbit - left mouse / touch: one-finger move
// Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish
-// Pan - right mouse, or left mouse + ctrl/meta/shiftKey, or arrow keys / touch: two-finger move
-
-var VoxelPainterControls = function ( object, domElement ) {
-
- if ( domElement === undefined ) console.warn( 'THREE.VoxelPainterControls: The second parameter "domElement" is now mandatory.' );
- if ( domElement === document ) console.error( 'THREE.VoxelPainterControls: "document" should not be used as the target "domElement". Please use "renderer.domElement" instead.' );
-
- this.object = object;
- this.domElement = domElement;
-
- // Set to false to disable this control
- this.enabled = true;
-
- // "target" sets the location of focus, where the object orbits around
- this.target = new Vector3();
-
- // How far you can dolly in and out ( PerspectiveCamera only )
- this.minDistance = 0;
- this.maxDistance = Infinity;
-
- // How far you can zoom in and out ( OrthographicCamera only )
- this.minZoom = 0;
- this.maxZoom = Infinity;
-
- // How far you can orbit vertically, upper and lower limits.
- // Range is 0 to Math.PI radians.
- this.minPolarAngle = 0; // radians
- this.maxPolarAngle = Math.PI; // radians
-
- // How far you can orbit horizontally, upper and lower limits.
- // If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ].
- this.minAzimuthAngle = - Infinity; // radians
- this.maxAzimuthAngle = Infinity; // radians
-
- // Set to true to enable damping (inertia)
- // If damping is enabled, you must call controls.update() in your animation loop
- this.enableDamping = false;
- this.dampingFactor = 0.05;
-
- // This option actually enables dollying in and out; left as "zoom" for backwards compatibility.
- // Set to false to disable zooming
- this.enableZoom = true;
- this.zoomSpeed = 1.0;
-
- // Set to false to disable rotating
- this.enableRotate = true;
- this.rotateSpeed = 1.0;
-
- // Set to false to disable panning
- this.enablePan = true;
- this.panSpeed = 1.0;
- this.screenSpacePanning = true; // if true, pan in screen-space
- this.keyPanSpeed = 20.0; // pixels moved per arrow key push
-
- // Set to true to automatically rotate around the target
- // If auto-rotate is enabled, you must call controls.update() in your animation loop
- this.autoRotate = false;
- this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60
-
- // Mouse buttons
- this.mouseButtons = { LEFT: MOUSE.ROTATE, MIDDLE: MOUSE.DOLLY, RIGHT: MOUSE.PAN };
-
- // Touch fingers
- this.touches = { ONE: TOUCH.ROTATE, TWO: TOUCH.DOLLY_PAN };
-
- // for reset
- this.target0 = this.target.clone();
- this.position0 = this.object.position.clone();
- this.zoom0 = this.object.zoom;
-
- //
- // public methods
- //
-
- this.getPolarAngle = function () {
-
- return spherical.phi;
-
- };
-
- this.getAzimuthalAngle = function () {
-
- return spherical.theta;
-
- };
-
- this.saveState = function () {
-
- scope.target0.copy( scope.target );
- scope.position0.copy( scope.object.position );
- scope.zoom0 = scope.object.zoom;
-
- };
-
- this.reset = function () {
-
- scope.target.copy( scope.target0 );
- scope.object.position.copy( scope.position0 );
- scope.object.zoom = scope.zoom0;
-
- scope.object.updateProjectionMatrix();
- scope.dispatchEvent( changeEvent );
-
- scope.update();
-
- state = STATE.NONE;
-
- };
-
- // this method is exposed, but perhaps it would be better if we can make it private...
- this.update = function () {
-
- var offset = new Vector3();
-
- // so camera.up is the orbit axis
- var quat = new Quaternion().setFromUnitVectors( object.up, new Vector3( 0, 1, 0 ) );
- var quatInverse = quat.clone().inverse();
-
- var lastPosition = new Vector3();
- var lastQuaternion = new Quaternion();
-
- var direction = new Vector3();
- var velocity = new Vector3();
- var prevTime = Date.now();
- var logTime = Date.now();
- var vec = new Vector3();
-
- return function update() {
- var time = Date.now();
-
- var log = false;
- if (time - logTime > 2000) {
- logTime = time;
- log = true;
- }
-
- var delta = ( time - prevTime ) / 1000.0;
- velocity.x -= velocity.x * 50.0 * delta;
- velocity.y -= velocity.y * 50.0 * delta;
- velocity.z -= velocity.z * 50.0 * delta;
- const length = velocity.length();
- if (length < 1 || length > 10) {
- velocity.set(0, 0, 0);
- }
-
- direction.x = Number( moveRight ) - Number( moveLeft );
- direction.y = Number( moveUp ) - Number( moveDown );
- direction.z = Number( moveForward ) - Number( moveBackward );
- direction.normalize();
-
- if ( moveLeft || moveRight ) velocity.x -= direction.x * 5000.0 * delta;
- if ( moveUp || moveDown ) velocity.y -= direction.y * 5000.0 * delta;
- if ( moveForward || moveBackward ) velocity.z -= direction.z * 2500.0 * delta;
-
- // controls.moveRight( -velocity.x * delta);
- // controls.moveUp( -velocity.y * delta);
- // forward
- if (log) {
- console.log(vec.x, vec.y, vec.z, delta);
- }
- vec.setFromMatrixColumn( scope.object.matrix, 0 );
- vec.crossVectors( scope.object.up, vec);
- vec.multiplyScalar( -velocity.z * delta );
- vec.y += -velocity.y * delta;
- panOffset.add( vec );
- vec.setFromMatrixColumn( scope.object.matrix, 0 );
- vec.multiplyScalar( -velocity.x * delta );
- panOffset.add( vec );
-
- prevTime = time;
-
-
- var position = scope.object.position;
-
- offset.copy( position ).sub( scope.target );
-
- // rotate offset to "y-axis-is-up" space
- offset.applyQuaternion( quat );
-
- // angle from z-axis around y-axis
- spherical.setFromVector3( offset );
-
- if ( scope.autoRotate && state === STATE.NONE ) {
-
- rotateLeft( getAutoRotationAngle() );
-
- }
-
- if ( scope.enableDamping ) {
-
- spherical.theta += sphericalDelta.theta * scope.dampingFactor;
- spherical.phi += sphericalDelta.phi * scope.dampingFactor;
-
- } else {
-
- spherical.theta += sphericalDelta.theta;
- spherical.phi += sphericalDelta.phi;
-
- }
-
- // restrict theta to be between desired limits
- spherical.theta = Math.max( scope.minAzimuthAngle, Math.min( scope.maxAzimuthAngle, spherical.theta ) );
-
- // restrict phi to be between desired limits
- spherical.phi = Math.max( scope.minPolarAngle, Math.min( scope.maxPolarAngle, spherical.phi ) );
-
- spherical.makeSafe();
-
-
- spherical.radius *= scale;
-
- // restrict radius to be between desired limits
- spherical.radius = Math.max( scope.minDistance, Math.min( scope.maxDistance, spherical.radius ) );
-
- // move target to panned location
-
- if (panOffset.length() > 10000) {
- panOffset.set(0, 0, 0);
- }
- if ( scope.enableDamping === true ) {
- scope.target.addScaledVector( panOffset, scope.dampingFactor );
- } else {
- scope.target.add( panOffset );
- }
- if (scope.target.y < 10.0) {
- scope.target.y = 10.0;
- }
-
-
- offset.setFromSpherical( spherical );
-
- // rotate offset back to "camera-up-vector-is-up" space
- offset.applyQuaternion( quatInverse );
-
- position.copy( scope.target ).add( offset );
-
- scope.object.lookAt( scope.target );
-
- if ( scope.enableDamping === true ) {
-
- sphericalDelta.theta *= ( 1 - scope.dampingFactor );
- sphericalDelta.phi *= ( 1 - scope.dampingFactor );
-
- panOffset.multiplyScalar( 1 - scope.dampingFactor );
-
- } else {
-
- sphericalDelta.set( 0, 0, 0 );
-
- panOffset.set( 0, 0, 0 );
-
- }
-
- scale = 1;
-
- // update condition is:
- // min(camera displacement, camera rotation in radians)^2 > EPS
- // using small-angle approximation cos(x/2) = 1 - x^2 / 8
-
- if ( zoomChanged ||
- lastPosition.distanceToSquared( scope.object.position ) > EPS ||
- 8 * ( 1 - lastQuaternion.dot( scope.object.quaternion ) ) > EPS ) {
-
- scope.dispatchEvent( changeEvent );
-
- lastPosition.copy( scope.object.position );
- lastQuaternion.copy( scope.object.quaternion );
- zoomChanged = false;
-
- return true;
-
- }
-
- return false;
-
- };
-
- }();
-
- this.dispose = function () {
-
- scope.domElement.removeEventListener( 'contextmenu', onContextMenu, false );
- scope.domElement.removeEventListener( 'mousedown', onMouseDown, false );
- scope.domElement.removeEventListener( 'wheel', onMouseWheel, false );
-
- scope.domElement.removeEventListener( 'touchstart', onTouchStart, false );
- scope.domElement.removeEventListener( 'touchend', onTouchEnd, false );
- scope.domElement.removeEventListener( 'touchmove', onTouchMove, false );
-
- document.removeEventListener( 'mousemove', onMouseMove, false );
- document.removeEventListener( 'mouseup', onMouseUp, false );
- document.removeEventListener( 'keydown', onDocumentKeyDown, false );
- document.removeEventListener( 'keyup', onDocumentKeyUp, false );
-
- //scope.dispatchEvent( { type: 'dispose' } ); // should this be added here?
-
- };
-
- var vec = new Vector3();
-
- this.moveForward = ( distance ) => {
- console.log("forward");
- return;
- vec.setFromMatrixColumn( scope.object.matrix, 0 );
- vec.crossVectors( scope.object.up, vec);
- scope.object.position.addScaledVector( vec, distance );
- };
- this.moveRight = ( distance ) => {
- console.log("down");
- return;
- vec.setFromMatrixColumn( scope.object.matrix, 0 );
- scope.object.position.addScaledVector( vec, distance );
- };
- this.moveUp = ( distance ) => {
- console.log("up");
- return;
- scope.object.position.y += distance;
- };
-
- //
- // internals
- //
-
- var scope = this;
-
- var changeEvent = { type: 'change' };
- var startEvent = { type: 'start' };
- var endEvent = { type: 'end' };
-
- var STATE = {
- NONE: - 1,
- ROTATE: 0,
- DOLLY: 1,
- PAN: 2,
- TOUCH_ROTATE: 3,
- TOUCH_PAN: 4,
- TOUCH_DOLLY_PAN: 5,
- TOUCH_DOLLY_ROTATE: 6
- };
-
- var state = STATE.NONE;
-
- var EPS = 0.000001;
-
- // current position in spherical coordinates
- var spherical = new Spherical();
- var sphericalDelta = new Spherical();
-
- var moveLeft = false;
- var moveRight = false;
- var moveForward = false;
- var moveBackward = false;
- var moveUp = false;
- var moveDown = false;
-
- var scale = 1;
- var panOffset = new Vector3();
- var zoomChanged = false;
-
- var rotateStart = new Vector2();
- var rotateEnd = new Vector2();
- var rotateDelta = new Vector2();
-
- var panStart = new Vector2();
- var panEnd = new Vector2();
- var panDelta = new Vector2();
-
- var dollyStart = new Vector2();
- var dollyEnd = new Vector2();
- var dollyDelta = new Vector2();
-
- function getAutoRotationAngle() {
-
- return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
-
- }
-
- function getZoomScale() {
-
- return Math.pow( 0.95, scope.zoomSpeed );
-
- }
-
- function rotateLeft( angle ) {
-
- sphericalDelta.theta -= angle;
-
- }
-
- function rotateUp( angle ) {
-
- sphericalDelta.phi -= angle;
-
- }
-
- var panLeft = function () {
-
- var v = new Vector3();
-
- return function panLeft( distance, objectMatrix ) {
-
- v.setFromMatrixColumn( objectMatrix, 0 ); // get X column of objectMatrix
- v.multiplyScalar( - distance );
-
- panOffset.add( v );
-
- };
-
- }();
-
- var panUp = function () {
-
- var v = new Vector3();
-
- return function panUp( distance, objectMatrix ) {
-
- if ( scope.screenSpacePanning === true ) {
- v.setFromMatrixColumn( objectMatrix, 1 );
- } else {
- v.setFromMatrixColumn( objectMatrix, 0 );
- v.crossVectors( scope.object.up, v );
- }
-
- v.multiplyScalar( distance );
-
- panOffset.add( v );
- };
-
- }();
-
- // deltaX and deltaY are in pixels; right and down are positive
- var pan = function () {
-
- var offset = new Vector3();
-
- return function pan( deltaX, deltaY ) {
-
- var element = scope.domElement;
-
- if ( scope.object.isPerspectiveCamera ) {
-
- // perspective
- var position = scope.object.position;
- offset.copy( position ).sub( scope.target );
- var targetDistance = offset.length();
-
- // half of the fov is center to top of screen
- targetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 );
-
- // we use only clientHeight here so aspect ratio does not distort speed
- panLeft( 2 * deltaX * targetDistance / element.clientHeight, scope.object.matrix );
- panUp( 2 * deltaY * targetDistance / element.clientHeight, scope.object.matrix );
-
- } else if ( scope.object.isOrthographicCamera ) {
-
- // orthographic
- panLeft( deltaX * ( scope.object.right - scope.object.left ) / scope.object.zoom / element.clientWidth, scope.object.matrix );
- panUp( deltaY * ( scope.object.top - scope.object.bottom ) / scope.object.zoom / element.clientHeight, scope.object.matrix );
-
- } else {
-
- // camera neither orthographic nor perspective
- console.warn( 'WARNING: VoxelPainterControls.js encountered an unknown camera type - pan disabled.' );
- scope.enablePan = false;
-
- }
-
- };
-
- }();
-
- function dollyIn( dollyScale ) {
-
- if ( scope.object.isPerspectiveCamera ) {
-
- scale /= dollyScale;
-
- } else if ( scope.object.isOrthographicCamera ) {
-
- scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom * dollyScale ) );
- scope.object.updateProjectionMatrix();
- zoomChanged = true;
-
- } else {
-
- console.warn( 'WARNING: VoxelPainterControls.js encountered an unknown camera type - dolly/zoom disabled.' );
- scope.enableZoom = false;
-
- }
-
- }
-
- function dollyOut( dollyScale ) {
-
- if ( scope.object.isPerspectiveCamera ) {
-
- scale *= dollyScale;
-
- } else if ( scope.object.isOrthographicCamera ) {
-
- scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom / dollyScale ) );
- scope.object.updateProjectionMatrix();
- zoomChanged = true;
-
- } else {
-
- console.warn( 'WARNING: VoxelPainterControls.js encountered an unknown camera type - dolly/zoom disabled.' );
- scope.enableZoom = false;
-
- }
-
- }
-
- //
- // event callbacks - update the object state
- //
-
- function handleMouseDownRotate( event ) {
-
- rotateStart.set( event.clientX, event.clientY );
-
- }
-
- function handleMouseDownDolly( event ) {
-
- dollyStart.set( event.clientX, event.clientY );
-
- }
-
- function handleMouseDownPan( event ) {
-
- panStart.set( event.clientX, event.clientY );
-
- }
-
- function handleMouseMoveRotate( event ) {
-
- rotateEnd.set( event.clientX, event.clientY );
-
- rotateDelta.subVectors( rotateEnd, rotateStart ).multiplyScalar( scope.rotateSpeed );
-
- var element = scope.domElement;
-
- rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientHeight ); // yes, height
-
- rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight );
-
- rotateStart.copy( rotateEnd );
-
- scope.update();
-
- }
-
- function handleMouseMoveDolly( event ) {
-
- dollyEnd.set( event.clientX, event.clientY );
-
- dollyDelta.subVectors( dollyEnd, dollyStart );
-
- if ( dollyDelta.y > 0 ) {
-
- dollyIn( getZoomScale() );
-
- } else if ( dollyDelta.y < 0 ) {
-
- dollyOut( getZoomScale() );
-
- }
-
- dollyStart.copy( dollyEnd );
-
- scope.update();
-
- }
-
- function handleMouseMovePan( event ) {
-
- panEnd.set( event.clientX, event.clientY );
-
- panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed );
-
- pan( panDelta.x, panDelta.y );
-
- panStart.copy( panEnd );
-
- scope.update();
-
- }
-
- function handleMouseUp( /*event*/ ) {
-
- // no-op
-
- }
-
- function handleMouseWheel( event ) {
-
- if ( event.deltaY < 0 ) {
-
- dollyOut( getZoomScale() );
-
- } else if ( event.deltaY > 0 ) {
-
- dollyIn( getZoomScale() );
-
- }
-
- scope.update();
-
- }
-
- function handleTouchStartRotate( event ) {
-
- if ( event.touches.length == 1 ) {
-
- rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
-
- } else {
-
- var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX );
- var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY );
-
- rotateStart.set( x, y );
-
- }
-
- }
-
- function handleTouchStartPan( event ) {
-
- if ( event.touches.length == 1 ) {
-
- panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
-
- } else {
-
- var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX );
- var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY );
-
- panStart.set( x, y );
-
- }
-
- }
-
- function handleTouchStartDolly( event ) {
-
- var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
- var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
-
- var distance = Math.sqrt( dx * dx + dy * dy );
-
- dollyStart.set( 0, distance );
-
- }
-
- function handleTouchStartDollyPan( event ) {
-
- if ( scope.enableZoom ) handleTouchStartDolly( event );
-
- if ( scope.enablePan ) handleTouchStartPan( event );
-
- }
-
- function handleTouchStartDollyRotate( event ) {
-
- if ( scope.enableZoom ) handleTouchStartDolly( event );
-
- if ( scope.enableRotate ) handleTouchStartRotate( event );
-
- }
-
- function handleTouchMoveRotate( event ) {
-
- if ( event.touches.length == 1 ) {
-
- rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
-
- } else {
-
- var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX );
- var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY );
-
- rotateEnd.set( x, y );
-
- }
-
- rotateDelta.subVectors( rotateEnd, rotateStart ).multiplyScalar( scope.rotateSpeed );
-
- var element = scope.domElement;
-
- rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientHeight ); // yes, height
-
- rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight );
-
- rotateStart.copy( rotateEnd );
-
- }
-
- function handleTouchMovePan( event ) {
-
- if ( event.touches.length == 1 ) {
-
- panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
-
- } else {
-
- var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX );
- var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY );
-
- panEnd.set( x, y );
-
- }
-
- panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed );
-
- pan( panDelta.x, panDelta.y );
-
- panStart.copy( panEnd );
-
- }
-
- function handleTouchMoveDolly( event ) {
-
- var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
- var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
-
- var distance = Math.sqrt( dx * dx + dy * dy );
-
- dollyEnd.set( 0, distance );
-
- dollyDelta.set( 0, Math.pow( dollyEnd.y / dollyStart.y, scope.zoomSpeed ) );
-
- dollyIn( dollyDelta.y );
-
- dollyStart.copy( dollyEnd );
-
- }
-
- function handleTouchMoveDollyPan( event ) {
-
- if ( scope.enableZoom ) handleTouchMoveDolly( event );
-
- if ( scope.enablePan ) handleTouchMovePan( event );
-
- }
-
- function handleTouchMoveDollyRotate( event ) {
-
- if ( scope.enableZoom ) handleTouchMoveDolly( event );
-
- if ( scope.enableRotate ) handleTouchMoveRotate( event );
-
- }
-
- function handleTouchEnd( /*event*/ ) {
-
- // no-op
-
- }
-
- //
- // event handlers - FSM: listen for events and reset state
- //
-
- function onMouseDown( event ) {
-
- if ( scope.enabled === false ) return;
-
- // Prevent the browser from scrolling.
- event.preventDefault();
-
- // Manually set the focus since calling preventDefault above
- // prevents the browser from setting it automatically.
-
- scope.domElement.focus ? scope.domElement.focus() : window.focus();
-
- var mouseAction;
-
- switch ( event.button ) {
-
- case 0:
-
- mouseAction = scope.mouseButtons.LEFT;
- break;
-
- case 1:
-
- mouseAction = scope.mouseButtons.MIDDLE;
- break;
-
- case 2:
-
- mouseAction = scope.mouseButtons.RIGHT;
- break;
-
- default:
-
- mouseAction = - 1;
-
- }
-
- switch ( mouseAction ) {
-
- case MOUSE.DOLLY:
-
- if ( scope.enableZoom === false ) return;
-
- handleMouseDownDolly( event );
-
- state = STATE.DOLLY;
-
- break;
-
- case MOUSE.ROTATE:
-
- if ( event.ctrlKey || event.metaKey ) {
-
- if ( scope.enablePan === false ) return;
-
- handleMouseDownPan( event );
-
- state = STATE.PAN;
-
- } else {
-
- if ( scope.enableRotate === false ) return;
-
- handleMouseDownRotate( event );
-
- state = STATE.ROTATE;
-
- }
-
- break;
-
- case MOUSE.PAN:
-
- if ( event.ctrlKey || event.metaKey ) {
-
- if ( scope.enableRotate === false ) return;
-
- handleMouseDownRotate( event );
-
- state = STATE.ROTATE;
-
- } else {
-
- if ( scope.enablePan === false ) return;
-
- handleMouseDownPan( event );
-
- state = STATE.PAN;
-
- }
-
- break;
-
- default:
-
- state = STATE.NONE;
-
- }
-
- if ( state !== STATE.NONE ) {
-
- document.addEventListener( 'mousemove', onMouseMove, false );
- document.addEventListener( 'mouseup', onMouseUp, false );
-
- scope.dispatchEvent( startEvent );
-
- }
-
- }
-
- function onDocumentKeyDown( event ) {
- switch ( event.keyCode ) {
- case 38: // up
- case 87: // w
- moveForward = true;
- break;
-
- case 37: // left
- case 65: // a
- moveLeft = true;
- break;
-
- case 40: // down
- case 83: // s
- moveBackward = true;
- break;
-
- case 39: // right
- case 68: // d
- moveRight = true;
- break;
-
- case 32: // space
- moveUp = true;
- break;
-
- case 16: //shift
- moveDown = true;
- break;
+// Pan - right mouse, or left mouse + ctrl/meta/shiftKey,
+// or arrow keys / touch: two-finger move
+
+class VoxelPainterControls extends EventDispatcher {
+ constructor(object, domElement) {
+ super();
+ // eslint-disable-next-line max-len
+ if (domElement === undefined) console.warn('THREE.VoxelPainterControls: The second parameter "domElement" is now mandatory.');
+ // eslint-disable-next-line max-len
+ if (domElement === document) console.error('THREE.VoxelPainterControls: "document" should not be used as the target "domElement". Please use "renderer.domElement" instead.');
+
+ this.object = object;
+ this.domElement = domElement;
+
+ // Set to false to disable this control
+ this.enabled = true;
+
+ // "target" sets the location of focus, where the object orbits around
+ this.target = new Vector3();
+
+ // How far you can dolly in and out ( PerspectiveCamera only )
+ this.minDistance = 0;
+ this.maxDistance = Infinity;
+
+ // How far you can zoom in and out ( OrthographicCamera only )
+ this.minZoom = 0;
+ this.maxZoom = Infinity;
+
+ // How far you can orbit vertically, upper and lower limits.
+ // Range is 0 to Math.PI radians.
+ this.minPolarAngle = 0; // radians
+ this.maxPolarAngle = Math.PI; // radians
+
+ // How far you can orbit horizontally, upper and lower limits.
+ // If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ].
+ this.minAzimuthAngle = -Infinity; // radians
+ this.maxAzimuthAngle = Infinity; // radians
+
+ // Set to true to enable damping (inertia)
+ // If damping is enabled, you must call controls.update() in your animation loop
+ this.enableDamping = false;
+ this.dampingFactor = 0.05;
+
+ // This option actually enables dollying in and out; left as "zoom" for backwards compatibility.
+ // Set to false to disable zooming
+ this.enableZoom = true;
+ this.zoomSpeed = 1.0;
+
+ // Set to false to disable rotating
+ this.enableRotate = true;
+ this.rotateSpeed = 1.0;
+
+ // Set to false to disable panning
+ this.enablePan = true;
+ this.panSpeed = 1.0;
+ this.screenSpacePanning = true; // if true, pan in screen-space
+ this.keyPanSpeed = 20.0; // pixels moved per arrow key push
+
+ // Set to true to automatically rotate around the target
+ // If auto-rotate is enabled, you must call controls.update() in your animation loop
+ this.autoRotate = false;
+ this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60
+
+ // Mouse buttons
+ this.mouseButtons = {
+ LEFT: MOUSE.ROTATE,
+ MIDDLE: MOUSE.DOLLY,
+ RIGHT: MOUSE.PAN,
+ };
+
+ // Touch fingers
+ this.touches = { ONE: TOUCH.ROTATE, TWO: TOUCH.DOLLY_PAN };
+
+ // for reset
+ this.target0 = this.target.clone();
+ this.position0 = this.object.position.clone();
+ this.zoom0 = this.object.zoom;
+
+
+ //
+ // internals
+ //
+
+ const scope = this;
+
+ const changeEvent = { type: 'change' };
+ const startEvent = { type: 'start' };
+ const endEvent = { type: 'end' };
+
+ const STATE = {
+ NONE: -1,
+ ROTATE: 0,
+ DOLLY: 1,
+ PAN: 2,
+ TOUCH_ROTATE: 3,
+ TOUCH_PAN: 4,
+ TOUCH_DOLLY_PAN: 5,
+ TOUCH_DOLLY_ROTATE: 6,
+ };
+
+ let state = STATE.NONE;
+
+ const EPS = 0.000001;
+
+ // current position in spherical coordinates
+ const spherical = new Spherical();
+ const sphericalDelta = new Spherical();
+
+ let moveLeft = false;
+ let moveRight = false;
+ let moveForward = false;
+ let moveBackward = false;
+ let moveUp = false;
+ let moveDown = false;
+
+ let scale = 1;
+ const panOffset = new Vector3();
+ let zoomChanged = false;
+
+ const rotateStart = new Vector2();
+ const rotateEnd = new Vector2();
+ const rotateDelta = new Vector2();
+
+ const panStart = new Vector2();
+ const panEnd = new Vector2();
+ const panDelta = new Vector2();
+
+ const dollyStart = new Vector2();
+ const dollyEnd = new Vector2();
+ const dollyDelta = new Vector2();
+
+ function getAutoRotationAngle() {
+ return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
}
- }
- function onDocumentKeyUp( event ) {
- switch ( event.keyCode ) {
- case 38: // up
- case 87: // w
- moveForward = false;
- break;
-
- case 37: // left
- case 65: // a
- moveLeft = false;
- break;
-
- case 40: // down
- case 83: // s
- moveBackward = false;
- break;
-
- case 39: // right
- case 68: // d
- moveRight = false;
- break;
-
- case 32: // space
- moveUp = false;
- break;
-
- case 16: //shift
- moveDown = false;
- break;
+ function getZoomScale() {
+ return 0.95 ** scope.zoomSpeed;
}
+
+ function rotateLeft(angle) {
+ sphericalDelta.theta -= angle;
+ }
+
+ function rotateUp(angle) {
+ sphericalDelta.phi -= angle;
+ }
+
+ const v = new Vector3();
+ const panLeft = (distance, objectMatrix) => {
+ v.setFromMatrixColumn(objectMatrix, 0); // get X column of objectMatrix
+ v.multiplyScalar(-distance);
+
+ panOffset.add(v);
+ };
+
+
+ const panUp = (distance, objectMatrix) => {
+ if (scope.screenSpacePanning === true) {
+ v.setFromMatrixColumn(objectMatrix, 1);
+ } else {
+ v.setFromMatrixColumn(objectMatrix, 0);
+ v.crossVectors(scope.object.up, v);
+ }
+
+ v.multiplyScalar(distance);
+
+ panOffset.add(v);
+ };
+
+ // deltaX and deltaY are in pixels; right and down are positive
+ const pan = (deltaX, deltaY) => {
+ const element = scope.domElement;
+
+ if (scope.object.isPerspectiveCamera) {
+ // perspective
+ const { position } = scope.object;
+ v.copy(position).sub(scope.target);
+ let targetDistance = v.length();
+
+ // half of the fov is center to top of screen
+ targetDistance *= Math.tan((scope.object.fov / 2) * Math.PI / 180.0);
+
+ // we use only clientHeight here so aspect ratio does not distort speed
+ panLeft(
+ 2 * deltaX * targetDistance / element.clientHeight,
+ scope.object.matrix,
+ );
+ panUp(
+ 2 * deltaY * targetDistance / element.clientHeight,
+ scope.object.matrix,
+ );
+ } else if (scope.object.isOrthographicCamera) {
+ // orthographic
+ panLeft(
+ // eslint-disable-next-line max-len
+ deltaX * (scope.object.right - scope.object.left) / scope.object.zoom / element.clientWidth,
+ scope.object.matrix,
+ );
+ panUp(
+ // eslint-disable-next-line max-len
+ deltaY * (scope.object.top - scope.object.bottom) / scope.object.zoom / element.clientHeight,
+ scope.object.matrix,
+ );
+ } else {
+ // camera neither orthographic nor perspective
+ // eslint-disable-next-line max-len
+ console.warn('WARNING: VoxelPainterControls.js encountered an unknown camera type - pan disabled.');
+ scope.enablePan = false;
+ }
+ };
+
+ function dollyIn(dollyScale) {
+ if (scope.object.isPerspectiveCamera) {
+ scale /= dollyScale;
+ } else if (scope.object.isOrthographicCamera) {
+ scope.object.zoom = Math.max(
+ scope.minZoom,
+ Math.min(scope.maxZoom, scope.object.zoom * dollyScale),
+ );
+ scope.object.updateProjectionMatrix();
+ zoomChanged = true;
+ } else {
+ // eslint-disable-next-line max-len
+ console.warn('WARNING: VoxelPainterControls.js encountered an unknown camera type - dolly/zoom disabled.');
+ scope.enableZoom = false;
+ }
+ }
+
+ function dollyOut(dollyScale) {
+ if (scope.object.isPerspectiveCamera) {
+ scale *= dollyScale;
+ } else if (scope.object.isOrthographicCamera) {
+ scope.object.zoom = Math.max(
+ scope.minZoom,
+ Math.min(scope.maxZoom, scope.object.zoom / dollyScale),
+ );
+ scope.object.updateProjectionMatrix();
+ zoomChanged = true;
+ } else {
+ // eslint-disable-next-line max-len
+ console.warn('WARNING: VoxelPainterControls.js encountered an unknown camera type - dolly/zoom disabled.');
+ scope.enableZoom = false;
+ }
+ }
+
+ //
+ // event callbacks - update the object state
+ //
+
+ function handleMouseDownRotate(event) {
+ rotateStart.set(event.clientX, event.clientY);
+ }
+
+ function handleMouseDownDolly(event) {
+ dollyStart.set(event.clientX, event.clientY);
+ }
+
+ function handleMouseDownPan(event) {
+ panStart.set(event.clientX, event.clientY);
+ }
+
+ function handleMouseMoveRotate(event) {
+ rotateEnd.set(event.clientX, event.clientY);
+
+ rotateDelta
+ .subVectors(rotateEnd, rotateStart)
+ .multiplyScalar(scope.rotateSpeed);
+ const element = scope.domElement;
+ rotateLeft(2 * Math.PI * rotateDelta.x / element.clientHeight); // yes, height
+ rotateUp(2 * Math.PI * rotateDelta.y / element.clientHeight);
+ rotateStart.copy(rotateEnd);
+ scope.update();
+ }
+
+ function handleMouseMoveDolly(event) {
+ dollyEnd.set(event.clientX, event.clientY);
+
+ dollyDelta.subVectors(dollyEnd, dollyStart);
+ if (dollyDelta.y > 0) {
+ dollyIn(getZoomScale());
+ } else if (dollyDelta.y < 0) {
+ dollyOut(getZoomScale());
+ }
+ dollyStart.copy(dollyEnd);
+ scope.update();
+ }
+
+ function handleMouseMovePan(event) {
+ panEnd.set(event.clientX, event.clientY);
+
+ panDelta.subVectors(panEnd, panStart).multiplyScalar(scope.panSpeed);
+ pan(panDelta.x, panDelta.y);
+ panStart.copy(panEnd);
+ scope.update();
+ }
+
+ function handleMouseUp(/* event */) {
+ // no-op
+ }
+
+ function handleMouseWheel(event) {
+ if (event.deltaY < 0) {
+ dollyOut(getZoomScale());
+ } else if (event.deltaY > 0) {
+ dollyIn(getZoomScale());
+ }
+ scope.update();
+ }
+
+ function handleTouchStartRotate(event) {
+ if (event.touches.length === 1) {
+ rotateStart.set(event.touches[0].pageX, event.touches[0].pageY);
+ } else {
+ const x = 0.5 * (event.touches[0].pageX + event.touches[1].pageX);
+ const y = 0.5 * (event.touches[0].pageY + event.touches[1].pageY);
+ rotateStart.set(x, y);
+ }
+ }
+
+ function handleTouchStartPan(event) {
+ if (event.touches.length === 1) {
+ panStart.set(event.touches[0].pageX, event.touches[0].pageY);
+ } else {
+ const x = 0.5 * (event.touches[0].pageX + event.touches[1].pageX);
+ const y = 0.5 * (event.touches[0].pageY + event.touches[1].pageY);
+
+ panStart.set(x, y);
+ }
+ }
+
+ function handleTouchStartDolly(event) {
+ const dx = event.touches[0].pageX - event.touches[1].pageX;
+ const dy = event.touches[0].pageY - event.touches[1].pageY;
+ const distance = Math.sqrt(dx * dx + dy * dy);
+
+ dollyStart.set(0, distance);
+ }
+
+ function handleTouchStartDollyPan(event) {
+ if (scope.enableZoom) handleTouchStartDolly(event);
+ if (scope.enablePan) handleTouchStartPan(event);
+ }
+
+ function handleTouchStartDollyRotate(event) {
+ if (scope.enableZoom) handleTouchStartDolly(event);
+ if (scope.enableRotate) handleTouchStartRotate(event);
+ }
+
+ function handleTouchMoveRotate(event) {
+ if (event.touches.length === 1) {
+ rotateEnd.set(event.touches[0].pageX, event.touches[0].pageY);
+ } else {
+ const x = 0.5 * (event.touches[0].pageX + event.touches[1].pageX);
+ const y = 0.5 * (event.touches[0].pageY + event.touches[1].pageY);
+
+ rotateEnd.set(x, y);
+ }
+
+ rotateDelta.subVectors(rotateEnd, rotateStart)
+ .multiplyScalar(scope.rotateSpeed);
+ const element = scope.domElement;
+ rotateLeft(2 * Math.PI * rotateDelta.x / element.clientHeight);
+ rotateUp(2 * Math.PI * rotateDelta.y / element.clientHeight);
+ rotateStart.copy(rotateEnd);
+ }
+
+ function handleTouchMovePan(event) {
+ if (event.touches.length === 1) {
+ panEnd.set(event.touches[0].pageX, event.touches[0].pageY);
+ } else {
+ const x = 0.5 * (event.touches[0].pageX + event.touches[1].pageX);
+ const y = 0.5 * (event.touches[0].pageY + event.touches[1].pageY);
+ panEnd.set(x, y);
+ }
+ panDelta.subVectors(panEnd, panStart).multiplyScalar(scope.panSpeed);
+ pan(panDelta.x, panDelta.y);
+ panStart.copy(panEnd);
+ }
+
+ function handleTouchMoveDolly(event) {
+ const dx = event.touches[0].pageX - event.touches[1].pageX;
+ const dy = event.touches[0].pageY - event.touches[1].pageY;
+
+ const distance = Math.sqrt(dx * dx + dy * dy);
+ dollyEnd.set(0, distance);
+ dollyDelta.set(0, (dollyEnd.y / dollyStart.y) ** scope.zoomSpeed);
+ dollyIn(dollyDelta.y);
+ dollyStart.copy(dollyEnd);
+ }
+
+ function handleTouchMoveDollyPan(event) {
+ if (scope.enableZoom) handleTouchMoveDolly(event);
+ if (scope.enablePan) handleTouchMovePan(event);
+ }
+
+ function handleTouchMoveDollyRotate(event) {
+ if (scope.enableZoom) handleTouchMoveDolly(event);
+ if (scope.enableRotate) handleTouchMoveRotate(event);
+ }
+
+ function handleTouchEnd(/* event */) {
+ // no-op
+ }
+
+ //
+ // event handlers - FSM: listen for events and reset state
+ //
+ //
+ function onDocumentKeyDown(event) {
+ if (scope.enabled === false) return;
+
+ switch (event.keyCode) {
+ case 38: // up
+ case 87: // w
+ moveForward = true;
+ break;
+
+ case 37: // left
+ case 65: // a
+ moveLeft = true;
+ break;
+
+ case 40: // down
+ case 83: // s
+ moveBackward = true;
+ break;
+
+ case 39: // right
+ case 68: // d
+ moveRight = true;
+ break;
+
+ case 32: // space
+ moveUp = true;
+ break;
+
+ case 16: // shift
+ moveDown = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ function onDocumentKeyUp(event) {
+ if (scope.enabled === false) return;
+
+ switch (event.keyCode) {
+ case 38: // up
+ case 87: // w
+ moveForward = false;
+ break;
+
+ case 37: // left
+ case 65: // a
+ moveLeft = false;
+ break;
+
+ case 40: // down
+ case 83: // s
+ moveBackward = false;
+ break;
+
+ case 39: // right
+ case 68: // d
+ moveRight = false;
+ break;
+
+ case 32: // space
+ moveUp = false;
+ break;
+
+ case 16: // shift
+ moveDown = false;
+ break;
+ default:
+ break;
+ }
+ }
+
+
+ function onMouseMove(event) {
+ if (scope.enabled === false) return;
+
+ event.preventDefault();
+
+ switch (state) {
+ case STATE.ROTATE:
+ handleMouseMoveRotate(event);
+ break;
+ case STATE.DOLLY:
+ handleMouseMoveDolly(event);
+ break;
+ case STATE.PAN:
+ handleMouseMovePan(event);
+ break;
+ default:
+ break;
+ }
+ }
+
+ function onMouseUp(event) {
+ if (scope.enabled === false) return;
+
+ handleMouseUp(event);
+ document.removeEventListener('mousemove', onMouseMove, false);
+ document.removeEventListener('mouseup', onMouseUp, false);
+ scope.dispatchEvent(endEvent);
+ state = STATE.NONE;
+ }
+
+ function onMouseWheel(event) {
+ if (scope.enabled === false
+ || scope.enableZoom === false
+ || (state !== STATE.NONE
+ && state !== STATE.ROTATE)
+ ) return;
+
+ event.preventDefault();
+ event.stopPropagation();
+
+ scope.dispatchEvent(startEvent);
+ handleMouseWheel(event);
+ scope.dispatchEvent(endEvent);
+ }
+
+ function onTouchStart(event) {
+ if (scope.enabled === false) return;
+
+ event.preventDefault();
+
+ switch (event.touches.length) {
+ case 1:
+
+ switch (scope.touches.ONE) {
+ case TOUCH.ROTATE:
+ if (scope.enableRotate === false) return;
+
+ handleTouchStartRotate(event);
+ state = STATE.TOUCH_ROTATE;
+ break;
+
+ case TOUCH.PAN:
+ if (scope.enablePan === false) return;
+
+ handleTouchStartPan(event);
+ state = STATE.TOUCH_PAN;
+ break;
+
+ default:
+ state = STATE.NONE;
+ }
+
+ break;
+
+ case 2:
+
+ switch (scope.touches.TWO) {
+ case TOUCH.DOLLY_PAN:
+ if (scope.enableZoom === false
+ && scope.enablePan === false
+ ) return;
+
+ handleTouchStartDollyPan(event);
+ state = STATE.TOUCH_DOLLY_PAN;
+ break;
+
+ case TOUCH.DOLLY_ROTATE:
+ if (scope.enableZoom === false
+ && scope.enableRotate === false
+ ) return;
+
+ handleTouchStartDollyRotate(event);
+ state = STATE.TOUCH_DOLLY_ROTATE;
+ break;
+
+ default:
+ state = STATE.NONE;
+ }
+
+ break;
+
+ default:
+
+ state = STATE.NONE;
+ }
+
+ if (state !== STATE.NONE) {
+ scope.dispatchEvent(startEvent);
+ }
+ }
+
+ function onTouchMove(event) {
+ if (scope.enabled === false) return;
+
+ event.preventDefault();
+ event.stopPropagation();
+
+ switch (state) {
+ case STATE.TOUCH_ROTATE:
+ if (scope.enableRotate === false) return;
+
+ handleTouchMoveRotate(event);
+ scope.update();
+ break;
+
+ case STATE.TOUCH_PAN:
+ if (scope.enablePan === false) return;
+
+ handleTouchMovePan(event);
+ scope.update();
+ break;
+
+ case STATE.TOUCH_DOLLY_PAN:
+ if (scope.enableZoom === false && scope.enablePan === false) return;
+
+ handleTouchMoveDollyPan(event);
+ scope.update();
+ break;
+
+ case STATE.TOUCH_DOLLY_ROTATE:
+ if (scope.enableZoom === false
+ && scope.enableRotate === false) return;
+
+ handleTouchMoveDollyRotate(event);
+ scope.update();
+ break;
+
+ default:
+ state = STATE.NONE;
+ }
+ }
+
+ function onTouchEnd(event) {
+ if (scope.enabled === false) return;
+
+ handleTouchEnd(event);
+ scope.dispatchEvent(endEvent);
+ state = STATE.NONE;
+ }
+
+ function onContextMenu(event) {
+ if (scope.enabled === false) return;
+
+ event.preventDefault();
+ }
+
+ function onMouseDown(event) {
+ if (scope.enabled === false) return;
+
+ // Prevent the browser from scrolling.
+ event.preventDefault();
+
+ // Manually set the focus since calling preventDefault above
+ // prevents the browser from setting it automatically.
+ if (scope.domElement.focus) {
+ scope.domElement.focus();
+ } else {
+ window.focus();
+ }
+
+ let mouseAction;
+
+ switch (event.button) {
+ case 0:
+ mouseAction = scope.mouseButtons.LEFT;
+ break;
+
+ case 1:
+ mouseAction = scope.mouseButtons.MIDDLE;
+ break;
+
+ case 2:
+ mouseAction = scope.mouseButtons.RIGHT;
+ break;
+
+ default:
+ mouseAction = -1;
+ }
+
+ switch (mouseAction) {
+ case MOUSE.DOLLY:
+ handleMouseDownDolly(event);
+ state = STATE.DOLLY;
+ break;
+
+ case MOUSE.ROTATE:
+ if (event.ctrlKey || event.metaKey) {
+ handleMouseDownPan(event);
+ state = STATE.PAN;
+ } else {
+ handleMouseDownRotate(event);
+ state = STATE.ROTATE;
+ }
+ break;
+
+ case MOUSE.PAN:
+ if (event.ctrlKey || event.metaKey) {
+ handleMouseDownRotate(event);
+ state = STATE.ROTATE;
+ } else {
+ handleMouseDownPan(event);
+ state = STATE.PAN;
+ }
+ break;
+
+ default:
+ state = STATE.NONE;
+ }
+
+ if (state !== STATE.NONE) {
+ document.addEventListener('mousemove', onMouseMove, false);
+ document.addEventListener('mouseup', onMouseUp, false);
+
+ scope.dispatchEvent(startEvent);
+ }
+ }
+
+
+ scope.domElement.addEventListener('contextmenu', onContextMenu, false);
+
+ scope.domElement.addEventListener('mousedown', onMouseDown, false);
+ scope.domElement.addEventListener('wheel', onMouseWheel, false);
+
+ scope.domElement.addEventListener('keydown', onDocumentKeyDown, false);
+ scope.domElement.addEventListener('keyup', onDocumentKeyUp, false);
+
+ scope.domElement.addEventListener('touchstart', onTouchStart, false);
+ scope.domElement.addEventListener('touchend', onTouchEnd, false);
+ scope.domElement.addEventListener('touchmove', onTouchMove, false);
+
+ // make sure element can receive keys.
+
+ if (scope.domElement.tabIndex === -1) {
+ scope.domElement.tabIndex = 0;
+ }
+
+ //
+ // public methods
+ //
+
+ this.getPolarAngle = () => spherical.phi;
+
+ this.getAzimuthalAngle = () => spherical.theta;
+
+ this.saveState = () => {
+ scope.target0.copy(scope.target);
+ scope.position0.copy(scope.object.position);
+ scope.zoom0 = scope.object.zoom;
+ };
+
+ this.reset = () => {
+ scope.target.copy(scope.target0);
+ scope.object.position.copy(scope.position0);
+ scope.object.zoom = scope.zoom0;
+
+ scope.object.updateProjectionMatrix();
+ scope.dispatchEvent(changeEvent);
+
+ scope.update();
+
+ state = STATE.NONE;
+ };
+
+ // this method is exposed, but perhaps it would be better if we can make it private...
+ this.update = (() => {
+ const offset = new Vector3();
+
+ // so camera.up is the orbit axis
+ const quat = new Quaternion()
+ .setFromUnitVectors(object.up, new Vector3(0, 1, 0));
+ const quatInverse = quat.clone().inverse();
+
+ const lastPosition = new Vector3();
+ const lastQuaternion = new Quaternion();
+
+ const direction = new Vector3();
+ const velocity = new Vector3();
+ let prevTime = Date.now();
+ let logTime = Date.now();
+ const vec = new Vector3();
+
+ return function update() {
+ const time = Date.now();
+
+ let log = false;
+ if (time - logTime > 2000) {
+ logTime = time;
+ log = true;
+ }
+
+ const delta = (time - prevTime) / 1000.0;
+ velocity.x -= velocity.x * 50.0 * delta;
+ velocity.y -= velocity.y * 50.0 * delta;
+ velocity.z -= velocity.z * 50.0 * delta;
+ const length = velocity.length();
+ if (length < 1 || length > 10) {
+ velocity.set(0, 0, 0);
+ }
+
+ direction.x = Number(moveRight) - Number(moveLeft);
+ direction.y = Number(moveUp) - Number(moveDown);
+ direction.z = Number(moveForward) - Number(moveBackward);
+ direction.normalize();
+
+ if (moveLeft || moveRight) {
+ velocity.x -= direction.x * 5000.0 * delta;
+ }
+ if (moveUp || moveDown) {
+ velocity.y -= direction.y * 5000.0 * delta;
+ }
+ if (moveForward || moveBackward) {
+ velocity.z -= direction.z * 2500.0 * delta;
+ }
+
+ // controls.moveRight( -velocity.x * delta);
+ // controls.moveUp( -velocity.y * delta);
+ // forward
+ if (log) {
+ console.log(vec.x, vec.y, vec.z, delta);
+ }
+ vec.setFromMatrixColumn(scope.object.matrix, 0);
+ vec.crossVectors(scope.object.up, vec);
+ vec.multiplyScalar(-velocity.z * delta);
+ vec.y += -velocity.y * delta;
+ panOffset.add(vec);
+ vec.setFromMatrixColumn(scope.object.matrix, 0);
+ vec.multiplyScalar(-velocity.x * delta);
+ panOffset.add(vec);
+
+ prevTime = time;
+
+
+ const { position } = scope.object;
+
+ offset.copy(position).sub(scope.target);
+
+ // rotate offset to "y-axis-is-up" space
+ offset.applyQuaternion(quat);
+
+ // angle from z-axis around y-axis
+ spherical.setFromVector3(offset);
+
+ if (scope.autoRotate && state === STATE.NONE) {
+ rotateLeft(getAutoRotationAngle());
+ }
+
+ if (scope.enableDamping) {
+ spherical.theta += sphericalDelta.theta * scope.dampingFactor;
+ spherical.phi += sphericalDelta.phi * scope.dampingFactor;
+ } else {
+ spherical.theta += sphericalDelta.theta;
+ spherical.phi += sphericalDelta.phi;
+ }
+
+ // restrict theta to be between desired limits
+ spherical.theta = Math.max(
+ scope.minAzimuthAngle,
+ Math.min(scope.maxAzimuthAngle, spherical.theta),
+ );
+
+ // restrict phi to be between desired limits
+ spherical.phi = Math.max(
+ scope.minPolarAngle,
+ Math.min(scope.maxPolarAngle, spherical.phi),
+ );
+ spherical.makeSafe();
+
+ spherical.radius *= scale;
+
+ // restrict radius to be between desired limits
+ spherical.radius = Math.max(
+ scope.minDistance,
+ Math.min(scope.maxDistance, spherical.radius),
+ );
+
+ // move target to panned location
+
+ if (panOffset.length() > 10000) {
+ panOffset.set(0, 0, 0);
+ }
+ if (scope.enableDamping === true) {
+ scope.target.addScaledVector(panOffset, scope.dampingFactor);
+ } else {
+ scope.target.add(panOffset);
+ }
+ if (scope.target.y < 10.0) {
+ scope.target.y = 10.0;
+ }
+
+
+ offset.setFromSpherical(spherical);
+
+ // rotate offset back to "camera-up-vector-is-up" space
+ offset.applyQuaternion(quatInverse);
+
+ position.copy(scope.target).add(offset);
+
+ scope.object.lookAt(scope.target);
+
+ if (scope.enableDamping === true) {
+ sphericalDelta.theta *= (1 - scope.dampingFactor);
+ sphericalDelta.phi *= (1 - scope.dampingFactor);
+
+ panOffset.multiplyScalar(1 - scope.dampingFactor);
+ } else {
+ sphericalDelta.set(0, 0, 0);
+
+ panOffset.set(0, 0, 0);
+ }
+
+ scale = 1;
+
+ // update condition is:
+ // min(camera displacement, camera rotation in radians)^2 > EPS
+ // using small-angle approximation cos(x/2) = 1 - x^2 / 8
+
+ if (zoomChanged
+ || lastPosition.distanceToSquared(scope.object.position) > EPS
+ || 8 * (1 - lastQuaternion.dot(scope.object.quaternion)) > EPS) {
+ scope.dispatchEvent(changeEvent);
+
+ lastPosition.copy(scope.object.position);
+ lastQuaternion.copy(scope.object.quaternion);
+ zoomChanged = false;
+
+ return true;
+ }
+
+ return false;
+ };
+ })();
+
+ this.dispose = () => {
+ scope.domElement.removeEventListener('contextmenu', onContextMenu, false);
+ scope.domElement.removeEventListener('mousedown', onMouseDown, false);
+ scope.domElement.removeEventListener('wheel', onMouseWheel, false);
+
+ scope.domElement.removeEventListener('touchstart', onTouchStart, false);
+ scope.domElement.removeEventListener('touchend', onTouchEnd, false);
+ scope.domElement.removeEventListener('touchmove', onTouchMove, false);
+
+ document.removeEventListener('mousemove', onMouseMove, false);
+ document.removeEventListener('mouseup', onMouseUp, false);
+ document.removeEventListener('keydown', onDocumentKeyDown, false);
+ document.removeEventListener('keyup', onDocumentKeyUp, false);
+
+ // scope.dispatchEvent( { type: 'dispose' } ); // should this be added here?
+ };
+
+ this.update();
}
+}
+// VoxelPainterControls.prototype = Object.create(EventDispatcher.prototype);
+// VoxelPainterControls.prototype.constructor = VoxelPainterControls;
- function onMouseMove( event ) {
- if ( scope.enabled === false ) return;
-
- event.preventDefault();
-
- switch ( state ) {
-
- case STATE.ROTATE:
-
- if ( scope.enableRotate === false ) return;
-
- handleMouseMoveRotate( event );
-
- break;
-
- case STATE.DOLLY:
-
- if ( scope.enableZoom === false ) return;
-
- handleMouseMoveDolly( event );
-
- break;
-
- case STATE.PAN:
-
- if ( scope.enablePan === false ) return;
-
- handleMouseMovePan( event );
-
- break;
-
- }
-
- }
-
- function onMouseUp( event ) {
-
- if ( scope.enabled === false ) return;
-
- handleMouseUp( event );
-
- document.removeEventListener( 'mousemove', onMouseMove, false );
- document.removeEventListener( 'mouseup', onMouseUp, false );
-
- scope.dispatchEvent( endEvent );
-
- state = STATE.NONE;
-
- }
-
- function onMouseWheel( event ) {
-
- if ( scope.enabled === false || scope.enableZoom === false || ( state !== STATE.NONE && state !== STATE.ROTATE ) ) return;
-
- event.preventDefault();
- event.stopPropagation();
-
- scope.dispatchEvent( startEvent );
-
- handleMouseWheel( event );
-
- scope.dispatchEvent( endEvent );
-
- }
-
- function onTouchStart( event ) {
-
- if ( scope.enabled === false ) return;
-
- event.preventDefault();
-
- switch ( event.touches.length ) {
-
- case 1:
-
- switch ( scope.touches.ONE ) {
-
- case TOUCH.ROTATE:
-
- if ( scope.enableRotate === false ) return;
-
- handleTouchStartRotate( event );
-
- state = STATE.TOUCH_ROTATE;
-
- break;
-
- case TOUCH.PAN:
-
- if ( scope.enablePan === false ) return;
-
- handleTouchStartPan( event );
-
- state = STATE.TOUCH_PAN;
-
- break;
-
- default:
-
- state = STATE.NONE;
-
- }
-
- break;
-
- case 2:
-
- switch ( scope.touches.TWO ) {
-
- case TOUCH.DOLLY_PAN:
-
- if ( scope.enableZoom === false && scope.enablePan === false ) return;
-
- handleTouchStartDollyPan( event );
-
- state = STATE.TOUCH_DOLLY_PAN;
-
- break;
-
- case TOUCH.DOLLY_ROTATE:
-
- if ( scope.enableZoom === false && scope.enableRotate === false ) return;
-
- handleTouchStartDollyRotate( event );
-
- state = STATE.TOUCH_DOLLY_ROTATE;
-
- break;
-
- default:
-
- state = STATE.NONE;
-
- }
-
- break;
-
- default:
-
- state = STATE.NONE;
-
- }
-
- if ( state !== STATE.NONE ) {
-
- scope.dispatchEvent( startEvent );
-
- }
-
- }
-
- function onTouchMove( event ) {
-
- if ( scope.enabled === false ) return;
-
- event.preventDefault();
- event.stopPropagation();
-
- switch ( state ) {
-
- case STATE.TOUCH_ROTATE:
-
- if ( scope.enableRotate === false ) return;
-
- handleTouchMoveRotate( event );
-
- scope.update();
-
- break;
-
- case STATE.TOUCH_PAN:
-
- if ( scope.enablePan === false ) return;
-
- handleTouchMovePan( event );
-
- scope.update();
-
- break;
-
- case STATE.TOUCH_DOLLY_PAN:
-
- if ( scope.enableZoom === false && scope.enablePan === false ) return;
-
- handleTouchMoveDollyPan( event );
-
- scope.update();
-
- break;
-
- case STATE.TOUCH_DOLLY_ROTATE:
-
- if ( scope.enableZoom === false && scope.enableRotate === false ) return;
-
- handleTouchMoveDollyRotate( event );
-
- scope.update();
-
- break;
-
- default:
-
- state = STATE.NONE;
-
- }
-
- }
-
- function onTouchEnd( event ) {
-
- if ( scope.enabled === false ) return;
-
- handleTouchEnd( event );
-
- scope.dispatchEvent( endEvent );
-
- state = STATE.NONE;
-
- }
-
- function onContextMenu( event ) {
-
- if ( scope.enabled === false ) return;
-
- event.preventDefault();
-
- }
-
- //
-
- scope.domElement.addEventListener( 'contextmenu', onContextMenu, false );
-
- scope.domElement.addEventListener( 'mousedown', onMouseDown, false );
- scope.domElement.addEventListener( 'wheel', onMouseWheel, false );
-
- scope.domElement.addEventListener( 'keydown', onDocumentKeyDown, false );
- scope.domElement.addEventListener( 'keyup', onDocumentKeyUp, false );
-
- scope.domElement.addEventListener( 'touchstart', onTouchStart, false );
- scope.domElement.addEventListener( 'touchend', onTouchEnd, false );
- scope.domElement.addEventListener( 'touchmove', onTouchMove, false );
-
- // make sure element can receive keys.
-
- if ( scope.domElement.tabIndex === - 1 ) {
-
- scope.domElement.tabIndex = 0;
-
- }
-
- // force an update at start
-
- this.update();
-
-};
-
-VoxelPainterControls.prototype = Object.create( EventDispatcher.prototype );
-VoxelPainterControls.prototype.constructor = VoxelPainterControls;
-
-
-// This set of controls performs orbiting, dollying (zooming), and panning.
-// Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default).
-// This is very similar to VoxelPainterControls, another set of touch behavior
-//
-// Orbit - right mouse, or left mouse + ctrl/meta/shiftKey / touch: two-finger rotate
-// Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish
-// Pan - left mouse, or arrow keys / touch: one-finger move
-
-var MapControls = function ( object, domElement ) {
-
- VoxelPainterControls.call( this, object, domElement );
-
- this.mouseButtons.LEFT = MOUSE.PAN;
- this.mouseButtons.RIGHT = MOUSE.ROTATE;
-
- this.touches.ONE = TOUCH.PAN;
- this.touches.TWO = TOUCH.DOLLY_ROTATE;
-
-};
-
-MapControls.prototype = Object.create( EventDispatcher.prototype );
-MapControls.prototype.constructor = MapControls;
-
-export { VoxelPainterControls, MapControls };
+export default VoxelPainterControls;
diff --git a/src/controls/keypress.js b/src/controls/keypress.js
new file mode 100644
index 0000000..597ca50
--- /dev/null
+++ b/src/controls/keypress.js
@@ -0,0 +1,37 @@
+/*
+ * keypress actions
+ * @flow
+ */
+import keycode from 'keycode';
+
+import store from '../ui/store';
+import {
+ toggleGrid,
+ togglePixelNotify,
+ toggleMute,
+} from '../actions';
+
+
+function onKeyPress(event: KeyboardEvent) {
+ // ignore key presses if modal is open or chat is used
+ if (event.target.nodeName === 'INPUT'
+ || event.target.nodeName === 'TEXTAREA'
+ ) {
+ return;
+ }
+
+ switch (keycode(event)) {
+ case 'g':
+ store.dispatch(toggleGrid());
+ break;
+ case 'c':
+ store.dispatch(togglePixelNotify());
+ break;
+ case 'm':
+ store.dispatch(toggleMute());
+ break;
+ default:
+ }
+}
+
+export default onKeyPress;
diff --git a/src/core/Image.js b/src/core/Image.js
index ecf239c..7d5c3c3 100644
--- a/src/core/Image.js
+++ b/src/core/Image.js
@@ -40,15 +40,17 @@ export async function imageABGR2Canvas(
const canvasMinXY = -(canvas.size / 2);
const imageData = new Uint32Array(data.buffer);
- const [ucx, ucy] = getChunkOfPixel([x, y], canvas.size);
- const [lcx, lcy] = getChunkOfPixel([(x + width), (y + height)], canvas.size);
+ const [ucx, ucy] = getChunkOfPixel(canvas.size, x, y);
+ const [lcx, lcy] = getChunkOfPixel(canvas.size, x + width, y + height);
logger.info(`Loading to chunks from ${ucx} / ${ucy} to ${lcx} / ${lcy} ...`);
let chunk;
for (let cx = ucx; cx <= lcx; cx += 1) {
for (let cy = ucy; cy <= lcy; cy += 1) {
chunk = await RedisCanvas.getChunk(cx, cy, canvasId);
- chunk = (chunk) ? new Uint8Array(chunk) : new Uint8Array(TILE_SIZE * TILE_SIZE);
+ chunk = (chunk)
+ ? new Uint8Array(chunk)
+ : new Uint8Array(TILE_SIZE * TILE_SIZE);
// offset of chunk in image
const cOffX = cx * TILE_SIZE + canvasMinXY - x;
const cOffY = cy * TILE_SIZE + canvasMinXY - y;
@@ -60,7 +62,9 @@ export async function imageABGR2Canvas(
const clrY = cOffY + py;
if (clrX >= 0 && clrY >= 0 && clrX < width && clrY < height) {
const clr = imageData[clrX + clrY * width];
- const clrIndex = (wipe) ? palette.abgr.indexOf(clr) : palette.abgr.indexOf(clr, 2);
+ const clrIndex = (wipe)
+ ? palette.abgr.indexOf(clr)
+ : palette.abgr.indexOf(clr, 2);
if (~clrIndex) {
const pixel = (protect) ? (clrIndex | 0x20) : clrIndex;
chunk[cOff] = pixel;
@@ -113,15 +117,17 @@ export async function imagemask2Canvas(
const imageData = new Uint8Array(data.buffer);
- const [ucx, ucy] = getChunkOfPixel([x, y], canvas.size);
- const [lcx, lcy] = getChunkOfPixel([(x + width), (y + height)], canvas.size);
+ const [ucx, ucy] = getChunkOfPixel(canvas.size, x, y);
+ const [lcx, lcy] = getChunkOfPixel(canvas.size, x + width, y + height);
logger.info(`Loading to chunks from ${ucx} / ${ucy} to ${lcx} / ${lcy} ...`);
let chunk;
for (let cx = ucx; cx <= lcx; cx += 1) {
for (let cy = ucy; cy <= lcy; cy += 1) {
chunk = await RedisCanvas.getChunk(cx, cy, canvasId);
- chunk = (chunk) ? new Uint8Array(chunk) : new Uint8Array(TILE_SIZE * TILE_SIZE);
+ chunk = (chunk)
+ ? new Uint8Array(chunk)
+ : new Uint8Array(TILE_SIZE * TILE_SIZE);
// offset of chunk in image
const cOffX = cx * TILE_SIZE + canvasMinXY - x;
const cOffY = cy * TILE_SIZE + canvasMinXY - y;
@@ -133,7 +139,10 @@ export async function imagemask2Canvas(
const clrY = cOffY + py;
if (clrX >= 0 && clrY >= 0 && clrX < width && clrY < height) {
let offset = (clrX + clrY * width) * 3;
- if (!imageData[offset++] && !imageData[offset++] && !imageData[offset]) {
+ if (!imageData[offset++]
+ && !imageData[offset++]
+ && !imageData[offset]
+ ) {
chunk[cOff] = filter(palette.abgr[chunk[cOff]]);
pxlCnt += 1;
}
diff --git a/src/core/constants.js b/src/core/constants.js
index 1cc9ba5..aced40c 100644
--- a/src/core/constants.js
+++ b/src/core/constants.js
@@ -62,6 +62,9 @@ export const DEFAULT_CANVASES = {
export const TILE_LOADING_IMAGE = './loading.png';
+// constants for 3D voxel canvas
+export const THREE_CANVAS_HEIGHT = 128;
+export const THREE_TILE_SIZE = 64;
// one bigchunk has 16x16 smallchunks, one smallchunk has 64x64 pixel, so one bigchunk is 1024x1024 pixels
export const TILE_SIZE = 256;
// how much to scale for a new tiled zoomlevel
diff --git a/src/core/draw.js b/src/core/draw.js
index 2631165..98c6bd4 100644
--- a/src/core/draw.js
+++ b/src/core/draw.js
@@ -8,9 +8,10 @@ import { getChunkOfPixel, getOffsetOfPixel } from './utils';
import webSockets from '../socket/websockets';
import logger from './logger';
import RedisCanvas from '../data/models/RedisCanvas';
-import { registerPixelChange } from './tileserver';
import canvases from '../canvases.json';
+import { THREE_CANVAS_HEIGHT } from './constants';
+
/**
*
@@ -21,13 +22,14 @@ import canvases from '../canvases.json';
*/
export function setPixel(
canvasId: number,
+ color: ColorIndex,
x: number,
y: number,
- color: ColorIndex,
+ z: number = null,
) {
const canvasSize = canvases[canvasId].size;
- const [i, j] = getChunkOfPixel([x, y], canvasSize);
- const offset = getOffsetOfPixel(x, y, canvasSize);
+ const [i, j] = getChunkOfPixel(canvasSize, x, y, z);
+ const offset = getOffsetOfPixel(canvasSize, x, y, z);
RedisCanvas.setPixelInChunk(i, j, offset, color, canvasId);
webSockets.broadcastPixel(canvasId, i, j, offset, color);
}
@@ -44,9 +46,10 @@ export function setPixel(
async function draw(
user: User,
canvasId: number,
+ color: ColorIndex,
x: number,
y: number,
- color: ColorIndex,
+ z: number = null,
): Promise