From 21d3b67237a8e1824bdcf130c84afca6f323c644 Mon Sep 17 00:00:00 2001 From: Andinus <andinus@nand.sh> Date: Wed, 17 Nov 2021 18:49:36 +0530 Subject: Remove output option, rename frame-rate option to fps --- lib/Fornax/CLI.rakumod | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/Fornax/CLI.rakumod b/lib/Fornax/CLI.rakumod index 35109b7..32303fc 100644 --- a/lib/Fornax/CLI.rakumod +++ b/lib/Fornax/CLI.rakumod @@ -15,14 +15,14 @@ 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 = 1, #= frame rate for video solution (default: 1) Bool :$skip-video, #= skip video solution - Bool :$verbose = True, #= verbosity + Bool :$verbose = True, #= verbosity (default: True) ) 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; @@ -166,7 +166,7 @@ multi sub MAIN( put "[fornax] Creating a slideshow." if $verbose; my Str $log-level = $verbose ?? "info" !! "error"; - run «ffmpeg -loglevel "$log-level" -r "$frame-rate" -i "$output/\%08d.png" + run «ffmpeg -loglevel "$log-level" -r "$fps" -i "$output/\%08d.png" -vf 'tpad=stop_mode=clone:stop_duration=4' -vcodec libx264 -crf 28 -pix_fmt yuv420p "$output/solution.mp4"»; } -- cgit 1.4.1-2-gfad0 From 22aa1c9b322c424724516d70b77d1d5b93155697 Mon Sep 17 00:00:00 2001 From: Andinus <andinus@nand.sh> Date: Wed, 17 Nov 2021 18:50:25 +0530 Subject: Alias frame-rate to fps Preserves backwards compatibility. --- lib/Fornax/CLI.rakumod | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/Fornax/CLI.rakumod b/lib/Fornax/CLI.rakumod index 32303fc..8b18215 100644 --- a/lib/Fornax/CLI.rakumod +++ b/lib/Fornax/CLI.rakumod @@ -17,7 +17,7 @@ multi sub MAIN( File $input, #= fornax format file (solved) Int() :$batch = 4, #= number of iterations to process at once (default: 4) - Int() :$fps = 1, #= frame rate for video solution (default: 1) + Int() :fps($frame-rate) = 1, #= frame rate for video solution (default: 1) Bool :$skip-video, #= skip video solution Bool :$verbose = True, #= verbosity (default: True) ) is export { @@ -166,7 +166,7 @@ multi sub MAIN( put "[fornax] Creating a slideshow." if $verbose; my Str $log-level = $verbose ?? "info" !! "error"; - run «ffmpeg -loglevel "$log-level" -r "$fps" -i "$output/\%08d.png" + 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"»; } -- cgit 1.4.1-2-gfad0 From eb96ab962545cf9b403f18422f1de698d9619499 Mon Sep 17 00:00:00 2001 From: Andinus <andinus@nand.sh> Date: Thu, 18 Nov 2021 16:56:58 +0530 Subject: Move frame generation to a module, update progress reporting --- lib/Fornax/CLI.rakumod | 123 ++++++++------------------------------- lib/Fornax/GenerateFrame.rakumod | 100 +++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+), 100 deletions(-) create mode 100644 lib/Fornax/GenerateFrame.rakumod (limited to 'lib') diff --git a/lib/Fornax/CLI.rakumod b/lib/Fornax/CLI.rakumod index 8b18215..d066ebf 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; @@ -19,7 +18,7 @@ multi sub MAIN( 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 (default: True) + Bool :$debug, #= debug logs ) is export { my IO() $output = "%s/fornax-%s".sprintf( '/tmp', ('a'...'z', 'A'...'Z', 0...9).roll(8).join @@ -27,40 +26,14 @@ multi sub MAIN( 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"»; } - put "[fornax] Output: '$output'"; } multi sub MAIN( diff --git a/lib/Fornax/GenerateFrame.rakumod b/lib/Fornax/GenerateFrame.rakumod new file mode 100644 index 0000000..e19d342 --- /dev/null +++ b/lib/Fornax/GenerateFrame.rakumod @@ -0,0 +1,100 @@ +use Cairo; +use Fornax::Hex2RGB; + +# Cells as defined by fornax format. +constant $PATH = '.'; +constant $BLOK = '#'; +constant $DEST = '$'; +constant $STRT = '^'; +constant $VIS = '-'; +constant $CUR = '@'; +constant $CURPATH = '~'; + +# 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)}; + +enum IterStatus <Walking Blocked Completed>; + +sub generate-frame( + :%CANVAS, :$out, :%excess, :$side, :%meta, :$iter is copy + , :$idx, :$debug, +) is export { + 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 "\n[fornax] $idx $iter $status" if $debug; + + 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; + + # This seems to be slower than creating an intermediate + # variable and assigning from that. Difference is not much + # so we'll ignore it. + for ^%meta<rows> X ^%meta<cols> -> ($r, $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($out); + .finish; + } +} -- cgit 1.4.1-2-gfad0 From 8af7ea1e0572870eb49e6a6784736a2ca803600e Mon Sep 17 00:00:00 2001 From: Andinus <andinus@nand.sh> Date: Thu, 18 Nov 2021 17:00:08 +0530 Subject: Set stop duration to 2 seconds --- lib/Fornax/CLI.rakumod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/Fornax/CLI.rakumod b/lib/Fornax/CLI.rakumod index d066ebf..bac08ff 100644 --- a/lib/Fornax/CLI.rakumod +++ b/lib/Fornax/CLI.rakumod @@ -91,7 +91,7 @@ multi sub MAIN( 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' + -vf 'tpad=stop_mode=clone:stop_duration=2' -vcodec libx264 -crf 28 -pix_fmt yuv420p "$output/solution.mp4"»; } } -- cgit 1.4.1-2-gfad0 From b1259dc7b9aba4831cd61bf3521708ec3089ed3c Mon Sep 17 00:00:00 2001 From: Andinus <andinus@nand.sh> Date: Thu, 18 Nov 2021 17:09:51 +0530 Subject: Set default crf to 24 From https://trac.ffmpeg.org/wiki/Encode/H.265: The default is 28, and it should visually correspond to libx264 video at CRF 23, but result in about half the file size. This will result in larger videos. Maybe the default should be libx265. I did try it, results in smaller videos but I wasn't able to play the video on Firefox. --- lib/Fornax/CLI.rakumod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/Fornax/CLI.rakumod b/lib/Fornax/CLI.rakumod index bac08ff..98f20a7 100644 --- a/lib/Fornax/CLI.rakumod +++ b/lib/Fornax/CLI.rakumod @@ -92,7 +92,7 @@ multi sub MAIN( 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=2' - -vcodec libx264 -crf 28 -pix_fmt yuv420p "$output/solution.mp4"»; + -vcodec libx264 -crf 24 -pix_fmt yuv420p "$output/solution.mp4"»; } } -- cgit 1.4.1-2-gfad0