File Coverage

blib/lib/Games/Terrain/DiamondSquare.pm
Criterion Covered Total %
statement 61 64 95.3
branch 7 12 58.3
condition 5 8 62.5
subroutine 13 13 100.0
pod 1 4 25.0
total 87 101 86.1


line stmt bran cond sub pod time code
1             package Games::Terrain::DiamondSquare;
2             {
3             $Games::Terrain::DiamondSquare::VERSION = '0.02';
4             }
5              
6             ## ABSTRACT: Random terrain generation via the Diamond Square algorithm
7              
8 1     1   186838 use strict;
  1         2  
  1         35  
9 1     1   6 use warnings;
  1         1  
  1         34  
10 1     1   4 use List::Util 'sum';
  1         6  
  1         104  
11 1     1   955 use POSIX 'floor';
  1         9844  
  1         6  
12 1     1   1295 use base 'Exporter';
  1         2  
  1         112  
13             our @EXPORT_OK = 'create_terrain';
14              
15             my ( $FULL_SIZE, $ROUGHNESS );
16 1     1   5 use constant NW => 0;
  1         1  
  1         56  
17 1     1   5 use constant NE => 1;
  1         2  
  1         39  
18 1     1   5 use constant SW => 2;
  1         1  
  1         36  
19 1     1   5 use constant SE => 3;
  1         1  
  1         745  
20              
21             sub create_terrain {
22 1     1 1 10 my ( $height, $width, $roughness ) = @_;
23 1   50     4 $roughness ||= .5;
24              
25             # seed the four corners of the grid with random color values
26 1         4 my @corners = map {rand} 1 .. 4;
  4         50  
27              
28 1         3 $ROUGHNESS = $roughness;
29 1         2 $FULL_SIZE = $height + $width;
30 1         2 my @points;
31 1         5 subdivide( \@points, 0, 0, $height, $width, \@corners );
32 1         24 return \@points;
33             }
34              
35             sub subdivide {
36 9557     9557 0 13640 my ( $points, $x, $y, $height, $width, $corners ) = @_;
37              
38 9557 100 100     33795 if ( $height > 1 || $width > 1 ) {
39 2389         4852 my $new_height = floor( $height / 2 );
40 2389         4430 my $new_width = floor( $width / 2 );
41              
42 2389         8429 my $middle
43             = sum(@$corners) / 4 + displace( $new_height + $new_width );
44 2389         4266 my $edge_1 = ( $corners->[NW] + $corners->[NE] ) / 2;
45 2389         3054 my $edge_2 = ( $corners->[NE] + $corners->[SW] ) / 2;
46 2389         21333 my $edge_3 = ( $corners->[SW] + $corners->[SE] ) / 2;
47 2389         3345 my $edge_4 = ( $corners->[SE] + $corners->[NW] ) / 2;
48              
49             $_ = constrain($_)
50 2389         5069 foreach $middle, $edge_1, $edge_2, $edge_3, $edge_4;
51              
52             # do it again for each of the four new grids.
53 2389         10779 subdivide(
54             $points, $x, $y, $new_height, $new_width,
55             [ $corners->[NW], $edge_1, $middle, $edge_4 ]
56             );
57 2389         8907 subdivide(
58             $points, $x + $new_height, $y, $height - $new_height, $new_width,
59             [ $edge_1, $corners->[NE], $edge_2, $middle ]
60             );
61 2389         9578 subdivide(
62             $points, $x + $new_height, $y + $new_width, $height - $new_height,
63             $width - $new_width,
64             [ $middle, $edge_2, $corners->[SW], $edge_3 ]
65             );
66 2389         9995 subdivide(
67             $points, $x, $y + $new_width, $new_height, $width - $new_width,
68             [ $edge_4, $middle, $edge_3, $corners->[SE] ]
69             );
70             }
71             else # this is the "base case," where each grid piece is less than the size of a pixel.
72             {
73              
74             # the corners of the grid piece will be averaged and drawn as a single pixel.
75 7168         14764 my $c = sum(@$corners) / 4;
76              
77 7168         10651 $points->[$x][$y] = $c;
78 7168 50       18340 if ( $height == 2 ) {
79 0         0 $points->[ $x + 1 ][$y] = $c;
80             }
81 7168 50       13596 if ( $width == 2 ) {
82 0         0 $points->[$x][ $y + 1 ] = $c;
83             }
84 7168 50 33     15547 if ( $height == 2 and $width == 2 ) {
85 0         0 $points->[ $x + 1 ][ $y + 1 ] = $c;
86             }
87             }
88 9557         13707 return;
89             }
90              
91             sub constrain {
92 11945     11945 0 12370 my $num = shift;
93             return
94 11945 50       41865 $num < 0 ? 0
    50          
95             : $num > 1 ? 1
96             : $num;
97             }
98              
99             sub displace {
100 2389     2389 0 2701 my $curr_size = shift;
101              
102 2389         3068 my $max = $curr_size / $FULL_SIZE * $ROUGHNESS;
103 2389         5229 return ( rand() - 0.5 ) * $max;
104             }
105              
106             1;
107              
108             __END__