Question 4: Behind the scenes 

use Math::PlanePath::HilbertCurve;
use Data::Dumper;

my $input_file = 'path-bitmap.xpm';
my $line_buffer = '';

my $pic = read_xpm_bitmap($input_file);
output_path($pic);

exit;

sub read_xpm_bitmap {
    my($file) = @_;

    open my $fh, '<', $file;
    scalar(<$fh>) foreach 1..5;   # skip the header lines
    my @rows;
    while(<$fh>) {
        my @pixels = m{([ \.])}g;
        push @rows, \@pixels if @pixels;
    }
    return \@rows;
}


sub output_path {
    my($pic) = @_;

    my $rows = scalar(@$pic);
    my $cols = scalar(@{ $pic->[0] });
    my $last = $rows * $cols - 1;  # starting from zero

    my $path = Math::PlanePath::HilbertCurve->new;

    my($r, $c, $new_r, $new_c, $pixel);
    foreach my $i (0 .. $last) {
        ($new_r, $new_c) = $path->n_to_xy($i);
        if($i > 0) {
            if($new_r < $r) {
                output('N');
            }
            elsif($new_r > $r) {
                output('S');
            }
            elsif($new_c > $c) {
                output('E');
            }
            else {
                output('W');
            }
        }
        $r = $new_r;
        $c = $new_c;
        $pixel = $pic->[$r]->[$c] // die "No pixel at row $r column $c";
        output('*') if $pixel ne ' ';
    }
    say $line_buffer;
}


sub output {
    my($char) = @_;

    if(length($line_buffer) >= 78) {
        say $line_buffer;
        $line_buffer = '';
    }
    $line_buffer .= $char;
}