using System; using System.Collections.Generic; using System.Linq; using Microsoft.Msagl.Core.Geometry; using Microsoft.Msagl.Core.Geometry.Curves; using System.Diagnostics; namespace Microsoft.Msagl.Routing.Spline.Bundling { internal class BundleBase { /// /// only one of those is not null /// internal BundleInfo OutgoingBundleInfo; internal BundleInfo IncomingBundleInfo; readonly Point[] points; readonly Point[] tangents; internal OrientedHubSegment[] OrientedHubSegments; /// /// boundary of cluster or hub containing this base /// internal ICurve Curve; /// /// this bundle base sits on a cluster boundary and the opposite base sits on a child of the cluster /// internal bool IsParent; /// /// if true then the base sits on a real node or cluster, otherwise it belongs to an intermediate hub /// internal bool BelongsToRealNode; /// /// position of the station containing the base /// (could be a center of a hub, or a point on the boundary of a cluster) /// internal Point Position; //need for debug only internal int stationIndex; /// /// constructor /// internal BundleBase(int count, ICurve boundaryCurve, Point position, bool belongsToRealNode, int stationIndex) { BelongsToRealNode = belongsToRealNode; Curve = boundaryCurve; Position = position; this.stationIndex = stationIndex; points = new Point[count]; tangents = new Point[count]; OrientedHubSegments = new OrientedHubSegment[count]; ParameterSpan = Curve.ParEnd - Curve.ParStart; } internal Point CurveCenter { get { return Curve.BoundingBox.Center; } } internal BundleBase OppositeBase { get { return OutgoingBundleInfo != null ? OutgoingBundleInfo.TargetBase : IncomingBundleInfo.SourceBase; } } internal int Count { get { return points.Length; } } internal Point[] Points { get { return points; } } internal Point[] Tangents { get { return tangents; } } double initialMidParameter; internal double InitialMidParameter { get { return initialMidParameter; } set { initialMidParameter = value; InitialMidPoint = Curve[value]; } } internal Point InitialMidPoint { get; set; } double parRight; /// /// corresponds to the left point of the base /// internal double ParRight { get { return parRight; } set { parRight = value; RightPoint = Curve[parRight]; } } double parLeft; /// /// corresponds to the right point of the base /// internal double ParLeft { get { return parLeft; } set { parLeft = value; LeftPoint = Curve[parLeft]; } } internal double ParMid { get { return (parRight + parLeft) / 2; } } internal Point RightPoint { get; set; } internal Point LeftPoint { get; set; } internal Point MidPoint { get { return (RightPoint + LeftPoint) / 2; } } //previous in ccw order internal BundleBase Prev; //next in ccw order internal BundleBase Next; internal double ParameterSpan; internal double Span { get { return SpanBetweenTwoPoints(parRight, parLeft); } } internal double SpanBetweenTwoPoints(double right, double left) { return (right <= left ? left - right : left - right + ParameterSpan); } internal Point RotateLeftPoint(int rotationOfSourceLeftPoint, double parameterChange) { if (rotationOfSourceLeftPoint == 0) return LeftPoint; return RotatePoint(rotationOfSourceLeftPoint, parLeft, parameterChange); } internal Point RotateRigthPoint(int rotationOfSourceRightPoint, double parameterChange) { if (rotationOfSourceRightPoint == 0) return RightPoint; return RotatePoint(rotationOfSourceRightPoint, parRight, parameterChange); } Point RotatePoint(int rotation, double t, double parameterChange) { double change = ParameterSpan * parameterChange; t = t + rotation * change; t = AdjustParam(t); return Curve[t]; } internal double AdjustParam(double t) { if (t > Curve.ParEnd) t = Curve.ParStart + (t - Curve.ParEnd); else if (t < Curve.ParStart) t = Curve.ParEnd - (Curve.ParStart - t); return t; } internal void RotateBy(int rotationOfRightPoint, int rotationOfLeftPoint, double parameterChange) { double change = ParameterSpan * parameterChange; if (rotationOfRightPoint != 0) ParRight = AdjustParam(ParRight + rotationOfRightPoint * change); if (rotationOfLeftPoint != 0) ParLeft = AdjustParam(ParLeft + rotationOfLeftPoint * change); } internal bool Intersect(BundleBase other) { return Intersect(parRight, parLeft, other.parRight, other.parLeft); } internal bool Intersect(double lParRight, double lParLeft, double rParRight, double rParLeft) { if (lParRight > lParLeft) return Intersect(lParRight, Curve.ParEnd, rParRight, rParLeft) || Intersect(Curve.ParStart, lParLeft, rParRight, rParLeft); if (rParRight > rParLeft) return Intersect(lParRight, lParLeft, rParRight, Curve.ParEnd) || Intersect(lParRight, lParLeft, Curve.ParStart, rParLeft); Debug.Assert(lParRight <= lParLeft); Debug.Assert(rParRight <= rParLeft); if (ApproximateComparer.LessOrEqual(lParLeft, rParRight)) return false; if (ApproximateComparer.LessOrEqual(rParLeft, lParRight)) return false; return true; } internal bool RelativeOrderOfBasesIsPreserved(int rotationOfRightPoint, int rotationOfLeftPoint, double parameterChange) { double change = ParameterSpan * parameterChange; //we do not swap parRight and parLeft double rnew = parRight + rotationOfRightPoint * change; double lnew = (parRight < parLeft ? parLeft + rotationOfLeftPoint * change : parLeft + ParameterSpan + rotationOfLeftPoint * change); if (rnew > lnew) return false; //span could not be greater than pi if (SpanBetweenTwoPoints(rnew, lnew) > ParameterSpan / 2.0) return false; //the base is the only base in the hub if (Prev == null) return true; //distance between mid points is larger than parameterChange => we can't change the order if (SpanBetweenTwoPoints(Prev.ParMid, ParMid) > change && SpanBetweenTwoPoints(ParMid, Next.ParMid) > change) return true; Point rSoP = RotateLeftPoint(rotationOfLeftPoint, parameterChange); Point lSoP = RotateRigthPoint(rotationOfRightPoint, parameterChange); Point newMidPoint = (rSoP + lSoP) / 2.0; Point curMidPoint = MidPoint; //check Prev if (Point.GetTriangleOrientation(CurveCenter, Prev.MidPoint, curMidPoint) != Point.GetTriangleOrientation(CurveCenter, Prev.MidPoint, newMidPoint)) return false; //Next if (Point.GetTriangleOrientation(CurveCenter, Next.MidPoint, curMidPoint) != Point.GetTriangleOrientation(CurveCenter, Next.MidPoint, newMidPoint)) return false; return true; } } }