File Coverage

lib/Graph/Easy/Edge/Cell.pm
Criterion Covered Total %
statement 236 291 81.1
branch 92 158 58.2
condition 36 61 59.0
subroutine 22 25 88.0
pod 4 7 57.1
total 390 542 71.9


' . "\n" . ' . "\n"; ' . "\n" . ' . "\n"; '; ' . "\n"; ' . "\n"; ' . "\n"; ' . "\n"; ' . "\n" . ', '. "\n" . ', ' . "\n" . ' . "\n" . ', '. "\n" . ', ' . "\n" . ' . "\n" . ', '. "\n" . ', ' . "\n" . ' . "\n" . ', '. "\n" . ', ' . "\n" . ', '. "\n" . ' . "\n", ' . "\n" . '. ', '. "\n" . ' . "\n", ', ' . "\n" . ', '. "\n" . ' . "\n", ' . "\n" . ' . "\n" . ', '. "\n" . ' . "\n", ', ' . "\n" . ', '. "\n" . ', ' . "\n" . ' . "\n" . ', '. "\n" . ', ' . "\n" . ' . "\n" . ', '. "\n" . ', ' . "\n" . ', '. "\n" . ', ' . "\n" . ', '. "\n" . ', ' . "\n" . ' . "\n" . ', '. "\n" . ', ' . "\n" . ' . "\n" . ', '. "\n" . ', ' . "\n" . ', ', ' . "\n" . ', ', ' . "\n" . ', ', '. ' . "\n" . ', ', ' . "\n" . ' . "\n" . ' . "\n", ', ' . "\n" . ' . "\n" . ' . "\n", ', ' . "\n" . ', ', ' . "\n" . ' . "\n" . ', ', ' . "\n" . ' . "\n" . ', ', ' . "\n" . ' . "\n" . ', ', ' . "\n" . ' . "\n" . ', ', ' . "\n" . ', ', ' . "\n" . ', ', ' . "\n". ' . "\n" . ', ' . "\n". '."\n". ', ' . "\n" . ', ' . "\n" . ', ' . "\n". '."\n". ', ', '."\n". ', ' . "\n". ', ', ' . "\n" . ' ."\n". ', ' ."\n". '."\n". ', ', ' . "\n" . ', ' ."\n". ', ' . "\n" . ' . "\n" . ', ' ."\n". ', ' . "\n" . ', ', ', ' ."\n". ', ', ' ."\n". ', ', ' ."\n". ', ', ' . "\n" . ', ' ."\n". ', ' . "\n" . ', ' ."\n". ', ' . "\n" , ' ."\n" . ', ' ."\n". ', ', ' ."\n". ', ', ' ."\n". ', ', ', ', ' . "\n" . $rc->[0]; '; ' . "\n " . ' . "\n", ' . "\n"; ' . "\n" ' . "\n" . ' . "\n", ' . "\n" . ' . "\n", ' ]; ' ];
line stmt bran cond sub pod time code
1             #############################################################################
2             # Part of Graph::Easy.
3             #
4             #############################################################################
5              
6             package Graph::Easy::Edge::Cell;
7              
8 49     49   34884 use strict;
  49         186  
  49         2366  
9 49     49   619 use warnings;
  49         1712  
  49         1967  
10 49     49   1969 use Graph::Easy::Edge;
  49         1003  
  49         1287  
11 49     49   380 use Graph::Easy::Attributes;
  49         114  
  49         2030  
12             require Exporter;
13              
14 49     49   271 use vars qw/$VERSION @EXPORT_OK @ISA/;
  49         96  
  49         5098  
15             @ISA = qw/Exporter Graph::Easy::Edge/;
16              
17             $VERSION = '0.75';
18              
19 49     49   287 use Scalar::Util qw/weaken/;
  49         90  
  49         300604  
20              
21             #############################################################################
22              
23             # The different cell types:
24             use constant {
25 49         39587 EDGE_CROSS => 0, # + crossing lines
26             EDGE_HOR => 1, # -- horizontal line
27             EDGE_VER => 2, # | vertical line
28              
29             EDGE_N_E => 3, # |_ corner (N to E)
30             EDGE_N_W => 4, # _| corner (N to W)
31             EDGE_S_E => 5, # ,- corner (S to E)
32             EDGE_S_W => 6, # -, corner (S to W)
33              
34             # Joints:
35             EDGE_S_E_W => 7, # -,- three-sided corner (S to W/E)
36             EDGE_N_E_W => 8, # -'- three-sided corner (N to W/E)
37             EDGE_E_N_S => 9, # |- three-sided corner (E to S/N)
38             EDGE_W_N_S => 10, # -| three-sided corner (W to S/N)
39              
40             EDGE_HOLE => 11, # a hole (placeholder for the "other"
41             # edge in a crossing section
42             # Holes are inserted in the layout stage
43             # and removed in the optimize stage, before
44             # rendering occurs.
45              
46             # these loop types must come last
47             EDGE_N_W_S => 12, # v--+ loop, northwards
48             EDGE_S_W_N => 13, # ^--+ loop, southwards
49             EDGE_E_S_W => 14, # [_ loop, westwards
50             EDGE_W_S_E => 15, # _] loop, eastwards
51              
52             EDGE_MAX_TYPE => 15, # last valid type
53             EDGE_LOOP_TYPE => 12, # first LOOP type
54              
55             # Flags:
56             EDGE_START_E => 0x0100, # start from East (sorted ESWN)
57             EDGE_START_S => 0x0200, # start from South
58             EDGE_START_W => 0x0400, # start from West
59             EDGE_START_N => 0x0800, # start from North
60              
61             EDGE_END_W => 0x0010, # end points to West (sorted WNES)
62             EDGE_END_N => 0x0020, # end points to North
63             EDGE_END_E => 0x0040, # end points to East
64             EDGE_END_S => 0x0080, # end points to South
65              
66             EDGE_LABEL_CELL => 0x1000, # this cell carries the label
67             EDGE_SHORT_CELL => 0x2000, # a short edge pice (for filling)
68              
69             EDGE_ARROW_MASK => 0x0FF0, # mask out the end/start type
70             EDGE_START_MASK => 0x0F00, # mask out the start type
71             EDGE_END_MASK => 0x00F0, # mask out the end type
72             EDGE_TYPE_MASK => 0x000F, # mask out the basic cell type
73             EDGE_FLAG_MASK => 0xFFF0, # mask out the flags
74             EDGE_MISC_MASK => 0xF000, # mask out the misc. flags
75             EDGE_NO_M_MASK => 0x0FFF, # anything except the misc. flags
76              
77             ARROW_RIGHT => 0,
78             ARROW_LEFT => 1,
79             ARROW_UP => 2,
80             ARROW_DOWN => 3,
81 49     49   315 };
  49         98  
82              
83             use constant {
84 49         22896 EDGE_ARROW_HOR => EDGE_END_E() + EDGE_END_W(),
85             EDGE_ARROW_VER => EDGE_END_N() + EDGE_END_S(),
86              
87             # shortcuts to not need to write EDGE_HOR + EDGE_START_W + EDGE_END_E
88             EDGE_SHORT_E => EDGE_HOR + EDGE_END_E + EDGE_START_W, # |-> start/end at this cell
89             EDGE_SHORT_S => EDGE_VER + EDGE_END_S + EDGE_START_N, # v start/end at this cell
90             EDGE_SHORT_W => EDGE_HOR + EDGE_END_W + EDGE_START_E, # <-| start/end at this cell
91             EDGE_SHORT_N => EDGE_VER + EDGE_END_N + EDGE_START_S, # ^ start/end at this cell
92              
93             EDGE_SHORT_BD_EW => EDGE_HOR + EDGE_END_E + EDGE_END_W, # <-> start/end at this cell
94             EDGE_SHORT_BD_NS => EDGE_VER + EDGE_END_S + EDGE_END_N, # ^
95             # | start/end at this cell
96             # v
97             EDGE_SHORT_UN_EW => EDGE_HOR + EDGE_START_E + EDGE_START_W, # --
98             EDGE_SHORT_UN_NS => EDGE_VER + EDGE_START_S + EDGE_START_N, # |
99              
100             EDGE_LOOP_NORTH => EDGE_N_W_S + EDGE_END_S + EDGE_START_N + EDGE_LABEL_CELL,
101             EDGE_LOOP_SOUTH => EDGE_S_W_N + EDGE_END_N + EDGE_START_S + EDGE_LABEL_CELL,
102             EDGE_LOOP_WEST => EDGE_W_S_E + EDGE_END_E + EDGE_START_W + EDGE_LABEL_CELL,
103             EDGE_LOOP_EAST => EDGE_E_S_W + EDGE_END_W + EDGE_START_E + EDGE_LABEL_CELL,
104 49     49   298 };
  49         94  
105              
106             #############################################################################
107              
108             @EXPORT_OK = qw/
109             EDGE_START_E
110             EDGE_START_W
111             EDGE_START_N
112             EDGE_START_S
113              
114             EDGE_END_E
115             EDGE_END_W
116             EDGE_END_N
117             EDGE_END_S
118              
119             EDGE_SHORT_E
120             EDGE_SHORT_W
121             EDGE_SHORT_N
122             EDGE_SHORT_S
123              
124             EDGE_SHORT_BD_EW
125             EDGE_SHORT_BD_NS
126              
127             EDGE_SHORT_UN_EW
128             EDGE_SHORT_UN_NS
129              
130             EDGE_HOR
131             EDGE_VER
132             EDGE_CROSS
133             EDGE_HOLE
134              
135             EDGE_N_E
136             EDGE_N_W
137             EDGE_S_E
138             EDGE_S_W
139              
140             EDGE_S_E_W
141             EDGE_N_E_W
142             EDGE_E_N_S
143             EDGE_W_N_S
144              
145             EDGE_LOOP_NORTH
146             EDGE_LOOP_SOUTH
147             EDGE_LOOP_EAST
148             EDGE_LOOP_WEST
149              
150             EDGE_N_W_S
151             EDGE_S_W_N
152             EDGE_E_S_W
153             EDGE_W_S_E
154              
155             EDGE_TYPE_MASK
156             EDGE_FLAG_MASK
157             EDGE_ARROW_MASK
158            
159             EDGE_START_MASK
160             EDGE_END_MASK
161             EDGE_MISC_MASK
162              
163             EDGE_LABEL_CELL
164             EDGE_SHORT_CELL
165              
166             EDGE_NO_M_MASK
167              
168             ARROW_RIGHT
169             ARROW_LEFT
170             ARROW_UP
171             ARROW_DOWN
172             /;
173              
174             my $edge_types = {
175             EDGE_HOR() => 'horizontal',
176             EDGE_VER() => 'vertical',
177              
178             EDGE_CROSS() => 'crossing',
179              
180             EDGE_N_E() => 'north/east corner',
181             EDGE_N_W() => 'north/west corner',
182             EDGE_S_E() => 'south/east corner',
183             EDGE_S_W() => 'south/west corner',
184              
185             EDGE_S_E_W() => 'joint south to east/west',
186             EDGE_N_E_W() => 'joint north to east/west',
187             EDGE_E_N_S() => 'joint east to north/south',
188             EDGE_W_N_S() => 'joint west to north/south',
189              
190             EDGE_N_W_S() => 'selfloop, northwards',
191             EDGE_S_W_N() => 'selfloop, southwards',
192             EDGE_E_S_W() => 'selfloop, eastwards',
193             EDGE_W_S_E() => 'selfloop, westwards',
194             };
195              
196             my $flag_types = {
197             EDGE_LABEL_CELL() => 'labeled',
198             EDGE_SHORT_CELL() => 'short',
199              
200             EDGE_START_E() => 'starting east',
201             EDGE_START_W() => 'starting west',
202             EDGE_START_N() => 'starting north',
203             EDGE_START_S() => 'starting south',
204              
205             EDGE_END_E() => 'ending east',
206             EDGE_END_W() => 'ending west',
207             EDGE_END_N() => 'ending north',
208             EDGE_END_S() => 'ending south',
209             };
210              
211 49     49   295 use constant isa_cell => 1;
  49         95  
  49         391313  
212              
213             sub edge_type
214             {
215             # convert edge type number to some descriptive text
216 5     5 0 500 my $type = shift;
217              
218 5         8 my $flags = $type & EDGE_FLAG_MASK;
219 5         5 $type &= EDGE_TYPE_MASK;
220              
221 5   33     18 my $t = $edge_types->{$type} || ('unknown edge type #' . $type);
222              
223 5         7 $flags &= EDGE_FLAG_MASK;
224              
225 5         6 my $mask = 0x0010;
226 5         12 while ($mask < 0xFFFF)
227             {
228 60         48 my $tf = $flags & $mask; $mask <<= 1;
  60         47  
229 60 100       132 $t .= ", $flag_types->{$tf}" if $tf != 0;
230             }
231              
232 5         20 $t;
233             }
234              
235             #############################################################################
236              
237             sub _init
238             {
239             # generic init, override in subclasses
240 2135     2135   3401 my ($self,$args) = @_;
241            
242 2135         4511 $self->{type} = EDGE_SHORT_E(); # -->
243 2135         8461 $self->{style} = 'solid';
244            
245 2135         3869 $self->{x} = 0;
246 2135         3517 $self->{y} = 0;
247 2135         4107 $self->{w} = undef;
248 2135         4696 $self->{h} = 3;
249              
250 2135         14843 foreach my $k (sort keys %$args)
251             {
252             # don't store "after" and "before"
253 8657 100       44376 next unless $k =~ /^(graph|edge|x|y|type)\z/;
254 8526         24340 $self->{$k} = $args->{$k};
255             }
256              
257 2135 50       6666 $self->_croak("Creating edge cell without a parent edge object")
258             unless defined $self->{edge};
259 2135 50       5262 $self->_croak("Creating edge cell without a type")
260             unless defined $self->{type};
261              
262             # take over settings from edge
263 2135         8739 $self->{style} = $self->{edge}->style();
264 2135         8984 $self->{class} = $self->{edge}->class();
265 2135         6275 $self->{graph} = $self->{edge}->{graph};
266 2135         4608 $self->{group} = $self->{edge}->{group};
267 2135         7116 weaken($self->{graph});
268 2135         4881 weaken($self->{group});
269 2135         5400 $self->{att} = $self->{edge}->{att};
270              
271             # register ourselves at this edge
272 2135         16249 $self->{edge}->_add_cell ($self, $args->{after}, $args->{before});
273              
274 2135         13077 $self;
275             }
276              
277             sub arrow_count
278             {
279             # return 0, 1 or 2, depending on the number of end points
280 0     0 0 0 my $self = shift;
281              
282 0 0       0 return 0 if $self->{edge}->{undirected};
283              
284 0         0 my $count = 0;
285 0         0 my $type = $self->{type};
286 0 0       0 $count ++ if ($type & EDGE_END_N) != 0;
287 0 0       0 $count ++ if ($type & EDGE_END_S) != 0;
288 0 0       0 $count ++ if ($type & EDGE_END_W) != 0;
289 0 0       0 $count ++ if ($type & EDGE_END_E) != 0;
290 0 0       0 if ($self->{edge}->{bidirectional})
291             {
292 0 0       0 $count ++ if ($type & EDGE_START_N) != 0;
293 0 0       0 $count ++ if ($type & EDGE_START_S) != 0;
294 0 0       0 $count ++ if ($type & EDGE_START_W) != 0;
295 0 0       0 $count ++ if ($type & EDGE_START_E) != 0;
296             }
297 0         0 $count;
298             }
299              
300             sub _make_cross
301             {
302             # Upgrade us to a cross-section.
303 20     20   76 my ($self, $edge, $flags) = @_;
304            
305 20         54 my $type = $self->{type} & EDGE_TYPE_MASK;
306            
307 20 50 66     122 $self->_croak("Trying to cross non hor/ver piece at $self->{x},$self->{y}")
308             if (($type != EDGE_HOR) && ($type != EDGE_VER));
309              
310 20         528 $self->{color} = $self->get_color_attribute('color');
311 20         100 $self->{style_ver} = $edge->style();
312 20         164 $self->{color_ver} = $edge->get_color_attribute('color');
313              
314             # if we are the VER piece, switch styles around
315 20 100       92 if ($type == EDGE_VER)
316             {
317 10         50 ($self->{style_ver}, $self->{style}) = ($self->{style},$self->{style_ver});
318 10         43 ($self->{color_ver}, $self->{color}) = ($self->{color},$self->{color});
319             }
320              
321 20   100     133 $self->{type} = EDGE_CROSS + ($flags || 0);
322              
323 20         59 $self;
324             }
325              
326             sub _make_joint
327             {
328             # Upgrade us to a joint
329 31     31   70 my ($self, $edge, $new_type) = @_;
330            
331 31         79 my $type = $self->{type} & EDGE_TYPE_MASK;
332              
333 31 50       105 $self->_croak("Trying to join non hor/ver piece (type: $type) at $self->{x},$self->{y}")
334             if $type >= EDGE_S_E_W;
335              
336 31         505 $self->{color} = $self->get_color_attribute('color');
337 31         174 $self->{style_ver} = $edge->style();
338 31         253 $self->{color_ver} = $edge->get_color_attribute('color');
339              
340             # if we are the VER piece, switch styles around
341 31 100       126 if ($type == EDGE_VER)
342             {
343 2         11 ($self->{style_ver}, $self->{style}) = ($self->{style},$self->{style_ver});
344 2         10 ($self->{color_ver}, $self->{color}) = ($self->{color},$self->{color});
345             }
346              
347 31 50       266 print STDERR "# creating joint at $self->{x}, $self->{y} with new type $new_type (old $type)\n"
348             if $self->{graph}->{debug};
349              
350 31         66 $self->{type} = $new_type;
351              
352 31         112 $self;
353             }
354              
355             #############################################################################
356             # conversion to HTML
357              
358             my $edge_end_north =
359             '  
360             ' ^
361             my $edge_end_south =
362             '  
363             ' v
364              
365             my $edge_empty_row =
366             '
367              
368             my $edge_arrow_west_upper =
369             '<
370             my $edge_arrow_west_lower =
371             ' 
372              
373             my $edge_arrow_east_upper =
374             '>
375             my $edge_arrow_east_lower =
376             '
377              
378             my $edge_html = {
379              
380             # The " " in empty table cells with borders are here to make IE display
381             # the border. I so hate browser bugs :-(
382              
383             EDGE_S_E() => [
384             '
385             '  
386             '',
387             '
388             '  
389             '',
390             ],
391              
392             EDGE_S_E() + EDGE_START_E() + EDGE_END_S() => [
393             '
394             '  
395             '
396             '',
397             '
398             '  
399             $edge_end_south,
400             ],
401              
402             EDGE_S_E() + EDGE_START_E() => [
403             '
404             '  
405             '
406             '',
407             '
408             '  
409             '',
410             ],
411              
412             EDGE_S_E() + EDGE_END_E() => [
413             '
414             '  
415             ' >
416             '',
417             '
418             '  
419             '',
420             ],
421              
422             EDGE_S_E() + EDGE_START_S() => [
423             '
424             '  
425             '',
426             '
427             '  
428             $edge_empty_row,
429             ],
430              
431             EDGE_S_E() + EDGE_START_S() + EDGE_END_E() => [
432             '
433             '  
434             ' >
435             '',
436             '
437             '  
438             '
439             ],
440              
441             EDGE_S_E() + EDGE_END_S() => [
442             '
443             '  
444             '',
445             '
446             '  
447             $edge_end_south,
448             ],
449              
450             EDGE_S_E() + EDGE_END_S() + EDGE_END_E() => [
451             '
452             '  
453             ' >
454             '',
455             '
456             '  
457             ' v
458             ],
459              
460             ###########################################################################
461             ###########################################################################
462             # S_W
463              
464             EDGE_S_W() => [
465             '  
466             '
467             '',
468             '
469             '  
470             '',
471             ],
472              
473             EDGE_S_W() + EDGE_START_W() => [
474             '
475             '  
476             '
477             '',
478             '
479             '  
480             '',
481             ],
482              
483             EDGE_S_W() + EDGE_END_W() => [
484             ' <
485             '  
486             '
487             '',
488             '
489             '  
490             '',
491             ],
492              
493             EDGE_S_W() + EDGE_START_S() => [
494             '  
495             '
496             '',
497             '
498             '  
499             $edge_empty_row,
500             ],
501              
502             EDGE_S_W() + EDGE_END_S() => [
503             '  
504             '
505             '',
506             '
507             '  
508             $edge_end_south,
509             ],
510              
511             EDGE_S_W() + EDGE_START_W() + EDGE_END_S() => [
512             '
513             '  
514             '
515             '',
516             '
517             '  
518             $edge_end_south,
519             ],
520              
521             EDGE_S_W() + EDGE_START_S() + EDGE_END_W() => [
522             ' <
523             '  
524             '
525             '',
526             '
527             '  
528             $edge_empty_row,
529             ],
530              
531             ###########################################################################
532             ###########################################################################
533             # N_W
534              
535             EDGE_N_W() => [
536             '  
537             '  
538             '',
539             '
540             '',
541             ],
542              
543             EDGE_N_W() + EDGE_START_N() => [
544             $edge_empty_row,
545             '  
546             '  
547             '',
548             '
549             ],
550              
551             EDGE_N_W() + EDGE_END_N() => [
552             $edge_end_north,
553             '  
554             '  
555             '
556             '',
557             ],
558              
559             EDGE_N_W() + EDGE_END_N() + EDGE_START_W() => [
560             $edge_end_north,
561             '
562             '  
563             '  
564             '
565             '',
566             ],
567              
568             EDGE_N_W() + EDGE_START_W() => [
569             '
570             '  
571             '  
572             '',
573             '
574             '',
575             ],
576              
577             EDGE_N_W() + EDGE_END_W() => [
578             ' <
579             '  
580             '  
581             '',
582             '
583             '',
584             ],
585              
586             ###########################################################################
587             ###########################################################################
588             # N_E
589              
590             EDGE_N_E() => [
591             '
592             '  
593             '',
594             '
595             '',
596             ],
597              
598             EDGE_N_E() + EDGE_START_E() => [
599             '
600             '  
601             '
602             '',
603             '
604             '',
605             ],
606              
607             EDGE_N_E() + EDGE_END_E() => [
608             '
609             '  
610             ' >
611             '',
612             '
613             '',
614             ],
615              
616             EDGE_N_E() + EDGE_END_E() + EDGE_START_N() => [
617             $edge_empty_row,
618             '
619             '  
620             ' >
621             '
622             '',
623             ],
624              
625             EDGE_N_E() + EDGE_START_E() + EDGE_END_N() => [
626             $edge_end_north,
627             '
628             '  
629             '  
630             '
631             '',
632             ],
633              
634             EDGE_N_E() + EDGE_START_N() => [
635             $edge_empty_row,
636             '
637             '  
638             '
639             '',
640             ],
641              
642             EDGE_N_E() + EDGE_END_N() => [
643             $edge_end_north,
644             '
645             '  
646             '',
647             '
648             ],
649              
650             ###########################################################################
651             ###########################################################################
652             # self loops
653              
654             EDGE_LOOP_NORTH() - EDGE_LABEL_CELL() => [
655             ' 
656             ' ##label##
657             '  
658             '',
659             ' 
660             '  
661             '  
662              
663             'v
664             '  
665              
666             ],
667              
668             EDGE_LOOP_SOUTH() - EDGE_LABEL_CELL() => [
669             '^
670             '  
671              
672             ' 
673             ' ##label##
674             '  
675              
676             '',
677              
678             ' 
679              
680             ],
681              
682             EDGE_LOOP_WEST() - EDGE_LABEL_CELL() => [
683             $edge_empty_row.
684             ' ##label##
685             '  
686              
687             '',
688              
689             ' 
690             ' >
691            
692             ' 
693             ],
694              
695             EDGE_LOOP_EAST() - EDGE_LABEL_CELL() => [
696              
697             ' 
698             ' ##label##
699             '  
700              
701             '',
702              
703             '<
704             '  
705             '  
706            
707             ' 
708             ],
709              
710             ###########################################################################
711             ###########################################################################
712             # joints
713              
714             ###########################################################################
715             # E_N_S
716              
717             EDGE_E_N_S() => [
718             ' 
719             '  
720             '',
721             ' 
722             '  
723             '',
724             ],
725              
726             EDGE_E_N_S() + EDGE_END_E() => [
727             ' 
728             '  
729             ' >
730             '',
731             ' 
732             '  
733             '',
734             ],
735              
736             ###########################################################################
737             # W_N_S
738              
739             EDGE_W_N_S() => [
740             ' 
741             '  
742             '',
743             ' 
744             '',
745             ],
746              
747             ###########################################################################
748             # S_E_W
749              
750             EDGE_S_E_W() => [
751             ' 
752             '',
753             ' 
754             '  
755             '',
756             ],
757              
758             EDGE_S_E_W() + EDGE_END_S() => [
759             ' 
760             '',
761             ' 
762             '  
763             $edge_end_south,
764             ],
765              
766             EDGE_S_E_W() + EDGE_START_S() => [
767             ' 
768             '',
769             ' 
770             '  
771             '
772             ],
773              
774             EDGE_S_E_W() + EDGE_START_W() => [
775             '
776             ' 
777             '',
778             ' 
779             '  
780             '',
781              
782             ],
783              
784             EDGE_S_E_W() + EDGE_END_E() => [
785             ' 
786             ' >
787             '',
788             ' 
789             '  
790             '',
791             ],
792              
793             EDGE_S_E_W() + EDGE_END_W() => [
794             $edge_arrow_west_upper .
795             ' 
796             '',
797             ' 
798             ' 
799             ],
800              
801             ###########################################################################
802             # N_E_W
803              
804             EDGE_N_E_W() => [
805             '  
806             ' 
807             '',
808             ' 
809             '',
810             ],
811              
812             EDGE_N_E_W() + EDGE_END_N() => [
813             $edge_end_north,
814             '  
815             ' 
816             '',
817             ' 
818             '',
819             ],
820              
821             EDGE_N_E_W() + EDGE_START_N() => [
822             $edge_empty_row,
823             '  
824             ' 
825             '',
826             ' 
827             '',
828             ],
829              
830             };
831              
832             sub _html_edge_hor
833             {
834             # Return HTML code for a horizontal edge (with all start/end combinations)
835             # as [], with code for each table row.
836 24     24   48 my ($self, $as) = @_;
837              
838 24         60 my $s_flags = $self->{type} & EDGE_START_MASK;
839 24         48 my $e_flags = $self->{type} & EDGE_END_MASK;
840              
841 24 50       1033 $e_flags = 0 if $as eq 'none';
842              
843             # XXX TODO: we could skip the output of "eb" parts when this edge doesn't belong
844             # to a group.
845              
846 24         96 my $rc = [
847             ' ##label##
848             '',
849             ' 
850             '',
851             ];
852              
853             # This assumes that only 2 end/start flags are set at the same time:
854              
855 24         41 my $mod = 4; # modifier
856 24 50       69 if ($s_flags & EDGE_START_W)
857             {
858 24         109 $mod--;
859 24         95 $rc->[0] = '
860             };
861 24 50       72 if ($s_flags & EDGE_START_E)
862             {
863 0         0 $mod--;
864 0         0 $rc->[0] .= "\n " . '
865             };
866 24 50       66 if ($e_flags & EDGE_END_W)
867             {
868 0         0 $mod--;
869 0         0 $rc->[0] = $edge_arrow_west_upper . $rc->[0];
870 0         0 $rc->[2] = $edge_arrow_west_lower . $rc->[2];
871             }
872 24 50       64 if ($e_flags & EDGE_END_E)
873             {
874 24         37 $mod--;
875 24         95 $rc->[0] .= "\n " . $edge_arrow_east_upper;
876 24         92 $rc->[2] .= "\n " . $edge_arrow_east_lower;
877             };
878              
879             # cx == 1: mod = 2..4, cx == 2: mod = 6..8, etc.
880 24   100     128 $self->{cx} ||= 1;
881 24         62 $mod = $self->{cx} * 4 - 4 + $mod;
882              
883 24         50 for my $e (@$rc)
884             {
885 96         362 $e =~ s/##mod##/$mod/g;
886             }
887              
888 24         66 $rc;
889             }
890              
891             sub _html_edge_ver
892             {
893             # Return HTML code for a vertical edge (with all start/end combinations)
894             # as [], with code for each table row.
895 0     0   0 my ($self, $as) = @_;
896              
897 0         0 my $s_flags = $self->{type} & EDGE_START_MASK;
898 0         0 my $e_flags = $self->{type} & EDGE_END_MASK;
899              
900 0 0       0 $e_flags = 0 if $as eq 'none';
901              
902 0         0 my $mod = 4; # modifier
903              
904             # normal vertical edge with no start/end flags
905 0         0 my $rc = [
906             ' 
907             '##label##
908             '',
909             '',
910             '',
911             ];
912              
913             # flag north
914 0 0       0 if ($s_flags & EDGE_START_N)
    0          
915             {
916 0         0 $mod--;
917 0         0 unshift @$rc, '
918 0         0 delete $rc->[-1];
919             }
920             elsif ($e_flags & EDGE_END_N)
921             {
922 0         0 $mod--;
923 0         0 unshift @$rc, $edge_end_north;
924 0         0 delete $rc->[-1];
925             }
926              
927             # flag south
928 0 0       0 if ($s_flags & EDGE_START_S)
929             {
930 0         0 $mod--;
931 0         0 $rc->[3] = '
932             }
933              
934 0 0       0 if ($e_flags & EDGE_END_S)
935             {
936 0         0 $mod--;
937 0         0 $rc->[3] = $edge_end_south;
938             }
939              
940 0   0     0 $self->{cy} ||= 1;
941 0         0 $mod = $self->{cy} * 4 - 4 + $mod;
942              
943 0         0 for my $e (@$rc)
944             {
945 0         0 $e =~ s/##mod##/$mod/g;
946             }
947              
948 0         0 $rc;
949             }
950              
951             sub _html_edge_cross
952             {
953             # Return HTML code for a crossingedge (with all start/end combinations)
954             # as [], with code for each table row.
955 0     0   0 my ($self, $N, $S, $E, $W) = @_;
956              
957             # my $s_flags = $self->{type} & EDGE_START_MASK;
958             # my $e_flags = $self->{type} & EDGE_END_MASK;
959              
960 0         0 my $rc = [
961             '  
962             '  
963             '',
964             '
965             '  
966             '',
967             ];
968              
969 0         0 $rc;
970             }
971              
972             sub as_html
973             {
974 25     25 1 57 my ($self) = shift;
975              
976 25         77 my $type = $self->{type} & EDGE_NO_M_MASK;
977 25         55 my $style = $self->{style};
978              
979             # none, open, filled, closed
980 25 50       34 my $as; $as = 'none' if $self->{edge}->{undirected};
  25         93  
981 25 50       143 $as = $self->attribute('arrowstyle') unless $as;
982            
983             # triangle, box, dot, inv, diamond, line etc.
984 25 50       50 my $ashape; $ashape = 'triangle' if $self->{edge}->{undirected};
  25         96  
985 25 50       98 $ashape = $self->attribute('arrowshape') unless $ashape;
986              
987 25         78 my $code = $edge_html->{$type};
988              
989 25 50       72 if (!defined $code)
990             {
991 25         56 my $t = $self->{type} & EDGE_TYPE_MASK;
992              
993 25 100       199 if ($style ne 'invisible')
994             {
995 24 50       143 $code = $self->_html_edge_hor($as) if $t == EDGE_HOR;
996 24 50       81 $code = $self->_html_edge_ver($as) if $t == EDGE_VER;
997 24 50       74 $code = $self->_html_edge_cross($as) if $t == EDGE_CROSS;
998             }
999             else
1000             {
1001 1         5 $code = [ '  
1002             }
1003              
1004 25 50       64 if (!defined $code)
1005             {
1006 0         0 $code = [ ' ???
1007 0         0 warn ("as_html: Unimplemented edge type $self->{type} ($type) at $self->{x},$self->{y} "
1008             . edge_type($self->{type}));
1009             }
1010             }
1011              
1012 25         79 my $id = $self->{graph}->{id};
1013              
1014 25         366 my $color = $self->get_color_attribute('color');
1015 25         55 my $label = '';
1016 25         37 my $label_style = '';
1017              
1018             # only include the label if we are the label cell
1019 25 100 66     159 if ($style ne 'invisible' && ($self->{type} & EDGE_LABEL_CELL))
1020             {
1021 24         43 my $switch_to_center;
1022 24         107 ($label,$switch_to_center) = $self->_label_as_html();
1023              
1024             # replace linebreaks by
, but remove extra spaces
1025 24         185 $label =~ s/\s*\\n\s*/
/g;
1026              
1027 24   66     174 my $label_color = $self->raw_color_attribute('labelcolor') || $color;
1028 24 100       78 $label_color = '' if $label_color eq '#000000';
1029 24 100       97 $label_style = "color: $label_color;" if $label_color;
1030              
1031 24   50     241 my $font = $self->attribute('font') || '';
1032 24 50 50     110 $font = '' if $font eq ($self->default_attribute('font') || '');
1033 24 50       87 $label_style = "font-family: $font;" if $font;
1034            
1035 24 100       98 $label_style .= $self->text_styles_as_css(1,1) unless $label eq '';
1036              
1037 24         172 $label_style =~ s/^\s*//;
1038              
1039 24         122 my $link = $self->link();
1040 24 100       84 if ($link ne '')
1041             {
1042             # encode critical entities
1043 1         3 $link =~ s/\s/\+/g; # space
1044 1         3 $link =~ s/'/%27/g; # single-quote
1045              
1046             # put the style on the link
1047 1 50       5 $label_style = " style='$label_style'" if $label_style;
1048 1         5 $label = "$label";
1049 1         2 $label_style = '';
1050             }
1051              
1052             }
1053             # without  , IE doesn't draw the cell-border nec. for edges
1054 25 100       108 $label = ' ' unless $label ne '';
1055              
1056             ###########################################################################
1057             # get the border styles/colors:
1058              
1059             # width for the edge is "2px"
1060 25         42 my $bow = '2';
1061 25         116 my $border = Graph::Easy::_border_attribute_as_html( $self->{style}, $bow, $color);
1062 25         51 my $border_v = $border;
1063              
1064 25 50       89 if (($self->{type} & EDGE_TYPE_MASK) == EDGE_CROSS)
1065             {
1066 0         0 $border_v = Graph::Easy::_border_attribute_as_html( $self->{style_ver}, $bow, $self->{color_ver});
1067             }
1068              
1069             ###########################################################################
1070 25 50       51 my $edge_color = ''; $edge_color = " color: $color;" if $color;
  25         81  
1071              
1072             # If the group doesn't have a fill attribute, then it is defined in the CSS
1073             # of the group, and since we get the same class, we can skip the background.
1074             # But if the group has a fill, we need to use this as override.
1075             # The idea behind is to omit the "background: #daffff;" as much as possible.
1076              
1077 25   50     98 my $bg = $self->attribute('background') || '';
1078 25         59 my $group = $self->{edge}->{group};
1079 25 50       71 $bg = '' if $bg eq 'inherit';
1080 25 50 33     103 $bg = $group->{att}->{fill} if $group->{att}->{fill} && $bg eq '';
1081 25 50       61 $bg = '' if $bg eq 'inherit';
1082 25 50       52 $bg = " background: $bg;" if $bg;
1083              
1084 25         112 my $title = $self->title();
1085 25         61 $title =~ s/"//g; # replace quotation marks
1086 25 100       179 $title = " title=\"$title\"" if $title ne ''; # add mouse-over title
1087              
1088             ###########################################################################
1089             # replace templates
1090            
1091 25 50       1192 require Graph::Easy::As_ascii if $as ne 'none'; # for _unicode_arrow()
1092              
1093             # replace borderv with the border for the vertical edge on CROSS sections
1094 25         210 $border =~ s/\s+/ /g; # collapse multiple spaces
1095 25         128 $border_v =~ s/\s+/ /g;
1096 25         125 my $cl = $self->class(); $cl =~ s/\./_/g; # group.cities => group_cities
  25         52  
1097              
1098 25         44 my $rc;
1099 25         73 for my $a (@$code)
1100             {
1101 97 50       221 if (ref($a))
1102             {
1103 0         0 for my $c (@$a)
1104             {
1105 0         0 push @$rc, $self->_format_td($c,
1106             $border, $border_v, $label_style, $edge_color, $bg, $as, $ashape, $title, $label, $cl);
1107             }
1108             }
1109             else
1110             {
1111 97         336 push @$rc, $self->_format_td($a,
1112             $border, $border_v, $label_style, $edge_color, $bg, $as, $ashape, $title, $label, $cl);
1113             }
1114             }
1115              
1116 25         187 $rc;
1117             }
1118              
1119             sub _format_td
1120             {
1121 97     97   281 my ($self, $c,
1122             $border, $border_v, $label_style, $edge_color, $bg, $as, $ashape, $title, $label, $cl) = @_;
1123              
1124             # insert 'style="##bg##"' unless there is already a style
1125 97         803 $c =~ s/( e[bl]")(>( )?<\/td>)/$1 style="##bg##"$2/g;
1126             # insert missing "##bg##"
1127 97         255 $c =~ s/style="border/style="##bg##border/g;
1128              
1129 97         319 $c =~ s/##class##/$cl/g;
1130 97         266 $c =~ s/##border##/$border/g;
1131 97         146 $c =~ s/##borderv##/$border_v/g;
1132 97         298 $c =~ s/##lc##/$label_style/g;
1133 97         148 $c =~ s/##edgecolor##/ style="$edge_color"/g;
1134 97         207 $c =~ s/##ec##/$edge_color/g;
1135 97         301 $c =~ s/##bg##/$bg/g;
1136 97         237 $c =~ s/ style=""//g; # remove empty styles
1137              
1138             # remove arrows if edge is undirected
1139 97 50       254 $c =~ s/>(v|\^|<|>)/>/g if $as eq 'none';
1140              
1141             # insert "nice" looking Unicode arrows
1142 97         461 $c =~ s/>(v|\^|<|>)/'>' . $self->_unicode_arrow($ashape, $as, $1); /eg;
  24         160  
1143              
1144             # insert the label last, other "v" as label might get replaced above
1145 97         261 $c =~ s/>##label##/$title>$label/;
1146             # for empty labels use a different class
1147 97 50       215 $c =~ s/ lh"/ eb"/ if $label eq '';
1148              
1149 97 100       337 $c .= "\n" unless $c =~ /\n\z/;
1150              
1151 97         288 $self->quoted_comment() . $c;
1152             }
1153              
1154             sub class
1155             {
1156 25     25 1 44 my $self = shift;
1157              
1158 25   50     206 my $c = $self->{class} . ($self->{cell_class} || '');
1159 25 50       114 $c = $self->{edge}->{group}->class() . ' ' . $c if ref($self->{edge}->{group});
1160              
1161 25         74 $c;
1162             }
1163              
1164             sub group
1165             {
1166             # return the group we belong to as the group of our parent-edge
1167 629     629 1 866 my $self = shift;
1168              
1169 629         3013 $self->{edge}->{group};
1170             }
1171              
1172             #############################################################################
1173             # accessor methods
1174              
1175             sub type
1176             {
1177             # get/set type of this path element
1178             # type - EDGE_START, EDGE_END, EDGE_HOR, EDGE_VER, etc
1179 1     1 0 6 my ($self,$type) = @_;
1180              
1181 1 50       4 if (defined $type)
1182             {
1183 0 0 0     0 if (defined $type && $type < 0 || $type > EDGE_MAX_TYPE)
      0        
1184             {
1185 0         0 require Carp;
1186 0         0 Carp::confess ("Cell type $type for cell $self->{x},$self->{y} is not valid.");
1187             }
1188 0         0 $self->{type} = $type;
1189             }
1190              
1191 1         5 $self->{type};
1192             }
1193              
1194             #############################################################################
1195              
1196             # For rendering this path element as ASCII, we need to correct our width based
1197             # on whether we have a border or not. But this is only known after parsing is
1198             # complete.
1199              
1200             sub _correct_size
1201             {
1202 1587     1587   2667 my ($self,$format) = @_;
1203              
1204 1587 100       4315 return if defined $self->{w};
1205              
1206             # min-size is this
1207 1557         2392 $self->{w} = 5; $self->{h} = 3;
  1557         2396  
1208             # make short cell pieces very small
1209 1557 100       4475 if (($self->{type} & EDGE_SHORT_CELL) != 0)
1210             {
1211 26         39 $self->{w} = 1; $self->{h} = 1;
  26         44  
1212 26         68 return;
1213             }
1214            
1215 1531         2850 my $arrows = ($self->{type} & EDGE_ARROW_MASK);
1216 1531         2326 my $type = ($self->{type} & EDGE_TYPE_MASK);
1217              
1218 1531 100 100     5374 if ($self->{edge}->{bidirectional} && $arrows != 0)
1219             {
1220 40 100       125 $self->{w}++ if $type == EDGE_HOR;
1221 40 100       123 $self->{h}++ if $type == EDGE_VER;
1222             }
1223              
1224             # make joints bigger if they got arrows
1225 1531         2305 my $ah = $self->{type} & EDGE_ARROW_HOR;
1226 1531         2381 my $av = $self->{type} & EDGE_ARROW_VER;
1227 1531 100 66     6080 $self->{w}++ if $ah && ($type == EDGE_S_E_W || $type == EDGE_N_E_W);
      66        
1228 1531 100 66     5026 $self->{h}++ if $av && ($type == EDGE_E_N_S || $type == EDGE_W_N_S);
      66        
1229              
1230 1531   50     5562 my $style = $self->{edge}->attribute('style') || 'solid';
1231              
1232             # make the edge to display ' ..-> ' instead of ' ..> ':
1233 1531 100       3912 $self->{w}++ if $style eq 'dot-dot-dash';
1234              
1235 1531 100       3315 if ($type >= EDGE_LOOP_TYPE)
1236             {
1237             # +---+
1238             # | V
1239              
1240             # +
1241             # +--> |
1242             # | |
1243             # +--- |
1244             # +
1245 31         71 $self->{w} = 7;
1246 31 100 100     212 $self->{w} = 8 if $type == EDGE_N_W_S || $type == EDGE_S_W_N;
1247 31         56 $self->{h} = 3;
1248 31 100 100     196 $self->{h} = 5 if $type != EDGE_N_W_S && $type != EDGE_S_W_N;
1249             }
1250              
1251 1531 100       7892 if ($self->{type} == EDGE_HOR)
    100          
    100          
1252             {
1253 146         426 $self->{w} = 0;
1254             }
1255             elsif ($self->{type} == EDGE_VER)
1256             {
1257 58         204 $self->{h} = 0;
1258             }
1259             elsif ($self->{type} & EDGE_LABEL_CELL)
1260             {
1261             # edges do not have borders
1262 814 100       3092 my ($w,$h) = $self->dimensions(); $h-- unless $h == 0;
  814         1999  
1263              
1264 814         1596 $h += $self->{h};
1265 814         1977 $w += $self->{w};
1266 814         1098 $self->{w} = $w;
1267 814         2888 $self->{h} = $h;
1268             }
1269             }
1270              
1271             #############################################################################
1272             # attribute handling
1273              
1274             sub attribute
1275             {
1276 5960     5960 1 9861 my ($self, $name) = @_;
1277              
1278 5960         11211 my $edge = $self->{edge};
1279              
1280             # my $native = $edge->{att}->{$name};
1281             # return $native if defined $native && $native ne 'inherit';
1282              
1283             # shortcut, look up the attribute directly
1284 5960 100 66     26958 return $edge->{att}->{$name}
1285             if defined $edge->{att}->{$name} && $edge->{att}->{$name} ne 'inherit';
1286              
1287 5623         21642 return $edge->attribute($name);
1288              
1289             # XXX TODO This does not work, since caching the attribute doesn't get invalidated
1290             # upon set_attribute().
1291              
1292             # $edge->{cache} = {} unless exists $edge->{cache};
1293             # $edge->{cache}->{att} = {} unless exists $edge->{cache}->{att};
1294             #
1295             # my $cache = $edge->{cache}->{att};
1296             # return $cache->{$name} if exists $cache->{$name};
1297             #
1298             # my $rc = $edge->attribute($name);
1299             # # only cache values that weren't inherited to avoid cache problems
1300             # $cache->{$name} = $rc unless defined $native && $native eq 'inherit';
1301             #
1302             # $rc;
1303             }
1304              
1305             1;
1306              
1307             #############################################################################
1308             #############################################################################
1309              
1310             package Graph::Easy::Edge::Cell::Empty;
1311              
1312             require Graph::Easy::Node::Cell;
1313             our @ISA = qw/Graph::Easy::Node::Cell/;
1314              
1315             #use vars qw/$VERSION/;
1316              
1317             our $VERSION = '0.75';
1318              
1319 49     49   1014 use constant isa_cell => 1;
  49         124  
  49         6459  
1320              
1321             1;
1322             __END__