@inject IJSRuntime JSRuntime @code { private ElementReference Ref; private ElementReference PathRef; private ElementReference SubPath1Ref; private ElementReference SubPath2Ref; private (double X, double Y) StartNode; private (double X, double Y) SubStartNode; private (double X, double Y) EndNode; private (double X, double Y) SubEndNode; private TrajectoryDegree Degree; private (double X, double Y) ControlPoint1; private (double X, double Y) ControlPoint2; public CreateCurveEdgeStep CreateStep { get; set; } = CreateCurveEdgeStep.Hidden; public async Task Update(double x, double y) { if (CreateStep == CreateCurveEdgeStep.CreateSubStartEdge) { SubStartNode.X = x; SubStartNode.Y = y; } else if (CreateStep == CreateCurveEdgeStep.FinishCreateSubStartEdge) { EndNode.X = x; EndNode.Y = y; await CreateTangentPath(); } else if (CreateStep == CreateCurveEdgeStep.CreateSubEndEdge) { SubEndNode.X = x; SubEndNode.Y = y; await CreateTangentPath(); } StateHasChanged(); } public async Task CreateSubEdgeAsync(double x, double y, TrajectoryDegree degree = TrajectoryDegree.Two) { Degree = degree; if (CreateStep == CreateCurveEdgeStep.Hidden) { await Visible(); StartNode.X = x; StartNode.Y = y; SubStartNode.X = x; SubStartNode.Y = y; CreateStep = CreateCurveEdgeStep.CreateSubStartEdge; } else if (CreateStep == CreateCurveEdgeStep.FinishCreateSubStartEdge) { EndNode.X = x; EndNode.Y = y; SubEndNode.X = x; SubEndNode.Y = y; CreateStep = CreateCurveEdgeStep.CreateSubEndEdge; await JSRuntime.InvokeVoidAsync("ElementSetAttribute", SubPath2Ref, "visibility", "visible"); } StateHasChanged(); } public async Task FinishSubEdgeAsync(double x, double y) { if (CreateStep == CreateCurveEdgeStep.CreateSubStartEdge) { SubStartNode.X = x; SubStartNode.Y = y; EndNode.X = x; EndNode.Y = y; CreateStep = CreateCurveEdgeStep.FinishCreateSubStartEdge; await CreateTangentPath(); } else if (CreateStep == CreateCurveEdgeStep.CreateSubEndEdge) CreateStep = CreateCurveEdgeStep.Finish; } public EdgeCreateModel GetEdgeCreateModel() { return new() { X1 = StartNode.X, Y1 = StartNode.Y, X2 = EndNode.X, Y2 = EndNode.Y, ControlPoint1X = ControlPoint1.X, ControlPoint1Y = ControlPoint1.Y, ControlPoint2X = ControlPoint2.X, ControlPoint2Y = ControlPoint2.Y, TrajectoryDegree = Degree, }; } private async Task CreateTangentPath() { var data = $"M {StartNode.X} {StartNode.Y}"; if(Degree == TrajectoryDegree.Two) { var cp = MapEditorHelper.CaculateIntersectionPoint(SubStartNode.X, SubStartNode.Y, StartNode.X, StartNode.Y, EndNode.X, EndNode.Y, SubEndNode.X, SubEndNode.Y); if (cp is null || CreateStep == CreateCurveEdgeStep.FinishCreateSubStartEdge) cp = MapEditorHelper.CaculateControlPoint(StartNode.X, StartNode.Y, EndNode.X, EndNode.Y, 45, 1 / Math.Sqrt(2)); data = $"{data} Q {cp.Value.X} {cp.Value.Y} {EndNode.X} {EndNode.Y}"; ControlPoint1 = (cp.Value.X, cp.Value.Y); } else if(Degree == TrajectoryDegree.Three) { if (CreateStep == CreateCurveEdgeStep.FinishCreateSubStartEdge) { ControlPoint1 = MapEditorHelper.CaculateControlPoint(StartNode.X, StartNode.Y, EndNode.X, EndNode.Y, 45, 1 / Math.Sqrt(2)); ControlPoint2 = MapEditorHelper.CaculateControlPoint(StartNode.X, StartNode.Y, EndNode.X, EndNode.Y, -45, 1 / Math.Sqrt(2)); } else (ControlPoint1.X, ControlPoint1.Y, ControlPoint2.X, ControlPoint2.Y) = MapEditorHelper.CaculateDoubleControlPoint(SubStartNode.X, SubStartNode.Y, StartNode.X, StartNode.Y, EndNode.X, EndNode.Y, SubEndNode.X, SubEndNode.Y); data = $"{data} C {ControlPoint1.X} {ControlPoint1.Y}, {ControlPoint2.X} {ControlPoint2.Y}, {EndNode.X} {EndNode.Y}"; } await JSRuntime.InvokeVoidAsync("ElementSetAttribute", PathRef, "d", data); StateHasChanged(); } public async Task Visible() { EndNode = (0, 0); await JSRuntime.InvokeVoidAsync("ElementSetAttribute", PathRef, "d", ""); await JSRuntime.InvokeVoidAsync("ElementSetAttribute", SubPath1Ref, "visibility", "visible"); await JSRuntime.InvokeVoidAsync("ElementSetAttribute", SubPath2Ref, "visibility", "hidden"); await JSRuntime.InvokeVoidAsync("ElementSetAttribute", Ref, "visibility", "visible"); } public async Task Hidden() { CreateStep = CreateCurveEdgeStep.Hidden; await JSRuntime.InvokeVoidAsync("ElementSetAttribute", SubPath1Ref, "visibility", "hidden"); await JSRuntime.InvokeVoidAsync("ElementSetAttribute", SubPath2Ref, "visibility", "hidden"); await JSRuntime.InvokeVoidAsync("ElementSetAttribute", Ref, "visibility", "hidden"); } public enum CreateCurveEdgeStep { Hidden, CreateSubStartEdge, FinishCreateSubStartEdge, CreateSubEndEdge, Finish, } }