line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Math::Geometry::Voronoi; |
2
|
|
|
|
|
|
|
|
3
|
2
|
|
|
2
|
|
48884
|
use 5.008; |
|
2
|
|
|
|
|
9
|
|
|
2
|
|
|
|
|
88
|
|
4
|
2
|
|
|
2
|
|
11
|
use strict; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
77
|
|
5
|
2
|
|
|
2
|
|
17
|
use warnings; |
|
2
|
|
|
|
|
12
|
|
|
2
|
|
|
|
|
160
|
|
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
our $VERSION = '1.3'; |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
require XSLoader; |
10
|
|
|
|
|
|
|
XSLoader::load('Math::Geometry::Voronoi', $VERSION); |
11
|
|
|
|
|
|
|
|
12
|
2
|
|
|
2
|
|
2331
|
use Params::Validate qw(validate ARRAYREF CODEREF); |
|
2
|
|
|
|
|
28829
|
|
|
2
|
|
|
|
|
201
|
|
13
|
2
|
|
|
2
|
|
20
|
use List::Util qw(min max sum); |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
290
|
|
14
|
|
|
|
|
|
|
|
15
|
2
|
|
|
2
|
|
12
|
use base 'Class::Accessor::Fast'; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
2175
|
|
16
|
|
|
|
|
|
|
__PACKAGE__->mk_accessors( |
17
|
|
|
|
|
|
|
qw(points lines edges vertices xmin ymin xmax ymax)); |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
sub new { |
20
|
3
|
|
|
3
|
1
|
16951
|
my $pkg = shift; |
21
|
3
|
|
|
|
|
404
|
my %args = validate(@_, {points => {type => ARRAYREF}}); |
22
|
2
|
|
|
|
|
18
|
my $self = bless({points => $args{points}}, $pkg); |
23
|
|
|
|
|
|
|
|
24
|
2
|
|
|
|
|
11
|
$self->sort_points(); |
25
|
|
|
|
|
|
|
|
26
|
2
|
|
|
|
|
14
|
return $self; |
27
|
|
|
|
|
|
|
} |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
# C code needs points sorted by y then by x and needs min and max for |
30
|
|
|
|
|
|
|
# both - should provide a way for the client to provide this |
31
|
|
|
|
|
|
|
sub sort_points { |
32
|
2
|
|
|
2
|
0
|
3
|
my $self = shift; |
33
|
2
|
|
|
|
|
13
|
my $points = $self->points(); |
34
|
|
|
|
|
|
|
|
35
|
7227
|
50
|
|
|
|
12031
|
@$points = |
36
|
2
|
|
|
|
|
55
|
sort { $a->[1] <=> $b->[1] || $a->[0] <=> $b->[0] } @$points; |
37
|
|
|
|
|
|
|
|
38
|
2
|
|
|
|
|
43
|
$self->ymin($points->[0][1]); |
39
|
2
|
|
|
|
|
24
|
$self->ymax($points->[-1][1]); |
40
|
|
|
|
|
|
|
|
41
|
2
|
|
|
|
|
15
|
my @x = map { $_->[0] } @$points; |
|
1101
|
|
|
|
|
1430
|
|
42
|
2
|
|
|
|
|
129
|
$self->xmin(min(@x)); |
43
|
2
|
|
|
|
|
37
|
$self->xmax(max(@x)); |
44
|
|
|
|
|
|
|
|
45
|
2
|
|
|
|
|
47
|
return; |
46
|
|
|
|
|
|
|
} |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
sub compute { |
49
|
2
|
|
|
2
|
1
|
4338
|
my $self = shift; |
50
|
|
|
|
|
|
|
|
51
|
2
|
|
|
|
|
10
|
my $result = compute_voronoi_xs($self->points, |
52
|
|
|
|
|
|
|
$self->xmin, |
53
|
|
|
|
|
|
|
$self->xmax, |
54
|
|
|
|
|
|
|
$self->ymin, |
55
|
|
|
|
|
|
|
$self->ymax); |
56
|
|
|
|
|
|
|
|
57
|
2
|
|
|
|
|
10453
|
$self->lines($result->{lines}); |
58
|
2
|
|
|
|
|
154
|
$self->vertices($result->{vertices}); |
59
|
2
|
|
|
|
|
17
|
$self->edges($result->{edges}); |
60
|
|
|
|
|
|
|
|
61
|
2
|
|
|
|
|
20
|
return; |
62
|
|
|
|
|
|
|
} |
63
|
|
|
|
|
|
|
|
64
|
3277
|
|
|
3277
|
0
|
5803
|
sub cmp_verts_ab { return cmp_verts($a,$b); } |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
# a low x value is placed before a high x value. if both x values |
67
|
|
|
|
|
|
|
# are the same, a high y value is placed before a low y value. |
68
|
|
|
|
|
|
|
sub cmp_verts { |
69
|
3319
|
|
100
|
3319
|
0
|
22636
|
return ($_[0]->[0] <=> $_[1]->[0] || $_[1]->[1] <=> $_[0]->[1] ); |
70
|
|
|
|
|
|
|
} |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
sub vert_inside_bounds { |
74
|
12390
|
|
|
12390
|
0
|
16601
|
my ($self, $x,$y) = @_; |
75
|
|
|
|
|
|
|
return ( |
76
|
12390
|
|
100
|
|
|
25875
|
$x >= $self->xmin and $x <= $self->xmax and |
77
|
|
|
|
|
|
|
$y >= $self->ymin and $y <= $self->ymax |
78
|
|
|
|
|
|
|
); |
79
|
|
|
|
|
|
|
} |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
sub boundry_interesection_verts { |
83
|
3271
|
|
|
3271
|
0
|
4385
|
my($self, $a,$b,$c) = @_; |
84
|
3271
|
|
|
|
|
4124
|
my $verts = []; |
85
|
|
|
|
|
|
|
|
86
|
3271
|
100
|
|
|
|
6080
|
if($b){ |
87
|
3111
|
|
|
|
|
7382
|
my $v1 = [$self->xmin,($c-$a*$self->xmin)/$b]; |
88
|
3111
|
|
|
|
|
28245
|
my $v2 = [$self->xmax,($c-$a*$self->xmax)/$b]; |
89
|
3111
|
100
|
|
|
|
24146
|
push ( @$verts, $v1 ) if ( $self->vert_inside_bounds( @$v1 ) ); |
90
|
3111
|
100
|
|
|
|
79670
|
push ( @$verts, $v2 ) if ( $self->vert_inside_bounds( @$v2 ) ); |
91
|
|
|
|
|
|
|
} |
92
|
|
|
|
|
|
|
|
93
|
3271
|
100
|
|
|
|
74307
|
if($a){ |
94
|
3084
|
|
|
|
|
7441
|
my $v1 = [($c-$b*$self->ymax)/$a,$self->ymax]; |
95
|
3084
|
|
|
|
|
25180
|
my $v2 = [($c-$b*$self->ymin)/$a,$self->ymin]; |
96
|
3084
|
100
|
|
|
|
24801
|
push ( @$verts, $v1 ) if ( $self->vert_inside_bounds( @$v1 ) ); |
97
|
3084
|
100
|
|
|
|
43942
|
push ( @$verts, $v2 ) if ( $self->vert_inside_bounds( @$v2 ) ); |
98
|
|
|
|
|
|
|
} |
99
|
|
|
|
|
|
|
|
100
|
3271
|
|
|
|
|
56366
|
$verts; |
101
|
|
|
|
|
|
|
} |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
sub polygons { |
105
|
2
|
|
|
2
|
1
|
143316
|
my $self = shift; |
106
|
2
|
|
|
|
|
64
|
my %args = validate(@_, |
107
|
|
|
|
|
|
|
{normalize_vertices => {type => CODEREF, |
108
|
|
|
|
|
|
|
optional => 1 |
109
|
|
|
|
|
|
|
}, |
110
|
|
|
|
|
|
|
}); |
111
|
2
|
|
|
|
|
19
|
my $points = $self->points; |
112
|
2
|
|
|
|
|
18
|
my $lines = $self->lines; |
113
|
2
|
|
|
|
|
16
|
my $edges = $self->edges; |
114
|
2
|
|
|
|
|
14
|
my $vertices = $self->vertices; |
115
|
|
|
|
|
|
|
|
116
|
2
|
100
|
|
|
|
17
|
if (my $norm = $args{normalize_vertices}) { |
117
|
1
|
|
|
|
|
13
|
$vertices = [map { [$norm->($_->[0]), $norm->($_->[1])] } @$vertices]; |
|
2166
|
|
|
|
|
9745
|
|
118
|
|
|
|
|
|
|
} |
119
|
|
|
|
|
|
|
|
120
|
2
|
|
|
|
|
228
|
my @edges_by_point; |
121
|
2
|
|
|
|
|
10
|
EDGE: foreach my $edge (@$edges) { |
122
|
3271
|
|
|
|
|
5349
|
my ($l, $v1, $v2) = @$edge; |
123
|
3271
|
50
|
66
|
|
|
7203
|
next EDGE if( $v1 == -1 and $v2 == -1 ); |
124
|
3271
|
|
|
|
|
3014
|
my ($lon1, $lat1, $lon2, $lat2); |
125
|
|
|
|
|
|
|
|
126
|
3271
|
|
|
|
|
2971
|
my $ivs = $self->boundry_interesection_verts(@{$lines->[$l]}); |
|
3271
|
|
|
|
|
9201
|
|
127
|
3271
|
|
|
|
|
9430
|
$ivs = [sort cmp_verts_ab @$ivs]; |
128
|
|
|
|
|
|
|
|
129
|
3271
|
100
|
|
|
|
9482
|
if( my $norm = $args{normalize_vertices}) { |
130
|
3260
|
|
|
|
|
4299
|
$ivs = [map { [$norm->($_->[0]), $norm->($_->[1])] } @$ivs]; |
|
6522
|
|
|
|
|
25685
|
|
131
|
|
|
|
|
|
|
} |
132
|
|
|
|
|
|
|
|
133
|
3271
|
100
|
|
|
|
29521
|
($lat1,$lon1) = @{$vertices->[$v1]} if( $v1 != -1 ); |
|
3258
|
|
|
|
|
9250
|
|
134
|
3271
|
100
|
|
|
|
6075
|
($lat2,$lon2) = @{$vertices->[$v2]} if( $v2 != -1 ); |
|
3258
|
|
|
|
|
5806
|
|
135
|
|
|
|
|
|
|
|
136
|
3271
|
100
|
|
|
|
6204
|
if( $v1 == -1 ) { |
137
|
13
|
50
|
33
|
|
|
120
|
next EDGE unless( @$ivs and $lat2 +0 == $lat2 and $lon2 +0 == $lon2 ); |
|
|
|
33
|
|
|
|
|
138
|
13
|
100
|
|
|
|
40
|
if( cmp_verts( [$lat2,$lon2], $ivs->[0] ) > 0 ) { |
|
|
50
|
|
|
|
|
|
139
|
7
|
|
|
|
|
10
|
($lat1,$lon1) = @{$ivs->[0]}; |
|
7
|
|
|
|
|
16
|
|
140
|
|
|
|
|
|
|
} elsif( cmp_verts( [$lat2,$lon2], $ivs->[1] ) > 0 ) { |
141
|
0
|
|
|
|
|
0
|
($lat1,$lon1) = @{$ivs->[1]}; |
|
0
|
|
|
|
|
0
|
|
142
|
|
|
|
|
|
|
} else { |
143
|
6
|
|
|
|
|
25
|
next EDGE; |
144
|
|
|
|
|
|
|
} |
145
|
|
|
|
|
|
|
} |
146
|
3265
|
100
|
|
|
|
5700
|
if( $v2 == -1 ) { |
147
|
13
|
50
|
33
|
|
|
88
|
next EDGE unless( @$ivs and $lat1 +0 == $lat1 and $lon1 +0 == $lon1 ); |
|
|
|
33
|
|
|
|
|
148
|
13
|
100
|
|
|
|
54
|
if( cmp_verts( [$lat1,$lon1], $ivs->[1] ) < 0 ) { |
|
|
50
|
|
|
|
|
|
149
|
3
|
|
|
|
|
5
|
($lat2,$lon2) = @{$ivs->[1]}; |
|
3
|
|
|
|
|
6
|
|
150
|
|
|
|
|
|
|
} elsif( cmp_verts( [$lat1,$lon1], $ivs->[0] ) < 0 ) { |
151
|
0
|
|
|
|
|
0
|
($lat2,$lon2) = @{$ivs->[0]}; |
|
0
|
|
|
|
|
0
|
|
152
|
|
|
|
|
|
|
} else { |
153
|
10
|
|
|
|
|
38
|
next EDGE; |
154
|
|
|
|
|
|
|
} |
155
|
|
|
|
|
|
|
} |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
# if any of the coords are NaN things break. |
158
|
3255
|
50
|
|
|
|
4159
|
next EDGE if( grep {$_ +0 != $_ } ($lat1,$lon1,$lat2,$lon2)); |
|
13020
|
|
|
|
|
25629
|
|
159
|
|
|
|
|
|
|
|
160
|
3255
|
|
|
|
|
5579
|
my ($p1, $p2) = ($lines->[$l][3], $lines->[$l][4]); |
161
|
|
|
|
|
|
|
|
162
|
3255
|
50
|
33
|
|
|
12762
|
if ($p1 != -1 and $p2 != -1) { |
163
|
3255
|
|
|
|
|
4503
|
foreach my $p ($p1, $p2) { |
164
|
6510
|
|
|
|
|
6093
|
push @{$edges_by_point[$p]}, [$lat1, $lon1, $lat2, $lon2]; |
|
6510
|
|
|
|
|
26200
|
|
165
|
|
|
|
|
|
|
} |
166
|
|
|
|
|
|
|
} |
167
|
|
|
|
|
|
|
} |
168
|
|
|
|
|
|
|
|
169
|
2
|
|
|
|
|
6
|
my @polygons; |
170
|
2
|
|
|
|
|
7
|
foreach my $p (0 .. $#$points) { |
171
|
1101
|
|
|
|
|
1944
|
my $stack = $edges_by_point[$p]; |
172
|
1101
|
50
|
|
|
|
3466
|
next unless $stack; |
173
|
|
|
|
|
|
|
# can't make a polygon with less than 2 edges |
174
|
1101
|
100
|
|
|
|
3661
|
next unless @$stack >= 2; |
175
|
|
|
|
|
|
|
|
176
|
1100
|
|
|
|
|
10338
|
my @poly = (); |
177
|
1100
|
|
|
|
|
2216
|
foreach my $this ( @$stack ) { |
178
|
6509
|
50
|
66
|
|
|
18231
|
if( |
|
|
|
66
|
|
|
|
|
179
|
23002
|
100
|
|
|
|
92845
|
!grep { $_->[0] == $this->[0] && $_->[1] == $this->[1] } @poly |
180
|
|
|
|
|
|
|
and $this->[0] +0 == $this->[0] |
181
|
|
|
|
|
|
|
and $this->[1] +0 == $this->[1] |
182
|
|
|
|
|
|
|
) { |
183
|
2985
|
|
|
|
|
7610
|
push @poly, [$this->[0],$this->[1]]; |
184
|
|
|
|
|
|
|
} |
185
|
6509
|
50
|
66
|
|
|
9641
|
if( |
|
|
|
66
|
|
|
|
|
186
|
25987
|
100
|
|
|
|
127155
|
!grep { $_->[0] == $this->[2] && $_->[1] == $this->[3] } @poly |
187
|
|
|
|
|
|
|
and $this->[2] +0 == $this->[2] |
188
|
|
|
|
|
|
|
and $this->[3] +0 == $this->[3] |
189
|
|
|
|
|
|
|
) { |
190
|
3505
|
|
|
|
|
10696
|
push @poly, [$this->[2],$this->[3]]; |
191
|
|
|
|
|
|
|
} |
192
|
|
|
|
|
|
|
} |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
#TODO: if this point is the closest point to a corner... |
195
|
|
|
|
|
|
|
# add that corner as a vert on this poly |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
# sort poly's verts (anti?) clockwise around the point $points->[$p]; |
198
|
10617
|
|
|
|
|
23484
|
@poly = sort { |
199
|
1100
|
|
|
|
|
3724
|
my($lat1,$lon1) = ( |
200
|
|
|
|
|
|
|
$a->[0] - $points->[$p]->[0], |
201
|
|
|
|
|
|
|
$a->[1] - $points->[$p]->[1] |
202
|
|
|
|
|
|
|
); |
203
|
|
|
|
|
|
|
|
204
|
10617
|
|
|
|
|
23780
|
my($lat2,$lon2) = ( |
205
|
|
|
|
|
|
|
$b->[0] - $points->[$p]->[0], |
206
|
|
|
|
|
|
|
$b->[1] - $points->[$p]->[1] |
207
|
|
|
|
|
|
|
); |
208
|
10617
|
|
|
|
|
26566
|
return atan2($lon1,$lat1) <=> atan2($lon2,$lat2); |
209
|
|
|
|
|
|
|
} @poly; |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
# make a list of the first points |
212
|
1100
|
|
|
|
|
1861
|
push @polygons, [$p, map { [$_->[0], $_->[1]] } @poly]; |
|
6490
|
|
|
|
|
23730
|
|
213
|
|
|
|
|
|
|
} |
214
|
|
|
|
|
|
|
|
215
|
2
|
|
|
|
|
4666
|
return @polygons; |
216
|
|
|
|
|
|
|
} |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
sub _dump_poly { |
219
|
0
|
|
|
0
|
|
|
my $poly = shift; |
220
|
|
|
|
|
|
|
return |
221
|
0
|
|
|
|
|
|
"[ \n\t" . join(", \n\t", map { "[$_->[0],$_->[1]]" } @$poly) . " ]\n"; |
|
0
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
} |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
1; |
225
|
|
|
|
|
|
|
__END__ |