@@ -2,10 +2,12 @@ import { useMemo } from 'react';
22import { Html , Line } from '@react-three/drei' ;
33import { Earth , Moon , Sun } from 'lucide-react' ;
44
5+ import { clamp } from '@/utils/math' ;
56import type { Vec3 } from '@/viewport3d/types' ;
67
78import { CELESTIAL_INDICATOR } from './constants' ;
89import { ricToPosition } from './coordinates' ;
10+ import type { CelestialBodyDir } from './scene' ;
911
1012type CelestialBody = 'earth' | 'sun' | 'moon' ;
1113
@@ -16,23 +18,43 @@ const ICON_BY_BODY = {
1618} as const ;
1719
1820type Props = {
19- unitRic : Vec3 ;
20- edgeM : number ;
21+ dir : CelestialBodyDir ;
22+ gridExtentM : number ;
2123 body : CelestialBody ;
2224 color : string ;
2325} ;
2426
25- export default function CelestialIndicator ( { unitRic, edgeM, body, color } : Props ) {
26- const { tip, iconPos } = useMemo ( ( ) => {
27- const tipRic : Vec3 = [ unitRic [ 0 ] * edgeM , unitRic [ 1 ] * edgeM , unitRic [ 2 ] * edgeM ] ;
28- const offset = edgeM * CELESTIAL_INDICATOR . labelOffsetRatio ;
27+ export default function CelestialIndicator ( { dir, gridExtentM, body, color } : Props ) {
28+ const { tip, iconPos, opacity, iconOpacity } = useMemo ( ( ) => {
29+ const { unitRic, distanceKm } = dir ;
30+ const log = Math . log10 ( Math . max ( distanceKm , 1 ) ) ;
31+ const span = CELESTIAL_INDICATOR . logKmMax - CELESTIAL_INDICATOR . logKmMin ;
32+ const t = clamp ( ( log - CELESTIAL_INDICATOR . logKmMin ) / span , 0 , 1 ) ;
33+
34+ const lengthRatio =
35+ CELESTIAL_INDICATOR . lengthRatioNear +
36+ t * ( CELESTIAL_INDICATOR . lengthRatioFar - CELESTIAL_INDICATOR . lengthRatioNear ) ;
37+ const lengthM = gridExtentM * lengthRatio ;
38+
39+ const opacity =
40+ CELESTIAL_INDICATOR . opacityNear +
41+ t * ( CELESTIAL_INDICATOR . opacityFar - CELESTIAL_INDICATOR . opacityNear ) ;
42+ const iconOpacity = Math . min ( 1 , opacity + CELESTIAL_INDICATOR . iconOpacityBias ) ;
43+
44+ const tipRic : Vec3 = [ unitRic [ 0 ] * lengthM , unitRic [ 1 ] * lengthM , unitRic [ 2 ] * lengthM ] ;
45+ const offset = lengthM * CELESTIAL_INDICATOR . labelOffsetRatio ;
2946 const iconRic : Vec3 = [
30- unitRic [ 0 ] * ( edgeM + offset ) ,
31- unitRic [ 1 ] * ( edgeM + offset ) ,
32- unitRic [ 2 ] * ( edgeM + offset ) ,
47+ unitRic [ 0 ] * ( lengthM + offset ) ,
48+ unitRic [ 1 ] * ( lengthM + offset ) ,
49+ unitRic [ 2 ] * ( lengthM + offset ) ,
3350 ] ;
34- return { tip : ricToPosition ( tipRic ) , iconPos : ricToPosition ( iconRic ) } ;
35- } , [ unitRic , edgeM ] ) ;
51+ return {
52+ tip : ricToPosition ( tipRic ) ,
53+ iconPos : ricToPosition ( iconRic ) ,
54+ opacity,
55+ iconOpacity,
56+ } ;
57+ } , [ dir , gridExtentM ] ) ;
3658
3759 const Icon = ICON_BY_BODY [ body ] ;
3860
@@ -42,15 +64,15 @@ export default function CelestialIndicator({ unitRic, edgeM, body, color }: Prop
4264 points = { [ [ 0 , 0 , 0 ] , tip ] }
4365 color = { color }
4466 lineWidth = { CELESTIAL_INDICATOR . lineWidth }
45- opacity = { CELESTIAL_INDICATOR . lineOpacity }
67+ opacity = { opacity }
4668 transparent
4769 depthWrite = { false }
4870 />
4971 < Html
5072 position = { iconPos }
5173 center
5274 zIndexRange = { [ 20 , 0 ] }
53- style = { { pointerEvents : 'none' , color } }
75+ style = { { pointerEvents : 'none' , color, opacity : iconOpacity } }
5476 >
5577 < Icon
5678 size = { CELESTIAL_INDICATOR . iconSizePx }
0 commit comments