From 6b7f8049fec7fcc9d5c7bcc1ad2c8a0398765d8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tr=C3=A8sAbhi?= <43380238+tresabhi@users.noreply.github.com> Date: Sat, 9 Aug 2025 12:31:36 -0500 Subject: [PATCH] feat: elevation --- .../AssaultRangesVisualizer/index.tsx | 7 +- .../GunFlexibilityVisualizer/index.tsx | 93 ++++++++++++++----- .../components/ReloadVisualizer/index.tsx | 8 +- .../components/RicochetVisualizer/index.tsx | 38 ++++---- .../components/TraverseVisualizer/index.tsx | 7 +- .../components/VisualizerCard.tsx | 17 ++++ 6 files changed, 117 insertions(+), 53 deletions(-) create mode 100644 packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/VisualizerCard.tsx diff --git a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/AssaultRangesVisualizer/index.tsx b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/AssaultRangesVisualizer/index.tsx index cdd089725..a6986e04e 100644 --- a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/AssaultRangesVisualizer/index.tsx +++ b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/AssaultRangesVisualizer/index.tsx @@ -1,6 +1,6 @@ import type { AssaultRanges } from '@blitzkit/core'; import { literals } from '@blitzkit/i18n'; -import { Box, Card, Flex, Text } from '@radix-ui/themes'; +import { Box, Flex, Text } from '@radix-ui/themes'; import { clamp } from 'three/src/math/MathUtils.js'; import { fakeLogSquishyHigh, @@ -9,6 +9,7 @@ import { import type { TankCharacteristics } from '../../../../../../../core/blitzkit/tankCharacteristics'; import { Var } from '../../../../../../../core/radix/var'; import { useLocale } from '../../../../../../../hooks/useLocale'; +import { VisualizerCard } from '../VisualizerCard'; import { AssaultCursor } from './components/Cursor'; interface Props { @@ -29,7 +30,7 @@ export function AssaultRangesVisualizer({ ranges: _ranges, stats }: Props) { const { strings } = useLocale(); return ( - + - + ); } diff --git a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/GunFlexibilityVisualizer/index.tsx b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/GunFlexibilityVisualizer/index.tsx index 37b65dbd3..a08728136 100644 --- a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/GunFlexibilityVisualizer/index.tsx +++ b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/GunFlexibilityVisualizer/index.tsx @@ -1,14 +1,14 @@ -import { Card } from '@radix-ui/themes'; import { awaitableModelDefinitions } from '../../../../../../../core/awaitables/modelDefinitions'; import { DEFAULT_PITCH_TRANSITION } from '../../../../../../../core/blitz/applyPitchYawLimits'; import { Duel } from '../../../../../../../stores/duel'; +import { VisualizerCard } from '../VisualizerCard'; -const MAX_MAG = 20; +const ANGLE_COEFFICIENT = 1 / 10; const modelDefinition = await awaitableModelDefinitions; function mag(x: number) { - return (x / MAX_MAG + 1) / 2; + return ((2 / Math.PI) * Math.atan(ANGLE_COEFFICIENT * x) + 1) / 2; } function c(thetaDeg: number, m = 1) { @@ -34,18 +34,18 @@ export function GunFlexibilityVisualizer() { let d = ''; // right - const ms = mag(p.max); + const mo = mag(p.max); if (y) { - d += `M ${c(90 - y.max, ms)}`; + d += `M ${c(90 - y.max, mo)}`; } else if (b) { - d += `M ${c(-90 + b.range / 2 + t, ms)}`; + d += `M ${c(-90 + b.range / 2 + t, mo)}`; } else { - d += `M ${c(-90 / 2, ms)}`; + d += `M ${c(-90 / 2, mo)}`; } if (f) { - d += `A ${ms} ${ms} 0 0 0 ${c(90 - f.range / 2 - t, ms)}`; + d += `A ${mo} ${mo} 0 0 0 ${c(90 - f.range / 2 - t, mo)}`; } else { - d += `A ${ms} ${ms} 0 0 0 ${c(90 / 2, ms)}`; + d += `A ${mo} ${mo} 0 0 0 ${c(90 / 2, mo)}`; } // front @@ -57,16 +57,16 @@ export function GunFlexibilityVisualizer() { // left if (f) { - d += `L ${c(90 + f.range / 2 + t, ms)}`; + d += `L ${c(90 + f.range / 2 + t, mo)}`; } else { - d += `L ${c(90 / 2, ms)}`; + d += `L ${c(90 / 2, mo)}`; } if (y) { - d += `A ${ms} ${ms} 0 0 0 ${c(90 - y.min, ms)}`; + d += `A ${mo} ${mo} 0 0 0 ${c(90 - y.min, mo)}`; } else if (b) { - d += `A ${ms} ${ms} 0 0 0 ${c(-90 - b.range / 2 - t, ms)}`; + d += `A ${mo} ${mo} 0 0 0 ${c(-90 - b.range / 2 - t, mo)}`; } else { - d += `A ${ms} ${ms} 0 0 0 ${c(-90 / 2, ms)}`; + d += `A ${mo} ${mo} 0 0 0 ${c(-90 / 2, mo)}`; } // back @@ -78,8 +78,53 @@ export function GunFlexibilityVisualizer() { d += `Z`; + // right + const mi = mag(p.min); + if (y) { + d += `M ${c(90 - y.max, mi)}`; + } else if (b) { + d += `M ${c(-90 + b.range / 2 + t, mi)}`; + } else { + d += `M ${c(-90 / 2, mi)}`; + } + if (f) { + d += `A ${mi} ${mi} 0 0 0 ${c(90 - f.range / 2 - t, mi)}`; + } else { + d += `A ${mi} ${mi} 0 0 0 ${c(90 / 2, mi)}`; + } + + // front + if (f) { + const m = mag(f.min); + d += `L ${c(90 - f.range / 2, m)}`; + d += `A ${m} ${m} 0 0 0 ${c(90 + f.range / 2, m)}`; + } + + // left + if (f) { + d += `L ${c(90 + f.range / 2 + t, mi)}`; + } else { + d += `L ${c(90 / 2, mi)}`; + } + if (y) { + d += `A ${mi} ${mi} 0 0 0 ${c(90 - y.min, mi)}`; + } else if (b) { + d += `A ${mi} ${mi} 0 0 0 ${c(-90 - b.range / 2 - t, mi)}`; + } else { + d += `A ${mi} ${mi} 0 0 0 ${c(-90 / 2, mi)}`; + } + + // back + if (!y && b) { + const m = mag(b.min); + d += `L ${c(-90 - b.range / 2, m)}`; + d += `A ${m} ${m} 0 0 0 ${c(-90 + b.range / 2, m)}`; + } + + d += `Z`; + return ( - + + - + ); } diff --git a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/ReloadVisualizer/index.tsx b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/ReloadVisualizer/index.tsx index 31a8de1a1..4141981a5 100644 --- a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/ReloadVisualizer/index.tsx +++ b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/ReloadVisualizer/index.tsx @@ -1,10 +1,11 @@ import { type Datum, type Serie } from '@nivo/line'; -import { Box, Card } from '@radix-ui/themes'; +import { Box } from '@radix-ui/themes'; import { times } from 'lodash-es'; import { Quicklime, type QuicklimeCallback } from 'quicklime'; import { useEffect, useRef } from 'react'; import { Var } from '../../../../../../../core/radix/var'; import type { StatsAcceptorProps } from '../TraverseVisualizer'; +import { VisualizerCard } from '../VisualizerCard'; import { Shell } from './components/Shell'; import { Target } from './components/Target'; @@ -51,7 +52,6 @@ export function ReloadVisualizer({ stats }: StatsAcceptorProps) { } function frame() { - const now = Date.now() / 1000; const dt = now - lastT; let reload; @@ -130,7 +130,7 @@ export function ReloadVisualizer({ stats }: StatsAcceptorProps) { }, []); return ( - + @@ -220,6 +220,6 @@ export function ReloadVisualizer({ stats }: StatsAcceptorProps) { times(stats.shells, (index) => ( ))} - + ); } diff --git a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/RicochetVisualizer/index.tsx b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/RicochetVisualizer/index.tsx index f6557e589..520eee968 100644 --- a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/RicochetVisualizer/index.tsx +++ b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/RicochetVisualizer/index.tsx @@ -1,12 +1,13 @@ import { asset, isExplosive } from '@blitzkit/core'; import { literals } from '@blitzkit/i18n'; -import { Box, Card, Code, Flex, Text } from '@radix-ui/themes'; +import { Box, Code, Flex, Text } from '@radix-ui/themes'; import { useRef, useState } from 'react'; import { clamp, degToRad, radToDeg } from 'three/src/math/MathUtils.js'; import { Var } from '../../../../../../../core/radix/var'; import { useLocale } from '../../../../../../../hooks/useLocale'; import { Duel } from '../../../../../../../stores/duel'; import type { StatsAcceptorProps } from '../TraverseVisualizer'; +import { VisualizerCard } from '../VisualizerCard'; import { ShellSwitcher } from './components/ShellSwitcher'; export function RicochetVisualizer({ stats }: StatsAcceptorProps) { @@ -26,13 +27,8 @@ export function RicochetVisualizer({ stats }: StatsAcceptorProps) { return ( - { if (!container.current || explodes) return; @@ -73,23 +69,23 @@ export function RicochetVisualizer({ stats }: StatsAcceptorProps) { position="absolute" style={{ background: `conic-gradient( - at 0 50%, + at 0 50%, - ${Var('ruby-a2')}, - ${Var('ruby-a1')} ${Math.PI / 2 - ricochet}rad, + ${Var('ruby-a2')}, + ${Var('ruby-a1')} ${Math.PI / 2 - ricochet}rad, - ${Var('jade-a2')} ${Math.PI / 2 - ricochet}rad, - ${Var('jade-a4')} ${Math.PI / 2 - normalization}rad, + ${Var('jade-a2')} ${Math.PI / 2 - ricochet}rad, + ${Var('jade-a4')} ${Math.PI / 2 - normalization}rad, - ${Var('cyan-a5')} ${Math.PI / 2 - normalization}rad, - ${Var('cyan-a3')} ${Math.PI / 2 + normalization}rad, + ${Var('cyan-a5')} ${Math.PI / 2 - normalization}rad, + ${Var('cyan-a3')} ${Math.PI / 2 + normalization}rad, - ${Var('jade-a2')} ${Math.PI / 2 + normalization}rad, - ${Var('jade-a4')} ${Math.PI / 2 + ricochet}rad, + ${Var('jade-a2')} ${Math.PI / 2 + normalization}rad, + ${Var('jade-a4')} ${Math.PI / 2 + ricochet}rad, - ${Var('ruby-a1')} ${Math.PI / 2 + ricochet}rad, - ${Var('ruby-a2')} 180deg - `, + ${Var('ruby-a1')} ${Math.PI / 2 + ricochet}rad, + ${Var('ruby-a2')} 180deg + `, }} /> @@ -262,7 +258,7 @@ export function RicochetVisualizer({ stats }: StatsAcceptorProps) { )} - + diff --git a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/TraverseVisualizer/index.tsx b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/TraverseVisualizer/index.tsx index 6657bea12..b232e9367 100644 --- a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/TraverseVisualizer/index.tsx +++ b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/TraverseVisualizer/index.tsx @@ -1,8 +1,9 @@ import { literals } from '@blitzkit/i18n'; -import { Card, Checkbox, Code, Flex, Tabs, Text } from '@radix-ui/themes'; +import { Checkbox, Code, Flex, Tabs, Text } from '@radix-ui/themes'; import { useEffect, useRef, useState } from 'react'; import type { TankCharacteristics } from '../../../../../../../core/blitzkit/tankCharacteristics'; import { useLocale } from '../../../../../../../hooks/useLocale'; +import { VisualizerCard } from '../VisualizerCard'; export interface StatsAcceptorProps { stats: TankCharacteristics; @@ -58,7 +59,7 @@ export function TraverseVisualizer({ stats }: StatsAcceptorProps) { }, [terrain, turretSpeedDeg, hullSpeedDeg]); return ( - +
)} - + ); } diff --git a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/VisualizerCard.tsx b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/VisualizerCard.tsx new file mode 100644 index 000000000..5b09b8623 --- /dev/null +++ b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/VisualizerCard.tsx @@ -0,0 +1,17 @@ +import { Card, type CardProps } from '@radix-ui/themes'; +import { forwardRef } from 'react'; + +export const VisualizerCard = forwardRef( + ({ style, ...props }, ref) => { + return ( + + ); + }, +);