diff --git a/RobotApp.Client/Pages/Components/Mapping/MapView.razor b/RobotApp.Client/Pages/Components/Mapping/MapView.razor
index 5900caf..df2d737 100644
--- a/RobotApp.Client/Pages/Components/Mapping/MapView.razor
+++ b/RobotApp.Client/Pages/Components/Mapping/MapView.razor
@@ -18,39 +18,13 @@
-
-
-
-
-
- Zoom: @($"{ZoomScale:F2}x")
-
- |
-
-
- Mouse: (@($"{MouseX:F0}"), @($"{MouseY:F0}"))
-
- |
-
-
- World: (@($"{WorldMouseX:F2}m"), @($"{WorldMouseY:F2}m"))
-
- |
-
-
- Translate: (@($"{CanvasTranslateX:F2}"), @($"{CanvasTranslateY:F2}"))
-
-
-
-
-
+
+ @onmouseleave="HandleMouseLeave">
@@ -106,18 +80,96 @@
await ctx.ClearRectAsync(0, 0, CanvasWidth, CanvasHeight);
- // Draw rulers first (outside transform)
await DrawRulers(ctx);
await ctx.SaveAsync();
await ctx.TranslateAsync(CanvasTranslateX, CanvasTranslateY);
await ctx.ScaleAsync(ZoomScale, ZoomScale);
-
await DrawGrid(ctx);
await DrawAxes(ctx);
await ctx.RestoreAsync();
+
+ // Draw mouse indicator (outside transform)
+ if (IsMouseInCanvas)
+ {
+ await DrawMouseIndicator(ctx);
+ }
+ }
+
+ private async Task DrawMouseIndicator(Context2D ctx)
+ {
+ await ctx.SaveAsync();
+
+ await ctx.StrokeStyleAsync("rgba(255, 50, 50, 0.8)");
+ await ctx.LineWidthAsync(1);
+ await ctx.SetLineDashAsync(new double[] { 3, 3 });
+
+ await ctx.BeginPathAsync();
+ await ctx.MoveToAsync(MouseX, RulerHeight);
+ await ctx.LineToAsync(MouseX, CanvasHeight);
+ await ctx.StrokeAsync();
+
+ await ctx.BeginPathAsync();
+ await ctx.MoveToAsync(RulerHeight, MouseY);
+ await ctx.LineToAsync(CanvasWidth, MouseY);
+ await ctx.StrokeAsync();
+
+ await ctx.SetLineDashAsync(new double[] { });
+
+ const double labelPadding = 7;
+ const double labelMargin = 8;
+
+ string coordinateText = $"({WorldMouseX:F2}m, {WorldMouseY:F2}m)";
+
+ await ctx.FontAsync("bold 12px Arial");
+ var textMetrics = await ctx.MeasureTextAsync(coordinateText);
+ double textWidth = textMetrics.Width;
+ double textHeight = 16;
+
+ double labelX = MouseX + labelMargin;
+ double labelY = MouseY - textHeight - labelPadding * 2 - labelMargin;
+
+ if (labelX + textWidth + labelPadding * 2 > CanvasWidth)
+ {
+ labelX = MouseX - textWidth - labelPadding * 2 - labelMargin;
+ }
+ if (labelY - textHeight - labelPadding * 2 < RulerHeight)
+ {
+ labelY = MouseY + labelMargin;
+ }
+
+ await ctx.FillStyleAsync("rgba(0, 0, 0, 0.8)");
+ await ctx.FillRectAsync(labelX, labelY, textWidth + labelPadding * 2, textHeight + labelPadding * 2);
+
+ await ctx.StrokeStyleAsync("rgba(255, 255, 255, 0.6)");
+ await ctx.LineWidthAsync(1);
+ await ctx.StrokeRectAsync(labelX, labelY, textWidth + labelPadding * 2, textHeight + labelPadding * 2);
+
+ await ctx.FillStyleAsync("rgba(255, 50, 50, 0.9)");
+ await ctx.BeginPathAsync();
+ await ctx.ArcAsync(MouseX, MouseY, 3, 0, Math.PI * 2);
+ await ctx.FillAsync(FillRule.NonZero);
+
+ await ctx.StrokeStyleAsync("rgba(255, 255, 255, 0.8)");
+ await ctx.LineWidthAsync(2);
+ await ctx.BeginPathAsync();
+ await ctx.ArcAsync(MouseX, MouseY, 6, 0, Math.PI * 2);
+ await ctx.StrokeAsync();
+
+ await ctx.SaveAsync();
+
+ await ctx.TranslateAsync(labelX + labelPadding + textWidth / 2, labelY + textHeight / 2);
+ await ctx.ScaleAsync(1, -1);
+
+ await ctx.FillStyleAsync("white");
+ await ctx.FontAsync("bold 12px Arial");
+ await ctx.TextAlignAsync(TextAlign.Center);
+ await ctx.TextBaseLineAsync(TextBaseLine.Bottom);
+ await ctx.FillTextAsync(coordinateText, 0, 0);
+
+ await ctx.RestoreAsync();
}
private async Task DrawRulers(Context2D ctx)
@@ -137,14 +189,14 @@
{
double pixelsPerMeter = BASE_PIXELS_PER_METER * ZoomScale;
- if (pixelsPerMeter >= 400) return 0.1;
+ if (pixelsPerMeter >= 400) return 0.1;
else if (pixelsPerMeter >= 200) return 0.2;
else if (pixelsPerMeter >= 100) return 0.5;
else if (pixelsPerMeter >= 50) return 1.0;
else if (pixelsPerMeter >= 25) return 2.0;
else if (pixelsPerMeter >= 12) return 5.0;
else if (pixelsPerMeter >= 6) return 10.0;
- else return 20.0;
+ else return 20.0;
}
private async Task DrawXRuler(Context2D ctx, double rulerHeight, double visibleWorldLeft, double visibleWorldRight, double scaleInterval)
@@ -184,17 +236,17 @@
if (isMajorTick && canvasX >= -20 && canvasX <= CanvasWidth + 20)
{
await ctx.SaveAsync();
-
+
await ctx.TranslateAsync(canvasX, rulerHeight - tickHeight - 8);
- await ctx.ScaleAsync(1, -1);
-
+ await ctx.ScaleAsync(1, -1);
+
await ctx.FillStyleAsync("blue");
await ctx.FontAsync("bold 10px Arial");
await ctx.TextAlignAsync(TextAlign.Center);
string labelText = FormatRulerLabel(worldX, scaleInterval);
await ctx.FillTextAsync(labelText, 0, 0);
-
+
await ctx.RestoreAsync();
}
}
@@ -240,9 +292,9 @@
await ctx.TranslateAsync(rulerWidth - tickWidth - 2, canvasY);
await ctx.ScaleAsync(1, -1);
- await ctx.RotateAsync(-Math.PI / 2);
+ await ctx.RotateAsync(-Math.PI / 2);
- await ctx.FillStyleAsync("blue");
+ await ctx.FillStyleAsync("blue");
await ctx.FontAsync("bold 10px Arial");
await ctx.TextAlignAsync(TextAlign.Center);
@@ -264,18 +316,7 @@
private string FormatRulerLabel(double worldValue, double scaleInterval)
{
- if (scaleInterval < 1.0)
- {
- return $"{worldValue:F1}m";
- }
- else if (scaleInterval < 10.0)
- {
- return $"{worldValue:F0}m";
- }
- else
- {
- return $"{worldValue:F0}m";
- }
+ return scaleInterval < 1.0 ? $"{worldValue:F1}m" : $"{worldValue:F0}m";
}
private async Task DrawAxes(Context2D ctx)
@@ -422,8 +463,8 @@
{
CanvasTranslateX += e.MovementX;
CanvasTranslateY -= e.MovementY;
- await DrawCanvas();
}
+ await DrawCanvas();
}
private async Task HandleMouseLeave(MouseEventArgs e)
diff --git a/RobotApp.Client/Pages/Components/Mapping/MapView.razor.css b/RobotApp.Client/Pages/Components/Mapping/MapView.razor.css
index 2d9e089..deafb45 100644
--- a/RobotApp.Client/Pages/Components/Mapping/MapView.razor.css
+++ b/RobotApp.Client/Pages/Components/Mapping/MapView.razor.css
@@ -30,144 +30,41 @@
background-color: var(--bs-gray-200);
}
- .view .toolbar .action-button:hover {
- background-color: var(--mud-palette-action-hover);
- transform: translateY(-1px);
- }
+ .view .toolbar .action-button:hover {
+ background-color: var(--mud-palette-action-hover);
+ transform: translateY(-1px);
+ }
- .view .toolbar .action-button:disabled {
- opacity: 0.6;
- cursor: not-allowed;
- }
+ .view .toolbar .action-button:disabled {
+ opacity: 0.6;
+ cursor: not-allowed;
+ }
- .view .toolbar .action-button:disabled:hover {
- transform: none;
- background-color: transparent;
- }
+ .view .toolbar .action-button:disabled:hover {
+ transform: none;
+ background-color: transparent;
+ }
.view .toolbar .icon-button {
font-size: 1.2rem;
color: var(--mud-palette-primary);
}
- .view canvas {
- transition: cursor 0.2s ease;
+
+ .view > div {
+ display: flex;
+ position: relative;
+ width: 100%;
+ flex-grow: 1;
+ overflow: hidden;
}
- .view canvas:hover {
- cursor: crosshair;
+ .view > div > canvas {
+ transition: cursor 0.2s ease;
+ display: block;
+ transform: scale(1, -1);
}
-/* Enhanced zoom and coordinate info styling */
-.zoom-info-container {
- display: flex;
- align-items: center;
- background-color: var(--mud-palette-background);
- border: 1px solid var(--mud-palette-divider);
- border-radius: 6px;
- padding: 0.5rem 0.75rem;
- box-shadow: 0 2px 4px rgba(0,0,0,0.1);
-}
-
-.zoom-info {
- font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
- font-size: 0.8rem;
- color: var(--mud-palette-text-primary);
- display: flex;
- align-items: center;
- gap: 0.5rem;
- white-space: nowrap;
-}
-
-.info-item {
- display: flex;
- align-items: center;
- gap: 0.25rem;
- font-weight: 500;
-}
-
- .info-item i {
- font-size: 0.9rem;
- color: var(--mud-palette-primary);
- }
-
-.info-separator {
- color: var(--mud-palette-divider);
- font-weight: 300;
- margin: 0 0.25rem;
-}
-
-/* Responsive design for info container */
-@media (max-width: 768px) {
- .zoom-info-container {
- padding: 0.25rem 0.5rem;
- }
-
- .zoom-info {
- font-size: 0.7rem;
- gap: 0.25rem;
- }
-
- .info-item {
- gap: 0.15rem;
- }
-
- .info-item i {
- font-size: 0.8rem;
- }
-}
-
-/* Very small screens - stack vertically */
-@media (max-width: 480px) {
- .zoom-info {
- flex-direction: column;
- gap: 0.15rem;
- text-align: center;
- }
-
- .info-separator {
- display: none;
- }
-
- .zoom-info-container {
- padding: 0.35rem;
- }
-}
-
-/* Hover effects for info container */
-.zoom-info-container:hover {
- background-color: var(--mud-palette-action-hover);
- box-shadow: 0 4px 8px rgba(0,0,0,0.15);
-}
-
-/* Animation for coordinate updates */
-.info-item {
- transition: color 0.2s ease;
-}
-
-.info-item:hover {
- color: var(--mud-palette-primary);
-}
-
-/* High contrast mode support */
-@media (prefers-contrast: high) {
- .zoom-info-container {
- border-width: 2px;
- background-color: var(--mud-palette-surface);
- }
-
- .info-item {
- font-weight: 600;
- }
-}
-
-/* Dark mode adjustments */
-@media (prefers-color-scheme: dark) {
- .zoom-info-container {
- box-shadow: 0 2px 4px rgba(0,0,0,0.3);
- }
-
- .zoom-info-container:hover {
- box-shadow: 0 4px 8px rgba(0,0,0,0.4);
- }
-}
+ .view > div > canvas:hover {
+ cursor: crosshair;
+ }
diff --git a/RobotApp/Controllers/ImagesController.cs b/RobotApp/Controllers/ImagesController.cs
index 875c9ca..05c2a21 100644
--- a/RobotApp/Controllers/ImagesController.cs
+++ b/RobotApp/Controllers/ImagesController.cs
@@ -1,10 +1,11 @@
-using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
-namespace RobotApp.Controllers
+namespace RobotApp.Controllers;
+
+[Route("api/[controller]")]
+[ApiController]
+[AllowAnonymous]
+public class ImagesController : ControllerBase
{
- [Route("api/[controller]")]
- [ApiController]
- public class ImagesController : ControllerBase
- {
- }
}
diff --git a/RobotApp/robot.db b/RobotApp/robot.db
index d2792ed..6249b2f 100644
Binary files a/RobotApp/robot.db and b/RobotApp/robot.db differ