Files
BQP/srcs/RobotNet10/FleetManager/RobotNet10.FleetManager.Client/Components/RobotModelManager/RobotModelImagePreview.razor
2026-07-13 09:25:40 +07:00

163 lines
6.6 KiB
Plaintext

@*
Component: RobotModelImagePreview
Purpose: Displays robot model image with navigation point overlay visualization.
The navigation point is shown as arrows (X+ in red, Y+ in green) and a blue marker.
Uses SVG viewBox to automatically scale overlay to match displayed image size.
*@
@using RobotNet10.FleetManager.Shared.DTOs.RobotModel
@using RobotNet10.FleetManager.Client.Services
@inject RobotModelApiService ApiService
@if (imageBase64 != null)
{
<div style="position: relative; width: 100%; max-width: 100%; max-height: 250px; overflow: hidden; display: flex; align-items: center; justify-content: center; border-radius: 4px;">
<div style="position: relative; display: inline-block; max-width: 100%; max-height: 250px;">
<img @ref="imageElement"
src="data:image/png;base64,@imageBase64"
alt="Robot Model Image"
style="max-width: 100%; max-height: 250px; height: auto; width: auto; display: block; object-fit: contain;"
@onload="OnImageLoaded" />
<!-- SVG Overlay for Navigation Point -->
@if (RobotModel.ImageWidth > 0 && RobotModel.ImageHeight > 0)
{
<svg style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none;"
viewBox="0 0 @RobotModel.ImageWidth @RobotModel.ImageHeight"
preserveAspectRatio="xMidYMid meet">
<!-- X-axis arrow (right) - Red arrow pointing right (X+) -->
<line x1="@svgX"
y1="@svgY"
x2="@(svgX + arrowLength)"
y2="@svgY"
stroke="red"
stroke-width="5"
marker-end="url(#arrowhead-x-@uniqueId)" />
<!-- Y-axis arrow (up) - Green arrow pointing up (Y+) -->
<line x1="@svgX"
y1="@svgY"
x2="@svgX"
y2="@(svgY - arrowLength)"
stroke="green"
stroke-width="5"
marker-end="url(#arrowhead-y-@uniqueId)" />
<!-- Navigation point marker -->
<circle cx="@svgX"
cy="@svgY"
r="10"
fill="blue"
stroke="white"
stroke-width="2" />
<!-- Arrow markers definition -->
<defs>
<marker id="arrowhead-x-@uniqueId"
markerWidth="10"
markerHeight="10"
refX="9"
refY="3"
orient="auto"
markerUnits="strokeWidth">
<polygon points="0 0, 10 3, 0 6" fill="red" />
</marker>
<marker id="arrowhead-y-@uniqueId"
markerWidth="10"
markerHeight="10" efX="9"
refY="3"
orient="auto"
markerUnits="strokeWidth">
<polygon points="0 0, 10 3, 0 6" fill="green" />
</marker>
</defs>
</svg>
}
</div>
</div>
}
else
{
<MudAlert Severity="Severity.Info">No image available</MudAlert>
}
@code {
[Parameter]
public RobotModelDto RobotModel { get; set; } = null!;
private string? imageBase64;
private double svgX;
private double svgY;
private double arrowLength;
private ElementReference imageElement;
private string uniqueId = Guid.NewGuid().ToString("N")[..8]; // Unique ID for SVG markers
protected override async Task OnInitializedAsync()
{
await LoadImageAsync();
}
protected override async Task OnParametersSetAsync()
{
await LoadImageAsync();
}
private void OnImageLoaded()
{
CalculateNavigationPoint();
}
private void CalculateNavigationPoint()
{
if (RobotModel.ImageWidth > 0 && RobotModel.ImageHeight > 0 && RobotModel.Length > 0 && RobotModel.Width > 0)
{
// Calculate scale: pixels per meter in original image
// This converts from robot coordinate system (meters) to image coordinate system (pixels)
var scaleX = RobotModel.ImageWidth / RobotModel.Length;
var scaleY = RobotModel.ImageHeight / RobotModel.Width;
// Navigation point coordinates are relative to the bottom-left corner of the image (in meters)
// Image coordinate system: (0,0) is at bottom-left corner
// X-axis: left to right (0 to ImageWidth)
// Y-axis: bottom to top (0 to ImageHeight in robot coords, but SVG Y increases downward)
// Convert navigation point from meters to pixels
// NavigationPointX: distance from left edge (in meters)
// NavigationPointY: distance from bottom edge (in meters)
var navX = RobotModel.NavigationPointX * scaleX;
var navY = RobotModel.NavigationPointY * scaleY;
// Final position in SVG coordinates (using original image coordinate system)
// SVG viewBox will automatically scale to match the displayed image size
// X: from left edge (0 is left, ImageWidth is right)
svgX = navX;
// Y: from bottom edge (0 is bottom in robot coords, but SVG Y=0 is top, Y=ImageHeight is bottom)
// So we need to invert: svgY = ImageHeight - navY
svgY = RobotModel.ImageHeight - navY;
// Arrow length: 15% of the smaller dimension for better visibility
arrowLength = Math.Min(RobotModel.ImageWidth, RobotModel.ImageHeight) * 0.15;
StateHasChanged();
}
}
private async Task LoadImageAsync()
{
try
{
imageBase64 = await ApiService.GetImageAsync(RobotModel.Id);
if (imageBase64 != null)
{
// Calculate navigation point when image loads
CalculateNavigationPoint();
}
}
catch (Exception)
{
imageBase64 = null;
}
}
}