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