FERS 1.0.0
The Flexible Extensible Radar Simulator
Loading...
Searching...
No Matches
MotionPathLine.tsx
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-only
2// Copyright (c) 2025-present FERS Contributors (see AUTHORS.md).
3
4import { useEffect, useMemo, useState } from 'react';
5import { Line } from '@react-three/drei';
6import { Vector3 } from 'three';
7import { invoke } from '@tauri-apps/api/core';
8import { useScenarioStore, Platform } from '@/stores/scenarioStore';
9import { fersColors } from '@/theme';
10
11const NUM_PATH_POINTS = 100; // The resolution of the rendered path line.
12
13// --- Type definitions for Tauri backend communication ---
14type InterpolationType = 'static' | 'linear' | 'cubic';
15
16interface InterpolatedPoint {
17 x: number; // Corresponds to ENU X -> Three.js X
18 y: number; // Corresponds to ENU Y -> Three.js -Z
19 z: number; // Corresponds to ENU Altitude -> Three.js Y
20}
21
22/**
23 * A component that fetches and renders the interpolated motion path for a given platform.
24 * It communicates with the Tauri backend to compute the path using the libfers core.
25 * @param {object} props - The component props.
26 * @param {Platform} props.platform - The platform whose motion path should be rendered.
27 */
28export function MotionPathLine({ platform }: { platform: Platform }) {
29 const [pathPoints, setPathPoints] = useState<Vector3[] | null>(null);
30 const showError = useScenarioStore((state) => state.showError);
31
32 const { waypoints, interpolation } = platform.motionPath;
33
34 useEffect(() => {
35 const fetchPath = async () => {
36 if (waypoints.length === 0) {
37 setPathPoints(null);
38 return;
39 }
40
41 // For static or single-waypoint paths, no need to call the backend.
42 // A static object has no visible path.
43 if (interpolation === 'static' || waypoints.length < 2) {
44 setPathPoints(null);
45 return;
46 }
47
48 try {
49 // The frontend 'PositionWaypoint' has an 'altitude' field, which the
50 // Rust backend expects. The Rust `MotionWaypoint` struct will map this
51 // correctly to the C-API's 'z' field.
52 const points = await invoke<InterpolatedPoint[]>(
53 'get_interpolated_motion_path',
54 {
55 waypoints: waypoints,
56 interpType: interpolation as InterpolationType,
57 numPoints: NUM_PATH_POINTS,
58 }
59 );
60
61 const vectors = points.map(
62 // ENU to Three.js coordinate system mapping:
63 // Backend X (East) -> Three.js X
64 // Backend Z (Up) -> Three.js Y
65 // Backend Y (North) -> Three.js -Z
66 (p) => new Vector3(p.x, p.z, -p.y)
67 );
68 setPathPoints(vectors);
69 } catch (error) {
70 const errorMessage =
71 error instanceof Error ? error.message : String(error);
72 console.error(
73 `Failed to fetch motion path for platform ${platform.name}:`,
74 errorMessage
75 );
76 showError(
77 `Failed to get motion path for ${platform.name}: ${errorMessage}`
78 );
79 setPathPoints(null);
80 }
81 };
82
83 void fetchPath();
84 }, [waypoints, interpolation, platform.name, showError]);
85
86 const linePoints = useMemo(() => {
87 if (!pathPoints || pathPoints.length < 2) return undefined;
88 return pathPoints;
89 }, [pathPoints]);
90
91 if (!linePoints) {
92 return null;
93 }
94
95 return (
96 <Line
97 points={linePoints}
98 color={fersColors.physics.motionPath}
99 lineWidth={1.5}
100 dashed={false}
101 />
102 );
103}