///<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); }
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; }