Files
Denso/srcs/RobotNet10/RobotApp/RobotNet10.RobotApp.Client/Components/SLAM/GoalPose.razor
2026-07-03 16:31:37 +07:00

122 lines
4.8 KiB
Plaintext

@using Microsoft.AspNetCore.Components
<!-- Robot goal pose: line robot->goal, line goal->mouse (with arrow), circle at goal with radius = distance(goal,mouse).
_goalOrientationRad được tính trong SetMousePosition: hướng từ goal đến mouse; 0 khi goal trùng mouse. -->
<g visibility="@_visibility" @ref="_groupRef">
<defs>
<marker id="@_arrowMarkerId" markerWidth="0.15" markerHeight="0.15" refX="0" refY="0.075" orient="auto" markerUnits="userSpaceOnUse">
<path d="M 0 0 L 0.15 0.075 L 0 0.15 Z" fill="#FF0000" stroke="none" />
</marker>
</defs>
<!-- Line 1: RobotPosition -> GoalPosition (red, dashed, 0.1); zero length when robot==goal is valid -->
<line x1="@_robotX" y1="@_robotY" x2="@_goalX" y2="@_goalY"
stroke="green" stroke-width="0.03" stroke-dasharray="0.3,0.2" fill="none" />
<!-- Line 2: GoalPosition -> current mouse (blue, dashed, 0.1, arrow at mouse) -->
<line x1="@_goalX" y1="@_goalY" x2="@_mouseX" y2="@_mouseY"
stroke="#FF0000" stroke-width="0.03" stroke-dasharray="0.3,0.2" fill="none"
marker-end="url(#@_arrowMarkerId)" />
<!-- Circle: center GoalPosition, radius = distance(Goal, mouse); r=0 when goal==mouse is valid -->
<circle cx="@_goalX" cy="@_goalY" r="@_radius"
stroke="green" stroke-width="0.03" stroke-dasharray="0.3,0.2" fill="none" />
</g>
@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";
/// <summary>Tọa độ X điểm đích (world coordinates).</summary>
public double GoalX => _goalX;
/// <summary>Tọa độ Y điểm đích (world coordinates).</summary>
public double GoalY => _goalY;
/// <summary>Hướng điểm đích (yaw, radians) — hướng từ goal đến mouse; 0 khi goal trùng mouse.</summary>
public double GoalYaw => _goalOrientationRad;
/// <summary>Bán kính circle = khoảng cách Goal -> mouse; 0 khi goal trùng mouse (hợp lệ).</summary>
private double _radius => Math.Sqrt((_mouseX - _goalX) * (_mouseX - _goalX) + (_mouseY - _goalY) * (_mouseY - _goalY));
/// <summary>
/// Hiển thị nhóm line và circle (robot->goal, goal->mouse, circle).
/// </summary>
public void Show()
{
_visibility = "visible";
StateHasChanged();
}
/// <summary>
/// Ẩn nhóm line và circle.
/// </summary>
public void Hide()
{
_visibility = "hidden";
StateHasChanged();
}
/// <summary>
/// Đặt tọa độ robot (world coordinates).
/// </summary>
public void SetRobotPosition(double x, double y)
{
_robotX = x;
_robotY = y;
StateHasChanged();
}
/// <summary>
/// Đặt tọa độ điểm đích (world coordinates).
/// </summary>
public void SetGoalPosition(double x, double y)
{
_goalX = x;
_goalY = y;
StateHasChanged();
}
/// <summary>
/// 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.
/// </summary>
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;
/// <summary>
/// Nếu khoảng cách từ (GoalX, GoalY) đến (mouseX, mouseY) &lt; 1 m thì gọi Hide().
/// Nếu khoảng cách &gt;= 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).
/// </summary>
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();
}
}