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 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>(); 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 { { 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("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(); 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 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>(); 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 { { 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("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($"/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 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 { { 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("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(); 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 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 saveEdge = []; if (backup.Obj is List 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 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 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>>(); 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 activeEdges = []; List 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>($"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 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 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); } }