private static Animation MakeLinkAnimation(Ani<Rect> src, Ani<Rect?> dst) { var p1 = src.Select(e => new Point(e.Right, e.Top.LerpTo(e.Bottom, 0.5))); var p4 = src.Combine(dst, (s, d) => { var cy = d.GetValueOrDefault().Top.LerpTo(d.GetValueOrDefault().Bottom, 0.5); var sy = s.Top.LerpTo(s.Bottom, 0.5); return new Point( d.GetValueOrDefault().Right, cy + (sy-cy)/10); }); var xmax = p1.Combine(p4, (e1, e2) => e1.X.Max(e2.X) + 10); var p2 = p1.Combine(xmax, (e, x) => new Point(x, e.Y)); var p3 = p4.Combine(xmax, (e, x) => new Point(x, e.Y)); var p3a = p4.Select(e => e + new Vector(2, 2)); var p3b = p4.Select(e => e + new Vector(2, -2)); var stroke = dst.Select(e => e.HasValue ? (Brush)Brushes.Black : Brushes.Transparent); return new Animation { new LineSegmentDesc(p1.Combine(p2, (e1, e2) => e1.To(e2)), stroke: stroke), new LineSegmentDesc(p2.Combine(p3, (e1, e2) => e1.To(e2)), stroke: stroke), new LineSegmentDesc(p3.Combine(p4, (e1, e2) => e1.To(e2)), stroke: stroke), new LineSegmentDesc(p3a.Combine(p4, (e1, e2) => e1.To(e2)), stroke: stroke), new LineSegmentDesc(p3b.Combine(p4, (e1, e2) => e1.To(e2)), stroke: stroke), }; }
private static Animation Arrow(Ani<Point> start, Ani<Vector> delta, Ani<Brush> stroke = null, Ani<double> thickness = null, Ani<double> wedgeLength = null, Ani<double> dashed = null) { wedgeLength = wedgeLength ?? 5; thickness = thickness ?? 1; return new Animation { new PointDesc(start, fill: stroke, radius: thickness.Select(e => e*0.5)), new LineSegmentDesc( pos: start.Combine(delta, (p, d) => new LineSegment(p, d)), stroke: stroke, thickness: thickness, dashed: dashed), new[] {+1, -1}.Select( s => new LineSegmentDesc( pos: start.Combine( delta, wedgeLength, (p, d, w) => new LineSegment(p + d, (s*d.Perp().Normal() - d.Normal()).Normal()*Math.Min(w, d.Length/2))), stroke: stroke, thickness: thickness, dashed: dashed)) }; }
public static Animation ShowComplex(Ani<Brush> fill, Ani<Brush> valueStroke, Ani<Complex> value, Ani<Point> position, Ani<double> unitRadius, Ani<Brush> valueGuideStroke = null, Ani<double> phaseOffset = null, Ani<Turn> rotation = null, Ani<Brush> sweepFill = null, Ani<double> squish = null, Ani<double> sweepScale = null) { phaseOffset = phaseOffset ?? 0; rotation = rotation ?? Turn.Zero; sweepFill = sweepFill ?? fill; squish = squish ?? 1.0; sweepScale = sweepScale ?? 0.5; var phaseRadius = from v in value from s in sweepScale select s * Math.Max(0.05, v.Magnitude); var mag = value.Select(e => e.Magnitude*e.Magnitude); return new Animation { new PolygonDesc( phaseOffset.Combine(value, position, unitRadius, phaseRadius, (o, v, p, r, f) => PhaseCurve(v.Phase, f * r, o).Select(e => e + p).ToArray().AsEnumerable()), stroke: sweepFill.Select(e => e.LerpTo(Brushes.Black, 0.5, lerpAlpha: false)), fill: sweepFill.Select(e => e.LerpToTransparent(0.5)), strokeThickness: value.Select(e => Math.Min(Math.Abs(e.Phase)*10,Math.Min(e.Magnitude*3,1)))), new RectDesc( pos: position.Combine(unitRadius, (e, r) => new Rect(e.X - r, e.Y - r, 2*r, 2*r)), stroke: valueGuideStroke ?? valueStroke, strokeThickness: 0.5, dashed: 4, rotation: rotation, rotationOrigin: new Point(0.5, 0.5)), // squared magnitude filling new RectDesc( pos: position.Combine(unitRadius, mag, squish, (e, r, v, s) => new Rect(e.X - r, e.Y + (1 - 2 * v) * r, 2 * r * s, 2 * r * v)), fill: fill, rotation: rotation, rotationOrigin: mag.Combine(squish, (v,s) => new Point(s < 0.001 ? 0 : (1/s)/2, 1 - 0.5/v))), // current value arrow Arrow(position, value.Combine(phaseOffset, (e, p) => e*Complex.Exp(Complex.ImaginaryOne*p)) .Combine(unitRadius, (v, r) => r*new Vector(v.Real, -v.Imaginary)), stroke: valueStroke) }; }
private static Animation CreateNetworkAnimation(Animation animation, Ani<GraphMessages> state, Lifetime life) { // --- end points // timeline animation.LinkMany( state.Select(s => s.Graph.EndPoints.Select( p => new LineSegmentDesc( new Point(s.Graph.GetX(p.Skew), s.Graph.GetY(p)).Sweep(new Vector(1000, 0)), Brushes.Black, 3))), life); // timeline label animation.LinkMany( state.Select(s => s.Graph.EndPoints.Select( p => new TextDesc( text: p.Name, pos: new Point(s.Graph.GetX(p.Skew), s.Graph.GetY(p)), fontWeight: FontWeights.Bold, reference: new Point(1.1, 0.5)))), life); // tick marks animation.LinkMany( state.Select(s => s.Graph.EndPoints.SelectMany( p => 10.Range().Select( j => new LineSegmentDesc( new Point(s.Graph.GetX(p.Skew + j.Seconds()), s.Graph.GetY(p) - 5).Sweep(new Vector(0, 10)), Brushes.Black, 2)))), life); // tick labels animation.LinkMany( state.Select(s => s.Graph.EndPoints.SelectMany( p => 10.Range().Select( j => new TextDesc( text: (j + "s"), pos: new Point(s.Graph.GetX(p.Skew + j.Seconds()), s.Graph.GetY(p) - 5) + new Vector(0, -2), fontSize: 10)))), life); // --- measurements var measurements = from s in state select from m in s.Measurements let x1 = s.Graph.GetX(m.X1) let y1 = s.Graph.GetY(m.V1) let x2 = s.Graph.GetX(m.X2) let y2 = s.Graph.GetY(m.V2) let y = m.Y ?? ((y1 + y2)/2) select new {s, m, x1, y1, x2, y2, y}; animation.LinkMany( measurements.LiftSelect( e => new LineSegmentDesc( new LineSegment(new Point(e.x1, e.y), new Point(e.x2, e.y)), Brushes.Black, 2, 1)), life); animation.LinkMany( measurements.LiftSelect( e => new LineSegmentDesc( new LineSegment(new Point(e.x1, e.y1), new Point(e.x1, e.y)), Brushes.Red, dashed: 1)), life); animation.LinkMany( measurements.LiftSelect( e => new LineSegmentDesc( new LineSegment(new Point(e.x2, e.y2), new Point(e.x2, e.y)), Brushes.Red, dashed: 1)), life); var off1 = new Vector(5, -5); var off2 = new Vector(5, 12); animation.LinkMany( measurements.LiftSelect( e => new TextDesc( text: e.m.Text, pos: new Point(Math.Max(e.x1, e.x2), e.y) + off1.Rotate(e.m.Angle), direction: e.m.Angle)), life); animation.LinkMany( measurements.LiftSelect( e => new TextDesc( text: string.Format("{0:0.00}s", (e.m.X2 - e.m.X1).TotalSeconds), pos: new Point(Math.Max(e.x1, e.x2), e.y) + off2.Rotate(e.m.Angle), direction: e.m.Angle)), life); // messages animation.LinkMany( state.Select(e => e.Messages).LiftSelect( e => new LineSegmentDesc( e.Pos, Brushes.Black)), life); animation.LinkMany( state.Select(e => e.Messages.SelectMany( m => new[] {m.PosSourcePoint, m.PosEndPoint}.Select( p => new PointDesc( p, Brushes.Transparent, Brushes.Black, 3, 0)))), life); animation.LinkMany( state.Select(e => e.Messages).LiftSelect( e => new TextDesc( text: "-> " + e.Text + " ->", pos: e.Pos.LerpAcross(0.1), fontSize: 10, direction: Dir.FromVector(e.Pos.Delta.X, e.Pos.Delta.Y), foreground: Brushes.Gray)), life); return animation; }