public static Animation CreateDoubleCheckedLockingAnimation() { var ani = new Animation(); var address1 = 0x100; var t = ani.Periodic(8.Seconds()); var n = 14.0; var t_writeP1 = 1/n; var t_writeX = 2/n; var t_writeY = 3/n; var t_writeS = 4/n; var t_cacheY = 5/n; var t_cacheX = 6/n; var t_cacheS = 7/n; var t_uncacheS = 8 / n; var t_checkS = 9 / n; var t_readX = 10/n; var t_garbage = 11/n; var thread1Value = t.Proper.Select(e => e < t_writeX ? new MemoryPoint { X = May<int>.NoValue, Y = May<int>.NoValue } : e < t_writeY ? new MemoryPoint { X = 1, Y = May<int>.NoValue } : new MemoryPoint { X = 1, Y = 1 }); var cacheValue = t.Proper.Select(e => e < t_cacheY ? new MemoryPoint { X = May<int>.NoValue, Y = May<int>.NoValue } : e < t_cacheX ? new MemoryPoint { X = May<int>.NoValue, Y = 1 } : new MemoryPoint { X = 1, Y = 1 }); var thread2Value = t.Proper.Select(e => new MemoryPoint { X = May<int>.NoValue, Y = May<int>.NoValue }); var sideMargin = 10; var thread1X = sideMargin; var cacheX = 107; var thread2X = 210; var thread2X2 = 210+50; var rightX = thread2X2 + sideMargin*2; //var centerX = rightX / 2; //t.Add(new TextDesc( // "No Acquire ⇒ Garbage", // new Point(centerX, 5), // new Point(0.5, 0), // //fontWeight: FontWeights.SemiBold, // fontSize: 18)); var mainY = 16; var sectionLabelY = -15 + mainY; var dividerY3 = -16 + mainY; var dividerY1 = mainY; var pointerY = 15 + mainY; var valueY = 55 + mainY; var localPointerY = 110 + mainY; var resultY = 110 + mainY; var dividerY2 = 140 + mainY; var topInstructionY = 145 + mainY; var p1 = new Point(thread1X, valueY); var p2 = new Point(cacheX, valueY); var p3 = new Point(thread2X, valueY); var v1 = MakePointMemoryAnimator(address1)(thread1Value, true, p1); var v2 = MakePointMemoryAnimator(address1)(cacheValue, true, p2); var v3 = MakePointMemoryAnimator(address1)(thread2Value, true, p3); var b1 = v1.Item2.WithTopLeft(p1); var b2 = v2.Item2.WithTopLeft(p2); var b3 = v3.Item2.WithTopLeft(p3); var thread1LocalPointer = t.Proper.Combine(b1, (e, b) => e < t_writeP1 ? Tuple.Create(0, b) : Tuple.Create(address1, b)); var thread1Pointer = t.Proper.Combine(b1, (e, b) => e < t_writeS ? Tuple.Create(0, b) : Tuple.Create(address1, b)); var cachePointer = t.Proper.Combine(b2, (e, b) => e < t_cacheS ? Tuple.Create(0, b) : Tuple.Create(address1, b)); var thread2Pointer = t.Proper.Combine(b3, (e, b) => e < t_uncacheS ? Tuple.Create(0, b) : Tuple.Create(address1, b)); var thread2CopyX = t.Proper.Select( e => e < t_readX ? "-" : null); var t1LocPtrPt = new Point(thread1X, localPointerY); var t1PtrPt = new Point(thread1X, pointerY); var cachePtrPt = new Point(cacheX, pointerY); var t2PtrPt = new Point(thread2X, pointerY); var t2RetPt = new Point(thread2X, resultY); var v4 = MakePointerAnimator("p")(thread1LocalPointer, true, t1LocPtrPt); var v6 = MakePointerAnimator("s")(thread1Pointer, true, t1PtrPt); var v7 = MakePointerAnimator("s")(cachePointer, true, cachePtrPt); var v8 = MakePointerAnimator("s")(thread2Pointer, true, t2PtrPt); var v9 = MakeBoxValueAnimator("returned")(thread2CopyX, true, t2RetPt); var t1LocPtrR = v4.Item2.WithTopLeft(t1LocPtrPt); var t1PtrR = v6.Item2.WithTopLeft(t1PtrPt); var cachePtrR = v7.Item2.WithTopLeft(cachePtrPt); var t2PtrR = v8.Item2.WithTopLeft(t2PtrPt); var t2RetR = v9.Item2.WithTopLeft(t2RetPt); var times = new[] { Tuple.Create(t_writeP1, t1LocPtrR, b1, "p = new Point()", 4), Tuple.Create(t_writeX, b1, (Ani<Rect>)null, "p.X = 1", 5), Tuple.Create(t_writeY, b1, (Ani<Rect>)null, "p.Y = 1", 6), Tuple.Create(t_writeS, t1PtrR, t1LocPtrR, "s = p", 7), Tuple.Create(t_cacheY, b2, b1, "[write] s.Y", 8), Tuple.Create(t_cacheX, b2, b1, "[write] s.X", 8), Tuple.Create(t_cacheS, cachePtrR, t1PtrR, "[write] s", 8), Tuple.Create(t_uncacheS, t2PtrR, cachePtrR, "[read] s", -1), Tuple.Create(t_checkS, t2PtrR, (Ani<Rect>)null, "s != null", -1), Tuple.Create(t_readX, t2RetR, b3, "return s.X", -10), Tuple.Create(t_garbage, t2RetR, (Ani<Rect>)null, "[garbage]", 0), Tuple.Create(1.01, t2RetR, (Ani<Rect>)null, "[read] s.X", 0), Tuple.Create(1.01, t2RetR, (Ani<Rect>)null, "[read] s.Y", 0) }; //var cur1 = 4; //var cur2 = 0; foreach (var i in times.Length.Range()) { var visible = t.Proper.Select(e => { //var prevT = i == 0 ? 0 : times[i - 1].Item1; var nextT = i == times.Length - 1 ? 1.01 : times[i + 1].Item1; var curT = times[i].Item1; var fade = 0.01; if (e < curT-fade) return 0; if (e >= curT && e < nextT - fade) return 1.0; if (e > nextT) return 0; if (e < curT) return (e - curT + fade) / fade; return (nextT - e) / fade; }); var margin = 0; //if (times[i].Item5 > 0) cur1 = times[i].Item5; //if (times[i].Item5 < 0) cur2 = -times[i].Item5; //var inst1 = cur1; //var inst2 = cur2; t.Add(new RectDesc( times[i].Item2.Select(e => new Rect(e.X - margin, e.Y - margin, e.Width + margin * 2, e.Height + margin * 2)), fill: visible.Select(e => (Brush)Brushes.Red.LerpToTransparent(0.5 + 0.5 * (1 - e))))); if (times[i].Item3 != null) { var line = times[i].Item2.Combine(times[i].Item3, (r1, r2) => { var c1 = r1.TopLeft.LerpTo(r1.BottomRight, 0.5); var c2 = r2.TopLeft.LerpTo(r2.BottomRight, 0.5); return c1.LerpTo(c2, 0.1).To(c2.LerpTo(c1, 0.1)); }); t.Add( new LineSegmentDesc( line, visible.Select(e => (Brush)Brushes.Black.LerpToTransparent(1-e)), 1)); t.Add( new LineSegmentDesc( line.Select(e => e.Start.Sweep((e.Delta + e.Delta.PerpClockwise()).Normal()*10)), visible.Select(e => (Brush)Brushes.Black.LerpToTransparent(1 - e)), 1)); t.Add( new LineSegmentDesc( line.Select(e => e.Start.Sweep((e.Delta + e.Delta.PerpCounterClockwise()).Normal() * 10)), visible.Select(e => (Brush)Brushes.Black.LerpToTransparent(1 - e)), 1)); t.Add(new RectDesc( times[i].Item3.Select(e => new Rect(e.X - margin, e.Y - margin, e.Width + margin * 2, e.Height + margin * 2)), fill: visible.Select(e => (Brush)Brushes.Blue.LerpToTransparent(0.75+(1-e)*0.25)))); } var localinsty = (i <= 6 ? i : i - 7) * 15 + topInstructionY; t.Add( new TextDesc(times[i].Item4, times[i].Item2.Select(e => new Point(e.X == thread2X ? thread2X2 : thread1X, localinsty)), times[i].Item2.Select(e => new Point(e.X == thread2X ? 1 : 0, 0)), fontSize: 12, foreground: Brushes.Black)); t.Add( new RectDesc( times[i].Item2.Select(e => new Rect(e.X == thread2X ? thread2X2 - 52 : thread1X, localinsty, e.X == thread2X ? 52 : 83, 15)), fill: visible.Select(e => e > 0.5 ? (Brush)(Brushes.Black).LerpToTransparent(0.75) : Brushes.Transparent))); //if (cur1 > 0) { // t.Add(new RectDesc( // new Rect(cacheX, 200-15+inst1*15, 100, 15), // fill: visible.Select(e => e > 0.5 ? (Brush)Brushes.Red.LerpToTransparent(0.5) : Brushes.Transparent))); //} //if (cur2 > 0) { // t.Add(new RectDesc( // new Rect(cacheX, 200 - 15 + inst2 * 15, 100, 15), // fill: visible.Select(e => e > 0.5 ? (Brush)Brushes.Blue.LerpToTransparent(0.5) : Brushes.Transparent))); //} } //var codeY = 200; //var codeN = 0; //Func<Point> makeCodeP = () => new Point(cacheX, codeY + codeN++*15); t.Add( v1.Item1, v2.Item1, v3.Item1, v4.Item1, v6.Item1, v7.Item1, v8.Item1, v9.Item1, //new TextDesc("var p = s", makeCodeP(), new Point(0, 0)), //new TextDesc("if (p == nil) {", makeCodeP(), new Point(0, 0)), //new TextDesc(" lock {", makeCodeP(), new Point(0, 0)), //new TextDesc(" p = new Point()", makeCodeP(), new Point(0, 0)), //new TextDesc(" p.X = 1", makeCodeP(), new Point(0, 0)), //new TextDesc(" p.Y = 1", makeCodeP(), new Point(0, 0)), //new TextDesc(" s = p", makeCodeP(), new Point(0, 0)), //new TextDesc(" }", makeCodeP(), new Point(0, 0)), //new TextDesc("}", makeCodeP(), new Point(0, 0)), //new TextDesc("return p.X", makeCodeP(), new Point(0, 0)), new TextDesc("Producer", new Point(thread1X, sectionLabelY), new Point(0, 0), fontWeight: FontWeights.SemiBold, fontSize: 12), new TextDesc("Memory", new Point(cacheX + 28, sectionLabelY), new Point(0.5, 0), fontWeight: FontWeights.SemiBold, fontSize: 12), new TextDesc("Consumer", new Point(thread2X2, sectionLabelY), new Point(1, 0), fontWeight: FontWeights.SemiBold, fontSize: 12), new LineSegmentDesc(new Point(0, dividerY3).Sweep(new Vector(rightX, 0)), Brushes.Black, 1), new LineSegmentDesc(new Point(0, dividerY2).Sweep(new Vector(rightX, 0)), Brushes.Black, 1), new LineSegmentDesc(new Point(0, dividerY1).Sweep(new Vector(rightX, 0)), Brushes.Black, 1) ); return ani; }
private static Animation CreateVaryQSolveSlopeAnimation(Rect rect) { var size = 13; var yq = 10; var y0 = 5; var ani = new Animation { CreateGrid(size, rect) }; var dt = 50.Milliseconds(); var p = ani.Periodic(dt.Times(size - 1)); for (var xq = 1; xq < size; xq++) { var slope = size.Range().Single(s => (y0 + s * xq) % size == yq); var q = p.LimitedNewTime(dt.Times(xq - 1), dt.Times(xq)); q.Add(CreateLine(y0, slope, size, rect)); q.Add(new PointDesc(GetP(0, y0, size, rect), fill: Brushes.Red, radius: 8)); q.Add(new TextDesc(string.Format("Target = ({0}, {1}), Slope = {2}, Y-Intercept = {3}", xq, yq, slope, y0), new Point(rect.Left + 67, rect.Bottom + 5), new Point(0, 0), fontSize: 14)); q.Add(new PointDesc(GetP(xq, yq, size, rect), fill: Brushes.Blue, radius: 8)); } ani.Add(new TextDesc("Varying Target, Solving Slope", new Point(rect.Left + rect.Width / 2, rect.Top - 30), new Point(0.5, 0), fontSize: 18)); return ani; }
public static Animation ShowMatrixMultiplication(TimeSpan period, Rect pos, ComplexMatrix u, ComplexVector v, Ani<Brush> or, Ani<Brush> blu, Ani<Brush> bla) { var d = Math.Min(pos.Width/(u.Columns.Count + 2), pos.Height/u.Rows.Count)/2; pos = new Rect(pos.TopLeft, new Size(d * (u.Columns.Count + 2)*2, d*u.Rows.Count*2)); var ur = d; var animation = new Animation { new RectDesc(new Rect(pos.X + d * 2, pos.Y, pos.Width - d * 4, pos.Height), Brushes.Black, strokeThickness: 0.6, dashed: 5), new RectDesc(new Rect(pos.X, pos.Y, d * 2, pos.Height), Brushes.Black, strokeThickness: 0.6, dashed: 5), new RectDesc(new Rect(pos.Right - d*2, pos.Y, d * 2, pos.Height), Brushes.Black, strokeThickness: 0.6, dashed: 5) }; var per = animation.Periodic(period); var s0 = per.LimitedSameTime(0.Seconds(), period.DividedBy(4)); foreach (var r in u.Rows.Count.Range()) { var pp = s0.Proper; // input vector var p1 = pos.TopLeft + new Vector(d, d + r*d*2); var p2 = pos.TopLeft + new Vector(d*3 + r*d*2, 0); var p3 = pos.TopLeft + new Vector(d*3 + r*d*2, u.Rows.Count*d*2); s0.Add(ShowComplex(pp.Combine(blu, (p, b) => b.LerpToTransparent(p.SmoothTransition(0, 0, 1))), pp.Combine(bla, (p, b) => b.LerpToTransparent(p.SmoothTransition(0, 0, 1))), v.Values[r], pp.Select(p => p.SmoothTransition(p1, p1, p2, p3)), ur, rotation: pp.Select(t => Turn.FromNaturalAngle(t.SmoothTransition(0, Math.PI/2, Math.PI/2))))); foreach (var c in u.Columns.Count.Range()) { // vector copied into matrix s0.Add(ShowComplex(pp.Combine(blu, (p, b) => b.LerpToTransparent(p.SmoothTransition(1, 1, 0))), pp.Combine(bla, (p, b) => b.LerpToTransparent(p.SmoothTransition(1, 1, 0))), v.Values[c], (pos.TopLeft + new Vector(d*3 + c*d*2, d + r*d*2)), ur, Brushes.Transparent, rotation: Turn.FromNaturalAngle(Math.PI/2))); // matrix s0.Add(ShowComplex(pp.Combine(or, (p, b) => b.LerpToTransparent(p.SmoothTransition(0, 0, 0))), pp.Combine(bla, (p, b) => b.LerpToTransparent(p.SmoothTransition(0, 0, 0))), u.Rows[r][c], (pos.TopLeft + new Vector(d*3 + c*d*2, d + r*d*2)), ur)); } } var s1 = per.LimitedSameTime(period.Times(0.25), period.Times(0.5)); foreach (var r in u.Rows.Count.Range()) { foreach (var c in u.Columns.Count.Range()) { s1.Add( ShowComplexProduct( blu, or, bla, bla, v.Values[c], u.Rows[r][c], (pos.TopLeft + new Vector(d*3 + c*d*2, d + r*d*2)), ur, s1.Proper)); } } var s2 = per.LimitedSameTime(period.Times(0.5), period); foreach (var r in u.Rows.Count.Range()) { s2.Add( ShowComplexSum( blu, blu, bla, u.Rows[r].Count.Range() .Select(e => u.Rows[r][e]*v.Values[e]) .Select(e => new ConstantAni<Complex>(e)), (pos.TopLeft + new Vector(d*3, d + r*d*2)), (pos.TopLeft + new Vector(d*3 + u.Columns.Count*d*2, d + r*d*2)), new Vector(d*2, 0), ur, s2.Proper)); } return animation; }
private static Animation CreateVarySlopeSolveYAnimation(int size, Rect rect, int xq, int yq) { var ani = new Animation { CreateGrid(size, rect) }; var dt = 50.Milliseconds(); var p = ani.Periodic(dt.Times(size)); for (var slope = 0; slope < size; slope++) { var y0 = size.Range().Single(y => (y + slope * xq) % size == yq); var q = p.LimitedNewTime(dt.Times(slope), dt.Times(slope + 1)); q.Add(CreateLine(y0, slope, size, rect)); q.Add(new PointDesc(GetP(0, y0, size, rect), fill: Brushes.Red, radius: 8)); q.Add(new TextDesc(string.Format("Target = ({0}, {1}), Slope = {2}, Y-Intercept = {3}", xq, yq, slope, y0), new Point(rect.Left + 67, rect.Bottom + 5), new Point(0, 0), fontSize: 14)); q.Add(new PointDesc(GetP(xq, yq, size, rect), fill: Brushes.Blue, radius: 8)); } ani.Add(new TextDesc("Varying Slope, Solving Y-Intercept", new Point(rect.Left + rect.Width / 2, rect.Top - 30), new Point(0.5, 0), fontSize: 18)); return ani; }
private static Animation CreateAnimationOfOperations(Interval renderInterval, Interval targetInterval, params Operation[] operations) { var keyNodes = new Dictionary<int, Tuple<NestingDepthTreeNode, Interval, NestingDepthTreeNode>>(); var activeOperation = default(Operation); var dt = 1.0; var ani = new Animation(); ani.Add(new TextDesc("Using a Tree to Track Referenced Intervals", new Point(250, 5), new Point(0, 0), fontSize:20)); ani.Add(new TextDesc("Referenced" + Environment.NewLine + "Intervals", new Point(10, 80))); ani.Add(new TextDesc("Tree", new Point(10, 220))); ani.Add(new TextDesc("Nesting" + Environment.NewLine + "Depth", new Point(10, 350))); ani.Add(new LineSegmentDesc(new Point(0, 30).Sweep(new Vector(10000, 0)), Brushes.Black, 1)); ani.Add(new LineSegmentDesc(new Point(0, 100).Sweep(new Vector(10000, 0)), Brushes.Black, 1)); ani.Add(new LineSegmentDesc(new Point(0, 300).Sweep(new Vector(10000, 0)), Brushes.Black, 1)); ani.Add(new LineSegmentDesc(new Point(75, 0).Sweep(new Vector(0, 10000)), Brushes.Black, 1)); var i = 0.0; var dealloced = new HashSet<int>(); for (var i2 = 0; i2 < renderInterval.Length; i2++) { if (i2 + renderInterval.Offset < targetInterval.Offset || i2 + renderInterval.Offset >= targetInterval.Offset + targetInterval.Length) { dealloced.Add(i2 + renderInterval.Offset); } } var x = 100; var y = 90; Func<double, Animation> addFrame = focus => { var roots = keyNodes.Select(e => NestingDepthTreeNode.RootOf(e.Value.Item1)).Distinct().ToArray(); var a = new Animation(); var s = 10; if (!double.IsNaN(focus) && !double.IsInfinity(focus)) { var dx = focus - renderInterval.Offset; a.Add(new LineSegmentDesc(new LineSegment(new Point(x + dx*s, 0), new Point(x + dx*s, 10000)), activeOperation.Interval.HasValue ? Brushes.Green : Brushes.Red, 1, 1.0)); } foreach (var r in roots) a.Add(RenderTree(renderInterval, r, dealloced)); ani.LimitedNewTime(i.Seconds(), (i + dt).Seconds()).Add(a); i += dt; var bb = 0; foreach (var e in keyNodes) { Brush brush; if (e.Key == activeOperation.Key && !double.IsInfinity(focus)) { brush = activeOperation.Interval.HasValue ? Brushes.Green : Brushes.Red; } else { brush = Brushes.Yellow; } var inv = e.Value.Item2; var dx = inv.Offset - renderInterval.Offset; a.Add(new RectDesc(new Rect(x + dx * s, y - (bb+1) * s*1.1, s*inv.Length, s), fill: brush, stroke: Brushes.Black, strokeThickness: 1)); bb += 1; } return a; }; foreach (var e in operations) { activeOperation = e; var affectedRoot = e.Interval.HasValue ? keyNodes.Select(n => NestingDepthTreeNode.RootOf(n.Value.Item1)).FirstOrDefault(n => NestingDepthTreeNode.GetInterval(n).Overlaps(e.Interval.Value)) : NestingDepthTreeNode.RootOf(keyNodes[e.Key].Item1); if (e.Interval.HasValue) { keyNodes.Add(e.Key, Tuple.Create((NestingDepthTreeNode)null, e.Interval.Value, (NestingDepthTreeNode)null)); addFrame(double.NaN); addFrame(e.Interval.Value.Offset); var a1 = NestingDepthTreeNode.Include(affectedRoot, e.Interval.Value.Offset, +1, +1); a1.AdjustedNode._fakeRefCount += 2; keyNodes[e.Key] = Tuple.Create(a1.AdjustedNode, e.Interval.Value, (NestingDepthTreeNode)null); addFrame(e.Interval.Value.Offset); addFrame(e.Interval.Value.Offset + e.Interval.Value.Length); var a2 = NestingDepthTreeNode.Include(a1.NewRoot, e.Interval.Value.Offset + e.Interval.Value.Length, -1, +1); a2.AdjustedNode._fakeRefCount += 2; keyNodes[e.Key] = Tuple.Create(a1.AdjustedNode, e.Interval.Value, a2.AdjustedNode); addFrame(e.Interval.Value.Offset + e.Interval.Value.Length); addFrame(double.PositiveInfinity); } else { var xs = keyNodes[e.Key]; var r = NestingDepthTreeNode.RootOf(xs.Item1); r = NestingDepthTreeNode.Include(r, xs.Item2.Offset + xs.Item2.Length, +1, 0).NewRoot; r = NestingDepthTreeNode.Include(r, xs.Item2.Offset, -1, 0).NewRoot; var hh = new HashSet<int>(); foreach (var ex in NestingDepthTreeNode.FindHolesIn(NestingDepthTreeNode.GetInterval(r), r)) { for (var ii = ex.Offset; ii < ex.Offset + ex.Length; ii++) { hh.Add(ii); } } r = NestingDepthTreeNode.Include(r, xs.Item2.Offset + xs.Item2.Length, -1, 0).NewRoot; r = NestingDepthTreeNode.Include(r, xs.Item2.Offset, +1, 0).NewRoot; xs.Item3._fakeRefCount -= 1; xs.Item3._fakeRefCount -= 1; xs.Item1._fakeRefCount -= 1; addFrame(xs.Item2.Offset + xs.Item2.Length); var r1 = NestingDepthTreeNode.Include(r, xs.Item2.Offset + xs.Item2.Length, +1, -1); addFrame(xs.Item2.Offset + xs.Item2.Length); xs.Item1._fakeRefCount -= 1; addFrame(xs.Item2.Offset); var r2 = NestingDepthTreeNode.Include(r1.NewRoot, xs.Item2.Offset, -1, -1); keyNodes.Remove(e.Key); addFrame(double.NaN); foreach (var xxx in hh) { dealloced.Add(xxx); } NestingDepthTreeNode.PartitionAroundHoles(r2.NewRoot); addFrame(double.NaN); } } var xx = new Animation(); xx.Periodic(i.Seconds()).Add(ani); return xx; }