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), }; }
public static Ani<Rect> SweepRightAndDown(this Ani<Point> point, Ani<Size> size) { return size.Combine(point, (s, p) => new Rect(p, s)); }
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 ShowComplexSum(Ani<Brush> fill1, Ani<Brush> fill2, Ani<Brush> valueStroke, IReadOnlyList<Ani<Complex>> values, Ani<Point> position, Ani<Point> target, Ani<Vector> positionDelta, Ani<double> unitRadius, Ani<double> time) { var sum = (Ani < Complex > )Complex.Zero; var animation = new Animation(); foreach (var i in values.Count.Range()) { animation.Things.Add( ShowComplex( fill1.Combine(time, (s, t) => s.LerpToTransparent(t.SmoothTransition(0, 2, 1, 1, 1))), valueStroke.Combine(time, (s, t) => s.LerpToTransparent(t.SmoothTransition(0, 0.1, 0.1, 1, 1))), values[i], position.Combine(positionDelta, time, sum, unitRadius, target, (p, d, t, s, u, p2) => (p + d*i).LerpTo(p2 + new Vector(s.Real*u, s.Imaginary*-u), t.SmoothTransition(0, 0, 1, 1, 1))), unitRadius, valueStroke.Combine(time, (s, t) => s.LerpToTransparent(t.SmoothTransition(0, 1, 1, 1, 1))), sweepFill: fill1.Combine(time, (s, t) => s.LerpToTransparent(t.SmoothTransition(0, 0, 1, 1, 1)))), Lifetime.Immortal); sum = sum.Combine(values[i], (s, v) => s + v); } animation.Things.Add( ShowComplex( fill2.Combine(time, (s, t) => s.LerpToTransparent(t.SmoothTransition(1, 1, 1, 1,0, 0))), valueStroke.Combine(time, (s, t) => s.LerpToTransparent(t.SmoothTransition(1, 1, 1,0, 0, 0))), sum, target, unitRadius, valueStroke.Combine(time, (s, t) => s.LerpToTransparent(t.SmoothTransition(1, 1, 1,0, 0, 0))), sweepFill: fill2.Combine(time, (s, t) => s.LerpToTransparent(t.SmoothTransition(1, 1, 1, 0, 0)))), Lifetime.Immortal); return animation; }
private static Animation ShowComplexProduct(Ani<Brush> fill1, Ani<Brush> fill2, Ani<Brush> valueStroke1, Ani<Brush> valueStroke2, Ani<Complex> value1, Ani<Complex> value2, Ani<Point> position, Ani<double> unitRadius, Ani<double> time, Ani<Brush> fill3 = null) { if (fill1 == null) throw new ArgumentNullException("fill1"); if (fill2 == null) throw new ArgumentNullException("fill2"); if (valueStroke1 == null) throw new ArgumentNullException("valueStroke1"); if (valueStroke2 == null) throw new ArgumentNullException("valueStroke2"); if (value1 == null) throw new ArgumentNullException("value1"); if (value2 == null) throw new ArgumentNullException("value2"); if (position == null) throw new ArgumentNullException("position"); if (unitRadius == null) throw new ArgumentNullException("unitRadius"); if (time == null) throw new ArgumentNullException("time"); fill3 = fill3 ?? fill1; return new Animation { // vertical input ShowComplex(fill1.Combine(time, (s, t) => s.LerpToTransparent(t.LerpTransition(0, 1, 1, 1, 1))), valueStroke1, time.Combine(value1, value2, (t, v1, v2) => Complex.FromPolarCoordinates( v1.Magnitude.LerpTo(v1.Magnitude*v2.Magnitude, t.SmoothTransition(0, 0, 1, 1, 1)), v1.Phase)), position, unitRadius, rotation: Turn.FromNaturalAngle(Math.PI/2), phaseOffset: time.Combine(value2, (t, v) => t.SmoothTransition(0, 0, 0, 1, 1)*v.Phase.ProperMod(Math.PI*2)), sweepFill: fill1.Combine(time, (f, t) => f.LerpToTransparent(t.SmoothTransition(0, 0, 0, 0, 1))), valueGuideStroke: Brushes.Transparent), // horizontal input ShowComplex(fill2.Combine(time, (s, t) => s.LerpToTransparent(t.LerpTransition(0, 1, 1, 1, 1))), valueStroke2.Combine(time, (s, t) => s.LerpToTransparent(t.LerpTransition(0, 0, 0, 0, 1))), time.Combine(value1, value2, (t, v1, v2) => Complex.FromPolarCoordinates( v2.Magnitude.LerpTo(v1.Magnitude*v2.Magnitude, t.SmoothTransition(0, 0, 1, 1, 1)), v2.Phase)), position, unitRadius, sweepFill: fill2.Combine(time, (s, t) => s.LerpToTransparent(t.LerpTransition(0, 0, 0, 0, 1))), valueGuideStroke: Brushes.Transparent), // result ShowComplex(fill3.Combine(time, (s, t) => s.LerpToTransparent(t.LerpTransition(1, 0, 0, 0, 0))), Brushes.Transparent, time.Combine(value1, value2, (t, v1, v2) => Complex.FromPolarCoordinates( v1.Magnitude < 0.001 ? 0 : v2.Magnitude.LerpTo(v1.Magnitude*v2.Magnitude, t.SmoothTransition(0, 0, 1, 1, 1)), v2.Phase + v1.Phase)), position, unitRadius, valueGuideStroke: valueStroke1, sweepFill: fill3.Combine(time, (f, t) => f.LerpToTransparent(t.SmoothTransition(1, 1, 1, 1, 0))), squish: time.Combine(value1, (t, v) => Math.Pow(t.SmoothTransition(v.Magnitude, v.Magnitude, 1, 1, 1), 2))) }; }
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)) }; }