1############################################################################# 2# Part of Graph::Easy. 3# 4############################################################################# 5 6package Graph::Easy::Edge::Cell; 7 8use strict; 9use warnings; 10use Graph::Easy::Edge; 11use Graph::Easy::Attributes; 12require Exporter; 13 14use vars qw/$VERSION @EXPORT_OK @ISA/; 15@ISA = qw/Exporter Graph::Easy::Edge/; 16 17$VERSION = '0.76'; 18 19use Scalar::Util qw/weaken/; 20 21############################################################################# 22 23# The different cell types: 24use constant { 25 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 }; 82 83use constant { 84 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 }; 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 174my $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 196my $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 211use constant isa_cell => 1; 212 213sub edge_type 214 { 215 # convert edge type number to some descriptive text 216 my $type = shift; 217 218 my $flags = $type & EDGE_FLAG_MASK; 219 $type &= EDGE_TYPE_MASK; 220 221 my $t = $edge_types->{$type} || ('unknown edge type #' . $type); 222 223 $flags &= EDGE_FLAG_MASK; 224 225 my $mask = 0x0010; 226 while ($mask < 0xFFFF) 227 { 228 my $tf = $flags & $mask; $mask <<= 1; 229 $t .= ", $flag_types->{$tf}" if $tf != 0; 230 } 231 232 $t; 233 } 234 235############################################################################# 236 237sub _init 238 { 239 # generic init, override in subclasses 240 my ($self,$args) = @_; 241 242 $self->{type} = EDGE_SHORT_E(); # --> 243 $self->{style} = 'solid'; 244 245 $self->{x} = 0; 246 $self->{y} = 0; 247 $self->{w} = undef; 248 $self->{h} = 3; 249 250 foreach my $k (sort keys %$args) 251 { 252 # don't store "after" and "before" 253 next unless $k =~ /^(graph|edge|x|y|type)\z/; 254 $self->{$k} = $args->{$k}; 255 } 256 257 $self->_croak("Creating edge cell without a parent edge object") 258 unless defined $self->{edge}; 259 $self->_croak("Creating edge cell without a type") 260 unless defined $self->{type}; 261 262 # take over settings from edge 263 $self->{style} = $self->{edge}->style(); 264 $self->{class} = $self->{edge}->class(); 265 $self->{graph} = $self->{edge}->{graph}; 266 $self->{group} = $self->{edge}->{group}; 267 weaken($self->{graph}); 268 weaken($self->{group}); 269 $self->{att} = $self->{edge}->{att}; 270 271 # register ourselves at this edge 272 $self->{edge}->_add_cell ($self, $args->{after}, $args->{before}); 273 274 $self; 275 } 276 277sub arrow_count 278 { 279 # return 0, 1 or 2, depending on the number of end points 280 my $self = shift; 281 282 return 0 if $self->{edge}->{undirected}; 283 284 my $count = 0; 285 my $type = $self->{type}; 286 $count ++ if ($type & EDGE_END_N) != 0; 287 $count ++ if ($type & EDGE_END_S) != 0; 288 $count ++ if ($type & EDGE_END_W) != 0; 289 $count ++ if ($type & EDGE_END_E) != 0; 290 if ($self->{edge}->{bidirectional}) 291 { 292 $count ++ if ($type & EDGE_START_N) != 0; 293 $count ++ if ($type & EDGE_START_S) != 0; 294 $count ++ if ($type & EDGE_START_W) != 0; 295 $count ++ if ($type & EDGE_START_E) != 0; 296 } 297 $count; 298 } 299 300sub _make_cross 301 { 302 # Upgrade us to a cross-section. 303 my ($self, $edge, $flags) = @_; 304 305 my $type = $self->{type} & EDGE_TYPE_MASK; 306 307 $self->_croak("Trying to cross non hor/ver piece at $self->{x},$self->{y}") 308 if (($type != EDGE_HOR) && ($type != EDGE_VER)); 309 310 $self->{color} = $self->get_color_attribute('color'); 311 $self->{style_ver} = $edge->style(); 312 $self->{color_ver} = $edge->get_color_attribute('color'); 313 314 # if we are the VER piece, switch styles around 315 if ($type == EDGE_VER) 316 { 317 ($self->{style_ver}, $self->{style}) = ($self->{style},$self->{style_ver}); 318 ($self->{color_ver}, $self->{color}) = ($self->{color},$self->{color}); 319 } 320 321 $self->{type} = EDGE_CROSS + ($flags || 0); 322 323 $self; 324 } 325 326sub _make_joint 327 { 328 # Upgrade us to a joint 329 my ($self, $edge, $new_type) = @_; 330 331 my $type = $self->{type} & EDGE_TYPE_MASK; 332 333 $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 $self->{color} = $self->get_color_attribute('color'); 337 $self->{style_ver} = $edge->style(); 338 $self->{color_ver} = $edge->get_color_attribute('color'); 339 340 # if we are the VER piece, switch styles around 341 if ($type == EDGE_VER) 342 { 343 ($self->{style_ver}, $self->{style}) = ($self->{style},$self->{style_ver}); 344 ($self->{color_ver}, $self->{color}) = ($self->{color},$self->{color}); 345 } 346 347 print STDERR "# creating joint at $self->{x}, $self->{y} with new type $new_type (old $type)\n" 348 if $self->{graph}->{debug}; 349 350 $self->{type} = $new_type; 351 352 $self; 353 } 354 355############################################################################# 356# conversion to HTML 357 358my $edge_end_north = 359 ' <td colspan=2 class="##class## eb" style="##bg####ec##"> </td>' . "\n" . 360 ' <td colspan=2 class="##class## eb" style="##bg####ec##"><span class="su">^</span></td>' . "\n"; 361my $edge_end_south = 362 ' <td colspan=2 class="##class## eb" style="##bg####ec##"> </td>' . "\n" . 363 ' <td colspan=2 class="##class## eb" style="##bg####ec##"><span class="sv">v</span></td>' . "\n"; 364 365my $edge_empty_row = 366 ' <td colspan=4 class="##class## eb"></td>'; 367 368my $edge_arrow_west_upper = 369 '<td rowspan=2 class="##class## eb" style="##ec####bg##"><span class="shl"><</span></td>' . "\n"; 370my $edge_arrow_west_lower = 371 '<td rowspan=2 class="##class## eb"> </td>' . "\n"; 372 373my $edge_arrow_east_upper = 374 '<td rowspan=2 class="##class## eb" style="##ec####bg##"><span class="sh">></span></td>' . "\n"; 375my $edge_arrow_east_lower = 376 '<td rowspan=2 class="##class## eb"></td>' . "\n"; 377 378my $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 ' <td colspan=2 rowspan=2 class="##class## eb"></td>' . "\n" . 385 ' <td colspan=2 rowspan=2 class="##class## eb" style="border-bottom: ##border##;"> </td>', 386 '', 387 ' <td colspan=2 rowspan=2 class="##class## eb"></td>'. "\n" . 388 ' <td colspan=2 rowspan=2 class="##class## eb" style="border-left: ##border##;"> </td>', 389 '', 390 ], 391 392 EDGE_S_E() + EDGE_START_E() + EDGE_END_S() => [ 393 ' <td colspan=2 rowspan=2 class="##class## eb"></td>' . "\n" . 394 ' <td rowspan=2 class="##class## eb" style="border-bottom: ##border##;"> </td>' . "\n" . 395 ' <td rowspan=4 class="##class## el"></td>', 396 '', 397 ' <td colspan=2 class="##class## eb"></td>'. "\n" . 398 ' <td class="##class## eb" style="border-left: ##border##;"> </td>', 399 $edge_end_south, 400 ], 401 402 EDGE_S_E() + EDGE_START_E() => [ 403 ' <td colspan=2 rowspan=2 class="##class## eb"></td>' . "\n" . 404 ' <td rowspan=2 class="##class## eb" style="border-bottom: ##border##;"> </td>' . "\n" . 405 ' <td rowspan=4 class="##class## el"></td>', 406 '', 407 ' <td colspan=2 rowspan=2 class="##class## eb"></td>'. "\n" . 408 ' <td colspan=2 rowspan=2 class="##class## eb" style="border-left: ##border##;"> </td>', 409 '', 410 ], 411 412 EDGE_S_E() + EDGE_END_E() => [ 413 ' <td colspan=2 rowspan=2 class="##class## eb"></td>' . "\n" . 414 ' <td rowspan=2 class="##class## eb" style="border-bottom: ##border##;"> </td>' . "\n" . 415 ' <td rowspan=4 class="##class##"##edgecolor##><span class="sa">></span></td>', 416 '', 417 ' <td colspan=2 rowspan=2 class="##class## eb"></td>'. "\n" . 418 ' <td rowspan=2 class="##class## eb" style="border-left: ##border##;"> </td>', 419 '', 420 ], 421 422 EDGE_S_E() + EDGE_START_S() => [ 423 ' <td colspan=2 rowspan=2 class="##class## eb"></td>' . "\n" . 424 ' <td colspan=2 rowspan=2 class="##class## eb" style="border-bottom: ##border##;"> </td>', 425 '', 426 ' <td colspan=2 class="##class## eb"></td>'. "\n" . 427 ' <td colspan=2 class="##class## eb" style="border-left: ##border##;"> </td>' . "\n", 428 $edge_empty_row, 429 ], 430 431 EDGE_S_E() + EDGE_START_S() + EDGE_END_E() => [ 432 ' <td colspan=2 rowspan=2 class="##class## eb"></td>' . "\n" . 433 ' <td rowspan=2 class="##class## eb" style="border-bottom: ##border##;"> </td>'. 434 ' <td rowspan=4 class="##class##"##edgecolor##><span class="sa">></span></td>', 435 '', 436 ' <td colspan=2 rowspan=2 class="##class## eb"></td>'. "\n" . 437 ' <td class="##class## eb" style="border-left: ##border##;"> </td>' . "\n", 438 ' <td class="##class## eb"></td>', 439 ], 440 441 EDGE_S_E() + EDGE_END_S() => [ 442 ' <td colspan=2 rowspan=2 class="##class## eb"></td>' . "\n" . 443 ' <td colspan=2 rowspan=2 class="##class## eb" style="border-bottom: ##border##;"> </td>', 444 '', 445 ' <td colspan=2 class="##class## eb"></td>'. "\n" . 446 ' <td colspan=2 class="##class## eb" style="border-left: ##border##;"> </td>' . "\n", 447 $edge_end_south, 448 ], 449 450 EDGE_S_E() + EDGE_END_S() + EDGE_END_E() => [ 451 ' <td colspan=2 rowspan=2 class="##class## eb"></td>' . "\n" . 452 ' <td rowspan=2 class="##class## eb" style="border-bottom: ##border##;"> </td>' . "\n" . 453 ' <td rowspan=4 class="##class## ha"##edgecolor##><span class="sa">></span></td>', 454 '', 455 ' <td colspan=2 class="##class## eb"></td>'. "\n" . 456 ' <td class="##class## eb" style="border-left: ##border##;"> </td>' . "\n", 457 ' <td colspan=3 class="##class## v"##edgecolor##>v</td>', 458 ], 459 460 ########################################################################### 461 ########################################################################### 462 # S_W 463 464 EDGE_S_W() => [ 465 ' <td colspan=2 rowspan=2 class="##class## eb" style="border-bottom: ##border##;"> </td>' . "\n" . 466 ' <td colspan=2 rowspan=2 class="##class## eb"></td>', 467 '', 468 ' <td colspan=2 rowspan=2 class="##class## eb"></td>'. "\n" . 469 ' <td colspan=2 rowspan=2 class="##class## eb" style="border-left: ##border##;"> </td>', 470 '', 471 ], 472 473 EDGE_S_W() + EDGE_START_W() => [ 474 ' <td rowspan=2 class="##class## el"></td>' . "\n" . 475 ' <td rowspan=2 class="##class## eb" style="border-bottom: ##border##;"> </td>' . "\n" . 476 ' <td colspan=2 rowspan=2 class="##class## eb"></td>', 477 '', 478 ' <td colspan=2 rowspan=2 class="##class## eb"></td>'. "\n" . 479 ' <td colspan=2 rowspan=2 class="##class## eb" style="border-left: ##border##;"> </td>', 480 '', 481 ], 482 483 EDGE_S_W() + EDGE_END_W() => [ 484 ' <td rowspan=2 class="##class## va"##edgecolor##><span class="shl"><</span></td>' . "\n" . 485 ' <td rowspan=2 class="##class## eb" style="border-bottom: ##border##;"> </td>' . "\n" . 486 ' <td colspan=2 rowspan=2 class="##class## eb"></td>', 487 '', 488 ' <td colspan=2 rowspan=2 class="##class## eb"></td>'. "\n" . 489 ' <td colspan=2 rowspan=2 class="##class## eb" style="border-left: ##border##;"> </td>', 490 '', 491 ], 492 493 EDGE_S_W() + EDGE_START_S() => [ 494 ' <td colspan=2 rowspan=2 class="##class## eb" style="border-bottom: ##border##;"> </td>' . "\n" . 495 ' <td colspan=2 rowspan=2 class="##class## eb"></td>', 496 '', 497 ' <td colspan=2 class="##class## eb"></td>'. "\n" . 498 ' <td colspan=2 class="##class## eb" style="border-left: ##border##;"> </td>', 499 $edge_empty_row, 500 ], 501 502 EDGE_S_W() + EDGE_END_S() => [ 503 ' <td colspan=2 rowspan=2 class="##class## eb" style="border-bottom: ##border##;"> </td>' . "\n" . 504 ' <td colspan=2 rowspan=2 class="##class## eb"></td>', 505 '', 506 ' <td colspan=2 class="##class## eb"></td>'. "\n" . 507 ' <td colspan=2 class="##class## eb" style="border-left: ##border##;"> </td>', 508 $edge_end_south, 509 ], 510 511 EDGE_S_W() + EDGE_START_W() + EDGE_END_S() => [ 512 ' <td rowspan=2 class="##class## el"></td>' . "\n" . 513 ' <td rowspan=2 class="##class## eb" style="border-bottom: ##border##;"> </td>' . "\n" . 514 ' <td colspan=2 rowspan=2 class="##class## eb"></td>', 515 '', 516 ' <td colspan=2 class="##class## eb"></td>'. "\n" . 517 ' <td colspan=2 class="##class## eb" style="border-left: ##border##;"> </td>', 518 $edge_end_south, 519 ], 520 521 EDGE_S_W() + EDGE_START_S() + EDGE_END_W() => [ 522 ' <td rowspan=3 class="##class## sh"##edgecolor##><</td>' . "\n" . 523 ' <td rowspan=2 class="##class## eb" style="border-bottom: ##border##;"> </td>' . "\n" . 524 ' <td colspan=2 rowspan=2 class="##class## eb"></td>', 525 '', 526 ' <td class="##class## eb"></td>'. "\n" . 527 ' <td colspan=2 class="##class## eb" style="border-left: ##border##;"> </td>', 528 $edge_empty_row, 529 ], 530 531 ########################################################################### 532 ########################################################################### 533 # N_W 534 535 EDGE_N_W() => [ 536 ' <td colspan=2 rowspan=2 class="##class## eb" style="border-bottom: ##border##;"> </td>' . "\n" . 537 ' <td colspan=2 rowspan=2 class="##class## eb" style="border-left: ##border##;"> </td>', 538 '', 539 ' <td colspan=4 rowspan=2 class="##class## eb"></td>', 540 '', 541 ], 542 543 EDGE_N_W() + EDGE_START_N() => [ 544 $edge_empty_row, 545 ' <td colspan=2 class="##class## eb" style="border-bottom: ##border##;"> </td>' . "\n" . 546 ' <td colspan=2 class="##class## eb" style="border-left: ##border##;"> </td>', 547 '', 548 ' <td colspan=4 rowspan=2 class="##class## eb"></td>', 549 ], 550 551 EDGE_N_W() + EDGE_END_N() => [ 552 $edge_end_north, 553 ' <td colspan=2 class="##class## eb" style="border-bottom: ##border##;"> </td>' . "\n" . 554 ' <td colspan=2 class="##class## eb" style="border-left: ##border##;"> </td>', 555 ' <td colspan=4 rowspan=2 class="##class## eb"></td>', 556 '', 557 ], 558 559 EDGE_N_W() + EDGE_END_N() + EDGE_START_W() => [ 560 $edge_end_north, 561 ' <td rowspan=3 class="##class## eb"></td>'. 562 ' <td class="##class## eb" style="border-bottom: ##border##;"> </td>' . "\n" . 563 ' <td colspan=2 class="##class## eb" style="border-left: ##border##;"> </td>', 564 ' <td colspan=4 rowspan=2 class="##class## eb"></td>', 565 '', 566 ], 567 568 EDGE_N_W() + EDGE_START_W() => [ 569 ' <td rowspan=2 class="##class## el"></td>' . "\n" . 570 ' <td rowspan=2 class="##class## eb" style="border-bottom: ##border##;"> </td>' . "\n" . 571 ' <td colspan=2 rowspan=2 class="##class## eb" style="border-left: ##border##;"> </td>' . "\n", 572 '', 573 ' <td colspan=4 rowspan=2 class="##class## eb"></td>', 574 '', 575 ], 576 577 EDGE_N_W() + EDGE_END_W() => [ 578 ' <td rowspan=4 class="##class## sh"##edgecolor##><</td>' . "\n" . 579 ' <td rowspan=2 class="##class## eb" style="border-bottom: ##border##;"> </td>' . "\n" . 580 ' <td colspan=2 rowspan=2 class="##class## eb" style="border-left: ##border##;"> </td>' . "\n", 581 '', 582 ' <td colspan=3 rowspan=2 class="##class## eb"></td>', 583 '', 584 ], 585 586 ########################################################################### 587 ########################################################################### 588 # N_E 589 590 EDGE_N_E() => [ 591 ' <td colspan=2 rowspan=2 class="##class## eb"></td>' . "\n" . 592 ' <td colspan=2 rowspan=2 class="##class## eb" style="border-bottom: ##border##; border-left: ##border##;"> </td>', 593 '', 594 ' <td colspan=4 rowspan=2 class="##class## eb"></td>', 595 '', 596 ], 597 598 EDGE_N_E() + EDGE_START_E() => [ 599 ' <td colspan=2 rowspan=2 class="##class## eb"></td>' . "\n" . 600 ' <td rowspan=2 class="##class## eb" style="border-bottom: ##border##; border-left: ##border##;"> </td>' . "\n" . 601 ' <td rowspan=4 class="##class## el"></td>', 602 '', 603 ' <td colspan=3 rowspan=2 class="##class## eb"></td>', 604 '', 605 ], 606 607 EDGE_N_E() + EDGE_END_E() => [ 608 ' <td colspan=2 rowspan=2 class="##class## eb"></td>' . "\n" . 609 ' <td rowspan=2 class="##class## eb" style="border-bottom: ##border##; border-left: ##border##;"> </td>' . "\n" . 610 ' <td rowspan=4 class="##class## va"##edgecolor##><span class="sa">></span></td>', 611 '', 612 ' <td colspan=3 rowspan=2 class="##class## eb"></td>', 613 '', 614 ], 615 616 EDGE_N_E() + EDGE_END_E() + EDGE_START_N() => [ 617 $edge_empty_row, 618 ' <td colspan=2 class="##class## eb"></td>' . "\n" . 619 ' <td class="##class## eb" style="border-bottom: ##border##; border-left: ##border##;"> </td>' . "\n" . 620 ' <td rowspan=3 class="##class## va"##edgecolor##><span class="sa">></span></td>', 621 ' <td colspan=3 rowspan=2 class="##class## eb"></td>', 622 '', 623 ], 624 625 EDGE_N_E() + EDGE_START_E() + EDGE_END_N() => [ 626 $edge_end_north, 627 ' <td colspan=2 class="##class## eb"></td>' . "\n" . 628 ' <td class="##class## eb" style="border-bottom: ##border##; border-left: ##border##;"> </td>' . "\n" . 629 ' <td rowspan=3 class="##class## eb"> </td>', 630 ' <td colspan=3 rowspan=2 class="##class## eb"></td>', 631 '', 632 ], 633 634 EDGE_N_E() + EDGE_START_N() => [ 635 $edge_empty_row, 636 ' <td colspan=2 rowspan=3 class="##class## eb"></td>' . "\n" . 637 ' <td colspan=2 class="##class## eb" style="border-bottom: ##border##; border-left: ##border##;"> </td>', 638 ' <td colspan=2 class="##class## eb"></td>', 639 '', 640 ], 641 642 EDGE_N_E() + EDGE_END_N() => [ 643 $edge_end_north, 644 ' <td colspan=2 rowspan=3 class="##class## eb"></td>' . "\n" . 645 ' <td colspan=2 class="##class## eb" style="border-bottom: ##border##; border-left: ##border##;"> </td>', 646 '', 647 ' <td colspan=2 class="##class## eb"></td>', 648 ], 649 650 ########################################################################### 651 ########################################################################### 652 # self loops 653 654 EDGE_LOOP_NORTH() - EDGE_LABEL_CELL() => [ 655 '<td rowspan=2 class="##class## eb"> </td>' . "\n". 656 ' <td colspan=2 rowspan=2 class="##class## lh" style="border-bottom: ##border##;##lc####bg##">##label##</td>' . "\n" . 657 ' <td rowspan=2 class="##class## eb"> </td>', 658 '', 659 '<td class="##class## eb"> </td>' . "\n". 660 ' <td colspan=2 class="##class## eb" style="border-left: ##border##;##bg##"> </td>'."\n". 661 ' <td class="##class## eb" style="border-left: ##border##;##bg##"> </td>', 662 663 '<td colspan=2 class="##class## v" style="##bg##"##edgecolor##>v</td>' . "\n" . 664 ' <td colspan=2 class="##class## eb"> </td>', 665 666 ], 667 668 EDGE_LOOP_SOUTH() - EDGE_LABEL_CELL() => [ 669 '<td colspan=2 class="##class## v" style="##bg##"##edgecolor##>^</td>' . "\n" . 670 ' <td colspan=2 class="##class## eb"> </td>', 671 672 '<td rowspan=2 class="##class## eb"> </td>' . "\n". 673 ' <td colspan=2 rowspan=2 class="##class## lh" style="border-left:##border##;border-bottom:##border##;##lc####bg##">##label##</td>'."\n". 674 ' <td rowspan=2 class="##class## eb" style="border-left:##border##;##bg##"> </td>', 675 676 '', 677 678 '<td colspan=4 class="##class## eb"> </td>', 679 680 ], 681 682 EDGE_LOOP_WEST() - EDGE_LABEL_CELL() => [ 683 $edge_empty_row. 684 ' <td colspan=2 rowspan=2 class="##class## lh" style="border-bottom: ##border##;##lc####bg##">##label##</td>'."\n". 685 ' <td rowspan=2 class="##class## eb"> </td>', 686 687 '', 688 689 '<td colspan=2 class="##class## eb" style="border-left: ##border##; border-bottom: ##border##;##bg##"> </td>' . "\n". 690 ' <td rowspan=2 class="##class## va" style="##bg##"##edgecolor##><span class="sa">></span></td>', 691 692 '<td colspan=2 class="##class## eb"> </td>', 693 ], 694 695 EDGE_LOOP_EAST() - EDGE_LABEL_CELL() => [ 696 697 '<td rowspan=2 class="##class## eb"> </td>' . "\n" . 698 ' <td colspan=2 rowspan=2 class="##class## lh" style="border-bottom: ##border##;##lc####bg##">##label##</td>' ."\n". 699 ' <td rowspan=2 class="##class## eb"> </td>', 700 701 '', 702 703 '<td rowspan=2 class="##class## va" style="##bg##"##edgecolor##><span class="sh"><</span></td>' ."\n". 704 ' <td colspan=2 class="##class## eb" style="border-bottom: ##border##;##bg##"> </td>'."\n". 705 ' <td class="##class## eb" style="border-left: ##border##;##bg##"> </td>', 706 707 '<td colspan=3 class="##class## eb"> </td>', 708 ], 709 710 ########################################################################### 711 ########################################################################### 712 # joints 713 714 ########################################################################### 715 # E_N_S 716 717 EDGE_E_N_S() => [ 718 '<td colspan=2 rowspan=2 class="##class## eb"> </td>' . "\n" . 719 ' <td colspan=2 rowspan=2 class="##class## eb" style="border-left:##borderv##;border-bottom:##border##;##bg##"> </td>', 720 '', 721 '<td colspan=2 rowspan=2 class="##class## eb"> </td>' ."\n". 722 ' <td colspan=2 rowspan=2 class="##class## eb" style="border-left: ##borderv##;##bg##"> </td>', 723 '', 724 ], 725 726 EDGE_E_N_S() + EDGE_END_E() => [ 727 '<td colspan=2 rowspan=2 class="##class## eb"> </td>' . "\n" . 728 ' <td rowspan=2 class="##class## eb" style="border-left: ##borderv##; border-bottom: ##border##;##bg##"> </td>' . "\n" . 729 ' <td rowspan=4 class="##class## va"##edgecolor##><span class="sa">></span></td>', 730 '', 731 '<td colspan=2 rowspan=2 class="##class## eb"> </td>' ."\n". 732 ' <td rowspan=2 class="##class## eb" style="border-left: ##borderv##;##bg##"> </td>', 733 '', 734 ], 735 736 ########################################################################### 737 # W_N_S 738 739 EDGE_W_N_S() => [ 740 '<td colspan=2 rowspan=2 class="##class## eb" style="border-bottom: ##border##;##bg##"> </td>' . "\n" . 741 ' <td colspan=2 rowspan=4 class="##class## eb" style="border-left: ##borderv##;##bg##"> </td>', 742 '', 743 '<td colspan=2 rowspan=2 class="##class## eb"> </td>', 744 '', 745 ], 746 747 ########################################################################### 748 # S_E_W 749 750 EDGE_S_E_W() => [ 751 '<td colspan=4 rowspan=2 class="##class## eb" style="border-bottom: ##border##;##bg##"> </td>', 752 '', 753 '<td colspan=2 rowspan=2 class="##class## eb"> </td>' ."\n". 754 ' <td colspan=2 rowspan=2 class="##class## eb" style="border-left: ##borderv##;##bg##"> </td>', 755 '', 756 ], 757 758 EDGE_S_E_W() + EDGE_END_S() => [ 759 '<td colspan=4 rowspan=2 class="##class## eb" style="border-bottom: ##border##;##bg##"> </td>', 760 '', 761 '<td colspan=2 class="##class## eb"> </td>' ."\n". 762 ' <td colspan=2 class="##class## eb" style="border-left: ##borderv##;##bg##"> </td>', 763 $edge_end_south, 764 ], 765 766 EDGE_S_E_W() + EDGE_START_S() => [ 767 '<td colspan=4 rowspan=2 class="##class## eb" style="border-bottom: ##border##;##bg##"> </td>', 768 '', 769 '<td colspan=2 class="##class## eb"> </td>' ."\n". 770 ' <td colspan=2 class="##class## eb" style="border-left: ##borderv##;##bg##"> </td>', 771 ' <td colspan=4 class="##class## eb"></td>', 772 ], 773 774 EDGE_S_E_W() + EDGE_START_W() => [ 775 '<td rowspan=4 class="##class## el"></td>' . "\n" . 776 '<td colspan=3 rowspan=2 class="##class## eb" style="border-bottom: ##border##;##bg##"> </td>', 777 '', 778 '<td rowspan=2 class="##class## eb"> </td>' ."\n". 779 ' <td rowspan=2 class="##class## eb" style="border-left: ##borderv##;##bg##"> </td>', 780 '', 781 782 ], 783 784 EDGE_S_E_W() + EDGE_END_E() => [ 785 '<td colspan=3 rowspan=2 class="##class## eb" style="border-bottom: ##border##;##bg##"> </td>' . "\n" . 786 ' <td rowspan=4 class="##class## va"##edgecolor##><span class="sa">></span></td>', 787 '', 788 '<td colspan=2 rowspan=2 class="##class## eb"> </td>' ."\n". 789 ' <td rowspan=2 class="##class## eb" style="border-left: ##borderv##;##bg##"> </td>', 790 '', 791 ], 792 793 EDGE_S_E_W() + EDGE_END_W() => [ 794 $edge_arrow_west_upper . 795 '<td colspan=3 rowspan=2 class="##class## eb" style="border-bottom: ##border##;##bg##"> </td>' . "\n" , 796 '', 797 '<td colspan=2 rowspan=2 class="##class## eb"> </td>' ."\n" . 798 '<td colspan=2 rowspan=2 class="##class## eb" style="border-left: ##borderv##;##bg##"> </td>', 799 ], 800 801 ########################################################################### 802 # N_E_W 803 804 EDGE_N_E_W() => [ 805 ' <td colspan=2 rowspan=2 class="##class## eb" style="border-bottom: ##borderv##;##bg##"> </td>' ."\n". 806 '<td colspan=2 rowspan=2 class="##class## eb" style="border-left: ##borderv##; border-bottom: ##border##;##bg##"> </td>', 807 '', 808 '<td colspan=4 rowspan=2 class="##class## eb"> </td>', 809 '', 810 ], 811 812 EDGE_N_E_W() + EDGE_END_N() => [ 813 $edge_end_north, 814 ' <td colspan=2 class="##class## eb" style="border-bottom: ##borderv##;##bg##"> </td>' ."\n". 815 '<td colspan=2 class="##class## eb" style="border-left: ##borderv##; border-bottom: ##border##;##bg##"> </td>', 816 '', 817 '<td colspan=4 rowspan=2 class="##class## eb"> </td>', 818 '', 819 ], 820 821 EDGE_N_E_W() + EDGE_START_N() => [ 822 $edge_empty_row, 823 ' <td colspan=2 class="##class## eb" style="border-bottom: ##borderv##;##bg##"> </td>' ."\n". 824 '<td colspan=2 class="##class## eb" style="border-left: ##borderv##; border-bottom: ##border##;##bg##"> </td>', 825 '', 826 '<td colspan=4 rowspan=2 class="##class## eb"> </td>', 827 '', 828 ], 829 830 }; 831 832sub _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 my ($self, $as) = @_; 837 838 my $s_flags = $self->{type} & EDGE_START_MASK; 839 my $e_flags = $self->{type} & EDGE_END_MASK; 840 841 $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 my $rc = [ 847 ' <td colspan=##mod## rowspan=2 class="##class## lh" style="border-bottom: ##border##;##lc####bg##">##label##</td>', 848 '', 849 '<td colspan=##mod## rowspan=2 class="##class## eb"> </td>', 850 '', 851 ]; 852 853 # This assumes that only 2 end/start flags are set at the same time: 854 855 my $mod = 4; # modifier 856 if ($s_flags & EDGE_START_W) 857 { 858 $mod--; 859 $rc->[0] = '<td rowspan=4 class="##class## el"></td>' . "\n" . $rc->[0]; 860 }; 861 if ($s_flags & EDGE_START_E) 862 { 863 $mod--; 864 $rc->[0] .= "\n " . '<td rowspan=4 class="##class## el"></td>'; 865 }; 866 if ($e_flags & EDGE_END_W) 867 { 868 $mod--; 869 $rc->[0] = $edge_arrow_west_upper . $rc->[0]; 870 $rc->[2] = $edge_arrow_west_lower . $rc->[2]; 871 } 872 if ($e_flags & EDGE_END_E) 873 { 874 $mod--; 875 $rc->[0] .= "\n " . $edge_arrow_east_upper; 876 $rc->[2] .= "\n " . $edge_arrow_east_lower; 877 }; 878 879 # cx == 1: mod = 2..4, cx == 2: mod = 6..8, etc. 880 $self->{cx} ||= 1; 881 $mod = $self->{cx} * 4 - 4 + $mod; 882 883 for my $e (@$rc) 884 { 885 $e =~ s/##mod##/$mod/g; 886 } 887 888 $rc; 889 } 890 891sub _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 my ($self, $as) = @_; 896 897 my $s_flags = $self->{type} & EDGE_START_MASK; 898 my $e_flags = $self->{type} & EDGE_END_MASK; 899 900 $e_flags = 0 if $as eq 'none'; 901 902 my $mod = 4; # modifier 903 904 # normal vertical edge with no start/end flags 905 my $rc = [ 906 '<td colspan=2 rowspan=##mod## class="##class## el"> </td>' . "\n " . 907 '<td colspan=2 rowspan=##mod## class="##class## lv" style="border-left: ##border##;##lc####bg##">##label##</td>' . "\n", 908 '', 909 '', 910 '', 911 ]; 912 913 # flag north 914 if ($s_flags & EDGE_START_N) 915 { 916 $mod--; 917 unshift @$rc, '<td colspan=4 class="##class## eb"></td>' . "\n"; 918 delete $rc->[-1]; 919 } 920 elsif ($e_flags & EDGE_END_N) 921 { 922 $mod--; 923 unshift @$rc, $edge_end_north; 924 delete $rc->[-1]; 925 } 926 927 # flag south 928 if ($s_flags & EDGE_START_S) 929 { 930 $mod--; 931 $rc->[3] = '<td colspan=4 class="##class## eb"></td>' . "\n" 932 } 933 934 if ($e_flags & EDGE_END_S) 935 { 936 $mod--; 937 $rc->[3] = $edge_end_south; 938 } 939 940 $self->{cy} ||= 1; 941 $mod = $self->{cy} * 4 - 4 + $mod; 942 943 for my $e (@$rc) 944 { 945 $e =~ s/##mod##/$mod/g; 946 } 947 948 $rc; 949 } 950 951sub _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 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 my $rc = [ 961 ' <td colspan=2 rowspan=2 class="##class## eb el" style="border-bottom: ##border##"> </td>' . "\n" . 962 ' <td colspan=2 rowspan=2 class="##class## eb el" style="border-left: ##borderv##; border-bottom: ##border##"> </td>' . "\n", 963 '', 964 ' <td colspan=2 rowspan=2 class="##class## eb el"></td>' . "\n" . 965 ' <td colspan=2 rowspan=2 class="##class## eb el" style="border-left: ##borderv##"> </td>' . "\n", 966 '', 967 ]; 968 969 $rc; 970 } 971 972sub as_html 973 { 974 my ($self) = shift; 975 976 my $type = $self->{type} & EDGE_NO_M_MASK; 977 my $style = $self->{style}; 978 979 # none, open, filled, closed 980 my $as; $as = 'none' if $self->{edge}->{undirected}; 981 $as = $self->attribute('arrowstyle') unless $as; 982 983 # triangle, box, dot, inv, diamond, line etc. 984 my $ashape; $ashape = 'triangle' if $self->{edge}->{undirected}; 985 $ashape = $self->attribute('arrowshape') unless $ashape; 986 987 my $code = $edge_html->{$type}; 988 989 if (!defined $code) 990 { 991 my $t = $self->{type} & EDGE_TYPE_MASK; 992 993 if ($style ne 'invisible') 994 { 995 $code = $self->_html_edge_hor($as) if $t == EDGE_HOR; 996 $code = $self->_html_edge_ver($as) if $t == EDGE_VER; 997 $code = $self->_html_edge_cross($as) if $t == EDGE_CROSS; 998 } 999 else 1000 { 1001 $code = [ ' <td colspan=4 rowspan=4 class="##class##"> </td>' ]; 1002 } 1003 1004 if (!defined $code) 1005 { 1006 $code = [ ' <td colspan=4 rowspan=4 class="##class##">???</td>' ]; 1007 warn ("as_html: Unimplemented edge type $self->{type} ($type) at $self->{x},$self->{y} " 1008 . edge_type($self->{type})); 1009 } 1010 } 1011 1012 my $id = $self->{graph}->{id}; 1013 1014 my $color = $self->get_color_attribute('color'); 1015 my $label = ''; 1016 my $label_style = ''; 1017 1018 # only include the label if we are the label cell 1019 if ($style ne 'invisible' && ($self->{type} & EDGE_LABEL_CELL)) 1020 { 1021 my $switch_to_center; 1022 ($label,$switch_to_center) = $self->_label_as_html(); 1023 1024 # replace linebreaks by <br>, but remove extra spaces 1025 $label =~ s/\s*\\n\s*/<br \/>/g; 1026 1027 my $label_color = $self->raw_color_attribute('labelcolor') || $color; 1028 $label_color = '' if $label_color eq '#000000'; 1029 $label_style = "color: $label_color;" if $label_color; 1030 1031 my $font = $self->attribute('font') || ''; 1032 $font = '' if $font eq ($self->default_attribute('font') || ''); 1033 $label_style = "font-family: $font;" if $font; 1034 1035 $label_style .= $self->text_styles_as_css(1,1) unless $label eq ''; 1036 1037 $label_style =~ s/^\s*//; 1038 1039 my $link = $self->link(); 1040 if ($link ne '') 1041 { 1042 # encode critical entities 1043 $link =~ s/\s/\+/g; # space 1044 $link =~ s/'/%27/g; # single-quote 1045 1046 # put the style on the link 1047 $label_style = " style='$label_style'" if $label_style; 1048 $label = "<a href='$link'$label_style>$label</a>"; 1049 $label_style = ''; 1050 } 1051 1052 } 1053 # without , IE doesn't draw the cell-border nec. for edges 1054 $label = ' ' unless $label ne ''; 1055 1056 ########################################################################### 1057 # get the border styles/colors: 1058 1059 # width for the edge is "2px" 1060 my $bow = '2'; 1061 my $border = Graph::Easy::_border_attribute_as_html( $self->{style}, $bow, $color); 1062 my $border_v = $border; 1063 1064 if (($self->{type} & EDGE_TYPE_MASK) == EDGE_CROSS) 1065 { 1066 $border_v = Graph::Easy::_border_attribute_as_html( $self->{style_ver}, $bow, $self->{color_ver}); 1067 } 1068 1069 ########################################################################### 1070 my $edge_color = ''; $edge_color = " color: $color;" if $color; 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 my $bg = $self->attribute('background') || ''; 1078 my $group = $self->{edge}->{group}; 1079 $bg = '' if $bg eq 'inherit'; 1080 $bg = $group->{att}->{fill} if $group->{att}->{fill} && $bg eq ''; 1081 $bg = '' if $bg eq 'inherit'; 1082 $bg = " background: $bg;" if $bg; 1083 1084 my $title = $self->title(); 1085 $title =~ s/"//g; # replace quotation marks 1086 $title = " title=\"$title\"" if $title ne ''; # add mouse-over title 1087 1088 ########################################################################### 1089 # replace templates 1090 1091 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 $border =~ s/\s+/ /g; # collapse multiple spaces 1095 $border_v =~ s/\s+/ /g; 1096 my $cl = $self->class(); $cl =~ s/\./_/g; # group.cities => group_cities 1097 1098 my $rc; 1099 for my $a (@$code) 1100 { 1101 if (ref($a)) 1102 { 1103 for my $c (@$a) 1104 { 1105 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 push @$rc, $self->_format_td($a, 1112 $border, $border_v, $label_style, $edge_color, $bg, $as, $ashape, $title, $label, $cl); 1113 } 1114 } 1115 1116 $rc; 1117 } 1118 1119sub _format_td 1120 { 1121 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 $c =~ s/( e[bl]")(>( )?<\/td>)/$1 style="##bg##"$2/g; 1126 # insert missing "##bg##" 1127 $c =~ s/style="border/style="##bg##border/g; 1128 1129 $c =~ s/##class##/$cl/g; 1130 $c =~ s/##border##/$border/g; 1131 $c =~ s/##borderv##/$border_v/g; 1132 $c =~ s/##lc##/$label_style/g; 1133 $c =~ s/##edgecolor##/ style="$edge_color"/g; 1134 $c =~ s/##ec##/$edge_color/g; 1135 $c =~ s/##bg##/$bg/g; 1136 $c =~ s/ style=""//g; # remove empty styles 1137 1138 # remove arrows if edge is undirected 1139 $c =~ s/>(v|\^|<|>)/>/g if $as eq 'none'; 1140 1141 # insert "nice" looking Unicode arrows 1142 $c =~ s/>(v|\^|<|>)/'>' . $self->_unicode_arrow($ashape, $as, $1); /eg; 1143 1144 # insert the label last, other "v" as label might get replaced above 1145 $c =~ s/>##label##/$title>$label/; 1146 # for empty labels use a different class 1147 $c =~ s/ lh"/ eb"/ if $label eq ''; 1148 1149 $c .= "\n" unless $c =~ /\n\z/; 1150 1151 $self->quoted_comment() . $c; 1152 } 1153 1154sub class 1155 { 1156 my $self = shift; 1157 1158 my $c = $self->{class} . ($self->{cell_class} || ''); 1159 $c = $self->{edge}->{group}->class() . ' ' . $c if ref($self->{edge}->{group}); 1160 1161 $c; 1162 } 1163 1164sub group 1165 { 1166 # return the group we belong to as the group of our parent-edge 1167 my $self = shift; 1168 1169 $self->{edge}->{group}; 1170 } 1171 1172############################################################################# 1173# accessor methods 1174 1175sub type 1176 { 1177 # get/set type of this path element 1178 # type - EDGE_START, EDGE_END, EDGE_HOR, EDGE_VER, etc 1179 my ($self,$type) = @_; 1180 1181 if (defined $type) 1182 { 1183 if (defined $type && $type < 0 || $type > EDGE_MAX_TYPE) 1184 { 1185 require Carp; 1186 Carp::confess ("Cell type $type for cell $self->{x},$self->{y} is not valid."); 1187 } 1188 $self->{type} = $type; 1189 } 1190 1191 $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 1200sub _correct_size 1201 { 1202 my ($self,$format) = @_; 1203 1204 return if defined $self->{w}; 1205 1206 # min-size is this 1207 $self->{w} = 5; $self->{h} = 3; 1208 # make short cell pieces very small 1209 if (($self->{type} & EDGE_SHORT_CELL) != 0) 1210 { 1211 $self->{w} = 1; $self->{h} = 1; 1212 return; 1213 } 1214 1215 my $arrows = ($self->{type} & EDGE_ARROW_MASK); 1216 my $type = ($self->{type} & EDGE_TYPE_MASK); 1217 1218 if ($self->{edge}->{bidirectional} && $arrows != 0) 1219 { 1220 $self->{w}++ if $type == EDGE_HOR; 1221 $self->{h}++ if $type == EDGE_VER; 1222 } 1223 1224 # make joints bigger if they got arrows 1225 my $ah = $self->{type} & EDGE_ARROW_HOR; 1226 my $av = $self->{type} & EDGE_ARROW_VER; 1227 $self->{w}++ if $ah && ($type == EDGE_S_E_W || $type == EDGE_N_E_W); 1228 $self->{h}++ if $av && ($type == EDGE_E_N_S || $type == EDGE_W_N_S); 1229 1230 my $style = $self->{edge}->attribute('style') || 'solid'; 1231 1232 # make the edge to display ' ..-> ' instead of ' ..> ': 1233 $self->{w}++ if $style eq 'dot-dot-dash'; 1234 1235 if ($type >= EDGE_LOOP_TYPE) 1236 { 1237 # +---+ 1238 # | V 1239 1240 # + 1241 # +--> | 1242 # | | 1243 # +--- | 1244 # + 1245 $self->{w} = 7; 1246 $self->{w} = 8 if $type == EDGE_N_W_S || $type == EDGE_S_W_N; 1247 $self->{h} = 3; 1248 $self->{h} = 5 if $type != EDGE_N_W_S && $type != EDGE_S_W_N; 1249 } 1250 1251 if ($self->{type} == EDGE_HOR) 1252 { 1253 $self->{w} = 0; 1254 } 1255 elsif ($self->{type} == EDGE_VER) 1256 { 1257 $self->{h} = 0; 1258 } 1259 elsif ($self->{type} & EDGE_LABEL_CELL) 1260 { 1261 # edges do not have borders 1262 my ($w,$h) = $self->dimensions(); $h-- unless $h == 0; 1263 1264 $h += $self->{h}; 1265 $w += $self->{w}; 1266 $self->{w} = $w; 1267 $self->{h} = $h; 1268 } 1269 } 1270 1271############################################################################# 1272# attribute handling 1273 1274sub attribute 1275 { 1276 my ($self, $name) = @_; 1277 1278 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 return $edge->{att}->{$name} 1285 if defined $edge->{att}->{$name} && $edge->{att}->{$name} ne 'inherit'; 1286 1287 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 13051; 1306 1307############################################################################# 1308############################################################################# 1309 1310package Graph::Easy::Edge::Cell::Empty; 1311 1312require Graph::Easy::Node::Cell; 1313our @ISA = qw/Graph::Easy::Node::Cell/; 1314 1315#use vars qw/$VERSION/; 1316 1317our $VERSION = '0.76'; 1318 1319use constant isa_cell => 1; 1320 13211; 1322__END__ 1323 1324=head1 NAME 1325 1326Graph::Easy::Edge::Cell - A cell in an edge in Graph::Easy 1327 1328=head1 SYNOPSIS 1329 1330 use Graph::Easy; 1331 1332 my $ssl = Graph::Easy::Edge->new( 1333 label => 'encrypted connection', 1334 style => 'solid', 1335 color => 'red', 1336 ); 1337 my $src = Graph::Easy::Node->new( 'source' ); 1338 my $dst = Graph::Easy::Node->new( 'destination' ); 1339 1340 $graph = Graph::Easy->new(); 1341 1342 $graph->add_edge($src, $dst, $ssl); 1343 1344 print $graph->as_ascii(); 1345 1346=head1 DESCRIPTION 1347 1348A C<Graph::Easy::Edge::Cell> represents an edge between two (or more) nodes 1349in a simple graph. 1350 1351Each edge has a direction (from source to destination, or back and forth), 1352plus a style (line width and style), colors etc. It can also have a name, 1353e.g. a text label associated with it. 1354 1355There should be no need to use this package directly. 1356 1357=head1 METHODS 1358 1359=head2 error() 1360 1361 $last_error = $edge->error(); 1362 1363 $cvt->error($error); # set new messages 1364 $cvt->error(''); # clear error 1365 1366Returns the last error message, or '' for no error. 1367 1368=head2 as_ascii() 1369 1370 my $ascii = $path->as_ascii(); 1371 1372Returns the path-cell as a little ascii representation. 1373 1374=head2 as_html() 1375 1376 my $html = $path->as_html($tag,$id); 1377 1378eturns the path-cell as HTML code. 1379 1380=head2 label() 1381 1382 my $label = $path->label(); 1383 1384Returns the name (also known as 'label') of the path-cell. 1385 1386=head2 style() 1387 1388 my $style = $edge->style(); 1389 1390Returns the style of the edge. 1391 1392=head1 EXPORT 1393 1394None by default. Can export the following on request: 1395 1396 EDGE_START_E 1397 EDGE_START_W 1398 EDGE_START_N 1399 EDGE_START_S 1400 1401 EDGE_END_E 1402 EDGE_END_W 1403 EDGE_END_N 1404 EDGE_END_S 1405 1406 EDGE_SHORT_E 1407 EDGE_SHORT_W 1408 EDGE_SHORT_N 1409 EDGE_SHORT_S 1410 1411 EDGE_SHORT_BD_EW 1412 EDGE_SHORT_BD_NS 1413 1414 EDGE_SHORT_UN_EW 1415 EDGE_SHORT_UN_NS 1416 1417 EDGE_HOR 1418 EDGE_VER 1419 EDGE_CROSS 1420 1421 EDGE_N_E 1422 EDGE_N_W 1423 EDGE_S_E 1424 EDGE_S_W 1425 1426 EDGE_S_E_W 1427 EDGE_N_E_W 1428 EDGE_E_N_S 1429 EDGE_W_N_S 1430 1431 EDGE_LOOP_NORTH 1432 EDGE_LOOP_SOUTH 1433 EDGE_LOOP_EAST 1434 EDGE_LOOP_WEST 1435 1436 EDGE_N_W_S 1437 EDGE_S_W_N 1438 EDGE_E_S_W 1439 EDGE_W_S_E 1440 1441 EDGE_TYPE_MASK 1442 EDGE_FLAG_MASK 1443 EDGE_ARROW_MASK 1444 1445 EDGE_START_MASK 1446 EDGE_END_MASK 1447 EDGE_MISC_MASK 1448 1449 ARROW_RIGHT 1450 ARROW_LEFT 1451 ARROW_UP 1452 ARROW_DOWN 1453 1454=head1 SEE ALSO 1455 1456L<Graph::Easy>. 1457 1458=head1 AUTHOR 1459 1460Copyright (C) 2004 - 2007 by Tels L<http://bloodgate.com>. 1461 1462See the LICENSE file for more details. 1463 1464=cut 1465