Introduce Path splintering

Primarily for use splintering parent Grid Paths onto Subgrids.
Only the sections of the Path present in the Subgrid get included.
main
Daniel Asher Resnick 11 months ago
parent b832bf9d00
commit 4681cb9010
  1. 19
      HexGrid.pm
  2. 99
      HexGrid/Path.pm
  3. 136
      wiki-map.pl

@ -68,6 +68,7 @@ sub make_path_from($this, $id, $tile_coords, %rest)
push @{$path->tiles}, $this->get_tile_at($pair->[0], $pair->[1]); push @{$path->tiles}, $this->get_tile_at($pair->[0], $pair->[1]);
} }
$this->add_path($path); $this->add_path($path);
return $path;
} }
sub add_image($this, $name, $source) sub add_image($this, $name, $source)
@ -118,7 +119,15 @@ sub subgrid_for_regions($this, @region_names)
embed_images => $this->{embed_images} embed_images => $this->{embed_images}
); );
$subgrid->add_region($this->{regions}{$_}) for @region_names; $subgrid->add_region($this->{regions}{$_}) for @region_names;
say STDERR Dumper($subgrid) if $DEBUG; # say STDERR Dumper($this->{paths});
foreach my $path (values %{$this->paths})
{
foreach my $splinter ($path->splinter($subgrid))
{
$subgrid->add_path($splinter);
}
}
say STDERR Dumper($subgrid->paths) if $DEBUG;
return $subgrid; return $subgrid;
} }
@ -155,6 +164,14 @@ sub subgrid_for_tiles($this, @coords_list)
} }
$subgrid->{regions}{$region->{name}}->add_tile($tile); $subgrid->{regions}{$region->{name}}->add_tile($tile);
} }
foreach my $path (values %{$this->paths})
{
foreach my $splinter ($path->splinter($subgrid))
{
$subgrid->add_path($splinter);
}
}
say STDERR Dumper($subgrid->paths) if $DEBUG;
return $subgrid; return $subgrid;
} }

@ -34,7 +34,10 @@ sub get_edge_direction($tile1, $tile2)
return $HexGrid::DIR{ne} if $nw_diff == 0 && $sw_diff == -1; return $HexGrid::DIR{ne} if $nw_diff == 0 && $sw_diff == -1;
return $HexGrid::DIR{n} if $nw_diff == 1 && $sw_diff == -1; return $HexGrid::DIR{n} if $nw_diff == 1 && $sw_diff == -1;
#TODO: should die here, to be caught/bubbled in render... #TODO: render and splinter should check this returns successfully;
carp("Tiles are not adjacent: " . $tile1->nw . "," . $tile1->sw . "—"
. $tile2->nw . "," . $tile2->sw);
return undef;
} }
sub curve_to($qx, $qy, $x, $y) sub curve_to($qx, $qy, $x, $y)
@ -45,15 +48,97 @@ sub curve_to($qx, $qy, $x, $y)
# Instance # Instance
sub clone_settings($this)
{
return HexGrid::Path->new
(
id => $this->id,
style => $this->style,
colour => $this->colour,
css_class => $this->css_class,
);
}
sub splinter($this, $grid)
{
my @splinters;
my $in_splinter = 0;
# If the base path sources at an edge, and the first tile is present,
# the first splinter must source at the same edge
# (the splinter implicitly exists since the first tile is present)
if($this->starts_from && $grid->get_tile_at($this->{tiles}[0]{nw}, $this->{tiles}[0]{sw}))
{
$in_splinter = 1;
my $splinter = $this->clone_settings;
$splinter->{id} .= "-0";
$splinter->{starts_from} = $this->starts_from;
push @splinters, $splinter;
}
for(my $i = 0; $i <= $#{$this->tiles}; $i++)
{
unless($in_splinter)
{
unless ($grid->get_tile_at($this->{tiles}[$i]{nw}, $this->{tiles}[$i]{sw}))
{
# Not in a splinter and tile not present, skip
next;
}
else
{
# Not in a splinter but tile present, start a new splinter,
# with source where previous tile would be
$in_splinter = 1;
my $splinter = $this->clone_settings;
# Don't set source on first tile
if($i >= 1)
{
$splinter->{starts_from} = get_edge_direction($this->{tiles}[$i], $this->{tiles}[$i-1]);
}
push @{$splinter->tiles}, $this->{tiles}[$i];
push @splinters, $splinter;
$splinter->{id} .= "-$#splinters";
}
}
else
{
if($grid->get_tile_at($this->{tiles}[$i]{nw}, $this->{tiles}[$i]{sw}))
{
# In a splinter and tile present, just extend current splinter with current tile
push @{$splinters[$#splinters]{tiles}}, $this->{tiles}[$i];
}
else
{
# In a splinter but tile not present, set previous tile sink to this missing tile
$in_splinter = 0;
$splinters[$#splinters]{ends_to} =
get_edge_direction($this->{tiles}[$i-1], $this->{tiles}[$i]);
}
}
}
# If the base path sinks at an edge and the last tile is present,
# the last splinter must sink at the same edge.
# (the splinter implicitly exists since the last tile is present)
my $last_tile = $this->{tiles}[$#{$this->tiles}];
if($this->ends_to && $grid->get_tile_at($last_tile->{nw}, $last_tile->{sw}))
{
$splinters[$#splinters]{ends_to} = $this->ends_to;
}
return @splinters;
}
sub render($this, $grid, $svg) sub render($this, $grid, $svg)
{ {
return unless @{$this->tiles}; return unless @{$this->tiles};
my $g = $svg->g(id => $this->id, class => $this->css_class); my $g = $svg->g(id => $this->id, class => $this->css_class);
my $current_tile = shift @{$this->tiles}; my @tiles = @{$this->tiles};
my $current_tile = shift @tiles;
# Single tile # Single tile
unless (@{$this->tiles}) unless (@tiles)
{ {
my ($cx, $cy) = $grid->coords_of_centre($current_tile->nw, $current_tile->sw); my ($cx, $cy) = $grid->coords_of_centre($current_tile->nw, $current_tile->sw);
if($this->starts_from) if($this->starts_from)
@ -63,7 +148,7 @@ sub render($this, $grid, $svg)
{ {
my ($x2, $y2) = $grid->coords_of_edge($current_tile->nw, $current_tile->sw, $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 # Curve from starts_from to ends_to with the centre as control point
$g->path(d => "M $x1,$y1 Q $cx,$cy $x2,$y2", $g->path(d => "M $x1,$y1 Q $cx,$cy $x2,$y2", fill => 'transparent',
stroke => $this->colour, style => $this->style, class => $this->css_class); stroke => $this->colour, style => $this->style, class => $this->css_class);
} }
else else
@ -95,7 +180,7 @@ sub render($this, $grid, $svg)
my ($x0, $x, $y0, $y); my ($x0, $x, $y0, $y);
my $path_spec; my $path_spec;
my $previous_tile = $current_tile; my $previous_tile = $current_tile;
$current_tile = shift @{$this->tiles}; $current_tile = shift @tiles;
my $next_edge = get_edge_direction($previous_tile, $current_tile); my $next_edge = get_edge_direction($previous_tile, $current_tile);
($x, $y) = $grid->coords_of_edge($previous_tile->nw, $previous_tile->sw, $next_edge); ($x, $y) = $grid->coords_of_edge($previous_tile->nw, $previous_tile->sw, $next_edge);
if($this->starts_from) if($this->starts_from)
@ -113,9 +198,9 @@ sub render($this, $grid, $svg)
my $previous_edge; # not defined yet my $previous_edge; # not defined yet
my $next_tile; # not defined yet my $next_tile; # not defined yet
while(@{$this->tiles}) while (@tiles)
{ {
$next_tile = shift @{$this->tiles}; $next_tile = shift @tiles;
$previous_edge = -$next_edge; $previous_edge = -$next_edge;
$next_edge = get_edge_direction($current_tile, $next_tile); $next_edge = get_edge_direction($current_tile, $next_tile);

@ -54,6 +54,8 @@ GetOptions
'regiondir=s' => \$regiondir 'regiondir=s' => \$regiondir
); );
# HexGrid::DEBUG();
$api_url // croak "Base API URL is required! Use --api-url to set"; $api_url // croak "Base API URL is required! Use --api-url to set";
my $grid = HexGrid->new(embed_images => $embed_images, defaults => { my $grid = HexGrid->new(embed_images => $embed_images, defaults => {
@ -203,7 +205,6 @@ my $background_query_results = $mw->api({ action => 'query',
iiprop => 'url' iiprop => 'url'
}) || carp $mw->{error}->{code} . ': ' . $mw->{error}->{details}; }) || carp $mw->{error}->{code} . ': ' . $mw->{error}->{details};
# say STDERR Dumper(\%background_pages);
foreach my $page (values %{$background_query_results->{query}{pages}}) foreach my $page (values %{$background_query_results->{query}{pages}})
{ {
if($page->{imageinfo}) if($page->{imageinfo})
@ -252,64 +253,6 @@ foreach my $page (values %{$tile_query_results->{query}{pages}})
} }
} }
say STDERR "Continuing Location processing";
$_->() for @location_continuations;
say STDERR "Getting Site pages";
my $site_query_results = $mw->api
( {
action => 'query',
generator => 'categorymembers',
prop => 'info|revisions',
gcmtitle => 'Category:Sites',
gcmlimit => 'max',
rvprop => 'content',
inprop => 'url',
} ) || croak $mw->{error}->{code} . ': ' . $mw->{error}->{details};
foreach my $site_page_ref (values %{$site_query_results->{query}{pages}})
{
next if $site_page_ref->{title} =~ /^Category:/;
my $site_name = $site_page_ref->{title};
say STDERR "Processing Site $site_name";
my $site_url = $site_page_ref->{canonicalurl};
my $site_content = $site_page_ref->{revisions}[0]{'*'};
my $parsed_template = MWTemplate::Parse($site_content, $site_template_name);
next unless $parsed_template;
my ($nw,$sw) = split /,/, $parsed_template->{named_params}{coords};
my $tile = $grid->get_tile_at($nw, $sw);
unless($tile)
{
carp "Coordinates of Site $site_name do not appear in the grid, skipping.";
next;
}
my $imageinfo_query_results = $mw->api({ action => 'query',
prop => 'imageinfo',
titles => "File:$parsed_template->{named_params}{icon}",
iiprop => 'url'
}) || carp $mw->{error}->{code} . ': ' . $mw->{error}->{details};
my %image_pages = %{$imageinfo_query_results->{query}{pages}};
my $image_url = (values %image_pages)[0]{imageinfo}[0]{url};
$grid->add_image(HexGrid::to_id($parsed_template->{named_params}{icon}), $image_url);
my $pin = HexGrid::Pin->new
(
name => $site_name,
id => HexGrid::to_id($site_name),
icon => HexGrid::to_id($parsed_template->{named_params}{icon}),
link => $site_url,
description => $parsed_template->{named_params}{abstract}
);
$tile->pin($pin);
}
my (%path_specs); my (%path_specs);
say STDERR "Getting Path pages"; say STDERR "Getting Path pages";
my $path_query_results = $mw->api my $path_query_results = $mw->api
@ -337,16 +280,18 @@ foreach my $path_page_ref (values %{$path_query_results->{query}{pages}})
$path_specs{$path_name} = $path_specs{$path_name} =
{ {
id => "$path_name-path", id => HexGrid::to_id($path_name) . "-path",
tile_page => "$path_name/Tiles", tile_page => "$path_name/Tiles",
colour => $parsed_template->{named_params}{colour}, colour => $parsed_template->{named_params}{colour},
stroke_width => $parsed_template->{named_params}{stroke_width} stroke_width => $parsed_template->{named_params}{stroke_width}
}; };
$path_specs{$path_name}{starts_from} = $parsed_template->{named_params}{starts_from}
if $parsed_template->{named_params}{starts_from};
$path_specs{$path_name}{ends_to} = $parsed_template->{named_params}{ends_to}
if $parsed_template->{named_params}{ends_to};
} }
say STDERR "Getting Path Tile pages"; say STDERR "Getting Path Tile pages";
my $path_tile_query_results = $mw->api my $path_tile_query_results = $mw->api
( { ( {
@ -368,11 +313,74 @@ foreach my $page (values %{$path_tile_query_results->{query}{pages}})
do { carp "Skipping bad spec: $coords"; next; } unless $coords =~ $coords_regex; do { carp "Skipping bad spec: $coords"; next; } unless $coords =~ $coords_regex;
push @path_coords, [$1,$2]; push @path_coords, [$1,$2];
} }
$grid->make_path_from($path_spec{id}, \@path_coords, css_class => 'path', colour => $path_spec{colour}, my $path = $grid->make_path_from($path_spec{id}, \@path_coords, css_class => 'path',
colour => $path_spec{colour},
style => { 'stroke-width' => $path_spec{stroke_width} // $default_path_stroke_width }); style => { 'stroke-width' => $path_spec{stroke_width} // $default_path_stroke_width });
$path->{starts_from} = $HexGrid::DIR{$path_spec{starts_from}}
if $path_spec{starts_from};
$path->{ends_to} = $HexGrid::DIR{$path_spec{ends_to}}
if $path_spec{ends_to};
} }
say STDERR "Continuing Location processing";
$_->() for @location_continuations;
say STDERR "Getting Site pages";
my $site_query_results = $mw->api
( {
action => 'query',
generator => 'categorymembers',
prop => 'info|revisions',
gcmtitle => 'Category:Sites',
gcmlimit => 'max',
rvprop => 'content',
inprop => 'url',
} ) || croak $mw->{error}->{code} . ': ' . $mw->{error}->{details};
foreach my $site_page_ref (values %{$site_query_results->{query}{pages}})
{
next if $site_page_ref->{title} =~ /^Category:/;
my $site_name = $site_page_ref->{title};
say STDERR "Processing Site $site_name";
my $site_url = $site_page_ref->{canonicalurl};
my $site_content = $site_page_ref->{revisions}[0]{'*'};
my $parsed_template = MWTemplate::Parse($site_content, $site_template_name);
next unless $parsed_template;
my ($nw,$sw) = split /,/, $parsed_template->{named_params}{coords};
my $tile = $grid->get_tile_at($nw, $sw);
unless($tile)
{
carp "Coordinates of Site $site_name do not appear in the grid, skipping.";
next;
}
my $imageinfo_query_results = $mw->api({ action => 'query',
prop => 'imageinfo',
titles => "File:$parsed_template->{named_params}{icon}",
iiprop => 'url'
}) || carp $mw->{error}->{code} . ': ' . $mw->{error}->{details};
my %image_pages = %{$imageinfo_query_results->{query}{pages}};
my $image_url = (values %image_pages)[0]{imageinfo}[0]{url};
$grid->add_image(HexGrid::to_id($parsed_template->{named_params}{icon}), $image_url);
my $pin = HexGrid::Pin->new
(
name => $site_name,
id => HexGrid::to_id($site_name),
icon => HexGrid::to_id($parsed_template->{named_params}{icon}),
link => $site_url,
description => $parsed_template->{named_params}{abstract}
);
$tile->pin($pin);
}
open (my $fh, "> $outfile") or croak "Couldn't open $outfile for writing: $!"; open (my $fh, "> $outfile") or croak "Couldn't open $outfile for writing: $!";
say $fh ($html_document ? wrap_in_html($grid) : $grid->render); say $fh ($html_document ? wrap_in_html($grid) : $grid->render);
close $fh; close $fh;
@ -399,6 +407,7 @@ if($regiondir)
} }
} }
say STDERR "Rendering Region's $region grid";
open (my $region_fh, "> $region.$extension") open (my $region_fh, "> $region.$extension")
or croak "Couldn't open $region.extension for writing: $!"; or croak "Couldn't open $region.extension for writing: $!";
say $region_fh ($html_document ? wrap_in_html($region_grid) : $region_grid->render); say $region_fh ($html_document ? wrap_in_html($region_grid) : $region_grid->render);
@ -407,6 +416,7 @@ if($regiondir)
# Location grids need to import images # Location grids need to import images
while(my ($location_name, $location_grid) = each %location_grids) while(my ($location_name, $location_grid) = each %location_grids)
{ {
say STDERR "Rendering Location's $location_name grid";
open (my $location_fh, "> $location_name.$extension") open (my $location_fh, "> $location_name.$extension")
or croak "Couldn't open $location_name.extension for writing: $!"; or croak "Couldn't open $location_name.extension for writing: $!";
say $location_fh ($html_document ? wrap_in_html($location_grid) : $location_grid->render); say $location_fh ($html_document ? wrap_in_html($location_grid) : $location_grid->render);

Loading…
Cancel
Save