Skip to main content

news_feed/
news_feed.rs

1// News Feed System Architecture Animation
2// Inspired by "System Design Interview: An Insider's Guide" (Second Edition) by Alex Xu.
3
4use motion_canvas_rs::prelude::*;
5use std::time::Duration;
6
7const BG: Color = Color::rgb8(0x0e, 0x0e, 0x12);
8const WHITE: Color = Color::rgb8(0xf0, 0xf0, 0xf0);
9const DIM: Color = Color::rgb8(0x55, 0x55, 0x66);
10const ACCENT: Color = Color::rgb8(0x68, 0xab, 0xdf);
11const YELLOW: Color = Color::rgb8(0xe6, 0xa7, 0x00);
12const GREEN: Color = Color::rgb8(0x25, 0xc2, 0x81);
13const RED: Color = Color::rgb8(0xe1, 0x32, 0x38);
14
15fn show(opacity: &Signal<f32>, d: Duration) -> Box<dyn Animation> {
16    opacity.to(1.0, d).ease(easings::cubic_out).into()
17}
18
19fn hide(opacity: &Signal<f32>, d: Duration) -> Box<dyn Animation> {
20    opacity.to(0.0, d).ease(easings::cubic_in).into()
21}
22
23// Helper for boxes to ensure they start completely hidden and slightly scaled down
24fn create_box(label: &str, center: Vec2, size: Vec2) -> (Rect, TextNode) {
25    let box_center = Vec2::new(center.x, center.y);
26
27    let text_pos = Vec2::new(center.x, center.y);
28
29    let r = Rect::default()
30        .with_position(box_center)
31        .with_size(size)
32        .with_scale(0.95)
33        .with_opacity(0.0)
34        .with_radius(8.0)
35        .with_stroke(ACCENT, 2.0)
36        .with_fill(Color::rgb8(0x26, 0x26, 0x2a));
37
38    let t = TextNode::default()
39        .with_position(text_pos)
40        .with_text(label)
41        .with_font_size(18.0)
42        .with_font("JetBrains Mono")
43        .with_fill(WHITE)
44        .with_scale(0.95)
45        .with_opacity(0.0);
46
47    (r, t)
48}
49
50fn create_line(start: Vec2, end: Vec2) -> (Line, Vec2) {
51    (
52        Line::default()
53            .with_start(start)
54            .with_end(start) // will animate to 'end'
55            .with_stroke(DIM, 2.0)
56            .with_opacity(0.0),
57        end,
58    )
59}
60
61fn main() {
62    let mut project = Project::default()
63        .with_fps(60)
64        .with_background(BG)
65        .with_title("News Feed")
66        .with_dimensions(1920, 1080)
67        .close_on_finish();
68
69    // -- Title --
70    let title = TextNode::default()
71        .with_position(Vec2::new(80.0, 50.0))
72        .with_anchor(Vec2::new(-1.0, -1.0))
73        .with_text("News Feed Architecture")
74        .with_font_size(52.0)
75        .with_font("JetBrains Mono")
76        .with_fill(WHITE)
77        .with_scale(0.95)
78        .with_opacity(0.0);
79
80    // -- Top Tier --
81    let (user_box, user_label) = create_box(
82        "User (Web/App)",
83        Vec2::new(960.0, 130.0),
84        Vec2::new(200.0, 60.0),
85    );
86
87    let (line_lb, lb_end) = create_line(Vec2::new(960.0, 130.0), Vec2::new(960.0, 260.0));
88    let (lb_box, lb_label) = create_box(
89        "Load Balancer",
90        Vec2::new(960.0, 260.0),
91        Vec2::new(160.0, 60.0),
92    );
93
94    let (line_ws, ws_end) = create_line(Vec2::new(960.0, 260.0), Vec2::new(960.0, 390.0));
95    let (ws_box, ws_label) = create_box(
96        "Web Servers",
97        Vec2::new(960.0, 390.0),
98        Vec2::new(300.0, 80.0),
99    );
100
101    // -- Services Tier --
102    let (line_post, post_end) = create_line(Vec2::new(960.0, 390.0), Vec2::new(600.0, 520.0));
103    let (post_box, post_label) = create_box(
104        "Post Service",
105        Vec2::new(600.0, 520.0),
106        Vec2::new(160.0, 60.0),
107    );
108
109    let (line_fanout, fanout_end) = create_line(Vec2::new(960.0, 390.0), Vec2::new(960.0, 520.0));
110    let (fanout_box, fanout_label) = create_box(
111        "Fanout Service",
112        Vec2::new(960.0, 520.0),
113        Vec2::new(180.0, 60.0),
114    );
115
116    let (line_notif, notif_end) = create_line(Vec2::new(960.0, 390.0), Vec2::new(1320.0, 520.0));
117    let (notif_box, notif_label) = create_box(
118        "Notification Service",
119        Vec2::new(1320.0, 520.0),
120        Vec2::new(240.0, 60.0),
121    );
122
123    // -- Post Caches & DB --
124    let (line_post_cache, post_cache_end) =
125        create_line(Vec2::new(600.0, 520.0), Vec2::new(450.0, 720.0));
126    let (post_cache_box, post_cache_label) = create_box(
127        "Post Cache",
128        Vec2::new(450.0, 720.0),
129        Vec2::new(160.0, 80.0),
130    );
131
132    let (line_post_db, post_db_end) = create_line(Vec2::new(600.0, 520.0), Vec2::new(750.0, 720.0));
133    let (post_db_box, post_db_label) =
134        create_box("Post DB", Vec2::new(750.0, 720.0), Vec2::new(140.0, 80.0));
135
136    // -- NF Cache --
137    let (line_nf_cache, nf_cache_end) =
138        create_line(Vec2::new(960.0, 520.0), Vec2::new(960.0, 720.0));
139    let (nf_cache_box, nf_cache_label) = create_box(
140        "News Feed Cache",
141        Vec2::new(960.0, 720.0),
142        Vec2::new(200.0, 80.0),
143    );
144
145    // -- Fanout DBs Flow --
146    let (line_graph_db, graph_db_end) =
147        create_line(Vec2::new(960.0, 520.0), Vec2::new(1280.0, 620.0));
148    let (graph_db_box, graph_db_label) =
149        create_box("Graph DB", Vec2::new(1280.0, 620.0), Vec2::new(140.0, 60.0));
150
151    let (line_user_cache, user_cache_end) =
152        create_line(Vec2::new(960.0, 520.0), Vec2::new(1200.0, 720.0));
153    let (user_cache_box, user_cache_label) = create_box(
154        "User Cache",
155        Vec2::new(1200.0, 720.0),
156        Vec2::new(160.0, 80.0),
157    );
158
159    let (line_user_db, user_db_end) =
160        create_line(Vec2::new(1200.0, 720.0), Vec2::new(1200.0, 860.0));
161    let (user_db_box, user_db_label) =
162        create_box("User DB", Vec2::new(1200.0, 860.0), Vec2::new(140.0, 80.0));
163
164    // -- Case Indicators --
165    let case1 = TextNode::default()
166        .with_position(Vec2::new(80.0, 110.0))
167        .with_text("Scenario 1: Cache Miss (Data Fallback)")
168        .with_anchor(Vec2::new(-1.0, -1.0))
169        .with_font_size(24.0)
170        .with_font("JetBrains Mono")
171        .with_fill(YELLOW)
172        .with_opacity(0.0);
173
174    let case2 = TextNode::default()
175        .with_position(Vec2::new(80.0, 110.0))
176        .with_text("Scenario 2: Cache Hit (Fast Path)")
177        .with_anchor(Vec2::new(-1.0, -1.0))
178        .with_font_size(24.0)
179        .with_font("JetBrains Mono")
180        .with_fill(GREEN)
181        .with_opacity(0.0);
182
183    let case3 = TextNode::default()
184        .with_position(Vec2::new(80.0, 110.0))
185        .with_text("Scenario 3: Rate Limited (Packet Dropped)")
186        .with_font_size(24.0)
187        .with_anchor(Vec2::new(-1.0, -1.0))
188        .with_font("JetBrains Mono")
189        .with_fill(RED)
190        .with_opacity(0.0);
191
192    // -- Flow Step Indicators --
193
194    let case4 = TextNode::default()
195        .with_position(Vec2::new(80.0, 110.0))
196        .with_text("Scenario 4: Full Publish (Parallel)")
197        .with_font_size(24.0)
198        .with_anchor(Vec2::new(-1.0, -1.0))
199        .with_font("JetBrains Mono")
200        .with_fill(GREEN)
201        .with_opacity(0.0);
202
203    let step1 = TextNode::default()
204        .with_position(Vec2::new(1120.0, 570.0))
205        .with_text("1: Get Friend IDs")
206        .with_font_size(16.0)
207        .with_font("JetBrains Mono")
208        .with_fill(YELLOW)
209        .with_opacity(0.0);
210
211    let step2 = TextNode::default()
212        .with_position(Vec2::new(1060.0, 600.0))
213        .with_text("2: Get Friend Data")
214        .with_font_size(16.0)
215        .with_font("JetBrains Mono")
216        .with_fill(YELLOW)
217        .with_opacity(0.0);
218
219    let step3 = TextNode::default()
220        .with_position(Vec2::new(960.0, 620.0))
221        .with_text("3: Update Feed")
222        .with_font_size(16.0)
223        .with_font("JetBrains Mono")
224        .with_fill(YELLOW)
225        .with_opacity(0.0);
226
227    // Initial packet using exact center point
228    let packet = Circle::default()
229        .with_position(Vec2::new(960.0, 130.0))
230        .with_radius(12.0)
231        .with_scale(0.95)
232        .with_opacity(0.0)
233        .with_fill(GREEN);
234
235    let packet_post = Circle::default()
236        .with_position(Vec2::new(960.0, 130.0))
237        .with_radius(12.0)
238        .with_scale(0.95)
239        .with_opacity(0.0)
240        .with_fill(ACCENT);
241
242    let packet_notif = Circle::default()
243        .with_position(Vec2::new(960.0, 130.0))
244        .with_radius(12.0)
245        .with_scale(0.95)
246        .with_opacity(0.0)
247        .with_fill(YELLOW);
248
249    let fast_in = easings::cubic_out;
250    let smooth = easings::cubic_in_out;
251    let appear_dur = Duration::from_millis(250);
252    let move_dur = Duration::from_millis(700);
253
254    // -- Sequence --
255    project.scene.video_timeline.add(chain![
256        wait(Duration::from_millis(500)),
257        all![
258            show(&title.opacity, appear_dur),
259            title
260                .scale
261                .to(Vec2::new(1.0, 1.0), appear_dur)
262                .ease(fast_in)
263        ],
264        // Topology Staggered
265        sequence![
266            Duration::from_millis(60),
267            all![
268                show(&user_box.opacity, appear_dur),
269                user_box
270                    .scale
271                    .to(Vec2::new(1.0, 1.0), appear_dur)
272                    .ease(fast_in),
273                show(&user_label.opacity, appear_dur),
274                user_label
275                    .scale
276                    .to(Vec2::new(1.0, 1.0), appear_dur)
277                    .ease(fast_in),
278            ],
279            all![
280                show(&line_lb.opacity, appear_dur),
281                line_lb.end.to(lb_end, appear_dur).ease(smooth),
282                show(&lb_box.opacity, appear_dur),
283                lb_box
284                    .scale
285                    .to(Vec2::new(1.0, 1.0), appear_dur)
286                    .ease(fast_in),
287                show(&lb_label.opacity, appear_dur),
288                lb_label
289                    .scale
290                    .to(Vec2::new(1.0, 1.0), appear_dur)
291                    .ease(fast_in),
292            ],
293            all![
294                show(&line_ws.opacity, appear_dur),
295                line_ws.end.to(ws_end, appear_dur).ease(smooth),
296                show(&ws_box.opacity, appear_dur),
297                ws_box
298                    .scale
299                    .to(Vec2::new(1.0, 1.0), appear_dur)
300                    .ease(fast_in),
301                show(&ws_label.opacity, appear_dur),
302                ws_label
303                    .scale
304                    .to(Vec2::new(1.0, 1.0), appear_dur)
305                    .ease(fast_in),
306            ]
307        ],
308        sequence![
309            Duration::from_millis(60),
310            all![
311                show(&line_post.opacity, appear_dur),
312                line_post.end.to(post_end, appear_dur).ease(smooth),
313                show(&post_box.opacity, appear_dur),
314                post_box
315                    .scale
316                    .to(Vec2::new(1.0, 1.0), appear_dur)
317                    .ease(fast_in),
318                show(&post_label.opacity, appear_dur),
319                post_label
320                    .scale
321                    .to(Vec2::new(1.0, 1.0), appear_dur)
322                    .ease(fast_in),
323            ],
324            all![
325                show(&line_fanout.opacity, appear_dur),
326                line_fanout.end.to(fanout_end, appear_dur).ease(smooth),
327                show(&fanout_box.opacity, appear_dur),
328                fanout_box
329                    .scale
330                    .to(Vec2::new(1.0, 1.0), appear_dur)
331                    .ease(fast_in),
332                show(&fanout_label.opacity, appear_dur),
333                fanout_label
334                    .scale
335                    .to(Vec2::new(1.0, 1.0), appear_dur)
336                    .ease(fast_in),
337            ],
338            all![
339                show(&line_notif.opacity, appear_dur),
340                line_notif.end.to(notif_end, appear_dur).ease(smooth),
341                show(&notif_box.opacity, appear_dur),
342                notif_box
343                    .scale
344                    .to(Vec2::new(1.0, 1.0), appear_dur)
345                    .ease(fast_in),
346                show(&notif_label.opacity, appear_dur),
347                notif_label
348                    .scale
349                    .to(Vec2::new(1.0, 1.0), appear_dur)
350                    .ease(fast_in),
351            ],
352        ],
353        sequence![
354            Duration::from_millis(60),
355            all![
356                show(&line_post_cache.opacity, appear_dur),
357                line_post_cache
358                    .end
359                    .to(post_cache_end, appear_dur)
360                    .ease(smooth),
361                show(&post_cache_box.opacity, appear_dur),
362                post_cache_box
363                    .scale
364                    .to(Vec2::new(1.0, 1.0), appear_dur)
365                    .ease(fast_in),
366                show(&post_cache_label.opacity, appear_dur),
367                post_cache_label
368                    .scale
369                    .to(Vec2::new(1.0, 1.0), appear_dur)
370                    .ease(fast_in),
371            ],
372            all![
373                show(&line_post_db.opacity, appear_dur),
374                line_post_db.end.to(post_db_end, appear_dur).ease(smooth),
375                show(&post_db_box.opacity, appear_dur),
376                post_db_box
377                    .scale
378                    .to(Vec2::new(1.0, 1.0), appear_dur)
379                    .ease(fast_in),
380                show(&post_db_label.opacity, appear_dur),
381                post_db_label
382                    .scale
383                    .to(Vec2::new(1.0, 1.0), appear_dur)
384                    .ease(fast_in),
385            ],
386            all![
387                show(&line_nf_cache.opacity, appear_dur),
388                line_nf_cache.end.to(nf_cache_end, appear_dur).ease(smooth),
389                show(&nf_cache_box.opacity, appear_dur),
390                nf_cache_box
391                    .scale
392                    .to(Vec2::new(1.0, 1.0), appear_dur)
393                    .ease(fast_in),
394                show(&nf_cache_label.opacity, appear_dur),
395                nf_cache_label
396                    .scale
397                    .to(Vec2::new(1.0, 1.0), appear_dur)
398                    .ease(fast_in),
399            ],
400            all![
401                show(&line_graph_db.opacity, appear_dur),
402                line_graph_db.end.to(graph_db_end, appear_dur).ease(smooth),
403                show(&graph_db_box.opacity, appear_dur),
404                graph_db_box
405                    .scale
406                    .to(Vec2::new(1.0, 1.0), appear_dur)
407                    .ease(fast_in),
408                show(&graph_db_label.opacity, appear_dur),
409                graph_db_label
410                    .scale
411                    .to(Vec2::new(1.0, 1.0), appear_dur)
412                    .ease(fast_in),
413            ],
414            all![
415                show(&line_user_cache.opacity, appear_dur),
416                line_user_cache
417                    .end
418                    .to(user_cache_end, appear_dur)
419                    .ease(smooth),
420                show(&user_cache_box.opacity, appear_dur),
421                user_cache_box
422                    .scale
423                    .to(Vec2::new(1.0, 1.0), appear_dur)
424                    .ease(fast_in),
425                show(&user_cache_label.opacity, appear_dur),
426                user_cache_label
427                    .scale
428                    .to(Vec2::new(1.0, 1.0), appear_dur)
429                    .ease(fast_in),
430            ],
431            all![
432                show(&line_user_db.opacity, appear_dur),
433                line_user_db.end.to(user_db_end, appear_dur).ease(smooth),
434                show(&user_db_box.opacity, appear_dur),
435                user_db_box
436                    .scale
437                    .to(Vec2::new(1.0, 1.0), appear_dur)
438                    .ease(fast_in),
439                show(&user_db_label.opacity, appear_dur),
440                user_db_label
441                    .scale
442                    .to(Vec2::new(1.0, 1.0), appear_dur)
443                    .ease(fast_in),
444            ]
445        ],
446        wait(Duration::from_secs(1)),
447        // ==========================================
448        //  SCENARIO 1: CACHE MISS
449        // ==========================================
450        all![
451            show(&case1.opacity, appear_dur),
452            show(&packet.opacity, Duration::from_millis(200)),
453            packet
454                .fill_paint
455                .to(Paint::Solid(GREEN), Duration::from_millis(0)), // Start healthy
456            packet
457                .scale
458                .to(Vec2::new(1.0, 1.0), Duration::from_millis(200))
459                .ease(fast_in),
460            packet
461                .position
462                .to(Vec2::new(960.0, 130.0), Duration::from_millis(0)),
463        ],
464        packet
465            .position
466            .to(Vec2::new(960.0, 260.0), move_dur)
467            .ease(smooth),
468        packet
469            .position
470            .to(Vec2::new(960.0, 390.0), move_dur)
471            .ease(smooth),
472        packet
473            .position
474            .to(Vec2::new(960.0, 520.0), move_dur)
475            .ease(smooth),
476        wait(Duration::from_millis(200)),
477        // 1: Graph DB Check
478        all![
479            packet
480                .position
481                .to(Vec2::new(1280.0, 620.0), move_dur)
482                .ease(smooth),
483            delay![move_dur / 2, show(&step1.opacity, appear_dur)]
484        ],
485        packet
486            .position
487            .to(Vec2::new(960.0, 520.0), move_dur)
488            .ease(smooth),
489        // 2: User Cache MISSED -> Query DB
490        all![
491            packet
492                .position
493                .to(Vec2::new(1200.0, 720.0), move_dur)
494                .ease(smooth),
495            delay![move_dur / 2, show(&step2.opacity, appear_dur)]
496        ],
497        // Alert Miss: Flash Yellow and head to DB
498        all![
499            packet
500                .fill_paint
501                .to(Paint::Solid(YELLOW), Duration::from_millis(200)),
502            packet
503                .scale
504                .to(Vec2::new(1.2, 1.2), Duration::from_millis(200))
505                .ease(easings::bounce_out),
506            delay![
507                Duration::from_millis(200),
508                packet
509                    .position
510                    .to(Vec2::new(1200.0, 860.0), move_dur)
511                    .ease(smooth)
512            ]
513        ],
514        // Return from DB to Fanout
515        all![
516            packet
517                .fill_paint
518                .to(Paint::Solid(GREEN), Duration::from_millis(200)),
519            packet
520                .scale
521                .to(Vec2::new(1.0, 1.0), Duration::from_millis(200)),
522            packet
523                .position
524                .to(Vec2::new(1200.0, 720.0), move_dur)
525                .ease(smooth)
526        ],
527        packet
528            .position
529            .to(Vec2::new(960.0, 520.0), move_dur)
530            .ease(smooth),
531        // 3: Write to News Feed Cache
532        all![
533            packet
534                .position
535                .to(Vec2::new(960.0, 720.0), move_dur)
536                .ease(smooth),
537            delay![move_dur / 2, show(&step3.opacity, appear_dur)]
538        ],
539        packet
540            .position
541            .to(Vec2::new(960.0, 520.0), move_dur)
542            .ease(smooth),
543        wait(Duration::from_millis(200)),
544        // Packet Flow (Back Track to User)
545        packet
546            .position
547            .to(Vec2::new(960.0, 390.0), move_dur)
548            .ease(smooth),
549        packet
550            .position
551            .to(Vec2::new(960.0, 260.0), move_dur)
552            .ease(smooth),
553        packet
554            .position
555            .to(Vec2::new(960.0, 130.0), move_dur)
556            .ease(smooth),
557        // Hide packet & Case states
558        all![
559            hide(&packet.opacity, Duration::from_millis(200)),
560            hide(&case1.opacity, appear_dur),
561            hide(&step1.opacity, appear_dur),
562            hide(&step2.opacity, appear_dur),
563            hide(&step3.opacity, appear_dur),
564        ],
565        wait(Duration::from_secs(1)),
566        // ==========================================
567        //  SCENARIO 2: CACHE HIT
568        // ==========================================
569        all![
570            show(&case2.opacity, appear_dur),
571            show(&packet.opacity, Duration::from_millis(200)),
572            packet
573                .position
574                .to(Vec2::new(960.0, 130.0), Duration::from_millis(0)),
575            packet
576                .scale
577                .to(Vec2::new(1.0, 1.0), Duration::from_millis(200))
578                .ease(fast_in),
579        ],
580        packet
581            .position
582            .to(Vec2::new(960.0, 260.0), move_dur)
583            .ease(smooth),
584        packet
585            .position
586            .to(Vec2::new(960.0, 390.0), move_dur)
587            .ease(smooth),
588        packet
589            .position
590            .to(Vec2::new(960.0, 520.0), move_dur)
591            .ease(smooth),
592        // 1: Graph DB
593        all![
594            packet
595                .position
596                .to(Vec2::new(1280.0, 620.0), move_dur)
597                .ease(smooth),
598            delay![move_dur / 2, show(&step1.opacity, appear_dur)]
599        ],
600        packet
601            .position
602            .to(Vec2::new(960.0, 520.0), move_dur)
603            .ease(smooth),
604        // 2: User Cache HIT (Skip DB entirely)
605        all![
606            packet
607                .position
608                .to(Vec2::new(1200.0, 720.0), move_dur)
609                .ease(smooth),
610            delay![move_dur / 2, show(&step2.opacity, appear_dur)]
611        ],
612        packet
613            .position
614            .to(Vec2::new(960.0, 520.0), move_dur)
615            .ease(smooth),
616        // 3: News Feed Cache
617        all![
618            packet
619                .position
620                .to(Vec2::new(960.0, 720.0), move_dur)
621                .ease(smooth),
622            delay![move_dur / 2, show(&step3.opacity, appear_dur)]
623        ],
624        packet
625            .position
626            .to(Vec2::new(960.0, 520.0), move_dur)
627            .ease(smooth),
628        // Return
629        packet
630            .position
631            .to(Vec2::new(960.0, 390.0), move_dur)
632            .ease(smooth),
633        packet
634            .position
635            .to(Vec2::new(960.0, 260.0), move_dur)
636            .ease(smooth),
637        packet
638            .position
639            .to(Vec2::new(960.0, 130.0), move_dur)
640            .ease(smooth),
641        all![
642            hide(&packet.opacity, Duration::from_millis(200)),
643            hide(&case2.opacity, appear_dur),
644            hide(&step1.opacity, appear_dur),
645            hide(&step2.opacity, appear_dur),
646            hide(&step3.opacity, appear_dur),
647        ],
648        wait(Duration::from_secs(1)),
649        // ==========================================
650        //  SCENARIO 3: DROPPED PACKET
651        // ==========================================
652        all![
653            show(&case3.opacity, appear_dur),
654            show(&packet.opacity, Duration::from_millis(200)),
655            packet
656                .fill_paint
657                .to(Paint::Solid(GREEN), Duration::from_millis(0)),
658            packet
659                .position
660                .to(Vec2::new(960.0, 130.0), Duration::from_millis(0)),
661            packet
662                .scale
663                .to(Vec2::new(1.0, 1.0), Duration::from_millis(200))
664                .ease(fast_in),
665        ],
666        // To LB
667        packet
668            .position
669            .to(Vec2::new(960.0, 260.0), move_dur)
670            .ease(smooth),
671        // Moving to Web Servers but fails mid-way / at LB
672        all![
673            packet
674                .position
675                .to(Vec2::new(960.0, 390.0), move_dur)
676                .ease(smooth),
677            // Turns Red and gets destroyed
678            sequence![
679                move_dur / 2, // Wait until halfway
680                all![
681                    packet
682                        .fill_paint
683                        .to(Paint::Solid(RED), Duration::from_millis(150)),
684                    packet
685                        .scale
686                        .to(Vec2::new(1.8, 1.8), Duration::from_millis(150))
687                        .ease(easings::elastic_out),
688                ],
689                all![
690                    hide(&packet.opacity, Duration::from_millis(200)),
691                    packet
692                        .scale
693                        .to(Vec2::new(0.0, 0.0), Duration::from_millis(200))
694                        .ease(easings::cubic_in),
695                ]
696            ]
697        ],
698        hide(&case3.opacity, appear_dur),
699        wait(Duration::from_secs(1)),
700        // ==========================================
701        //  SCENARIO 4: PARALLEL PUBLISH
702        // ==========================================
703        all![
704            show(&case4.opacity, appear_dur),
705            show(&packet.opacity, Duration::from_millis(200)),
706            show(&packet_post.opacity, Duration::from_millis(200)),
707            show(&packet_notif.opacity, Duration::from_millis(200)),
708            packet
709                .fill_paint
710                .to(Paint::Solid(GREEN), Duration::from_millis(0)),
711            packet
712                .position
713                .to(Vec2::new(960.0, 130.0), Duration::from_millis(0)),
714            packet
715                .scale
716                .to(Vec2::new(1.0, 1.0), Duration::from_millis(200))
717                .ease(fast_in),
718            packet_post
719                .position
720                .to(Vec2::new(960.0, 130.0), Duration::from_millis(0)),
721            packet_post
722                .scale
723                .to(Vec2::new(1.0, 1.0), Duration::from_millis(200))
724                .ease(fast_in),
725            packet_notif
726                .position
727                .to(Vec2::new(960.0, 130.0), Duration::from_millis(0)),
728            packet_notif
729                .scale
730                .to(Vec2::new(1.0, 1.0), Duration::from_millis(200))
731                .ease(fast_in),
732        ],
733        // Flow down collectively to Web Servers
734        all![
735            packet
736                .position
737                .to(Vec2::new(960.0, 260.0), move_dur)
738                .ease(smooth),
739            packet_post
740                .position
741                .to(Vec2::new(960.0, 260.0), move_dur)
742                .ease(smooth),
743            packet_notif
744                .position
745                .to(Vec2::new(960.0, 260.0), move_dur)
746                .ease(smooth),
747        ],
748        all![
749            packet
750                .position
751                .to(Vec2::new(960.0, 390.0), move_dur)
752                .ease(smooth),
753            packet_post
754                .position
755                .to(Vec2::new(960.0, 390.0), move_dur)
756                .ease(smooth),
757            packet_notif
758                .position
759                .to(Vec2::new(960.0, 390.0), move_dur)
760                .ease(smooth),
761        ],
762        // BRANCHING!
763        all![
764            // Main packet goes to fanout
765            packet
766                .position
767                .to(Vec2::new(960.0, 520.0), move_dur)
768                .ease(smooth),
769            // Post packet to Post Service
770            packet_post
771                .position
772                .to(Vec2::new(600.0, 520.0), move_dur)
773                .ease(smooth),
774            // Notif packet to Notification Service
775            packet_notif
776                .position
777                .to(Vec2::new(1320.0, 520.0), move_dur)
778                .ease(smooth),
779        ],
780        wait(Duration::from_millis(200)),
781        // Post DB, NF Cache, Notifications APNS fly off
782        all![
783            // Fanout updates NF Cache
784            packet
785                .position
786                .to(Vec2::new(960.0, 720.0), move_dur)
787                .ease(smooth),
788            // Post updates DB
789            packet_post
790                .position
791                .to(Vec2::new(750.0, 720.0), move_dur)
792                .ease(smooth),
793            // Notif flies off-screen (Apple Push Notification Server etc)
794            packet_notif
795                .position
796                .to(Vec2::new(1500.0, 520.0), move_dur)
797                .ease(smooth),
798            hide(&packet_notif.opacity, move_dur),
799        ],
800        wait(Duration::from_millis(200)),
801        // Return
802        all![
803            packet
804                .position
805                .to(Vec2::new(960.0, 520.0), move_dur)
806                .ease(smooth),
807            packet_post
808                .position
809                .to(Vec2::new(600.0, 520.0), move_dur)
810                .ease(smooth),
811        ],
812        all![
813            packet
814                .position
815                .to(Vec2::new(960.0, 390.0), move_dur)
816                .ease(smooth),
817            packet_post
818                .position
819                .to(Vec2::new(960.0, 390.0), move_dur)
820                .ease(smooth),
821        ],
822        all![
823            packet
824                .position
825                .to(Vec2::new(960.0, 260.0), move_dur)
826                .ease(smooth),
827            packet_post
828                .position
829                .to(Vec2::new(960.0, 260.0), move_dur)
830                .ease(smooth),
831        ],
832        all![
833            packet
834                .position
835                .to(Vec2::new(960.0, 130.0), move_dur)
836                .ease(smooth),
837            packet_post
838                .position
839                .to(Vec2::new(960.0, 130.0), move_dur)
840                .ease(smooth),
841        ],
842        // Hide
843        all![
844            hide(&packet.opacity, Duration::from_millis(200)),
845            hide(&packet_post.opacity, Duration::from_millis(200)),
846            hide(&case4.opacity, appear_dur),
847        ],
848        wait(Duration::from_secs(2)),
849    ]);
850
851    // -- Adding to Scene --
852    for element in [
853        Box::new(title) as Box<dyn Node>,
854        Box::new(line_lb),
855        Box::new(line_ws),
856        Box::new(line_post),
857        Box::new(line_fanout),
858        Box::new(line_notif),
859        Box::new(line_post_cache),
860        Box::new(line_post_db),
861        Box::new(line_nf_cache),
862        Box::new(line_graph_db),
863        Box::new(line_user_cache),
864        Box::new(line_user_db),
865        Box::new(user_box),
866        Box::new(user_label),
867        Box::new(lb_box),
868        Box::new(lb_label),
869        Box::new(ws_box),
870        Box::new(ws_label),
871        Box::new(post_box),
872        Box::new(post_label),
873        Box::new(fanout_box),
874        Box::new(fanout_label),
875        Box::new(notif_box),
876        Box::new(notif_label),
877        Box::new(post_cache_box),
878        Box::new(post_cache_label),
879        Box::new(post_db_box),
880        Box::new(post_db_label),
881        Box::new(nf_cache_box),
882        Box::new(nf_cache_label),
883        Box::new(graph_db_box),
884        Box::new(graph_db_label),
885        Box::new(user_cache_box),
886        Box::new(user_cache_label),
887        Box::new(user_db_box),
888        Box::new(user_db_label),
889        Box::new(case1),
890        Box::new(case2),
891        Box::new(case3),
892        Box::new(step1),
893        Box::new(step2),
894        Box::new(step3),
895        Box::new(case4),
896        Box::new(packet_post),
897        Box::new(packet_notif),
898        Box::new(packet),
899    ] {
900        project.scene.add(element);
901    }
902
903    project.show().expect("Failed to render");
904}