///<summary>Positions a line control to match a line segment.</summary>
 public static void Reposition(this Line lineControl, LineSegment position)
 {
     lineControl.X1 = position.Start.X;
     lineControl.Y1 = position.Start.Y;
     lineControl.X2 = position.End.X;
     lineControl.Y2 = position.End.Y;
 }
 public static double DistanceFrom(this Point p, LineSegment line)
 {
     var s = p.LerpProjectOnto(line);
     if (s < 0) return p.DistanceFrom(line.Start);
     if (s > 1) return p.DistanceFrom(line.End);
     return p.DistanceFrom(line.LerpAcross(s));
 }
        public static IntersectionParameters? LineDefinedByMovingEndPointsCrossesOrigin(LineSegment endPath1,
                                                                            LineSegment endPath2,
                                                                            Point origin = default(Point)) {
            var b = endPath1.Start - origin;
            var d = endPath2.Start - endPath1.Start;
            var db = endPath1.Delta;
            var dd = endPath2.Delta - endPath1.Delta;

            return (from t in QuadraticRoots(dd.Cross(db),
                                             d.Cross(db) + dd.Cross(b),
                                             d.Cross(b))
                    where t >= 0 && t <= 1
                    let p0 = endPath1.LerpAcross(t)
                    let p1 = endPath2.LerpAcross(t)
                    let s = origin.LerpProjectOnto(p0.To(p1))
                    where s >= 0 && s <= 1
                    select (IntersectionParameters?)new IntersectionParameters {T = t, S = s}
                   ).FirstOrDefault();
        }
        public static Animation Animate(Lifetime life)
        {
            var animation = new Animation();

            var state = Ani.Anon(dt => {
                var t = dt.TotalSeconds;
                var x = Math.Cos(t)*100 + 150;
                var y = Math.Sin(t)*100 + 150;
                var vx = Math.Cos(3*t);
                var vy = Math.Sin(3*t);

                var c = new Point(x, y);
                var v = new Vector(vx, vy);
                var r = 20;
                var li = new LineSegment(new Point(150 - 89, 150 - 89), new Point(150 + 50, 150 + 20));
                var h = GeometryUtilities.WhenMovingCircleWillIntersectLineSegment(c, r, v, li);

                return new {c, r, v, li, h};
            });

            animation.Things.Add(new LineSegmentDesc(state.Select(e => e.li)), life);
            animation.Things.Add(
                new PointDesc(
                    state.Select(e => e.c),
                    Brushes.Black,
                    Brushes.Gray,
                    state.Select(e => (double)e.r),
                    1.0),
                life);
            animation.Things.Add(new LineSegmentDesc(state.Select(e => e.c.Sweep(e.v * 1000)), Brushes.Red), life);
            animation.Things.Add(new LineSegmentDesc(state.Select(e => e.c.Sweep(e.v * 1000) + e.v.Perp() * e.r), Brushes.LightGray), life);
            animation.Things.Add(new LineSegmentDesc(state.Select(e => e.c.Sweep(e.v * 1000) - e.v.Perp() * e.r), Brushes.LightGray), life);
            animation.Things.Add(
                new PointDesc(
                    state.Select(e => (e.c + e.v * e.h) ?? new Point(-10000, -10000)),
                    Brushes.Gray,
                    Brushes.LightGray,
                    state.Select(e => (double)e.r),
                    1.0),
                life);

            return animation;
        }
        private static LineSegment? ClipLine(LineSegment line, Rect rect)
        {
            var left = new Point(rect.Left, rect.Top - 100000).To(new Point(rect.Left, rect.Bottom + 100000));
            var right = new Point(rect.Right, rect.Top - 100000).To(new Point(rect.Right, rect.Bottom + 100000));
            var top = new Point(rect.Left - 100000, rect.Top).To(new Point(rect.Right + 100000, rect.Top));
            var bottom = new Point(rect.Left - 100000, rect.Bottom).To(new Point(rect.Right + 100000, rect.Bottom));

            var p1 = line.TryIntersectionPointInside(left);
            var p2 = line.TryIntersectionPointInside(right);
            var p3 = line.TryIntersectionPointInside(top);
            var p4 = line.TryIntersectionPointInside(bottom);

            var es = new[] {line.Start, line.End};

            if (p1.HasValue) return ClipLine(p1.Value.To(es.MayMaxBy(e => e.X).ForceGetValue()), rect);
            if (p2.HasValue) return ClipLine(p2.Value.To(es.MayMaxBy(e => -e.X).ForceGetValue()), rect);
            if (p3.HasValue) return ClipLine(p3.Value.To(es.MayMaxBy(e => e.Y).ForceGetValue()), rect);
            if (p4.HasValue) return ClipLine(p4.Value.To(es.MayMaxBy(e => -e.Y).ForceGetValue()), rect);

            if (!rect.Contains(line.Mid)) return null;
            return line;
        }
 ///<summary>The point on a line segment that is closest to a target point.</summary>
 public static Point ClosestPointOn(this Point point, LineSegment line) {
     return line.Start + line.Delta.Normal() * (point - line.Start).ScalarProjectOnto(line.Delta).Clamp(0, line.Delta.Length);
 }
        ///<summary>Handles cutting connectors with a moving kill point.</summary>
        public static void SetupMouseCutter(this Game game,
                                            PerishableCollection<UIElement> controls,
                                            Func<Point?> liveMousePosition,
                                            double cutTolerance) {
            var lastUsedMousePos = liveMousePosition();
            game.LoopActions.Add(
                step => {
                    // get a path between last and current mouse positions, if any
                    var prev = lastUsedMousePos;
                    var cur = liveMousePosition();
                    lastUsedMousePos = cur;
                    if (!prev.HasValue || !cur.HasValue) return;
                    var cutPath = new LineSegment(prev.Value, cur.Value);

                    // cut any connectors that crossed the cut path
                    foreach (var cut in from e in game.Connectors.CurrentItems()
                                        let connector = e.Value
                                        let endPath1 = new LineSegment(connector.Parent.LastPos, connector.Parent.Pos)
                                        let endPath2 = new LineSegment(connector.Child.LastPos, connector.Child.Pos)
                                        let pt = cutPath.TryGetCut(endPath1, endPath2)
                                        where pt != null
                                        select new { pt, connector} ) {
                        cut.connector.CutPoint = cut.pt.Item1;
                        cut.connector.CutDir = cut.pt.Item2.Normal();
                        cut.connector.Child.Life.EndLifetime();
                    }
                },
                game.Life);
        }
 ///<summary>Positions a line control to match a line segment.</summary>
 public static void Reposition(this Line lineControl, LineSegment position) {
     lineControl.X1 = position.Start.X;
     lineControl.Y1 = position.Start.Y;
     lineControl.X2 = position.End.X;
     lineControl.Y2 = position.End.Y;
 }
 public static Point IntersectionPoint(this LineSegment line1, LineSegment line2)
 {
     var p = line1.TryIntersectionPoint(line2);
     if (p == null) throw new InvalidOperationException();
     return p.Value;
 }
        ///<summary>Returns the first non-negative time, if any, where a moving circle will intersect a fixed line segment.</summary>
        public static double? WhenMovingCircleWillIntersectLineSegment(Point center, double radius, Vector velocity, LineSegment line)
        {
            var epsilon = 0.00001;

            // use whatever's earliest and is actually touching
            return new[] {
                WhenMovingCircleWillIntersectExtendedLine(center, radius, velocity, line.Start, line.Delta),
                WhenMovingCircleWillIntersectPoint(center, radius, velocity, line.Start),
                WhenMovingCircleWillIntersectPoint(center, radius, velocity, line.End)
            }.OrderBy(e => e)
             .FirstOrDefault(t => t.HasValue && (center + velocity * t.GetValueOrDefault()).DistanceFrom(line) <= radius + epsilon);
        }
 public static Point? TryIntersectionPointInside(this LineSegment line1, LineSegment line2)
 {
     var p = TryIntersectionPoint(line1, line2);
     if (!p.HasValue) return null;
     var q = p.Value;
     if (q.DistanceFrom(line1.Start) < 0.001) return null;
     if (q.DistanceFrom(line1.End) < 0.001) return null;
     if (q.DistanceFrom(line2.Start) < 0.001) return null;
     if (q.DistanceFrom(line2.End) < 0.001) return null;
     return q;
 }
        public static Point? TryIntersectionPoint(this LineSegment line1, LineSegment line2)
        {
            if (line1.Delta.IsParallelTo(line2.Delta)) return null;

            //[solve intersection in 2d, then transform back]
            //create transformed coordinate axies [x along L1, y along L2 (with x component removed), z=0]
            var u_x = line1.Delta.Normal();
            var u_y = line2.Delta.PerpOnto(u_x).Normal();
            //transform L2's direction vector
            var u_L2_dir = new Vector(line2.Delta.ScalarProjectOnto(u_x), line2.Delta.ScalarProjectOnto(u_y));
            if (u_L2_dir.Y.Abs() < 0.001) return null;
            //get the transformed y-distance between L1 and L2's starting points, and convert it to a time
            var u_L1_L2_dy = (line1.Start - line2.Start).ScalarProjectOnto(u_y);
            var u_t = u_L1_L2_dy/u_L2_dir.Y;
            //get the transformed intersection point
            var c_2d = u_L2_dir * u_t;
            //transform intersection point back to normal coordinates
            var c_3d = line2.Start + c_2d.X * u_x + c_2d.Y * u_y;

            //check that 2d intersection is in fact a 3d intersection
            if (!line1.ContainsPoint(c_3d)) return null;
            if (!line2.ContainsPoint(c_3d)) return null;
            return c_3d;
        }