internal OrientedHubSegment(ICurve seg, bool reversed, int index, BundleBase bundleBase) { Segment = seg; Reversed = reversed; Index = index; BundleBase = bundleBase; }
double GetAssymetryCostForBase(BundleBase bundleBase) { if (bundleBase.BelongsToRealNode) { return(0); } double assymetryWeight = bundleBase.OppositeBase.BelongsToRealNode ? 200 : 500; double cost = 0; foreach (var o in bundleBase.OrientedHubSegments) { int i0 = o.Index; int i1 = o.Other.Index; var a = bundleBase.Points[i0]; var ta = bundleBase.Tangents[i0]; var oppositeBase = o.Other.BundleBase; var b = oppositeBase.Points[i1]; var tb = oppositeBase.Tangents[i1]; double s = bundleBase.Count + oppositeBase.Count; cost += GetAssymetryCostOnData(a, ta, b, tb, assymetryWeight) / s; } return(cost); }
double SeparationCostForBundleBase(BundleBase bBase) { if (bBase.Prev == null) { return(0); } return(SeparationCostForAdjacentBundleBases(bBase, bBase.Prev) + SeparationCostForAdjacentBundleBases(bBase, bBase.Next)); }
static Point FindCurveEnd(MetroGraphData metroGraphData, IMetroMapOrderingAlgorithm metroOrdering, Metroline metroline) { Station u = metroGraphData.PointToStations[metroline.Polyline.EndPoint.Prev.Point]; Station v = metroGraphData.PointToStations[metroline.Polyline.EndPoint.Point]; BundleBase bb = v.BundleBases[u]; int index = (!bb.IsParent ? metroOrdering.GetLineIndexInOrder(u, v, metroline) : metroOrdering.GetLineIndexInOrder(v, u, metroline)); return(bb.Points[index]); }
bool FanEdgesOfHubSegment(BundleBase bundleHub) { bool ret = false; for (int i = 0; i < bundleHub.Count - 1; i++) { ret |= FanCouple(bundleHub, i, bundleHub.CurveCenter, bundleHub.Curve.BoundingBox.Diagonal / 2); } return(ret); }
void AllocateBundleBases() { externalBases = new Dictionary <ICurve, List <BundleBase> >(); internalBases = new Dictionary <ICurve, List <BundleBase> >(); Bundles = new List <BundleInfo>(); foreach (var station in metroGraphData.Stations) { if (station.BoundaryCurve == null) { station.BoundaryCurve = new Ellipse(station.Radius, station.Radius, station.Position); } } foreach (var station in metroGraphData.Stations) { foreach (Station neighbor in station.Neighbors) { if (station < neighbor) { var bb = new BundleBase(metroGraphData.RealEdgeCount(station, neighbor), station.BoundaryCurve, station.Position, station.IsRealNode, neighbor.SerialNumber); station.BundleBases[neighbor] = bb; var bb2 = new BundleBase(metroGraphData.RealEdgeCount(station, neighbor), neighbor.BoundaryCurve, neighbor.Position, neighbor.IsRealNode, station.SerialNumber); neighbor.BundleBases[station] = bb2; if (Curve.PointRelativeToCurveLocation(neighbor.Position, station.BoundaryCurve) != PointLocation.Outside) { bb.IsParent = true; CollectionUtilities.AddToMap(internalBases, station.BoundaryCurve, bb); CollectionUtilities.AddToMap(externalBases, neighbor.BoundaryCurve, bb2); } else if (Curve.PointRelativeToCurveLocation(station.Position, neighbor.BoundaryCurve) != PointLocation.Outside) { bb2.IsParent = true; CollectionUtilities.AddToMap(externalBases, station.BoundaryCurve, bb); CollectionUtilities.AddToMap(internalBases, neighbor.BoundaryCurve, bb2); } else { CollectionUtilities.AddToMap(externalBases, station.BoundaryCurve, bb); CollectionUtilities.AddToMap(externalBases, neighbor.BoundaryCurve, bb2); } Set <Polyline> obstaclesToIgnore = metroGraphData.tightIntersections.ObstaclesToIgnoreForBundle(station, neighbor); var bundle = new BundleInfo(bb, bb2, obstaclesToIgnore, bundlingSettings.EdgeSeparation, metroOrdering.GetOrder(station, neighbor).Select(l => l.Width / 2).ToArray()); bb.OutgoingBundleInfo = bb2.IncomingBundleInfo = bundle; Bundles.Add(bundle); } } } //neighbors SetBundleBaseNeighbors(); }
double SeparationCostForAdjacentBundleBases(BundleBase base0, BundleBase base1) { Debug.Assert(base0.Curve == base1.Curve); ICurve boundaryCurve = base0.Curve; double len = IntervalsOverlapLength(base0.ParRight, base0.ParLeft, base1.ParRight, base1.ParLeft, boundaryCurve); double mn = Math.Min(base0.Span, base1.Span); Debug.Assert(ApproximateComparer.LessOrEqual(len, mn)); Debug.Assert(mn > 0); return(Math.Exp(len / mn * 10) - 1); }
/// <summary> /// fans the couple i,i+1 /// </summary> bool FanCouple(BundleBase bundleHub, int i, Point center, double radius) { OrientedHubSegment lSeg = bundleHub.OrientedHubSegments[i]; OrientedHubSegment rSeg = bundleHub.OrientedHubSegments[i + 1]; if (lSeg == null) { return(false); } Point x; if (LineSegment.Intersect(lSeg.Segment.Start, lSeg.Segment.End, rSeg.Segment.Start, rSeg.Segment.End, out x)) { return(false); //it doesn not make sense to push these segs apart } if (Point.GetTriangleOrientation(lSeg[0], lSeg[0.5], lSeg[1]) != Point.GetTriangleOrientation(rSeg[0], rSeg[0.5], rSeg[1])) { return(false); } double ll = BaseLength(lSeg); double rl = BaseLength(rSeg); if (Math.Abs(ll - rl) < ApproximateComparer.IntersectionEpsilon) { return(false); } if (ll > rl) { return(AdjustLongerSeg(lSeg, rSeg, center, radius)); } return(AdjustLongerSeg(rSeg, lSeg, center, radius)); /* * var del0 = lSeg.Start - rSeg.Start; * var del1 = lSeg.End - rSeg.End; * var desiredDelta = Math.Min(del0, del1); * var leftMiddle = lSeg[0.5]; * var rightMiddle = rSeg[0.5]; * if ((leftMiddle - rightMiddle).Length >= desiredDelta - ApproximateComparer.DistanceEpsilon) * return false; * var leftMiddleToCenter = (leftMiddle - BundleHub.Vertex).Length; * var rightMiddleToCenter = (rightMiddle - BundleHub.Vertex).Length; * if (leftMiddleToCenter > rightMiddleToCenter) { * if (MoveSegToDesiredDistance(rightMiddle, lSeg, desiredDelta)) * return true; * } else if (MoveSegToDesiredDistance(leftMiddle, rSeg, desiredDelta)) * return true; * * return false;*/ }
void CreateOrientedSegsOnLineVertex(Metroline line, PolylinePoint polyPoint) { Station u = metroGraphData.PointToStations[polyPoint.Prev.Point]; Station v = metroGraphData.PointToStations[polyPoint.Point]; Station w = metroGraphData.PointToStations[polyPoint.Next.Point]; BundleBase h0 = v.BundleBases[u]; BundleBase h1 = v.BundleBases[w]; int j0 = metroOrdering.GetLineIndexInOrder(u, v, line); int j1 = metroOrdering.GetLineIndexInOrder(w, v, line); OrientedHubSegment or0 = h0.OrientedHubSegments[j0] = new OrientedHubSegment(null, false, j0, h0); OrientedHubSegment or1 = h1.OrientedHubSegments[j1] = new OrientedHubSegment(null, true, j1, h1); or1.Other = or0; or0.Other = or1; }
void UpdatePointsOnBundleBase(BundleBase bb) { int count = bb.Count; Point[] pns = bb.Points; var ls = new LineSegment(bb.LeftPoint, bb.RightPoint); var scale = 1 / TotalRequiredWidth; var t = HalfWidthArray[0]; pns[0] = ls[t * scale]; for (int i = 1; i < count; i++) { t += HalfWidthArray[i - 1] + EdgeSeparation + HalfWidthArray[i]; pns[i] = ls[t * scale]; } }
void CreateOrientedSegsOnLineVertex(Metroline line, PolylinePoint polyPoint) { Station u = metroGraphData.PointToStations[polyPoint.Prev.Point]; Station v = metroGraphData.PointToStations[polyPoint.Point]; Station w = metroGraphData.PointToStations[polyPoint.Next.Point]; BundleBase h0 = v.BundleBases[u]; BundleBase h1 = v.BundleBases[w]; int j0 = metroOrdering.GetLineIndexInOrder(u, v, line); int j1 = metroOrdering.GetLineIndexInOrder(w, v, line); var seg = bundlingSettings.UseCubicBezierSegmentsInsideOfHubs ? StandardBezier(h0.Points[j0], h0.Tangents[j0], h1.Points[j1], h1.Tangents[j1]) : BiArc(h0.Points[j0], h0.Tangents[j0], h1.Points[j1], h1.Tangents[j1]); h0.OrientedHubSegments[j0].Segment = seg; h1.OrientedHubSegments[j1].Segment = seg; }
static ICurve SegOnLineVertex(MetroGraphData metroGraphData, IMetroMapOrderingAlgorithm metroOrdering, Metroline line, PolylinePoint i) { Station u = metroGraphData.PointToStations[i.Prev.Point]; Station v = metroGraphData.PointToStations[i.Point]; BundleBase h0 = v.BundleBases[u]; int j0 = metroOrdering.GetLineIndexInOrder(u, v, line); if (h0.OrientedHubSegments[j0] == null || h0.OrientedHubSegments[j0].Segment == null) { var w = metroGraphData.PointToStations[i.Next.Point]; var otherBase = v.BundleBases[w]; var j1 = metroOrdering.GetLineIndexInOrder(w, v, line); return(new LineSegment(h0.Points[j0], otherBase.Points[j1])); } return(h0.OrientedHubSegments[j0].Segment); }
void AllocateBundleBases() { externalBases = new Dictionary<ICurve, List<BundleBase>>(); internalBases = new Dictionary<ICurve, List<BundleBase>>(); Bundles = new List<BundleInfo>(); foreach (var station in metroGraphData.Stations) { if (station.BoundaryCurve == null) station.BoundaryCurve = new Ellipse(station.Radius, station.Radius, station.Position); } foreach (var station in metroGraphData.Stations) { foreach (Station neighbor in station.Neighbors) { if (station < neighbor) { var bb = new BundleBase(metroGraphData.RealEdgeCount(station, neighbor), station.BoundaryCurve, station.Position, station.IsRealNode, neighbor.SerialNumber); station.BundleBases[neighbor] = bb; var bb2 = new BundleBase(metroGraphData.RealEdgeCount(station, neighbor), neighbor.BoundaryCurve, neighbor.Position, neighbor.IsRealNode, station.SerialNumber); neighbor.BundleBases[station] = bb2; if (Curve.PointRelativeToCurveLocation(neighbor.Position, station.BoundaryCurve) != PointLocation.Outside) { bb.IsParent = true; CollectionUtilities.AddToMap(internalBases, station.BoundaryCurve, bb); CollectionUtilities.AddToMap(externalBases, neighbor.BoundaryCurve, bb2); } else if (Curve.PointRelativeToCurveLocation(station.Position, neighbor.BoundaryCurve) != PointLocation.Outside) { bb2.IsParent = true; CollectionUtilities.AddToMap(externalBases, station.BoundaryCurve, bb); CollectionUtilities.AddToMap(internalBases, neighbor.BoundaryCurve, bb2); } else { CollectionUtilities.AddToMap(externalBases, station.BoundaryCurve, bb); CollectionUtilities.AddToMap(externalBases, neighbor.BoundaryCurve, bb2); } Set<Polyline> obstaclesToIgnore = metroGraphData.tightIntersections.ObstaclesToIgnoreForBundle(station, neighbor); var bundle = new BundleInfo(bb, bb2, obstaclesToIgnore, bundlingSettings.EdgeSeparation, metroOrdering.GetOrder(station, neighbor).Select(l => l.Width / 2).ToArray()); bb.OutgoingBundleInfo = bb2.IncomingBundleInfo = bundle; Bundles.Add(bundle); } } } //neighbors SetBundleBaseNeighbors(); }
void AdjustCurrentBundleWidthsOnCurve(List <BundleBase> bases) { int count = bases.Count; if (count <= 1) { return; } for (int i = 0; i < count; i++) { BundleBase rBase = bases[i]; BundleBase lBase = rBase.Next; ShrinkBasesToMakeTwoConsecutiveNeighborsHappy(rBase, lBase); Debug.Assert(!rBase.Intersect(lBase)); } }
internal BundleInfo(BundleBase sourceBase, BundleBase targetBase, Set<Polyline> obstaclesToIgnore, double edgeSeparation, double[] halfWidthArray) { SourceBase = sourceBase; TargetBase = targetBase; this.obstaclesToIgnore = obstaclesToIgnore; EdgeSeparation = edgeSeparation; HalfWidthArray = halfWidthArray; TotalRequiredWidth = EdgeSeparation * HalfWidthArray.Length + HalfWidthArray.Sum() * 2; longEnoughSideLength = new Rectangle(sourceBase.Curve.BoundingBox, targetBase.Curve.BoundingBox).Diagonal; //sometimes TotalRequiredWidth is too large to fit into the circle, so we evenly scale everything double mn = Math.Max(sourceBase.Curve.BoundingBox.Diagonal, targetBase.Curve.BoundingBox.Diagonal); if (TotalRequiredWidth > mn) { double scale = TotalRequiredWidth / mn; for (int i = 0; i < HalfWidthArray.Length; i++) HalfWidthArray[i] /= scale; TotalRequiredWidth /= scale; EdgeSeparation /= scale; } }
double CenterCost(BundleBase bundleBase) { if (!bundleBase.BelongsToRealNode) { return(0); } double currentMid = bundleBase.ParMid; double mn = Math.Min(bundleBase.InitialMidParameter, currentMid); double mx = Math.Max(bundleBase.InitialMidParameter, currentMid); double dist = Math.Min(mx - mn, mn + bundleBase.ParameterSpan - mx); if (bundleBase.CurveCenter == bundleBase.Position || bundleBase.IsParent) { return(25 * dist * dist); } else { return(500 * dist * dist); } }
double GetBaseMiddleParamInDirection(BundleBase targetBase, Point sPos, Point neighbPos) { var curve = targetBase.Curve; var circle = curve as Ellipse; if (circle != null && circle.IsArc()) { return(Point.Angle(circle.AxisA, neighbPos - sPos)); } var intersections = Curve.GetAllIntersections(curve, new LineSegment(sPos, neighbPos), true); foreach (var intersectionInfo in intersections) { var xP = intersectionInfo.IntersectionPoint; if ((xP - sPos) * (xP - neighbPos) <= 0) { return(intersectionInfo.Par0); } } throw new InvalidOperationException(); }
internal BundleInfo(BundleBase sourceBase, BundleBase targetBase, Set <Polyline> obstaclesToIgnore, double edgeSeparation, double[] halfWidthArray) { SourceBase = sourceBase; TargetBase = targetBase; this.obstaclesToIgnore = obstaclesToIgnore; EdgeSeparation = edgeSeparation; HalfWidthArray = halfWidthArray; TotalRequiredWidth = EdgeSeparation * HalfWidthArray.Length + HalfWidthArray.Sum() * 2; longEnoughSideLength = new Rectangle(sourceBase.Curve.BoundingBox, targetBase.Curve.BoundingBox).Diagonal; //sometimes TotalRequiredWidth is too large to fit into the circle, so we evenly scale everything double mn = Math.Max(sourceBase.Curve.BoundingBox.Diagonal, targetBase.Curve.BoundingBox.Diagonal); if (TotalRequiredWidth > mn) { double scale = TotalRequiredWidth / mn; for (int i = 0; i < HalfWidthArray.Length; i++) { HalfWidthArray[i] /= scale; } TotalRequiredWidth /= scale; EdgeSeparation /= scale; } }
internal bool Intersect(BundleBase other) { return(Intersect(parRight, parLeft, other.parRight, other.parLeft)); }
double SeparationCostForAdjacentBundleBases(BundleBase base0, BundleBase base1) { Debug.Assert(base0.Curve == base1.Curve); ICurve boundaryCurve = base0.Curve; double len = IntervalsOverlapLength(base0.ParRight, base0.ParLeft, base1.ParRight, base1.ParLeft, boundaryCurve); double mn = Math.Min(base0.Span, base1.Span); Debug.Assert(ApproximateComparer.LessOrEqual(len, mn)); Debug.Assert(mn > 0); return Math.Exp(len / mn * 10) - 1; }
bool FanEdgesOfHubSegment(BundleBase bundleHub) { bool ret = false; for (int i = 0; i < bundleHub.Count - 1; i++) ret |= FanCouple(bundleHub, i, bundleHub.CurveCenter, bundleHub.Curve.BoundingBox.Diagonal / 2); return ret; }
double GetAssymetryCostForBase(BundleBase bundleBase) { if (bundleBase.BelongsToRealNode) return 0; double assymetryWeight = bundleBase.OppositeBase.BelongsToRealNode ? 200 : 500; double cost = 0; foreach (var o in bundleBase.OrientedHubSegments) { int i0 = o.Index; int i1 = o.Other.Index; var a = bundleBase.Points[i0]; var ta = bundleBase.Tangents[i0]; var oppositeBase = o.Other.BundleBase; var b = oppositeBase.Points[i1]; var tb = oppositeBase.Tangents[i1]; double s = bundleBase.Count + oppositeBase.Count; cost += GetAssymetryCostOnData(a, ta, b, tb, assymetryWeight) / s; } return cost; }
double SeparationCostForBundleBase(BundleBase bBase) { if (bBase.Prev == null) return 0; return SeparationCostForAdjacentBundleBases(bBase, bBase.Prev) + SeparationCostForAdjacentBundleBases(bBase, bBase.Next); }
double CenterCost(BundleBase bundleBase) { if (!bundleBase.BelongsToRealNode) return 0; double currentMid = bundleBase.ParMid; double mn = Math.Min(bundleBase.InitialMidParameter, currentMid); double mx = Math.Max(bundleBase.InitialMidParameter, currentMid); double dist = Math.Min(mx - mn, mn + bundleBase.ParameterSpan - mx); if (bundleBase.CurveCenter == bundleBase.Position || bundleBase.IsParent) return 25 * dist * dist; else return 500 * dist * dist; }
void ShrinkBasesToMakeTwoConsecutiveNeighborsHappy(BundleBase rBase, BundleBase lBase) { if (!rBase.Intersect(lBase)) return; //segments are now [l1..r1] and [l2..r2] double l1 = rBase.ParRight; double r1 = rBase.ParLeft; double l2 = lBase.ParRight; double r2 = lBase.ParLeft; double span = lBase.ParameterSpan; //make them regular if (l1 > r1) l1 -= span; if (l2 > r2) l2 -= span; //make them intersecting if (l2 > r1) { l2 -= span; r2 -= span; } if (l1 > r2) { l1 -= span; r1 -= span; } //they do intersect! Debug.Assert(!(l2 >= r1) && !(l1 >= r2)); double t = RegularCut(l1, r1, l2, r2, rBase.Span, lBase.Span); TriangleOrientation to = Point.GetTriangleOrientation(lBase.CurveCenter, lBase.OppositeBase.InitialMidPoint, rBase.OppositeBase.InitialMidPoint); if (to == TriangleOrientation.Clockwise) { r1 = t; l2 = t; } else if (to == TriangleOrientation.Counterclockwise) { r2 = t; l1 = t; } else { if (r2 - l1 >= r1 - l2) { r1 = t; l2 = t; } else { r2 = t; l1 = t; } } Debug.Assert(!rBase.Intersect(l1, r1, l2, r2)); lBase.ParRight = lBase.AdjustParam(l2); lBase.ParLeft = lBase.AdjustParam(r2); rBase.ParRight = rBase.AdjustParam(l1); rBase.ParLeft = rBase.AdjustParam(r1); }
double GetBaseMiddleParamInDirection(BundleBase targetBase, Point sPos, Point neighbPos) { var curve = targetBase.Curve; var circle = curve as Ellipse; if (circle != null && circle.IsArc()) return Point.Angle(circle.AxisA, neighbPos - sPos); var intersections = Curve.GetAllIntersections(curve, new LineSegment(sPos, neighbPos), true); foreach (var intersectionInfo in intersections) { var xP = intersectionInfo.IntersectionPoint; if ((xP - sPos) * (xP - neighbPos) <= 0) { return intersectionInfo.Par0; } } throw new InvalidOperationException(); }
void ShrinkBasesToMakeTwoConsecutiveNeighborsHappy(BundleBase rBase, BundleBase lBase) { if (!rBase.Intersect(lBase)) { return; } //segments are now [l1..r1] and [l2..r2] double l1 = rBase.ParRight; double r1 = rBase.ParLeft; double l2 = lBase.ParRight; double r2 = lBase.ParLeft; double span = lBase.ParameterSpan; //make them regular if (l1 > r1) { l1 -= span; } if (l2 > r2) { l2 -= span; } //make them intersecting if (l2 > r1) { l2 -= span; r2 -= span; } if (l1 > r2) { l1 -= span; r1 -= span; } //they do intersect! Debug.Assert(!(l2 >= r1) && !(l1 >= r2)); double t = RegularCut(l1, r1, l2, r2, rBase.Span, lBase.Span); TriangleOrientation to = Point.GetTriangleOrientation(lBase.CurveCenter, lBase.OppositeBase.InitialMidPoint, rBase.OppositeBase.InitialMidPoint); if (to == TriangleOrientation.Clockwise) { r1 = t; l2 = t; } else if (to == TriangleOrientation.Counterclockwise) { r2 = t; l1 = t; } else { if (r2 - l1 >= r1 - l2) { r1 = t; l2 = t; } else { r2 = t; l1 = t; } } Debug.Assert(!rBase.Intersect(l1, r1, l2, r2)); lBase.ParRight = lBase.AdjustParam(l2); lBase.ParLeft = lBase.AdjustParam(r2); rBase.ParRight = rBase.AdjustParam(l1); rBase.ParLeft = rBase.AdjustParam(r1); }
/// <summary> /// fans the couple i,i+1 /// </summary> bool FanCouple(BundleBase bundleHub, int i, Point center, double radius) { OrientedHubSegment lSeg = bundleHub.OrientedHubSegments[i]; OrientedHubSegment rSeg = bundleHub.OrientedHubSegments[i + 1]; if (lSeg == null) return false; Point x; if (LineSegment.Intersect(lSeg.Segment.Start, lSeg.Segment.End, rSeg.Segment.Start, rSeg.Segment.End, out x)) return false; //it doesn not make sense to push these segs apart if (Point.GetTriangleOrientation(lSeg[0], lSeg[0.5], lSeg[1]) != Point.GetTriangleOrientation(rSeg[0], rSeg[0.5], rSeg[1])) return false; double ll = BaseLength(lSeg); double rl = BaseLength(rSeg); if (Math.Abs(ll - rl) < ApproximateComparer.IntersectionEpsilon) return false; if (ll > rl) return AdjustLongerSeg(lSeg, rSeg, center, radius); return AdjustLongerSeg(rSeg, lSeg, center, radius); /* var del0 = lSeg.Start - rSeg.Start; var del1 = lSeg.End - rSeg.End; var desiredDelta = Math.Min(del0, del1); var leftMiddle = lSeg[0.5]; var rightMiddle = rSeg[0.5]; if ((leftMiddle - rightMiddle).Length >= desiredDelta - ApproximateComparer.DistanceEpsilon) return false; var leftMiddleToCenter = (leftMiddle - BundleHub.Vertex).Length; var rightMiddleToCenter = (rightMiddle - BundleHub.Vertex).Length; if (leftMiddleToCenter > rightMiddleToCenter) { if (MoveSegToDesiredDistance(rightMiddle, lSeg, desiredDelta)) return true; } else if (MoveSegToDesiredDistance(leftMiddle, rSeg, desiredDelta)) return true; return false;*/ }
internal bool Intersect(BundleBase other) { return Intersect(parRight, parLeft, other.parRight, other.parLeft); }