File Coverage

lib/SVG/Estimate/Path/QuadraticBezier.pm
Criterion Covered Total %
statement 16 35 45.7
branch n/a
condition n/a
subroutine 5 6 83.3
pod 0 1 0.0
total 21 42 50.0


line stmt bran cond sub pod time code
1             package SVG::Estimate::Path::QuadraticBezier;
2             $SVG::Estimate::Path::QuadraticBezier::VERSION = '1.0114';
3 10     10   1305 use Moo;
  10         21  
  10         61  
4 10     10   3090 use List::Util qw/min max/;
  10         19  
  10         734  
5 10     10   767 use Clone qw/clone/;
  10         4099  
  10         7048  
6              
7             extends 'SVG::Estimate::Path::Command';
8             with 'SVG::Estimate::Role::Pythagorean';
9             with 'SVG::Estimate::Role::SegmentLength';
10             with 'SVG::Estimate::Role::EndToPoint';
11              
12             =head1 NAME
13              
14             SVG::Estimate::Path::QuadraticBezier - Handles estimating quadratic bezier curves.
15              
16             =head1 VERSION
17              
18             version 1.0114
19              
20             =head1 SYNOPSIS
21              
22             my $curve = SVG::Estimate::Path::QuadraticBezier->new(
23             transformer => $transform,
24             start_point => [13, 19],
25             point => [45,13],
26             control => [10,3],
27             );
28              
29             my $length = $curve->length;
30              
31             =head1 INHERITANCE
32              
33             This class extends L and consumes L and L
34              
35             =head1 METHODS
36              
37             =head2 new()
38              
39             Constructor.
40              
41             =over
42              
43             =item point
44              
45             An array ref containing two floats that represent a point.
46              
47             =item control
48              
49             An array ref containing two floats that represent a point.
50              
51             =back
52              
53             =cut
54              
55             has point => (
56             is => 'ro',
57             required => 1,
58             );
59              
60             has control => (
61             is => 'ro',
62             required => 1,
63             );
64              
65             sub BUILDARGS {
66             my ($class, @args) = @_;
67             ##Upgrade to hashref
68             my $args = @args % 2 ? $args[0] : { @args };
69             if ($args->{transformer}->has_transforms) {
70             $args->{point} = $args->{transformer}->transform($args->{point});
71             $args->{control} = $args->{transformer}->transform($args->{control});
72             }
73             $args->{end_point} = clone $args->{point};
74             #$args->{shape_length} = $class->_calculate_length($args);
75             my $start = $class->this_point($args, 0);
76             my $end = $class->this_point($args, 1);
77             ##Bounding box points approximated by the control points.
78             $args->{min_x} = min $args->{start_point}->[0], $args->{control}->[0], $args->{point}->[0];
79             $args->{max_x} = max $args->{start_point}->[0], $args->{control}->[0], $args->{point}->[0];
80             $args->{min_y} = min $args->{start_point}->[1], $args->{control}->[1], $args->{point}->[1];
81             $args->{max_y} = max $args->{start_point}->[1], $args->{control}->[1], $args->{point}->[1];
82              
83             $args->{shape_length} = $class->segment_length($args, 0, 1, $start, $end, 1e-4, 5, 0);
84             $args->{travel_length} = 0;
85              
86             return $args;
87             }
88              
89             sub _calculate_length {
90 0     0   0 my $class = shift;
91 0         0 my $args = shift;
92 0         0 my $start = $args->{start_point};
93 0         0 my $control = $args->{control};
94 0         0 my $end = $args->{point};
95              
96             ##http://www.malczak.info/blog/quadratic-bezier-curve-length/
97 0         0 my $a_x = $start->[0] - 2 * $control->[0] + $end->[0];
98 0         0 my $a_y = $start->[1] - 2 * $control->[1] + $end->[1];
99 0         0 my $b_x = 2 * ($end->[0] - $start->[0]);
100 0         0 my $b_y = 2 * ($end->[1] - $start->[1]);
101              
102 0         0 my $A = 4 * ($a_x**2 + $a_y**2);
103 0         0 my $B = 4 * ($a_x*$b_x + $a_y*$b_y);
104 0         0 my $C = $b_x**2 + $b_y**2;
105              
106 0         0 my $SABC = 2 * sqrt($A + $B +$C);
107 0         0 my $SA = sqrt($A);
108 0         0 my $A32 = 2 * $A * $SA;
109 0         0 my $SC = 2*sqrt($C);
110 0         0 my $BA = $B / $SA;
111              
112 0         0 my $length = ( $A32 + $SA*$B*($SABC-$SC) + (4*$C*$A - $B*$B)*log( (2*$SA + $BA + $SABC)/($BA + $SC) ) ) / (4*($A32));
113 0         0 return $length;
114             }
115              
116             sub this_point {
117 197     197 0 1414 my $class = shift;
118 197         203 my $args = shift;
119 197         175 my $t = shift;
120             return [
121             $class->_this_point($t, $args->{start_point}->[0], $args->{control}->[0], $args->{point}->[0]),
122 197         285 $class->_this_point($t, $args->{start_point}->[1], $args->{control}->[1], $args->{point}->[1])
123             ];
124             }
125              
126             sub _this_point {
127 394     394   358 shift;
128 394         429 my ($t, $s, $c, $p) = @_;
129 394         957 return ((1 - $t)**2 * $s)
130             + (2*(1 - $t)*$t*$c)
131             + ($t**2 * $p)
132             ;
133             }
134              
135             1;