You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
wiki-map/HexGrid/Path.pm

150 lines
4.4 KiB

package HexGrid::Path;
use v5.30;
use Moo;
use MooX::Aliases;
use Data::Dumper;
use feature "signatures";
no warnings "experimental::signatures";
has tiles => (is => 'rw', default => sub { [] });
has id => (is => 'ro', required => 1);
has style => (is => 'rw', default => sub { {} });
has colour => (is => 'rw', alias => 'color', default => 'blue');
has css_class => (is => 'rw');
has starts_from => (is => 'rw');
has ends_to => (is => 'rw');
# Class
my $DEFAULT_WIDTH = 5;
sub get_edge_direction($tile1, $tile2)
{
my $nw_diff = $tile2->nw - $tile1->nw;
my $sw_diff = $tile2->sw - $tile1->sw;
return $HexGrid::DIR{nw} if $nw_diff == 1 && $sw_diff == 0;
return $HexGrid::DIR{sw} if $nw_diff == 0 && $sw_diff == 1;
return $HexGrid::DIR{s} if $nw_diff == -1 && $sw_diff == 1;
return $HexGrid::DIR{se} if $nw_diff == -1 && $sw_diff == 0;
return $HexGrid::DIR{ne} if $nw_diff == 0 && $sw_diff == -1;
return $HexGrid::DIR{n} if $nw_diff == 1 && $sw_diff == -1;
#TODO: should die here, to be caught/bubbled in render...
}
sub curve_to($qx, $qy, $x, $y)
{
return "Q $qx,$qy $x,$y";
}
# Instance
sub render($this, $grid, $svg)
{
return unless @{$this->tiles};
my $g = $svg->g(id => $this->id, class => $this->css_class);
my $current_tile = shift @{$this->tiles};
# Single tile
unless (@{$this->tiles})
{
my ($cx, $cy) = $grid->coords_of_centre($current_tile->nw, $current_tile->sw);
if($this->starts_from)
{
my ($x1, $y1) = $grid->coords_of_edge($current_tile->nw, $current_tile->sw, $this->starts_from);
if($this->ends_to)
{
my ($x2, $y2) = $grid->coords_of_edge($current_tile->nw, $current_tile->sw, $this->ends_to);
# Curve from starts_from to ends_to with the centre as control point
$g->path(d => "M $x1,$y1 Q $cx,$cy $x2,$y2",
stroke => $this->colour, style => $this->style, class => $this->css_class);
}
else
{
# line from starts_from to the centre
$g->line(x1 => $x1, y1 => $y1, x2 => $cx, y2 => $cy,
stroke => $this->colour, style => $this->style, class => $this->css_class);
}
}
else
{
if($this->ends_to)
{
# line from the centre to ends_to
my ($x2, $y2) = $grid->coords_of_edge($current_tile->nw, $current_tile->sw, $this->ends_to);
$g->line(x1 => $cx, y1 => $cy, x2 => $x2, y2 => $y2,
stroke => $this->colour, style => $this->style, class => $this->css_class);
}
else
{
$g->circle(cx => $cx, cy => $cy,
r => $this->{style}{'stroke-width'} // $DEFAULT_WIDTH,
fill => $this->colour, style => $this->style, class => $this->css_class);
}
}
return $g;
}
my ($x0, $x, $y0, $y);
my $path_spec;
my $previous_tile = $current_tile;
$current_tile = shift @{$this->tiles};
my $next_edge = get_edge_direction($previous_tile, $current_tile);
($x, $y) = $grid->coords_of_edge($previous_tile->nw, $previous_tile->sw, $next_edge);
if($this->starts_from)
{
($x0, $y0) = $grid->coords_of_edge($previous_tile->nw, $previous_tile->sw, $this->starts_from);
$path_spec .= "M $x0,$y0 ";
my ($cx, $cy) = $grid->coords_of_centre($previous_tile->nw, $previous_tile->sw);
$path_spec .= curve_to($cx, $cy, $x, $y);
}
else
{
($x0, $y0) = $grid->coords_of_centre($previous_tile->nw, $previous_tile->sw);
$path_spec .= "M $x0,$y0 L $x,$y";
}
my $previous_edge; # not defined yet
my $next_tile; # not defined yet
while(@{$this->tiles})
{
$next_tile = shift @{$this->tiles};
$previous_edge = -$next_edge;
$next_edge = get_edge_direction($current_tile, $next_tile);
my ($qx,$qy) = $grid->coords_of_centre($current_tile->nw, $current_tile->sw);
($x,$y) = $grid->coords_of_edge($current_tile->nw, $current_tile->sw, $next_edge);
$path_spec .= " " . curve_to($qx,$qy, $x,$y);
$previous_tile = $current_tile;
$current_tile = $next_tile;
}
# When loop is done (or if it was empty) $current_tile is the last tile
# $next_edge is the last used edge, so use it's opposite for the source of last line
if($this->ends_to)
{
($x, $y) = $grid->coords_of_centre($current_tile->nw, $current_tile->sw);
my ($xe, $ye) = $grid->coords_of_edge($current_tile->nw, $current_tile->sw, $this->ends_to);
$path_spec .= curve_to($x, $y, $xe, $ye);
}
else
{
($x, $y) = $grid->coords_of_centre($current_tile->nw, $current_tile->sw);
$path_spec .= " L $x,$y";
}
$g->path(d => $path_spec, fill => 'transparent',
stroke => $this->colour, style => $this->style, class => $this->css_class
);
return $g;
}
1;