go through store with moveUVW and make movement controls work,
experiment with touch controls
This commit is contained in:
parent
236e83694b
commit
a14b16247a
|
@ -4,7 +4,10 @@
|
|||
|
||||
import { persistStore } from 'redux-persist';
|
||||
|
||||
import createKeyPressHandler from './controls/keypress';
|
||||
import {
|
||||
createKeyDownHandler,
|
||||
createKeyUpHandler,
|
||||
} from './controls/keypress';
|
||||
import {
|
||||
initTimer,
|
||||
urlChange,
|
||||
|
@ -57,8 +60,10 @@ persistStore(store, {}, () => {
|
|||
window.name = 'main';
|
||||
renderApp(document.getElementById('app'), store);
|
||||
|
||||
const onKeyPress = createKeyPressHandler(store);
|
||||
document.addEventListener('keydown', onKeyPress, false);
|
||||
const onKeyDown = createKeyDownHandler(store);
|
||||
const onKeyUp = createKeyUpHandler(store);
|
||||
document.addEventListener('keydown', onKeyDown, false);
|
||||
document.addEventListener('keyup', onKeyUp, false);
|
||||
|
||||
// garbage collection
|
||||
setInterval(() => {
|
||||
|
|
|
@ -3,29 +3,33 @@
|
|||
* Menu for WASD keys for mobile users
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { useSelector, shallowEqual } from 'react-redux';
|
||||
import React, {
|
||||
useCallback,
|
||||
} from 'react';
|
||||
import {
|
||||
useSelector,
|
||||
shallowEqual,
|
||||
useDispatch,
|
||||
} from 'react-redux';
|
||||
|
||||
import { getRenderer } from '../../ui/rendererFactory';
|
||||
import {
|
||||
setMoveU,
|
||||
setMoveV,
|
||||
setMoveW,
|
||||
} from '../../store/actions';
|
||||
|
||||
const btnStyle = {
|
||||
fontSize: 34,
|
||||
};
|
||||
|
||||
function cancelMovement() {
|
||||
const renderer = getRenderer();
|
||||
renderer.controls.moveU = 0;
|
||||
renderer.controls.moveV = 0;
|
||||
renderer.controls.moveW = 0;
|
||||
}
|
||||
|
||||
const MovementControls = () => {
|
||||
const [pencilEnabled, is3D] = useSelector((state) => [
|
||||
state.gui.pencilEnabled,
|
||||
const [holdPaint, is3D] = useSelector((state) => [
|
||||
state.gui.holdPaint,
|
||||
state.canvas.is3D,
|
||||
], shallowEqual);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
if (!pencilEnabled && !is3D) {
|
||||
if (!holdPaint && !is3D) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -37,25 +41,13 @@ const MovementControls = () => {
|
|||
tabIndex={0}
|
||||
style={{
|
||||
...btnStyle,
|
||||
// left: 46,
|
||||
left: 57,
|
||||
// bottom: 128,
|
||||
bottom: 139,
|
||||
}}
|
||||
onMouseDown={() => {
|
||||
getRenderer().controls.moveV = -1;
|
||||
}}
|
||||
onMouseUp={() => {
|
||||
getRenderer().controls.moveV = 0;
|
||||
}}
|
||||
onTouchStart={() => {
|
||||
getRenderer().controls.moveV = -1;
|
||||
}}
|
||||
onTouchEnd={() => {
|
||||
getRenderer().controls.moveV = 0;
|
||||
}}
|
||||
onTouchCancel={cancelMovement}
|
||||
onMouseLeave={cancelMovement}
|
||||
onMouseDown={() => dispatch(setMoveV(-1))}
|
||||
onMouseUp={() => dispatch(setMoveV(0))}
|
||||
onTouchStart={() => dispatch(setMoveV(-1))}
|
||||
onTouchEnd={() => dispatch(setMoveV(0))}
|
||||
>
|
||||
↑
|
||||
</div>
|
||||
|
@ -65,24 +57,13 @@ const MovementControls = () => {
|
|||
tabIndex={0}
|
||||
style={{
|
||||
...btnStyle,
|
||||
// left: 46,
|
||||
left: 57,
|
||||
bottom: 98,
|
||||
}}
|
||||
onMouseDown={() => {
|
||||
getRenderer().controls.moveV = 1;
|
||||
}}
|
||||
onMouseUp={() => {
|
||||
getRenderer().controls.moveV = 0;
|
||||
}}
|
||||
onTouchStart={() => {
|
||||
getRenderer().controls.moveV = 1;
|
||||
}}
|
||||
onTouchEnd={() => {
|
||||
getRenderer().controls.moveV = 0;
|
||||
}}
|
||||
onTouchCancel={cancelMovement}
|
||||
onMouseLeave={cancelMovement}
|
||||
onMouseDown={() => dispatch(setMoveV(1))}
|
||||
onMouseUp={() => dispatch(setMoveV(0))}
|
||||
onTouchStart={(event) => dispatch(setMoveV(1))}
|
||||
onTouchEnd={(event) => dispatch(setMoveV(0))}
|
||||
>
|
||||
↓
|
||||
</div>
|
||||
|
@ -95,20 +76,10 @@ const MovementControls = () => {
|
|||
left: 16,
|
||||
bottom: 98,
|
||||
}}
|
||||
onMouseDown={() => {
|
||||
getRenderer().controls.moveU = -1;
|
||||
}}
|
||||
onMouseUp={() => {
|
||||
getRenderer().controls.moveU = 0;
|
||||
}}
|
||||
onTouchStart={() => {
|
||||
getRenderer().controls.moveU = -1;
|
||||
}}
|
||||
onTouchEnd={() => {
|
||||
getRenderer().controls.moveU = 0;
|
||||
}}
|
||||
onTouchCancel={cancelMovement}
|
||||
onMouseLeave={cancelMovement}
|
||||
onMouseDown={() => dispatch(setMoveU(-1))}
|
||||
onMouseUp={() => dispatch(setMoveU(0))}
|
||||
onTouchStart={() => dispatch(setMoveU(-1))}
|
||||
onTouchEnd={() => dispatch(setMoveU(0))}
|
||||
>
|
||||
←
|
||||
</div>
|
||||
|
@ -118,24 +89,13 @@ const MovementControls = () => {
|
|||
tabIndex={0}
|
||||
style={{
|
||||
...btnStyle,
|
||||
// left: 76,
|
||||
left: 98,
|
||||
bottom: 98,
|
||||
}}
|
||||
onMouseDown={() => {
|
||||
getRenderer().controls.moveU = 1;
|
||||
}}
|
||||
onMouseUp={() => {
|
||||
getRenderer().controls.moveU = 0;
|
||||
}}
|
||||
onTouchStart={() => {
|
||||
getRenderer().controls.moveU = 1;
|
||||
}}
|
||||
onTouchEnd={() => {
|
||||
getRenderer().controls.moveU = 0;
|
||||
}}
|
||||
onTouchCancel={cancelMovement}
|
||||
onMouseLeave={cancelMovement}
|
||||
onMouseDown={() => dispatch(setMoveU(1))}
|
||||
onMouseUp={() => dispatch(setMoveU(0))}
|
||||
onTouchStart={() => dispatch(setMoveU(1))}
|
||||
onTouchEnd={() => dispatch(setMoveU(0))}
|
||||
>
|
||||
→
|
||||
</div>
|
||||
|
@ -145,24 +105,13 @@ const MovementControls = () => {
|
|||
tabIndex={0}
|
||||
style={{
|
||||
...btnStyle,
|
||||
// left: 76,
|
||||
left: 16,
|
||||
bottom: 139,
|
||||
}}
|
||||
onMouseDown={() => {
|
||||
getRenderer().controls.moveW = -1;
|
||||
}}
|
||||
onMouseUp={() => {
|
||||
getRenderer().controls.moveW = 0;
|
||||
}}
|
||||
onTouchStart={() => {
|
||||
getRenderer().controls.moveW = -1;
|
||||
}}
|
||||
onTouchEnd={() => {
|
||||
getRenderer().controls.moveW = 0;
|
||||
}}
|
||||
onTouchCancel={cancelMovement}
|
||||
onMouseLeave={cancelMovement}
|
||||
onMouseDown={() => dispatch(setMoveW(-1))}
|
||||
onMouseUp={() => dispatch(setMoveW(0))}
|
||||
onTouchStart={() => dispatch(setMoveW(-1))}
|
||||
onTouchEnd={() => dispatch(setMoveW(0))}
|
||||
>
|
||||
↖
|
||||
</div>
|
||||
|
@ -172,24 +121,13 @@ const MovementControls = () => {
|
|||
tabIndex={0}
|
||||
style={{
|
||||
...btnStyle,
|
||||
// left: 76,
|
||||
left: 98,
|
||||
bottom: 139,
|
||||
}}
|
||||
onMouseDown={() => {
|
||||
getRenderer().controls.moveW = 1;
|
||||
}}
|
||||
onMouseUp={() => {
|
||||
getRenderer().controls.moveW = 0;
|
||||
}}
|
||||
onTouchStart={() => {
|
||||
getRenderer().controls.moveW = 1;
|
||||
}}
|
||||
onTouchEnd={() => {
|
||||
getRenderer().controls.moveW = 0;
|
||||
}}
|
||||
onTouchCancel={cancelMovement}
|
||||
onMouseLeave={cancelMovement}
|
||||
onMouseDown={() => dispatch(setMoveW(1))}
|
||||
onMouseUp={() => dispatch(setMoveW(0))}
|
||||
onTouchStart={() => dispatch(setMoveW(1))}
|
||||
onTouchEnd={() => dispatch(setMoveW(0))}
|
||||
>
|
||||
↘
|
||||
</div>
|
||||
|
|
|
@ -7,22 +7,29 @@ import { useSelector, useDispatch } from 'react-redux';
|
|||
import { TbPencil, TbPencilMinus } from 'react-icons/tb';
|
||||
import { t } from 'ttag';
|
||||
|
||||
import { togglePencil } from '../../store/actions';
|
||||
import { HOLD_PAINT } from '../../core/constants';
|
||||
import { selectHoldPaint } from '../../store/actions';
|
||||
|
||||
const PencilButton = () => {
|
||||
const pencilEnabled = useSelector((state) => state.gui.pencilEnabled);
|
||||
const holdPaint = useSelector((state) => state.gui.holdPaint);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
return (
|
||||
<div
|
||||
id="pencilbutton"
|
||||
className={`actionbuttons${pencilEnabled ? ' pressed' : ''}`}
|
||||
className={
|
||||
`actionbuttons${(holdPaint === HOLD_PAINT.PENCIL) ? ' pressed' : ''}`
|
||||
}
|
||||
role="button"
|
||||
title={(pencilEnabled) ? t`Disable Pencil` : t`Enable Pencil`}
|
||||
title={(holdPaint === HOLD_PAINT.PENCIL)
|
||||
? t`Disable Pencil`
|
||||
: t`Enable Pencil`}
|
||||
tabIndex={-1}
|
||||
onClick={() => dispatch(togglePencil())}
|
||||
onClick={() => dispatch(selectHoldPaint(
|
||||
(holdPaint === HOLD_PAINT.PENCIL) ? HOLD_PAINT.OFF : HOLD_PAINT.PENCIL,
|
||||
))}
|
||||
>
|
||||
{pencilEnabled ? <TbPencilMinus /> : <TbPencil />}
|
||||
{(holdPaint === HOLD_PAINT.PENCIL) ? <TbPencilMinus /> : <TbPencil />}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -17,6 +17,9 @@ import {
|
|||
getChunkOfPixel,
|
||||
getOffsetOfPixel,
|
||||
} from '../core/utils';
|
||||
import {
|
||||
HOLD_PAINT,
|
||||
} from '../core/constants';
|
||||
|
||||
class PixelPainterControls {
|
||||
store;
|
||||
|
@ -27,28 +30,19 @@ class PixelPainterControls {
|
|||
clickTapStartTime = 0;
|
||||
clickTapStartCoords = [0, 0];
|
||||
tapStartDist = 50;
|
||||
//
|
||||
// on mouse: true as long as left mouse button is pressed
|
||||
// on touch: set to true when one finger touches the screen
|
||||
// set to false when second finger touches or touch ends
|
||||
isClicking = false;
|
||||
// on touch: true if more than one finger on screen
|
||||
isMultiTab = false;
|
||||
isMultiTap = false;
|
||||
// on touch: true if current tab was ever more than one figher at any time
|
||||
wasEverMultiTap = false;
|
||||
// on touch: when painting with holdPaint is active
|
||||
isTapPainting = false;
|
||||
// on touch: timeout to detect long-press
|
||||
tapTimeout = null;
|
||||
/*
|
||||
* if we are shift-hold-painting
|
||||
* 0: no
|
||||
* 1: left shift
|
||||
* 2: right shift
|
||||
*/
|
||||
holdPainting = 0;
|
||||
// if we are moving
|
||||
moveU = 0;
|
||||
moveV = 0;
|
||||
moveW = 0;
|
||||
// time of last tick
|
||||
prevTime = Date.now();
|
||||
// if we are waiting before placing pixel via holdPainting again
|
||||
// if we are waiting before placing pixel via holdPaint again
|
||||
coolDownDelta = false;
|
||||
|
||||
constructor(renderer, viewport, store) {
|
||||
|
@ -57,8 +51,6 @@ class PixelPainterControls {
|
|||
this.viewport = viewport;
|
||||
|
||||
this.onMouseDown = this.onMouseDown.bind(this);
|
||||
this.onKeyDown = this.onKeyDown.bind(this);
|
||||
this.onKeyUp = this.onKeyUp.bind(this);
|
||||
this.onAuxClick = this.onAuxClick.bind(this);
|
||||
this.onMouseOut = this.onMouseOut.bind(this);
|
||||
this.onMouseMove = this.onMouseMove.bind(this);
|
||||
|
@ -68,8 +60,6 @@ class PixelPainterControls {
|
|||
this.onTouchEnd = this.onTouchEnd.bind(this);
|
||||
this.onTouchMove = this.onTouchMove.bind(this);
|
||||
|
||||
document.addEventListener('keydown', this.onKeyDown, false);
|
||||
document.addEventListener('keyup', this.onKeyUp, false);
|
||||
viewport.addEventListener('auxclick', this.onAuxClick, false);
|
||||
viewport.addEventListener('mousedown', this.onMouseDown, false);
|
||||
viewport.addEventListener('mousemove', this.onMouseMove, false);
|
||||
|
@ -82,10 +72,8 @@ class PixelPainterControls {
|
|||
viewport.addEventListener('touchcancel', this.onMouseOut, false);
|
||||
}
|
||||
|
||||
dispose() {
|
||||
document.removeEventListener('keydown', this.onKeyDown, false);
|
||||
document.removeEventListener('keyup', this.onKeyUp, false);
|
||||
}
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
dispose() {}
|
||||
|
||||
gotCoolDownDelta(delta) {
|
||||
this.coolDownDelta = true;
|
||||
|
@ -129,16 +117,10 @@ class PixelPainterControls {
|
|||
// thresholds for single click / holding
|
||||
if (clickTapStartTime > Date.now() - 250
|
||||
&& coordsDiff[0] < 2 && coordsDiff[1] < 2) {
|
||||
const cell = screenToWorld(
|
||||
renderer.view,
|
||||
renderer.viewscale,
|
||||
this.viewport,
|
||||
[clientX, clientY],
|
||||
);
|
||||
PixelPainterControls.placePixel(
|
||||
store,
|
||||
renderer,
|
||||
cell,
|
||||
this.screenToWorld([clientX, clientY]),
|
||||
);
|
||||
}
|
||||
this.viewport.style.cursor = 'auto';
|
||||
|
@ -147,22 +129,14 @@ class PixelPainterControls {
|
|||
}
|
||||
|
||||
static getTouchCenter(event) {
|
||||
switch (event.touches.length) {
|
||||
case 1: {
|
||||
const { pageX, pageY } = event.touches[0];
|
||||
return [pageX, pageY];
|
||||
}
|
||||
case 2: {
|
||||
const pageX = Math.floor(0.5
|
||||
* (event.touches[0].pageX + event.touches[1].pageX));
|
||||
const pageY = Math.floor(0.5
|
||||
* (event.touches[0].pageY + event.touches[1].pageY));
|
||||
return [pageX, pageY];
|
||||
}
|
||||
default:
|
||||
break;
|
||||
let x = 0;
|
||||
let y = 0;
|
||||
for (const { pageX, pageY } of event.touches) {
|
||||
x += pageX;
|
||||
y += pageY;
|
||||
}
|
||||
return null;
|
||||
const { length } = event.touches;
|
||||
return [x / length, y / length];
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -171,22 +145,25 @@ class PixelPainterControls {
|
|||
*/
|
||||
static placePixel(store, renderer, cell, colorIndex = null) {
|
||||
const state = store.getState();
|
||||
const { autoZoomIn } = state.gui;
|
||||
const { clrIgnore, isHistoricalView } = state.canvas;
|
||||
if (state.canvas.isHistoricalView) {
|
||||
return;
|
||||
}
|
||||
const selectedColor = colorIndex
|
||||
?? PixelPainterControls.getWantedColor(state, renderer, cell);
|
||||
if (selectedColor === null) {
|
||||
return;
|
||||
}
|
||||
const { viewscale: scale } = renderer;
|
||||
const selectedColor = (colorIndex === null)
|
||||
? state.canvas.selectedColor
|
||||
: colorIndex;
|
||||
|
||||
if (isHistoricalView) return;
|
||||
|
||||
if (autoZoomIn && scale < 8) {
|
||||
if (state.gui.autoZoomIn && scale < 8) {
|
||||
renderer.updateView([cell[0], cell[1], 12]);
|
||||
return;
|
||||
}
|
||||
|
||||
// allow placing of pixel just on low zoomlevels
|
||||
if (scale < 3) return;
|
||||
if (scale < 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
const curColor = renderer.getColorIndexOfPixel(...cell);
|
||||
if (selectedColor === curColor) {
|
||||
|
@ -194,7 +171,7 @@ class PixelPainterControls {
|
|||
}
|
||||
|
||||
// placing unset pixel
|
||||
if (selectedColor < clrIgnore) {
|
||||
if (selectedColor < state.canvas.clrIgnore) {
|
||||
const { palette } = state.canvas;
|
||||
const { rgb } = palette;
|
||||
let clrOffset = selectedColor * 3;
|
||||
|
@ -249,35 +226,48 @@ class PixelPainterControls {
|
|||
document.activeElement.blur();
|
||||
|
||||
this.renderer.cancelStoreViewInState();
|
||||
this.clearTabTimeout();
|
||||
this.isTapPainting = false;
|
||||
this.clickTapStartTime = Date.now();
|
||||
this.clickTapStartCoords = PixelPainterControls.getTouchCenter(event);
|
||||
this.clickTapStartView = this.renderer.view;
|
||||
|
||||
if (event.touches.length > 1) {
|
||||
this.tapStartDist = PixelPainterControls.getMultiTouchDistance(event);
|
||||
this.isMultiTab = true;
|
||||
this.clearTabTimeout();
|
||||
this.isMultiTap = true;
|
||||
this.wasEverMultiTap = true;
|
||||
} else {
|
||||
this.isClicking = true;
|
||||
this.isMultiTab = false;
|
||||
this.tapTimeout = setTimeout(() => {
|
||||
// check for longer tap to select taped color
|
||||
PixelPainterControls.selectColor(
|
||||
this.store,
|
||||
this.viewport,
|
||||
this.renderer,
|
||||
this.clickTapStartCoords,
|
||||
);
|
||||
}, 600);
|
||||
this.isMultiTap = false;
|
||||
this.wasEverMultiTap = false;
|
||||
const state = this.store.getState();
|
||||
if (state.gui.holdPaint) {
|
||||
this.tapTimeout = setTimeout(() => {
|
||||
this.isTapPainting = true;
|
||||
PixelPainterControls.placePixel(
|
||||
this.store,
|
||||
this.renderer,
|
||||
this.screenToWorld(this.clickTapStartCoords),
|
||||
);
|
||||
}, 200);
|
||||
} else {
|
||||
this.tapTimeout = setTimeout(() => {
|
||||
// check for longer tap to select taped color
|
||||
this.selectColorFromScreen(this.clickTapStartCoords);
|
||||
}, 600);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onTouchEnd(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
if (event.touches.length) {
|
||||
// still other touches left
|
||||
return;
|
||||
}
|
||||
|
||||
const { store, renderer } = this;
|
||||
if (event.touches.length === 0 && this.isClicking) {
|
||||
if (!this.wasEverMultiTap) {
|
||||
const { pageX, pageY } = event.changedTouches[0];
|
||||
const { clickTapStartCoords, clickTapStartTime } = this;
|
||||
const coordsDiff = [
|
||||
|
@ -286,17 +276,12 @@ class PixelPainterControls {
|
|||
];
|
||||
// thresholds for single click / holding
|
||||
if (clickTapStartTime > Date.now() - 580
|
||||
&& coordsDiff[0] < 2 && coordsDiff[1] < 2) {
|
||||
const cell = screenToWorld(
|
||||
renderer.view,
|
||||
renderer.viewscale,
|
||||
this.viewport,
|
||||
[pageX, pageY],
|
||||
);
|
||||
&& coordsDiff[0] < 2 && coordsDiff[1] < 2
|
||||
) {
|
||||
PixelPainterControls.placePixel(
|
||||
store,
|
||||
this.renderer,
|
||||
cell,
|
||||
this.screenToWorld([pageX, pageY]),
|
||||
);
|
||||
setTimeout(() => {
|
||||
store.dispatch(unsetHover());
|
||||
|
@ -312,19 +297,34 @@ class PixelPainterControls {
|
|||
event.stopPropagation();
|
||||
|
||||
const multiTouch = (event.touches.length > 1);
|
||||
const state = this.store.getState();
|
||||
|
||||
const [clientX, clientY] = PixelPainterControls.getTouchCenter(event);
|
||||
if (this.isMultiTab !== multiTouch) {
|
||||
if (this.isMultiTap !== multiTouch) {
|
||||
this.wasEverMultiTap = true;
|
||||
// if one finger got lifted or added, reset clickTabStart
|
||||
this.isMultiTab = multiTouch;
|
||||
this.isMultiTap = multiTouch;
|
||||
this.clickTapStartCoords = [clientX, clientY];
|
||||
this.clickTapStartView = this.renderer.view;
|
||||
this.tapStartDist = PixelPainterControls.getMultiTouchDistance(event);
|
||||
} else {
|
||||
// pan
|
||||
const { clickTapStartView, clickTapStartCoords } = this;
|
||||
return;
|
||||
}
|
||||
const { clickTapStartView, clickTapStartCoords } = this;
|
||||
// pinch
|
||||
if (multiTouch) {
|
||||
this.clearTabTimeout();
|
||||
const a = event.touches[0];
|
||||
const b = event.touches[1];
|
||||
const dist = Math.sqrt(
|
||||
(b.pageX - a.pageX) ** 2 + (b.pageY - a.pageY) ** 2,
|
||||
);
|
||||
const pinchScale = dist / this.tapStartDist;
|
||||
const [x, y] = this.renderer.view;
|
||||
this.renderer.updateView([x, y, clickTapStartView[2] * pinchScale]);
|
||||
}
|
||||
// pan
|
||||
if (!state.gui.holdPaint || multiTouch) {
|
||||
const [lastPosX, lastPosY] = clickTapStartView;
|
||||
|
||||
const deltaX = clientX - clickTapStartCoords[0];
|
||||
const deltaY = clientY - clickTapStartCoords[1];
|
||||
if (deltaX > 2 || deltaY > 2) {
|
||||
|
@ -335,26 +335,24 @@ class PixelPainterControls {
|
|||
lastPosX - (deltaX / scale),
|
||||
lastPosY - (deltaY / scale),
|
||||
]);
|
||||
|
||||
// pinch
|
||||
if (multiTouch) {
|
||||
this.clearTabTimeout();
|
||||
|
||||
const a = event.touches[0];
|
||||
const b = event.touches[1];
|
||||
const { tapStartDist } = this;
|
||||
const dist = Math.sqrt(
|
||||
(b.pageX - a.pageX) ** 2 + (b.pageY - a.pageY) ** 2,
|
||||
} else if (!this.wasEverMultiTap && !this.coolDownDelta) {
|
||||
// hold paint
|
||||
if (this.isTapPainting) {
|
||||
PixelPainterControls.placePixel(
|
||||
this.store,
|
||||
this.renderer,
|
||||
this.screenToWorld([clientX, clientY]),
|
||||
);
|
||||
const pinchScale = dist / tapStartDist;
|
||||
const [x, y] = this.renderer.view;
|
||||
this.renderer.updateView([x, y, clickTapStartView[2] * pinchScale]);
|
||||
} else {
|
||||
// while we are waiting for isTapPainting to trigger track coordinates
|
||||
this.clickTapStartCoords = [clientX, clientY];
|
||||
this.clickTapStartView = this.renderer.view;
|
||||
this.tapStartDist = PixelPainterControls.getMultiTouchDistance(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clearTabTimeout() {
|
||||
this.isClicking = false;
|
||||
if (this.tapTimeout) {
|
||||
clearTimeout(this.tapTimeout);
|
||||
this.tapTimeout = null;
|
||||
|
@ -376,6 +374,22 @@ class PixelPainterControls {
|
|||
this.renderer.storeViewInState();
|
||||
}
|
||||
|
||||
holdPaintStarted(immediate) {
|
||||
// if hold painting is started by keyboard,
|
||||
// we immeidately have to place, and not just when mousemove starts
|
||||
if (!immediate) {
|
||||
return;
|
||||
}
|
||||
const { hover } = this.store.getState().canvas;
|
||||
if (hover) {
|
||||
PixelPainterControls.placePixel(
|
||||
this.store,
|
||||
this.renderer,
|
||||
hover,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
onWheel(event) {
|
||||
event.preventDefault();
|
||||
document.activeElement.blur();
|
||||
|
@ -392,6 +406,54 @@ class PixelPainterControls {
|
|||
}
|
||||
}
|
||||
|
||||
static getWantedColor(state, renderer, cell) {
|
||||
if (state.gui.holdPaint === HOLD_PAINT.HISTORY) {
|
||||
return renderer.getColorIndexOfPixel(...cell, true);
|
||||
}
|
||||
return state.canvas.selectedColor;
|
||||
}
|
||||
|
||||
screenToWorld(screenCoor) {
|
||||
return screenToWorld(
|
||||
this.renderer.view,
|
||||
this.renderer.viewscale,
|
||||
this.viewport,
|
||||
screenCoor,
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* set hover from screen coordinates
|
||||
* @param [x, y] screen coordinates
|
||||
* @return null if hover didn't changed,
|
||||
* hover if it changed
|
||||
*/
|
||||
setHoverFromScrrenCoor(screenCoor) {
|
||||
const { store } = this;
|
||||
const state = store.getState();
|
||||
const { hover: prevHover } = state.canvas;
|
||||
const hover = this.screenToWorld(screenCoor);
|
||||
const [x, y] = hover;
|
||||
|
||||
/* out of bounds check */
|
||||
const { canvasSize } = state.canvas;
|
||||
const maxCoords = canvasSize / 2;
|
||||
if (x < -maxCoords || x >= maxCoords
|
||||
|| y < -maxCoords || y >= maxCoords
|
||||
) {
|
||||
if (prevHover) {
|
||||
store.dispatch(unsetHover());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!prevHover || prevHover[0] !== x || prevHover[1] !== y) {
|
||||
store.dispatch(setHover(hover));
|
||||
return hover;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
onMouseMove(event) {
|
||||
event.preventDefault();
|
||||
|
||||
|
@ -413,61 +475,18 @@ class PixelPainterControls {
|
|||
lastPosY - (deltaY / viewscale),
|
||||
]);
|
||||
} else {
|
||||
const { store } = this;
|
||||
const state = store.getState();
|
||||
const { hover } = state.canvas;
|
||||
const { view } = renderer;
|
||||
const screenCoor = screenToWorld(
|
||||
view,
|
||||
viewscale,
|
||||
this.viewport,
|
||||
[clientX, clientY],
|
||||
);
|
||||
const [x, y] = screenCoor;
|
||||
|
||||
/* out of bounds check */
|
||||
const { canvasSize } = state.canvas;
|
||||
const maxCoords = canvasSize / 2;
|
||||
if (x < -maxCoords || x >= maxCoords
|
||||
|| y < -maxCoords || y >= maxCoords
|
||||
) {
|
||||
if (hover) {
|
||||
store.dispatch(unsetHover());
|
||||
}
|
||||
const hover = this.setHoverFromScrrenCoor([clientX, clientY]);
|
||||
if (!hover) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hover || hover[0] !== x || hover[1] !== y) {
|
||||
store.dispatch(setHover(screenCoor));
|
||||
/* shift placing */
|
||||
if (!this.coolDownDelta) {
|
||||
switch (this.holdPainting) {
|
||||
case 1: {
|
||||
/* left shift: from selected color */
|
||||
PixelPainterControls.placePixel(
|
||||
store,
|
||||
this.renderer,
|
||||
screenCoor,
|
||||
);
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
/* right shift: from historical view */
|
||||
const colorIndex = this.renderer
|
||||
.getColorIndexOfPixel(x, y, true);
|
||||
if (colorIndex !== null) {
|
||||
PixelPainterControls.placePixel(
|
||||
store,
|
||||
this.renderer,
|
||||
screenCoor,
|
||||
colorIndex,
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
const state = this.store.getState();
|
||||
if (!this.coolDownDelta && state.gui.holdPaint) {
|
||||
/* hold paint */
|
||||
PixelPainterControls.placePixel(
|
||||
this.store,
|
||||
this.renderer,
|
||||
hover,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -476,20 +495,15 @@ class PixelPainterControls {
|
|||
const { store, viewport } = this;
|
||||
viewport.style.cursor = 'auto';
|
||||
store.dispatch(unsetHover());
|
||||
this.holdPainting = 0;
|
||||
this.clearTabTimeout();
|
||||
}
|
||||
|
||||
static selectColor(store, viewport, renderer, center) {
|
||||
if (renderer.viewscale < 3) {
|
||||
selectColorFromScreen(center) {
|
||||
const { renderer, store } = this;
|
||||
if (this.renderer.viewscale < 3) {
|
||||
return;
|
||||
}
|
||||
const coords = screenToWorld(
|
||||
renderer.view,
|
||||
renderer.viewscale,
|
||||
viewport,
|
||||
center,
|
||||
);
|
||||
const coords = this.screenToWorld(center);
|
||||
const clrIndex = renderer.getColorIndexOfPixel(...coords);
|
||||
if (clrIndex !== null) {
|
||||
store.dispatch(selectColor(clrIndex));
|
||||
|
@ -503,146 +517,12 @@ class PixelPainterControls {
|
|||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
|
||||
PixelPainterControls.selectColor(
|
||||
this.store,
|
||||
this.viewport,
|
||||
this.renderer,
|
||||
[clientX, clientY],
|
||||
);
|
||||
}
|
||||
|
||||
onKeyUp(event) {
|
||||
/*
|
||||
* key locations
|
||||
*/
|
||||
switch (event.code) {
|
||||
case 'ArrowUp':
|
||||
case 'KeyW':
|
||||
this.moveV = 0;
|
||||
return;
|
||||
case 'ArrowLeft':
|
||||
case 'KeyA':
|
||||
this.moveU = 0;
|
||||
return;
|
||||
case 'ArrowDown':
|
||||
case 'KeyS':
|
||||
this.moveV = 0;
|
||||
return;
|
||||
case 'ArrowRight':
|
||||
case 'KeyD':
|
||||
this.moveU = 0;
|
||||
return;
|
||||
case 'KeyE':
|
||||
this.moveW = 0;
|
||||
return;
|
||||
case 'KeyQ':
|
||||
this.moveW = 0;
|
||||
return;
|
||||
default:
|
||||
}
|
||||
|
||||
/*
|
||||
* key char
|
||||
*/
|
||||
switch (event.key) {
|
||||
case 'Shift':
|
||||
case 'CapsLock':
|
||||
this.holdPainting = 0;
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
onKeyDown(event) {
|
||||
// ignore key presses if modal is open or chat is used
|
||||
if (event.target.nodeName === 'INPUT'
|
||||
|| event.target.nodeName === 'TEXTAREA'
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* key location
|
||||
*/
|
||||
switch (event.code) {
|
||||
case 'ArrowUp':
|
||||
case 'KeyW':
|
||||
this.moveV = -1;
|
||||
return;
|
||||
case 'ArrowLeft':
|
||||
case 'KeyA':
|
||||
this.moveU = -1;
|
||||
return;
|
||||
case 'ArrowDown':
|
||||
case 'KeyS':
|
||||
this.moveV = 1;
|
||||
return;
|
||||
case 'ArrowRight':
|
||||
case 'KeyD':
|
||||
this.moveU = 1;
|
||||
return;
|
||||
case 'KeyE':
|
||||
this.moveW = 1;
|
||||
return;
|
||||
case 'KeyQ':
|
||||
this.moveW = -1;
|
||||
return;
|
||||
default:
|
||||
}
|
||||
|
||||
/*
|
||||
* key char
|
||||
*/
|
||||
switch (event.key) {
|
||||
case '+':
|
||||
this.zoom(1);
|
||||
return;
|
||||
case '-':
|
||||
this.zoom(-1);
|
||||
return;
|
||||
case 'Control':
|
||||
case 'Shift': {
|
||||
const { store } = this;
|
||||
const state = store.getState();
|
||||
const { hover } = state.canvas;
|
||||
if (hover) {
|
||||
if (event.key === 'Control') {
|
||||
// ctrl
|
||||
const clrIndex = this.renderer.getColorIndexOfPixel(...hover);
|
||||
store.dispatch(selectColor(clrIndex));
|
||||
return;
|
||||
}
|
||||
if (event.location === KeyboardEvent.DOM_KEY_LOCATION_LEFT) {
|
||||
// left shift
|
||||
this.holdPainting = 1;
|
||||
PixelPainterControls.placePixel(store, this.renderer, hover);
|
||||
return;
|
||||
}
|
||||
if (event.location === KeyboardEvent.DOM_KEY_LOCATION_RIGHT) {
|
||||
// right shift
|
||||
this.holdPainting = 2;
|
||||
const colorIndex = this.renderer
|
||||
.getColorIndexOfPixel(...hover, true);
|
||||
if (colorIndex !== null) {
|
||||
PixelPainterControls.placePixel(
|
||||
store,
|
||||
this.renderer,
|
||||
hover,
|
||||
colorIndex,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
}
|
||||
this.selectColorFromScreen([clientX, clientY]);
|
||||
}
|
||||
|
||||
update() {
|
||||
let time = Date.now();
|
||||
const { moveU, moveV, moveW } = this;
|
||||
const { moveU, moveV, moveW } = this.store.getState().gui;
|
||||
|
||||
if (!(moveU || moveV || moveW)) {
|
||||
this.prevTime = time;
|
||||
|
|
|
@ -440,7 +440,7 @@ class VoxelPainterControls {
|
|||
return;
|
||||
case 'KeyQ':
|
||||
this.moveW = -1;
|
||||
return;
|
||||
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
@ -468,7 +468,7 @@ class VoxelPainterControls {
|
|||
return;
|
||||
case 'KeyQ':
|
||||
this.moveW = 0;
|
||||
return;
|
||||
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,14 +10,72 @@ import {
|
|||
togglePixelNotify,
|
||||
toggleMute,
|
||||
selectCanvas,
|
||||
selectHoverColor,
|
||||
selectHoldPaint,
|
||||
setMoveU,
|
||||
setMoveV,
|
||||
setMoveW,
|
||||
} from '../store/actions';
|
||||
import {
|
||||
HOLD_PAINT,
|
||||
} from '../core/constants';
|
||||
import {
|
||||
notify,
|
||||
} from '../store/actions/thunks';
|
||||
|
||||
const usedKeys = ['g', 'h', 'x', 'm', 'r', 'p'];
|
||||
const charKeys = ['g', 'h', 'x', 'm', 'r', 'p', '+', '-'];
|
||||
|
||||
function createKeyPressHandler(store) {
|
||||
export function createKeyUpHandler(store) {
|
||||
return (event) => {
|
||||
/*
|
||||
* key locations
|
||||
*/
|
||||
switch (event.code) {
|
||||
case 'ArrowUp':
|
||||
case 'KeyW':
|
||||
store.dispatch(setMoveV(0));
|
||||
return;
|
||||
case 'ArrowLeft':
|
||||
case 'KeyA':
|
||||
store.dispatch(setMoveU(0));
|
||||
return;
|
||||
case 'ArrowDown':
|
||||
case 'KeyS':
|
||||
store.dispatch(setMoveV(0));
|
||||
return;
|
||||
case 'ArrowRight':
|
||||
case 'KeyD':
|
||||
store.dispatch(setMoveU(0));
|
||||
return;
|
||||
case 'KeyE':
|
||||
store.dispatch(setMoveW(0));
|
||||
return;
|
||||
case 'KeyQ':
|
||||
store.dispatch(setMoveW(0));
|
||||
return;
|
||||
default:
|
||||
}
|
||||
|
||||
/*
|
||||
* key char
|
||||
*/
|
||||
switch (event.key) {
|
||||
case '+':
|
||||
store.dispatch(setMoveW(0));
|
||||
return;
|
||||
case '-':
|
||||
store.dispatch(setMoveW(0));
|
||||
return;
|
||||
case 'Shift':
|
||||
case 'CapsLock':
|
||||
store.dispatch(selectHoldPaint(HOLD_PAINT.OFF));
|
||||
break;
|
||||
default:
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function createKeyDownHandler(store) {
|
||||
return (event) => {
|
||||
// ignore key presses if modal is open or chat is used
|
||||
if (event.target.nodeName === 'INPUT'
|
||||
|
@ -44,12 +102,70 @@ function createKeyPressHandler(store) {
|
|||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* key locations
|
||||
*/
|
||||
switch (event.code) {
|
||||
case 'ArrowUp':
|
||||
case 'KeyW':
|
||||
store.dispatch(setMoveV(-1));
|
||||
return;
|
||||
case 'ArrowLeft':
|
||||
case 'KeyA':
|
||||
store.dispatch(setMoveU(-1));
|
||||
return;
|
||||
case 'ArrowDown':
|
||||
case 'KeyS':
|
||||
store.dispatch(setMoveV(1));
|
||||
return;
|
||||
case 'ArrowRight':
|
||||
case 'KeyD':
|
||||
store.dispatch(setMoveU(1));
|
||||
return;
|
||||
case 'KeyE':
|
||||
store.dispatch(setMoveW(1));
|
||||
return;
|
||||
case 'KeyQ':
|
||||
store.dispatch(setMoveW(-1));
|
||||
return;
|
||||
default:
|
||||
}
|
||||
|
||||
/*
|
||||
* key char
|
||||
*/
|
||||
switch (event.key) {
|
||||
case '+':
|
||||
store.dispatch(setMoveW(1));
|
||||
return;
|
||||
case '-':
|
||||
store.dispatch(setMoveW(-1));
|
||||
return;
|
||||
case 'Control':
|
||||
store.dispatch(selectHoverColor(-1));
|
||||
return;
|
||||
case 'Shift': {
|
||||
if (event.location === KeyboardEvent.DOM_KEY_LOCATION_LEFT) {
|
||||
// left shift
|
||||
store.dispatch(selectHoldPaint(HOLD_PAINT.PENCIL), true);
|
||||
return;
|
||||
}
|
||||
if (event.location === KeyboardEvent.DOM_KEY_LOCATION_RIGHT) {
|
||||
// right shift
|
||||
store.dispatch(selectHoldPaint(HOLD_PAINT.HISTORY), true);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
default:
|
||||
}
|
||||
|
||||
/*
|
||||
* if char of key isn't used by a keybind,
|
||||
* we check if the key location is where a
|
||||
* key that is used would be on QWERTY
|
||||
*/
|
||||
if (!usedKeys.includes(key)) {
|
||||
if (!charKeys.includes(key)) {
|
||||
key = event.code;
|
||||
if (!key.startsWith('Key')) {
|
||||
return;
|
||||
|
@ -98,5 +214,3 @@ function createKeyPressHandler(store) {
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default createKeyPressHandler;
|
||||
|
|
|
@ -54,3 +54,10 @@ export const VIEW_UPDATE_DELAY = 1000;
|
|||
export const MAX_LOADED_CHUNKS = 2000;
|
||||
export const MAX_CHUNK_AGE = 300000;
|
||||
export const GC_INTERVAL = 300000;
|
||||
|
||||
export const HOLD_PAINT = {
|
||||
OFF: 0,
|
||||
PENCIL: 1,
|
||||
HISTORY: 2,
|
||||
OVERLAY: 3,
|
||||
};
|
||||
|
|
|
@ -93,9 +93,17 @@ export function toggleOpenPalette() {
|
|||
};
|
||||
}
|
||||
|
||||
export function togglePencil() {
|
||||
export function selectHoldPaint(value, immediate) {
|
||||
return {
|
||||
type: 's/TGL_PENCIL',
|
||||
type: 's/SELECT_HOLD_PAINT',
|
||||
value,
|
||||
immediate,
|
||||
};
|
||||
}
|
||||
|
||||
export function selectHoverColor() {
|
||||
return {
|
||||
type: 'SELECT_HOVER_COLOR',
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -174,6 +182,34 @@ export function setScale(scale, zoompoint) {
|
|||
};
|
||||
}
|
||||
|
||||
export function setMoveU(value) {
|
||||
return {
|
||||
type: 's/SET_MOVE_U',
|
||||
value,
|
||||
};
|
||||
}
|
||||
|
||||
export function setMoveV(value) {
|
||||
return {
|
||||
type: 's/SET_MOVE_V',
|
||||
value,
|
||||
};
|
||||
}
|
||||
|
||||
export function setMoveW(value) {
|
||||
return {
|
||||
type: 's/SET_MOVE_W',
|
||||
value,
|
||||
};
|
||||
}
|
||||
|
||||
export function cancelMove(value) {
|
||||
return {
|
||||
type: 's/CANCEL_MOVE',
|
||||
value,
|
||||
};
|
||||
}
|
||||
|
||||
export function requestBigChunk(center) {
|
||||
return {
|
||||
type: 'REQ_BIG_CHUNK',
|
||||
|
|
|
@ -10,6 +10,9 @@ import {
|
|||
getRenderer,
|
||||
initRenderer,
|
||||
} from '../../ui/rendererFactory';
|
||||
import {
|
||||
selectColor,
|
||||
} from '../actions';
|
||||
|
||||
export default (store) => (next) => (action) => {
|
||||
const { type } = action;
|
||||
|
@ -28,7 +31,14 @@ export default (store) => (next) => (action) => {
|
|||
);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'SELECT_HOVER_COLOR': {
|
||||
const renderer = getRenderer();
|
||||
const clr = renderer.getPointedColor();
|
||||
if (clr !== null) {
|
||||
store.dispatch(selectColor(clr));
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// nothing
|
||||
}
|
||||
|
@ -112,7 +122,26 @@ export default (store) => (next) => (action) => {
|
|||
|
||||
case 's/TGL_HISTORICAL_VIEW': {
|
||||
const renderer = getRenderer();
|
||||
renderer.updateView(state.view);
|
||||
renderer.updateView(state.canvas.view);
|
||||
break;
|
||||
}
|
||||
|
||||
case 's/SELECT_HOLD_PAINT': {
|
||||
if (action.value) {
|
||||
const renderer = getRenderer();
|
||||
renderer.controls?.holdPaintStarted?.(action.immediate);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 's/SET_MOVE_U':
|
||||
case 's/SET_MOVE_V':
|
||||
case 's/SET_MOVE_W':
|
||||
case 's/CANCEL_MOVE': {
|
||||
if (!action.value) {
|
||||
const renderer = getRenderer();
|
||||
renderer.storeViewInState();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { HOLD_PAINT } from '../../core/constants';
|
||||
|
||||
const initialState = {
|
||||
showGrid: false,
|
||||
showPixelNotify: false,
|
||||
|
@ -6,7 +8,6 @@ const initialState = {
|
|||
isLightGrid: false,
|
||||
compactPalette: false,
|
||||
paletteOpen: true,
|
||||
pencilEnabled: false,
|
||||
mute: false,
|
||||
chatNotify: true,
|
||||
// top-left button menu
|
||||
|
@ -15,6 +16,11 @@ const initialState = {
|
|||
onlineCanvas: false,
|
||||
// selected theme
|
||||
style: 'default',
|
||||
// properties that aren't saved
|
||||
holdPaint: HOLD_PAINT.OFF,
|
||||
moveU: 0,
|
||||
moveV: 0,
|
||||
moveW: 0,
|
||||
};
|
||||
|
||||
|
||||
|
@ -79,13 +85,6 @@ export default function gui(
|
|||
};
|
||||
}
|
||||
|
||||
case 's/TGL_PENCIL': {
|
||||
return {
|
||||
...state,
|
||||
pencilEnabled: !state.pencilEnabled,
|
||||
};
|
||||
}
|
||||
|
||||
case 's/TGL_OPEN_MENU': {
|
||||
return {
|
||||
...state,
|
||||
|
@ -93,6 +92,25 @@ export default function gui(
|
|||
};
|
||||
}
|
||||
|
||||
case 's/TGL_MUTE':
|
||||
return {
|
||||
...state,
|
||||
mute: !state.mute,
|
||||
};
|
||||
|
||||
case 's/TGL_CHAT_NOTIFY':
|
||||
return {
|
||||
...state,
|
||||
chatNotify: !state.chatNotify,
|
||||
};
|
||||
|
||||
case 's/SELECT_HOLD_PAINT': {
|
||||
return {
|
||||
...state,
|
||||
holdPaint: action.value,
|
||||
};
|
||||
}
|
||||
|
||||
case 's/SELECT_STYLE': {
|
||||
const { style } = action;
|
||||
return {
|
||||
|
@ -117,22 +135,43 @@ export default function gui(
|
|||
};
|
||||
}
|
||||
|
||||
case 's/TGL_MUTE':
|
||||
case 's/SET_MOVE_U': {
|
||||
return {
|
||||
...state,
|
||||
mute: !state.mute,
|
||||
moveU: action.value,
|
||||
};
|
||||
}
|
||||
|
||||
case 's/TGL_CHAT_NOTIFY':
|
||||
case 's/SET_MOVE_V': {
|
||||
return {
|
||||
...state,
|
||||
chatNotify: !state.chatNotify,
|
||||
moveV: action.value,
|
||||
};
|
||||
}
|
||||
|
||||
case 's/SET_MOVE_W': {
|
||||
return {
|
||||
...state,
|
||||
moveW: action.value,
|
||||
};
|
||||
}
|
||||
|
||||
case 's/CANCEL_MOVE': {
|
||||
return {
|
||||
...state,
|
||||
moveU: 0,
|
||||
moveV: 0,
|
||||
moveW: 0,
|
||||
};
|
||||
}
|
||||
|
||||
case 'persist/REHYDRATE':
|
||||
return {
|
||||
...state,
|
||||
pencilEnabled: false,
|
||||
holdPaint: HOLD_PAINT.OFF,
|
||||
moveU: 0,
|
||||
moveV: 0,
|
||||
moveW: 0,
|
||||
};
|
||||
|
||||
default:
|
||||
|
|
|
@ -62,7 +62,7 @@ class ChunkLoader2D extends ChunkLoader {
|
|||
const key = `${this.canvasMaxTiledZoom}:${cx}:${cy}`;
|
||||
const chunk = this.cget(key);
|
||||
if (!chunk) {
|
||||
return 0;
|
||||
return null;
|
||||
}
|
||||
return chunk.getColorIndex(
|
||||
getCellInsideChunk(canvasSize, [x, y]),
|
||||
|
|
|
@ -95,6 +95,10 @@ class Renderer {
|
|||
|
||||
updateCanvasData() {}
|
||||
|
||||
getPointedColor() {
|
||||
return null;
|
||||
}
|
||||
|
||||
isChunkInView() {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -166,6 +166,12 @@ class Renderer2D extends Renderer {
|
|||
return this.chunkLoader.getColorIndexOfPixel(cx, cy);
|
||||
}
|
||||
|
||||
getPointedColor() {
|
||||
return this.getColorIndexOfPixel(
|
||||
...this.store.getState().canvas.hover,
|
||||
);
|
||||
}
|
||||
|
||||
updateView(view, origin) {
|
||||
let [x, y, scale] = view;
|
||||
const state = this.store.getState();
|
||||
|
|
|
@ -35,7 +35,7 @@ import {
|
|||
import {
|
||||
setHover,
|
||||
unsetHover,
|
||||
selectColor,
|
||||
selectHoverColor,
|
||||
} from '../store/actions';
|
||||
import pixelTransferController from './PixelTransferController';
|
||||
|
||||
|
@ -526,6 +526,40 @@ class Renderer3D extends Renderer {
|
|||
this.updateRollOverMesh(0, 0);
|
||||
}
|
||||
|
||||
getPointedColor() {
|
||||
const {
|
||||
objects,
|
||||
raycaster,
|
||||
mouse,
|
||||
camera,
|
||||
} = this;
|
||||
raycaster.setFromCamera(mouse, camera);
|
||||
|
||||
const intersects = raycaster.intersectObjects(objects);
|
||||
if (intersects.length <= 0) {
|
||||
return null;
|
||||
}
|
||||
const intersect = intersects[0];
|
||||
const target = intersect.point.clone()
|
||||
.add(intersect.face.normal.multiplyScalar(-0.5))
|
||||
.floor()
|
||||
.addScalar(0.5)
|
||||
.floor();
|
||||
if (target.y < 0) {
|
||||
return null;
|
||||
}
|
||||
if (target.clone().sub(camera.position).length() < 120) {
|
||||
const cell = target.toArray();
|
||||
if (this.chunkLoader) {
|
||||
const clr = this.chunkLoader.getVoxel(...cell);
|
||||
if (clr) {
|
||||
return clr;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
placeVoxel(x, y, z, color = null) {
|
||||
const {
|
||||
store,
|
||||
|
@ -666,11 +700,8 @@ class Renderer3D extends Renderer {
|
|||
innerHeight,
|
||||
} = window;
|
||||
const {
|
||||
camera,
|
||||
objects,
|
||||
raycaster,
|
||||
mouse,
|
||||
store,
|
||||
mouse,
|
||||
} = this;
|
||||
|
||||
mouse.set(
|
||||
|
@ -678,6 +709,18 @@ class Renderer3D extends Renderer {
|
|||
-(clientY / innerHeight) * 2 + 1,
|
||||
);
|
||||
|
||||
if (button === 1) {
|
||||
// middle mouse button
|
||||
store.dispatch(selectHoverColor());
|
||||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
camera,
|
||||
objects,
|
||||
raycaster,
|
||||
} = this;
|
||||
|
||||
raycaster.setFromCamera(mouse, camera);
|
||||
|
||||
const intersects = raycaster.intersectObjects(objects);
|
||||
|
@ -695,25 +738,6 @@ class Renderer3D extends Renderer {
|
|||
const [x, y, z] = target.toArray();
|
||||
this.placeVoxel(x, y, z);
|
||||
}
|
||||
} else if (button === 1) {
|
||||
// middle mouse button
|
||||
const target = intersect.point.clone()
|
||||
.add(intersect.face.normal.multiplyScalar(-0.5))
|
||||
.floor()
|
||||
.addScalar(0.5)
|
||||
.floor();
|
||||
if (target.y < 0) {
|
||||
return;
|
||||
}
|
||||
if (target.clone().sub(camera.position).length() < 120) {
|
||||
const cell = target.toArray();
|
||||
if (this.chunkLoader) {
|
||||
const clr = this.chunkLoader.getVoxel(...cell);
|
||||
if (clr) {
|
||||
store.dispatch(selectColor(clr));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (button === 2) {
|
||||
// right mouse button
|
||||
const target = intersect.point.clone()
|
||||
|
|
Loading…
Reference in New Issue
Block a user