| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | =head1 NAME | 
| 2 |  |  |  |  |  |  |  | 
| 3 |  |  |  |  |  |  | Algorithm::Line::Bresenham - simple pixellated line-drawing algorithm | 
| 4 |  |  |  |  |  |  |  | 
| 5 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 6 |  |  |  |  |  |  |  | 
| 7 |  |  |  |  |  |  | use Algorithm::Line::Bresenham qw/line/; | 
| 8 |  |  |  |  |  |  | my @points = line(3,3 => 5,0); | 
| 9 |  |  |  |  |  |  | # returns the list: [3,3], [4,2], [4,1], [5,0] | 
| 10 |  |  |  |  |  |  | my @points = circle(30,30,5); | 
| 11 |  |  |  |  |  |  | # returns the points to draw a circle centered at 30,30, radius 5 | 
| 12 |  |  |  |  |  |  |  | 
| 13 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 14 |  |  |  |  |  |  |  | 
| 15 |  |  |  |  |  |  | Bresenham is one of the canonical line drawing algorithms for pixellated grids. | 
| 16 |  |  |  |  |  |  | Given a start and an end-point, Bresenham calculates which points on the grid | 
| 17 |  |  |  |  |  |  | need to be filled to generate the line between them. | 
| 18 |  |  |  |  |  |  |  | 
| 19 |  |  |  |  |  |  | Googling for 'Bresenham', and 'line drawing algorithms' gives some good | 
| 20 |  |  |  |  |  |  | overview.  The code here are adapted from various sources, mainly from | 
| 21 |  |  |  |  |  |  | C code at https://gist.github.com/bert/1085538 | 
| 22 |  |  |  |  |  |  |  | 
| 23 |  |  |  |  |  |  | =head1 FUNCTIONS | 
| 24 |  |  |  |  |  |  |  | 
| 25 |  |  |  |  |  |  | =cut | 
| 26 |  |  |  |  |  |  |  | 
| 27 |  |  |  |  |  |  |  | 
| 28 |  |  |  |  |  |  | package Algorithm::Line::Bresenham; | 
| 29 | 1 |  |  | 1 |  | 57506 | use strict; use warnings; | 
|  | 1 |  |  | 1 |  | 3 |  | 
|  | 1 |  |  |  |  | 24 |  | 
|  | 1 |  |  |  |  | 4 |  | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 35 |  | 
| 30 |  |  |  |  |  |  | our $VERSION = 0.13; | 
| 31 | 1 |  |  | 1 |  | 5 | use base 'Exporter'; | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 170 |  | 
| 32 |  |  |  |  |  |  | our @EXPORT_OK = qw/line circle ellipse_rect quad_bezier polyline/; | 
| 33 |  |  |  |  |  |  |  | 
| 34 |  |  |  |  |  |  |  | 
| 35 |  |  |  |  |  |  | =head2 C | 
| 36 |  |  |  |  |  |  |  | 
| 37 |  |  |  |  |  |  | line ($from_x, $from_y => $to_x, $to_y); | 
| 38 |  |  |  |  |  |  |  | 
| 39 |  |  |  |  |  |  | Generates a list of all the intermediate points.  This is returned as a list | 
| 40 |  |  |  |  |  |  | of array references. Previous versions used to include a callback parameter | 
| 41 |  |  |  |  |  |  | as a CODE ref to act on each point in turn.  This version omits that for | 
| 42 |  |  |  |  |  |  | performance reasons | 
| 43 |  |  |  |  |  |  |  | 
| 44 |  |  |  |  |  |  | =cut | 
| 45 |  |  |  |  |  |  |  | 
| 46 |  |  |  |  |  |  | sub line { # ported from https://gist.github.com/bert/1085538 | 
| 47 | 1 |  |  | 1 |  | 461 | use integer; | 
|  | 1 |  |  |  |  | 13 |  | 
|  | 1 |  |  |  |  | 4 |  | 
| 48 | 6 |  |  | 6 | 1 | 87 | my ($x0, $y0, $x1, $y1,$callback)=@_; | 
| 49 | 1 |  |  | 1 |  | 39 | use integer; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 3 |  | 
| 50 | 6 |  |  |  |  | 11 | my $dx =  abs ($x1 - $x0); | 
| 51 | 6 | 100 |  |  |  | 12 | my $sx = $x0 < $x1 ? 1 : -1; | 
| 52 | 6 |  |  |  |  | 8 | my $dy = -abs ($y1 - $y0); | 
| 53 | 6 | 100 |  |  |  | 12 | my $sy = $y0 < $y1 ? 1 : -1; | 
| 54 | 6 |  |  |  |  | 9 | my $err = $dx + $dy; | 
| 55 | 6 |  |  |  |  | 7 | my $e2; #/* error value e_xy */ | 
| 56 |  |  |  |  |  |  | my @points; | 
| 57 |  |  |  |  |  |  |  | 
| 58 | 6 |  |  |  |  | 9 | while(1){  #/* loop */ | 
| 59 | 21 |  |  |  |  | 32 | push @points,[$x0,$y0]; | 
| 60 | 21 | 100 | 100 |  |  | 48 | last if ($x0 == $x1 && $y0 == $y1); | 
| 61 | 15 |  |  |  |  | 18 | $e2 = 2 * $err; | 
| 62 | 15 | 100 |  |  |  | 26 | if ($e2 >= $dy) { $err += $dy; $x0 += $sx; } #/* e_xy+e_x > 0 */ | 
|  | 13 |  |  |  |  | 14 |  | 
|  | 13 |  |  |  |  | 15 |  | 
| 63 | 15 | 100 |  |  |  | 21 | if ($e2 <= $dx) { $err += $dx; $y0 += $sy; } #/* e_xy+e_y < 0 */ | 
|  | 10 |  |  |  |  | 11 |  | 
|  | 10 |  |  |  |  | 12 |  | 
| 64 |  |  |  |  |  |  | } | 
| 65 | 6 |  |  |  |  | 23 | return @points; | 
| 66 |  |  |  |  |  |  | } | 
| 67 |  |  |  |  |  |  |  | 
| 68 |  |  |  |  |  |  |  | 
| 69 |  |  |  |  |  |  | =head2 C | 
| 70 |  |  |  |  |  |  |  | 
| 71 |  |  |  |  |  |  | my @points = circle ($x, $y, $radius) | 
| 72 |  |  |  |  |  |  |  | 
| 73 |  |  |  |  |  |  | Returns the points to draw a circle centered on C<$x,$y> with | 
| 74 |  |  |  |  |  |  | radius C<$radius> | 
| 75 |  |  |  |  |  |  |  | 
| 76 |  |  |  |  |  |  | =cut | 
| 77 |  |  |  |  |  |  |  | 
| 78 |  |  |  |  |  |  | sub circle { # ported from https://gist.github.com/bert/1085538 | 
| 79 | 1 |  |  | 1 | 1 | 22 | my ($xm, $ym, $r)=@_; | 
| 80 | 1 |  |  | 1 |  | 139 | use integer; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 4 |  | 
| 81 | 1 |  |  |  |  | 3 | my $x = -$r; | 
| 82 | 1 |  |  |  |  | 2 | my $y = 0; | 
| 83 | 1 |  |  |  |  | 3 | my $err = 2-2*$r; #/* II. Quadrant */ | 
| 84 | 1 |  |  |  |  | 1 | my @points; | 
| 85 | 1 |  |  |  |  | 2 | do { | 
| 86 | 1 |  |  |  |  | 3 | push @points,[$xm-$x, $ym+$y];# /*   I. Quadrant */ | 
| 87 | 1 |  |  |  |  | 3 | push @points,[$xm-$y, $ym-$x];# /*  II. Quadrant */ | 
| 88 | 1 |  |  |  |  | 3 | push @points,[$xm+$x, $ym-$y];# /* III. Quadrant */ | 
| 89 | 1 |  |  |  |  | 2 | push @points,[$xm+$y, $ym+$x];# /*  IV. Quadrant */ | 
| 90 | 1 |  |  |  |  | 2 | $r = $err; | 
| 91 | 1 | 50 |  |  |  | 4 | $err += ++$x*2+1 if ($r >  $x); #/* e_xy+e_x > 0 */ | 
| 92 | 1 | 50 |  |  |  | 5 | $err += ++$y*2+1 if ($r <= $y); #/* e_xy+e_y < 0 */ | 
| 93 |  |  |  |  |  |  | } while ($x < 0); | 
| 94 | 1 |  |  |  |  | 5 | return @points; | 
| 95 |  |  |  |  |  |  | } | 
| 96 |  |  |  |  |  |  |  | 
| 97 |  |  |  |  |  |  | =head2 C | 
| 98 |  |  |  |  |  |  |  | 
| 99 |  |  |  |  |  |  | my @points = ellipse_rect ($x0, $y0, $x1, $y1) | 
| 100 |  |  |  |  |  |  |  | 
| 101 |  |  |  |  |  |  | Returns the points to an ellipse bound within a rectangle defined by | 
| 102 |  |  |  |  |  |  | the two coordinate pairs passed. | 
| 103 |  |  |  |  |  |  |  | 
| 104 |  |  |  |  |  |  | =cut | 
| 105 |  |  |  |  |  |  |  | 
| 106 |  |  |  |  |  |  |  | 
| 107 |  |  |  |  |  |  | sub ellipse_rect{ # ported from https://gist.github.com/bert/1085538 | 
| 108 | 1 |  |  | 1 |  | 125 | use integer; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 2 |  | 
| 109 | 1 |  |  | 1 | 1 | 4 | my ($x0, $y0, $x1, $y1)=@_; | 
| 110 | 1 |  |  |  |  | 2 | my $a = abs ($x1 - $x0); | 
| 111 | 1 |  |  |  |  | 3 | my $b = abs ($y1 - $y0); | 
| 112 | 1 |  |  |  |  | 3 | my $b1 = $b & 1; #/* values of diameter */ | 
| 113 | 1 |  |  |  |  | 3 | my $dx = 4 * (1 - $a) * $b * $b; | 
| 114 | 1 |  |  |  |  | 3 | my $dy = 4 * ($b1 + 1) * $a * $a; #/* error increment */ | 
| 115 | 1 |  |  |  |  | 2 | my $err = $dx + $dy + $b1 * $a * $a; | 
| 116 | 1 |  |  |  |  | 1 | my $e2; #/* error of 1.step */ | 
| 117 |  |  |  |  |  |  |  | 
| 118 | 1 | 50 |  |  |  | 3 | if ($x0 > $x1) { $x0 = $x1; $x1 += $a; } #/* if called with swapped points */ | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 119 | 1 | 50 |  |  |  | 13 | $y0 = $y1 if ($y0 >$y1);# /* .. exchange them */ | 
| 120 | 1 |  |  |  |  | 3 | $y0 += ($b + 1) / 2; | 
| 121 | 1 |  |  |  |  | 2 | $y1 = $y0-$b1;   #/* starting pixel */ | 
| 122 | 1 |  |  |  |  | 2 | $a *= 8 * $a; $b1 = 8 * $b * $b; | 
|  | 1 |  |  |  |  | 2 |  | 
| 123 | 1 |  |  |  |  | 1 | my @points; | 
| 124 |  |  |  |  |  |  | do | 
| 125 | 1 |  |  |  |  | 2 | { | 
| 126 | 3 |  |  |  |  | 6 | push @points,[$x1, $y0];# /*   I. Quadrant */ | 
| 127 | 3 |  |  |  |  | 6 | push @points,[$x0, $y0];# /*  II. Quadrant */ | 
| 128 | 3 |  |  |  |  | 5 | push @points,[$x0, $y1];# /* III. Quadrant */ | 
| 129 | 3 |  |  |  |  | 6 | push @points,[$x1, $y1];# /*  IV. Quadrant */ | 
| 130 | 3 |  |  |  |  | 4 | $e2 = 2 * $err; | 
| 131 | 3 | 50 |  |  |  | 4 | if ($e2 >= $dx) | 
| 132 |  |  |  |  |  |  | { | 
| 133 | 3 |  |  |  |  | 3 | $x0++; | 
| 134 | 3 |  |  |  |  | 8 | $x1--; | 
| 135 | 3 |  |  |  |  | 6 | $err += $dx += $b1;   # does this translate into perl | 
| 136 |  |  |  |  |  |  | } #/* x step */ | 
| 137 | 3 | 50 |  |  |  | 10 | if ($e2 <= $dy) | 
| 138 |  |  |  |  |  |  | { | 
| 139 | 0 |  |  |  |  | 0 | $y0++; | 
| 140 | 0 |  |  |  |  | 0 | $y1--; | 
| 141 | 0 |  |  |  |  | 0 | $err += $dy += $a; | 
| 142 |  |  |  |  |  |  | }  #/* y step */ | 
| 143 |  |  |  |  |  |  | } while ($x0 <= $x1); | 
| 144 | 1 |  |  |  |  | 4 | while ($y0-$y1 < $b) | 
| 145 |  |  |  |  |  |  | {  #/* too early stop of flat ellipses a=1 */ | 
| 146 | 0 |  |  |  |  | 0 | push @points,[$x0-1, $y0]; #/* -> finish tip of ellipse */ | 
| 147 | 0 |  |  |  |  | 0 | push @points,[$x1+1, $y0++]; | 
| 148 | 0 |  |  |  |  | 0 | push @points,[$x0-1, $y1]; | 
| 149 | 0 |  |  |  |  | 0 | push @points,[$x1+1, $y1--]; | 
| 150 |  |  |  |  |  |  | } | 
| 151 |  |  |  |  |  |  |  | 
| 152 | 1 |  |  |  |  | 6 | return @points; | 
| 153 |  |  |  |  |  |  | } | 
| 154 |  |  |  |  |  |  |  | 
| 155 |  |  |  |  |  |  | =head2 C | 
| 156 |  |  |  |  |  |  |  | 
| 157 |  |  |  |  |  |  | my @points = basic_bezier ($x0, $y0, $x1, $y1, $x2, $y2) | 
| 158 |  |  |  |  |  |  |  | 
| 159 |  |  |  |  |  |  | This is not usefull on its own.  Iteturns the points to segment of a | 
| 160 |  |  |  |  |  |  | bezier curve without a gradient sign change.   It is a companion to | 
| 161 |  |  |  |  |  |  | the C function that splits a bezier into segments | 
| 162 |  |  |  |  |  |  | with each gradient direction and these segments are computed in | 
| 163 |  |  |  |  |  |  | C | 
| 164 |  |  |  |  |  |  |  | 
| 165 |  |  |  |  |  |  | =cut | 
| 166 |  |  |  |  |  |  |  | 
| 167 |  |  |  |  |  |  |  | 
| 168 |  |  |  |  |  |  | sub basic_bezier{  # without gradient changes adapted from https://gist.github.com/bert/1085538 | 
| 169 | 2 |  |  | 2 | 1 | 5 | my ($x0, $y0, $x1, $y1, $x2, $y2)=@_; | 
| 170 | 2 | 50 |  |  |  | 6 | my $sx = $x0 < $x2 ? 1 : -1; | 
| 171 | 2 | 100 |  |  |  | 5 | my $sy = $y0 < $y2 ? 1 : -1; #/* step direction */ | 
| 172 | 2 |  |  |  |  | 5 | my $cur = $sx * $sy *(($x0 - $x1) * ($y2 - $y1) - ($x2 - $x1) * ($y0 - $y1)); #/* curvature */ | 
| 173 | 2 |  |  |  |  | 4 | my $x = $x0 - 2 * $x1 + $x2; | 
| 174 | 2 |  |  |  |  | 3 | my $y = $y0 - 2 * $y1 +$y2; | 
| 175 | 2 |  |  |  |  | 3 | my  $xy = 2 * $x * $y * $sx * $sy; | 
| 176 |  |  |  |  |  |  | # /* compute error increments of P0 */ | 
| 177 | 2 |  |  |  |  | 6 | my $dx = (1 - 2 * abs ($x0 - $x1)) * $y * $y + abs ($y0 - $y1) * $xy - 2 * $cur * abs ($y0 - $y2); | 
| 178 | 2 |  |  |  |  | 6 | my $dy = (1 - 2 * abs ($y0 - $y1)) * $x * $x + abs ($x0 - $x1) * $xy + 2 * $cur * abs ($x0 - $x2); | 
| 179 |  |  |  |  |  |  | #/* compute error increments of P2 */ | 
| 180 | 2 |  |  |  |  | 6 | my $ex = (1 - 2 * abs ($x2 - $x1)) * $y * $y + abs ($y2 - $y1) * $xy + 2 * $cur * abs ($y0 - $y2); | 
| 181 | 2 |  |  |  |  | 6 | my $ey = (1 - 2 * abs ($y2 - $y1)) * $x * $x + abs ($x2 - $x1) * $xy - 2 * $cur * abs ($x0 - $x2); | 
| 182 |  |  |  |  |  |  | # /* sign of gradient must not change */ | 
| 183 | 2 | 50 | 33 |  |  | 10 | warn "gradient change detected" unless (($x0 - $x1) * ($x2 - $x1) <= 0 && ($y0 - $y1) * ($y2 - $y1) <= 0); | 
| 184 | 2 | 50 |  |  |  | 4 | if ($cur == 0) | 
| 185 |  |  |  |  |  |  | { #/* straight line */ | 
| 186 | 0 |  |  |  |  | 0 | return line ($x0, $y0, $x2, $y2); | 
| 187 |  |  |  |  |  |  |  | 
| 188 |  |  |  |  |  |  | } | 
| 189 | 2 |  |  |  |  | 2 | $x *= 2 * $x; | 
| 190 | 2 |  |  |  |  | 4 | $y *= 2 * $y; | 
| 191 | 2 | 100 |  |  |  | 4 | if ($cur < 0) | 
| 192 |  |  |  |  |  |  | { #/* negated curvature */ | 
| 193 | 1 |  |  |  |  | 2 | $x = -$x; | 
| 194 | 1 |  |  |  |  | 2 | $dx = -$dx; | 
| 195 | 1 |  |  |  |  | 1 | $ex = -$ex; | 
| 196 | 1 |  |  |  |  | 1 | $xy = -$xy; | 
| 197 | 1 |  |  |  |  | 2 | $y = -$y; | 
| 198 | 1 |  |  |  |  | 1 | $dy = -$dy; | 
| 199 | 1 |  |  |  |  | 1 | $ey = -$ey; | 
| 200 |  |  |  |  |  |  | } | 
| 201 |  |  |  |  |  |  | #/* algorithm fails for almost straight line, check error values */ | 
| 202 | 2 | 50 | 33 |  |  | 22 | if ($dx >= -$y || $dy <= -$x || $ex <= -$y || $ey >= -$x) | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
| 203 |  |  |  |  |  |  | { | 
| 204 | 0 |  |  |  |  | 0 | return (line ($x0, $y0, $x1, $y1), line ($x1, $y1, $x2, $y2)); #/* simple approximation */ | 
| 205 |  |  |  |  |  |  | } | 
| 206 | 2 |  |  |  |  | 3 | $dx -= $xy; | 
| 207 | 2 |  |  |  |  | 7 | $ex = $dx + $dy; | 
| 208 | 2 |  |  |  |  | 3 | $dy -= $xy; #/* error of 1.step */ | 
| 209 | 2 |  |  |  |  | 2 | my @points; | 
| 210 | 2 |  |  |  |  | 3 | while(1) | 
| 211 |  |  |  |  |  |  | { #/* plot curve */ | 
| 212 | 7 |  |  |  |  | 14 | push @points,[$x0, $y0]; | 
| 213 | 7 |  |  |  |  | 9 | $ey = 2 * $ex - $dy; #/* save value for test of y step */ | 
| 214 | 7 | 50 |  |  |  | 12 | if (2 * $ex >= $dx) | 
| 215 |  |  |  |  |  |  | { #/* x step */ | 
| 216 | 7 | 100 |  |  |  | 12 | last if ($x0 == $x2); | 
| 217 | 5 |  |  |  |  | 6 | $x0 += $sx; | 
| 218 | 5 |  |  |  |  | 5 | $dy -= $xy; | 
| 219 | 5 |  |  |  |  | 5 | $ex += $dx += $y; | 
| 220 |  |  |  |  |  |  | } | 
| 221 | 5 | 100 |  |  |  | 8 | if ($ey <= 0) | 
| 222 |  |  |  |  |  |  | { #/* y step */ | 
| 223 | 2 | 50 |  |  |  | 4 | last if ($y0 == $y2); | 
| 224 | 2 |  |  |  |  | 3 | $y0 += $sy; | 
| 225 | 2 |  |  |  |  | 2 | $dx -= $xy; | 
| 226 | 2 |  |  |  |  | 3 | $ex += $dy += $x; | 
| 227 |  |  |  |  |  |  | } | 
| 228 |  |  |  |  |  |  | } | 
| 229 | 2 |  |  |  |  | 6 | return @points; | 
| 230 |  |  |  |  |  |  | } | 
| 231 |  |  |  |  |  |  |  | 
| 232 |  |  |  |  |  |  |  | 
| 233 |  |  |  |  |  |  | =head2 C | 
| 234 |  |  |  |  |  |  |  | 
| 235 |  |  |  |  |  |  | my @points = quad_bezier ($x0, $y0, $x1, $y1, $x2, $y2) | 
| 236 |  |  |  |  |  |  |  | 
| 237 |  |  |  |  |  |  | Draws a Bezier curve from C<($x0,$y0)> to  C<($x2,$y2)> using control | 
| 238 |  |  |  |  |  |  | point  C<($x1,$y1)> | 
| 239 |  |  |  |  |  |  |  | 
| 240 |  |  |  |  |  |  | =cut | 
| 241 |  |  |  |  |  |  |  | 
| 242 |  |  |  |  |  |  |  | 
| 243 |  |  |  |  |  |  | sub quad_bezier{  # adapted from http://members.chello.at/easyfilter/bresenham.html | 
| 244 | 1 |  |  | 1 | 1 | 4 | my ($x0, $y0, $x1, $y1, $x2, $y2)=@_;# /* plot any quadratic Bezier curve */ | 
| 245 | 1 |  |  |  |  | 2 | my $x = $x0-$x1; | 
| 246 | 1 |  |  |  |  | 3 | my $y = $y0-$y1; | 
| 247 | 1 |  |  |  |  | 3 | my $t = $x0-2*$x1+$x2; | 
| 248 | 1 |  |  |  |  | 2 | my $r; | 
| 249 |  |  |  |  |  |  | my @points; | 
| 250 | 1 | 50 |  |  |  | 4 | if ($x*($x2-$x1) > 0) { #/* horizontal cut at P4? */ | 
| 251 | 0 | 0 |  |  |  | 0 | if ($y*($y2-$y1) > 0){ #/* vertical cut at P6 too? */ | 
| 252 | 0 | 0 |  |  |  | 0 | if (abs(($y0-2*$y1+$y2)/$t*$x) > abs($y)) { #/* which first? */ | 
| 253 | 0 |  |  |  |  | 0 | $x0 = $x2; $x2 = $x+$x1; $y0 = $y2; $y2 = $y+$y1;# /* swap points */ | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 254 |  |  |  |  |  |  | } #/* now horizontal cut at P4 comes first */ | 
| 255 |  |  |  |  |  |  | } | 
| 256 | 0 |  |  |  |  | 0 | $t = ($x0-$x1)/$t; | 
| 257 | 0 |  |  |  |  | 0 | $r = (1-$t)*((1-$t)*$y0+2.0*$t*$y1)+$t*$t*$y2;# /* By(t=P4) */ | 
| 258 | 0 |  |  |  |  | 0 | $t = ($x0*$x2-$x1*$x1)*$t/($x0-$x1); #/* gradient dP4/dx=0 */ | 
| 259 | 0 |  |  |  |  | 0 | $x = int($t+0.5); $y = int($r+0.5); | 
|  | 0 |  |  |  |  | 0 |  | 
| 260 | 0 |  |  |  |  | 0 | $r = ($y1-$y0)*($t-$x0)/($x1-$x0)+$y0; #/* intersect P3 | P0 P1 */ | 
| 261 | 0 |  |  |  |  | 0 | push @points, basic_bezier($x0,$y0, $x,int($r+0.5), $x,$y); | 
| 262 | 0 |  |  |  |  | 0 | $r = ($y1-$y2)*($t-$x2)/($x1-$x2)+$y2; #/* intersect P4 | P1 P2 */ | 
| 263 | 0 |  |  |  |  | 0 | $x0 = $x1 = $x; $y0 = $y; $y1 = int($r+0.5);# /* P0 = P4, P1 = P8 */ | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 264 |  |  |  |  |  |  | } | 
| 265 | 1 | 50 |  |  |  | 6 | if (($y0-$y1)*($y2-$y1) > 0) { #/* vertical cut at P6? */ | 
| 266 | 1 |  |  |  |  | 2 | $t = $y0-2*$y1+$y2; $t = ($y0-$y1)/$t; | 
|  | 1 |  |  |  |  | 3 |  | 
| 267 | 1 |  |  |  |  | 14 | $r = (1-$t)*((1-$t)*$x0+2.0*$t*$x1)+$t*$t*$x2; # /* Bx(t=P6) */ | 
| 268 | 1 |  |  |  |  | 3 | $t = ($y0*$y2-$y1*$y1)*$t/($y0-$y1); #/* gradient dP6/dy=0 */ | 
| 269 | 1 |  |  |  |  | 3 | $x = int($r+0.5); $y = int($t+0.5); | 
|  | 1 |  |  |  |  | 2 |  | 
| 270 | 1 |  |  |  |  | 3 | $r = ($x1-$x0)*($t-$y0)/($y1-$y0)+$x0; #/* intersect P6 | P0 P1 */ | 
| 271 | 1 |  |  |  |  | 5 | push @points, basic_bezier($x0,$y0, int($r+0.5),$y, $x,$y); | 
| 272 | 1 |  |  |  |  | 3 | $r = ($x1-$x2)*($t-$y2)/($y1-$y2)+$x2; #/* intersect P7 | P1 P2 */ | 
| 273 | 1 |  |  |  |  | 2 | $x0 = $x; $x1 = int($r+0.5); $y0 = $y1 = $y;# /* P0 = P6, P1 = P7 */ | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 1 |  | 
| 274 |  |  |  |  |  |  | } | 
| 275 | 1 |  |  |  |  | 3 | push @points, basic_bezier($x0,$y0, $x1,$y1, $x2,$y2); #/* remaining part */ | 
| 276 | 1 |  |  |  |  | 12 | return @points; | 
| 277 |  |  |  |  |  |  | } | 
| 278 |  |  |  |  |  |  |  | 
| 279 |  |  |  |  |  |  | =head2 C | 
| 280 |  |  |  |  |  |  |  | 
| 281 |  |  |  |  |  |  | my @points = polyline ($x0, $y0, $x1, $y1, $x2, $y2) | 
| 282 |  |  |  |  |  |  |  | 
| 283 |  |  |  |  |  |  | Draws a polyline between points served as a list of x,y pairs | 
| 284 |  |  |  |  |  |  |  | 
| 285 |  |  |  |  |  |  | =cut | 
| 286 |  |  |  |  |  |  |  | 
| 287 |  |  |  |  |  |  | sub polyline{ | 
| 288 | 1 |  |  | 1 | 1 | 2 | my @vertices; | 
| 289 | 1 |  |  |  |  | 7 | push @vertices,[shift,shift] while (@_>1); | 
| 290 | 1 |  |  |  |  | 2 | my @points; | 
| 291 | 1 |  |  |  |  | 4 | foreach my $vertex(0..(@vertices-2)){ | 
| 292 | 2 |  |  |  |  | 2 | push @points,line(@{$vertices[$vertex]},@{$vertices[$vertex+1]}); | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 4 |  | 
| 293 | 2 | 100 |  |  |  | 7 | pop @points if ($vertex < (@vertices-2)); # remove duplicated points | 
| 294 |  |  |  |  |  |  | } | 
| 295 | 1 |  |  |  |  | 12 | return @points; | 
| 296 |  |  |  |  |  |  | } | 
| 297 |  |  |  |  |  |  |  | 
| 298 |  |  |  |  |  |  | 1; | 
| 299 |  |  |  |  |  |  | __END__ |