/// <summary> /// Generate the left and right edges of the paths in this network, /// automatically joining them at shared end nodes. Nodes should have been /// generated for the network prior to calling this function. /// </summary> /// <typeparam name="TPath"></typeparam> /// <param name="paths"></param> public static void GenerateNetworkPathEdges <TPath>(this IList <TPath> paths) where TPath : IWidePath { var pathMap = new Dictionary <Guid, TPath>(); // Generate initial curves + build map: foreach (TPath path in paths) { if (path.Spine != null) { pathMap[path.Spine.GUID] = path; path.GenerateInitialPathEdges(); path.CurveInitialPathEdges(); } } NodeCollection nodes = paths.ExtractNetworkPathNodes(); // Trim edges at nodes: foreach (Node node in nodes) { if (node.Vertices.Count > 0) { // Sort connected vertices by the angle pointing away from the node var angleSorted = new SortedList <double, Vertex>(node.Vertices.Count); foreach (var v in node.Vertices) { if (v.Owner != null && v.Owner is Curve && pathMap.ContainsKey(v.Owner.GUID)) { Curve crv = (Curve)v.Owner; if (v.IsStart) { angleSorted.AddSafe(crv.TangentAt(0).Angle, v); } else if (v.IsEnd) { angleSorted.AddSafe(crv.TangentAt(1).Reverse().Angle, v); } } } if (angleSorted.Count > 1) { for (int i = 0; i < angleSorted.Count - 1; i++) { // Reference case is path leading away from node Vertex vR = angleSorted.Values.GetWrapped(i - 1); Vertex v = angleSorted.Values[i]; Vertex vL = angleSorted.Values.GetWrapped(i + 1); Angle a = new Angle(angleSorted.Keys[i]).NormalizeTo2PI(); Angle aR = new Angle(angleSorted.Keys.GetWrapped(i - 1) - a); Angle aL = new Angle(angleSorted.Keys.GetWrapped(i + 1) - a).Explement(); TPath pathR = pathMap[vR.Owner.GUID]; TPath path = pathMap[v.Owner.GUID]; TPath pathL = pathMap[vL.Owner.GUID]; // Work out correct edges to match up based on direction: Vertex edgeVR; Vertex edgeVL; double offsR; double offsL; if (v.IsStart) { // Curve is pointing away from the node edgeVR = path.RightEdge.Start; edgeVL = path.LeftEdge.Start; offsR = path.RightOffset; offsL = path.LeftOffset; } else { // Curve is pointing towards the node - flip everything! edgeVR = path.LeftEdge.End; edgeVL = path.RightEdge.End; offsR = path.LeftOffset; offsL = path.RightOffset; } Vertex edgeVR2; double offsR2; if (vR.IsStart) { edgeVR2 = pathR.LeftEdge.Start; offsR2 = pathR.LeftOffset; } else { edgeVR2 = pathR.RightEdge.End; offsR2 = pathR.RightOffset; } Vertex edgeVL2; double offsL2; if (vL.IsStart) { edgeVL2 = pathL.RightEdge.Start; offsL2 = pathL.RightOffset; } else { edgeVL2 = pathL.LeftEdge.End; offsL2 = pathL.LeftOffset; } bool canTrimR = true; bool canTrimR2 = true; if (aR.IsReflex) { if (offsR > offsR2) { canTrimR = false; } else if (offsR2 > offsR) { canTrimR2 = false; } } if (!Curve.MatchEnds(edgeVR, edgeVR2, true, canTrimR, canTrimR2)) { if (offsR > offsR2) { Curve.ExtendToLineXY(edgeVR2, node.Position, edgeVR.Position - node.Position); path.SetEndEdge(edgeVR, new Line(edgeVR.Position, edgeVR2.Position)); } else { Curve.ExtendToLineXY(edgeVR, node.Position, edgeVR2.Position - node.Position); pathR.SetEndEdge(edgeVR2, new Line(edgeVR2.Position, edgeVR.Position)); } /*(Curve crv = Curve.Connect(edgeVR, edgeVR2.Position); * if (crv != null) * { * if (v.IsStart) path.RightEdge = crv; * else path.LeftEdge = crv; * }*/ } bool canTrimL = true; bool canTrimL2 = true; if (aL.IsReflex) { if (offsL > offsL2) { canTrimL = false; } else if (offsL2 > offsL) { canTrimL2 = false; } } if (!Curve.MatchEnds(edgeVL, edgeVL2, true, canTrimL, canTrimL2)) { if (offsL > offsL2) { Curve.ExtendToLineXY(edgeVL2, node.Position, edgeVL.Position - node.Position); path.SetEndEdge(edgeVL, new Line(edgeVL.Position, edgeVL2.Position)); } else { Curve.ExtendToLineXY(edgeVL, node.Position, edgeVL2.Position - node.Position); pathL.SetEndEdge(edgeVL2, new Line(edgeVL2.Position, edgeVL.Position)); } /*Curve crv = Curve.Connect(edgeVL, edgeVL2.Position); * if (crv != null) * { * if (v.IsStart) path.LeftEdge = crv; * else path.RightEdge = crv; * }*/ } } } else if (angleSorted.Count == 1) { // Close off end: Vertex v = angleSorted.Values[0]; TPath path = pathMap[v.Owner.GUID]; if (v.IsStart) { path.StartCapLeft = new Line(path.LeftEdge.StartPoint, path.RightEdge.StartPoint); } else { path.EndCapLeft = new Line(path.LeftEdge.EndPoint, path.RightEdge.EndPoint); } } } } }
/// <summary> /// Extract from this PolyCurve a chain of subcurves, starting with the /// longest and continuing to select the longest edge until no more can /// be added without adding an edge which is within an angle tolerance of /// an existing curve within the chain. /// </summary> /// <returns></returns> public PolyCurve ExtractLongCurveChain(Angle maxAngle) { Curve longest = SubCurves.GetLongest(); if (longest != null) { var tangents = new List <Vector>(); tangents.Add(longest.TangentAt(0.5)); var result = new PolyCurve(longest); int iL = SubCurves.IndexOf(longest); int i0 = iL - 1; int i1 = iL + 1; bool closed = Closed; bool tryPre = closed || i0 >= 0; bool tryPost = closed || i1 < SubCurves.Count; while (tryPre || tryPost) { Curve pre = tryPre ? SubCurves.GetWrapped(i0, closed) : null; Curve post = tryPost ? SubCurves.GetWrapped(i1, closed) : null; if (post != null && (pre == null || post.Length > pre.Length)) { Vector tang = post.TangentAt(0.5); if (result.SubCurves.Contains(post.GUID) || tangents.MaximumAngleBetween(tang).Abs() > maxAngle) { tryPost = false; } else { result.SubCurves.Add(post); tangents.Add(tang); i1++; if (!closed && i1 >= SubCurves.Count) { tryPost = false; } } } else if (pre != null) { Vector tang = pre.TangentAt(0.5); if (result.SubCurves.Contains(pre.GUID) || tangents.MaximumAngleBetween(tang).Abs() > maxAngle) { tryPre = false; } else { result.SubCurves.Insert(0, pre); tangents.Add(tang); i0--; if (!closed && i0 < 0) { tryPre = false; } } } else { tryPre = false; tryPost = false; } } return(result); } return(null); }