File Coverage

lib/SVG/Estimate/Path/Arc.pm
Criterion Covered Total %
statement 108 108 100.0
branch 22 30 73.3
condition n/a
subroutine 7 7 100.0
pod 1 3 33.3
total 138 148 93.2


line stmt bran cond sub pod time code
1             package SVG::Estimate::Path::Arc;
2             $SVG::Estimate::Path::Arc::VERSION = '1.0114';
3 10     10   1262 use Moo;
  10         23  
  10         57  
4 10     10   3156 use Math::Trig qw/pi acos deg2rad rad2deg/;
  10         22  
  10         712  
5 10     10   791 use Clone qw/clone/;
  10         4052  
  10         457  
6 10     10   67 use strict;
  10         23  
  10         11221  
7              
8             extends 'SVG::Estimate::Path::Command';
9             with 'SVG::Estimate::Role::Pythagorean';
10             with 'SVG::Estimate::Role::SegmentLength';
11              
12             =head1 NAME
13              
14             SVG::Estimate::Path::Arc - Handles estimating arcs.
15              
16             =head1 VERSION
17              
18             version 1.0114
19              
20             =head1 SYNOPSIS
21              
22             my $arc = SVG::Estimate::Path::Arc->new(
23             transformer => $transform,
24             start_point => [13, 19],
25             x => 45,
26             y => 13,
27             rx => 1,
28             ry => 3,
29             x_axis_rotation => 0,
30             large_arc_flag => 0,
31             sweep_flag => 0,
32             );
33              
34             my $length = $arc->length;
35              
36             =head1 INHERITANCE
37              
38             This class extends L and consumes L, L, and L.
39              
40             =head1 METHODS
41              
42             =head2 new()
43              
44             Constructor.
45              
46             =over
47              
48             =item x
49              
50             The x coordinate for the end-point of the arc.
51              
52             =item y
53              
54             The y coordinate for the end-point of the arc.
55              
56             =item rx
57              
58             Float representing the x radius.
59              
60             =item ry
61              
62             Float representing the y radius.
63              
64             =item x_axis_rotation
65              
66             Float that indicates how the ellipse as a whole is rotated relative to the current coordinate system.
67              
68             =item large_arc_flag
69              
70             Must be 1 or 0. See details L.
71              
72             =item sweep_flag
73              
74             Must be 1 or 0. See details L.
75              
76             =back
77              
78             =cut
79              
80             has rx => (
81             is => 'ro',
82             required => 1,
83             );
84              
85             has ry => (
86             is => 'ro',
87             required => 1,
88             );
89              
90             has x_axis_rotation => (
91             is => 'ro',
92             required => 1,
93             );
94              
95             has large_arc_flag => (
96             is => 'ro',
97             required => 1,
98             );
99              
100             has sweep_flag => (
101             is => 'ro',
102             required => 1,
103             );
104              
105             has x => (
106             is => 'ro',
107             required => 1,
108             );
109              
110             has y => (
111             is => 'ro',
112             required => 1,
113             );
114              
115             ##Used for conversion from endpoint to center parameterization
116             has _delta => (
117             is => 'rw',
118             );
119              
120             has _theta => (
121             is => 'rw',
122             );
123              
124             has _center => (
125             is => 'rw',
126             );
127              
128             sub BUILDARGS {
129 3     3 0 11278 my ($class, @args) = @_;
130             ##Upgrade to hashref
131 3 100       20 my $args = @args % 2 ? $args[0] : { @args };
132 3 100       19 if ($args->{transformer}->has_transforms) {
133             ##The start point and end point are in different coordinate systems (view and user, respectively).
134             ##To make the set of point in the user space, transform the start_point into user space
135             ##Then run all the calculations
136 1         8 my $view_start_point = clone $args->{start_point};
137 1         5 $args->{start_point} = $args->{transformer}->untransform($args->{start_point});
138 1         585 $class->endpoint_to_center($args);
139 1         2 my $point;
140 1         1 my $first = 1;
141 1         1 my $start;
142 1         1 my $length = 0;
143 1         3 POINT: for (my $t=0; $t<=1; $t+=1/12) {
144 13         21 $point = $class->this_point($args, $t);
145 13         31 $point = $args->{transformer}->transform($point);
146 13 100       1802 if ($first) {
147 1         1 $first = 0;
148 1         2 $start = $point;
149 1         2 $args->{min_x} = $args->{max_x} = $point->[0];
150 1         3 $args->{min_y} = $args->{max_y} = $point->[1];
151 1         4 next POINT;
152             }
153 12         23 $length += $class->pythagorean($start, $point);
154 12 50       25 $args->{min_x} = $point->[0] if $point->[0] < $args->{min_x};
155 12 50       16 $args->{min_y} = $point->[1] if $point->[1] < $args->{min_y};
156 12 50       17 $args->{max_x} = $point->[0] if $point->[0] > $args->{max_x};
157 12 50       16 $args->{max_y} = $point->[1] if $point->[1] > $args->{max_y};
158 12         23 $start = $point;
159             }
160             ##Restore the original start point in the viewport coordinate system
161 1         2 $args->{start_point} = $view_start_point;
162 1         2 $args->{end_point} = $point;
163 1         2 $args->{shape_length} = $length;
164 1         2 $args->{travel_length} = 0;
165 1         17 return $args;
166             }
167 2         8 $class->endpoint_to_center($args);
168 2         7 my $start = $class->this_point($args, 0);
169 2         8 my $end = $class->this_point($args, 1);
170 2 50       11 $args->{min_x} = $start->[0] < $end->[0] ? $start->[0] : $end->[0];
171 2 100       10 $args->{max_x} = $start->[0] > $end->[0] ? $start->[0] : $end->[0];
172 2 50       9 $args->{min_y} = $start->[1] < $end->[1] ? $start->[1] : $end->[1];
173 2 50       9 $args->{max_y} = $start->[1] > $end->[1] ? $start->[1] : $end->[1];
174 2         12 $args->{shape_length} = $class->segment_length($args, 0, 1, $start, $end, 1e-4, 5, 0);
175 2         6 $args->{travel_length} = 0;
176 2         22 $args->{end_point} = clone $end;
177 2         63 return $args;
178             }
179              
180             sub endpoint_to_center {
181 3     3 0 6 my $class = shift;
182 3         5 my $args = shift;
183 3         20 my $rotr = deg2rad($args->{x_axis_rotation});
184 3         71 my $cosr = cos $rotr;
185 3         8 my $sinr = sin $rotr;
186 3         12 my $dx = ($args->{start_point}->[0] - $args->{x}) / 2; #*
187 3         10 my $dy = ($args->{start_point}->[1] - $args->{y}) / 2; #*
188              
189 3         8 my $x1prim = $cosr * $dx + $sinr * $dy; #*
190 3         10 my $y1prim = -1*$sinr * $dx + $cosr * $dy; #*
191              
192 3         7 my $x1prim_sq = $x1prim**2; #*
193 3         8 my $y1prim_sq = $y1prim**2; #*
194              
195 3         6 my $rx = $args->{rx}; #*
196 3         13 my $ry = $args->{ry}; #*
197              
198 3         7 my $rx_sq = $rx**2; #*
199 3         5 my $ry_sq = $ry**2; #*
200              
201 3         7 my $t1 = $rx_sq * $y1prim_sq;
202 3         4 my $t2 = $ry_sq * $x1prim_sq;
203 3         5 my $ts = $t1 + $t2;
204 3         9 my $c = sqrt(abs( (($rx_sq * $ry_sq) - $ts) / ($ts) ) );
205              
206 3 100       22 if ($args->{large_arc_flag} == $args->{sweep_flag}) {
207 2         4 $c *= -1;
208             }
209 3         9 my $cxprim = $c * $rx * $y1prim / $ry;
210 3         9 my $cyprim = -1 *$c * $ry * $x1prim / $rx;
211              
212             $args->{_center} = [
213             ($cosr * $cxprim - $sinr * $cyprim) + ( ($args->{start_point}->[0] + $args->{x}) / 2 ),
214 3         18 ($sinr * $cxprim + $cosr * $cyprim) + ( ($args->{start_point}->[1] + $args->{y}) / 2 )
215             ];
216              
217             ##**
218              
219             ##Theta calculation
220 3         7 my $ux = ($x1prim - $cxprim) / $rx; #*
221 3         8 my $uy = ($y1prim - $cyprim) / $ry; #*
222 3         8 my $n = sqrt($ux**2 + $uy**2);
223 3         9 my $p = $ux;
224 3         5 my $d = $p / $n;
225 3         17 my $theta = rad2deg(acos($p/$n));
226 3 50       72 if ($uy < 0) {
227 3         6 $theta *= -1;
228             }
229 3         7 $args->{_theta} = $theta % 360;
230              
231 3         7 my $vx = -1 * ($x1prim + $cxprim) / $rx;
232 3         5 my $vy = -1 * ($y1prim + $cyprim) / $ry;
233 3         9 $n = sqrt( ($ux**2 + $uy**2) * ($vx**2 + $vy**2));
234 3         4 $p = $ux*$vx + $uy*$vy;
235 3         7 $d = $p / $n;
236              
237 3         9 my $delta = rad2deg(acos($d));
238 3 100       39 if (($ux * $vy - $uy * $vx) < 0 ) {
239 2         2 $delta *= -1;
240             }
241 3         6 $delta = $delta % 360;
242              
243 3 100       9 if (! $args->{sweep_flag}) {
244 2         8 $delta -= 360;
245             }
246 3         10 $args->{_delta} = $delta;
247             }
248              
249             =head2 this_point (args, t)
250              
251             Calculate a point on the graph, normalized from start point to end point as t, in 2-D space
252              
253             =cut
254              
255             sub this_point {
256 271     271 1 296 my $class = shift;
257 271         237 my $args = shift;
258 271         253 my $t = shift;
259 271         492 my $angle = deg2rad($args->{_theta} + ($args->{_delta} * $t));
260 271         1646 my $rotr = deg2rad($args->{x_axis_rotation});
261 271         1424 my $cosr = cos $rotr;
262 271         336 my $sinr = sin $rotr;
263 271         591 my $x = ($cosr * cos($angle) * $args->{rx} - $sinr * sin($angle) * $args->{ry} + $args->{_center}->[0]);
264 271         441 my $y = ($sinr * cos($angle) * $args->{rx} + $cosr * sin($angle) * $args->{ry} + $args->{_center}->[1]);
265 271         502 return [$x, $y];
266             }
267              
268             1;