add buttons for pencil mode

This commit is contained in:
HF 2024-01-22 06:20:10 +01:00
parent eb6c1772db
commit 236e83694b
10 changed files with 378 additions and 307 deletions

View File

@ -9,6 +9,7 @@ import CoolDownBox from './CoolDownBox';
import NotifyBox from './NotifyBox'; import NotifyBox from './NotifyBox';
import GlobeButton from './buttons/GlobeButton'; import GlobeButton from './buttons/GlobeButton';
import PalselButton from './buttons/PalselButton'; import PalselButton from './buttons/PalselButton';
import PencilButton from './buttons/PencilButton';
import MovementControls from './buttons/MovementControls'; import MovementControls from './buttons/MovementControls';
import Palette from './Palette'; import Palette from './Palette';
import Alert from './Alert'; import Alert from './Alert';
@ -35,7 +36,10 @@ const UI = () => {
<PalselButton /> <PalselButton />
<Palette /> <Palette />
{(!is3D) && <GlobeButton />} {(!is3D) && <GlobeButton />}
{(isOnMobile) && <MovementControls />} {// (isOnMobile) && <MovementControls />
}
<MovementControls />
{(!is3D) && <PencilButton />}
<CoolDownBox /> <CoolDownBox />
</> </>
)} )}

View File

@ -17,7 +17,7 @@ const ExpandMenuButton = () => {
return ( return (
<div <div
id="menubutton" id="menubutton"
className="actionbuttons" className={`actionbuttons${menuOpen ? ' pressed' : ''}`}
role="button" role="button"
title={(menuOpen) ? t`Close Menu` : t`Open Menu`} title={(menuOpen) ? t`Close Menu` : t`Open Menu`}
tabIndex={-1} tabIndex={-1}

View File

@ -4,6 +4,7 @@
*/ */
import React from 'react'; import React from 'react';
import { useSelector, shallowEqual } from 'react-redux';
import { getRenderer } from '../../ui/rendererFactory'; import { getRenderer } from '../../ui/rendererFactory';
@ -11,213 +12,189 @@ const btnStyle = {
fontSize: 34, fontSize: 34,
}; };
function move(action, bool) {
const renderer = getRenderer();
switch (action) {
case 'FORWARD': {
renderer.controls.moveForward = bool;
break;
}
case 'BACKWARD': {
renderer.controls.moveBackward = bool;
break;
}
case 'LEFT': {
renderer.controls.moveLeft = bool;
break;
}
case 'RIGHT': {
renderer.controls.moveRight = bool;
break;
}
case 'UP': {
renderer.controls.moveUp = bool;
break;
}
case 'DOWN': {
renderer.controls.moveDown = bool;
break;
}
default:
break;
}
}
function cancelMovement() { function cancelMovement() {
const renderer = getRenderer(); const renderer = getRenderer();
renderer.controls.moveForward = false; renderer.controls.moveU = 0;
renderer.controls.moveBackward = false; renderer.controls.moveV = 0;
renderer.controls.moveLeft = false; renderer.controls.moveW = 0;
renderer.controls.moveRight = false;
renderer.controls.moveUp = false;
renderer.controls.moveDown = false;
} }
const MovementControls = () => ( const MovementControls = () => {
<div> const [pencilEnabled, is3D] = useSelector((state) => [
<div state.gui.pencilEnabled,
className="actionbuttons" state.canvas.is3D,
role="button" ], shallowEqual);
tabIndex={0}
style={{ if (!pencilEnabled && !is3D) {
...btnStyle, return null;
// left: 46, }
left: 57,
// bottom: 128, return (
bottom: 139, <>
}} <div
onMouseDown={() => { className="actionbuttons"
move('FORWARD', true); role="button"
}} tabIndex={0}
onMouseUp={() => { style={{
move('FORWARD', false); ...btnStyle,
}} // left: 46,
onTouchStart={() => { left: 57,
move('FORWARD', true); // bottom: 128,
}} bottom: 139,
onTouchEnd={() => { }}
move('FORWARD', false); onMouseDown={() => {
}} getRenderer().controls.moveV = -1;
onTouchCancel={cancelMovement} }}
onMouseLeave={cancelMovement} onMouseUp={() => {
> getRenderer().controls.moveV = 0;
}}
</div> onTouchStart={() => {
<div getRenderer().controls.moveV = -1;
className="actionbuttons" }}
role="button" onTouchEnd={() => {
tabIndex={0} getRenderer().controls.moveV = 0;
style={{ }}
...btnStyle, onTouchCancel={cancelMovement}
// left: 46, onMouseLeave={cancelMovement}
left: 57, >
bottom: 98,
}} </div>
onMouseDown={() => { <div
move('BACKWARD', true); className="actionbuttons"
}} role="button"
onMouseUp={() => { tabIndex={0}
move('BACKWARD', false); style={{
}} ...btnStyle,
onTouchStart={() => { // left: 46,
move('BACKWARD', true); left: 57,
}} bottom: 98,
onTouchEnd={() => { }}
move('BACKWARD', false); onMouseDown={() => {
}} getRenderer().controls.moveV = 1;
onTouchCancel={cancelMovement} }}
onMouseLeave={cancelMovement} onMouseUp={() => {
> getRenderer().controls.moveV = 0;
}}
</div> onTouchStart={() => {
<div getRenderer().controls.moveV = 1;
className="actionbuttons" }}
role="button" onTouchEnd={() => {
tabIndex={0} getRenderer().controls.moveV = 0;
style={{ }}
...btnStyle, onTouchCancel={cancelMovement}
left: 16, onMouseLeave={cancelMovement}
bottom: 98, >
}}
onMouseDown={() => { </div>
move('LEFT', true); <div
}} className="actionbuttons"
onMouseUp={() => { role="button"
move('LEFT', false); tabIndex={0}
}} style={{
onTouchStart={() => { ...btnStyle,
move('LEFT', true); left: 16,
}} bottom: 98,
onTouchEnd={() => { }}
move('LEFT', false); onMouseDown={() => {
}} getRenderer().controls.moveU = -1;
onTouchCancel={cancelMovement} }}
onMouseLeave={cancelMovement} onMouseUp={() => {
> getRenderer().controls.moveU = 0;
}}
</div> onTouchStart={() => {
<div getRenderer().controls.moveU = -1;
className="actionbuttons" }}
role="button" onTouchEnd={() => {
tabIndex={0} getRenderer().controls.moveU = 0;
style={{ }}
...btnStyle, onTouchCancel={cancelMovement}
// left: 76, onMouseLeave={cancelMovement}
left: 98, >
bottom: 98,
}} </div>
onMouseDown={() => { <div
move('RIGHT', true); className="actionbuttons"
}} role="button"
onMouseUp={() => { tabIndex={0}
move('RIGHT', false); style={{
}} ...btnStyle,
onTouchStart={() => { // left: 76,
move('RIGHT', true); left: 98,
}} bottom: 98,
onTouchEnd={() => { }}
move('RIGHT', false); onMouseDown={() => {
}} getRenderer().controls.moveU = 1;
onTouchCancel={cancelMovement} }}
onMouseLeave={cancelMovement} onMouseUp={() => {
> getRenderer().controls.moveU = 0;
}}
</div> onTouchStart={() => {
<div getRenderer().controls.moveU = 1;
className="actionbuttons" }}
role="button" onTouchEnd={() => {
tabIndex={0} getRenderer().controls.moveU = 0;
style={{ }}
...btnStyle, onTouchCancel={cancelMovement}
// left: 76, onMouseLeave={cancelMovement}
left: 16, >
bottom: 139,
}} </div>
onMouseDown={() => { <div
move('UP', true); className="actionbuttons"
}} role="button"
onMouseUp={() => { tabIndex={0}
move('UP', false); style={{
}} ...btnStyle,
onTouchStart={() => { // left: 76,
move('UP', true); left: 16,
}} bottom: 139,
onTouchEnd={() => { }}
move('UP', false); onMouseDown={() => {
}} getRenderer().controls.moveW = -1;
onTouchCancel={cancelMovement} }}
onMouseLeave={cancelMovement} onMouseUp={() => {
> getRenderer().controls.moveW = 0;
}}
</div> onTouchStart={() => {
<div getRenderer().controls.moveW = -1;
className="actionbuttons" }}
role="button" onTouchEnd={() => {
tabIndex={0} getRenderer().controls.moveW = 0;
style={{ }}
...btnStyle, onTouchCancel={cancelMovement}
// left: 76, onMouseLeave={cancelMovement}
left: 98, >
bottom: 139,
}} </div>
onMouseDown={() => { <div
move('DOWN', true); className="actionbuttons"
}} role="button"
onMouseUp={() => { tabIndex={0}
move('DOWN', false); style={{
}} ...btnStyle,
onTouchStart={() => { // left: 76,
move('DOWN', true); left: 98,
}} bottom: 139,
onTouchEnd={() => { }}
move('DOWN', false); onMouseDown={() => {
}} getRenderer().controls.moveW = 1;
onTouchCancel={cancelMovement} }}
onMouseLeave={cancelMovement} onMouseUp={() => {
> getRenderer().controls.moveW = 0;
}}
</div> onTouchStart={() => {
</div> getRenderer().controls.moveW = 1;
); }}
onTouchEnd={() => {
getRenderer().controls.moveW = 0;
}}
onTouchCancel={cancelMovement}
onMouseLeave={cancelMovement}
>
</div>
</>
);
};
export default MovementControls; export default MovementControls;

View File

@ -21,7 +21,7 @@ const PalselButton = () => {
return ( return (
<div <div
id="palselbutton" id="palselbutton"
className={`actionbuttons ${(paletteOpen) ? '' : 'pressed'}`} className={`actionbuttons${(paletteOpen) ? ' pressed' : ''}`}
style={{ style={{
color: palette.isDark(selectedColor) ? 'white' : 'black', color: palette.isDark(selectedColor) ? 'white' : 'black',
backgroundColor: palette.colors[selectedColor], backgroundColor: palette.colors[selectedColor],

View File

@ -0,0 +1,30 @@
/**
*
*/
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { TbPencil, TbPencilMinus } from 'react-icons/tb';
import { t } from 'ttag';
import { togglePencil } from '../../store/actions';
const PencilButton = () => {
const pencilEnabled = useSelector((state) => state.gui.pencilEnabled);
const dispatch = useDispatch();
return (
<div
id="pencilbutton"
className={`actionbuttons${pencilEnabled ? ' pressed' : ''}`}
role="button"
title={(pencilEnabled) ? t`Disable Pencil` : t`Enable Pencil`}
tabIndex={-1}
onClick={() => dispatch(togglePencil())}
>
{pencilEnabled ? <TbPencilMinus /> : <TbPencil />}
</div>
);
};
export default React.memo(PencilButton);

View File

@ -43,6 +43,11 @@ class PixelPainterControls {
* 2: right shift * 2: right shift
*/ */
holdPainting = 0; holdPainting = 0;
// if we are moving
moveU = 0;
moveV = 0;
moveW = 0;
prevTime = Date.now();
// if we are waiting before placing pixel via holdPainting again // if we are waiting before placing pixel via holdPainting again
coolDownDelta = false; coolDownDelta = false;
@ -82,9 +87,6 @@ class PixelPainterControls {
document.removeEventListener('keyup', this.onKeyUp, false); document.removeEventListener('keyup', this.onKeyUp, false);
} }
// eslint-disable-next-line class-methods-use-this
update() {}
gotCoolDownDelta(delta) { gotCoolDownDelta(delta) {
this.coolDownDelta = true; this.coolDownDelta = true;
setTimeout(() => { setTimeout(() => {
@ -367,7 +369,7 @@ class PixelPainterControls {
this.renderer.storeViewInState(); this.renderer.storeViewInState();
} }
move(direction) { step(direction) {
const [x, y, scale] = this.renderer.view; const [x, y, scale] = this.renderer.view;
const [dx, dy] = direction.map((z) => z * 100.0 / scale); const [dx, dy] = direction.map((z) => z * 100.0 / scale);
this.renderer.updateView([x + dx, y + dy]); this.renderer.updateView([x + dx, y + dy]);
@ -511,6 +513,38 @@ class PixelPainterControls {
} }
onKeyUp(event) { 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) { switch (event.key) {
case 'Shift': case 'Shift':
case 'CapsLock': case 'CapsLock':
@ -527,7 +561,6 @@ class PixelPainterControls {
) { ) {
return; return;
} }
const { store } = this;
/* /*
* key location * key location
@ -535,25 +568,25 @@ class PixelPainterControls {
switch (event.code) { switch (event.code) {
case 'ArrowUp': case 'ArrowUp':
case 'KeyW': case 'KeyW':
this.move([0, -1]); this.moveV = -1;
return; return;
case 'ArrowLeft': case 'ArrowLeft':
case 'KeyA': case 'KeyA':
this.move([-1, 0]); this.moveU = -1;
return; return;
case 'ArrowDown': case 'ArrowDown':
case 'KeyS': case 'KeyS':
this.move([0, 1]); this.moveV = 1;
return; return;
case 'ArrowRight': case 'ArrowRight':
case 'KeyD': case 'KeyD':
this.move([1, 0]); this.moveU = 1;
return; return;
case 'KeyE': case 'KeyE':
this.zoom(1); this.moveW = 1;
return; return;
case 'KeyQ': case 'KeyQ':
this.zoom(-1); this.moveW = -1;
return; return;
default: default:
} }
@ -570,6 +603,7 @@ class PixelPainterControls {
return; return;
case 'Control': case 'Control':
case 'Shift': { case 'Shift': {
const { store } = this;
const state = store.getState(); const state = store.getState();
const { hover } = state.canvas; const { hover } = state.canvas;
if (hover) { if (hover) {
@ -605,6 +639,33 @@ class PixelPainterControls {
default: default:
} }
} }
update() {
let time = Date.now();
const { moveU, moveV, moveW } = this;
if (!(moveU || moveV || moveW)) {
this.prevTime = time;
return false;
}
// set to time since last tick
time -= this.prevTime;
this.prevTime += time;
const [x, y, scale] = this.renderer.view;
const directionalStep = time * 0.4 / scale;
let scaleFactor = scale >= 1.0 ? 1.0005 : 1.0003;
scaleFactor **= moveW;
this.renderer.updateView([
x + directionalStep * moveU,
y + directionalStep * moveV,
scale * scaleFactor ** time,
]);
return true;
}
} }
export default PixelPainterControls; export default PixelPainterControls;

View File

@ -103,12 +103,9 @@ class VoxelPainterControls {
spherical = new Spherical(); spherical = new Spherical();
sphericalDelta = new Spherical(); sphericalDelta = new Spherical();
// //
moveLeft = false; moveU = 0;
moveRight = false; moveV = 0;
moveForward = false; moveW = 0;
moveBackward = false;
moveUp = false;
moveDown = false;
// //
scale = 1; scale = 1;
panOffset = new Vector3(); panOffset = new Vector3();
@ -421,67 +418,58 @@ class VoxelPainterControls {
return; return;
} }
switch (event.keyCode) { switch (event.code) {
case 38: // up case 'ArrowUp':
case 87: // w case 'KeyW':
this.moveForward = true; this.moveV = -1;
break; return;
case 37: // left case 'ArrowLeft':
case 65: // a case 'KeyA':
this.moveLeft = true; this.moveU = -1;
break; return;
case 40: // down case 'ArrowDown':
case 83: // s case 'KeyS':
this.moveBackward = true; this.moveV = 1;
break; return;
case 39: // right case 'ArrowRight':
case 68: // d case 'KeyD':
this.moveRight = true; this.moveU = 1;
break; return;
case 69: // E case 'KeyE':
this.moveUp = true; this.moveW = 1;
break; return;
case 67: // C case 'KeyQ':
this.moveDown = true; this.moveW = -1;
break; return;
default: default:
break;
} }
} }
onDocumentKeyUp(event) { onDocumentKeyUp(event) {
// ignore key presses if modal is open or chat is used switch (event.code) {
if (event.target.nodeName === 'INPUT' case 'ArrowUp':
|| event.target.nodeName === 'TEXTAREA' case 'KeyW':
) { this.moveV = 0;
return; return;
} case 'ArrowLeft':
case 'KeyA':
switch (event.keyCode) { this.moveU = 0;
case 38: // up return;
case 87: // w case 'ArrowDown':
this.moveForward = false; case 'KeyS':
break; this.moveV = 0;
case 37: // left return;
case 65: // a case 'ArrowRight':
this.moveLeft = false; case 'KeyD':
break; this.moveU = 0;
case 40: // down return;
case 83: // s case 'KeyE':
this.moveBackward = false; this.moveW = 0;
break; return;
case 39: // right case 'KeyQ':
case 68: // d this.moveW = 0;
this.moveRight = false; return;
break;
case 69: // E
this.moveUp = false;
break;
case 67: // C
this.moveDown = false;
break;
default: default:
break;
} }
} }
@ -688,27 +676,22 @@ class VoxelPainterControls {
} }
update(force) { update(force) {
const { const time = Date.now();
moveRight, const { moveU, moveV, moveW } = this;
moveLeft,
moveUp,
moveDown,
moveForward,
moveBackward,
} = this;
if (!(force if (!(force
|| this.state !== STATE.NONE || this.state !== STATE.NONE
|| this.forceNextUpdate || this.forceNextUpdate
|| moveRight || moveLeft || moveU || moveV || moveW
|| moveUp || moveDown
|| moveForward || moveBackward
)) { )) {
this.prevTime = Date.now(); this.prevTime = time;
return false; return false;
} }
this.forceNextUpdate = false; this.forceNextUpdate = false;
const delta = (time - this.prevTime) / 1000.0;
this.prevTime = time;
const { const {
camera, camera,
target, target,
@ -720,9 +703,7 @@ class VoxelPainterControls {
panOffset, panOffset,
sphericalDelta, sphericalDelta,
} = this; } = this;
const time = Date.now();
const delta = (time - this.prevTime) / 1000.0;
velocity.x -= velocity.x * 40.0 * delta; velocity.x -= velocity.x * 40.0 * delta;
velocity.y -= velocity.y * 40.0 * delta; velocity.y -= velocity.y * 40.0 * delta;
velocity.z -= velocity.z * 40.0 * delta; velocity.z -= velocity.z * 40.0 * delta;
@ -731,20 +712,12 @@ class VoxelPainterControls {
velocity.set(0, 0, 0); velocity.set(0, 0, 0);
} }
direction.x = Number(moveRight) - Number(moveLeft); direction.set(moveU, -moveW, -moveV);
direction.y = Number(moveUp) - Number(moveDown);
direction.z = Number(moveForward) - Number(moveBackward);
direction.normalize(); direction.normalize();
if (moveLeft || moveRight) { if (moveU) velocity.x -= direction.x * 1000.0 * delta;
velocity.x -= direction.x * 1000.0 * delta; if (moveW) velocity.y -= direction.y * 1000.0 * delta;
} if (moveV) velocity.z -= direction.z * 1000.0 * delta;
if (moveUp || moveDown) {
velocity.y -= direction.y * 1000.0 * delta;
}
if (moveForward || moveBackward) {
velocity.z -= direction.z * 1000.0 * delta;
}
vec.setFromMatrixColumn(camera.matrix, 0); vec.setFromMatrixColumn(camera.matrix, 0);
vec.crossVectors(camera.up, vec); vec.crossVectors(camera.up, vec);
@ -755,8 +728,6 @@ class VoxelPainterControls {
vec.multiplyScalar(-velocity.x * delta); vec.multiplyScalar(-velocity.x * delta);
panOffset.add(vec); panOffset.add(vec);
this.prevTime = time;
offset.copy(camera.position).sub(target); offset.copy(camera.position).sub(target);
// rotate offset to "y-axis-is-up" space // rotate offset to "y-axis-is-up" space

View File

@ -93,6 +93,12 @@ export function toggleOpenPalette() {
}; };
} }
export function togglePencil() {
return {
type: 's/TGL_PENCIL',
};
}
export function selectStyle(style) { export function selectStyle(style) {
return { return {
type: 's/SELECT_STYLE', type: 's/SELECT_STYLE',

View File

@ -6,6 +6,7 @@ const initialState = {
isLightGrid: false, isLightGrid: false,
compactPalette: false, compactPalette: false,
paletteOpen: true, paletteOpen: true,
pencilEnabled: false,
mute: false, mute: false,
chatNotify: true, chatNotify: true,
// top-left button menu // top-left button menu
@ -78,6 +79,13 @@ export default function gui(
}; };
} }
case 's/TGL_PENCIL': {
return {
...state,
pencilEnabled: !state.pencilEnabled,
};
}
case 's/TGL_OPEN_MENU': { case 's/TGL_OPEN_MENU': {
return { return {
...state, ...state,
@ -121,6 +129,12 @@ export default function gui(
chatNotify: !state.chatNotify, chatNotify: !state.chatNotify,
}; };
case 'persist/REHYDRATE':
return {
...state,
pencilEnabled: false,
};
default: default:
return state; return state;
} }

View File

@ -460,6 +460,14 @@ tr:nth-child(even) {
bottom: 16px; bottom: 16px;
right: 57px; right: 57px;
} }
#pencilbutton {
bottom: 98px;
left: 16px;
transition: bottom 300ms ease-in-out;
}
#pencilbutton.pressed {
bottom: 180px;
}
#historyselect { #historyselect {
position: fixed; position: fixed;