From b832bf9d00475a49e1c7b6f58736e9170b40e25b Mon Sep 17 00:00:00 2001 From: Daniel Asher Resnick Date: Sun, 31 Dec 2023 13:48:17 -0600 Subject: [PATCH] Add starts_from and ends_to path functionality --- HexGrid.pm | 4 +-- HexGrid/Path.pm | 77 ++++++++++++++++++++++++++++++++++++++++++------- tests/paths.pl | 42 +++++++++++++++++++++++++-- 3 files changed, 108 insertions(+), 15 deletions(-) diff --git a/HexGrid.pm b/HexGrid.pm index 4d0977b..fde185c 100644 --- a/HexGrid.pm +++ b/HexGrid.pm @@ -140,12 +140,12 @@ sub subgrid_for_tiles($this, @coords_list) my ($tile, $region) = $this->get_tile_and_region_at($coords->{nw}, $coords->{sw}); unless ($tile) { - carp "No tile at " . $coords->{nw} . "," . $coords->{sw}, skipping. + carp "No tile at " . $coords->{nw} . "," . $coords->{sw} . ", skipping."; next; } unless ($region) { - carp "No region at " . $coords->{nw} . "," . $coords->{sw}, skipping. + carp "No region at " . $coords->{nw} . "," . $coords->{sw} . ", skipping."; next; } unless(exists $subgrid->{regions}{$region->{name}}) diff --git a/HexGrid/Path.pm b/HexGrid/Path.pm index 125e0dc..e2e02e7 100644 --- a/HexGrid/Path.pm +++ b/HexGrid/Path.pm @@ -15,6 +15,8 @@ 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 @@ -48,24 +50,66 @@ sub render($this, $grid, $svg) return unless @{$this->tiles}; my $g = $svg->g(id => $this->id, class => $this->css_class); - my ($x0, $x, $y0, $y); - my $current_tile = shift @{$this->tiles}; + + # Single tile unless (@{$this->tiles}) { my ($cx, $cy) = $grid->coords_of_centre($current_tile->nw, $current_tile->sw); - $g->circle(cx => $cx, cy => $cy, - r => $this->{style}{'stroke-width'} // $DEFAULT_WIDTH, - fill => $this->colour, style => $this->style, class => $this->css_class); - return; + 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); - ($x0, $y0) = $grid->coords_of_centre($previous_tile->nw, $previous_tile->sw); ($x, $y) = $grid->coords_of_edge($previous_tile->nw, $previous_tile->sw, $next_edge); - $path_spec .= "M $x0,$y0 L $x,$y"; + 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 @@ -84,12 +128,23 @@ sub render($this, $grid, $svg) $current_tile = $next_tile; } # When loop is done (or if it was empty) $current_tile is the last tile - # $next_edge will be the last used edge, so use it's opposite for the source of last line - ($x, $y) = $grid->coords_of_centre($current_tile->nw, $current_tile->sw); - $path_spec .= " L $x,$y"; + # $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; diff --git a/tests/paths.pl b/tests/paths.pl index c29df74..03fe575 100644 --- a/tests/paths.pl +++ b/tests/paths.pl @@ -8,11 +8,11 @@ use Carp; use Data::Dumper; -my $MAP_SIZE = 3; +my $MAP_SIZE = 5; my $grid = HexGrid->new(defaults => { style => { 'stroke-width' => 1, stroke => 'white' }, - show_coords => 0}); + show_coords => 1}); my $region = $grid->make_region("TEST"); for (my $nw=-$MAP_SIZE; $nw <= $MAP_SIZE; $nw++) @@ -23,13 +23,51 @@ for (my $nw=-$MAP_SIZE; $nw <= $MAP_SIZE; $nw++) } } +# generic "standard" path $grid->make_path_from('test-id', [[0,0], [1,0], [1,1], [2,0], [2,-1], [2,-2], [1,-2], [1,-1], [2,-1], [3,-1]], colour => 'lime', css_class => 'path', style => { 'stroke-width' => 5 } ); + + +# Single tile paths $grid->make_path_from('point-id', [[-1,1]], colour => 'cyan', css_class => 'path', style => { 'stroke-width' => 5 } ); + +$grid->add_path(HexGrid::Path->new(id => 'point-starts', tiles => [$grid->get_tile_at(-2,2)], + starts_from => $HexGrid::DIR{n}, + colour => 'yellow', css_class => 'path', style => { 'stroke-width' => 5 })); + +$grid->add_path(HexGrid::Path->new(id => 'point-ends', tiles => [$grid->get_tile_at(-3,3)], + ends_to => $HexGrid::DIR{nw}, + colour => 'blue', css_class => 'path', style => { 'stroke-width' => 5 })); + +$grid->add_path(HexGrid::Path->new(id => 'point-starts-ends-1', tiles => [$grid->get_tile_at(-2,3)], + starts_from => $HexGrid::DIR{sw}, ends_to => $HexGrid::DIR{ne}, + colour => 'magenta', css_class => 'path', style => { 'stroke-width' => 5 })); + +$grid->add_path(HexGrid::Path->new(id => 'point-starts-ends-2', tiles => [$grid->get_tile_at(-1,2)], + starts_from => $HexGrid::DIR{sw}, ends_to => $HexGrid::DIR{n}, + colour => 'orange', css_class => 'path', style => { 'stroke-width' => 5 })); + + +# starts_from/ends_to paths +$grid->add_path(HexGrid::Path->new(id => 'starts', starts_from => $HexGrid::DIR{ne}, + tiles => [$grid->get_tile_at(4,-1), $grid->get_tile_at(5,-1), $grid->get_tile_at(5,0)], + colour => 'yellow', css_class => 'path', style => { 'stroke-width' => 5 })); + +$grid->add_path(HexGrid::Path->new(id => 'ends', ends_to => $HexGrid::DIR{nw}, + tiles => [$grid->get_tile_at(4,1), $grid->get_tile_at(4,0), $grid->get_tile_at(3,1)], + colour => 'blue', css_class => 'path', style => { 'stroke-width' => 5 })); + +$grid->add_path(HexGrid::Path->new(id => 'starts-ends', + starts_from => $HexGrid::DIR{sw}, ends_to => $HexGrid::DIR{s}, + tiles => [$grid->get_tile_at(2,3), $grid->get_tile_at(3,3), $grid->get_tile_at(4,3)], + colour => 'magenta', css_class => 'path', style => { 'stroke-width' => 5 })); + + +#loop $grid->make_path_from('loop-id', [[-2,0], [-1,0], [-1,-1], [-2,-1], [-2,0]], colour => 'red', css_class => 'path', style => { 'stroke-width' => 5 }