about summary refs log tree commit diff stats
path: root/lib/Fornax/CLI.rakumod
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Fornax/CLI.rakumod')
-rw-r--r--lib/Fornax/CLI.rakumod135
1 files changed, 29 insertions, 106 deletions
diff --git a/lib/Fornax/CLI.rakumod b/lib/Fornax/CLI.rakumod
index 35109b7..98f20a7 100644
--- a/lib/Fornax/CLI.rakumod
+++ b/lib/Fornax/CLI.rakumod
@@ -1,5 +1,4 @@
-use Cairo;
-use Fornax::Hex2RGB;
+use Fornax::GenerateFrame;
 
 subset File of Str where *.IO.f;
 
@@ -15,52 +14,26 @@ proto MAIN(|) is export { unless so @*ARGS { put $*USAGE; exit }; {*} }
 #| Collection of tools to visualize Path Finding Algorithms
 multi sub MAIN(
     File $input, #= fornax format file (solved)
-    IO() :$out = '/tmp', #= output directory (default: /tmp)
-    Int() :$batch = 4, #= batch size (generate frames in parallel)
-    Rat() :$frame-rate = 1, #= frame rate (default: 1)
+
+    Int() :$batch = 4, #= number of iterations to process at once (default: 4)
+    Int() :fps($frame-rate) = 1, #= frame rate for video solution (default: 1)
     Bool :$skip-video, #= skip video solution
-    Bool :$verbose = True, #= verbosity
+    Bool :$debug, #= debug logs
 ) is export {
     my IO() $output = "%s/fornax-%s".sprintf(
-        $out.absolute, ('a'...'z', 'A'...'Z', 0...9).roll(8).join
+        '/tmp', ('a'...'z', 'A'...'Z', 0...9).roll(8).join
     );
     mkdir $output;
     die "Output directory doesn't exist" unless $output.d;
 
-    put "[fornax] Output: '$output'" if $verbose;
+    put "[fornax] Output: '$output'";
 
     my Str @lines = $input.IO.lines;
     my Int() %meta{Str} = Metadata.parse(@lines.first).Hash
-                             or die "Cannot parse metadata";
-
-    # Cells as defined by fornax format.
-    constant $PATH = '.';
-    constant $BLOK = '#';
-    constant $DEST = '$';
-    constant $STRT = '^';
-    constant $VIS = '-';
-    constant $CUR = '@';
-    constant $CURPATH = '~';
+                                  or die "Cannot parse metadata";
 
     constant %CANVAS = :1920width, :1080height;
 
-    # Colors.
-    constant %C = (
-        bg-main => "#ffffff",
-
-        red-subtle-bg => "#f2b0a2",
-        blue-subtle-bg => "#b5d0ff",
-        cyan-subtle-bg => "#c0efff",
-        green-subtle-bg => "#aecf90",
-
-        fg-main => "#000000",
-
-        fg-special-cold => "#093060",
-        fg-special-warm => "#5d3026",
-        fg-special-mild => "#184034",
-        fg-special-calm => "#61284f",
-    ).map: {.key => hex2rgb(.value)};
-
     # Every cell must be square. Get the maximum width, height and use
     # that to decide which is to be used.
     my Int %cell = width => %CANVAS<width> div %meta<cols>,
@@ -79,7 +52,8 @@ multi sub MAIN(
         $side = %cell<height>;
     }
 
-    enum IterStatus <Walking Blocked Completed>;
+    my $render-start = now;
+    my Int $total-frames = @lines.elems - 1;
 
     my Promise @p;
     for @lines.skip.kv -> $idx, $iter is rw {
@@ -88,89 +62,38 @@ multi sub MAIN(
         if @p.elems == $batch {
             await @p;
             @p = [];
+
+            print "\r";
+            print "%s  Remaining: %.2fs  Elapsed: %.2fs %s".sprintf(
+                "[fornax $idx/$total-frames]",
+                ((now - $render-start) / $idx) * ($total-frames - $idx),
+                now - $render-start, "        ",
+            );
         }
 
         push @p, start {
-            my IterStatus $status;
-            given $iter.substr(0, 1) {
-                when '|' { $status = Completed }
-                when '!' { $status = Blocked }
-                default { $status = Walking }
-            };
-
-            # Remove marker.
-            $iter .= substr(1) if $status == Completed|Blocked;
-
-            put "[fornax] $idx $iter $status" if $verbose;
-
-            my @grid = $iter.comb.rotor: %meta<cols>;
-            warn "Invalid grid: $idx $iter $status" unless @grid.elems == %meta<rows>;
-
-            given Cairo::Image.create(
-                Cairo::FORMAT_ARGB32, %CANVAS<width>, %CANVAS<height>
-            ) {
-                given Cairo::Context.new($_) {
-                    # Paint the entire canvas white.
-                    .rgb: |%C<bg-main>;
-                    .rectangle(0, 0, %CANVAS<width>, %CANVAS<height>);
-                    .fill;
-
-                    for ^%meta<rows> -> $r {
-                        for ^%meta<cols> -> $c {
-                            my Int @target = %excess<width> div 2 + $c * $side,
-                                             %excess<height> div 2 + $r * $side,
-                                             $side, $side;
-
-                            .rectangle: |@target;
-
-                            given @grid[$r][$c] -> $cell {
-                                # According to the format, current
-                                # position may be prioritized over
-                                # Destination symbol so we colorize it
-                                # according to $status.
-                                when $cell eq $CUR {
-                                    .rgba: |%C<fg-special-cold>, 0.56;
-                                    .rgba: |%C<fg-special-mild>, 0.72 if $status == Completed;
-                                    .rgba: |%C<fg-special-warm>, 0.72 if $status == Blocked;
-                                }
-                                when $cell eq $CURPATH {
-                                    .rgba: |%C<blue-subtle-bg>, 0.84;
-                                    .rgba: |%C<green-subtle-bg>, 0.96 if $status == Completed;
-                                    .rgba: |%C<red-subtle-bg>, 0.96 if $status == Blocked;
-                                }
-                                when $cell eq $VIS {
-                                    .rgba: |%C<cyan-subtle-bg>, 0.72;
-                                }
-                                when $cell eq $BLOK { .rgba: |%C<fg-main>, 0.56 }
-                                when $cell eq $STRT|$DEST { .rgba: |%C<fg-special-mild>, 0.72 }
-                                default { .rgba: |%C<fg-main>, 0.08 }
-                            }
-                            .fill :preserve;
-
-                            .rgb: |%C<fg-main>;
-                            .stroke;
-                        }
-                    }
-                }
-                .write_png("%s/%08d.png".sprintf: $output, $idx);
-                .finish;
-            }
+            generate-frame(
+                :%CANVAS, :%excess, :$side, :%meta, :$iter, :$idx, :$debug,
+                :out("%s/%08d.png".sprintf: $output, $idx),
+            );
         }
     }
     # Wait for remaining jobs to finish.
     await @p;
 
-    put "[fornax] Generated images." if $verbose;
+    print "\r";
+    put "[fornax] Generated $total-frames frames in %.2fs. %s".sprintf(
+        now - $render-start, " " x 16,
+    );
 
     unless $skip-video {
-        put "[fornax] Creating a slideshow." if $verbose;
+        put "[fornax] Creating a slideshow.";
 
-        my Str $log-level = $verbose ?? "info" !! "error";
+        my Str $log-level = $debug ?? "info" !! "error";
         run «ffmpeg -loglevel "$log-level" -r "$frame-rate" -i "$output/\%08d.png"
-                    -vf 'tpad=stop_mode=clone:stop_duration=4'
-                    -vcodec libx264 -crf 28 -pix_fmt yuv420p "$output/solution.mp4"»;
+                    -vf 'tpad=stop_mode=clone:stop_duration=2'
+                    -vcodec libx264 -crf 24 -pix_fmt yuv420p "$output/solution.mp4"»;
     }
-    put "[fornax] Output: '$output'";
 }
 
 multi sub MAIN(