File Coverage

blib/lib/Math/Shape/Point.pm
Criterion Covered Total %
statement 95 95 100.0
branch 18 18 100.0
condition 12 15 80.0
subroutine 22 22 100.0
pod 16 16 100.0
total 163 166 98.1


line stmt bran cond sub pod time code
1             package Math::Shape::Point;
2             $Math::Shape::Point::VERSION = '1.05';
3 1     1   77627 use strict;
  1         3  
  1         31  
4 1     1   5 use warnings;
  1         1  
  1         23  
5 1     1   19 use 5.008;
  1         7  
  1         38  
6 1     1   5 use Math::Trig ':pi';
  1         1  
  1         134  
7 1     1   913 use Regexp::Common;
  1         3163  
  1         6  
8 1     1   61413 use Carp 'croak';
  1         2  
  1         1242  
9              
10             # ABSTRACT: a 2d point object in cartesian space with utility angle methods
11              
12              
13             sub new {
14 6 100   6 1 121 croak 'Incorrect number of arguments for new()' unless @_ == 4;
15 4         9 my ($class, $x, $y, $r) = @_;
16 4         25 my $self = bless { x => $x,
17             y => $y,
18             r => 0,
19             },
20             $class;
21 4         14 $self->rotate($r);
22 4         30 return $self;
23             }
24              
25              
26 56     56 1 238 sub get_location { [$_[0]->{x}, $_[0]->{y}] }
27              
28              
29              
30             sub set_location {
31 15     15 1 757 my ($self, $x, $y) = @_;
32 15         23 $self->{x} = $x;
33 15         21 $self->{y} = $y;
34 15         48 1;
35             }
36              
37              
38             sub get_direction {
39 24     24 1 83 return $_[0]->{r};
40             }
41              
42              
43             sub set_direction {
44 3     3 1 11 $_[0]->{r} = $_[0]->normalize_radian($_[1]);
45 3         11 1;
46             }
47              
48              
49             sub advance {
50 2     2 1 12 $_[0]->{x} += int(sin($_[0]->{r}) * $_[1]);
51 2         10 $_[0]->{y} += int(cos($_[0]->{r}) * $_[1]);
52 2         9 1;
53             }
54              
55              
56             sub retreat {
57 2     2 1 13 $_[0]->{x} -= int(sin($_[0]->{r}) * $_[1]);
58 2         10 $_[0]->{y} -= int(cos($_[0]->{r}) * $_[1]);
59 2         9 1;
60             }
61              
62              
63             sub move_left {
64 2     2 1 12 $_[0]->{x} += int(sin( $_[0]->{r} - pip2 ) * $_[1]);
65 2         10 $_[0]->{y} += int(cos( $_[0]->{r} - pip2 ) * $_[1]);
66 2         9 1;
67             }
68              
69              
70             sub move_right {
71 2     2 1 12 $_[0]->{x} += int(sin( $_[0]->{r} + pip2 ) * $_[1]);
72 2         9 $_[0]->{y} += int(cos( $_[0]->{r} + pip2 ) * $_[1]);
73 2         7 1;
74             }
75              
76              
77             sub rotate {
78 17     17 1 60 $_[0]->{r} = $_[0]->normalize_radian($_[0]->{r} + $_[1]);
79 17         35 1;
80             }
81              
82              
83             sub rotate_about_point {
84 8     8 1 17 my ($self, $origin, $r) = @_;
85              
86 8         16 my $nr = $self->normalize_radian($r);
87             # $nr = $nr > 0 ? pi2 - $nr
88             # : abs $nr;
89              
90 8         45 my $s = sin $nr;
91 8         32 my $c = cos $nr;
92              
93 8         14 $self->{x} -= $origin->{x};
94 8         17 $self->{y} -= $origin->{y};
95              
96             # rotate point
97 8         19 my $xnew = $self->{x} * $c - $self->{y} * $s;
98 8         18 my $ynew = $self->{x} * $s + $self->{y} * $c;
99              
100             # translate point back:
101 8         16 $self->{x} = int $xnew + $origin->{x};
102 8         12 $self->{y} = int $ynew + $origin->{y};
103              
104 8         19 $self->rotate($r);
105 8         29 1;
106             }
107              
108              
109             sub get_distance_to_point {
110 5     5 1 43 sqrt ( abs($_[0]->{x} - $_[1]->{x}) ** 2 + abs($_[0]->{y} - $_[1]->{y}) ** 2);
111             }
112              
113              
114             sub get_angle_to_point {
115 21     21 1 30 my ($self, $p) = @_;
116              
117             # check points are not at the same location
118 21 100 100     37 if ($self->get_location->[0] == $p->get_location->[0]
119             && $self->get_location->[1] == $p->get_location->[1])
120             {
121 1         23 croak 'Error: points are at the same location';
122             }
123              
124 20         90 my $atan = atan2($p->{y} - $self->{y}, $p->{x} - $self->{x});
125              
126 20 100       54 if ($atan <= 0) { # lower half
    100          
127 9         27 return abs($atan) + pip2 + $self->get_direction;
128             }
129             elsif ($atan <= pip2) { # upper right quadrant
130 6         14 return abs($atan - pip2) + $self->get_direction;
131             }
132             else { # upper left quadrant
133 5         19 return pi2 - $atan + pip2 + $self->get_direction;
134             }
135             }
136              
137              
138             sub get_direction_to_point {
139 21     21 1 40 my ($self, $p) = @_;
140 21         44 my $angle = $self->get_angle_to_point($p);
141 20 100 66     160 if ($angle > 0 - pip4 && $angle <= pip4) { return 'front' }
  6 100 66     27  
    100 66        
142 5         23 elsif ($angle > pip4 && $angle <= pi - pip4) { return 'right' }
143 4         19 elsif ($angle > pi - pip4 && $angle <= pi + pip4) { return 'back' }
144 5         22 return 'left';
145             }
146              
147              
148             sub normalize_radian {
149 36     36 1 57 my ($self, $radians) = @_;
150              
151 36         51 my $pi_ratio = $radians / pi2;
152 36 100       134 $pi_ratio < 1
153             ? $radians
154             : $radians - pi2 * int $pi_ratio;
155             }
156              
157              
158             sub print_coordinates {
159 1     1 1 3 my $self = shift;
160              
161 1         217 print "Coordinates x: $self->{x}, y: $self->{y}, r: $self->{r}\n";
162              
163             # print grid
164 1         6 my $min_x = $self->{x} + -10;
165 1         2 my $max_x = $self->{x} + 10;
166 1         2 my $min_y = $self->{y} + -10;
167 1         3 my $max_y = $self->{y} + 10;
168              
169 1         40 print ' ';
170 1         3 for ($min_x..$max_x) { printf "%3s", $_ }
  21         470  
171 1         92 printf "%3s", "x\n";
172 1         7 for my $y (reverse $min_y..$max_y) {
173 21         5165 printf "%3s", $y;
174 21         57 for my $x ($min_x..$max_x) {
175 441 100 100     1429 if ($self->{x} == $x && $self->{y} == $y) { printf "%3s", '@' }
  1         28  
176 440         13201 else { printf "%3s", '.' }
177             }
178 21         2626 print "\n";
179             }
180 1         13 printf "%3s", "y\n";
181 1         12 1;
182             }
183              
184             1;
185              
186             __END__