RobotNet/RobotNet.WebApp/Maps/Components/Editor/MapContainer.razor.cs
2025-10-15 15:15:53 +07:00

1093 lines
45 KiB
C#

using MudBlazor;
using RobotNet.MapShares;
using RobotNet.MapShares.Dtos;
using RobotNet.MapShares.Enums;
using RobotNet.MapShares.Models;
using RobotNet.Shares;
using RobotNet.WebApp.Components;
using RobotNet.WebApp.Maps.Models;
using System.Net.Http.Json;
namespace RobotNet.WebApp.Maps.Components.Editor;
public class MergeNodeBackup
{
public Guid NodeId { get; set; }
public List<(Guid edgeId, NodeModel node)> EdgesMerge { get; set; } = [];
}
public partial class MapContainer
{
public readonly List<MapEditorBackup> EditorBackup = [];
public async Task CreateEdge(double x1, double y1, double x2, double y2, TrajectoryDegree degree = TrajectoryDegree.One, double cpX1 = 0, double cpY1 = 0, double cpX2 = 0, double cpY2 = 0)
{
if (MapData is null) { Snackbar.Add("Dữ liệu bản đồ rỗng", Severity.Warning); return; }
if (MapData.Active) { Snackbar.Add("Không thể tạo edge do bản đồ đang được Active", Severity.Warning); return; }
var result = await (await Http.PostAsJsonAsync($"api/Edges", new EdgeCreateModel()
{
MapId = MapData.Id,
X1 = x1,
Y1 = y1,
X2 = x2,
Y2 = y2,
TrajectoryDegree = degree,
ControlPoint1X = cpX1,
ControlPoint1Y = cpY1,
ControlPoint2X = cpX2,
ControlPoint2Y = cpY2,
})).Content.ReadFromJsonAsync<MessageResult<EdgeCreateDto>>();
if (result is null) { Snackbar.Add("Lỗi giao tiếp với hệ thống", Severity.Error); return; }
else if (!result.IsSuccess) { Snackbar.Add(result.Message ?? "Tạo Edge không thành công", Severity.Error); return; }
else if (result.Data == null || !result.Data.EdgesDto.Any() || result.Data.EdgesDto.Any(e => e.StartNode is null || e.EndNode is null))
{
Snackbar.Add("Lỗi dữ liệu trả về", Severity.Error);
return;
}
foreach (var edge in result.Data.EdgesDto)
{
if (edge.StartNode is null || edge.EndNode is null) continue;
if (!Nodes.ContainsKey(edge.StartNodeId)) Nodes.Add(edge.StartNode);
if (!Nodes.ContainsKey(edge.EndNodeId)) Nodes.Add(edge.EndNode);
Edges.Add(edge, Nodes[edge.StartNodeId], Nodes[edge.EndNodeId]);
}
foreach (var removeEdgeId in result.Data.RemoveEdge)
{
var removeEdge = Edges.FirstOrDefault(e => e.Id == removeEdgeId);
if (removeEdge is not null)
{
Edges.Remove(removeEdge);
var edgeEditSteps = EditorBackup.Where(step => (step.Type == MapEditorBackupType.ControlPoint1Edge || step.Type == MapEditorBackupType.ControlPoint2Edge) && step.Id == removeEdge.Id);
}
}
Snackbar.Add("Tạo Edge mới thành công", Severity.Success);
}
public async Task DeleteEdge()
{
if (EditorBackup.Count > 0)
{
if (!await SaveChanged()) return;
}
if (Edges.ActivedEdges.Count == 0) return;
if (MapData is null) { Snackbar.Add("Dữ liệu bản đồ rỗng", Severity.Warning); return; }
if (MapData.Active) { Snackbar.Add("Không thể xóa edge do bản đồ đang được Active", Severity.Warning); return; }
var parameters = new DialogParameters<ConfirmDialog>
{
{ x => x.Content, "Bạn chắc chắn muốn xóa edge đi không?" },
{ x => x.ConfirmText, "Delete" },
{ x => x.Color, Color.Secondary }
};
var Confirm = await Dialog.ShowAsync<ConfirmDialog>("Xoá Edge", parameters);
var ConfirmResult = await Confirm.Result;
if (ConfirmResult is not null && ConfirmResult.Data is not null && bool.TryParse(ConfirmResult.Data.ToString(), out bool data) && data)
{
OverlayIsVisible = true;
StateHasChanged();
var deleteListEdgeId = Edges.ActivedEdges.Select(x => x.Id).ToList();
HttpRequestMessage request = new()
{
Content = JsonContent.Create(deleteListEdgeId),
Method = HttpMethod.Delete,
RequestUri = new Uri($"{Http.BaseAddress}api/Edges")
};
var response = await Http.SendAsync(request);
var result = await response.Content.ReadFromJsonAsync<MessageResult>();
if (result is null) { Snackbar.Add("Lỗi giao tiếp với hệ thống", Severity.Error); return; }
else if (!result.IsSuccess) { Snackbar.Add(result.Message ?? "Xóa Edge không thành công", Severity.Error); return; }
if (Edges.ActivedEdges.Count < 20)
{
List<EdgeModel> ActivedEdgeCopy = [.. Edges.ActivedEdges];
Nodes.UnActivedNode();
Edges.UnActivedEdge();
foreach (var edge in ActivedEdgeCopy)
{
var removeEdge = Edges.FirstOrDefault(e => e.Id == edge.Id);
if (removeEdge == null) continue;
Edges.Remove(removeEdge);
var edgeEditSteps = EditorBackup.Where(step => (step.Type == MapEditorBackupType.ControlPoint1Edge || step.Type == MapEditorBackupType.ControlPoint2Edge) && step.Id == removeEdge.Id);
for (int i = 0; i < edgeEditSteps.Count(); i++) EditorBackup.Remove(edgeEditSteps.ElementAt(i));
if (removeEdge.StartNode.NumberOfEdgeReference <= 1)
{
Nodes.Remove(removeEdge.StartNode);
var element = Elements.FirstOrDefault(el => el.NodeId == removeEdge.StartNode.Id);
if (element is not null) Elements.Remove(element);
var nodeEditSteps = EditorBackup.Where(step => step.Type == MapEditorBackupType.Node && step.Id == removeEdge.StartNode.Id);
for (int i = 0; i < nodeEditSteps.Count(); i++) EditorBackup.Remove(nodeEditSteps.ElementAt(i));
}
if (removeEdge.EndNode.NumberOfEdgeReference <= 1)
{
Nodes.Remove(removeEdge.EndNode);
var element = Elements.FirstOrDefault(el => el.NodeId == removeEdge.EndNode.Id);
if (element is not null) Elements.Remove(element);
var nodeEditSteps = EditorBackup.Where(step => step.Type == MapEditorBackupType.Node && step.Id == removeEdge.EndNode.Id);
for (int i = 0; i < nodeEditSteps.Count(); i++) EditorBackup.Remove(nodeEditSteps.ElementAt(i));
}
}
await OnSelectedEdgeChanged(null);
await MultiselectedEdgeChanged.InvokeAsync(false);
await MultiselectedNodeChanged.InvokeAsync(false);
Snackbar.Add("Xóa Edge thành công", Severity.Success);
OverlayIsVisible = false;
}
else await LoadMap();
StateHasChanged();
}
}
public async Task CreateZone(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4, ZoneType type)
{
if (MapData is null) { Snackbar.Add("Dữ liệu bản đồ rỗng", Severity.Warning); return; }
if (MapData.Active) { Snackbar.Add("Không thể tạo edge do bản đồ đang được Active", Severity.Warning); return; }
var result = await (await Http.PostAsJsonAsync($"api/Zones", new ZoneCreateModel()
{
MapId = MapData.Id,
X1 = x1,
Y1 = y1,
X2 = x2,
Y2 = y2,
X3 = x3,
Y3 = y3,
X4 = x4,
Y4 = y4,
Type = type
})).Content.ReadFromJsonAsync<MessageResult<ZoneDto>>();
if (result is null) { Snackbar.Add("Lỗi giao tiếp với hệ thống", Severity.Error); return; }
else if (!result.IsSuccess) { Snackbar.Add(result.Message ?? "Tạo Zone không thành công", Severity.Error); return; }
else if (result.Data is null) { Snackbar.Add("Lỗi dữ liệu trả về", Severity.Error); return; }
Zones.Add(result.Data);
Snackbar.Add("Tạo Zone mới thành công", Severity.Success);
}
public async Task DeleteZone()
{
if (EditorBackup.Count > 0)
{
if (!await SaveChanged()) return;
}
if (Zones.ZoneActived is null) return;
if (MapData is null) { Snackbar.Add("Dữ liệu bản đồ rỗng", Severity.Warning); return; }
if (MapData.Active) { Snackbar.Add("Không thể tạo edge do bản đồ đang được Active", Severity.Warning); return; }
var parameters = new DialogParameters<ConfirmDialog>
{
{ x => x.Content, "Bạn chắc chắn muốn xóa Zone đi không?" },
{ x => x.ConfirmText, "Delete" },
{ x => x.Color, Color.Secondary }
};
var Confirm = await Dialog.ShowAsync<ConfirmDialog>("Xoá Zone", parameters);
var ConfirmResult = await Confirm.Result;
if (ConfirmResult is not null && ConfirmResult.Data is not null && bool.TryParse(ConfirmResult.Data.ToString(), out bool data) && data)
{
var result = await Http.DeleteFromJsonAsync<MessageResult>($"/api/zones/{Zones.ZoneActived.Id}");
if (result is null) { Snackbar.Add("Lỗi giao tiếp với hệ thống", Severity.Error); return; }
else if (!result.IsSuccess) { Snackbar.Add(result.Message ?? "Xoá Zone không thành công", Severity.Error); return; }
var removeZone = Zones.ZoneActived;
Zones.Remove(removeZone);
var zoneEditSteps = EditorBackup.Where(step => step.Id == removeZone.Id);
for (int i = 0; i < zoneEditSteps.Count(); i++) EditorBackup.Remove(zoneEditSteps.ElementAt(i));
ZoneControlPointRef.SetControl(null);
Snackbar.Add("Xoá Zone thành công", Severity.Success);
}
}
public async Task<bool> SaveChanged()
{
if (EditorBackup.Count == 0) return true;
if (MapData is null) { Snackbar.Add("Dữ liệu bản đồ rỗng", Severity.Warning); return false; }
if (MapData.Active) { Snackbar.Add("Không thể tạo edge do bản đồ đang được Active", Severity.Warning); return false; }
var parameters = new DialogParameters<ConfirmDialog>
{
{ x => x.Content, "Có dữ liệu thay đổi, bạn có muốn lưu lại không?" },
{ x => x.ConfirmText, "Save" },
{ x => x.Color, Color.Primary }
};
var Confirm = await Dialog.ShowAsync<ConfirmDialog>("Lưu", parameters);
var ConfirmResult = await Confirm.Result;
if (ConfirmResult is not null && ConfirmResult.Data is not null && bool.TryParse(ConfirmResult.Data.ToString(), out bool data) && data)
{
var editBackupSteps = new List<MapEditorBackup>();
foreach (var backup in EditorBackup)
{
switch (backup.Type)
{
case MapEditorBackupType.Node:
if (Nodes.TryGetValue(backup.Id, out var node) && node != null)
{
editBackupSteps.Add(new()
{
Type = MapEditorBackupType.Node,
Id = node.Id,
Obj = new PositionBackup()
{
Id = node.Id,
X = node.X,
Y = node.Y,
}
});
}
break;
case MapEditorBackupType.ControlPoint1Edge:
if (Edges.TryGetValue(backup.Id, out var edge1) && edge1 is not null)
{
editBackupSteps.Add(new()
{
Type = MapEditorBackupType.ControlPoint1Edge,
Id = edge1.Id,
Obj = new PositionBackup()
{
Id = edge1.Id,
X = edge1.ControlPoint1X,
Y = edge1.ControlPoint1Y,
}
});
}
break;
case MapEditorBackupType.ControlPoint2Edge:
if (Edges.TryGetValue(backup.Id, out var edge2) && edge2 is not null)
{
editBackupSteps.Add(new()
{
Type = MapEditorBackupType.ControlPoint2Edge,
Id = edge2.Id,
Obj = new PositionBackup()
{
Id = edge2.Id,
X = edge2.ControlPoint2X,
Y = edge2.ControlPoint2Y,
}
});
}
break;
case MapEditorBackupType.Zone:
if (Zones.TryGetValue(backup.Id, out var zone) && zone is not null)
{
editBackupSteps.Add(new()
{
Type = MapEditorBackupType.Zone,
Id = zone.Id,
Obj = new ZoneShapeBackup()
{
X1 = zone.X1,
Y1 = zone.Y1,
X2 = zone.X2,
Y2 = zone.Y2,
X3 = zone.X3,
Y3 = zone.Y3,
X4 = zone.X4,
Y4 = zone.Y4,
}
});
}
break;
case MapEditorBackupType.MoveNode:
if (backup.Obj is List<PositionBackup> nodesChange)
{
foreach (var nodeChange in nodesChange)
{
if (Nodes.TryGetValue(nodeChange.Id, out var nodemove) && nodemove != null)
{
editBackupSteps.Add(new()
{
Type = MapEditorBackupType.Node,
Id = nodemove.Id,
Obj = new PositionBackup()
{
Id = nodemove.Id,
X = nodemove.X,
Y = nodemove.Y,
}
});
}
}
}
break;
case MapEditorBackupType.MoveEdge:
List<EdgeBackup> saveEdge = [];
if (backup.Obj is List<EdgeBackup> edgesBackup)
{
foreach (var edgeChange in edgesBackup)
{
if (Edges.TryGetValue(edgeChange.Id, out var edge) && edge != null)
{
saveEdge.Add(new()
{
Id = edge.Id,
TrajectoryDegree = edge.TrajectoryDegree,
StartX = edge.X1,
StartY = edge.Y1,
EndX = edge.X2,
EndY = edge.Y2,
ControlPoint1X = edge.ControlPoint1X,
ControlPoint1Y = edge.ControlPoint1Y,
ControlPoint2X = edge.ControlPoint2X,
ControlPoint2Y = edge.ControlPoint2Y
});
}
}
}
editBackupSteps.Add(new()
{
Type = MapEditorBackupType.MoveEdge,
Obj = saveEdge,
});
break;
case MapEditorBackupType.Copy:
if (MapCopyRef.isMoving)
{
List<EdgeMapCopyModel> edgeCopyModel = [.. MapCopyRef.EdgeModels.Select(e => new EdgeMapCopyModel()
{
X1 = e.X1,
Y1 = e.Y1,
X2 = e.X2,
Y2 = e.Y2,
MapId = e.MapId,
TrajectoryDegree = e.TrajectoryDegree,
StartNodeId = e.StartNode.Id,
EndNodeId = e.EndNode.Id,
ControlPoint1X = e.ControlPoint1X,
ControlPoint1Y = e.ControlPoint1Y,
ControlPoint2X = e.ControlPoint2X,
ControlPoint2Y = e.ControlPoint2Y,
Actions = e.Actions,
AllowedDeviationTheta = e.AllowedDeviationTheta,
AllowedDeviationXy = e.AllowedDeviationXy,
DirectionAllowed = e.DirectionAllowed,
RotationAllowed = e.RotationAllowed,
MaxHeight = e.MaxHeight,
MinHeight = e.MinHeight,
MaxRotationSpeed = e.MaxRotationSpeed,
MaxSpeed = e.MaxSpeed,
})];
editBackupSteps.Add(new()
{
Type = MapEditorBackupType.Copy,
Obj = edgeCopyModel,
});
}
else MapCopyRef.OnShow(false, []);
break;
case MapEditorBackupType.SplitNode:
editBackupSteps.Add(backup);
break;
case MapEditorBackupType.MergeNode:
if (backup.Obj is MergeNodeBackup mergeBackup)
{
Dictionary<Guid, Guid> edgeMerge = [];
mergeBackup.EdgesMerge.ForEach(e => edgeMerge.Add(e.edgeId, e.node.Id));
editBackupSteps.Add(new()
{
Type = MapEditorBackupType.MergeNode,
Id = mergeBackup.NodeId,
Obj = new MergeNodeUpdate()
{
NodeId = mergeBackup.NodeId,
EdgesMerge = edgeMerge,
}
});
}
break;
}
}
var result = await (await Http.PutAsJsonAsync($"api/MapsData/{MapData.Id}/updates", new MapEditorBackupModel() { Steps = [.. editBackupSteps], })).Content.ReadFromJsonAsync<MessageResult<IEnumerable<EdgeDto>>>();
if (result is null) { Snackbar.Add("Lỗi giao tiếp với hệ thống", Severity.Error); return false; }
else if (!result.IsSuccess) { Snackbar.Add(result.Message ?? "Tạo Edge không thành công", Severity.Error); return false; }
else if (result.Data is null) { Snackbar.Add("Lỗi dữ liệu trả về", Severity.Error); return false; }
else if (result.Data.Any())
{
foreach (var edge in result.Data)
{
if (edge.StartNode is null || edge.EndNode is null) continue;
if (!Nodes.ContainsKey(edge.StartNodeId)) Nodes.Add(edge.StartNode);
if (!Nodes.ContainsKey(edge.EndNodeId)) Nodes.Add(edge.EndNode);
Edges.Add(edge, Nodes[edge.StartNodeId], Nodes[edge.EndNodeId]);
}
MapCopyRef.OnShow(false, []);
}
EditorBackup.Clear();
await NodesUndoableChanged.InvokeAsync(false);
Snackbar.Add("Cập nhật thành công", Severity.Success);
return true;
}
return false;
}
public void UndoEditorBackup()
{
if (EditorBackup.Count == 0 || MapData.Active) return;
var lastChange = EditorBackup[^1];
EditorBackup.Remove(lastChange);
switch (lastChange.Type)
{
case MapEditorBackupType.Node:
if (lastChange.Obj is PositionBackup nodepos)
{
if (Nodes.TryGetValue(lastChange.Id, out var node) && node != null)
{
node.UpdatePosition(nodepos.X, nodepos.Y);
}
}
break;
case MapEditorBackupType.ControlPoint1Edge:
if (lastChange.Obj is PositionBackup cppos)
{
if (Edges.TryGetValue(lastChange.Id, out var edge) && edge != null)
{
edge.UpdateControlPoint1(cppos.X, cppos.Y);
}
}
break;
case MapEditorBackupType.ControlPoint2Edge:
if (lastChange.Obj is PositionBackup cp2pos)
{
if (Edges.TryGetValue(lastChange.Id, out var edge) && edge != null)
{
edge.UpdateControlPoint2(cp2pos.X, cp2pos.Y);
}
}
break;
case MapEditorBackupType.Zone:
if (lastChange.Obj is ZoneShapeBackup zoneshape)
{
if (Zones.TryGetValue(lastChange.Id, out var zone) && zone is not null)
{
zone.UpdateControlNode(zoneshape.X1, zoneshape.Y1, zoneshape.X2, zoneshape.Y2, zoneshape.X3, zoneshape.Y3, zoneshape.X4, zoneshape.Y4);
}
}
break;
case MapEditorBackupType.MoveNode:
if (lastChange.Obj is List<PositionBackup> nodesbackup)
{
foreach (var node in nodesbackup)
{
if (Nodes.TryGetValue(node.Id, out var model) && model != null)
{
model.UpdatePosition(node.X, node.Y);
}
}
}
break;
case MapEditorBackupType.MoveEdge:
if (lastChange.Obj is List<EdgeBackup> edgesBackup)
{
foreach (var edge in edgesBackup)
{
if (Edges.TryGetValue(edge.Id, out var model) && model != null)
{
model.StartNode.UpdatePosition(edge.StartX, edge.StartY);
model.EndNode.UpdatePosition(edge.EndX, edge.EndY);
if (model.TrajectoryDegree == TrajectoryDegree.Two || model.TrajectoryDegree == TrajectoryDegree.Three) model.UpdateControlPoint1(edge.ControlPoint1X, edge.ControlPoint1Y);
if (model.TrajectoryDegree == TrajectoryDegree.Three) model.UpdateControlPoint2(edge.ControlPoint2X, edge.ControlPoint2Y);
}
}
}
break;
case MapEditorBackupType.Copy:
MapCopyRef.OnShow(false, []);
break;
case MapEditorBackupType.SplitNode:
if (lastChange.Obj is SplitNodeBackup splitBackup)
{
var node = Nodes[splitBackup.NodeId];
if (node is not null)
{
foreach (var data in splitBackup.EdgeSplit)
{
var edge = Edges[data.Key];
if (edge is not null && (edge.StartNode.Id == data.Value.Id || edge.EndNode.Id == data.Value.Id))
{
edge.UpdateNode(data.Value.Id, node);
Nodes.Remove(Nodes[data.Value.Id]);
}
}
}
}
break;
case MapEditorBackupType.MergeNode:
if (lastChange.Obj is MergeNodeBackup mergeBackup)
{
foreach (var (edgeId, node) in mergeBackup.EdgesMerge)
{
var edge = Edges[edgeId];
if (edge is not null && (edge.StartNode.Id == mergeBackup.NodeId || edge.EndNode.Id == mergeBackup.NodeId))
{
Nodes.Add(new NodeDto()
{
Id = node.Id,
MapId = node.MapId,
Name = node.Name,
X = node.X,
Y = node.Y,
Theta = node.Theta,
AllowedDeviationXy = node.AllowedDeviationXy,
AllowedDeviationTheta = node.AllowedDeviationTheta,
Actions = node.Actions
});
edge.UpdateNode(mergeBackup.NodeId, Nodes[node.Id]);
}
}
}
break;
}
NodesUndoableChanged.InvokeAsync(EditorBackup.Count > 0);
}
public void HorizontalLeft()
{
if (Nodes.ActivedNodes.Count > 0)
{
List<PositionBackup> ActiveNode = [];
double MinX = Nodes.ActivedNodes.First().X;
foreach (var node in Nodes.ActivedNodes)
{
ActiveNode.Add(new()
{
Id = node.Id,
X = node.X,
Y = node.Y,
});
if (MinX > node.X) MinX = node.X;
}
EditorBackup.Add(new()
{
Type = MapEditorBackupType.MoveNode,
Obj = ActiveNode,
});
NodesUndoableChanged.InvokeAsync(true);
foreach (var node in Nodes.ActivedNodes)
{
node.UpdatePosition(MinX, node.Y);
}
}
}
public void HorizontalRight()
{
if (Nodes.ActivedNodes.Count > 0)
{
List<PositionBackup> ActiveNode = [];
double MaxX = Nodes.ActivedNodes.First().X;
foreach (var node in Nodes.ActivedNodes)
{
ActiveNode.Add(new()
{
Id = node.Id,
X = node.X,
Y = node.Y,
});
if (MaxX < node.X) MaxX = node.X;
}
EditorBackup.Add(new()
{
Type = MapEditorBackupType.MoveNode,
Obj = ActiveNode,
});
NodesUndoableChanged.InvokeAsync(true);
foreach (var node in Nodes.ActivedNodes)
{
node.UpdatePosition(MaxX, node.Y);
}
}
}
public void VerticalTop()
{
if (Nodes.ActivedNodes.Count > 0)
{
List<PositionBackup> ActiveNode = [];
double MaxY = Nodes.ActivedNodes.First().Y;
foreach (var node in Nodes.ActivedNodes)
{
ActiveNode.Add(new()
{
Id = node.Id,
X = node.X,
Y = node.Y,
});
if (MaxY < node.Y) MaxY = node.Y;
}
EditorBackup.Add(new()
{
Type = MapEditorBackupType.MoveNode,
Obj = ActiveNode,
});
NodesUndoableChanged.InvokeAsync(true);
foreach (var node in Nodes.ActivedNodes)
{
node.UpdatePosition(node.X, MaxY);
}
}
}
public void VerticalBottom()
{
if (Nodes.ActivedNodes.Count > 0)
{
List<PositionBackup> ActiveNode = [];
double MinY = Nodes.ActivedNodes.First().Y;
foreach (var node in Nodes.ActivedNodes)
{
ActiveNode.Add(new()
{
Id = node.Id,
X = node.X,
Y = node.Y,
});
if (MinY > node.Y) MinY = node.Y;
}
EditorBackup.Add(new()
{
Type = MapEditorBackupType.MoveNode,
Obj = ActiveNode,
});
NodesUndoableChanged.InvokeAsync(true);
foreach (var node in Nodes.ActivedNodes)
{
node.UpdatePosition(node.X, MinY);
}
}
}
public void HorizontalCenter()
{
if (Nodes.ActivedNodes.Count > 2)
{
List<PositionBackup> ActiveNode = [];
double MinX = Nodes.ActivedNodes.First().X;
double MaxX = Nodes.ActivedNodes.First().X;
foreach (var node in Nodes.ActivedNodes)
{
ActiveNode.Add(new()
{
Id = node.Id,
X = node.X,
Y = node.Y,
});
if (MinX > node.X) MinX = node.X;
if (MaxX < node.X) MaxX = node.X;
}
EditorBackup.Add(new()
{
Type = MapEditorBackupType.MoveNode,
Obj = ActiveNode,
});
NodesUndoableChanged.InvokeAsync(true);
List<NodeModel> NodeSort = [.. Nodes.ActivedNodes.OrderBy(n => n.X)];
var offset = (MaxX - MinX) / (Nodes.ActivedNodes.Count - 1);
for (int i = 1; i < NodeSort.Count - 1; i++)
{
var node = Nodes.ActivedNodes.FirstOrDefault(n => n.Id == NodeSort[i].Id);
node?.UpdatePosition(NodeSort[0].X + offset * i, node.Y);
}
}
}
public void VerticalCenter()
{
if (Nodes.ActivedNodes.Count > 2)
{
List<PositionBackup> ActiveNode = [];
double MinY = Nodes.ActivedNodes.First().Y;
double MaxY = Nodes.ActivedNodes.First().Y;
foreach (var node in Nodes.ActivedNodes)
{
ActiveNode.Add(new()
{
Id = node.Id,
X = node.X,
Y = node.Y,
});
if (MinY > node.Y) MinY = node.Y;
if (MaxY < node.Y) MaxY = node.Y;
}
EditorBackup.Add(new()
{
Type = MapEditorBackupType.MoveNode,
Obj = ActiveNode,
});
NodesUndoableChanged.InvokeAsync(true);
List<NodeModel> NodeSort = [.. Nodes.ActivedNodes.OrderBy(n => n.Y)];
var offset = (MaxY - MinY) / (Nodes.ActivedNodes.Count - 1);
for (int i = 1; i < NodeSort.Count - 1; i++)
{
var node = Nodes.ActivedNodes.FirstOrDefault(n => n.Id == NodeSort[i].Id);
node?.UpdatePosition(node.X, NodeSort[0].Y + offset * i);
}
}
}
public void ZoneUpdateShape(double x, double y)
{
if (Zones.ZoneActived is not null)
{
if (EditorBackup.Count == 0 || EditorBackup.Last().Id != Zones.ZoneActived.Id || EditorBackup.Count > 0 && EditorBackup.Last().Obj is ZoneShapeBackup zonebackup && zonebackup.NodeChange != Zones.ZoneActived.ActiveNode)
{
EditorBackup.Add(new()
{
Id = Zones.ZoneActived.Id,
Type = MapEditorBackupType.Zone,
Obj = new ZoneShapeBackup()
{
NodeChange = Zones.ZoneActived.ActiveNode,
X1 = Zones.ZoneActived.X1,
Y1 = Zones.ZoneActived.Y1,
X2 = Zones.ZoneActived.X2,
Y2 = Zones.ZoneActived.Y2,
X3 = Zones.ZoneActived.X3,
Y3 = Zones.ZoneActived.Y3,
X4 = Zones.ZoneActived.X4,
Y4 = Zones.ZoneActived.Y4,
}
});
NodesUndoableChanged.InvokeAsync(true);
}
if (Zones.ZoneActived.ActiveNode > 0) Zones.ZoneActived.UpdateControlNode(Zones.ZoneActived.ActiveNode, x, y);
}
}
public void EdgesPositionMove(double x, double y)
{
if (EditorBackup.Count == 0)
{
List<EdgeBackup> BackupEdge = [];
foreach (var edge in Edges.ActivedEdges)
{
BackupEdge.Add(new()
{
Id = edge.Id,
TrajectoryDegree = edge.TrajectoryDegree,
StartX = edge.X1,
StartY = edge.Y1,
EndX = edge.X2,
EndY = edge.Y2,
ControlPoint1X = edge.ControlPoint1X,
ControlPoint1Y = edge.ControlPoint1Y,
ControlPoint2X = edge.ControlPoint2X,
ControlPoint2Y = edge.ControlPoint2Y,
});
}
EditorBackup.Add(new()
{
Type = MapEditorBackupType.MoveEdge,
Obj = BackupEdge,
});
NodesUndoableChanged.InvokeAsync(true);
}
Edges.UpdateMoveEdge(x, y);
}
public void NodesPositionMove(double x, double y)
{
if (EditorBackup.Count == 0)
{
List<PositionBackup> ActiveNode = [];
foreach (var node in Nodes.ActivedNodes)
{
ActiveNode.Add(new()
{
Id = node.Id,
X = x,
Y = y,
});
}
EditorBackup.Add(new()
{
Type = MapEditorBackupType.MoveNode,
Obj = ActiveNode,
});
NodesUndoableChanged.InvokeAsync(true);
}
Nodes.UpdateMoveNode(x, y);
}
public void NodePositionMove(double x, double y)
{
if (Nodes.SelectedNode is not null)
{
if (EditorBackup.Count == 0 || EditorBackup.Last().Id != Nodes.SelectedNode.Id)
{
EditorBackup.Add(new()
{
Id = Nodes.SelectedNode.Id,
Type = MapEditorBackupType.Node,
Obj = new PositionBackup()
{
Id = Nodes.SelectedNode.Id,
X = Nodes.SelectedNode.X,
Y = Nodes.SelectedNode.Y,
}
});
NodesUndoableChanged.InvokeAsync(true);
}
Nodes.SelectedNode.UpdatePosition(x, y);
}
else if (Edges.ActivedEdges.Count == 1)
{
if (Edges.ActivedEdges[0].ActivedControlPoint1)
{
if (EditorBackup.Count == 0 || EditorBackup.Last().Id != Edges.ActivedEdges[0].Id || EditorBackup.Last().Id == Edges.ActivedEdges[0].Id && EditorBackup.Last().Type != MapEditorBackupType.ControlPoint1Edge)
{
EditorBackup.Add(new()
{
Id = Edges.ActivedEdges[0].Id,
Type = MapEditorBackupType.ControlPoint1Edge,
Obj = new PositionBackup()
{
Id = Edges.ActivedEdges[0].Id,
X = Edges.ActivedEdges[0].ControlPoint1X,
Y = Edges.ActivedEdges[0].ControlPoint1Y,
}
});
NodesUndoableChanged.InvokeAsync(true);
}
Edges.ActivedEdges[0].UpdateControlPoint1(x, y);
}
else if (Edges.ActivedEdges[0].ActivedControlPoint2)
{
if (EditorBackup.Count == 0 || EditorBackup.Last().Id != Edges.ActivedEdges[0].Id || EditorBackup.Last().Id == Edges.ActivedEdges[0].Id && EditorBackup.Last().Type != MapEditorBackupType.ControlPoint2Edge)
{
EditorBackup.Add(new()
{
Id = Edges.ActivedEdges[0].Id,
Type = MapEditorBackupType.ControlPoint2Edge,
Obj = new PositionBackup()
{
Id = Edges.ActivedEdges[0].Id,
X = Edges.ActivedEdges[0].ControlPoint2X,
Y = Edges.ActivedEdges[0].ControlPoint2Y,
}
});
NodesUndoableChanged.InvokeAsync(true);
}
Edges.ActivedEdges[0].UpdateControlPoint2(x, y);
}
}
}
public async Task SplitNode()
{
if (EditorBackup.Count > 0)
{
if (!await SaveChanged()) return;
}
if (MapData is null) { Snackbar.Add("Dữ liệu bản đồ rỗng", Severity.Warning); return; }
if (MapData.Active) { Snackbar.Add("Không thể tạo edge do bản đồ đang được Active", Severity.Warning); return; }
if (Nodes.ActivedNodes.Count > 1) { Snackbar.Add("Vui lòng chỉ chọn 1 Node để tách", Severity.Warning); return; }
if (Nodes.ActivedNodes.Count == 0) { Snackbar.Add("Vui lòng chọn 1 Node để tách", Severity.Warning); return; }
var nodeSplit = Nodes.ActivedNodes[0];
var edges = Edges.Where(edge => edge.StartNode.Id == nodeSplit.Id || edge.EndNode.Id == nodeSplit.Id).ToList();
if (edges.Count <= 1) { Snackbar.Add("Không thể tách node đơn", Severity.Warning); return; }
Dictionary<Guid, NodeDto> EdgeSplit = [];
for (int i = 1; i < edges.Count; i++)
{
var newNode = new NodeDto()
{
Id = Guid.NewGuid(),
Name = string.Empty,
X = nodeSplit.X,
Y = nodeSplit.Y,
Theta = nodeSplit.Theta,
MapId = nodeSplit.MapId,
AllowedDeviationXy = nodeSplit.AllowedDeviationXy,
AllowedDeviationTheta = nodeSplit.AllowedDeviationTheta,
Actions = nodeSplit.Actions,
};
Nodes.Add(newNode);
edges[i].UpdateNode(nodeSplit.Id, Nodes[newNode.Id]);
EdgeSplit.Add(edges[i].Id, newNode);
}
Nodes.UnActivedNode();
EditorBackup.Add(new()
{
Type = MapEditorBackupType.SplitNode,
Id = nodeSplit.Id,
Obj = new SplitNodeBackup()
{
NodeId = nodeSplit.Id,
EdgeSplit = EdgeSplit,
},
});
await NodesUndoableChanged.InvokeAsync(true);
Snackbar.Add("Tách Node thành công", Severity.Success);
}
public async Task MergeNode()
{
if (EditorBackup.Count > 0)
{
if (!await SaveChanged()) return;
}
if (MapData is null) { Snackbar.Add("Dữ liệu bản đồ rỗng", Severity.Warning); return; }
if (MapData.Active) { Snackbar.Add("Không thể tạo edge do bản đồ đang được Active", Severity.Warning); return; }
if (Nodes.ActivedNodes.Count <= 1) { Snackbar.Add("Vui lòng chọn nhiều hơn 1 Node để gộp", Severity.Warning); return; }
if (Edges.ActivedEdges.Count > 0) { Snackbar.Add("Vui lòng không chọn Edge", Severity.Warning); return; }
MergeNodeBackup MergeNodeBackup = new()
{
NodeId = Nodes.ActivedNodes[0].Id,
};
List<NodeModel> activedNodes = [.. Nodes.ActivedNodes];
Nodes.UnActivedNode();
for (int i = 1; i < activedNodes.Count; i++)
{
var edges = Edges.Where(e => e.StartNode.Id == activedNodes[i].Id || e.EndNode.Id == activedNodes[i].Id).ToList();
if (edges.Count > 0)
{
foreach (var edge in edges)
{
edge.UpdateNode(activedNodes[i].Id, Nodes[activedNodes[0].Id]);
MergeNodeBackup.EdgesMerge.Add((edge.Id, activedNodes[i]));
Nodes.Remove(activedNodes[i]);
}
}
}
EditorBackup.Add(new()
{
Type = MapEditorBackupType.MergeNode,
Id = activedNodes[0].Id,
Obj = MergeNodeBackup,
});
await NodesUndoableChanged.InvokeAsync(true);
Snackbar.Add("Gộp Node thành công", Severity.Success);
}
public async Task ScanerActive(double x1, double y1, double x2, double y2)
{
List<EdgeModel> activeEdges = [];
List<NodeModel> activeNodes = [];
foreach (var edge in Edges)
{
if (MapEditorHelper.NodeInScanZone(edge.StartNode.X, edge.StartNode.Y, x1, y1, x2, y2) && MapEditorHelper.NodeInScanZone(edge.EndNode.X, edge.EndNode.Y, x1, y1, x2, y2))
{
activeEdges.Add(edge);
if (!activeNodes.Any(n => n.Id == edge.StartNode.Id)) activeNodes.Add(edge.StartNode);
if (!activeNodes.Any(n => n.Id == edge.EndNode.Id)) activeNodes.Add(edge.EndNode);
}
}
foreach (var node in Nodes)
{
if (!activeNodes.Any(n => n.Id == node.Id) && MapEditorHelper.NodeInScanZone(node.X, node.Y, x1, y1, x2, y2)) activeNodes.Add(node);
}
Edges.ActivedEdge(activeEdges);
Nodes.ActivedNode(activeNodes);
await MultiselectedNodeChanged.InvokeAsync(activeNodes.Count > 0);
await MultiselectedEdgeChanged.InvokeAsync(activeEdges.Count > 0);
}
public async Task CheckMap()
{
await MapIsChecking.InvokeAsync(true);
var mapSetting = await Http.GetFromJsonAsync<MessageResult<MapSettingDefaultDto>>($"api/MapsSetting/{MapData.Id}");
if (mapSetting is null) { Snackbar.Add("Lỗi giao tiếp với hệ thống", Severity.Error); return; }
else if (!mapSetting.IsSuccess) { Snackbar.Add(mapSetting.Message ?? "Lấy dữ liệu bản đồ không thành công", Severity.Error); return; }
else if (mapSetting.Data == null) { Snackbar.Add("Lỗi dữ liệu trả về", Severity.Error); return; }
foreach (var edge in Edges)
{
var caculateEdge = new EdgeCaculatorModel()
{
MapId = edge.MapId,
X1 = edge.X1,
Y1 = edge.Y1,
X2 = edge.X2,
Y2 = edge.Y2,
ControlPoint1X = edge.ControlPoint1X,
ControlPoint1Y = edge.ControlPoint1Y,
ControlPoint2X = edge.ControlPoint2X,
ControlPoint2Y = edge.ControlPoint2Y,
TrajectoryDegree = edge.TrajectoryDegree,
};
if (MapEditorHelper.GetEdgeLength(caculateEdge) < mapSetting.Data.EdgeMinLength) edge.SetError(true);
else edge.SetError(false);
}
List<ZoneModel> ErrorZones = [];
foreach (var zone in Zones)
{
if (MapEditorHelper.CalculateQuadrilateralArea(zone.X1, zone.Y1, zone.X2, zone.Y2, zone.X3, zone.Y3, zone.X4, zone.Y4) < mapSetting.Data.ZoneMinSquare)
{
ErrorZones.Add(zone);
}
}
List<NodeModel> ErrorNodes = [];
foreach (var node in Nodes)
{
if (ErrorNodes.Any(e => e.Id == node.Id)) continue;
foreach (var checkNode in Nodes)
{
if (checkNode.Id == node.Id) continue;
var distance = Math.Sqrt(Math.Pow(node.X - checkNode.X, 2) + Math.Pow(node.Y - checkNode.Y, 2));
if (distance < mapSetting.Data.EdgeMinLength)
{
if (!Edges.Any(e => e.StartNode.Id == node.Id && e.EndNode.Id == checkNode.Id || e.EndNode.Id == node.Id && e.StartNode.Id == checkNode.Id))
{
ErrorNodes.Add(node);
node.SetError(true);
break;
}
}
}
if (!ErrorNodes.Any(e => e.Id == node.Id)) node.SetError(false);
}
await MapIsChecking.InvokeAsync(false);
Snackbar.Add("Kiểm tra xong", Severity.Success);
}
}