1 /***********************************************************************/
2 /* Open Visualization Data Explorer */
3 /* (C) Copyright IBM Corp. 1989,1999 */
4 /* ALL RIGHTS RESERVED */
5 /* This code licensed under the */
6 /* "IBM PUBLIC LICENSE - Open Visualization Data Explorer" */
7 /***********************************************************************/
8
9 #define ONLY_IN_GRAPH_LAYOUT_CODE 1
10 #include "GraphLayout.h"
11 #include "EditorWindow.h"
12 #include "ListIterator.h"
13 #include "Network.h"
14 #include "EditorWorkSpace.h"
15 #include "Node.h"
16 #include "StandIn.h"
17 #include "Ark.h"
18 #include "ArkStandIn.h"
19 #include "ErrorDialogManager.h"
20 #include "VPEAnnotator.h"
21 #include "DecoratorStyle.h"
22 #include "Dictionary.h"
23 #include <math.h>
24 //#include <limits.h> // for INT_MIN and INT_MAX
25 //
26 // How it works:
27 //
28 // ...in a nutshell: Traverse the graph assigning a hop count
29 // to each node computed as the number of hops from the top of
30 // the graph to reach the node. Sort the graph by hop count.
31 // Among nodes that share a hop count, sort by the x coord
32 // of the nodes' arc's destination. Iterate over the sorted
33 // lists placing nodes with output tab above input tab
34 // whereever possible.
35 //
36 // ...in more detail:
37 // - For each decorator creating a list of nodes sorted by
38 // increasing distance from the decorator. This is used at
39 // the end of the layout so that I can place the decorator close
40 // to some node that it was close to before the layout started.
41 // - Tag every node with the number of hops required to reach
42 // the node from the top of the graph. After that's computed
43 // I use that number * a constant to compute the node's y coord.
44 // For many nodes I tweak the tag after the initial computation
45 // because many nodes could really be represented by a variety
46 // of values. Example: The max hop count from top to bottom
47 // of the graph is 5, but there's also an Input->Output. The
48 // Input and Output nodes could be tagged 1,2 or 4,5. Either
49 // is correct and it just depends on how you want things to
50 // look.
51 // - Create group data structures. There is 1 group for every
52 // set of connected nodes. In other words any 2 nodes that can
53 // be reached without lifting pen from paper are in the same
54 // group.
55 // - Sort the graph according to descending hop tag.
56 // - Perform the group's layout method starting from the
57 // beginning of the sorted list. Within the group, create
58 // row data structures - 1 row for all nodes that share
59 // a hop tag. The set of nodes placed in the layout method
60 // consists of all the nodes ABOVE the root node.
61 // - Perform the layout at the very bottom of the graph
62 // trivially by placing it at INITIAL_X,INITIAL_Y.
63 // - Perform each subsequent row's layout by sorting the
64 // nodes in the row by increasing x coord of the destination
65 // arc. The x coord is available after the preceeding row has
66 // been placed.
67 //
68
69 //
70 // ToDo:
71 // 1) When preparing the call to qsort() in LayoutRow there are some nodes
72 // that have 2 destination arcs on the next lower row. On behalf of those nodes,
73 // it would be nice to perform the sort and layout multiple times and measure
74 // the quality of the result. Then choose the one that worked the best.
75 //
76
77 //
78 // How far should successive 'levels' of the graph be separated
79 //
80 int GraphLayout::HeightPerLevel = 90;
81 #define ABSOLUTE_MINIMUM_HEIGHT_PER_LEVEL 70
82 #define MAXIMUM_HEIGHT_PER_LEVEL 200
83
84 //
85 //
86 //
87 const char* GraphLayout::ErrorMessages[] = {
88 "-autoLayoutHeight is outside the legal range [80 - 200]. (The default is 90.)\n",
89 "-autoLayoutGroupSpacing is outside the legal range [10 - 100]. (The default is 30.)\n",
90 "-autoLayoutNodeSpacing is outside the legal range [5 - 100]. (The default is 15.)\n",
91 NULL
92 };
93
94 //
95 // Allow a resource to control the vertical spreading out
96 // If there HeightPerLevel were too small, then it would become
97 // impossible to lay anything out.
98 //
SetHeightPerLevel(int hpl)99 const char* GraphLayout::SetHeightPerLevel(int hpl)
100 {
101 if ((hpl < ABSOLUTE_MINIMUM_HEIGHT_PER_LEVEL) ||(hpl > MAXIMUM_HEIGHT_PER_LEVEL)) {
102 return GraphLayout::ErrorMessages[0];
103 }
104 GraphLayout::HeightPerLevel = hpl;
105 return NUL(const char*);
106 }
107
108 //
109 // Identify original location of a decorator with respect to its
110 // closes node.
111 //
112 #define DECORATOR_ABOVE 1
113 #define DECORATOR_BELOW 2
114 #define DECORATOR_LEFT 4
115 #define DECORATOR_RIGHT 8
116
117 //
118 // x distance to separate disconnected groups of nodes from each other
119 //
120 int GraphLayout::GroupSpacing = 30;
121 #define MINIMUM_HORIZONTAL_GROUP_SPACING 10
122 #define MAXIMUM_HORIZONTAL_GROUP_SPACING 100
123
SetGroupSpacing(int gs)124 const char* GraphLayout::SetGroupSpacing(int gs)
125 {
126 if ((gs<MINIMUM_HORIZONTAL_GROUP_SPACING)||(gs>MAXIMUM_HORIZONTAL_GROUP_SPACING)) {
127 return ErrorMessages[1];
128 }
129 GraphLayout::GroupSpacing = gs;
130 return NUL(const char*);
131 }
132
133 //
134 // When attempting to position nodes, we always check for collisions.
135 // In most cases there should never be any. Only when merging two
136 // chunks of the same graph can it happen, but we check for it. If
137 // there would be one then we place somewhere else. Note: if you
138 // perform the layout, but find the Ctrl-U can't undo because it
139 // produces a warning message, the problem is probably that there
140 // was a collision when placing nodes. The undo feature won't move
141 // nodes if doing so would cause bumper cars.
142 // Currently unused
143 #define COLLISION_OFFSET 15
144
145 //
146 // Leave a little extra horizontal room between standins because
147 // crowded levels must still allow lines to pass.
148 // We'll allow REQUIRED_ if that will let us make straight arcs
149 // but if we can't make straight arcs, then we'll offset by
150 // DESIRED_
151 //
152 #define REQUIRED_HSEP 5
153 #define MINIMUM_DESIRED_HSEP 5
154 #define MAXIMUM_DESIRED_HSEP 100
155 int GraphLayout::NodeSpacing = 15;
SetNodeSpacing(int ns)156 const char* GraphLayout::SetNodeSpacing(int ns)
157 {
158 if ((ns < MINIMUM_DESIRED_HSEP) || (ns > MAXIMUM_DESIRED_HSEP)) {
159 return ErrorMessages[2];
160 }
161 GraphLayout::NodeSpacing = ns;
162 return NUL(const char*);
163 }
164
165
166 //
167 // Starting coords for graph placement. They have no meaning. They
168 // just shouldn't get negative or greater than 64K since they might
169 // get stored into an unsigned short.
170 //
171 #define INITIAL_X 0//5000
172 #define INITIAL_Y 0//5000
173
174 //#define DEBUG_PLACEMENT
175 #if defined(DEBUG_PLACEMENT)
176 //
177 // $ setenv DEBUG_PLACEMENT
178 // in order to use this.
179 //
180 static boolean debug = FALSE;
181 #endif
182
183 //
184 // Comparator function used with qsort. It says we're going to sort things
185 // by decreasing hop count. So the things early in the list are at the bottom
186 // of the graph.
187 //
SortByHop(const void * a,const void * b)188 int NodeInfo::SortByHop (const void* a, const void* b)
189 {
190 Node** aptr = (Node**)a;
191 Node** bptr = (Node**)b;
192 Node* anode = (Node*)*aptr;
193 Node* bnode = (Node*)*bptr;
194 NodeInfo* ainfo = (NodeInfo*)anode->getLayoutInformation();
195 NodeInfo* binfo = (NodeInfo*)bnode->getLayoutInformation();
196
197 // descending order of hop count
198 if (ainfo->hops > binfo->hops) return -1;
199 if (ainfo->hops < binfo->hops) return 1;
200
201 //
202 // If the 2 nodes connect to different outputs of the same parent,
203 // then sort by that. Different strategies to try:
204 // 1) output 1 1st, output 2 2nd, etc...
205 // 2) middle output 1st, 1 left 2nd, 1 right 3rd, 2 left 4th, etc...
206 // 3) odd numbered outputs first, then even numbered outputs. This
207 // might produce a few straight lines.
208 //
209 boolean shares_output1;
210 Ark* ark1 = GraphLayout::IsSingleInputNoOutputNode(anode, shares_output1, FALSE);
211 if (ark1) {
212 boolean shares_output2;
213 Ark* ark2 = GraphLayout::IsSingleInputNoOutputNode(bnode, shares_output2, FALSE);
214
215 if (ark2) {
216 Node *parent1, *parent2;
217 int input1, input2;
218 parent1 = ark1->getSourceNode(input1);
219 parent2 = ark2->getSourceNode(input2);
220 if (parent1 == parent2) {
221 if (input1 != input2) {
222 int output_count = parent1->getOutputCount();
223 int middle = (output_count>>1)+1;
224 int diff1 = middle - input1;
225 int diff2 = middle - input2;
226 if (diff1<0) diff1=-diff1;
227 if (diff2<0) diff2=-diff2;
228 if (diff1 < diff2) return -1;
229 if (diff2 < diff1) return 1;
230 }
231 }
232 }
233 }
234
235 return 0;
236 }
237
initialize(UIComponent * uic)238 void LayoutInfo::initialize (UIComponent* uic)
239 {
240 ASSERT(uic);
241 uic->getXYPosition(&this->orig_x, &this->orig_y);
242 this->x = this->y = -1;
243 uic->getXYSize(&this->w, &this->h);
244 this->initialized = TRUE;
245 }
246
initialize(Node * n,int hops)247 void NodeInfo::initialize(Node* n, int hops)
248 {
249 n->setLayoutInformation(this);
250 this->hops = hops;
251 this->LayoutInfo::initialize(n->getStandIn());
252 }
253
initialize(VPEAnnotator * dec)254 void AnnotationInfo::initialize (VPEAnnotator* dec)
255 {
256 dec->setLayoutInformation(this);
257 this->LayoutInfo::initialize(dec);
258 }
259
260 //
261 // This is the entry point for automatic graph layout. This is a public
262 // method called from EditorWindow.
263 //
entireGraph(WorkSpace * workSpace,const List & nodes,const List & decorators)264 boolean GraphLayout::entireGraph(WorkSpace* workSpace, const List& nodes, const List& decorators)
265 {
266 int offset;
267 List nodeList;
268
269 # if defined(DEBUG_PLACEMENT)
270 debug = (getenv("DEBUG_PLACEMENT") ? TRUE : FALSE);
271 # endif
272
273 ListIterator li;
274 boolean retval = TRUE;
275 Node* n;
276
277 // exclude the nodes that aren't in the current page
278 li.setList((List&)nodes);
279 while ((n=(Node*)li.getNext())) {
280 StandIn* si = n->getStandIn();
281 if (!si) continue;
282 if (si->getWorkSpace() != workSpace) continue;
283 nodeList.appendElement(n);
284 }
285
286 //
287 // Must store the nodes to reflow in an array because later
288 // we're going to use qsort.
289 //
290 Node** reflow;
291 int reflow_count=0;
292 int reflow_size=nodeList.getSize();
293 reflow = (Node**)malloc(sizeof(Node*) * reflow_size);
294 li.setList(nodeList);
295 while ((n=(Node*)li.getNext())) {
296 reflow[reflow_count++] = n;
297 }
298 ASSERT (reflow_count == reflow_size);
299
300 //
301 // 1) For each node, record the hops required to reach the node
302 // from the top of the graph. Optionally push nodes up or
303 // down as aesthetics require.
304 //
305 this->computeHopCounts(reflow, reflow_count);
306
307 //
308 // 2) Set up associations between annotations and nodes by finding
309 // each annotation's nearest node. This happens after computeHopCounts()
310 // because we need to use the LayoutInfo that computeHopCounts() sets
311 // up.
312 //
313 List decorators_in_current_page;
314 int widest_decorator = 0;
315 int tallest_decorator = 0;
316 this->prepareAnnotationPlacement(decorators_in_current_page,
317 reflow, reflow_count, decorators, workSpace, widest_decorator,
318 tallest_decorator);
319
320 //
321 // 3) Sort the list in descending order of hop,ancestor counts.
322 // Internal nodes are placed according to the order in which
323 // they're wired to inputs.
324 //
325 qsort (reflow, reflow_count, sizeof(Node*), NodeInfo::SortByHop);
326
327 //
328 // 4) Group the nodes. Any 2 nodes that have a path between them
329 // go into the same group.
330 //
331 int next;
332 for (next=0; next<reflow_count; next++) {
333 Node* n = reflow[next];
334 NodeInfo* ni = (NodeInfo*)n->getLayoutInformation();
335 List* connected = ni->getConnectedNodes(n);
336 ListIterator iter(*connected);
337 if (ni->getLayoutGroup()) continue;
338 LayoutGroup* group = new LayoutGroup(this->layout_groups.getSize());
339 this->layout_groups.appendElement(group);
340 while ((n=(Node*)iter.getNext())) {
341 NodeInfo* info = (NodeInfo*)n->getLayoutInformation();
342 info->setLayoutGroup(group);
343 }
344 }
345 # if defined(DEBUG_PLACEMENT)
346 if (debug) {
347 for (next=0; next<reflow_count; next++) {
348 Node* n = reflow[next];
349 NodeInfo* ni = (NodeInfo*)n->getLayoutInformation();
350 LayoutGroup* group = ni->getLayoutGroup();
351 ASSERT (group);
352 fprintf (stdout, "%s[%d] %s hops=%d in group %d\n",
353 __FILE__,__LINE__,n->getNameString(), ni->getGraphDepth(),
354 group->getId());
355 }
356 }
357 # endif
358
359 //
360 // 5) For the first node in the list, walk up the graph placing nodes.
361 //
362 n = reflow[0];
363
364 # if defined(DEBUG_PLACEMENT)
365 if (debug)
366 fprintf (stdout, "%s[%d] %s placing\n", __FILE__,__LINE__,n->getNameString());
367 # endif
368
369 List positioned;
370 NodeInfo* linfo = (NodeInfo*)n->getLayoutInformation();
371 LayoutGroup* group = linfo->getLayoutGroup();
372 group->layout(n, this, positioned);
373 this->repositionNewPlacements (n, TRUE, positioned);
374
375 //
376 // 6) For each subsequent unmarked node in the list, walk up the graph
377 // until hitting a marked node.
378 //
379 for (next=1 ; next<reflow_count; next++) {
380 n = reflow[next];
381 linfo = (NodeInfo*)n->getLayoutInformation();
382 if (linfo->isPositioned()) continue;
383
384
385 # if defined(DEBUG_PLACEMENT)
386 if (debug)
387 fprintf (stdout, "%s[%d] %s placing...",
388 __FILE__,__LINE__,n->getNameString());
389 # endif
390
391 //
392 // Locate a connected, positioned node so that we can
393 // get the right y coordinate.
394 //
395 List* connected = linfo->getConnectedNodes(n);
396 ListIterator iter(*connected);
397 Node* node;
398 boolean separated = TRUE;
399 int y;
400 while ((node=(Node*)iter.getNext())) {
401 NodeInfo* ninfo = (NodeInfo*)node->getLayoutInformation();
402 if (!ninfo->isPositioned()) continue;
403 int dummy;
404 ninfo->getXYPosition (dummy, y);
405 separated = FALSE;
406 int dd = ninfo->getGraphDepth() - linfo->getGraphDepth();
407 y-= (dd*GraphLayout::HeightPerLevel);
408 break;
409 }
410
411 if (!separated) {
412 # if defined(DEBUG_PLACEMENT)
413 if (debug) fprintf (stdout, " [%d] connected\n", __LINE__);
414 # endif
415 // The value 20000 isn't meaningful. Its purpose is to
416 // keep new node placments clear of existing node placements
417 // because we want the new ones to get a decent arrangement
418 // without colliding with others in the group. After we've
419 // finished this section of the graph, then we'll take all
420 // the nodes just placed and move them as a group, to a better
421 // location relative to the others in their group.
422 linfo->setProposedLocation (20000, y);
423 } else {
424 # if defined(DEBUG_PLACEMENT)
425 if (debug) fprintf (stdout, " [%d] disconnected", __LINE__);
426 # endif
427 // There was no positioned connected node. We are
428 // be working on a disconnected subgraph.
429 }
430 positioned.clear();
431 group = linfo->getLayoutGroup();
432 group->layout (n, this, positioned);
433
434 this->repositionNewPlacements(n, separated, positioned);
435 # if defined(DEBUG_PLACEMENT)
436 if (debug) fprintf (stdout, "\n");
437 # endif
438 }
439
440 //
441 // 7) All nodes have now been divied up into groups. Within groups,
442 // the nodes have been positioned. Each group is a set of nodes with
443 // connections internal to the group. Now it's time to position each
444 // group. This should be accomplished with respect shown to the arrangement
445 // of the groups within the canvas before automatic layout was kicked
446 // off. This is what gives authors a measure of control over the
447 // appearance of the result.
448 //
449 this->repositionGroups(reflow, reflow_count);
450
451 //
452 // 9) To apply placmenets, compute the bounding box of the nodes'
453 // placments, then translate so that all nodes have positive x,y
454 // and so that the center of the graph is near the center of the
455 // vpe if possible.
456 //
457 int minx, miny, maxx, maxy;
458 this->computeBoundingBox (reflow, reflow_count, minx, miny, maxx, maxy);
459
460 workSpace->beginManyPlacements();
461
462 //
463 // The curtain is almost ready to go up. We must adjust the location of all
464 // nodes to be close to the upper left corner of the canvas. The vpe annotation
465 // still hasn't been placed, so we'll allow some extra room so that the annotation
466 // can be placed conveniently.
467 //
468 offset = MIN(widest_decorator, 200);
469 offset = MAX(offset, 10);
470 minx-= offset;
471 offset = MIN(tallest_decorator, 200);
472 offset = MAX(offset, 10);
473 miny-= offset;
474
475 int l;
476 for (l=0; l<reflow_count; l++) {
477 int x,y;
478 Node* n = reflow[l];
479 LayoutInfo* linfo = (LayoutInfo*)n->getLayoutInformation();
480 linfo->getXYPosition (x, y);
481 x-= minx;
482 y-= miny;
483 linfo->setProposedLocation (x, y);
484 // By uncommenting this line, I'm able to verify that everything
485 // worked as planned. I like this information but I don't
486 // what it shown to users.
487 //if (linfo->collided()) y+=COLLISION_OFFSET;
488 this->editor->saveLocationForUndo(n->getStandIn(), FALSE, (boolean)l);
489 n->setVpePosition (x,y);
490 }
491
492 this->repositionDecorators(decorators_in_current_page, (boolean)l, reflow, reflow_count);
493
494 //
495 // 10) Rebuild all the arcs by fetching the workspace data structure,
496 // destroying it and creating a new one. The workspace lines can
497 // be created and destroyed but never modified.
498 //
499 int k;
500 for (k=0; k<reflow_count; k++) {
501 Node* n = reflow[k];
502 int input_count = n->getInputCount();
503 StandIn* si = n->getStandIn();
504 int j;
505 for (j=1; j<=input_count; j++) {
506 if (!n->isInputVisible(j)) continue;
507 List* arcs = (List*)n->getInputArks(j);
508 Ark* arc;
509 ListIterator iter(*arcs);
510 while ((arc=(Ark*)iter.getNext())) {
511 ArkStandIn *asi = arc->getArkStandIn();
512 delete asi;
513 si->addArk (this->editor, arc);
514 }
515 }
516 }
517 workSpace->endManyPlacements();
518
519 cleanup:
520 //
521 // Each node will delete its NodeInfo
522 //
523 int i;
524 for (i=0; i<reflow_count; i++) {
525 Node* n = reflow[i];
526 // deletes the layout information
527 n->setLayoutInformation(NULL);
528 }
529 free (reflow);
530
531 //
532 // erase all the group records
533 //
534 ListIterator iter(this->layout_groups);
535 LayoutGroup* g;
536 while ((g=(LayoutGroup*)iter.getNext())) delete g;
537 this->layout_groups.clear();
538
539 //
540 // each decorator will deletes its AnnotationInfo
541 //
542 VPEAnnotator* dec;
543 iter.setList(decorators_in_current_page);
544 while ((dec=(VPEAnnotator*)iter.getNext())) {
545 dec->setLayoutInformation(NUL(AnnotationInfo*));
546 }
547 return retval;
548 }
549
unmarkAllNodes(Node * reflow[],int reflow_count)550 void GraphLayout::unmarkAllNodes(Node* reflow[], int reflow_count)
551 {
552 int i;
553 for (i=0; i<reflow_count; i++)
554 reflow[i]->clearMarked();
555 }
hasConnectedInputs(Node * n)556 boolean GraphLayout::hasConnectedInputs(Node *n)
557 {
558 int input_count = n->getInputCount();
559 int h;
560 for (h=1; h<=input_count; h++) {
561 if (!n->isInputVisible(h)) continue;
562 List* inputs = (List*)n->getInputArks(h);
563 if (!inputs) continue;
564 if (!inputs->getSize()) continue;
565 return TRUE;
566 }
567 return FALSE;
568 }
569
hasConnectedOutputs(Node * n,Node * other_than)570 boolean GraphLayout::hasConnectedOutputs(Node *n, Node* other_than)
571 {
572 int dummy;
573 int output_count = n->getOutputCount();
574 int h;
575 for (h=1; h<=output_count; h++) {
576 if (!n->isOutputVisible(h)) continue;
577 List* outputs = (List*)n->getOutputArks(h);
578 if (!outputs) continue;
579 if (!outputs->getSize()) continue;
580 if (other_than) {
581 boolean others_found = FALSE;
582 ListIterator iter(*outputs);
583 Ark* arc;
584 while ((arc=(Ark*)iter.getNext())) {
585 Node* destination = arc->getDestinationNode(dummy);
586 if (destination != other_than) {
587 others_found = TRUE;
588 }
589 }
590 if (others_found)
591 return TRUE;
592 else
593 continue;
594 }
595 return TRUE;
596 }
597 return FALSE;
598 }
599
600 //
601 // Returns true if the destination location is empty. We need this because
602 // placing one StandIn on top of another leads to bumper cars.
603 //
nodeCanMoveTo(Node * n,int x,int y)604 boolean GraphLayout::nodeCanMoveTo (Node* n, int x, int y)
605 {
606 NodeInfo* info = (NodeInfo*)n->getLayoutInformation();
607 List* connected_nodes = info->getConnectedNodes(n);
608
609 int width, height;
610 info->getXYSize(width, height);
611
612 ListIterator iter(*connected_nodes);
613 Node* target;
614 int tw, th, tx, ty;
615 while ((target=(Node*)iter.getNext())) {
616 NodeInfo* target_info = (NodeInfo*)target->getLayoutInformation();
617 if (target_info == info) continue;
618 if (!target_info->isPositioned()) continue;
619 target_info->getXYPosition (tx, ty);
620 target_info->getXYSize(tw,th);
621 if ((x > (tx+tw)) || (y > (ty+th))) continue;
622 if (((x+width) < tx) || ((y+height) < ty)) continue;
623 # if defined(DEBUG_PLACEMENT)
624 if (debug) {
625 fprintf (stdout, "%s[%d] %s movement to %d,%d conflicted with %s\n",
626 __FILE__,__LINE__,n->getNameString(), x,y,target->getNameString());
627 }
628 # endif
629 return FALSE;
630 }
631 return TRUE;
632 }
633
CanMoveTo(LayoutInfo * info,int x,int y,Node * reflow[],int count,List * decorators)634 boolean GraphLayout::CanMoveTo (LayoutInfo* info, int x, int y, Node* reflow[], int count, List* decorators)
635 {
636 int width, height;
637 info->getXYSize(width, height);
638
639 int tw, th, tx, ty;
640 int i;
641 for (i=0; i<count; i++) {
642 Node* target = reflow[i];
643 NodeInfo* target_info = (NodeInfo*)target->getLayoutInformation();
644 if (target_info == info) continue;
645 if (!target_info->isPositioned()) continue;
646 target_info->getXYPosition (tx, ty);
647 target_info->getXYSize(tw,th);
648 if ((x > (tx+tw)) || (y > (ty+th))) continue;
649 if (((x+width) < tx) || ((y+height) < ty)) continue;
650 return FALSE;
651 }
652 if (decorators) {
653 ListIterator iter(*decorators);
654 LayoutInfo* target_info;
655 VPEAnnotator* dec;
656 while ((dec=(VPEAnnotator*)iter.getNext())) {
657 target_info = (LayoutInfo*)dec->getLayoutInformation();
658 if (target_info == info) continue;
659 if (!target_info->isPositioned()) continue;
660 target_info->getXYPosition (tx, ty);
661 target_info->getXYSize(tw,th);
662 if ((x > (tx+tw)) || (y > (ty+th))) continue;
663 if (((x+width) < tx) || ((y+height) < ty)) continue;
664 return FALSE;
665 }
666 }
667 return TRUE;
668 }
669
670 //
671 // Compute a location for the source node of arc. The location should
672 // result in a straight wire from the source's output tab to the
673 // destination's input tab.
674 //
positionSourceOverDest(Ark * arc,int & x,int & y)675 boolean GraphLayout::positionSourceOverDest (Ark* arc, int& x, int& y)
676 {
677 int input, output;
678 int dx,dy, sx, sy;
679 int xincr,yincr;
680 Node* destination = arc->getDestinationNode(input);
681 Node* source = arc->getSourceNode(output);
682 NodeInfo* src_info = (NodeInfo*)source->getLayoutInformation();
683 NodeInfo* dst_info = (NodeInfo*)destination->getLayoutInformation();
684 StandIn* src_si = source->getStandIn();
685 StandIn* dst_si = destination->getStandIn();
686 ASSERT (dst_info->isPositioned());
687 dst_info->getXYPosition (dx,dy);
688 int dst_tab_x = dst_si->getInputParameterTabX(input);
689 int src_tab_x = src_si->getOutputParameterTabX(output);
690 sx = dx + (dst_tab_x - src_tab_x);
691 sy = dy + (src_info->getGraphDepth()-dst_info->getGraphDepth()) *
692 GraphLayout::HeightPerLevel;
693
694 boolean collided = (this->nodeCanMoveTo(source, sx,sy) == FALSE);
695
696 x = sx;
697 y = sy;
698 return collided;
699 }
700
701
702 //
703 // Position the standIn so that the tab used by arc will have its line
704 // at the the specified x coord.
705 //
positionSource(Ark * arc,int xcoord_of_arc)706 boolean GraphLayout::positionSource (Ark* arc, int xcoord_of_arc)
707 {
708 int input,dummy,dx,dy, sx, sy;
709 Node* destination = arc->getDestinationNode(dummy);
710 Node* source = arc->getSourceNode(input);
711 NodeInfo* src_info = (NodeInfo*)source->getLayoutInformation();
712 NodeInfo* dst_info = (NodeInfo*)destination->getLayoutInformation();
713 StandIn* src_si = source->getStandIn();
714 ASSERT (dst_info->isPositioned());
715 dst_info->getXYPosition (dummy,dy);
716 int half_tab_width = 9;
717 sy = dy + (src_info->getGraphDepth()-dst_info->getGraphDepth()) *
718 GraphLayout::HeightPerLevel;
719
720 int src_tab_x = src_si->getOutputParameterTabX(input);
721 sx = xcoord_of_arc - (src_tab_x + half_tab_width);
722
723 src_info->setProposedLocation (sx,sy);
724 return (this->nodeCanMoveTo(source, sx,sy) == FALSE);
725 }
726
computeBoundingBox(Node * reflow[],int reflow_count,int & minx,int & miny,int & maxx,int & maxy,List * decorators)727 boolean GraphLayout::computeBoundingBox (Node* reflow[], int reflow_count,
728 int& minx, int& miny, int& maxx, int& maxy, List* decorators)
729 {
730 boolean valid_information = FALSE;
731 minx = miny = 99999999;
732 maxx = maxy = -99999999;
733 int x,y,w,h;
734 int i;
735 for (i=0; i<reflow_count; i++) {
736 Node* n = reflow[i];
737 LayoutInfo *li = (LayoutInfo*)n->getLayoutInformation();
738 ASSERT (li->isPositioned());
739 li->getXYPosition (x,y);
740 li->getXYSize(w,h);
741 if (x < minx) minx = x;
742 if (y < miny) miny = y;
743 if ((x+w) > maxx) maxx = x+w;
744 if ((y+h) > maxy) maxy = y+h;
745 valid_information = TRUE;
746 }
747 if (decorators) {
748 ListIterator iter(*decorators);
749 VPEAnnotator* dec;
750 while ((dec=(VPEAnnotator*)iter.getNext())) {
751 LayoutInfo* info = (LayoutInfo*)dec->getLayoutInformation();
752 info->getXYPosition (x,y);
753 info->getXYSize(w,h);
754 if (x < minx) minx = x;
755 if (y < miny) miny = y;
756 if ((x+w) > maxx) maxx = x+w;
757 if ((y+h) > maxy) maxy = y+h;
758 }
759 }
760 return valid_information;
761 }
762
computeBoundingBox(List & nodes,int & minx,int & miny,int & maxx,int & maxy)763 boolean GraphLayout::computeBoundingBox (List& nodes,
764 int& minx, int& miny, int& maxx, int& maxy)
765 {
766 boolean valid_information = FALSE;
767 minx = miny = 99999999;
768 maxx = maxy = -99999999;
769 ListIterator li(nodes);
770 Node* n;
771 while ((n=(Node*)li.getNext())) {
772 LayoutInfo* linfo = (LayoutInfo*)n->getLayoutInformation();
773 ASSERT (linfo->isPositioned());
774 int x,y,w,h;
775 linfo->getXYPosition(x,y);
776 linfo->getXYSize(w,h);
777 if (x < minx) minx = x;
778 if (y < miny) miny = y;
779 if ((x+w) > maxx) maxx = x+w;
780 if ((y+h) > maxy) maxy = y+h;
781 valid_information = TRUE;
782 }
783 return valid_information;
784 }
785
repositionDecorators(List & decorators,boolean sef,Node * reflow[],int reflow_count)786 void GraphLayout::repositionDecorators(List& decorators, boolean sef, Node* reflow[], int reflow_count)
787 {
788 int minx, miny, maxx, maxy;
789 this->computeBoundingBox (reflow, reflow_count, minx, miny, maxx, maxy);
790 AnnotationInfo::NextX = maxx+4;
791 AnnotationInfo::NextY = 10;
792 ListIterator decs(decorators);
793 boolean same_event_flag = sef;
794 int decx, decy;
795 VPEAnnotator* dec;
796 while ((dec=(VPEAnnotator*)decs.getNext())) {
797 this->editor->saveLocationForUndo(dec, FALSE, same_event_flag);
798 same_event_flag = TRUE;
799 AnnotationInfo* ainfo = (AnnotationInfo*)dec->getLayoutInformation();
800 ainfo->reposition(reflow, reflow_count, decorators);
801 ainfo->getXYPosition (decx, decy);
802 dec->setXYPosition (decx, decy);
803 }
804 }
805
IsSingleOutputNoInputNode(Node * n)806 boolean GraphLayout::IsSingleOutputNoInputNode (Node* n)
807 {
808 int i;
809 int output_count = n->getOutputCount();
810 int voc=0; // visible output count
811 for (i=1; i<=output_count; i++) {
812 if (n->isOutputVisible(i)) {
813 voc++;
814 if (voc > 1) return FALSE;
815 }
816 }
817 int input_count = n->getInputCount();
818 Ark* arc;
819 for (i=1; i<=input_count; i++) {
820 if (!n->isInputVisible(i)) continue;
821 List* arcs = (List*)n->getInputArks(i);
822 if (!arcs) continue;
823 if (arcs->getSize()) return FALSE;
824 }
825 return TRUE;
826 }
IsSingleInputNoOutputNode(Node * n,boolean & shares_an_output,boolean positioned)827 Ark* GraphLayout::IsSingleInputNoOutputNode (Node* n, boolean& shares_an_output, boolean positioned)
828 {
829 int output_count = n->getOutputCount();
830 int output;
831 for (output=1; output<=output_count; output++) {
832 if (!n->isOutputVisible(output)) continue;
833 List* outputs = (List*)n->getOutputArks(output);
834 if (!outputs) continue;
835 if (!outputs->getSize()) continue;
836 return NULL;
837 }
838
839 int input_count = n->getInputCount();
840 Ark* input_arc = NULL;
841 Ark* arc;
842 int input;
843 for (input=1; input<=input_count; input++) {
844 if (!n->isInputVisible(input)) continue;
845 List* inputs = (List*)n->getInputArks(input);
846 if (!inputs) continue;
847 if (!inputs->getSize()) continue;
848 ListIterator arcs(*inputs);
849 while ((arc=(Ark*)arcs.getNext())) {
850 if (!input_arc) {
851 // test for the output has only 1 destination node
852 int output;
853 Node* source = arc->getSourceNode(output);
854 List* outputs = (List*)source->getOutputArks(output);
855 ASSERT(outputs);
856 if (outputs->getSize() != 1) {
857 shares_an_output = TRUE;
858 } else {
859 shares_an_output = FALSE;
860 }
861 if (positioned) {
862 LayoutInfo* linfo = (LayoutInfo*)source->getLayoutInformation();
863 if (!linfo->isPositioned()) break;
864 }
865 input_arc = arc;
866 } else {
867 return NULL;
868 }
869 }
870 }
871 return input_arc;
872 }
873
positionDestBesideSibling(Ark * arc,int & x,int & y,boolean left,List * unusable_nodes)874 boolean GraphLayout::positionDestBesideSibling (Ark* arc, int& x, int& y, boolean left,List* unusable_nodes)
875 {
876 int output, input, dummy;
877 Node* source = arc->getSourceNode(output);
878 Node* destination = arc->getDestinationNode(input);
879 List* arcs = (List*)source->getOutputArks(output);
880 ListIterator iter(*arcs);
881 Ark* oa;
882 while ((oa=(Ark*)iter.getNext())) {
883 Node* dst = oa->getDestinationNode(dummy);
884 if (dst == destination) continue;
885
886 // At this point, we've located a sibling. Attempt a position
887 // beside the standIn.
888 NodeInfo* dinfo = (NodeInfo*)dst->getLayoutInformation();
889 if (!dinfo->isPositioned()) continue;
890
891 // We cannot use a sibling if it's a member of the set of
892 // nodes that we're trying to place.
893 if (unusable_nodes->isMember(dst)) continue;
894
895 int w,h;
896 NodeInfo* info = (NodeInfo*)destination->getLayoutInformation();
897 info->getXYSize(w,h);
898
899 int sibling_x;
900 dinfo->getXYPosition (sibling_x, y);
901 if (left) {
902 x = sibling_x - (w+GraphLayout::NodeSpacing);
903 } else {
904 int sibling_w;
905 dinfo->getXYSize(sibling_w, dummy);
906 x = sibling_x + sibling_w + GraphLayout::NodeSpacing;
907 }
908 }
909 boolean collided = (this->nodeCanMoveTo(destination, x,y)==FALSE);
910 return collided;
911 }
912
913 //
914 // Compute a location for the destination node of arc. The location should
915 // result in a straight wire from the source's output tab to the
916 // destination's input tab.
917 //
positionDestUnderSource(Ark * arc,int & x,int & y)918 boolean GraphLayout::positionDestUnderSource (Ark* arc, int& x, int& y)
919 {
920 int input, output;
921 int dx,dy, sx,sy;
922 int xincr,yincr;
923 Node* destination = arc->getDestinationNode(input);
924 Node* source = arc->getSourceNode(output);
925 NodeInfo* src_info = (NodeInfo*)source->getLayoutInformation();
926 NodeInfo* dst_info = (NodeInfo*)destination->getLayoutInformation();
927 StandIn* src_si = source->getStandIn();
928 StandIn* dst_si = destination->getStandIn();
929 ASSERT (src_info->isPositioned());
930 src_info->getXYPosition (sx,sy);
931 int dst_tab_x = dst_si->getInputParameterTabX(input);
932 int src_tab_x = src_si->getOutputParameterTabX(output);
933 dx = sx + (-dst_tab_x + src_tab_x);
934 dy = sy + ( (dst_info->getGraphDepth()-src_info->getGraphDepth()) *
935 GraphLayout::HeightPerLevel
936 );
937 boolean collided = (this->nodeCanMoveTo(destination, dx,dy)==FALSE);
938 x = dx;
939 y = dy;
940 return collided;
941 }
942
bottomUpTraversal(Node * visit_parents_of,int current_depth,int & min)943 void GraphLayout::bottomUpTraversal (Node* visit_parents_of, int current_depth, int& min)
944 {
945 int input_count = visit_parents_of->getInputCount();
946 NodeInfo* linfo = (NodeInfo*)visit_parents_of->getLayoutInformation();
947 int next_depth = current_depth - 1;
948
949 linfo->setGraphDepth(current_depth);
950 int input;
951 for (input=1; input<=input_count; input++) {
952 if (!visit_parents_of->isInputVisible(input)) continue;
953
954 List* inputs = (List*)visit_parents_of->getInputArks(input);
955 ListIterator iter(*inputs);
956 Ark* arc;
957 while ((arc=(Ark*)iter.getNext())) {
958 int output;
959 Node* source = arc->getSourceNode(output);
960 NodeInfo* sinfo = (NodeInfo*)source->getLayoutInformation();
961 if (next_depth < min)
962 min = next_depth;
963 int cd;
964 if (!sinfo) {
965 sinfo = new NodeInfo();
966 sinfo->initialize(source, next_depth);
967 cd = next_depth;
968 } else {
969 cd = sinfo->getGraphDepth();
970 }
971 if (next_depth < cd) {
972 sinfo->setGraphDepth(next_depth);
973 this->bottomUpTraversal(source, next_depth, min);
974 } else {
975 this->bottomUpTraversal(source, cd, min);
976 }
977 }
978 }
979 }
980
981 //
982 // For any node that has an immediate ancestor with a hop count that
983 // is more than 1 away, we'll try to decrease the hop count so that the
984 // 2 nodes are adjacent. This is necessary because we numbered the
985 // graph starting at the bottom. We look only at leaf nodes.
986 // We also examine siblings of the node because that prevents us from
987 // increasing arc length between our ancestor and our sibling.
988 //
adjustHopCounts(Node * reflow[],int reflow_count,int & min)989 boolean GraphLayout::adjustHopCounts (Node* reflow[], int reflow_count, int& min)
990 {
991 int i;
992 boolean changes_made = FALSE;
993 for (i=0; i<reflow_count; i++) {
994 Node* n = reflow[i];
995 if (this->hasConnectedOutputs(n)) continue;
996 NodeInfo* linfo = (NodeInfo*)n->getLayoutInformation();
997 int required_hops = this->computeRequiredHopsTo(n);
998 int sibling_hops = this->computeRequiredHopsToSiblingsOf (n);
999 required_hops = MAX(required_hops, sibling_hops);
1000 if (required_hops < linfo->getGraphDepth()) {
1001 int gd = required_hops;
1002 this->adjustAncestorHops (n,gd-1,min);
1003 linfo->setGraphDepth(gd);
1004 if (gd < min)
1005 min = gd;
1006 changes_made = TRUE;
1007 }
1008 }
1009
1010 return changes_made;
1011 }
1012
adjustAncestorHops(Node * parent,int new_hop_count,int & min)1013 void GraphLayout::adjustAncestorHops (Node* parent, int new_hop_count, int& min)
1014 {
1015 int input_count = parent->getInputCount();
1016 int output;
1017 int input;
1018 for (input=1; input<=input_count; input++) {
1019 if (!parent->isInputVisible(input)) continue;
1020 List* arcs = (List*)parent->getInputArks(input);
1021 if (!arcs) continue;
1022 ListIterator iter(*arcs);
1023 Ark* arc;
1024 while ((arc=(Ark*)iter.getNext())) {
1025 Node* source = arc->getSourceNode(output);
1026 NodeInfo* linfo = (NodeInfo*)source->getLayoutInformation();
1027 if (new_hop_count < linfo->getGraphDepth()) {
1028 linfo->setGraphDepth(new_hop_count);
1029 if (new_hop_count < min) min = new_hop_count;
1030 this->adjustAncestorHops (source, new_hop_count-1, min);
1031 }
1032 }
1033 }
1034 }
1035
1036 //
1037 // When we decrement a node's hop count, we also want to decrement any
1038 // descendants of the node if the descendant has no other inputs and
1039 // no outputs.
1040 //
adjustDescendantHops(Node * parent,int new_hop_count)1041 void GraphLayout::adjustDescendantHops(Node* parent, int new_hop_count)
1042 {
1043 int output_count = parent->getOutputCount();
1044 int output;
1045 int input;
1046 for (output=1; output<=output_count; output++) {
1047 if (!parent->isOutputVisible(output)) continue;
1048 List* arcs = (List*)parent->getOutputArks(output);
1049 if (!arcs) continue;
1050 ListIterator iter(*arcs);
1051 Ark* arc;
1052 while ((arc=(Ark*)iter.getNext())) {
1053 Node* dest = arc->getDestinationNode(input);
1054 boolean dummy;
1055 if (GraphLayout::IsSingleInputNoOutputNode(dest, dummy, FALSE)) {
1056 NodeInfo* info = (NodeInfo*)dest->getLayoutInformation();
1057 ASSERT (new_hop_count <= info->getGraphDepth());
1058 # if defined(DEBUG_PLACEMENT)
1059 if (debug) {
1060 fprintf (stdout, "%s[%d] moving %s:%d from %d to %d\n",
1061 __FILE__,__LINE__, dest->getNameString(),
1062 dest->getInstanceNumber(), info->getGraphDepth(), new_hop_count);
1063 }
1064 # endif
1065 info->setGraphDepth(new_hop_count);
1066 }
1067 }
1068 }
1069 }
1070
computeRequiredHopsTo(Node * n)1071 int GraphLayout::computeRequiredHopsTo(Node* n)
1072 {
1073 int output;
1074 int max_count = 1;
1075 int input_count = n->getInputCount();
1076 int input;
1077 for (input=1; input<=input_count; input++) {
1078 if (!n->isInputVisible(input)) continue;
1079 List* arcs = (List*)n->getInputArks(input);
1080 if (!arcs) continue;
1081 ListIterator iter(*arcs);
1082 Ark* arc;
1083 while ((arc=(Ark*)iter.getNext())) {
1084 int count = this->computeRequiredHopsTo(arc->getSourceNode(output));
1085 max_count = MAX(count+1, max_count);
1086 }
1087 }
1088 return max_count;
1089 }
1090
computeRequiredHopsToSiblingsOf(Node * n)1091 int GraphLayout::computeRequiredHopsToSiblingsOf (Node* n)
1092 {
1093 int dummy;
1094 int input_count = n->getInputCount();
1095 int max_hop_count = 1;
1096 int input;
1097 for (input=1; input<=input_count; input++) {
1098 if (!n->isInputVisible(input)) continue;
1099 List* arcs = (List*)n->getInputArks(input);
1100 if (!arcs) continue;
1101 ListIterator iter(*arcs);
1102 Ark* arc;
1103 while ((arc=(Ark*)iter.getNext())) {
1104 Node* source = arc->getSourceNode(dummy);
1105
1106 int output_count = source->getOutputCount();
1107 // for each output, fetch connected nodes.
1108 int output;
1109 for (output=1; output<=output_count; output++) {
1110 if (!source->isOutputVisible(output)) continue;
1111 List* oarcs = (List*)source->getOutputArks(output);
1112 if (!oarcs) continue;
1113 ListIterator it(*oarcs);
1114 Ark* oarc;
1115 // for each of those nodes fetch required hops
1116 while ((oarc=(Ark*)it.getNext())) {
1117 Node* dest = oarc->getDestinationNode(dummy);
1118 max_hop_count = MAX(max_hop_count, this->computeRequiredHopsTo(dest));
1119 }
1120 }
1121
1122 }
1123 }
1124 return max_hop_count;
1125 }
1126
1127 //
1128 // compute hops by starting at every leaf node and walking up the graph.
1129 // Initialize the hop counter with a huge number and decrement by 1 at
1130 // each hop. Then translate all hop counts so that the smallest is set
1131 // to 1. Then adjust all hop counters so that there is no unneeded
1132 // distance between connected nodes. We get unneeded space because
1133 // we default leaf nodes to the largest hop count even though that isn't
1134 // accurate.
1135 //
computeHopCounts(Node * reflow[],int reflow_count)1136 void GraphLayout::computeHopCounts (Node* reflow[], int reflow_count)
1137 {
1138 int i;
1139 //
1140 // The starting hop counter has no units. After we finish, we'll
1141 // translate all hop counters so that the smallest one in the graph
1142 // is set to 1.
1143 //
1144 int smallest_hop_count=99999;
1145 for (i=0; i<reflow_count; i++) {
1146 Node* n = reflow[i];
1147 if (!this->hasConnectedOutputs(n)) {
1148 NodeInfo* linfo = new NodeInfo();
1149 linfo->initialize (n, 99999);
1150 this->bottomUpTraversal(n,99999,smallest_hop_count);
1151 }
1152 }
1153
1154 boolean changes_made;
1155
1156 //
1157 // We might want to make some adjustments to hop counts. Sounds like
1158 // nonsense, but the problem is that there are some nodes that could
1159 // really have several different hop counts. Think about a receiver
1160 // wired directly to an (and only to an) output. Should the receiver
1161 // be marked 1 and the output be marked 2? ...or should the output be
1162 // (max hops) and the receiver be 1-max? Usually I've tried to keep
1163 // things pushed down towards the bottom of the graph. Here we want
1164 // to push things back towards the top of the graph i.e. reduce the
1165 // hop count if that will have the effect of shorting the arcs.
1166 //
1167 changes_made = TRUE;
1168 while (changes_made)
1169 changes_made = this->adjustHopCounts(reflow, reflow_count, smallest_hop_count);
1170
1171 //
1172 // Rules say that we decrement the hop count of any node that is
1173 // connected to multiple non-consecutive input tabs on 1 row.
1174 // The idea is to devote more screen realestate to displaying
1175 // lines if there are lots of lines between 1 pair of nodes.
1176 //
1177 do {
1178 changes_made = FALSE;
1179 for (i=0; i<reflow_count; i++) {
1180 Node* n = reflow[i];
1181 changes_made|= this->spreadOutSpaghettiFrom(n, smallest_hop_count);
1182 }
1183 } while (changes_made);
1184
1185 //
1186 // Before we raise the curtain, just one more tweak. Sometimes
1187 // (usually at the top of a graph) we get some nodes that have a slew of
1188 // recievers providing inputs. It's difficult to place all of these
1189 // in a pleasing way, so we'll do a final check for this. If the
1190 // situation exists, then we'll artificially decrement the hop count
1191 // for every other reciever so that the pattern is a little garbagey
1192 // looking. We sort before the call because if obviates the need
1193 // to make the call in a loop the way we do above.
1194 //
1195 qsort (reflow, reflow_count, sizeof(Node*), NodeInfo::SortByHop);
1196 for (i=0; i<reflow_count; i++) {
1197 Node* n = reflow[i];
1198 this->fixForTooManyReceivers(n, smallest_hop_count);
1199 }
1200
1201 //
1202 // Translate to the origin
1203 //
1204 for (i=0; i<reflow_count; i++) {
1205 Node* n = reflow[i];
1206 NodeInfo* linfo = (NodeInfo*)n->getLayoutInformation();
1207 ASSERT (linfo);
1208 int gd = linfo->getGraphDepth();
1209 gd = (gd-smallest_hop_count) + 1;
1210 linfo->setGraphDepth(gd);
1211 }
1212 }
1213
1214 //
1215 // if n has multiple arcs that go to the next higher hop count, and those
1216 // arcs aren't consecutive, then decrement n's hop count. Reasoning is that
1217 // having too many arcs in a cramped area is hard to read.
1218 //
spreadOutSpaghettiFrom(Node * n,int & min)1219 boolean GraphLayout::spreadOutSpaghettiFrom (Node* n, int& min)
1220 {
1221 int dummy;
1222 if (this->countConnectedOutputs (n, 1, dummy) <= 1) return FALSE;
1223
1224 // don't screw around with gets and sets
1225 const char* name = n->getNameString();
1226 if (EqualString(name, "GetLocal") || EqualString(name, "GetGlobal") ||
1227 EqualString(name, "Get")) {
1228 return FALSE;
1229 }
1230
1231 boolean retval = FALSE;
1232
1233 NodeInfo* info = (NodeInfo*)n->getLayoutInformation();
1234 int depth = info->getGraphDepth();
1235 int output_count = n->getOutputCount();
1236 int output;
1237 Ark* arc;
1238 int levels_to_decr = 0;
1239 for (output=1; output<=output_count; output++) {
1240 if (!n->isOutputVisible(output)) continue;
1241 List* arcs = (List*)n->getOutputArks(output);
1242 if (!arcs) continue;
1243 ListIterator iter(*arcs);
1244 int input;
1245 Node* arc_dest = NUL(Node*);
1246 while ((arc=(Ark*)iter.getNext())) {
1247 Node* dest = arc->getDestinationNode(input);
1248 NodeInfo* dinfo = (NodeInfo*)dest->getLayoutInformation();
1249 int cnctn_count = this->countConnectionsBetween(n,dest,FALSE);
1250 int desired_depth = dinfo->getGraphDepth() - cnctn_count;
1251 if (depth > desired_depth) {
1252 levels_to_decr = MAX(levels_to_decr, (depth-desired_depth));
1253 }
1254 }
1255 }
1256
1257 if (levels_to_decr) {
1258 int new_depth = depth-levels_to_decr;
1259 # if defined(DEBUG_PLACEMENT)
1260 if (debug) {
1261 fprintf (stdout, "%s[%d] changing %s:%d from %d to %d\n", __FILE__,__LINE__,
1262 n->getNameString(), n->getInstanceNumber(), depth, new_depth);
1263 }
1264 # endif
1265 this->adjustAncestorHops (n,new_depth-1,min);
1266 this->adjustDescendantHops (n, new_depth+1);
1267 if (new_depth < min) min = new_depth;
1268 info->setGraphDepth(new_depth);
1269 retval = TRUE;
1270 }
1271
1272 return retval;
1273 }
1274
1275 //
1276 // If a node has wide recievers (a.k.a no input-1 output node)
1277 // connected to adjacent input tabs then
1278 // we'll decrement the hop count of 1 receiver so that things aren't
1279 // bunched up too much. In general this is bad because anything that
1280 // increases arc length is likely to increase the wire crossings.
1281 //
fixForTooManyReceivers(Node * n,int & min)1282 void GraphLayout::fixForTooManyReceivers(Node* n, int& min)
1283 {
1284 NodeInfo* ninfo = (NodeInfo*)n->getLayoutInformation();
1285 int dummy;
1286 int input;
1287 int input_count = n->getInputCount();
1288 List wide_nodes;
1289 for (input=1; input<=input_count; input++) {
1290 if (!n->isInputVisible(input)) continue;
1291 List* arcs = (List*)n->getInputArks(input);
1292 if (!arcs) continue;
1293 ListIterator iter(*arcs);
1294 Ark* arc;
1295 while ((arc=(Ark*)iter.getNext())) {
1296 Node* source = arc->getSourceNode(dummy);
1297 if (wide_nodes.isMember(source)) continue;
1298 if (!this->hasNoCloserDescendant(source,n)) continue;
1299 LayoutInfo* linfo = (LayoutInfo*)source->getLayoutInformation();
1300 int w,h;
1301 linfo->getXYSize(w,h);
1302 wide_nodes.appendElement(source);
1303 }
1304 }
1305 int wnsize = wide_nodes.getSize();
1306 if (wnsize <= 2) return ;
1307
1308 // saw tooth pattern
1309 // We're controlling how high up/down the pattern goes.
1310 int state = 0;
1311 int max_state = wnsize>>1;
1312 if (wnsize&1) max_state++;
1313 int incr = 1;
1314 boolean had_inputs = FALSE;
1315 for (input=1; input<=input_count; input++) {
1316 if (!n->isInputVisible(input)) continue;
1317 List* arcs = (List*)n->getInputArks(input);
1318 if (!arcs) continue;
1319 ListIterator iter(*arcs);
1320 Ark* arc;
1321 while ((arc=(Ark*)iter.getNext())) {
1322 Node* source = arc->getSourceNode(dummy);
1323 if (state == 3) {
1324 if ((!this->hasConnectedInputs(source)) && (!had_inputs))
1325 state = 0;
1326 }
1327 NodeInfo* linfo = (NodeInfo*)source->getLayoutInformation();
1328 int gd;
1329 switch (state) {
1330 case -1:
1331 incr = -incr;
1332 case 0:
1333 state = 1;
1334 break;
1335 default:
1336 gd = ninfo->getGraphDepth()-(state+1);
1337 // if this actually does anything...
1338 if (gd < linfo->getGraphDepth()) {
1339 this->adjustAncestorHops (source, gd-1, min);
1340 linfo->setGraphDepth(gd);
1341 if (gd < min) min = gd;
1342 }
1343 state+= incr;
1344 break;
1345 }
1346 if (state == max_state) {
1347 incr = -incr;
1348 state+= 2*incr;
1349 }
1350 had_inputs = this->hasConnectedInputs(source);
1351 }
1352 }
1353 }
1354
1355 //
1356 // return TRUE if dest has the hop count that is closest to the hop
1357 // count of source. return FALSE if there is some other node that's connected
1358 // to source that has a shorter arcs. We use this information in order to
1359 // cause source to be placed with its closest living relative.
1360 // return FALSE if there is some other node that's connected to dest
1361 // by more arks than source.
1362 //
hasNoCloserDescendant(Node * source,Node * dest)1363 boolean GraphLayout::hasNoCloserDescendant (Node* source, Node* dest)
1364 {
1365 int dummy;
1366 int output;
1367 NodeInfo* src_info = (NodeInfo*)source->getLayoutInformation();
1368 NodeInfo* dst_info = (NodeInfo*)dest->getLayoutInformation();
1369
1370 int connection_count_to_beat = this->countConnectionsBetween(source, dest);
1371
1372 int min_depth = dst_info->getGraphDepth();
1373 int output_count = source->getOutputCount();
1374 for (output=1; output<=output_count; output++) {
1375 if (!source->isOutputVisible(output)) continue;
1376 List* arcs = (List*)source->getOutputArks(output);
1377 if (!arcs) continue;
1378 ListIterator iter(*arcs);
1379 Ark* arc;
1380 while ((arc=(Ark*)iter.getNext())) {
1381 Node* destination = arc->getDestinationNode(dummy);
1382 if (destination == dest) continue;
1383 NodeInfo* info = (NodeInfo*)destination->getLayoutInformation();
1384 if (this->countConnectionsBetween(source,destination) <
1385 connection_count_to_beat) {
1386 continue;
1387 }
1388 if (info->getGraphDepth() < min_depth) {
1389 // a closer relative was found
1390 return FALSE;
1391 }
1392 }
1393 }
1394
1395 return TRUE;
1396 }
1397
1398 //
1399 // Compute the location of the new placements relative to the old
1400 // placements and adjust the new placements' locations so that the
1401 // 2 bounding boxes don't intersect.
1402 // If known_disjoint is TRUE, then that means we can move the new set
1403 // anywhere we want it. Otherwise we have to maintain any left-of
1404 // right-of relationships.
1405 //
repositionNewPlacements(Node * root,boolean disjoint,List & placed)1406 void GraphLayout::repositionNewPlacements (Node* root, boolean disjoint, List& placed)
1407 {
1408 Node* n;
1409 NodeInfo* ninfo = (NodeInfo*)root->getLayoutInformation();
1410 LayoutGroup* group = ninfo->getLayoutGroup();
1411 placed.appendElement(root);
1412
1413 # if defined(DEBUG_PLACEMENT)
1414 if (debug) {
1415 ListIterator li(placed);
1416 Node* node;
1417 fprintf (stdout, "%s[%d] placing in group %d...\n",
1418 __FILE__,__LINE__, group->getId());
1419 while (node=(Node*)li.getNext()) {
1420 fprintf (stdout, "\t%s:%d\n", node->getNameString(),
1421 node->getInstanceNumber());
1422 }
1423 }
1424 # endif
1425
1426 int minx2, miny2, maxx2, maxy2;
1427 this->computeBoundingBox (placed, minx2, miny2, maxx2,maxy2);
1428
1429 boolean group_had_placements=TRUE;
1430 int minx1, miny1, maxx1, maxy1;
1431
1432 //
1433 // At this point n is not initialized but the method only reads from
1434 // n if the connected list hasn't been built yet. At this point,
1435 // all such lists are built, so the uninitialized n is supplied here
1436 // only as a misfeature of the api.
1437 //
1438 List* connected = ninfo->getConnectedNodes(n);
1439
1440 if (!disjoint) {
1441 // Form the bounding box of the starting condition
1442 List original;
1443 Node* node;
1444 ListIterator iter(*connected);
1445 while ((node=(Node*)iter.getNext())) {
1446 LayoutInfo* info = (LayoutInfo*)node->getLayoutInformation();
1447 if (info->isPositioned()) {
1448 if (!placed.isMember(node)) original.appendElement(node);
1449 }
1450 }
1451 group_had_placements =
1452 this->computeBoundingBox (original, minx1, miny1, maxx1,maxy1);
1453 }
1454
1455 int ydelta, xdelta;
1456 if ((disjoint) || (!group_had_placements)) {
1457 xdelta = -minx2;
1458 ydelta = -miny2;
1459 ListIterator iter(placed);
1460 while ((n=(Node*)iter.getNext())) {
1461 NodeInfo* info = (NodeInfo*)n->getLayoutInformation();
1462 int x,y;
1463 info->getXYPosition(x,y);
1464 info->setProposedLocation(x+xdelta, y+ydelta);
1465 }
1466 group->initialize(&placed);
1467 return ;
1468 }
1469
1470 ASSERT (group_had_placements);
1471 //
1472 // Now we have 2 bounding boxes, 1 for the nodes that were
1473 // already placed and 1 for the batch of additional placements.
1474 // We know we can scoot the new comers over so that the 2 bounding
1475 // boxes butt up against each other, but we might be able to do
1476 // better. How to figure out how much we can scoot over without
1477 // having overlapping?
1478 //
1479 // The goal is to scoot over far enough so that a wire that joins
1480 // 2 nodes - 1 in the new placments and 1 in the old placements -
1481 // becomes a straight line.
1482 //
1483 // Collect all the arcs that cross from 1 set to the other set.
1484 // Then sort those arcs by decreasing length. Then for each
1485 // arc, try to line things up without any collisions. If none
1486 // works, then just butt the bounding boxes up against eachother.
1487 //
1488 List crossing_arcs;
1489 ListIterator iter(placed);
1490 while ((n=(Node*)iter.getNext())) {
1491 int input_count = n->getInputCount();
1492 int output_count = n->getOutputCount();
1493 int input, output;
1494 for (input=1; input<=input_count; input++) {
1495 if (!n->isInputVisible(input)) continue;
1496 List* arks = (List*)n->getInputArks(input);
1497 if (!arks) continue;
1498 ListIterator li(*arks);
1499 Ark* arc;
1500 while ((arc=(Ark*)li.getNext())) {
1501 Node* source = arc->getSourceNode(output);
1502 NodeInfo* sinfo = (NodeInfo*)source->getLayoutInformation();
1503 if (!sinfo->isPositioned()) continue;
1504 if (!placed.isMember(source)) {
1505 if (crossing_arcs.isMember(arc) == FALSE)
1506 crossing_arcs.appendElement(arc);
1507 }
1508 }
1509 }
1510 for (output=1; output<=output_count; output++) {
1511 if (!n->isOutputVisible(output)) continue;
1512 List* arks = (List*)n->getOutputArks(output);
1513 if (!arks) continue;
1514 ListIterator li(*arks);
1515 Ark* arc;
1516 while ((arc=(Ark*)li.getNext())) {
1517 Node* dest = arc->getDestinationNode(input);
1518 NodeInfo* dinfo = (NodeInfo*)dest->getLayoutInformation();
1519 if (!dinfo->isPositioned()) continue;
1520 if (!placed.isMember(dest)) {
1521 if (crossing_arcs.isMember(arc) == FALSE)
1522 crossing_arcs.appendElement(arc);
1523 }
1524 }
1525 }
1526 }
1527 int maxarcs = 128;
1528 int arc_count = crossing_arcs.getSize();
1529 arc_count = MIN(maxarcs, arc_count);
1530 // this assert says that we should have detected a disconnected
1531 // subgraph in an earlier test.
1532 //ASSERT(arc_count);
1533 // arc_count can be 0 if we the nodes to which the new placements
1534 // are connected haven't been placed yet.
1535
1536 boolean prefer_left = TRUE;
1537
1538 boolean arc_found = FALSE;
1539 Ark* aa[128];
1540 int i,j;
1541 if (arc_count) {
1542 for (i=0,j=1; i<arc_count; i++,j++) {
1543 aa[i] = (Ark*)crossing_arcs.getElement(j);
1544 }
1545 qsort (aa, arc_count, sizeof(Ark*), GraphLayout::ArcComparator);
1546
1547 //
1548 // There may come a time when we have to decide if we want to place
1549 // new nodes to the left or to the right of existing placements. When
1550 // we use this info we're really just taking our chances. Also, we don't
1551 // always need this info but we have to have it computed before we need
1552 // it.
1553 int center = 0;
1554 int j;
1555 for (j=0; j<arc_count; j++) {
1556 //
1557 // The choice of arc has an influence of the way we lay out the
1558 // graph especially when Gets and Sets are involved. I don't
1559 // know of a 'proper' way to pick one arc. So, loop over all arcs
1560 // involved, give each 1 vote which it can cast in 1 of 3 ways...
1561 // go left, go right, don't care.
1562 //
1563 boolean prefer_left = FALSE;
1564 Ark* arc = aa[j];
1565
1566 int input, output;
1567 Node* src = arc->getSourceNode(output);
1568 Node* dst = arc->getDestinationNode(input);
1569 int nth_input_tab, nth_output_tab;
1570 int vi = this->countConnectedInputs(dst, input, nth_input_tab);
1571 // destination node casts his vote...
1572 if ((vi==1)||((nth_input_tab==(vi>>1))&&((vi&1)==1))) {
1573 // don't care
1574 # if defined(DEBUG_PLACEMENT)
1575 if (debug)
1576 fprintf (stdout, "\t%s destination didn't vote\n",
1577 dst->getNameString());
1578 # endif
1579 } else if (nth_input_tab<=(vi>>1)) {
1580 prefer_left = (placed.isMember(dst)==FALSE);
1581 center+= (prefer_left?-1:1);
1582 # if defined(DEBUG_PLACEMENT)
1583 if (debug)
1584 fprintf (stdout, "\t%s destination voted %s\n",
1585 dst->getNameString(), (prefer_left?"LEFT":"RIGHT"));
1586 # endif
1587 } else if (nth_input_tab>(vi>>1)) {
1588 prefer_left = placed.isMember(dst);
1589 center+= (prefer_left?-1:1);
1590 # if defined(DEBUG_PLACEMENT)
1591 if (debug)
1592 fprintf (stdout, "\t%s destination voted %s\n",
1593 dst->getNameString(), (prefer_left?"LEFT":"RIGHT"));
1594 # endif
1595 }
1596
1597
1598 // source node casts his vote..
1599 int vo = this->countConnectedOutputs(src, output, nth_output_tab);
1600 if ((vo==1)||((nth_output_tab==(vo>>1))&&((vo&1)==1))) {
1601 //don't care
1602 # if defined(DEBUG_PLACEMENT)
1603 if (debug)
1604 fprintf (stdout, "\t%s source didn't vote\n",
1605 src->getNameString());
1606 # endif
1607 } else if (nth_output_tab <= (vo>>1)) {
1608 prefer_left = (placed.isMember(src)==FALSE);
1609 center+= (prefer_left?-1:1);
1610 # if defined(DEBUG_PLACEMENT)
1611 if (debug)
1612 fprintf (stdout, "\t%s source voted %s\n",
1613 src->getNameString(), (prefer_left?"LEFT":"RIGHT"));
1614 # endif
1615 } else if (nth_output_tab > (vo>>1)) {
1616 prefer_left = placed.isMember(src);
1617 center+= (prefer_left?-1:1);
1618 # if defined(DEBUG_PLACEMENT)
1619 if (debug)
1620 fprintf (stdout, "\t%s source voted %s\n",
1621 src->getNameString(), (prefer_left?"LEFT":"RIGHT"));
1622 # endif
1623 }
1624
1625 // the source may have another node connected to the same output.
1626 // It should cast a vote also.
1627 List* arcs = (List*)src->getOutputArks(output);
1628 ListIterator iter(*arcs);
1629 Node* other_dst = NUL(Node*);
1630 int oinput;
1631 while ((arc=(Ark*)iter.getNext())) {
1632 other_dst = arc->getDestinationNode(oinput);
1633 if (other_dst == dst) continue;
1634 if (placed.isMember(other_dst)) continue;
1635 break;
1636 }
1637 if (other_dst) {
1638 int vi = this->countConnectedInputs(other_dst, oinput, nth_input_tab);
1639 if (vi==1) {
1640 // don't care
1641 # if defined(DEBUG_PLACEMENT)
1642 if (debug)
1643 fprintf (stdout, "\t%s destination didn't vote\n",
1644 dst->getNameString());
1645 # endif
1646 } else if (nth_input_tab<=(vi>>1)) {
1647 prefer_left = TRUE;
1648 center+= (prefer_left?-1:1);
1649 # if defined(DEBUG_PLACEMENT)
1650 if (debug)
1651 fprintf (stdout, "\t%s source voted %s\n",
1652 src->getNameString(), (prefer_left?"LEFT":"RIGHT"));
1653 # endif
1654 } else if (nth_input_tab>(vi>>1)) {
1655 prefer_left = FALSE;
1656 center+= (prefer_left?-1:1);
1657 # if defined(DEBUG_PLACEMENT)
1658 if (debug)
1659 fprintf (stdout, "\t%s source voted %s\n",
1660 src->getNameString(), (prefer_left?"LEFT":"RIGHT"));
1661 # endif
1662 }
1663 }
1664 }
1665 prefer_left = (center<=0);
1666
1667 // for each arc, compute the x location that will make the wire
1668 // straight and test each node in placed to see if the entire
1669 // set can move.
1670 for (i=0; i<arc_count; i++) {
1671 Ark* arc = aa[i];
1672 int input, output;
1673 Node* source = arc->getSourceNode(output);
1674 Node* dest = arc->getDestinationNode(input);
1675 NodeInfo* sinfo = (NodeInfo*)source->getLayoutInformation();
1676 NodeInfo* dinfo = (NodeInfo*)dest->getLayoutInformation();
1677 boolean node_can_move = FALSE;
1678 int prevx, prevy;
1679
1680 int x,y,dummy;
1681 if (placed.isMember(source)) {
1682 if (!this->positionSourceOverDest(arc, x,y)) {
1683 node_can_move = TRUE;
1684 sinfo->getXYPosition (prevx, prevy);
1685 xdelta = x-prevx;
1686 ydelta = y-prevy;
1687 }
1688 } else {
1689 if (!this->positionDestUnderSource(arc, x,y)) {
1690 node_can_move = TRUE;
1691 dinfo->getXYPosition (prevx, prevy);
1692 xdelta = x-prevx;
1693 ydelta = y-prevy;
1694 } else if (!this->positionDestBesideSibling (arc, x,y,prefer_left,&placed)) {
1695 // potential problem. I'm not checking that the arc isn't
1696 // shorter than some other arc that the destination node
1697 // has coming in. If it is shorter, then I could place
1698 // a destination node higher on the screen that it ought
1699 // to be. An example of this in InterpolateCameraMacro.net.
1700 node_can_move = TRUE;
1701 dinfo->getXYPosition (prevx, prevy);
1702 xdelta = x-prevx;
1703 ydelta = y-prevy;
1704 }
1705 }
1706
1707 // now check all other nodes.
1708 if (node_can_move) {
1709 arc_found = TRUE;
1710 //ListIterator iter(*connected);
1711 ListIterator iter(placed);
1712 Node* n;
1713 while ((n=(Node*)iter.getNext())) {
1714 NodeInfo* info = (NodeInfo*)n->getLayoutInformation();
1715 int nx,ny;
1716 info->getXYPosition(nx,ny);
1717 if (!this->nodeCanMoveTo (n, nx+xdelta, ny+ydelta)) {
1718 arc_found = FALSE;
1719 break;
1720 }
1721 }
1722 if (arc_found) break;
1723 }
1724 }
1725 }
1726
1727 // If no arc is found, then compute the xdelta so that the
1728 // two bounding boxes butt up against eachother. The only
1729 // missing information is which bounding box should be on
1730 // the left and which on the right. This is difficult to
1731 // figure out. Possible clues are the positioning of the
1732 // input and output tabs in use. A high-numbered input
1733 // wants to be on the left of its source. A low-numbered
1734 // output wants to be on the right of its destination.
1735 if (!arc_found) {
1736 # if defined(DEBUG_PLACEMENT)
1737 if (debug)
1738 fprintf (stdout, "%s[%d] %d arcs will vote on placement\n",
1739 __FILE__,__LINE__,arc_count);
1740 # endif
1741
1742 if (prefer_left) {
1743 xdelta = -(maxx2 + GraphLayout::NodeSpacing - minx1);
1744 ydelta = 0;
1745 } else {
1746 xdelta = (maxx1 + GraphLayout::NodeSpacing - minx2);
1747 ydelta = 0;
1748 }
1749 }
1750
1751 //
1752 // perform the translation
1753 //
1754 iter.setList(placed);
1755 while ((n=(Node*)iter.getNext())) {
1756 NodeInfo* linfo = (NodeInfo*)n->getLayoutInformation();
1757 int x,y;
1758 ASSERT (linfo->isPositioned());
1759 linfo->getXYPosition(x,y);
1760 # if defined(DEBUG_PLACEMENT)
1761 if (debug)
1762 fprintf (stdout, "%s[%d] moving %s from %d,%d to %d,%d\n",
1763 __FILE__,__LINE__,n->getNameString(),x,y,x+xdelta,y+ydelta);
1764 # endif
1765 linfo->setProposedLocation(x+xdelta, y+ydelta);
1766 }
1767 group->initialize(&placed);
1768 }
1769
prepareAnnotationPlacement(List & decorators,Node * reflow[],int reflow_count,const List & all_decorators,WorkSpace * workSpace,int & widest,int & tallest)1770 void GraphLayout::prepareAnnotationPlacement(List& decorators, Node* reflow[], int reflow_count, const List& all_decorators, WorkSpace* workSpace, int& widest, int& tallest)
1771 {
1772 //
1773 // exclude the nodes that aren't in the current page
1774 //
1775 ListIterator decs;
1776 VPEAnnotator* dec;
1777 decs.setList((List&)all_decorators);
1778 while ((dec=(VPEAnnotator*)decs.getNext())) {
1779 if (dec->getWorkSpace() != workSpace) continue;
1780 decorators.appendElement(dec);
1781 AnnotationInfo* ai = new AnnotationInfo();
1782 ai->initialize(dec);
1783 ai->findNearestNode(reflow, reflow_count);
1784
1785 int w,h;
1786 ai->getXYSize(w,h);
1787 NodeDistance* nd = ai->nearby[0];
1788 if (nd->getLocation() & DECORATOR_LEFT) {
1789 if (w > widest) widest = w;
1790 }
1791 if (nd->getLocation() & DECORATOR_ABOVE) {
1792 if (h > tallest) tallest = h;
1793 }
1794 }
1795 }
1796
~AnnotationInfo()1797 AnnotationInfo::~AnnotationInfo()
1798 {
1799 if (this->nearby) {
1800 int i;
1801 for (i=0; i<this->nearbyCnt; i++)
1802 delete this->nearby[i];
1803 free(this->nearby);
1804 }
1805 }
1806 //
1807 // Distance between 2 rectangles is the vertical distance between 2 horizontal
1808 // line segments or horizontal distance between 2 vertical line segments
1809 // if they overlap. If the line segments don't overlap, then the distance
1810 // is the smallest of the 4 distances between the the endpoints of the line
1811 // segments.
1812 //
findNearestNode(Node * reflow[],int reflow_count)1813 void AnnotationInfo::findNearestNode(Node* reflow[], int reflow_count)
1814 {
1815 int i;
1816 ASSERT (this->isInitialized());
1817
1818 int x1 = this->orig_x;
1819 int x2 = this->orig_x + (this->w-1);
1820 int y1 = this->orig_y;
1821 int y2 = this->orig_y + (this->h-1);
1822
1823 this->nearby = (NodeDistance**)malloc(sizeof(NodeDistance*) * reflow_count);;
1824 this->nearbyCnt = reflow_count;
1825
1826 int min_dist = 999999;
1827 for (i=0; i<reflow_count; i++) {
1828 Node* n = reflow[i];
1829 LayoutInfo* linfo = (LayoutInfo*)n->getLayoutInformation();
1830 int nx1,ny1,nw,nh;
1831 linfo->getOriginalXYPosition(nx1,ny1);
1832 linfo->getXYSize(nw,nh);
1833 int nx2 = nx1+(nw-1);
1834 int ny2 = ny1+(nh-1);
1835
1836 int vdist = 999999;
1837 int hdist = 999999;
1838
1839 // vertical edges
1840 int hloc=0;
1841 if (x1 > nx2) {
1842 hloc = DECORATOR_RIGHT;
1843 if ((y1>=ny1) && (y1<=ny2)) {
1844 hdist = x1-nx2;
1845 } else if ((y2>=ny1) && (y2<=ny2)) {
1846 hdist = x1-nx2;
1847 } else if ((y1<=ny1) && (y2>=ny2)) {
1848 hdist = x1-nx2;
1849 } else if (y1>ny2) {
1850 hdist = (int)sqrt((double)((x1-nx2)*(x1-nx2) + (y1-ny2)*(y1-ny2)));
1851 hloc|= DECORATOR_BELOW;
1852 } else {
1853 hdist = (int)sqrt((double)((x1-nx2)*(x1-nx2) + (y2-ny1)*(y2-ny1)));
1854 hloc|= DECORATOR_ABOVE;
1855 }
1856 } else if (x2 < nx1) {
1857 hloc = DECORATOR_LEFT;
1858 if ((y1>=ny1) && (y1<=ny2)) {
1859 hdist = nx1-x2;
1860 } else if ((y2>=ny1) && (y2<=ny2)) {
1861 hdist = nx1-x2;
1862 } else if ((y1<=ny1) && (y2>=ny2)) {
1863 hdist = nx1-x2;
1864 } else if (y1>ny2) {
1865 hdist = (int)sqrt((double)((x2-nx1)*(x2-nx1) + (y1-ny2)*(y1-ny2)));
1866 hloc|= DECORATOR_BELOW;
1867 } else {
1868 hdist = (int)sqrt((double)((x2-nx1)*(x2-nx1) + (y2-ny1)*(y2-ny1)));
1869 hloc|= DECORATOR_ABOVE;
1870 }
1871 }
1872
1873 // horizontal edges
1874 int vloc=0;
1875 if (y1 > ny2) {
1876 vloc = DECORATOR_BELOW;
1877 if ((x1>=nx1) && (x1<=nx2)) {
1878 vdist = y1-ny2;
1879 } else if ((x2>=nx1) && (x2<=nx2)) {
1880 vdist = y1-ny2;
1881 } else if ((x1<=nx1) && (x2>=nx2)) {
1882 vdist = y1-ny2;
1883 } else if (x1 > nx2) {
1884 vdist = (int)sqrt((double)((x1-nx2)*(x1-nx2) + (y1-ny2)*(y1-ny2)));
1885 vloc|= DECORATOR_RIGHT;
1886 } else {
1887 vdist = (int)sqrt((double)((x2-nx1)*(x2-nx1) + (y1-ny2)*(y1-ny2)));
1888 vloc|= DECORATOR_LEFT;
1889 }
1890 } else if (y2 < ny1) {
1891 vloc = DECORATOR_ABOVE;
1892 if ((x1>=nx1) && (x1<=nx2)) {
1893 vdist = ny1-y2;
1894 } else if ((x2>=nx1) && (x2<=nx2)) {
1895 vdist = ny1-y2;
1896 } else if ((x1<=nx1) && (x2>=nx2)) {
1897 vdist = ny1-y2;
1898 } else if (x1 > nx2) {
1899 vdist = (int)sqrt((double)((x1-nx2)*(x1-nx2) + (y2-ny1)*(y2-ny1)));
1900 vloc|= DECORATOR_RIGHT;
1901 } else {
1902 vdist = (int)sqrt((double)((x2-nx1)*(x2-nx1) + (y2-ny1)*(y2-ny1)));
1903 vloc|= DECORATOR_LEFT;
1904 }
1905 }
1906
1907 if (hdist < vdist) this->nearby[i] = new NodeDistance (n, hdist, hloc);
1908 else this->nearby[i] = new NodeDistance (n, vdist, vloc);
1909 }
1910 qsort (this->nearby, this->nearbyCnt, sizeof(NodeDistance*), AnnotationInfo::SortByDistance);
1911 }
1912
ArcComparator(const void * a,const void * b)1913 int GraphLayout::ArcComparator(const void *a, const void* b)
1914 {
1915 Ark** aptr = (Ark**)a;
1916 Ark** bptr = (Ark**)b;
1917 Ark* aarc = *aptr;
1918 Ark* barc = *bptr;
1919
1920 int ahops,bhops;
1921 Node *src, *dst;
1922 int dummy;
1923
1924 src = aarc->getSourceNode(dummy);
1925 dst = aarc->getDestinationNode(dummy);
1926 NodeInfo* sinfo = (NodeInfo*)src->getLayoutInformation();
1927 NodeInfo* dinfo = (NodeInfo*)dst->getLayoutInformation();
1928 ahops = dinfo->getGraphDepth() - sinfo->getGraphDepth();
1929
1930 src = barc->getSourceNode(dummy);
1931 dst = barc->getDestinationNode(dummy);
1932 sinfo = (NodeInfo*)src->getLayoutInformation();
1933 dinfo = (NodeInfo*)dst->getLayoutInformation();
1934 bhops = dinfo->getGraphDepth() - sinfo->getGraphDepth();
1935
1936 if (ahops < bhops) return -1;
1937 if (ahops > bhops) return -1;
1938 return 0;
1939 }
1940
SortByDistance(const void * a,const void * b)1941 int AnnotationInfo::SortByDistance(const void *a, const void* b)
1942 {
1943 NodeDistance** aptr = (NodeDistance**)a;
1944 NodeDistance** bptr = (NodeDistance**)b;
1945 NodeDistance* aNd = (NodeDistance*)*aptr;
1946 NodeDistance* bNd = (NodeDistance*)*bptr;
1947 if (aNd->getDistance() < bNd->getDistance()) return -1;
1948 if (aNd->getDistance() > bNd->getDistance()) return 1;
1949 return 0;
1950 }
1951
1952 int AnnotationInfo::NextX;
1953 int AnnotationInfo::NextY;
1954
reposition(Node * reflow[],int reflow_count,List & others)1955 void AnnotationInfo::reposition(Node* reflow[], int reflow_count, List& others)
1956 {
1957 // spot sequence
1958 int spots[] = {
1959 DECORATOR_RIGHT,
1960 DECORATOR_RIGHT | DECORATOR_ABOVE,
1961 DECORATOR_RIGHT | DECORATOR_BELOW,
1962 DECORATOR_LEFT,
1963 DECORATOR_LEFT | DECORATOR_ABOVE,
1964 DECORATOR_LEFT | DECORATOR_BELOW,
1965 DECORATOR_ABOVE,
1966 DECORATOR_BELOW
1967 };
1968 int spot_cnt = sizeof(spots) / sizeof(int);
1969
1970 boolean did_it = FALSE;
1971
1972 int node_to_try = 0;
1973 while ((!did_it) && (node_to_try < this->nearbyCnt)) {
1974
1975 NodeDistance* nd = this->nearby[node_to_try];
1976 Node* n = nd->getNode();
1977 int loc = nd->getLocation();
1978 LayoutInfo* linfo = (LayoutInfo*)n->getLayoutInformation();
1979 int nodex, nodey;
1980 int nodew, nodeh;
1981 linfo->getXYPosition(nodex,nodey);
1982 linfo->getXYSize(nodew,nodeh);
1983
1984 int spot_index = 0;
1985 while (spots[spot_index] != loc) spot_index++;
1986
1987 int tries = 0;
1988 while (tries < spot_cnt) {
1989 int location = spots[spot_index];
1990 int x = nodex + ((nodew-this->w)/2);
1991 int y = nodey + ((nodeh-this->h)/2);
1992 if (location & DECORATOR_ABOVE) {
1993 y = nodey - (1+this->h);
1994 } else if (location & DECORATOR_BELOW) {
1995 y = nodey + nodeh + 1;
1996 }
1997 if (location & DECORATOR_LEFT) {
1998 x = nodex - (1+this->w);
1999 } else if (location & DECORATOR_RIGHT) {
2000 x = nodex + 1 + nodew;
2001 }
2002
2003 if ((x>0) && (y>0) &&
2004 (GraphLayout::CanMoveTo(this, x, y, reflow, reflow_count, &others))) {
2005 this->setProposedLocation (x, y);
2006 did_it = TRUE;
2007 break;
2008 }
2009 tries++;
2010 spot_index++;
2011 if (spot_index==spot_cnt) spot_index = 0;
2012 }
2013 node_to_try++;
2014 }
2015
2016 if (!did_it) {
2017 this->setProposedLocation (AnnotationInfo::NextX, AnnotationInfo::NextY);
2018 AnnotationInfo::NextY+= this->h+1;
2019 }
2020 }
2021
initialize(List * nodes)2022 void LayoutGroup::initialize(List* nodes)
2023 {
2024 ListIterator iter(*nodes);
2025 Node* n;
2026 while ((n=(Node*)iter.getNext())) {
2027 NodeInfo* linfo = (NodeInfo*)n->getLayoutInformation();
2028 int x,y,w,h;
2029 linfo->getOriginalXYPosition(x,y);
2030 linfo->getXYSize(w,h);
2031 if (x < this->orig_x1) this->orig_x1 = x;
2032 if (y < this->orig_y1) this->orig_y1 = y;
2033 if ((x+w) > this->orig_x2) this->orig_x2 = x+w;
2034 if ((y+h) > this->orig_y2) this->orig_y2 = y+h;
2035 linfo->getXYPosition(x,y);
2036 if (x < this->x1) this->x1 = x;
2037 if (y < this->y1) this->y1 = y;
2038 if ((x+w) > this->x2) this->x2 = x+w;
2039 if ((y+h) > this->y2) this->y2 = y+h;
2040 linfo->setLayoutGroup(this);
2041 }
2042 this->initialized = TRUE;
2043 }
2044
Comparator(const void * a,const void * b)2045 int LayoutGroup::Comparator (const void* a, const void* b)
2046 {
2047 LayoutGroup** aptr = (LayoutGroup**)a;
2048 LayoutGroup** bptr = (LayoutGroup**)b;
2049 LayoutGroup* agroup = *aptr;
2050 LayoutGroup* bgroup = *bptr;
2051
2052 //
2053 // If the height ranges don't overlap then return the one with
2054 // the lower y.
2055 //
2056 if ((agroup->orig_y1 < bgroup->orig_y1) && (agroup->orig_y2 < bgroup->orig_y2))
2057 return -1;
2058 if ((bgroup->orig_y1 < agroup->orig_y1) && (bgroup->orig_y2 < agroup->orig_y2))
2059 return 1;
2060
2061 //
2062 // If the height ranges overlap, then return the one with the
2063 // large x.
2064 //
2065 if ((agroup->orig_x1 < bgroup->orig_x1) && (agroup->orig_x2 < bgroup->orig_x2))
2066 return -1;
2067 if ((bgroup->orig_x1 < agroup->orig_x1) && (bgroup->orig_x2 < agroup->orig_x2))
2068 return 1;
2069
2070 if (((agroup->orig_y1 + agroup->orig_y2)>>1) <
2071 ((bgroup->orig_y1 + bgroup->orig_y2)>>1))
2072 return -1;
2073 if (((agroup->orig_y1 + agroup->orig_y2)>>1) >
2074 ((bgroup->orig_y1 + bgroup->orig_y2)>>1))
2075 return 1;
2076 if (((agroup->orig_x1 + agroup->orig_x2)>>1) <
2077 ((bgroup->orig_x1 + bgroup->orig_x2)>>1))
2078 return -1;
2079 if (((agroup->orig_x1 + agroup->orig_x2)>>1) >
2080 ((bgroup->orig_x1 + bgroup->orig_x2)>>1))
2081 return 1;
2082 return 0;
2083 }
2084
repositionGroups(Node * reflow[],int reflow_count)2085 void GraphLayout::repositionGroups(Node* reflow[], int reflow_count)
2086 {
2087 int i;
2088 LayoutGroup** groups = (LayoutGroup**)malloc(sizeof(LayoutGroup*) *
2089 this->layout_groups.getSize());
2090 ListIterator gi(this->layout_groups);
2091 LayoutGroup* group;
2092 int gcnt = 0;
2093 while ((group=(LayoutGroup*)gi.getNext())) groups[gcnt++] = group;
2094 qsort (groups, gcnt, sizeof(LayoutGroup*), LayoutGroup::Comparator);
2095 int gx=0,gy=0;
2096 int prevy2 = 0;
2097 int dummy;
2098 int w=0,h=0;
2099 int max_height_in_line = 0;
2100
2101 groups[0]->getOriginalXYPosition(dummy, prevy2);
2102 groups[0]->getOriginalXYSize(dummy, h);
2103 prevy2+= h;
2104
2105 w=h=0;
2106 boolean isNewLine = FALSE;
2107 for (i=0; i<gcnt; i++) {
2108 group = groups[i];
2109 int x1,y1;
2110 int ow,oh;
2111 group->getOriginalXYPosition(x1,y1);
2112 group->getOriginalXYSize(ow,oh);
2113 int w,h;
2114 group->getXYSize(w,h);
2115 isNewLine = (y1 > prevy2);
2116 if (isNewLine) {
2117 gx = 0;
2118 gy+=max_height_in_line + GraphLayout::HeightPerLevel;
2119 max_height_in_line = h;
2120 } else if (h>max_height_in_line) {
2121 max_height_in_line = h;
2122 }
2123 group->setProposedLocation(gx,gy);
2124 //printf ("%s[%d] group %d at %d,%d\n", __FILE__,__LINE__,group->getId(), gx, gy);
2125 gx+= w + GraphLayout::GroupSpacing;
2126 prevy2 = y1+oh;
2127 }
2128
2129 for (i=0; i<reflow_count; i++) {
2130 Node* n = reflow[i];
2131 NodeInfo* ninfo = (NodeInfo*)n->getLayoutInformation();
2132 LayoutGroup* group = ninfo->getLayoutGroup();
2133 int x,y;
2134 group->getProposedLocation (x,y);
2135 int x1,y1;
2136 group->getXYPosition (x1,y1);
2137 int nx,ny;
2138 ninfo->getXYPosition (nx, ny);
2139 ninfo->setProposedLocation (nx+x-x1, ny+y-y1);
2140 }
2141 free(groups);
2142 }
2143
~NodeInfo()2144 NodeInfo::~NodeInfo()
2145 {
2146 if (this->connected_nodes) {
2147 if (this->owns_list)
2148 delete this->connected_nodes;
2149 }
2150 }
2151
setConnectedNodes(List * list)2152 void NodeInfo::setConnectedNodes(List* list)
2153 {
2154 if (this->owns_list) return ;
2155 this->connected_nodes = list;
2156 }
getConnectedNodes(Node * n)2157 List* NodeInfo::getConnectedNodes(Node* n)
2158 {
2159 if (this->connected_nodes) return this->connected_nodes;
2160
2161 this->connected_nodes = new List();
2162 this->owns_list = TRUE;
2163 NodeInfo::BuildList (n, this->connected_nodes);
2164 return this->connected_nodes;
2165 }
2166
BuildList(Node * n,List * nodes)2167 void NodeInfo::BuildList(Node* n, List* nodes)
2168 {
2169 if (nodes->isMember(n)) return ;
2170 nodes->appendElement(n);
2171
2172 NodeInfo* info = (NodeInfo*)n->getLayoutInformation();
2173 if (info) info->setConnectedNodes(nodes);
2174
2175 int input_count = n->getInputCount();
2176 int input;
2177 int dummy;
2178 for (input=1; input<=input_count; input++) {
2179 if (!n->isInputVisible(input)) continue;
2180 List* arcs = (List*)n->getInputArks(input);
2181 if (!arcs) continue;
2182 ListIterator iter(*arcs);
2183 Ark* arc;
2184 while ((arc=(Ark*)iter.getNext())) {
2185 Node* source = arc->getSourceNode(dummy);
2186 NodeInfo::BuildList(source, nodes);
2187 }
2188 }
2189 int output_count = n->getOutputCount();
2190 int output;
2191 for (output=1; output<=output_count; output++) {
2192 if (!n->isOutputVisible(output)) continue;
2193 List* arcs = (List*)n->getOutputArks(output);
2194 if (!arcs) continue;
2195 ListIterator iter(*arcs);
2196 Ark* arc;
2197 while ((arc=(Ark*)iter.getNext())) {
2198 Node* destination = arc->getDestinationNode(dummy);
2199 NodeInfo::BuildList(destination, nodes);
2200 }
2201 }
2202 }
2203
countConnectedInputs(Node * n,int input,int & nth_tab)2204 int GraphLayout::countConnectedInputs(Node* n, int input, int& nth_tab)
2205 {
2206 int input_count = n->getInputCount();
2207 int i, connected=0;
2208 nth_tab = 1;
2209 for (i=1; i<=input_count; i++) {
2210 if (!n->isInputVisible(i)) continue;
2211 if (n->isInputConnected(i)) connected++;
2212 if (i<input) nth_tab++;
2213 }
2214 return connected;
2215 }
2216
countConnectedOutputs(Node * n,int output,int & nth_tab)2217 int GraphLayout::countConnectedOutputs (Node* n, int output, int& nth_tab)
2218 {
2219 int output_count = n->getOutputCount();
2220 int i, connected=0;
2221 nth_tab = 1;
2222 for (i=1; i<=output_count; i++) {
2223 if (!n->isOutputVisible(i)) continue;
2224 if (n->isOutputConnected(i)) connected++;
2225 if (i<output) nth_tab++;
2226 }
2227 return connected;
2228 }
2229
2230 //
2231 // 0) Decrement the hop count of any node that is connected to multiple
2232 // non-consecutive input tabs on 1 row.
2233 // 1) Divide all nodes into rows.
2234 // 2) Sort the contents of each row by increasing x coord of the destination.
2235 // 3) Starting with the middle node in the sorted list, position each
2236 // above its destination or in the case of node traveling more than 1 hops,
2237 // above a space between nodes, or place the node directly above its destination
2238 // if that is available.
2239 //
layout(Node * node,GraphLayout * mgr,List & positioned)2240 void LayoutGroup::layout(Node* node, GraphLayout* mgr, List& positioned)
2241 {
2242 positioned.clear();
2243
2244 //
2245 // Starting with all nodes in this layout group, form an
2246 // array that we can sort by hop count. This will be used
2247 // to form rows. A node that is in our group is copied into this
2248 // array if we're going to place the node on this trip up the
2249 // graph. We make this decision based on the node's connectivity
2250 // (and in rare cases by type i.e. Get/Set) We won't place a node
2251 // on this trip if the node ought to be placed during some other
2252 // trip up the graph. I.O.W. if the node has a closer descendant,
2253 // then let that descendant take care of placing the node so that
2254 // the node will be placed in close proximity to the nearby
2255 // relative instead of close proximity to the distant relative.
2256 //
2257 List one_hop_up_nodes;
2258 List one_hop_up;
2259 mgr->getSpecialAncestorsOf(node, one_hop_up_nodes, one_hop_up);
2260 # if defined(DEBUG_PLACEMENT)
2261 if (debug) {
2262 fprintf (stdout, "%s[%d] Placing ancestors of %s:%d\n",
2263 __FILE__,__LINE__,node->getNameString(), node->getInstanceNumber());
2264 ListIterator li(one_hop_up_nodes);
2265 Node* n;
2266 while (n=(Node*)li.getNext()) {
2267 fprintf (stdout, "\t%s:%d\n", n->getNameString(), n->getInstanceNumber());
2268 }
2269 }
2270 # endif
2271 Node** node_array;
2272 node_array = (Node**)malloc((1+one_hop_up_nodes.getSize()) * sizeof(Node*));
2273 int i = 1;
2274 int nodes=0;
2275 int size = one_hop_up_nodes.getSize();
2276 ASSERT (one_hop_up_nodes.getSize() == one_hop_up.getSize());
2277 while (i<=size) {
2278 Node* n = (Node*)one_hop_up_nodes.getElement(i);
2279 node_array[nodes++] = n;
2280 positioned.appendElement(n);
2281 NodeInfo* info = (NodeInfo*)n->getLayoutInformation();
2282 info->setDestination ((Ark*)one_hop_up.getElement(i));
2283 i++;
2284 }
2285 // at this point nodes may or may not be equal to ->getSize()
2286 // add our param manually because it's not one hop up.
2287 node_array[nodes++] = node;
2288 qsort (node_array, nodes, sizeof(Node*), LayoutGroup::SortByHop);
2289
2290 //
2291 // Create one data structure for each row in the array.
2292 // nodes is the wrong number to use here, but rows is guaranteed to
2293 // be <= nodes
2294 LayoutRow** row_array = (LayoutRow**)malloc(sizeof(LayoutRow*) * nodes);
2295 LayoutRow* row = NUL(LayoutRow*);
2296 int prev = -1;
2297 i=0;
2298 int rows = 0;
2299 while (i<nodes) {
2300 node = node_array[i];
2301 NodeInfo* ninfo = (NodeInfo*)node->getLayoutInformation();
2302 if (ninfo->getGraphDepth() != prev) {
2303 row_array[rows++] = row = new LayoutRow(ninfo->getGraphDepth());
2304 }
2305 row->addNode(node);
2306 prev = ninfo->getGraphDepth();
2307 i++;
2308 }
2309 int prev_id;
2310 row_array[0]->sort();
2311 row_array[0]->layout(mgr, prev_id);
2312 SlotList* slots = row_array[0]->getSlotList();
2313 for (i=1; i<rows; i++) {
2314 row_array[i]->sort();
2315 row_array[i]->layout(slots, mgr, prev_id);
2316 slots = row_array[i]->getSlotList();
2317 }
2318
2319 //
2320 // provide additional spacing for crowded situations
2321 //
2322 this->straightenArcs(row_array, rows);
2323
2324 for (i=0; i<rows; i++)
2325 delete row_array[i];
2326 free(row_array);
2327 free(node_array);
2328 }
2329
sort()2330 void LayoutRow::sort()
2331 {
2332 int size = this->nodes.getSize();
2333 this->node_array = (Node**)malloc(sizeof(Node*) * size);
2334 ListIterator iter(this->nodes);
2335 Node* n;
2336 int i = 0;
2337 while ((n=(Node*)iter.getNext())) this->node_array[i++] = n;
2338 qsort (this->node_array, size, sizeof(Node*), LayoutRow::SortByDestinationX);
2339 this->sorted_by_destination_x = TRUE;
2340 this->sorted_by_x = FALSE;
2341 # if defined(DEBUG_PLACEMENT)
2342 if (debug) {
2343 fprintf (stdout, "%s[%d] Row %d contains...\n", __FILE__,__LINE__, this->getId());
2344 int j;
2345 for (j=0; j<size; j++) {
2346 fprintf (stdout, "\t%s:%d\n", this->node_array[j]->getNameString(),
2347 this->node_array[j]->getInstanceNumber());
2348 }
2349 }
2350 # endif
2351 }
2352
~LayoutRow()2353 LayoutRow::~LayoutRow()
2354 {
2355 if (this->node_array) free(this->node_array);
2356 }
2357
2358 // Among the nodes we're placing, there are no connected outputs. There
2359 // should be only 1 node in this row.
layout(GraphLayout * mgr,int & previous_id)2360 void LayoutRow::layout(GraphLayout* mgr, int& previous_id)
2361 {
2362 Node* n = this->node_array[0];
2363 NodeInfo* info = (NodeInfo*)n->getLayoutInformation();
2364 if (!info->isPositioned()) info->setProposedLocation(INITIAL_X, INITIAL_Y);
2365 int x,y,w,h;
2366 info->getXYSize(w,h);
2367 info->getXYPosition(x,y);
2368 int left_edge = x - (w>>1);
2369 int right_edge = x + (w>>1);
2370 this->slot_list.clear();
2371 previous_id = this->getId();
2372 }
2373
~SlotList()2374 SlotList::~SlotList()
2375 {
2376 ListIterator li(*this);
2377 Slot* slot;
2378 while ((slot=(Slot*)li.getNext())) delete slot;
2379 }
2380 //
2381 // Return x if that location is empty. Otherwise return the next location to
2382 // the left or right that is empty.
2383 //
isAvailable(int x,boolean left)2384 int SlotList::isAvailable (int x, boolean left)
2385 {
2386 ListIterator li(*this);
2387 Slot *slot, *prev=NUL(Slot*);
2388 while ((slot=(Slot*)li.getNext())) {
2389 if ((slot->min <= x) && (slot->max >= x)) return x;
2390 if ((slot->min >= x) && (left)) {
2391 // this assert says that we can't request something less
2392 // than INT_MIN and that before advancing min past x, we
2393 // would have to have gone thru the loop at least 1 time.
2394 ASSERT(prev);
2395 return prev->max-5;
2396 } else if ((slot->min > x) && (!left)) {
2397 return slot->min+5;
2398 }
2399 prev = slot;
2400 }
2401 li.setList(*this);
2402 # if defined(DEBUG_PLACEMENT)
2403 if (debug) {
2404 fprintf (stdout, "%s[%d] failed %d (%x)\n", __FILE__,__LINE__,x,left);
2405 while (slot=(Slot*)li.getNext()) {
2406 fprintf (stdout, "\t %d <--> %d\n", slot->min, slot->max);
2407 }
2408 }
2409 # endif
2410
2411 ASSERT(FALSE);
2412 return 0;
2413 }
2414
2415 //
2416 // split a Slot
2417 //
occupy(int x,int width)2418 void SlotList::occupy (int x, int width)
2419 {
2420 int size = this->getSize();
2421 Slot *slot;
2422 int found = 0;
2423 int i;
2424 for (i=1; i<=size; i++) {
2425 slot = (Slot*)this->getElement(i);
2426 if ((slot->min <= x) && (slot->max >= x)) {
2427 found = i;
2428 break;
2429 }
2430 }
2431 # if defined(DEBUG_PLACEMENT)
2432 if (debug) {
2433 if (!found) {
2434 for (i=1; i<=size; i++) {
2435 slot = (Slot*)this->getElement(i);
2436 fprintf (stdout, "\t %d - %d\n", slot->min, slot->max);
2437 }
2438 }
2439 }
2440 # endif
2441 ASSERT(found);
2442
2443 int right = x+width;
2444 if (right < slot->max) {
2445 int old_max = slot->max;
2446 slot->max = x;
2447 this->insertElement (new Slot(right, old_max), found+1);
2448 } else {
2449 slot->max = x;
2450 Slot* prev=NUL(Slot*);
2451 List to_delete;
2452 for (i=found+1; i<=size; i++) {
2453 slot = (Slot*)this->getElement(i);
2454 if (slot->max > right) {
2455 if (slot->min < right) {
2456 slot->min = right;
2457 } else {
2458 # if defined(DEBUG_PLACEMENT)
2459 if (debug) {
2460 fprintf (stdout, "%s[%d] Warning, splitting a slot failed\n",
2461 __FILE__,__LINE__);
2462 Slot* s;
2463 for (int j=1; j<=size; j++) {
2464 s = (Slot*)this->getElement(j);
2465 fprintf (stdout, "\t %d to %d\n", s->min, s->max);
2466 }
2467 }
2468 # endif
2469 }
2470 break;
2471 } else {
2472 to_delete.appendElement(slot);
2473 }
2474 prev = slot;
2475 }
2476 ListIterator iter(to_delete);
2477 while ((slot=(Slot*)iter.getNext())) {
2478 ASSERT(this->removeElement(slot));
2479 delete slot;
2480 }
2481 }
2482 }
2483
2484 //
2485 // 3) Starting with the middle node in the sorted list, position each
2486 // above its destination or in the case of node traveling more than 1 hop,
2487 // above a space between nodes, or place the node directly above its destination
2488 // if that is available.
2489 //
layout(SlotList * slots,GraphLayout * mgr,int & previous_id)2490 void LayoutRow::layout(SlotList* slots, GraphLayout* mgr, int& previous_id)
2491 {
2492 int size = this->nodes.getSize();
2493 int middle = (size>>1);
2494 int i,left_edge,right_edge;
2495
2496
2497 //
2498 // The decision to err on the side of {left,right}-wards movment is made
2499 // based on the location of the destination's input tab. If the tab is more
2500 // than half-way across the standIn, then we favor moving right.
2501 //
2502 NodeInfo* info = (NodeInfo*)this->node_array[middle]->getLayoutInformation();
2503 boolean go_left = this->favorsLeftShift (info->getDestination(), mgr, TRUE);
2504
2505 if (go_left) {
2506 left_edge = -9999999; // could also be INT_MIN, INT_MAX
2507 right_edge = 9999999;
2508 } else {
2509 left_edge = 9999999; // could also be INT_MIN, INT_MAX
2510 right_edge = -9999999;
2511 }
2512
2513 this->position (this->node_array[middle], left_edge, right_edge, mgr, go_left, slots, previous_id);
2514
2515 int saved_left_edge = left_edge;
2516 int saved_right_edge = right_edge;
2517
2518
2519 // for each node left and right of center
2520 go_left = TRUE;
2521 for (i=middle-1;i>=0;i--) {
2522 this->position (this->node_array[i], left_edge, right_edge, mgr, go_left, slots, previous_id);
2523 }
2524 go_left = FALSE;
2525 left_edge = saved_left_edge;
2526 right_edge = saved_right_edge;
2527 for (i=middle+1;i<size;i++) {
2528 this->position (this->node_array[i], left_edge, right_edge, mgr, go_left, slots, previous_id);
2529 }
2530
2531 previous_id = this->getId();
2532 }
position(Node * n,int & left_edge,int & right_edge,GraphLayout * lay,boolean go_left,SlotList * slots,int previous_id)2533 void LayoutRow::position (Node* n, int& left_edge, int& right_edge,
2534 GraphLayout* lay, boolean go_left, SlotList* slots, int previous_id)
2535 {
2536 int half_tab_width = 9;
2537 int x,y,dx,dy,w,h;
2538 NodeInfo* info = (NodeInfo*)n->getLayoutInformation();
2539 info->getXYSize(w,h);
2540 int offset = 0;
2541 Ark* arc = info->getDestination();
2542 int hop;
2543 int output;
2544 arc->getSourceNode(output);
2545
2546 StandIn *si = n->getStandIn();
2547 int tabx = si->getOutputParameterTabX(output);
2548 int xcoord_of_line = info->getDestinationLocation(hop);
2549 ASSERT (hop > info->getGraphDepth());
2550 if (go_left) {
2551 int desired_right_edge = xcoord_of_line + w - (tabx+half_tab_width);
2552 if ((left_edge-REQUIRED_HSEP) > desired_right_edge) {
2553 } else if ((left_edge-GraphLayout::NodeSpacing) < desired_right_edge) {
2554 offset = (left_edge-GraphLayout::NodeSpacing) - desired_right_edge;
2555 }
2556 } else {
2557 int desired_left_edge = xcoord_of_line - (half_tab_width + tabx);
2558 if ((right_edge+REQUIRED_HSEP) < desired_left_edge) {
2559 } else if ((right_edge+GraphLayout::NodeSpacing) > desired_left_edge) {
2560 offset = (right_edge+GraphLayout::NodeSpacing) - desired_left_edge;
2561 }
2562 }
2563 //
2564 // If we can make the arc straight, then notify the destination that
2565 // he has achieved straightness and doesn't need to have his incoming
2566 // arcs further straightened, which will happen post-layout in
2567 // LayoutGroup::straightenArcs();
2568 //
2569 int dummy;
2570 Node* dest = arc->getDestinationNode(dummy);
2571 NodeInfo* dinfo = (NodeInfo*)dest->getLayoutInformation();
2572
2573 int target_x = xcoord_of_line + offset;
2574 // If we're wired to something on the previous row which is usually 1 hop down.
2575 // It could also be that the 2 nodes could be like Supervise{State,Window} with
2576 // much different hop counts but with no intervening rows.
2577 if ((hop == (info->getGraphDepth()+1)) || (hop == previous_id)) {
2578 if (lay->positionSource(info->getDestination(),target_x)) {
2579 # if defined(DEBUG_PLACEMENT)
2580 if (debug) {
2581 fprintf (stdout, "%s[%d] %s:%d collided\n", __FILE__,__LINE__,
2582 n->getNameString(), n->getInstanceNumber());
2583 }
2584 # endif
2585 }
2586 dinfo->registerStraightArc(offset, arc);
2587 } else {
2588 int available_x = slots->isAvailable(target_x, go_left);
2589 if (lay->positionSource(info->getDestination(),available_x)) {
2590 # if defined(DEBUG_PLACEMENT)
2591 if (debug) {
2592 fprintf (stdout, "%s[%d] %s:%d collided\n", __FILE__,__LINE__,
2593 n->getNameString(), n->getInstanceNumber());
2594 }
2595 # endif
2596 }
2597 dinfo->registerStraightArc(available_x-xcoord_of_line, arc);
2598 }
2599 info->getXYPosition(x,y);
2600 # if defined(DEBUG_PLACEMENT)
2601 if (debug) {
2602 fprintf (stdout, "%s[%d] %s:%d placed at %d,%d occupies %d to %d\n",
2603 __FILE__,__LINE__,n->getNameString(),n->getInstanceNumber(),x,y,
2604 x, x+w);
2605 }
2606 # endif
2607 // What's the meaning of the -3,+6? Really only the space from x to x+w is
2608 // occupied. Nevertheless, the line router won't place a line closer to
2609 // a node than 3 pixels. So we pretend that we're occupying 3 pixels
2610 // either side of the left and right edges. That way we won't try to
2611 // use those areas in a subsequent pass.
2612 this->slot_list.occupy (x-3, w+6);
2613 left_edge = x;
2614 right_edge = x+w;
2615 }
2616
getDestinationLocation(int & hop)2617 int NodeInfo::getDestinationLocation (int& hop)
2618 {
2619 int input;
2620 Node* dst = this->destination->getDestinationNode(input);
2621 NodeInfo* dinfo = (NodeInfo*)dst->getLayoutInformation();
2622 hop = dinfo->getGraphDepth();
2623 StandIn* si = dst->getStandIn();
2624 int half_tab_width = 9;
2625 int dx,dy;
2626 dinfo->getXYPosition(dx,dy);
2627 int tabx = si->getInputParameterTabX(input);
2628 return tabx + dx + half_tab_width;
2629 }
2630
SortByHop(const void * a,const void * b)2631 int LayoutGroup::SortByHop (const void* a, const void* b)
2632 {
2633 Node** aptr = (Node**)a;
2634 Node** bptr = (Node**)b;
2635 Node* anode = (Node*)*aptr;
2636 Node* bnode = (Node*)*bptr;
2637 NodeInfo* ainfo = (NodeInfo*)anode->getLayoutInformation();
2638 NodeInfo* binfo = (NodeInfo*)bnode->getLayoutInformation();
2639
2640 // descending order of hop count
2641 int ahops = ainfo->getGraphDepth();
2642 int bhops = binfo->getGraphDepth();
2643 if (ahops > bhops) return -1;
2644 if (ahops < bhops) return 1;
2645 return 0;
2646 }
2647
SortByDestinationX(const void * a,const void * b)2648 int LayoutRow::SortByDestinationX (const void* a, const void* b)
2649 {
2650 int ainput,binput,dummy;
2651 Node** aptr = (Node**)a;
2652 Node** bptr = (Node**)b;
2653 Node* anode = (Node*)*aptr;
2654 Node* bnode = (Node*)*bptr;
2655 NodeInfo* ainfo = (NodeInfo*)anode->getLayoutInformation();
2656 NodeInfo* binfo = (NodeInfo*)bnode->getLayoutInformation();
2657
2658 int half_tab_width = 9;
2659 int ai = ainfo->getDestinationLocation(dummy);// + half_tab_width;
2660 int bi = binfo->getDestinationLocation(dummy);// + half_tab_width;
2661 if (ai < bi) return -1;
2662 if (ai > bi) return 1;
2663
2664 int ahops = ainfo->getGraphDepth();
2665 int bhops = binfo->getGraphDepth();
2666 if (ahops > bhops) return -1;
2667 if (ahops < bhops) return 1;
2668
2669 return 0;
2670 }
2671
SortByX(const void * a,const void * b)2672 int LayoutRow::SortByX (const void* a, const void* b)
2673 {
2674 int ainput,binput,ax,bx,dummy;
2675 Node** aptr = (Node**)a;
2676 Node** bptr = (Node**)b;
2677 Node* anode = (Node*)*aptr;
2678 Node* bnode = (Node*)*bptr;
2679 NodeInfo* ainfo = (NodeInfo*)anode->getLayoutInformation();
2680 NodeInfo* binfo = (NodeInfo*)bnode->getLayoutInformation();
2681
2682 ainfo->getXYPosition(ax,dummy);
2683 binfo->getXYPosition(bx,dummy);
2684 if (ax < bx) return -1;
2685 if (ax > bx) return 1;
2686 return 0;
2687 }
2688
getSpecialAncestorsOf(Node * root,List & ancestors,List & arcs,boolean last_call)2689 void GraphLayout::getSpecialAncestorsOf (Node* root, List& ancestors, List& arcs, boolean last_call)
2690 {
2691 int input, output;
2692 int input_count = root->getInputCount();
2693 int k;
2694 for (k=1; k<=input_count; k++) {
2695 if (!root->isInputVisible(k)) continue;
2696 List* inputs = (List*)root->getInputArks(k);
2697 if (!inputs) continue;
2698 ListIterator li(*inputs);
2699 Ark* arc;
2700 int dummy;
2701 while ((arc=(Ark*)li.getNext())) {
2702 Node* src = arc->getSourceNode(output);
2703 LayoutInfo* linfo = (LayoutInfo*)src->getLayoutInformation();
2704 if (linfo->isPositioned()) continue;
2705 if (ancestors.isMember(src) == FALSE) {
2706 const char* name = src->getNameString();
2707 if (EqualString("Get",name)||
2708 EqualString("GetGlobal",name)||
2709 EqualString("GetLocal",name)) {
2710 if (output != 2) continue;
2711 arcs.appendElement(arc);
2712 ancestors.appendElement(src);
2713 this->getSpecialAncestorsOf(src, ancestors, arcs, FALSE);
2714 } else if (this->hasNoCloserDescendant(src, root)) {
2715 // only handle distant ancestor nodes if those node don't
2716 // have a more closely related descendant.
2717 arcs.appendElement(arc);
2718 ancestors.appendElement(src);
2719 this->getSpecialAncestorsOf(src, ancestors, arcs, FALSE);
2720 }
2721 }
2722 }
2723 }
2724
2725 if (last_call) {
2726 //
2727 // Examine each arc we've picked. See if each arc ought to be replaced
2728 // with a better choice. A choice is better if the src and dst nodes
2729 // have multiple arcs and it's more near the center of the set of arcs.
2730 //
2731 ListIterator iter(arcs);
2732 List to_add;
2733 List to_remove;
2734 Ark* arc;
2735 while ((arc=(Ark*)iter.getNext())) {
2736 Node* src = arc->getSourceNode(output);
2737 Node* dst = arc->getDestinationNode(input);
2738 int cnt = this->countConnectionsBetween(src, dst);
2739 if (cnt<=2) continue;
2740 int to_skip = cnt>>1;
2741 int skipped = 0;
2742 input_count = dst->getInputCount();
2743 boolean changed = FALSE;
2744 for (input=1; input<=input_count; input++) {
2745 if (!dst->isInputVisible(input)) continue;
2746 List* inputs = (List*)dst->getInputArks(input);
2747 if (!inputs) continue;
2748 ListIterator li(*inputs);
2749 Ark* ia;
2750 while ((ia=(Ark*)li.getNext())) {
2751 int output2;
2752 Node* source = ia->getSourceNode(output2);
2753 if (source == src) {
2754 if (skipped == to_skip) {
2755 if (ia != arc) {
2756 to_add.appendElement(ia);
2757 to_remove.appendElement(arc);
2758 # if defined(DEBUG_PLACEMENT)
2759 if (debug) {
2760 fprintf (stdout, "%s[%d] replacing output %d of %s"
2761 " with output %d\n", __FILE__,__LINE__,
2762 output, src->getNameString(), output2);
2763 }
2764 # endif
2765 }
2766 changed = TRUE;
2767 break;
2768 }
2769 skipped++;
2770 }
2771 }
2772 if (changed) break;
2773 }
2774 }
2775 iter.setList(to_remove);
2776 while ((arc=(Ark*)iter.getNext()))
2777 arcs.removeElement(arc);
2778 iter.setList(to_add);
2779 while (arc=(Ark*)iter.getNext())
2780 arcs.appendElement(arc);
2781 ancestors.clear();
2782 int size = arcs.getSize();
2783 for (int i=1; i<=size; i++) {
2784 arc = (Ark*)arcs.getElement(i);
2785 int dummy;
2786 Node* src = arc->getSourceNode(dummy);
2787 ancestors.appendElement(src);
2788 }
2789 }
2790 }
2791
2792 //
2793 // If we can't decide left vs right, then return the default value
2794 //
favorsLeftShift(Ark * arc,GraphLayout * mgr,boolean default_value)2795 boolean LayoutRow::favorsLeftShift (Ark* arc, GraphLayout* mgr, boolean default_value)
2796 {
2797 boolean retval = default_value;
2798
2799 int input, output, nth_tab;
2800 Node* src = arc->getSourceNode(output);
2801 Node* dst = arc->getDestinationNode(input);
2802
2803 int ci = mgr->countConnectedInputs (dst, input, nth_tab);
2804 int input_count = dst->getInputCount();
2805 int visible_input_count = 0;
2806 for (input=1; input<input_count; input++) {
2807 if (dst->isInputVisible(input))
2808 visible_input_count++;
2809 }
2810
2811 if (nth_tab > (ci>>1)) {
2812 retval = FALSE;
2813 } else if (input > (visible_input_count>>1)) {
2814 retval = FALSE;
2815 }
2816
2817 return retval;
2818 }
2819
clear()2820 void SlotList::clear()
2821 {
2822 ListIterator iter(*this);
2823 Slot* slot;
2824 while ((slot=(Slot*)iter.getNext())) delete slot;
2825 this->List::clear();
2826 this->appendElement(new Slot(-999999, 999999));
2827 }
2828
countConnectionsBetween(Node * source,Node * dest,boolean count_consecutive)2829 int GraphLayout::countConnectionsBetween (Node* source, Node* dest, boolean count_consecutive)
2830 {
2831 int output_count = source->getOutputCount();
2832 int output, input;
2833 int connections = 0;
2834 int prev = -1;
2835 for (output=1; output<=output_count; output++) {
2836 if (!source->isOutputVisible(output)) continue;
2837 List* arks = (List*)source->getOutputArks(output);
2838 if (!arks) continue;
2839 ListIterator iter(*arks);
2840 Ark* arc;
2841 while ((arc=(Ark*)iter.getNext())) {
2842 Node* destination = arc->getDestinationNode(input);
2843 if (destination == dest) {
2844 if (count_consecutive) {
2845 connections++;
2846 } else {
2847 int nth_tab;
2848 this->countConnectedInputs(destination, input, nth_tab);
2849 if (nth_tab != (prev+1)) {
2850 connections++;
2851 }
2852 prev = nth_tab;
2853 }
2854 }
2855 }
2856 }
2857 return connections;
2858 }
2859
2860 //
2861 // Starting with the nodes at the top, try to shift them out farther in order
2862 // to straighten some arc. Nodes that get in the way are in turn
2863 // shifted outward providing more space.
2864 //
straightenArcs(LayoutRow * row_array[],int rows)2865 boolean LayoutGroup::straightenArcs (LayoutRow* row_array[], int rows)
2866 {
2867 boolean retval = FALSE;
2868
2869 for (int i=rows-1; i>=0; i--) {
2870 LayoutRow* row = row_array[i];
2871 row->straightenArcs(retval);
2872 }
2873 return retval;
2874 }
2875
straightenArcs(boolean & changes_made_on_previous_row)2876 void LayoutRow::straightenArcs(boolean& changes_made_on_previous_row)
2877 {
2878 if (!this->sorted_by_x) {
2879 qsort (this->node_array, this->nodes.getSize(),
2880 sizeof(Node*), LayoutRow::SortByX);
2881 this->sorted_by_x = TRUE;
2882 this->sorted_by_destination_x = FALSE;
2883 }
2884
2885 boolean changes_made = changes_made_on_previous_row;
2886 boolean changes_made_on_current_row = FALSE;
2887
2888 int input, output;
2889 int size = this->nodes.getSize();
2890 int midpt = size>>1;
2891 for (int j=0; j<size; j++) {
2892 Node* dest = this->node_array[j];
2893 NodeInfo* dinfo = (NodeInfo*)dest->getLayoutInformation();
2894 Ark* arc;
2895 int offset;
2896 arc = dinfo->hasStraightArc(offset);
2897 if (arc == NUL(Ark*)) continue;
2898
2899 //
2900 // problem right here is that, the value we've recorded for
2901 // offset might not be correct any more if the src node was
2902 // moved in an earlier pass. FIXME
2903 //
2904 if ((!changes_made) && (!offset)) continue;
2905
2906 ASSERT (dest == arc->getDestinationNode(input));
2907 Node* src = arc->getSourceNode(output);
2908 NodeInfo* sinfo = (NodeInfo*)src->getLayoutInformation();
2909
2910 int sx,sy;
2911 sinfo->getXYPosition(sx,sy);
2912 int dx,dy;
2913 dinfo->getXYPosition(dx,dy);
2914
2915 //
2916 // Compute the amount the dest has to shift in order to straighten
2917 // the arc. This is based on the the locations of the standIns and
2918 // the relative locations of their input and output tabs.
2919 //
2920 StandIn* ssi = src->getStandIn();
2921 StandIn* dsi = dest->getStandIn();
2922 int dtx = dsi->getInputParameterTabX(input);
2923 int stx = ssi->getOutputParameterTabX(output);
2924
2925 int delta = - ((dx+dtx) - (sx + stx));
2926 int end, incr;
2927
2928 if (delta < 0) {
2929 end = -1;
2930 incr = -1;
2931 } else if (delta > 0) {
2932 end = size;
2933 incr = 1;
2934 } else {
2935 dinfo->registerStraightArc (0, arc);
2936 continue;
2937 }
2938
2939 for (int k=j; k!=end; k+=incr) {
2940 Node* n = this->node_array[k];
2941 NodeInfo* info = (NodeInfo*)n->getLayoutInformation();
2942 # if defined(DEBUG_PLACEMENT)
2943 if (debug) {
2944 if (k==j) {
2945 fprintf (stdout, "%s[%d] moving %s:%d %d row(%d)\n",
2946 __FILE__,__LINE__, n->getNameString(),
2947 n->getInstanceNumber(), delta, this->getId());
2948 } else {
2949 fprintf (stdout, "\tmoving %s:%d \n",
2950 n->getNameString(), n->getInstanceNumber());
2951 }
2952 }
2953 # endif
2954 info->getXYPosition(dx,dy);
2955 int dx2 = dx + delta;
2956 info->setProposedLocation (dx2, dy);
2957
2958
2959 //
2960 // continue scooting neighbors over until we have created enough
2961 // whitespace so that it's no longer necessary.
2962 //
2963 boolean keep_scooting = TRUE;
2964
2965 int nextk = k+incr;
2966 if (nextk != end) {
2967 Node* nn = this->node_array[nextk];
2968 int nnx,nny;
2969 NodeInfo* nninfo = (NodeInfo*)nn->getLayoutInformation();
2970 nninfo->getXYPosition(nnx,nny);
2971 if (incr > 0) {
2972 // does the right edge of n encroach on the left edge of nn
2973 int dw,dh;
2974 info->getXYSize(dw,dh);
2975 if ((dx2 + dw + REQUIRED_HSEP) < nnx)
2976 keep_scooting = FALSE;
2977 } else {
2978 // does the left edge of n encroach on the right edge of nn
2979 int nnw,nnh;
2980 nninfo->getXYSize(nnw,nnh);
2981 if ((nnx+nnw+REQUIRED_HSEP) < dx2)
2982 keep_scooting = FALSE;
2983 }
2984 }
2985
2986 if (!keep_scooting) break;
2987 }
2988 changes_made = TRUE;
2989 changes_made_on_current_row = TRUE;
2990 }
2991 if (!changes_made_on_current_row) changes_made = FALSE;
2992 }
2993
hasStraightArc(int & offset)2994 Ark* NodeInfo::hasStraightArc(int& offset)
2995 {
2996 if (!this->straightness_set) return NUL(Ark*);
2997 offset = this->offset_for_straightness;
2998 return this->straightness_opportunity;
2999 }
3000
registerStraightArc(int offset,Ark * arc)3001 void NodeInfo::registerStraightArc(int offset, Ark* arc)
3002 {
3003 int dummy;
3004 Node* src;
3005 NodeInfo* sinfo;
3006
3007 //
3008 // The this is the info for the node at the destination end of arc
3009 //
3010 if (!this->straightness_set) {
3011 this->offset_for_straightness = offset;
3012 this->straightness_opportunity = arc;
3013 this->straightness_set = TRUE;
3014
3015 src = arc->getSourceNode(dummy);
3016 sinfo = (NodeInfo*)src->getLayoutInformation();
3017 sinfo->setStraightnessDestination(this);
3018
3019 return ;
3020 }
3021 if (abs(offset) < abs(this->offset_for_straightness)) {
3022 this->offset_for_straightness = offset;
3023
3024 src = arc->getSourceNode(dummy);
3025 sinfo = (NodeInfo*)src->getLayoutInformation();
3026 sinfo->setStraightnessDestination(NUL(NodeInfo*));
3027 this->straightness_opportunity = arc;
3028
3029 // find the NodeInfo at the src end of the arc.
3030 // Notify that object that this of a partnership.
3031 src = arc->getSourceNode(dummy);
3032 sinfo = (NodeInfo*)src->getLayoutInformation();
3033 sinfo->setStraightnessDestination(this);
3034 }
3035 }
3036
3037 //
3038 // The purpose for shiftStraightArc(), setStraightnessDestination(),
3039 // and a virtual setProposedLocation() is to modify our notion
3040 // of straightness so that if a src is moved in order to straighten
3041 // one of its incoming arcs, we'll notify downstream arcs that had
3042 // once believed themselves to be straight, that they're no longer
3043 // straight. All these methods are used as a result of ::straightenArcs().
3044 // Note that by storing a reference to a NodeInfo* inside the
3045 // NodeInfo* object we create a potential crash bug if that NodeInfo*
3046 // is deleted.
3047 //
shiftStraightArc(int dx)3048 void NodeInfo::shiftStraightArc(int dx)
3049 {
3050 if (!this->straightness_set) return ;
3051 this->offset_for_straightness+= dx;
3052 }
3053
setProposedLocation(int x,int y)3054 void NodeInfo::setProposedLocation (int x, int y)
3055 {
3056 int dx;
3057 boolean was_positioned = this->positioned_yet;
3058
3059 if (was_positioned) dx = x - this->x;
3060
3061 this->LayoutInfo::setProposedLocation(x,y);
3062 if (!was_positioned) return ;
3063
3064 //
3065 // If the destination is using this node as it's
3066 // targeted straight arc and the destination thought
3067 // that the arc was stragith, then ensure that the
3068 // destination knows that it's arc isn't straight
3069 // any longer.
3070 //
3071 if (this->straightness_destination)
3072 this->straightness_destination->shiftStraightArc(dx);
3073 }
3074
setStraightnessDestination(NodeInfo * dest)3075 void NodeInfo::setStraightnessDestination(NodeInfo* dest)
3076 {
3077 this->straightness_destination = dest;
3078 }
3079