From 4e8a59668535badfe7e1975f52fb04489e467585 Mon Sep 17 00:00:00 2001 From: Andinus Date: Wed, 3 Nov 2021 20:20:07 +0530 Subject: Parse fornax format to generate the images Switched to PNG because SVGs were larger in this case. Also, video will be created with `ffmpeg` so it's just easier to use PNGs. --- lib/Fornax/CLI.rakumod | 117 ++++++++++++++++++++++++++++++++------------- lib/Fornax/Hex2RGB.rakumod | 8 ++++ 2 files changed, 91 insertions(+), 34 deletions(-) create mode 100644 lib/Fornax/Hex2RGB.rakumod (limited to 'lib/Fornax') diff --git a/lib/Fornax/CLI.rakumod b/lib/Fornax/CLI.rakumod index ffbe813..5ca60bb 100644 --- a/lib/Fornax/CLI.rakumod +++ b/lib/Fornax/CLI.rakumod @@ -1,63 +1,112 @@ use Cairo; +use Fornax::Hex2RGB; +subset File of Str where *.IO.f; subset Directory of Str where *.IO.d; + +#| Parses fornax format file to extract metadata. +grammar Metadata { + rule TOP { } + token rows { 'rows:' <(\d+)> } + token cols { 'cols:' <(\d+)> } +} + proto MAIN(|) is export { unless so @*ARGS { put $*USAGE; exit }; {*} } #| Collection of tools to visualize Path Finding Algorithms multi sub MAIN( - Str $script, #= script to run (e.g. java/DFS) - Directory :$algorithms = 'algorithms', #= algorithms directory - Directory :$output = 'output', #= output directory + File $input, #= fornax format file (solved) + Directory :$output = 'output', #= output directory (existing) ) is export { - my Str $interpreter = $script.split("/").first; - my IO() $program-path = "$algorithms/$script.$interpreter"; + my Str @lines = $input.IO.lines; + my Int() %meta{Str} = Metadata.parse(@lines.first).Hash + or die "Cannot parse metadata"; - die "Program path invalid" unless $program-path.IO.f; + # Cells as defined by fornax format. + constant $PATH = '.'; + constant $BLOK = '#'; + constant $DEST = '$'; + constant $VIS = '-'; + constant $CUR = '@'; - my $proc = run «$interpreter $program-path», :out; - my @out = $proc.out.slurp(:close).lines; + constant %CANVAS = :1920width, :1080height; - my Int() $rows = @out[0].split(":")[0]; - my Int() $columns = @out[0].split(":")[1]; + # Colors. + constant %C = ( + black => "#000000", + white => "#ffffff", + green => "#aecf90", + cyan => "#c0efff", + red => "#f2b0a2", + pointer => "#093060" + ).map: {.key => hex2rgb(.value)}; - my Int $c-width = 512; - my Int $c-height = 512; + # 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 div %meta, + height => %CANVAS div %meta; - for @out[1].split(" ", :skip-empty) -> $iter { - my $file = "$output/" ~ $++ ~ ".svg"; - given Cairo::Surface::SVG.create($file, $c-width, $c-height) { - given Cairo::Context.new($_) { - my $COMPLETE = so $iter.comb.first eq "|"; - my @iter-corrected = $COMPLETE ?? $iter.comb.skip !! $iter.comb; - my @grid = @iter-corrected.rotor: $columns; + # Consider width if cells with dimension (width * width) fit + # within the canvas, otherwise consider the height. + if (%cell * %meta) < %CANVAS { + %cell = %cell; + } else { + %cell = %cell; + } + + enum IterStatus ; - die "Invalid grid: $iter" unless @grid.elems == $rows; + for @lines.skip.kv -> $idx, $iter is rw { - my $x-grid = $c-width / $rows; - my $y-grid = $c-height / $columns; + 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 "$idx $iter $status"; + + my @grid = $iter.comb.rotor: %meta; + warn "Invalid grid: $idx $iter $status" unless @grid.elems == %meta; + + given Cairo::Image.create( + Cairo::FORMAT_ARGB32, %CANVAS, %CANVAS + ) { + given Cairo::Context.new($_) { + # Paint the entire canvas white. + .rgb: |%C; + .rectangle(0, 0, %CANVAS, %CANVAS); + .fill :preserve; + .stroke; - for ^$rows -> $r { - for ^$columns -> $c { - .rectangle($c * $y-grid, $r * $x-grid, $y-grid, $x-grid); + for ^%meta -> $r { + for ^%meta -> $c { + .rectangle($c * %cell, $r * %cell, %cell, %cell); given @grid[$r][$c] -> $cell { - when $cell eq 'x' { - .rgba(192 / 255, 239 / 255, 255 / 255, 0.8); - .rgba(174 / 255, 207 / 255, 144 / 255, 1) if $COMPLETE; + when $cell eq $VIS|$CUR { + .rgba: |%C, 0.8; + .rgba: |%C, 0.8 if $status == Completed; + .rgba: |%C, 0.8 if $status == Blocked; } - when $cell eq '#' { .rgba(0, 0, 0, 0.5); } - when $cell eq '$' { .rgba(174 / 255, 207 / 255, 144 / 255, 1); } - when $cell eq '_' { .rgba(0, 0, 0, 0.1); } - default { .rgb(1, 0, 0); } + when $cell eq $BLOK { .rgba: |%C, 0.5 } + when $cell eq $DEST { .rgb: |%C } + default { .rgb: |%C } } .fill :preserve; - .rgba(0, 0, 0, 0.6); - .rectangle($c * $y-grid, $r * $x-grid, $y-grid, $x-grid); + .rgb: |%C; + .rectangle($c * %cell, $r * %cell, %cell, %cell); .stroke; } } } + .write_png("%s/%08d.png".sprintf: $output, $idx); .finish; } } diff --git a/lib/Fornax/Hex2RGB.rakumod b/lib/Fornax/Hex2RGB.rakumod new file mode 100644 index 0000000..aa77739 --- /dev/null +++ b/lib/Fornax/Hex2RGB.rakumod @@ -0,0 +1,8 @@ +#| Takes hex value and returns RGB equivalent. +sub hex2rgb(Str $hex --> List) is export { + # Skip the first character, group each by 2 and parse as base 16. + # Divide by 255 to return value between 0, 1. + $hex.comb.skip.rotor(2).map( + *.join.parse-base(16) / 255 + )>>.Rat +} -- cgit 1.4.1-2-gfad0