122 lines
4.8 KiB
Plaintext
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) < 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).
|
|
/// </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();
|
|
}
|
|
}
|