File Coverage

blib/lib/PeGS/PDF.pm
Criterion Covered Total %
statement 74 261 28.3
branch 8 28 28.5
condition 5 15 33.3
subroutine 21 57 36.8
pod 3 35 8.5
total 111 396 28.0


line stmt bran cond sub pod time code
1 4     4   1326479 use v5.36;
  4         15  
2 4     4   2188 use utf8;
  4         907  
  4         41  
3              
4             package PeGS::PDF;
5 4     4   240 use strict;
  4         8  
  4         94  
6              
7 4     4   41 use warnings;
  4         12  
  4         226  
8 4     4   21 no warnings;
  4         6  
  4         185  
9              
10 4     4   2086 use subs qw();
  4         1255  
  4         152  
11 4     4   29 use vars qw($VERSION);
  4         7  
  4         333  
12              
13             $VERSION = '0.103';
14              
15             =encoding utf8
16              
17             =head1 NAME
18              
19             PeGS::PDF - Draw simple Perl Graphical Structures
20              
21             =head1 SYNOPSIS
22              
23             use PeGS::PDF;
24              
25             my $pdf = PeGS::PDF->new(
26             {
27             file => "array.pdf",
28             'x' => 1.50 * 72,
29             'y' => 2.25 * 72,
30             }
31             );
32             die "Could not create object!" unless ref $pdf;
33              
34             $pdf->make_array( '@cats', [ qw(Buster Mimi Ginger Ella) ], 10, 120 );
35              
36             $pdf->close;
37              
38              
39             =head1 DESCRIPTION
40              
41             =over 4
42              
43             =cut
44              
45 4     4   42 use base qw(PDF::EasyPDF);
  4         12  
  4         2223  
46              
47 4     4   15040 use List::Util qw(max);
  4         11  
  4         17470  
48              
49             =item padding_factor
50              
51             =cut
52              
53 0     0 1 0 sub padding_factor { 0.7 }
54 0     0 0 0 sub font_height { 10 }
55 0     0 0 0 sub font_width { 6 }
56 0     0 0 0 sub font_size { 10 }
57 0     0 0 0 sub connector_height { 10 }
58 0     0 0 0 sub black_bar_height { 5 }
59 0     0 0 0 sub stroke_width { 0.5 }
60 0     0 0 0 sub pointy_width { ( $_[0]->font_height + 2 * $_[0]->y_padding ) / 2 * sqrt(2) }
61 0     0 0 0 sub box_height { $_[0]->font_height + 2 * $_[0]->y_padding }
62              
63 0     0 0 0 sub y_padding { $_[0]->padding_factor * $_[0]->font_height }
64 0     0 0 0 sub x_padding { $_[0]->padding_factor * $_[0]->font_width }
65              
66             sub make_reference {
67 0     0 0 0 my( $pdf, $name, $value, $bottom_left_x, $bottom_left_y ) = @_;
68              
69 0         0 my $scalar_width = $pdf->font_width * length $name;
70              
71 0         0 $pdf->make_pointy_box(
72             $bottom_left_x,
73             $bottom_left_y,
74             $scalar_width + 2 * $pdf->x_padding,
75             $pdf->box_height,
76             $name
77             );
78              
79 0         0 $pdf->lines(
80             ( $bottom_left_x + $pdf->x_padding + $scalar_width / 2 ), $bottom_left_y,
81             ( $bottom_left_x + $pdf->x_padding + $scalar_width / 2 ), $bottom_left_y - 10,
82             );
83              
84 0         0 $pdf->make_text_box(
85             $bottom_left_x,
86             $bottom_left_y - 10 - $pdf->font_height - 2 * $pdf->y_padding,
87             $scalar_width + 2 * $pdf->x_padding,
88             $pdf->box_height,
89             ''
90             );
91              
92 0         0 my $arrow_start = XYPoint->new(
93             $bottom_left_x + ( $scalar_width + 2 * $pdf->x_padding ) / 2,
94             $bottom_left_y + $pdf->box_height / 2 - $pdf->connector_height - $pdf->box_height - 2*$pdf->stroke_width,
95             );
96              
97 0   0     0 my $target = $pdf->{refs}{"$value"} //= XYPoint->new(
98             $arrow_start->clone->add_x( 50 )->x,
99             $arrow_start->y, # ++ ,,
100             );
101              
102 0         0 $pdf->make_reference_arrow(
103             $arrow_start,
104             $target,
105             );
106              
107 0         0 $pdf->make_reference_icon($arrow_start);
108              
109 0         0 my $x = $pdf->arrow_length( $scalar_width ) + $bottom_left_x + ( $scalar_width + 2 * $pdf->x_padding ) / 2;
110              
111 0 0       0 if( ref $value eq ref \ '' ) {
    0          
    0          
112              
113             }
114             elsif( ref $value eq ref [] ) {
115 0         0 $pdf->make_list(
116             $value,
117             $target->x,
118             $target->y - $pdf->black_bar_height / 2, # -
119             );
120             }
121             elsif( ref $value eq ref {} ) {
122 0         0 $pdf->make_anonymous_hash(
123             $value,
124             $target->x,
125             $target->y - $pdf->black_bar_height / 2, # -
126             );
127             }
128              
129             }
130              
131             sub make_circle {
132 0     0 0 0 my( $pdf,
133             $xc, # x at the center of the circle
134             $yc, # y at the center of the circle
135             $r # radius
136             ) = @_;
137              
138 0         0 $pdf->lines( $xc, $yc + 30, $xc, $yc - 30 );
139 0         0 $pdf->lines( $xc - 30, $yc, $xc + 30, $yc );
140              
141 0         0 my $points = 5;
142 0         0 my $Pi = 3.1415926;
143              
144 0         0 my $arc = 2 * $Pi / $points;
145              
146 0         0 my $darc = $arc * 360 / ( 2 * $Pi );
147              
148             =pod
149              
150             my @points = map
151             [ $xc + $r * cos( $arc * $_ / 2 ), $yc + $r * sin( $arc * $_ / 2 ) ],
152             0 .. $points - 1;
153              
154             =cut
155              
156 0         0 my @points = (
157             [ $r * cos( $arc / 2 ), $r * sin( $arc / 2 ) ],
158             [ $r * cos( - $arc / 2 ), $r * sin( - $arc / 2 ) ],
159             );
160              
161 0         0 $pdf->{stream} .= "@{$points[0]} m\n";
  0         0  
162              
163 0         0 foreach my $i ( 0 .. $points - 1 ) {
164 0         0 my( @xp, @yp );
165              
166 0         0 ( $xp[0], $yp[0], $xp[3], $yp[3] ) = ( @{ $points[0] }, @{ $points[1] } );
  0         0  
  0         0  
167              
168 0         0 ( $xp[1], $yp[1] ) = ( (4 * $r - $xp[0])/3, (1-$xp[0])*(3-$xp[0])/(3*$yp[0]) );
169              
170 0         0 ( $xp[2], $yp[2] ) = ( $xp[1], -$yp[1] );
171              
172             # rotate and translate
173 0         0 my @x = map { $_ + $xc } map { $xp[$_] * cos( $arc * $i ) + $yp[$_] * sin( $arc * $i ) } 0 .. $#xp;
  0         0  
  0         0  
174 0         0 my @y = map { $_ + $yc } map { - $xp[$_] * sin( $arc * $i ) + $yp[$_] * cos( $arc * $i ) } 0 .. $#yp;
  0         0  
  0         0  
175              
176 0         0 $pdf->{stream} .= "$x[0] $y[0] m\n$x[1] $y[1] $x[2] $y[2] $x[3] $y[3] c\nf\n";
177              
178             #$pdf->lines( $x0, $y0, $x1, $y1 );
179             #$pdf->lines( $x1, $y1, $x1, $y1 + 10 );
180             #$pdf->lines( $x3, $y3, $x2, $y2 );
181             #$pdf->lines( $x2, $y2, $x2, $y2 - 10 );
182             }
183              
184             }
185              
186             =pod
187              
188             $c .= sprintf(' %.2f %.2f %.2f %.2f %.2f %.2f c',
189                               $x + $b, $y,
190                               $x + $r, $y - $r + $b,
191                               $x + $r, $y - $r);
192                 /* Set x/y to the final point. */
193                 $x = $x + $r;
194                 $y = $y - $r;
195                 /* Third circle quarter. */
196                 $c .= sprintf(' %.2f %.2f %.2f %.2f %.2f %.2f c',
197                               $x, $y - $b,
198                               $x - $r + $b, $y - $r,
199                               $x - $r, $y - $r);
200                 /* Set x/y to the final point. */
201                 $x = $x - $r;
202                 $y = $y - $r;
203                 /* Fourth circle quarter. */
204                 $c .= sprintf(' %.2f %.2f %.2f %.2f %.2f %.2f c %s',
205                               $x - $b, $y,
206                               $x - $r, $y + $r - $b,
207                               $x - $r, $y + $r,
208                               $op);
209             =cut
210              
211             sub make_magic_circle {
212 0     0 0 0 my( $pdf,
213             $center,
214             $r # radius
215             ) = @_;
216              
217 0         0 my( $xc, $yc ) = $center->xy;
218              
219 0         0 my $magic = $r * 0.552;
220 0         0 my( $x0p, $y0p ) = ( $xc - $r, $yc );
221 0         0 $pdf->{stream} .= "$x0p $y0p m\n";
222              
223             {
224 0         0 ( $x0p, $y0p ) = ( $xc - $r, $yc );
225 0         0 my( $x1, $y1 ) = ( $x0p, $y0p + $magic );
226 0         0 my( $x2, $y2 ) = ( $x0p + $r - $magic, $y0p + $r );
227 0         0 my( $x3, $y3 ) = ( $x0p + $r, $y0p + $r );
228 0         0 $pdf->{stream} .= "$x1 $y1 $x2 $y2 $x3 $y3 c\n";
229             }
230              
231             {
232 0         0 ( $x0p, $y0p ) = ( $xc, $yc + $r );
  0         0  
233 0         0 my( $x1, $y1 ) = ( $x0p + $magic, $y0p );
234 0         0 my( $x2, $y2 ) = ( $x0p + $r, $y0p - $r + $magic );
235 0         0 my( $x3, $y3 ) = ( $x0p + $r, $y0p - $r );
236 0         0 $pdf->{stream} .= "$x1 $y1 $x2 $y2 $x3 $y3 c\n";
237             }
238              
239             {
240 0         0 ( $x0p, $y0p ) = ( $xc + $r, $yc );
  0         0  
241 0         0 my( $x1, $y1 ) = ( $x0p, $y0p - $magic );
242 0         0 my( $x2, $y2 ) = ( $x0p - $r + $magic, $y0p - $r );
243 0         0 my( $x3, $y3 ) = ( $x0p - $r, $y0p - $r );
244 0         0 $pdf->{stream} .= "$x1 $y1 $x2 $y2 $x3 $y3 c\n";
245             }
246              
247             {
248 0         0 ( $x0p, $y0p ) = ( $xc, $yc - $r );
  0         0  
  0         0  
249 0         0 my( $x1, $y1 ) = ( $x0p - $magic, $y0p );
250 0         0 my( $x2, $y2 ) = ( $x0p - $r, $y0p + $r - $magic );
251 0         0 my( $x3, $y3 ) = ( $x0p - $r, $y0p + $r );
252 0         0 $pdf->{stream} .= "$x1 $y1 $x2 $y2 $x3 $y3 c\n";
253             }
254              
255 0         0 $pdf->{stream} .= "f\n";
256             }
257              
258             sub make_regular_polygon {
259 0     0 0 0 my( $pdf,
260             $xc, # x at the center of the circle
261             $yc, # y at the center of the circle
262             $points,
263             $r # radius,
264             ) = @_;
265              
266 0         0 my $arc = 2 * 3.1415926 / $points;
267              
268 0         0 my @points = map
269             [ $xc + $r * cos( $arc * $_ ), $yc + $r * sin( $arc * $_ ) ],
270             0 .. $points - 1;
271              
272              
273 0         0 foreach my $i ( 0 .. $#points ) {
274             $pdf->lines(
275 0         0 @{ $points[$i] },
276 0         0 @{ $points[$i-1] },
  0         0  
277             );
278             }
279              
280             }
281              
282 0     0 0 0 sub arrow_factor { 15 }
283              
284             sub arrow_length {
285 0     0 0 0 my( $pdf, $base ) = @_;
286              
287 0 0       0 if( defined $base ) { $base + $pdf->arrow_factor; }
  0         0  
288 0         0 else { 85 }
289              
290             }
291              
292              
293 0     0 0 0 sub arrow_angle { 0 }
294              
295             =item arrowhead_length
296              
297             =item arrowhead_width
298              
299             =cut
300              
301 0     0 1 0 sub arrowhead_length { 10 }
302 0     0 1 0 sub arrowhead_width { 5 }
303              
304             sub make_reference_arrow {
305 0     0 0 0 my( $pdf, $start, $target ) = @_;
306              
307 0         0 my($angle, $length) = $start->angle_length_to($target);
308              
309 0         0 my $L = $pdf->arrowhead_length;
310 0         0 my $W = $pdf->arrowhead_width;
311              
312 0         0 my $arrow_retro_tip_high = $target
313             ->clone
314             ->add(
315             - $L * cos($angle) - $W * sin($angle),
316             - $L * sin($angle) + $W * cos($angle)
317             );
318              
319 0         0 my $arrow_retro_tip_low = $target
320             ->clone
321             ->add(
322             - $L * cos($angle) + $W * sin($angle),
323             - $L * sin($angle) - $W * cos($angle)
324             );
325              
326 0         0 $pdf->lines_xy( $start, $target );
327              
328 0         0 $pdf->filledPolygon(
329             $target->xy,
330             $arrow_retro_tip_high->xy,
331             $arrow_retro_tip_low->xy,
332             );
333              
334 0         0 return $target;
335             }
336              
337             sub lines_xy {
338 0     0 0 0 my( $pdf, $start, $end ) = @_;
339              
340 0         0 $pdf->SUPER::lines(
341             $start->xy,
342             $end->xy,
343             );
344             }
345              
346             sub make_reference_icon {
347 0     0 0 0 my( $pdf, $center ) = @_;
348              
349 0         0 $pdf->make_magic_circle(
350             $center,
351             $pdf->box_height / 6,
352             );
353              
354 0         0 $center;
355             }
356              
357             =for comment
358              
359             http://www.adobe.com/devnet/acrobat/pdfs/PDF32000_2008.pdf
360              
361             sub make_circle
362             {
363             my( $pdf, $x, $y, $radius, $start_angle, $end_angle ) = @_;
364              
365             # theta is sweep, which is 360
366              
367             my $Pi2 = 3.1415926 * 2;
368              
369             my( $x0, $y0 ) = ( cos( 180 / $Pi2 ), sin( 180 / $Pi2 ) );
370             my( $x1, $y1 ) = ( (4 - $x0) / 3, (1-$x0)*(3-$x0)/(3*$y0) )
371             my( $x2, $y2 ) = ( $x1, -$y0 );
372             my( $x3, $y3 ) = ( $x1, -$y1 );
373              
374             $pdf->{stream} .= <<"PDF";
375             $x $y m
376             $x1 $y1 $x2 $y2 $x3 $y3 c
377              
378              
379             PDF
380              
381              
382             }
383              
384             =cut
385              
386             sub make_scalar {
387 0     0 0 0 my( $pdf, $name, $value, $bottom_left_x, $bottom_left_y ) = @_;
388              
389 0         0 my $length = max( map { length $_ } $name, $$value );
  0         0  
390              
391 0         0 my $scalar_width = $pdf->font_width * $length;
392 0         0 my $scalar_height = 10;
393              
394 0         0 $pdf->make_pointy_box(
395             $bottom_left_x,
396             $bottom_left_y,
397             $scalar_width + 2 * $pdf->x_padding,
398             $pdf->box_height,
399             $name
400             );
401              
402 0         0 $pdf->lines(
403             ( $bottom_left_x + $pdf->x_padding + $scalar_width / 2 ), $bottom_left_y,
404             ( $bottom_left_x + $pdf->x_padding + $scalar_width / 2 ), $bottom_left_y - 10,
405             );
406              
407 0         0 $pdf->make_text_box(
408             $bottom_left_x,
409             $bottom_left_y - 10 - $pdf->font_height - 2 * $pdf->y_padding,
410             $scalar_width + 2 * $pdf->x_padding,
411             $pdf->box_height,
412             $value
413             );
414             }
415              
416             sub make_array {
417 0     0 0 0 my( $pdf, $name, $array, $bottom_left_x, $bottom_left_y ) = @_;
418              
419 0         0 my $length = max( map { length $_ } $name, grep { ! ref $_ } @$array );
  0         0  
  0         0  
420              
421 0         0 my $scalar_width = $pdf->font_width * $length;
422              
423 0         0 $pdf->make_pointy_box(
424             $bottom_left_x,
425             $bottom_left_y,
426             $scalar_width + 2 * $pdf->x_padding,
427             $pdf->box_height,
428             $name
429             );
430              
431 0         0 $pdf->lines(
432             ( $bottom_left_x + $pdf->x_padding + $scalar_width / 2 ), $bottom_left_y,
433             ( $bottom_left_x + $pdf->x_padding + $scalar_width / 2 ), $bottom_left_y - $pdf->connector_height,
434             );
435              
436 0         0 $pdf->make_list(
437             $array,
438             $bottom_left_x,
439             $bottom_left_y - $pdf->connector_height - $pdf->black_bar_height,
440             $scalar_width + 2 * $pdf->x_padding
441             );
442              
443             }
444              
445             sub make_list {
446 0     0 0 0 my( $pdf, $array, $bottom_left_x, $bottom_left_y, $width ) = @_;
447 0 0       0 return if exists $pdf->{refs}{ "$array" };
448              
449 0   0     0 my $scalar_width = $width || $pdf->get_list_width( $array );
450              
451 0         0 $pdf->make_collection_bar(
452             $bottom_left_x,
453             $bottom_left_y,
454             $scalar_width + $pdf->pointy_width + $pdf->x_padding,
455             );
456              
457 0         0 $pdf->{refs}{ "$array" } = XYPoint->new(
458             $bottom_left_x,
459             $bottom_left_y + $pdf->box_height / 2
460             );
461              
462 0         0 my $count = 0;
463 0         0 foreach my $value ( @$array ) {
464 0         0 $count++;
465              
466 0 0       0 my $box_value = ref $value ? '' : $value;
467 0         0 $pdf->make_text_box(
468             $bottom_left_x,
469             $bottom_left_y - $count*($pdf->font_height + 2 * $pdf->y_padding),
470             $scalar_width + $pdf->x_padding,
471             $pdf->box_height,
472             \ $box_value
473             );
474              
475 0 0       0 if( ref $value ) {
476 0         0 my $center = XYPoint->new(
477             $bottom_left_x + ( $scalar_width + $pdf->x_padding )/2 + $pdf->x_padding,
478             $bottom_left_y + $pdf->box_height / 2 - $count*$pdf->box_height,
479             );
480              
481 0   0     0 my $target = $pdf->{refs}{ "$value" } //
482             XYPoint->new(
483             $center->x + $pdf->arrow_length( $scalar_width + $pdf->x_padding ),
484             $center->y, # ,,
485             );
486              
487 0         0 $pdf->make_reference_icon( $center );
488              
489 0         0 my $arrow_end = $pdf->make_reference_arrow(
490             $center,
491             $target,
492             );
493              
494 0         0 my $ref_start = $arrow_end->clone;
495 0         0 $ref_start->add_y( - $pdf->black_bar_height / 2 );
496              
497 0 0       0 if( ref $value eq ref [] ) {
    0          
498 0         0 $pdf->make_list( $value, $ref_start->xy );
499             }
500             elsif( ref $value eq ref {} ) {
501 0         0 $pdf->make_anonymous_hash( $value, $ref_start->xy );
502             }
503             }
504             }
505              
506             }
507              
508             sub get_list_height {
509 0     0 0 0 my( $pdf, $array ) = @_;
510              
511             }
512              
513 0     0 0 0 sub minimum_scalar_width { 3 * $_[0]->font_width }
514              
515             sub get_list_width {
516 0     0 0 0 my( $pdf, $array ) = @_;
517              
518 0         0 my $length = max( map { length $_ } grep { ! ref $_ } @$array );
  0         0  
  0         0  
519              
520 0         0 my $scalar_width = max( $pdf->minimum_scalar_width, $pdf->font_width * $length );
521             }
522              
523             sub make_hash {
524 0     0 0 0 my( $pdf, $name, $hash, $bottom_left_x, $bottom_left_y ) = @_;
525              
526 0         0 my( $key_length, $value_length ) = $pdf->get_hash_lengths( $hash );
527              
528 0         0 my $scalar_width = $pdf->font_width * ( $key_length + $value_length ) + 4 * $pdf->x_padding + $pdf->pointy_width;
529              
530 0         0 $pdf->make_pointy_box(
531             $bottom_left_x,
532             $bottom_left_y,
533             $scalar_width,
534             $pdf->box_height,
535             $name
536             );
537              
538 0         0 $pdf->lines(
539             ( $bottom_left_x + $pdf->x_padding + $scalar_width / 2 ), $bottom_left_y,
540             ( $bottom_left_x + $pdf->x_padding + $scalar_width / 2 ), $bottom_left_y - $pdf->connector_height,
541             );
542              
543 0         0 $pdf->make_anonymous_hash(
544             $hash,
545             $bottom_left_x,
546             $bottom_left_y - $pdf->connector_height - $pdf->black_bar_height,
547             );
548              
549             }
550              
551             sub get_hash_lengths {
552 0     0 0 0 my( $pdf, $hash ) = @_;
553              
554 0         0 my $key_length = max( map { length $_ } keys %$hash );
  0         0  
555 0         0 my $value_length = max( map { length $_ } grep { ! ref $_ } values %$hash );
  0         0  
  0         0  
556              
557 0         0 ( $key_length, $value_length );
558             }
559              
560             sub make_anonymous_hash {
561 0     0 0 0 my( $pdf, $hash, $bottom_left_x, $bottom_left_y ) = @_;
562              
563 0         0 my( $key_length, $value_length ) = $pdf->get_hash_lengths( $hash );
564              
565 0         0 my $scalar_width =
566             $pdf->font_width * ( $key_length + $value_length ) +
567             4 * $pdf->x_padding +
568             $pdf->pointy_width;
569              
570 0         0 $pdf->make_collection_bar(
571             $bottom_left_x,
572             $bottom_left_y,
573             $scalar_width + $pdf->pointy_width,
574             );
575              
576 0         0 my $count = 0;
577 0         0 foreach my $key ( keys %$hash ) {
578 0         0 $count++;
579              
580 0         0 my $key_box_width =
581             $pdf->font_width * $key_length + 1 * $pdf->x_padding + $pdf->pointy_width / 2;
582              
583             ; # share name box extra
584              
585 0         0 $pdf->make_pointy_box(
586             $bottom_left_x,
587             $bottom_left_y - $count*($pdf->font_height + 2 * $pdf->y_padding),
588             $key_box_width,
589             $pdf->box_height,
590             $key
591             );
592              
593             $pdf->make_text_box(
594             $bottom_left_x + $key_box_width + $pdf->pointy_width + 2 * $pdf->stroke_width,
595             $bottom_left_y - $count*($pdf->font_height + 2 * $pdf->y_padding),
596             $pdf->font_width * $value_length + $pdf->x_padding - 2.125*$pdf->stroke_width,
597             $pdf->box_height,
598 0         0 \ $hash->{$key}
599             );
600             }
601              
602             }
603              
604             sub make_collection_bar {
605 0     0 0 0 my( $pdf, $bottom_left_x, $bottom_left_y, $width ) = @_;
606              
607 0         0 my $height = $pdf->black_bar_height;
608              
609 0         0 $pdf->filledRectangle(
610             $bottom_left_x - $pdf->stroke_width,
611             $bottom_left_y,
612             $width + 2 * $pdf->stroke_width,
613             $height,
614             );
615              
616 0         0 $pdf->strokePath;
617             }
618              
619             sub make_text_box {
620 0     0 0 0 my( $pdf, $bottom_left_x, $bottom_left_y, $width, $height, $text ) = @_;
621              
622 0         0 $pdf->rectangle(
623             $bottom_left_x,
624             $bottom_left_y,
625             $width + $height/2 * sqrt(2),
626             $height,
627             );
628              
629 0 0       0 $pdf->text(
630             $bottom_left_x + $pdf->x_padding,
631             $bottom_left_y + $pdf->y_padding,
632             ref $text ? $$text : $text
633             );
634              
635             }
636              
637             sub make_pointy_box {
638 0     0 0 0 my( $pdf, $bottom_left_x, $bottom_left_y, $width, $height, $text ) = @_;
639              
640 0         0 my $point_y = $bottom_left_y + $height / 2;
641 0         0 my $point_x = $bottom_left_x + $width + $height/2 * sqrt(2);
642              
643 0         0 my @vertices = (
644             $bottom_left_x, $bottom_left_y,
645             $bottom_left_x + $width, $bottom_left_y,
646             $point_x, $point_y,
647             $bottom_left_x + $width, $bottom_left_y + $height,
648             $bottom_left_x , $bottom_left_y + $height
649             );
650              
651 0         0 $pdf->polygon( @vertices );
652              
653 0         0 $pdf->text(
654             $bottom_left_x + $pdf->x_padding,
655             $bottom_left_y + $pdf->y_padding,
656             $text
657             );
658              
659             }
660              
661             =back
662              
663             =head1 TO DO
664              
665             Everything.
666              
667             =head1 SEE ALSO
668              
669              
670             =head1 SOURCE AVAILABILITY
671              
672             This source is in Github:
673              
674             http://github.com/briandfoy/pegs-pdf/
675              
676             =head1 AUTHOR
677              
678             brian d foy, C<< >>
679              
680             =head1 COPYRIGHT AND LICENSE
681              
682             Copyright © 2009-2025, brian d foy . All rights reserved.
683              
684             You may redistribute this under the terms of the Artistic License 2.0.
685              
686             =cut
687              
688              
689 0         0 BEGIN {
690             package XYPoint;
691 4     4   2514 use POSIX qw(atan);
  4         33451  
  4         26  
692              
693 4     4   7586 use constant π => 3.1415926;
  4     0   10  
  4         3262  
694              
695 20     20   64198 sub new { bless [ @_[1,2] ], $_[0] }
696 41     41   4051 sub x { $_[0][0] }
697             sub y ###
698 41     41   210 { $_[0][1] }
699              
700 2     2   596 sub add ($self, $delta_x, $delta_y) {
  2         6  
  2         5  
  2         10  
  2         3  
701 2         9 $self->add_x( $delta_x );
702 2         7 $self->add_y( $delta_y );
703 2         4 $self;
704             }
705 2     2   4 sub add_x ($self, $delta_x) { $self->[0] += $delta_x; $self }
  2         3  
  2         5  
  2         3  
  2         4  
  2         5  
706 2     2   4 sub add_y ($self, $delta_y) { $self->[1] += $delta_y; $self }
  2         3  
  2         4  
  2         4  
  2         4  
  2         4  
707              
708             sub angle_length_to {
709 8     8   10429 my( $self, $target ) = @_;
710              
711 8         34 my $h = $target->y - $self->y; # -
712 8         30 my $b = $target->x - $self->x;
713              
714 8 100       29 if( $b == 0 ) {
715 2 100       9 my $sign = $h > 0 ? 1 : -1;
716 2         13 return ( $sign * 1/2 * π, abs($h) );
717             }
718              
719 6         17 my $ratio = $h / $b;
720              
721 6         163 say STDERR "H: $h B: $b";
722 6         189 my $angle = atan( $ratio );
723 6         82 say STDERR "angle before: $angle";
724              
725 6 100 100     62 if( $b < 0 and $h < 0 ) { $angle -= π }
  1 100 66     4  
726 2         5 elsif( $b < 0 and $h >= 0 ) { $angle += π }
727              
728 6         64 say STDERR "angle after: $angle";
729              
730 6         26 my $length = sqrt( $b**2 + $h**2 );
731 6         31 ($angle, $length);
732             }
733              
734             sub rotate ( $self, $angle ) {
735             }
736              
737 3     3   12 sub xy { ( $_[0]->x, $_[0]->y ) }
738              
739 1     1   5 sub clone { (ref $_[0])->new( $_[0]->xy ) }
740              
741 16     16   98 sub as_string { sprintf "(%d, %d)", $_[0]->x, $_[0]->y }
742             }
743              
744             1;