163 lines
6.6 KiB
Plaintext
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;
|
|
}
|
|
}
|
|
}
|