File Coverage

blib/lib/App/Asciio/Elements.pm
Criterion Covered Total %
statement 40 320 12.5
branch 0 68 0.0
condition 0 51 0.0
subroutine 13 48 27.0
pod 0 36 0.0
total 53 523 10.1


line stmt bran cond sub pod time code
1              
2             package App::Asciio ;
3              
4             $|++ ;
5              
6 4     4   27 use strict;
  4         9  
  4         173  
7 4     4   22 use warnings;
  4         7  
  4         316  
8 4     4   27 use utf8;
  4         7  
  4         42  
9 4     4   134 use Carp ;
  4         7  
  4         319  
10              
11 4     4   27 use Data::TreeDumper ;
  4         7  
  4         311  
12 4     4   2902 use File::Slurp ;
  4         173906  
  4         378  
13 4     4   42 use Clone;
  4         9  
  4         217  
14 4     4   26 use List::Util qw(min max) ;
  4         8  
  4         307  
15 4     4   25 use List::MoreUtils qw(any minmax first_value) ;
  4         9  
  4         43  
16 4     4   4222 use Readonly ;
  4         9  
  4         243  
17              
18 4     4   2491 use App::Asciio::Connections ;
  4         11  
  4         19382  
19              
20             #-----------------------------------------------------------------------------
21              
22 0     0 0 0 sub set_modified_state { my ($self, $state) = @_ ; $self->{MODIFIED} = $state ; }
  0         0  
23              
24             #-----------------------------------------------------------------------------
25              
26 0     0 0 0 sub get_modified_state { my ($self) = @_ ; $self->{MODIFIED} ; }
  0         0  
27              
28             #-----------------------------------------------------------------------------
29              
30             sub get_group_color
31             {
32             # cycle through color to give visual clue to user
33 0     0 0 0 my ($self) = @_ ;
34              
35 0         0 my $colors = $self->{COLORS}{group_colors}[$self->{NEXT_GROUP_COLOR}] ;
36              
37 0         0 $self->{NEXT_GROUP_COLOR}++ ;
38 0 0       0 $self->{NEXT_GROUP_COLOR} = 0 if $self->{NEXT_GROUP_COLOR} >= scalar(@{$self->{COLORS}{group_colors}}) ;
  0         0  
39              
40 0         0 return ($colors) ;
41             }
42              
43             #-----------------------------------------------------------------------------
44              
45             sub add_ruler_lines
46             {
47 0     0 0 0 my ($self, @lines) = @_ ;
48 0         0 push @{$self->{RULER_LINES}}, @lines ;
  0         0  
49              
50 0         0 $self->{MODIFIED }++ ;
51             }
52              
53             sub remove_ruler_lines
54             {
55 0     0 0 0 my ($self, @ruler_lines_to_remove) = @_ ;
56              
57 0         0 my %removed ;
58              
59 0         0 for my $ruler_line_to_remove (@ruler_lines_to_remove)
60             {
61 0         0 for my $ruler_line (@{$self->{RULER_LINES}})
  0         0  
62             {
63 0 0 0     0 if
64             (
65             $ruler_line->{TYPE} eq $ruler_line_to_remove->{TYPE}
66             && $ruler_line->{POSITION} == $ruler_line_to_remove->{POSITION}
67             )
68             {
69 0         0 $removed{$ruler_line} ++ ;
70             }
71             }
72             }
73              
74 0         0 $self->{RULER_LINES} = [ grep { ! exists $removed{$_} } @{$self->{RULER_LINES}} ] ;
  0         0  
  0         0  
75             }
76              
77             sub exists_ruler_line
78             {
79 0     0 0 0 my ($self, @ruler_lines_to_check) = @_ ;
80              
81 0         0 my $exists = 0 ;
82              
83 0         0 for my $ruler_line_to_check (@ruler_lines_to_check)
84             {
85 0         0 for my $ruler_line (@{$self->{RULER_LINES}})
  0         0  
86             {
87 0 0 0     0 if
88             (
89             $ruler_line->{TYPE} eq $ruler_line_to_check->{TYPE}
90             && $ruler_line->{POSITION} == $ruler_line_to_check->{POSITION}
91             )
92             {
93 0         0 $exists++ ;
94 0         0 last ;
95             }
96             }
97             }
98              
99 0         0 return $exists ;
100             }
101              
102             #-----------------------------------------------------------------------------
103              
104             sub add_new_element_named
105             {
106 0     0 0 0 my ($self, $element_name, $x, $y) = @_ ;
107              
108 0         0 my $element_index = $self->{ELEMENT_TYPES_BY_NAME}{$element_name} ;
109              
110 0 0       0 if(defined $element_index)
111             {
112 0         0 return add_new_element_of_type($self, $self->{ELEMENT_TYPES}[$element_index], $x, $y) ;
113             }
114             else
115             {
116 0         0 croak "add_new_element_named: can't create element named '$element_name'!\n" ;
117             }
118             }
119              
120             #-----------------------------------------------------------------------------
121              
122             sub add_new_element_of_type
123             {
124 0     0 0 0 my ($self, $element, $x, $y) = @_ ;
125              
126 0         0 my $new_element = Clone::clone($element) ;
127              
128 0         0 @$new_element{'X', 'Y', 'SELECTED'} = ($x, $y, 0) ;
129 0         0 $self->add_elements($new_element) ;
130              
131 0         0 return($new_element) ;
132             }
133              
134             #-----------------------------------------------------------------------------
135              
136             sub set_element_position
137             {
138 0     0 0 0 my ($self, $element, $x, $y) = @_ ;
139              
140 0         0 @$element{'X', 'Y'} = ($x, $y) ;
141             }
142              
143             #-----------------------------------------------------------------------------
144              
145             sub set_elements_position
146             {
147 0     0 0 0 my ($self, $elements, $x, $y) = @_ ;
148              
149 0         0 @$_{'X', 'Y'} = ($x, $y) for @$elements ;
150             }
151              
152             #-----------------------------------------------------------------------------
153              
154             sub add_element_at
155             {
156 0     0 0 0 my ($self, $element, $x, $y) = @_ ;
157              
158 0         0 $self->add_element_at_no_connection($element, $x, $y) ;
159 0         0 $self->connect_elements($element) ;
160              
161 0         0 $element
162             }
163              
164             sub add_element_at_no_connection
165             {
166 0     0 0 0 my ($self, $element, $x, $y) = @_ ;
167              
168 0         0 $self->set_element_position($element, $x, $y) ;
169 0         0 $self->add_elements_no_connection($element) ;
170              
171 0         0 $element
172             }
173              
174             #-----------------------------------------------------------------------------
175              
176             sub add_elements
177             {
178 169     169 0 2259 my ($self, @elements) = @_ ;
179              
180 169         641 $self->add_elements_no_connection(@elements) ;
181 169         827 $self->connect_elements(@elements) ;
182             }
183              
184             sub add_elements_no_connection
185             {
186 169     169 0 415 my ($self, @elements) = @_ ;
187 169         248 push @{$self->{ELEMENTS}}, @elements ;
  169         449  
188              
189 169         398 $self->{MODIFIED }++ ;
190             }
191              
192             #-----------------------------------------------------------------------------
193              
194             sub unshift_elements
195             {
196 0     0 0   my ($self, @elements) = @_ ;
197 0           unshift @{$self->{ELEMENTS}}, @elements ;
  0            
198 0           $self->{MODIFIED }++ ;
199             }
200              
201             #-----------------------------------------------------------------------------
202              
203             sub move_elements
204             {
205 0     0 0   my ($self, $x_offset, $y_offset, @elements) = @_ ;
206              
207 0           my %selected_elements = map { $_ => 1} @elements ;
  0            
208              
209 0           for my $element (@elements)
210             {
211 0           @$element{'X', 'Y'} = ($element->{X} + $x_offset, $element->{Y} + $y_offset) ;
212            
213             # handle arrow element
214 0           my (@current_element_connections, %used_connectors) ;
215            
216 0 0         if($self->is_connected($element))
217             {
218             # disconnect current connections if it is not connected to another elements we are moving
219             # connectees move their connected along
220            
221 0           @current_element_connections = $self->get_connections_containing($element) ,
222            
223             my (@connections_to_delete, @connections_to_keep) ;
224 0           for my $current_element_connection (@current_element_connections)
225             {
226 0 0         if(exists $selected_elements{$current_element_connection->{CONNECTEE}})
227             {
228 0           $used_connectors{$current_element_connection->{CONNECTOR}{NAME}}++ ;
229 0           push @connections_to_keep, $current_element_connection ;
230             }
231             else
232             {
233 0           push @connections_to_delete, $current_element_connection ;
234             }
235             }
236            
237 0           $self->delete_connections(@connections_to_delete) ;
238 0           @current_element_connections = @connections_to_keep ;
239             }
240            
241             # connect to new elements if the connection doesn't already exist
242             # and connection not already done with one of the elements being moved
243             my @new_connections =
244             grep
245             { # connector already used to connect to a moved element
246             ! exists $used_connectors{$_->{CONNECTOR}{NAME}}
247 0           }
248             grep
249             { # connection to that element already exists, don't reconnect to moved element
250 0           ! exists $selected_elements{$_->{CONNECTEE}}
251 0           }
252             $self->get_possible_connections($element) ;
253            
254 0           $self->add_connections(@new_connections) ;
255            
256             # handle box element
257 0           for my $connection ($self->get_connected($element))
258             {
259             # move connected with connectees
260 0 0         if (exists $selected_elements{$connection->{CONNECTED}})
261             {
262             # arrow is part of the selection being moved
263             }
264             else
265             {
266             my ($x_offset, $y_offset, $width, $height, $new_connector) =
267             $connection->{CONNECTED}->move_connector
268             (
269             $connection->{CONNECTOR}{NAME},
270 0           $x_offset, $y_offset
271             ) ;
272            
273 0           $connection->{CONNECTED}{X} += $x_offset ;
274 0           $connection->{CONNECTED}{Y} += $y_offset;
275            
276             # the connection point has also changed
277 0           $connection->{CONNECTOR} = $new_connector ;
278 0           $connection->{FIXED}++ ;
279            
280             #find the other connectors belonging to this connected
281 0           for my $other_connection (grep{ ! $_->{FIXED}} @{$self->{CONNECTIONS}})
  0            
  0            
282             {
283             # move them relatively to their previous position
284 0 0         if($connection->{CONNECTED} == $other_connection->{CONNECTED})
285             {
286             my ($new_connector) = # in characters relative to element origin
287 0           $other_connection->{CONNECTED}->get_named_connection($other_connection->{CONNECTOR}{NAME}) ;
288            
289 0           $other_connection->{CONNECTOR} = $new_connector ;
290 0           $other_connection->{FIXED}++ ;
291             }
292             }
293             }
294             }
295            
296 0           for my $connection (@{$self->{CONNECTIONS}})
  0            
297             {
298 0           delete $connection->{FIXED} ;
299             }
300            
301 0           $self->{MODIFIED }++ ;
302             }
303             }
304              
305             #-----------------------------------------------------------------------------
306              
307             sub resize_element
308             {
309 0     0 0   my ($self, $reference_x, $reference_y, $new_x, $new_y, $selected_element, $connector_name) = @_;
310              
311 0           my ($x_offset, $y_offset, undef, undef, $resized_connector_name) =
312             $selected_element->resize($reference_x, $reference_y, $new_x, $new_y, undef, $connector_name) ;
313              
314 0           $selected_element->{X} += $x_offset ;
315 0           $selected_element->{Y} += $y_offset;
316              
317             # handle connections
318 0 0         if($self->is_connected($selected_element))
319             {
320             # disconnect current connections
321 0           $self->delete_connections_containing($selected_element) ;
322             }
323              
324 0           $self->connect_elements($selected_element) ; # connect to new elements if any
325              
326 0           for my $connection ($self->get_connected($selected_element))
327             {
328             # all connection where the selected element is the connectee
329            
330             my ($new_connection) = # in characters relative to element origin
331 0           $selected_element->get_named_connection($connection->{CONNECTION}{NAME}) ;
332            
333 0 0         if(defined $new_connection)
334             {
335             my ($x_offset, $y_offset, $width, $height, $new_connector) =
336             $connection->{CONNECTED}->move_connector
337             (
338             $connection->{CONNECTOR}{NAME},
339             $new_connection->{X} - $connection->{CONNECTION}{X},
340             $new_connection->{Y}- $connection->{CONNECTION}{Y}
341 0           ) ;
342            
343 0           $connection->{CONNECTED}{X} += $x_offset ;
344 0           $connection->{CONNECTED}{Y} += $y_offset ;
345            
346             # the connection point has also changed
347 0           $connection->{CONNECTOR} = $new_connector ;
348 0           $connection->{CONNECTION} = $new_connection ;
349            
350 0           $connection->{FIXED}++ ;
351            
352             #find the other connectors belonging to this connected
353 0           for my $other_connection (grep{ ! $_->{FIXED}} @{$self->{CONNECTIONS}})
  0            
  0            
354             {
355             # move them relatively to their previous position
356 0 0         if($connection->{CONNECTED} == $other_connection->{CONNECTED})
357             {
358             my ($new_connector) = # in characters relative to element origin
359 0           $other_connection->{CONNECTED}->get_named_connection($other_connection->{CONNECTOR}{NAME}) ;
360            
361 0           $other_connection->{CONNECTOR} = $new_connector ;
362 0           $other_connection->{FIXED}++ ;
363             }
364             }
365            
366 0           for my $connection (@{$self->{CONNECTIONS}})
  0            
367             {
368 0           delete $connection->{FIXED} ;
369             }
370             }
371             else
372             {
373 0           $self->delete_connections($connection) ;
374             }
375             }
376              
377 0           return($x_offset, $y_offset, $resized_connector_name) ;
378             }
379              
380             #-----------------------------------------------------------------------------
381              
382             sub move_elements_to_front
383             {
384 0     0 0   my ($self, @elements) = @_ ;
385              
386 0           my %elements_to_move = map {$_ => 1} @elements ;
  0            
387 0           my @new_element_list ;
388              
389 0           for(@{$self->{ELEMENTS}})
  0            
390             {
391 0 0         push @new_element_list, $_ unless (exists $elements_to_move{$_}) ;
392             }
393              
394 0           $self->{ELEMENTS} = [@new_element_list, @elements] ;
395             } ;
396              
397             #----------------------------------------------------------------------------------------------
398              
399             sub move_elements_to_back
400             {
401 0     0 0   my ($self, @elements) = @_ ;
402              
403 0           my %elements_to_move = map {$_ => 1} @elements ;
  0            
404 0           my @new_element_list ;
405              
406 0           for(@{$self->{ELEMENTS}})
  0            
407             {
408 0 0         push @new_element_list, $_ unless (exists $elements_to_move{$_}) ;
409             }
410              
411 0           $self->{ELEMENTS} = [@elements, @new_element_list] ;
412             } ;
413              
414             #-----------------------------------------------------------------------------
415              
416             sub delete_elements
417             {
418 0     0 0   my($self, @elements) = @_ ;
419              
420 0           my %elements_to_delete = map {$_, 1} grep { defined $_ } @elements ;
  0            
  0            
421              
422 0           for my $element (@{$self->{ELEMENTS}})
  0            
423             {
424 0 0         if(exists $elements_to_delete{$element})
425             {
426 0           $self->delete_connections_containing($element) ;
427 0           $element = undef ;
428             }
429             }
430              
431 0           @{$self->{ELEMENTS}} = grep { defined $_} @{$self->{ELEMENTS}} ;
  0            
  0            
  0            
432              
433 0           $self->{MODIFIED }++ ;
434             }
435              
436             #-----------------------------------------------------------------------------
437              
438             sub edit_element
439             {
440 0     0 0   my ($self, $selected_element) = @_ ;
441              
442 0           $selected_element->edit($self) ;
443              
444             # handle connections
445 0 0         if($self->is_connected($selected_element))
446             {
447             # disconnect current connections
448 0           $self->delete_connections_containing($selected_element) ;
449             }
450              
451 0           $self->connect_elements($selected_element) ; # connect to new elements if any
452              
453 0           for my $connection ($self->get_connected($selected_element))
454             {
455             # all connection where the selected element is the connectee
456            
457             my ($new_connection) = # in characters relative to element origin
458 0           $selected_element->get_named_connection($connection->{CONNECTION}{NAME}) ;
459            
460 0 0         if(defined $new_connection)
461             {
462             my ($x_offset, $y_offset, $width, $height, $new_connector) =
463             $connection->{CONNECTED}->move_connector
464             (
465             $connection->{CONNECTOR}{NAME},
466             $new_connection->{X} - $connection->{CONNECTION}{X},
467             $new_connection->{Y}- $connection->{CONNECTION}{Y}
468 0           ) ;
469            
470 0           $connection->{CONNECTED}{X} += $x_offset ;
471 0           $connection->{CONNECTED}{Y} += $y_offset;
472            
473             # the connection point has also changed
474 0           $connection->{CONNECTOR} = $new_connector ;
475 0           $connection->{CONNECTION} = $new_connection ;
476            
477 0           $connection->{FIXED}++ ;
478            
479             #find the other connectors belonging to this connected
480 0           for my $other_connection (grep{ ! $_->{FIXED}} @{$self->{CONNECTIONS}})
  0            
  0            
481             {
482             # move them relatively to their previous position
483 0 0         if($connection->{CONNECTED} == $other_connection->{CONNECTED})
484             {
485             my ($new_connector) = # in characters relative to element origin
486 0           $other_connection->{CONNECTED}->get_named_connection($other_connection->{CONNECTOR}{NAME}) ;
487            
488 0           $other_connection->{CONNECTOR} = $new_connector ;
489 0           $other_connection->{FIXED}++ ;
490             }
491             }
492            
493 0           for my $connection (@{$self->{CONNECTIONS}})
  0            
494             {
495 0           delete $connection->{FIXED} ;
496             }
497             }
498             else
499             {
500 0           $self->delete_connections($connection) ;
501             }
502             }
503              
504 0           $self->{MODIFIED }++ ;
505             }
506              
507             #-----------------------------------------------------------------------------
508              
509             sub get_selected_elements
510             {
511 0     0 0   my ($self, $state) = @_ ;
512              
513             return
514             (
515             grep
516             {
517 0 0         if($state)
518             {
519 0 0         exists $_->{SELECTED} && $_->{SELECTED} != 0
520             }
521             else
522             {
523 0 0         ! exists $_->{SELECTED} || $_->{SELECTED} == 0
524             }
525 0           } @{$self->{ELEMENTS}}
  0            
526             ) ;
527             }
528              
529             #-----------------------------------------------------------------------------
530              
531             sub any_select_elements
532             {
533 0     0 0   my ($self) = @_ ;
534              
535 0     0     return(any {$_->{SELECTED}} @{$self->{ELEMENTS}}) ;
  0            
  0            
536             }
537              
538             #-----------------------------------------------------------------------------
539              
540             sub select_elements
541             {
542 0     0 0   my ($self, $state, @elements) = @_ ;
543              
544 0           my %groups_to_select ;
545              
546 0           for my $element (@elements)
547             {
548 0 0         if($state)
549             {
550 0           $element->{SELECTED} = ++$self->{SELECTION_INDEX} ;
551             }
552             else
553             {
554 0           $element->{SELECTED} = 0 ;
555             }
556            
557 0 0 0       if(exists $element->{GROUP} && defined $element->{GROUP}[-1])
558             {
559 0           $groups_to_select{$element->{GROUP}[-1]}++ ;
560             }
561             }
562              
563             # select groups
564 0           for my $element (@{$self->{ELEMENTS}})
  0            
565             {
566 0 0 0       if
      0        
567             (
568             exists $element->{GROUP} && defined $element->{GROUP}[-1]
569             && exists $groups_to_select{$element->{GROUP}[-1]}
570             )
571             {
572 0 0         if($state)
573             {
574 0           $element->{SELECTED} = ++$self->{SELECTION_INDEX} ;
575             }
576             else
577             {
578 0           $element->{SELECTED} = 0 ;
579             }
580             }
581             }
582              
583 0 0         delete $self->{SELECTION_INDEX} unless $self->get_selected_elements(1) ;
584             }
585              
586             #-----------------------------------------------------------------------------
587              
588             sub select_all_elements_by_search_words
589             {
590 0     0 0   my ($self) = @_ ;
591              
592 0           my $search_words = $self->display_edit_dialog("input search words", '', $self);
593              
594 0           for my $element (@{$self->{ELEMENTS}})
  0            
595             {
596 0 0         $self->select_elements(1, $element) if ($self->transform_elements_to_ascii_buffer($element) =~ m/$search_words/i);
597             }
598             }
599              
600             #-----------------------------------------------------------------------------
601              
602             sub select_all_elements_by_search_words_ignore_group
603             {
604 0     0 0   my ($self) = @_ ;
605              
606 0           my $search_words = $self->display_edit_dialog("input search words", '', $self);
607              
608 0           for my $element (@{$self->{ELEMENTS}})
  0            
609             {
610 0 0         $element->{SELECTED} = ++$self->{SELECTION_INDEX} if ($self->transform_elements_to_ascii_buffer($element) =~ m/$search_words/i);
611             }
612             }
613              
614             #-----------------------------------------------------------------------------
615              
616             sub select_all_elements
617             {
618 0     0 0   my ($self) = @_ ;
619              
620 0           $self->select_elements(1, @{$self->{ELEMENTS}}) ;
  0            
621             }
622              
623             #-----------------------------------------------------------------------------
624              
625             sub deselect_all_elements
626             {
627 0     0 0   my ($self) = @_ ;
628              
629 0           $self->select_elements(0, @{$self->{ELEMENTS}}) ;
  0            
630             }
631              
632             #-----------------------------------------------------------------------------
633              
634             sub select_elements_flip
635             {
636 0     0 0   my ($self, @elements) = @_ ;
637              
638 0           for my $element (@elements)
639             {
640 0 0         $self->select_elements($element->{SELECTED} ? 0 : 1, $element) ;
641             }
642              
643 0 0         delete $self->{SELECTION_INDEX} unless $self->get_selected_elements(1) ;
644             }
645              
646             #-----------------------------------------------------------------------------
647              
648             sub is_element_selected
649             {
650 0     0 0   my ($self, $element) = @_ ;
651              
652 0           $element->{SELECTED} ;
653             }
654              
655             #-----------------------------------------------------------------------------
656              
657             sub is_over_element
658             {
659 0     0 0   my ($self, $element, $x, $y, $field) = @_ ;
660              
661 0   0       $field //= 0 ;
662 0           my $is_under = 0 ;
663              
664 0           for my $strip (@{$element->get_stripes()})
  0            
665             {
666 0           my $stripe_x = $element->{X} + $strip->{X_OFFSET} ;
667 0           my $stripe_y = $element->{Y} + $strip->{Y_OFFSET} ;
668            
669 0 0 0       if
      0        
      0        
670             (
671             $stripe_x - $field <= $x && $x < $stripe_x + $strip->{WIDTH} + $field
672             && $stripe_y - $field <= $y && $y < $stripe_y + $strip->{HEIGHT} + $field
673             )
674             {
675 0           $is_under++ ;
676 0           last ;
677             }
678             }
679              
680 0           return($is_under) ;
681             }
682              
683             #-----------------------------------------------------------------------------
684              
685             sub element_completely_within_rectangle
686             {
687 0     0 0   my ($self, $element, $rectangle) = @_ ;
688              
689 0           my ($start_x, $start_y) = ($rectangle->{START_X}, $rectangle->{START_Y}) ;
690 0           my $width = $rectangle->{END_X} - $rectangle->{START_X} ;
691 0           my $height = $rectangle->{END_Y} - $rectangle->{START_Y};
692              
693 0 0         if($width < 0)
694             {
695 0           $width *= -1 ;
696 0           $start_x -= $width ;
697             }
698            
699 0 0         if($height < 0)
700             {
701 0           $height *= -1 ;
702 0           $start_y -= $height ;
703             }
704              
705 0           my $is_under = 1 ;
706              
707 0           for my $strip (@{$element->get_stripes()})
  0            
708             {
709 0           my $stripe_x = $element->{X} + $strip->{X_OFFSET} ;
710 0           my $stripe_y = $element->{Y} + $strip->{Y_OFFSET} ;
711            
712 0 0 0       if
      0        
      0        
713             (
714             $start_x <= $stripe_x
715             && ($stripe_x + $strip->{WIDTH}) <= $start_x +$width
716             && $start_y <= $stripe_y
717             && ($stripe_y + $strip->{HEIGHT}) <= $start_y + $height
718             )
719             {
720             }
721             else
722             {
723 0           $is_under = 0 ;
724             last
725 0           }
726             }
727              
728 0           return($is_under) ;
729             }
730              
731             #-----------------------------------------------------------------------------
732              
733             sub get_extent_box
734             {
735 0     0 0   my ($self, @elements) = @_ ;
736              
737 0 0         @elements = $self->get_selected_elements(1) unless @elements ;
738              
739 0           my ($xs, $ys, $xe, $ye) = (10_000, 10_000, 0, 0) ;
740              
741 0           for (grep { ref($_) !~ /arrow/ } @elements)
  0            
742             {
743 0   0       $xs = min($xs//10_000, $_->{X} + $_->{EXTENTS}[0]) ;
744 0   0       $ys = min($ys//10_000, $_->{Y} + $_->{EXTENTS}[1]) ;
745 0   0       $xe = max($xe//0, $_->{X} + $_->{EXTENTS}[2]) ;
746 0   0       $ye = max($ye//0, $_->{Y} + $_->{EXTENTS}[3]) ;
747             }
748              
749 0   0       ($xs // 0, $ys // 0, $xe // 0, $ye // 0) ;
      0        
      0        
      0        
750             }
751              
752             #-----------------------------------------------------------------------------
753              
754             sub pixel_to_character_x
755             {
756 0     0 0   my ($self, @pixels) = @_ ;
757              
758 0           my ($character_width, $character_height) = $self->get_character_size() ;
759              
760 0           map {int($_ / $character_width)} @pixels ;
  0            
761             }
762              
763             #-----------------------------------------------------------------------------
764              
765             sub pixel_to_character_y
766             {
767 0     0 0   my ($self, @pixels) = @_ ;
768              
769 0           my ($character_width, $character_height) = $self->get_character_size() ;
770              
771 0           map {int($_ / $character_height)} @pixels ;
  0            
772             }
773              
774             #-----------------------------------------------------------------------------
775              
776             sub closest_character
777             {
778 0     0 0   my ($self, $x, $y) = @_ ;
779              
780 0           my ($character_width, $character_height) = $self->get_character_size() ;
781              
782 0           my $character_x = int($x / $character_width) ;
783 0           my $character_y = int($y / $character_height) ;
784              
785 0           return($character_x, $character_y) ;
786             }
787              
788             #-----------------------------------------------------------------------------
789              
790             1 ;
791