@using Microsoft.AspNetCore.Components @code { private ElementReference _groupRef; private string _arrowMarkerId = "goal-mouse-arrow-" + Guid.NewGuid().ToString("N")[..8]; private double _robotX; private double _robotY; private double _goalX; private double _goalY; private double _goalOrientationRad; private double _mouseX; private double _mouseY; private string _visibility = "hidden"; /// Tọa độ X điểm đích (world coordinates). public double GoalX => _goalX; /// Tọa độ Y điểm đích (world coordinates). public double GoalY => _goalY; /// Hướng điểm đích (yaw, radians) — hướng từ goal đến mouse; 0 khi goal trùng mouse. public double GoalYaw => _goalOrientationRad; /// Bán kính circle = khoảng cách Goal -> mouse; 0 khi goal trùng mouse (hợp lệ). private double _radius => Math.Sqrt((_mouseX - _goalX) * (_mouseX - _goalX) + (_mouseY - _goalY) * (_mouseY - _goalY)); /// /// Hiển thị nhóm line và circle (robot->goal, goal->mouse, circle). /// public void Show() { _visibility = "visible"; StateHasChanged(); } /// /// Ẩn nhóm line và circle. /// public void Hide() { _visibility = "hidden"; StateHasChanged(); } /// /// Đặt tọa độ robot (world coordinates). /// public void SetRobotPosition(double x, double y) { _robotX = x; _robotY = y; StateHasChanged(); } /// /// Đặt tọa độ điểm đích (world coordinates). /// public void SetGoalPosition(double x, double y) { _goalX = x; _goalY = y; StateHasChanged(); } /// /// Cập nhật vị trí chuột (world coordinates). Parent gọi từ OnMouseMove để vẽ line goal->mouse và circle. /// Tự tính _goalOrientationRad = hướng từ goal đến mouse (radians); 0 khi goal trùng mouse. /// public void SetMousePosition(double x, double y) { _mouseX = x; _mouseY = y; var dx = _mouseX - _goalX; var dy = _mouseY - _goalY; _goalOrientationRad = (Math.Abs(dx) < 1e-9 && Math.Abs(dy) < 1e-9) ? 0 : Math.Atan2(dy, dx); StateHasChanged(); } private const double OptimizeMinDistanceMeters = 1.0; /// /// Nếu khoảng cách từ (GoalX, GoalY) đến (mouseX, mouseY) < 1 m thì gọi Hide(). /// Nếu khoảng cách >= 1 m thì đặt lại _mouseX, _mouseY sao cho khoảng cách đúng 1 m (giữ hướng). /// Tọa độ trong SVG là mét (viewBox + scaleY(-1) trong MapLocalization). /// public void Optimize() { var dx = _mouseX - _goalX; var dy = _mouseY - _goalY; var d = Math.Sqrt(dx * dx + dy * dy); if (d < OptimizeMinDistanceMeters) { Hide(); return; } var scale = OptimizeMinDistanceMeters / d; _mouseX = _goalX + dx * scale; _mouseY = _goalY + dy * scale; _goalOrientationRad = (Math.Abs(dx) < 1e-9 && Math.Abs(dy) < 1e-9) ? 0 : Math.Atan2(dy, dx); StateHasChanged(); } }