feat: pitch and yaw limits for inputs

This commit is contained in:
TrèsAbhi committed 2024-01-06 13:21:11 -06:00
1 parent 21122c620b
commit 14fb62bda4
4 files changed
+113 -150

No files matched your search

@@ -1,7 +1,7 @@
import { ThreeEvent, useThree } from '@react-three/fiber';
import { clamp } from 'lodash';
import { RefObject, forwardRef, useImperativeHandle, useRef } from 'react';
import { Group, Mesh, Object3D, Vector3 } from 'three';
import { applyPitchYawLimits } from '../../../../../core/blitz/applyPitchYawLimits';
import {
InitialTurretRotation,
PitchLimits,
@@ -78,77 +78,11 @@ export const GunContainer = forwardRef<Group, GunContainerProps>(
window.addEventListener('pointerup', handlePointerUp);
}
function handlePointerMove(event: PointerEvent) {
if (yawLimits) {
draftYaw = clamp(
draftYaw + event.movementX * (Math.PI / canvas.width),
-yawLimits.max * (Math.PI / 180),
-yawLimits.min * (Math.PI / 180),
);
} else {
draftYaw += event.movementX * ((2 * Math.PI) / canvas.width);
}
let lowerPitch = -pitchLimits.max * (Math.PI / 180);
let upperPitch = -pitchLimits.min * (Math.PI / 180);
const transition = (pitchLimits.transition ?? 10) * (Math.PI / 180);
if (pitchLimits.back) {
const range = pitchLimits.back.range * (Math.PI / 180);
const yawRotatedAbs = Math.abs(
normalizeAnglePI(draftYaw - Math.PI),
);
if (yawRotatedAbs <= range + transition) {
if (yawRotatedAbs <= range) {
// inside range
lowerPitch = -pitchLimits.back.max * (Math.PI / 180);
upperPitch = -pitchLimits.back.min * (Math.PI / 180);
} else {
// inside transition
const transitionProgress =
(yawRotatedAbs - range) / transition;
const maxInterpolatedPitch =
(1 - transitionProgress) * pitchLimits.back.max +
transitionProgress * pitchLimits.max;
const minInterpolatedPitch =
(1 - transitionProgress) * pitchLimits.back.min +
transitionProgress * pitchLimits.min;
lowerPitch = -maxInterpolatedPitch * (Math.PI / 180);
upperPitch = -minInterpolatedPitch * (Math.PI / 180);
}
}
}
if (pitchLimits.front) {
const range = pitchLimits.front.range * (Math.PI / 180);
const yawAbs = Math.abs(normalizeAnglePI(draftYaw));
if (yawAbs <= range + transition) {
if (yawAbs <= range) {
// inside range
lowerPitch = -pitchLimits.front.max * (Math.PI / 180);
upperPitch = -pitchLimits.front.min * (Math.PI / 180);
} else {
// inside transition
const transitionProgress = (yawAbs - range) / transition;
const maxInterpolatedPitch =
(1 - transitionProgress) * pitchLimits.front.max +
transitionProgress * pitchLimits.max;
const minInterpolatedPitch =
(1 - transitionProgress) * pitchLimits.front.min +
transitionProgress * pitchLimits.min;
lowerPitch = -maxInterpolatedPitch * (Math.PI / 180);
upperPitch = -minInterpolatedPitch * (Math.PI / 180);
}
}
}
draftPitch = clamp(
[draftPitch, draftYaw] = applyPitchYawLimits(
draftPitch - event.movementY * (Math.PI / canvas.height),
lowerPitch,
upperPitch,
draftYaw + event.movementX * (Math.PI / canvas.width),
pitchLimits,
yawLimits,
);
if (gunContainer.current) {
@@ -1,6 +1,6 @@
import { Flex, TextField } from '@radix-ui/themes';
import { clamp } from 'lodash';
import { use, useEffect, useRef } from 'react';
import { applyPitchYawLimits } from '../../../../../core/blitz/applyPitchYawLimits';
import { modelDefinitions } from '../../../../../core/blitzkrieg/modelDefinitions';
import { normalizeAnglePI } from '../../../../../core/math/normalizeAngle180';
import mutateTankopedia, {
@@ -94,13 +94,19 @@ export function RotationInputs({
<TextField.Input
defaultValue={Math.round(model.turretYaw * (180 / Math.PI))}
onBlur={() => {
const normalized = normalizeAnglePI(
const [pitch, yaw] = applyPitchYawLimits(
model.gunPitch,
-Number(turretYawInput.current?.value) * (Math.PI / 180),
gunModelDefinition.pitch,
turretModelDefinition.yaw,
);
mutateTankopedia((state) => {
state.model.turretYaw = normalized;
state.model.gunPitch = pitch;
state.model.turretYaw = yaw;
});
turretYawInput.current!.value = `${Math.round(normalized)}`;
turretYawInput.current!.value = `${Math.round(
yaw * (180 / Math.PI),
)}`;
}}
onKeyDown={(event) => {
if (event.key === 'Enter') {
@@ -124,15 +130,19 @@ export function RotationInputs({
<TextField.Input
defaultValue={-Math.round(model.gunPitch * (180 / Math.PI))}
onBlur={() => {
const clamped = clamp(
Number(gunPitchInput.current?.value),
gunModelDefinition.pitch.min,
gunModelDefinition.pitch.max,
const [pitch, yaw] = applyPitchYawLimits(
-Number(gunPitchInput.current?.value) * (Math.PI / 180),
model.turretYaw,
gunModelDefinition.pitch,
turretModelDefinition.yaw,
);
mutateTankopedia((state) => {
state.model.gunPitch = -clamped * (Math.PI / 180);
state.model.gunPitch = pitch;
state.model.turretYaw = yaw;
});
gunPitchInput.current!.value = `${Math.round(clamped)}`;
gunPitchInput.current!.value = `${Math.round(
pitch * (180 / Math.PI),
)}`;
}}
onKeyDown={(event) => {
if (event.key === 'Enter') {
@@ -1,5 +1,4 @@
import { ThreeEvent, useThree } from '@react-three/fiber';
import { clamp } from 'lodash';
import {
ReactNode,
RefObject,
@@ -8,6 +7,7 @@ import {
useRef,
} from 'react';
import { Euler, Group, Mesh, Object3D, Vector3 } from 'three';
import { applyPitchYawLimits } from '../../../../../core/blitz/applyPitchYawLimits';
import {
InitialTurretRotation,
PitchLimits,
@@ -99,74 +99,12 @@ export const TurretContainer = forwardRef(
}
}
function handlePointerMove(event: PointerEvent) {
if (yawLimits) {
draftYaw = clamp(
draftYaw + event.movementX * (Math.PI / canvas.width),
-yawLimits.max * (Math.PI / 180),
-yawLimits.min * (Math.PI / 180),
);
} else {
draftYaw += event.movementX * ((2 * Math.PI) / canvas.width);
}
let lowerPitch = -pitchLimits.max * (Math.PI / 180);
let upperPitch = -pitchLimits.min * (Math.PI / 180);
const transition = (pitchLimits.transition ?? 10) * (Math.PI / 180);
if (pitchLimits.back) {
const range = pitchLimits.back.range * (Math.PI / 180);
const yawRotatedAbs = Math.abs(
normalizeAnglePI(draftYaw - Math.PI),
);
if (yawRotatedAbs <= range + transition) {
if (yawRotatedAbs <= range) {
// inside range
lowerPitch = -pitchLimits.back.max * (Math.PI / 180);
upperPitch = -pitchLimits.back.min * (Math.PI / 180);
} else {
// inside transition
const transitionProgress =
(yawRotatedAbs - range) / transition;
const maxInterpolatedPitch =
(1 - transitionProgress) * pitchLimits.back.max +
transitionProgress * pitchLimits.max;
const minInterpolatedPitch =
(1 - transitionProgress) * pitchLimits.back.min +
transitionProgress * pitchLimits.min;
lowerPitch = -maxInterpolatedPitch * (Math.PI / 180);
upperPitch = -minInterpolatedPitch * (Math.PI / 180);
}
}
}
if (pitchLimits.front) {
const range = pitchLimits.front.range * (Math.PI / 180);
const yawAbs = Math.abs(normalizeAnglePI(draftYaw));
if (yawAbs <= range + transition) {
if (yawAbs <= range) {
// inside range
lowerPitch = -pitchLimits.front.max * (Math.PI / 180);
upperPitch = -pitchLimits.front.min * (Math.PI / 180);
} else {
// inside transition
const transitionProgress = (yawAbs - range) / transition;
const maxInterpolatedPitch =
(1 - transitionProgress) * pitchLimits.front.max +
transitionProgress * pitchLimits.max;
const minInterpolatedPitch =
(1 - transitionProgress) * pitchLimits.front.min +
transitionProgress * pitchLimits.min;
lowerPitch = -maxInterpolatedPitch * (Math.PI / 180);
upperPitch = -minInterpolatedPitch * (Math.PI / 180);
}
}
}
draftPitch = clamp(draftPitch, lowerPitch, upperPitch);
[draftPitch, draftYaw] = applyPitchYawLimits(
draftPitch,
draftYaw + event.movementX * (Math.PI / canvas.width),
pitchLimits,
yawLimits,
);
if (gunContainer.current) {
gunContainer.current.position
+81
View File
@@ -0,0 +1,81 @@
import { clamp } from 'lodash';
import { PitchLimits, YawLimits } from '../blitzkrieg/modelDefinitions';
import { normalizeAnglePI } from '../math/normalizeAngle180';
export function applyPitchYawLimits(
pitch: number, // rad
yaw: number, // rad
pitchLimits: PitchLimits, // deg
yawLimits?: YawLimits, // deg
) {
let newPitch = pitch;
let newYaw = yaw;
if (yawLimits) {
newYaw = normalizeAnglePI(
clamp(
newYaw,
-yawLimits.max * (Math.PI / 180),
-yawLimits.min * (Math.PI / 180),
),
);
}
let lowerPitch = -pitchLimits.max * (Math.PI / 180);
let upperPitch = -pitchLimits.min * (Math.PI / 180);
const transition = (pitchLimits.transition ?? 10) * (Math.PI / 180);
if (pitchLimits.back) {
const range = pitchLimits.back.range * (Math.PI / 180);
const yawRotatedAbs = Math.abs(normalizeAnglePI(newYaw - Math.PI));
if (yawRotatedAbs <= range + transition) {
if (yawRotatedAbs <= range) {
// inside range
lowerPitch = -pitchLimits.back.max * (Math.PI / 180);
upperPitch = -pitchLimits.back.min * (Math.PI / 180);
} else {
// inside transition
const transitionProgress = (yawRotatedAbs - range) / transition;
const maxInterpolatedPitch =
(1 - transitionProgress) * pitchLimits.back.max +
transitionProgress * pitchLimits.max;
const minInterpolatedPitch =
(1 - transitionProgress) * pitchLimits.back.min +
transitionProgress * pitchLimits.min;
lowerPitch = -maxInterpolatedPitch * (Math.PI / 180);
upperPitch = -minInterpolatedPitch * (Math.PI / 180);
}
}
}
if (pitchLimits.front) {
const range = pitchLimits.front.range * (Math.PI / 180);
const yawAbs = Math.abs(normalizeAnglePI(newYaw));
if (yawAbs <= range + transition) {
if (yawAbs <= range) {
// inside range
lowerPitch = -pitchLimits.front.max * (Math.PI / 180);
upperPitch = -pitchLimits.front.min * (Math.PI / 180);
} else {
// inside transition
const transitionProgress = (yawAbs - range) / transition;
const maxInterpolatedPitch =
(1 - transitionProgress) * pitchLimits.front.max +
transitionProgress * pitchLimits.max;
const minInterpolatedPitch =
(1 - transitionProgress) * pitchLimits.front.min +
transitionProgress * pitchLimits.min;
lowerPitch = -maxInterpolatedPitch * (Math.PI / 180);
upperPitch = -minInterpolatedPitch * (Math.PI / 180);
}
}
}
newPitch = clamp(newPitch, lowerPitch, upperPitch);
return [newPitch, newYaw];
}