1 /* -*- tab-width: 4 -*-
2  *
3  * Electric(tm) VLSI Design System
4  *
5  * File: conlay.c
6  * Hierarchical layout constraint system
7  * Written by: Steven M. Rubin, Static Free Software
8  *
9  * Copyright (c) 2001 Static Free Software
10  *
11  * Electric(tm) is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * Electric(tm) is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with Electric(tm); see the file COPYING.  If not, write to
23  * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
24  * Boston, Mass 02111-1307, USA.
25  *
26  * Static Free Software
27  * 4119 Alpine Road
28  * Portola Valley, California 94028
29  * info@staticfreesoft.com
30  */
31 #include "global.h"
32 #include "conlay.h"
33 #include "database.h"
34 #include "usr.h"
35 
36 /* working memory for "cla_modwithin()" */
37 static INTBIG    cla_workingarccount = 0;
38 static ARCINST **cla_workingarcs;
39 
40 /* routines referenced in "conlin.c"*/
41 void cla_oldportposition(NODEINST*, PORTPROTO*, INTBIG*, INTBIG*);
42 void cla_adjustmatrix(NODEINST*, PORTPROTO*, XARRAY);
43 
44 /* prototypes for local routines */
45 static BOOLEAN cla_modifynodeinst(NODEINST *ni, INTBIG deltalx, INTBIG deltaly, INTBIG deltahx,
46 				INTBIG deltahy, INTBIG dangle, INTBIG dtrans, BOOLEAN announce);
47 static BOOLEAN cla_modnodearcs(NODEINST*, INTBIG, INTBIG);
48 static void cla_modwithin(NODEINST*, INTBIG, INTBIG);
49 static BOOLEAN cla_modrigid(NODEINST*, INTBIG, INTBIG);
50 static BOOLEAN cla_modflex(NODEINST*, INTBIG, INTBIG);
51 static void cla_nonorthogfixang(ARCINST*, INTBIG, INTBIG, NODEINST*, INTBIG[2], INTBIG[2]);
52 static void cla_ensurearcinst(ARCINST*, INTBIG);
53 static void cla_updatearc(ARCINST*, INTBIG, INTBIG, INTBIG, INTBIG, INTBIG);
54 static void cla_domovearcinst(ARCINST*, INTBIG, INTBIG, INTBIG, INTBIG, INTBIG);
55 static void cla_makeoldrot(NODEINST*, XARRAY);
56 static void cla_makeoldtrans(NODEINST*, XARRAY);
57 static void cla_computecell(NODEPROTO*, BOOLEAN);
58 static BOOLEAN cla_lookdown(NODEPROTO*);
59 
60 #define CLA_DEBUG 1			/* comment out for normal life */
61 
62 /* command completion table for this constraint solver */
63 static KEYWORD layconopt[] =
64 {
65 	{x_("debug-toggle"),      0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
66 	TERMKEY
67 };
68 COMCOMP cla_layconp = {layconopt, NOTOPLIST, NONEXTLIST, NOPARAMS,
69 	0, x_(" \t"), M_("layout constraint system option"), 0};
70 
71 INTBIG cla_changeclock;
72 #ifdef CLA_DEBUG
73 static BOOLEAN cla_conlaydebug;
74 #endif
75 CONSTRAINT *cla_constraint;	/* the constraint object for this solver */
76 
77 /******************** CONSTRAINT SYSTEM HOOKS *************************/
78 
cla_layconinit(CONSTRAINT * con)79 void cla_layconinit(CONSTRAINT *con)
80 {
81 	/* only function during pass 1 of initialization */
82 	if (con == NOCONSTRAINT) return;
83 	cla_constraint = con;
84 	cla_changeclock = 10;
85 #ifdef CLA_DEBUG
86 	cla_conlaydebug = FALSE;
87 #endif
88 }
89 
cla_layconterm(void)90 void cla_layconterm(void)
91 {
92 	if (cla_workingarccount > 0) efree((CHAR *)cla_workingarcs);
93 	cla_workingarccount = 0;
94 }
95 
cla_layconsetmode(INTBIG count,CHAR * par[])96 void cla_layconsetmode(INTBIG count, CHAR *par[])
97 {
98 	REGISTER INTBIG l;
99 	REGISTER CHAR *pp;
100 
101 	if (count == 0)
102 	{
103 		ttyputusage(x_("constraint tell layout OPTION"));
104 		return;
105 	}
106 
107 	if (el_curconstraint != cla_constraint)
108 	{
109 		ttyputerr(M_("Must first switch to this solver with 'constraint use'"));
110 		return;
111 	}
112 
113 	l = estrlen(pp = par[0]);
114 
115 	/* debugging switch */
116 	if (namesamen(pp, x_("debug-toggle"), l) == 0 && l >= 1)
117 	{
118 #ifdef CLA_DEBUG
119 		cla_conlaydebug = !cla_conlaydebug;
120 		if (cla_conlaydebug) ttyputmsg(M_("Layout constraint debugging on")); else
121 			ttyputmsg(M_("Layout constraint debugging off"));
122 #else
123 		ttyputmsg(M_("Sorry, constraint debugging code has been compiled out"));
124 #endif
125 		return;
126 	}
127 	ttyputbadusage(x_("constraint tell layout"));
128 }
129 
130 /*
131  * the valid "command" is "describearc" which returns a string that describes
132  * the constraints on the arc in "arg1".
133  */
cla_layconrequest(CHAR * command,INTBIG arg1)134 INTBIG cla_layconrequest(CHAR *command, INTBIG arg1)
135 {
136 	REGISTER ARCINST *ai;
137 	REGISTER INTBIG l;
138 	REGISTER void *infstr;
139 
140 	l = estrlen(command);
141 
142 	if (namesamen(command, x_("describearc"), l) == 0)
143 	{
144 		ai = (ARCINST *)arg1;
145 		infstr = initinfstr();
146 		if ((ai->userbits&FIXED) != 0) addtoinfstr(infstr, 'R'); else
147 		{
148 			switch (ai->userbits&(FIXANG|CANTSLIDE))
149 			{
150 				case 0:                addtoinfstr(infstr, 'S');         break;
151 				case FIXANG:           addstringtoinfstr(infstr, x_("FS"));  break;
152 				case CANTSLIDE:        addtoinfstr(infstr, 'X');         break;
153 				case FIXANG|CANTSLIDE: addtoinfstr(infstr, 'F');         break;
154 			}
155 		}
156 		return((INTBIG)returninfstr(infstr));
157 	}
158 	return(0);
159 }
160 
161 /*
162  * routine to do hierarchical update on any cells that changed
163  */
cla_layconsolve(NODEPROTO * np)164 void cla_layconsolve(NODEPROTO *np)
165 {
166 	REGISTER CHANGECELL *cc;
167 	REGISTER CHANGEBATCH *curbatch;
168 	REGISTER CHANGE *c;
169 
170 	/* if only one cell is requested, solve that */
171 	curbatch = db_getcurrentbatch();
172 	if (np != NONODEPROTO)
173 	{
174 		cla_computecell(np, FALSE);
175 	} else
176 	{
177 		/* solve all cells that changed */
178 		if (curbatch != NOCHANGEBATCH)
179 			for(cc = curbatch->firstchangecell; cc != NOCHANGECELL; cc = cc->nextchangecell)
180 				cla_computecell(cc->changecell, cc->forcedlook);
181 	}
182 
183 	if (curbatch == NOCHANGEBATCH) return;
184 	for(c = curbatch->changehead; c != NOCHANGE; c = c->nextchange)
185 		switch (c->changetype)
186 	{
187 		case NODEINSTNEW:
188 		case NODEINSTKILL:
189 		case NODEINSTMOD:
190 			((NODEINST *)c->entryaddr)->changeaddr = (CHAR *)NOCHANGE;
191 			break;
192 		case ARCINSTNEW:
193 		case ARCINSTKILL:
194 		case ARCINSTMOD:
195 			((ARCINST *)c->entryaddr)->changeaddr = (CHAR *)NOCHANGE;
196 			break;
197 		case PORTPROTONEW:
198 		case PORTPROTOKILL:
199 		case PORTPROTOMOD:
200 			((PORTPROTO *)c->entryaddr)->changeaddr = (CHAR *)NOCHANGE;
201 			break;
202 		case NODEPROTONEW:
203 		case NODEPROTOKILL:
204 		case NODEPROTOMOD:
205 			((NODEPROTO *)c->entryaddr)->changeaddr = (CHAR *)NOCHANGE;
206 			break;
207 	}
208 }
209 
210 /*
211  * If an export is created, touch all instances of the cell
212  */
cla_layconnewobject(INTBIG addr,INTBIG type)213 void cla_layconnewobject(INTBIG addr, INTBIG type)
214 {
215 	REGISTER NODEPROTO *np;
216 	REGISTER PORTPROTO *pp;
217 	REGISTER NODEINST *ni;
218 
219 	if (type == VPORTPROTO)
220 	{
221 		pp = (PORTPROTO *)addr;
222 		np = pp->parent;
223 		for(ni = np->firstinst; ni != NONODEINST; ni = ni->nextinst)
224 			(void)db_change((INTBIG)ni, OBJECTSTART, VNODEINST, 0, 0, 0, 0, 0);
225 		for(ni = np->firstinst; ni != NONODEINST; ni = ni->nextinst)
226 			(void)db_change((INTBIG)ni, NODEINSTMOD, ni->lowx, ni->lowy,
227 				ni->highx, ni->highy, ni->rotation, ni->transpose);
228 		for(ni = np->firstinst; ni != NONODEINST; ni = ni->nextinst)
229 			(void)db_change((INTBIG)ni, OBJECTEND, VNODEINST, 0, 0, 0, 0, 0);
230 	}
231 }
232 
233 /*
234  * If an export is deleted, touch all instances of the cell
235  */
cla_layconkillobject(INTBIG addr,INTBIG type)236 void cla_layconkillobject(INTBIG addr, INTBIG type)
237 {
238 	REGISTER NODEPROTO *np;
239 	REGISTER PORTPROTO *pp;
240 	REGISTER NODEINST *ni;
241 
242 	if (type == VPORTPROTO)
243 	{
244 		pp = (PORTPROTO *)addr;
245 		np = pp->parent;
246 		for(ni = np->firstinst; ni != NONODEINST; ni = ni->nextinst)
247 		{
248 			(void)db_change((INTBIG)ni, OBJECTSTART, VNODEINST, 0, 0, 0, 0, 0);
249 			(void)db_change((INTBIG)ni, NODEINSTMOD, ni->lowx, ni->lowy,
250 				ni->highx, ni->highy, ni->rotation, ni->transpose);
251 			(void)db_change((INTBIG)ni, OBJECTEND, VNODEINST, 0, 0, 0, 0, 0);
252 		}
253 	}
254 }
255 
256 /*
257  * If an export is renamed, touch all instances of the cell
258  */
cla_layconnewvariable(INTBIG addr,INTBIG type,INTBIG skey,INTBIG stype)259 void cla_layconnewvariable(INTBIG addr, INTBIG type, INTBIG skey, INTBIG stype)
260 {
261 	CHAR *name;
262 	REGISTER NODEPROTO *np;
263 	REGISTER PORTPROTO *pp;
264 	REGISTER NODEINST *ni;
265 
266 	if (type == VPORTPROTO)
267 	{
268 		if ((stype&VCREF) != 0)
269 		{
270 			name = changedvariablename(type, skey, stype);
271 			if (estrcmp(name, x_("protoname")) == 0)
272 			{
273 				pp = (PORTPROTO *)addr;
274 				np = pp->parent;
275 				for(ni = np->firstinst; ni != NONODEINST; ni = ni->nextinst)
276 				{
277 					(void)db_change((INTBIG)ni, OBJECTSTART, VNODEINST, 0, 0, 0, 0, 0);
278 					(void)db_change((INTBIG)ni, NODEINSTMOD, ni->lowx, ni->lowy,
279 						ni->highx, ni->highy, ni->rotation, ni->transpose);
280 					(void)db_change((INTBIG)ni, OBJECTEND, VNODEINST, 0, 0, 0, 0, 0);
281 				}
282 			}
283 		}
284 	}
285 }
286 
287 /*
288  * set layout constraints on arc instance "ai" according to "changetype".
289  * the routine returns true if the arc change is not successful (or already
290  * done)
291  */
cla_layconsetobject(INTBIG addr,INTBIG type,INTBIG changetype,INTBIG changedata)292 BOOLEAN cla_layconsetobject(INTBIG addr, INTBIG type, INTBIG changetype,
293 	INTBIG /*@unused@*/ changedata)
294 {
295 	REGISTER ARCINST *ai;
296 
297 	if ((type&VTYPE) != VARCINST) return(TRUE);
298 	ai = (ARCINST *)addr;
299 	if (ai == NOARCINST) return(TRUE);
300 	switch (changetype)
301 	{
302 		case CHANGETYPERIGID:				/* arc rigid */
303 			if ((ai->userbits & FIXED) != 0) return(TRUE);
304 			(void)setval((INTBIG)ai, VARCINST, x_("userbits"), ai->userbits|FIXED, VINTEGER);
305 			break;
306 		case CHANGETYPEUNRIGID:				/* arc un-rigid */
307 			if ((ai->userbits & FIXED) == 0) return(TRUE);
308 			(void)setval((INTBIG)ai, VARCINST, x_("userbits"), ai->userbits & ~FIXED, VINTEGER);
309 			break;
310 		case CHANGETYPEFIXEDANGLE:			/* arc fixed-angle */
311 			if ((ai->userbits & FIXANG) != 0) return(TRUE);
312 			(void)setval((INTBIG)ai, VARCINST, x_("userbits"), ai->userbits|FIXANG, VINTEGER);
313 			break;
314 		case CHANGETYPENOTFIXEDANGLE:		/* arc not fixed-angle */
315 			if ((ai->userbits & FIXANG) == 0) return(TRUE);
316 			(void)setval((INTBIG)ai, VARCINST, x_("userbits"), ai->userbits & ~FIXANG, VINTEGER);
317 			break;
318 		case CHANGETYPESLIDABLE:			/* arc slidable */
319 			if ((ai->userbits & CANTSLIDE) == 0) return(TRUE);
320 			(void)setval((INTBIG)ai, VARCINST, x_("userbits"), ai->userbits & ~CANTSLIDE, VINTEGER);
321 			break;
322 		case CHANGETYPENOTSLIDABLE:			/* arc nonslidable */
323 			if ((ai->userbits & CANTSLIDE) != 0) return(TRUE);
324 			(void)setval((INTBIG)ai, VARCINST, x_("userbits"), ai->userbits|CANTSLIDE, VINTEGER);
325 			break;
326 		case CHANGETYPETEMPRIGID:			/* arc temporarily rigid */
327 			if (ai->changed == cla_changeclock + 2) return(TRUE);
328 			ai->changed = cla_changeclock + 2;
329 			break;
330 		case CHANGETYPETEMPUNRIGID:			/* arc temporarily un-rigid */
331 			if (ai->changed == cla_changeclock + 3) return(TRUE);
332 			ai->changed = cla_changeclock + 3;
333 			break;
334 		case CHANGETYPEREMOVETEMP:			/* remove temporarily state */
335 			if (ai->changed != cla_changeclock + 3 && ai->changed != cla_changeclock + 2) return(TRUE);
336 			ai->changed = cla_changeclock - 3;
337 			break;
338 	}
339 	return(FALSE);
340 }
341 
cla_layconmodifynodeinst(NODEINST * ni,INTBIG dlx,INTBIG dly,INTBIG dhx,INTBIG dhy,INTBIG drot,INTBIG dtrans)342 void cla_layconmodifynodeinst(NODEINST *ni, INTBIG dlx, INTBIG dly, INTBIG dhx, INTBIG dhy,
343 	INTBIG drot, INTBIG dtrans)
344 {
345 	/* advance the change clock */
346 	cla_changeclock += 4;
347 
348 	/* change the nodeinst */
349 	if (cla_modifynodeinst(ni, dlx, dly, dhx, dhy, drot, dtrans, FALSE))
350 		db_forcehierarchicalanalysis(ni->parent);
351 
352 	/* change the arcs on the nodeinst */
353 	if (cla_modnodearcs(ni, drot, dtrans))
354 		db_forcehierarchicalanalysis(ni->parent);
355 }
356 
cla_layconmodifynodeinsts(INTBIG count,NODEINST ** nis,INTBIG * dlxs,INTBIG * dlys,INTBIG * dhxs,INTBIG * dhys,INTBIG * drots,INTBIG * dtranss)357 void cla_layconmodifynodeinsts(INTBIG count, NODEINST **nis, INTBIG *dlxs, INTBIG *dlys,
358 	INTBIG *dhxs, INTBIG *dhys, INTBIG *drots, INTBIG *dtranss)
359 {
360 	REGISTER INTBIG i;
361 	REGISTER NODEPROTO *parent;
362 
363 	/* advance the change clock */
364 	cla_changeclock += 4;
365 
366 	/* change the nodeinst */
367 	parent = NONODEPROTO;
368 	for(i=0; i<count; i++)
369 	{
370 		if (cla_modifynodeinst(nis[i], dlxs[i], dlys[i], dhxs[i], dhys[i],
371 			drots[i], dtranss[i], FALSE)) parent = nis[i]->parent;
372 	}
373 
374 	/* change the arcs on the nodeinst */
375 	for(i=0; i<count; i++)
376 	{
377 		if (cla_modnodearcs(nis[i], drots[i], dtranss[i]))
378 			parent = nis[i]->parent;
379 	}
380 	if (parent != NONODEPROTO) db_forcehierarchicalanalysis(parent);
381 }
382 
cla_layconmodifyarcinst(ARCINST * ai,INTBIG oldx0,INTBIG oldy0,INTBIG oldx1,INTBIG oldy1,INTBIG oldwid,INTBIG oldlen)383 void cla_layconmodifyarcinst(ARCINST /*@unused@*/ *ai, INTBIG /*@unused@*/ oldx0,
384 	INTBIG /*@unused@*/ oldy0, INTBIG /*@unused@*/ oldx1,
385 	INTBIG /*@unused@*/ oldy1, INTBIG /*@unused@*/ oldwid, INTBIG /*@unused@*/ oldlen) {}
cla_layconmodifyportproto(PORTPROTO * pp,NODEINST * oni,PORTPROTO * opp)386 void cla_layconmodifyportproto(PORTPROTO /*@unused@*/ *pp, NODEINST /*@unused@*/ *oni,
387 	PORTPROTO /*@unused@*/ *opp) {}
cla_layconmodifynodeproto(NODEPROTO * np)388 void cla_layconmodifynodeproto(NODEPROTO /*@unused@*/ *np) {}
cla_layconnewlib(LIBRARY * lib)389 void cla_layconnewlib(LIBRARY /*@unused@*/ *lib) {}
cla_layconkilllib(LIBRARY * lib)390 void cla_layconkilllib(LIBRARY /*@unused@*/ *lib) {}
cla_layconkillvariable(INTBIG addr,INTBIG type,INTBIG key,INTBIG saddr,INTBIG stype,UINTBIG * olddes)391 void cla_layconkillvariable(INTBIG /*@unused@*/ addr, INTBIG /*@unused@*/ type,
392 	INTBIG /*@unused@*/ key, INTBIG /*@unused@*/ saddr,
393 	INTBIG /*@unused@*/ stype, UINTBIG /*@unused@*/ *olddes) {}
cla_layconinsertvariable(INTBIG addr,INTBIG type,INTBIG key,INTBIG aindex)394 void cla_layconinsertvariable(INTBIG /*@unused@*/ addr, INTBIG /*@unused@*/ type,
395 	INTBIG /*@unused@*/ key, INTBIG /*@unused@*/ aindex) {}
cla_laycondeletevariable(INTBIG addr,INTBIG type,INTBIG key,INTBIG aindex,INTBIG oldval)396 void cla_laycondeletevariable(INTBIG /*@unused@*/ addr, INTBIG /*@unused@*/ type,
397 	INTBIG /*@unused@*/ key, INTBIG /*@unused@*/ aindex, INTBIG /*@unused@*/ oldval) {}
cla_layconsetvariable(void)398 void cla_layconsetvariable(void) {}
399 
400 /******************** TEXT MODIFICATION CODE *************************/
401 
402 /* #define TEXTPARTOFOBJECTBOUNDS 1 */
403 
cla_layconmodifyvariable(INTBIG addr,INTBIG type,INTBIG key,INTBIG stype,INTBIG aindex,INTBIG oldval)404 void cla_layconmodifyvariable(INTBIG addr, INTBIG type, INTBIG key, INTBIG stype,
405 	INTBIG aindex, INTBIG oldval)
406 {
407 #ifdef TEXTPARTOFOBJECTBOUNDS
408 	REGISTER CHAR *name;
409 	REGISTER PORTPROTO *pp;
410 	REGISTER NODEINST *ni;
411 
412 	/* look for modified text descriptor on exports */
413 	if (type == VPORTPROTO)
414 	{
415 		if ((stype&VCREF) != 0)
416 		{
417 			name = changedvariablename(type, key, stype);
418 			if (estrcmp(name, x_("textdescript")) == 0)
419 			{
420 				if (aindex == 1)
421 				{
422 					pp = (PORTPROTO *)addr;
423 					ni = pp->subnodeinst;
424 					updategeom(ni->geom, ni->parent);
425 				}
426 			}
427 		}
428 	}
429 #endif
430 }
431 
cla_layconmodifydescript(INTBIG addr,INTBIG type,INTBIG key,UINTBIG * olddes)432 void cla_layconmodifydescript(INTBIG addr, INTBIG type, INTBIG key, UINTBIG *olddes)
433 {
434 #ifdef TEXTPARTOFOBJECTBOUNDS
435 	REGISTER NODEINST *ni;
436 	REGISTER ARCINST *ai;
437 	REGISTER PORTPROTO *pp;
438 
439 	switch (type)
440 	{
441 		case VNODEINST:
442 			ni = (NODEINST *)addr;
443 			updategeom(ni->geom, ni->parent);
444 			break;
445 		case VPORTPROTO:
446 			pp = (PORTPROTO *)addr;
447 			ni = pp->subnodeinst;
448 			updategeom(ni->geom, ni->parent);
449 			break;
450 		case VARCINST:
451 			ai = (ARCINST *)addr;
452 			updategeom(ai->geom, ai->parent);
453 			break;
454 	}
455 #endif
456 }
457 
458 /******************** NODE MODIFICATION CODE *************************/
459 
460 /*
461  * The meaning of cla_changeclock for object modification:
462  *
463  * ai->changed <  cla_changeclock-2  unmodified         arcs
464  * ai->changed == cla_changeclock-2  unmodified rigid   arcs
465  * ai->changed == cla_changeclock-1  unmodified unrigid arcs
466  * ai->changed == cla_changeclock      modified rigid   arcs
467  * ai->changed == cla_changeclock+1    modified unrigid arcs
468  * ni->changed <  cla_changeclock-1  unmodified         nodes
469  * ni->changed == cla_changeclock-1  size-changed       nodes
470  * ni->changed == cla_changeclock    position-changed   nodes
471  */
472 
473 /*
474  * routine to modify nodeinst "ni" by "deltalx" in low X, "deltaly" in low Y,
475  * "deltahx" in high X, "deltahy" in high Y, and "dangle" tenth-degrees.  If
476  * "announce" is true, report "start" and "end" changes on the node.
477  * If the nodeinst is a portproto of the current cell and has any arcs
478  * connected to it, the routine returns nonzero to indicate that the outer
479  * cell has ports that moved (the nodeinst has exports).
480  */
cla_modifynodeinst(NODEINST * ni,INTBIG deltalx,INTBIG deltaly,INTBIG deltahx,INTBIG deltahy,INTBIG dangle,INTBIG dtrans,BOOLEAN announce)481 BOOLEAN cla_modifynodeinst(NODEINST *ni, INTBIG deltalx, INTBIG deltaly, INTBIG deltahx,
482 	INTBIG deltahy, INTBIG dangle, INTBIG dtrans, BOOLEAN announce)
483 {
484 	REGISTER INTBIG oldlx, oldly, oldhx, oldhy, change;
485 	REGISTER INTSML oldang, oldtrans;
486 
487 	/* determine whether this is a position or size change */
488 	if (deltalx == deltahx && deltaly == deltahy)
489 	{
490 		if (deltalx == 0 && deltaly == 0 && dangle == 0 && dtrans == 0) change = -1; else
491 			change = 0;
492 	} else change = -1;
493 
494 	/* reject if this change has already been done */
495 	if (ni->changed >= cla_changeclock+change) return(FALSE);
496 
497 	/* if simple rotation on transposed nodeinst, reverse rotation */
498 	if (ni->transpose != 0 && dtrans == 0) dangle = (3600 - dangle) % 3600;
499 
500 	if (ni->changed < cla_changeclock-1 && announce)
501 		(void)db_change((INTBIG)ni, OBJECTSTART, VNODEINST, 0, 0, 0, 0, 0);
502 
503 	/* make changes to the nodeinst */
504 	oldang = ni->rotation;     ni->rotation = (INTSML)((ni->rotation + dangle) % 3600);
505 	oldtrans = ni->transpose;  ni->transpose = (INTSML)((ni->transpose + dtrans) & 1);
506 	oldlx = ni->lowx;          ni->lowx += deltalx;
507 	oldhx = ni->highx;         ni->highx += deltahx;
508 	oldly = ni->lowy;          ni->lowy += deltaly;
509 	oldhy = ni->highy;         ni->highy += deltahy;
510 	updategeom(ni->geom, ni->parent);
511 
512 #ifdef CLA_DEBUG
513 	if (cla_conlaydebug)
514 		ttyputmsg(M_("Change node %s by X(%s %s) Y(%s %s) r=%ld t=%ld"),
515 			describenodeinst(ni), latoa(deltalx, 0), latoa(deltahx, 0), latoa(deltaly, 0),
516 				latoa(deltahy, 0), dangle, dtrans);
517 #endif
518 
519 	/* mark that this nodeinst has changed */
520 	if (ni->changed < cla_changeclock-1)
521 	{
522 		ni->changeaddr = (CHAR *)db_change((INTBIG)ni, NODEINSTMOD, oldlx,
523 			oldly, oldhx, oldhy, oldang, oldtrans);
524 		if (announce)
525 			(void)db_change((INTBIG)ni, OBJECTEND, VNODEINST, 0, 0, 0, 0, 0);
526 	}
527 
528 	ni->changed = cla_changeclock + change;
529 
530 	/* see if this nodeinst is a port of the current cell */
531 	if (ni->firstportexpinst == NOPORTEXPINST) return(FALSE);
532 	return(TRUE);
533 }
534 
535 /*
536  * routine to modify all of the arcs connected to nodeinst "ni" (which has
537  * been rotated by "dangle" tenth-degrees).  If the routine returns nonzero,
538  * some ports on the current cell have moved and the cell must be
539  * re-examined for portproto locations.
540  */
cla_modnodearcs(NODEINST * ni,INTBIG dangle,INTBIG dtrans)541 BOOLEAN cla_modnodearcs(NODEINST *ni, INTBIG dangle, INTBIG dtrans)
542 {
543 	REGISTER BOOLEAN examinecell;
544 
545 	/* assume cell needs no further looks */
546 	examinecell = FALSE;
547 
548 	/* next look at arcs that run within this nodeinst */
549 	cla_modwithin(ni, dangle, dtrans);
550 
551 	/* next look at the rest of the rigid arcs on this nodeinst */
552 	if (cla_modrigid(ni, dangle, dtrans)) examinecell = TRUE;
553 
554 	/* finally, look at rest of the flexible arcs on this nodeinst */
555 	if (cla_modflex(ni, dangle, dtrans)) examinecell = TRUE;
556 
557 	return(examinecell);
558 }
559 
560 /*
561  * routine to modify the arcs that run within nodeinst "ni"
562  */
cla_modwithin(NODEINST * ni,INTBIG dangle,INTBIG dtrans)563 void cla_modwithin(NODEINST *ni, INTBIG dangle, INTBIG dtrans)
564 {
565 	REGISTER PORTARCINST *pi;
566 	REGISTER ARCINST *ai;
567 	REGISTER INTBIG ox, oy, i, total;
568 	INTBIG nox, noy, onox, onoy;
569 	XARRAY trans;
570 
571 	/* ignore all this stuff if the node just got created */
572 	if (((CHANGE *)ni->changeaddr)->changetype == NODEINSTNEW) return;
573 
574 	/* build a list of arcs with both ends on this nodeinst */
575 	total = 0;
576 	for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
577 	{
578 		/* ignore if arcinst is not within the node */
579 		ai = pi->conarcinst;
580 		if (ai->end[0].nodeinst != ai->end[1].nodeinst) continue;
581 		if (ai->changed == cla_changeclock) continue;
582 		total++;
583 	}
584 	if (total == 0) return;
585 	if (total > cla_workingarccount)
586 	{
587 		if (cla_workingarccount > 0) efree((CHAR *)cla_workingarcs);
588 		cla_workingarccount = 0;
589 		cla_workingarcs = (ARCINST **)emalloc(total * (sizeof (ARCINST *)), db_cluster);
590 		if (cla_workingarcs == 0) return;
591 		cla_workingarccount = total;
592 	}
593 	total = 0;
594 	for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
595 	{
596 		/* ignore if arcinst is not within the node */
597 		ai = pi->conarcinst;
598 		if (ai->end[0].nodeinst != ai->end[1].nodeinst) continue;
599 		if (ai->changed == cla_changeclock) continue;
600 		for(i=0; i<total; i++) if (cla_workingarcs[i] == ai) break;
601 		if (i >= total)
602 			cla_workingarcs[total++] = ai;
603 	}
604 
605 	/* look for arcs with both ends on this nodeinst */
606 	for(i=0; i<total; i++)
607 	{
608 		ai = cla_workingarcs[i];
609 		if ((ai->userbits&DEADA) != 0) continue;
610 
611 		/* prepare transformation matrix */
612 		makeangle(dangle, dtrans, trans);
613 
614 		/* compute old center of nodeinst */
615 		ox = (((CHANGE *)ni->changeaddr)->p1 + ((CHANGE *)ni->changeaddr)->p3) / 2;
616 		oy = (((CHANGE *)ni->changeaddr)->p2 + ((CHANGE *)ni->changeaddr)->p4) / 2;
617 
618 		/* determine the new ends of the arcinst */
619 		cla_adjustmatrix(ni, ai->end[0].portarcinst->proto, trans);
620 		xform(ai->end[0].xpos-ox, ai->end[0].ypos-oy, &nox, &noy, trans);
621 		cla_adjustmatrix(ni, ai->end[1].portarcinst->proto, trans);
622 		xform(ai->end[1].xpos-ox, ai->end[1].ypos-oy, &onox, &onoy, trans);
623 #ifdef	CLA_DEBUG
624 		if (cla_conlaydebug)
625 			ttyputmsg(M_("Internal arc %s moves 0:%s,%s  1:%s,%s"),
626 				describearcinst(ai), latoa(nox, 0), latoa(noy, 0), latoa(onox, 0), latoa(onoy, 0));
627 #endif
628 		/* move the arcinst */
629 		cla_domovearcinst(ai, nox, noy, onox, onoy, 0);
630 	}
631 }
632 
633 /*
634  * routine to modify the rigid arcs that run from nodeinst "ni".  The nodeinst
635  * has changed "dangle" tenth-degrees.  If any nodes that are ports in the
636  * current cell change position, the routine returns nonzero to indicate
637  * that instances of the current cell must be examined for arcinst motion.
638  */
cla_modrigid(NODEINST * ni,INTBIG dangle,INTBIG dtrans)639 BOOLEAN cla_modrigid(NODEINST *ni, INTBIG dangle, INTBIG dtrans)
640 {
641 	REGISTER PORTPROTO *opt;
642 	REGISTER PORTARCINST *pi;
643 	REGISTER ARCINST *ai, **arclist;
644 	REGISTER NODEINST *ono;
645 	INTBIG ax[2], ay[2], onox, onoy, dx, dy;
646 	XARRAY trans;
647 	REGISTER INTBIG othx, othy, ox, oy, i, total, thisend, thatend;
648 	REGISTER INTBIG nextangle;
649 	REGISTER BOOLEAN examinecell, locked;
650 
651 	/* build a list of the rigid arcs on this nodeinst */
652 	total = 0;
653 	for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
654 	{
655 		/* ignore if arcinst is not rigid */
656 		ai = pi->conarcinst;
657 		if (ai->changed == cla_changeclock-1 || ai->changed == cla_changeclock+1) continue;
658 		if (ai->changed != cla_changeclock-2 && (ai->userbits&FIXED) == 0) continue;
659 		total++;
660 	}
661 	if (total == 0) return(FALSE);
662 	arclist = (ARCINST **)emalloc(total * (sizeof (ARCINST *)), el_tempcluster);
663 	if (arclist == 0) return(FALSE);
664 	i = 0;
665 	for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
666 	{
667 		/* ignore if arcinst is not rigid */
668 		ai = pi->conarcinst;
669 		if (ai->changed == cla_changeclock-1 || ai->changed == cla_changeclock+1) continue;
670 		if (ai->changed != cla_changeclock-2 && (ai->userbits&FIXED) == 0) continue;
671 		arclist[i++] = ai;
672 	}
673 
674 	/* if simple rotation on transposed nodeinst, reverse rotation */
675 	nextangle = dangle;
676 	if (((CHANGE *)ni->changeaddr)->changetype != NODEINSTNEW &&
677 		((CHANGE *)ni->changeaddr)->p6 != 0 && dtrans != 0)
678 			nextangle = (3600 - dangle) % 3600;
679 
680 	/* prepare transformation matrix and angle/transposition information */
681 	makeangle(nextangle, dtrans, trans);
682 
683 	/* look for rigid arcs on this nodeinst */
684 	examinecell = FALSE;
685 	for(i=0; i<total; i++)
686 	{
687 		ai = arclist[i];
688 		if ((ai->userbits&DEADA) != 0) continue;
689 		ai->userbits &= ~FIXEDMOD;
690 
691 		/* if rigid arcinst has already been changed check its connectivity */
692 		if (ai->changed == cla_changeclock)
693 		{
694 			cla_ensurearcinst(ai, 0);
695 			continue;
696 		}
697 
698 		/* find out which end of the arcinst is where, ignore internal arcs */
699 		thisend = thatend = 0;
700 		if (ai->end[0].nodeinst == ni) thatend++;
701 		if (ai->end[1].nodeinst == ni) thisend++;
702 		if (thisend == thatend) continue;
703 
704 		if (((CHANGE *)ni->changeaddr)->changetype == NODEINSTNEW) ox = oy = 0; else
705 		{
706 			ox = (((CHANGE *)ni->changeaddr)->p1 + ((CHANGE *)ni->changeaddr)->p3) / 2;
707 			oy = (((CHANGE *)ni->changeaddr)->p2 + ((CHANGE *)ni->changeaddr)->p4) / 2;
708 			cla_adjustmatrix(ni, ai->end[thisend].portarcinst->proto, trans);
709 		}
710 
711 		/* figure out the new location of this arcinst connection */
712 		xform(ai->end[thisend].xpos-ox, ai->end[thisend].ypos-oy,
713 			&ax[thisend], &ay[thisend], trans);
714 
715 		ono = ai->end[thatend].nodeinst;
716 		opt = ai->end[thatend].portarcinst->proto;
717 #ifdef	CLA_DEBUG
718 		if (cla_conlaydebug)
719 			ttyputmsg(M_("Modify rigid arc %s from %s to %s"),
720 				describearcinst(ai), describenodeinst(ni), describenodeinst(ono));
721 #endif
722 
723 		/* figure out the new location of that arcinst connection */
724 		xform(ai->end[thatend].xpos-ox, ai->end[thatend].ypos-oy, &ax[thatend],
725 			&ay[thatend], trans);
726 #ifdef	CLA_DEBUG
727 		if (cla_conlaydebug)
728 			ttyputmsg(M_("Other end of arc moves to (%s,%s)"),
729 				latoa(ax[thatend], 0), latoa(ay[thatend], 0));
730 #endif
731 
732 		/* see if other nodeinst has changed */
733 		locked = FALSE;
734 		if (ono->changed == cla_changeclock) locked = TRUE; else
735 		{
736 			if ((ono->userbits&NILOCKED) != 0) locked = TRUE; else
737 			{
738 				if (ono->proto->primindex == 0)
739 				{
740 					if ((ono->parent->userbits&NPILOCKED) != 0) locked = TRUE;
741 				} else
742 				{
743 					if ((us_useroptions&NOPRIMCHANGES) != 0 &&
744 						(ono->proto->userbits&LOCKEDPRIM) != 0) locked = TRUE;
745 				}
746 			}
747 		}
748 		if (!locked)
749 		{
750 			/* compute port motion within the other nodeinst (is this right? !!!) */
751 			cla_oldportposition(ono, opt, &onox, &onoy);
752 			portposition(ono, opt, &dx, &dy);
753 			othx = dx - onox;   othy = dy - onoy;
754 
755 			/* figure out the new location of the other nodeinst */
756 			onox = (ono->lowx + ono->highx) / 2;
757 			onoy = (ono->lowy + ono->highy) / 2;
758 			xform(onox-ox, onoy-oy, &dx, &dy, trans);
759 			dx = dx - onox - othx;
760 			dy = dy - onoy - othy;
761 
762 			/* move the other nodeinst */
763 			nextangle = dangle;
764 			if (dtrans != 0 && ono->transpose != ((CHANGE *)ni->changeaddr)->p6)
765 				nextangle = (3600 - nextangle) % 3600;
766 
767 			/* ignore null motion on nodes that have already been examined */
768 			if (dx != 0 || dy != 0 || nextangle != 0 || dtrans != 0 || ono->changed != cla_changeclock-1)
769 			{
770 				ai->userbits |= FIXEDMOD;
771 				if (cla_modifynodeinst(ono, dx, dy, dx, dy, nextangle, dtrans, TRUE))
772 					examinecell = TRUE;
773 			}
774 		}
775 
776 		/* move the arcinst */
777 #ifdef	CLA_DEBUG
778 		if (cla_conlaydebug)
779 			ttyputmsg(M_("Now arc runs from (%s,%s) to (%s,%s)"),
780 				latoa(ax[0], 0), latoa(ay[0], 0), latoa(ax[1], 0), latoa(ay[1], 0));
781 #endif
782 		cla_domovearcinst(ai, ax[0],ay[0], ax[1],ay[1], 0);
783 	}
784 
785 	/* re-scan rigid arcs and recursively modify arcs on other nodes */
786 	for(i=0; i<total; i++)
787 	{
788 		ai = arclist[i];
789 		if ((ai->userbits&DEADA) != 0) continue;
790 
791 		/* only want arcinst that was just explored */
792 		if ((ai->userbits & FIXEDMOD) == 0) continue;
793 
794 		/* get the other nodeinst */
795 		if (ai->end[0].nodeinst == ni) ono = ai->end[1].nodeinst; else
796 			ono = ai->end[0].nodeinst;
797 
798 		nextangle = dangle;
799 		if (dtrans != 0 && ((CHANGE *)ono->changeaddr)->p6 != ((CHANGE *)ni->changeaddr)->p6)
800 			nextangle = (3600 - nextangle) % 3600;
801 		if (cla_modnodearcs(ono, nextangle, dtrans)) examinecell = TRUE;
802 	}
803 	efree((CHAR *)arclist);
804 	return(examinecell);
805 }
806 
807 /*
808  * routine to modify the flexible arcs connected to nodeinst "ni".  If any
809  * nodes that are ports move, the routine returns nonzero to indicate that
810  * instances of the current cell must be examined for arcinst motion
811  */
cla_modflex(NODEINST * ni,INTBIG dangle,INTBIG dtrans)812 BOOLEAN cla_modflex(NODEINST *ni, INTBIG dangle, INTBIG dtrans)
813 {
814 	REGISTER PORTARCINST *pi;
815 	REGISTER ARCINST *ai, **arclist;
816 	REGISTER NODEINST *ono;
817 	REGISTER INTBIG i, total, ox, oy, odx, ody, thisend, thatend, mangle;
818 	REGISTER INTBIG nextangle;
819 	REGISTER BOOLEAN examinecell;
820 	XARRAY trans;
821 	INTBIG ax[2], ay[2], dx, dy, lx, hx, ly, hy;
822 	static POLYGON *poly = NOPOLYGON;
823 
824 	/* build a list of the flexible arcs on this nodeinst */
825 	total = 0;
826 	for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
827 	{
828 		/* ignore if arcinst is not flexible */
829 		ai = pi->conarcinst;
830 		if (ai->changed == cla_changeclock-2 || ai->changed == cla_changeclock) continue;
831 		if (ai->changed != cla_changeclock-1 && (ai->userbits&FIXED) != 0) continue;
832 		total++;
833 	}
834 	if (total == 0) return(FALSE);
835 	arclist = (ARCINST **)emalloc(total * (sizeof (ARCINST *)), el_tempcluster);
836 	if (arclist == 0) return(FALSE);
837 	i = 0;
838 	for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
839 	{
840 		/* ignore if arcinst is not flexible */
841 		ai = pi->conarcinst;
842 		if (ai->changed == cla_changeclock-2 || ai->changed == cla_changeclock) continue;
843 		if (ai->changed != cla_changeclock-1 && (ai->userbits&FIXED) != 0) continue;
844 		arclist[i++] = ai;
845 	}
846 
847 	/* if simple rotation on transposed nodeinst, reverse rotation */
848 	nextangle = dangle;
849 	if (((CHANGE *)ni->changeaddr)->changetype != NODEINSTNEW &&
850 		((CHANGE *)ni->changeaddr)->p6 != 0 && dtrans != 0)
851 			nextangle = (3600 - dangle) % 3600;
852 
853 	/* prepare transformation matrix and angle/transposition information */
854 	makeangle(nextangle, dtrans, trans);
855 
856 	/* look at all of the flexible arcs on this nodeinst */
857 	examinecell = FALSE;
858 	for(i=0; i<total; i++)
859 	{
860 		ai = arclist[i];
861 		if ((ai->userbits&DEADA) != 0) continue;
862 
863 		/* if flexible arcinst has been changed, verify its connectivity */
864 #ifdef	CLA_DEBUG
865 		if (cla_conlaydebug)
866 		{
867 			ttyputmsg(M_("Considering arc %s (clock=%ld, curclock=%ld)"),
868 				describearcinst(ai), ai->changed, cla_changeclock+1);
869 		}
870 #endif
871 		if (ai->changed >= cla_changeclock+1)
872 		{
873 			cla_ensurearcinst(ai, 1);
874 			continue;
875 		}
876 
877 		/* figure where each end of the arcinst is, ignore internal arcs */
878 		thisend = thatend = 0;
879 		if (ai->end[0].nodeinst == ni) thatend++;
880 		if (ai->end[1].nodeinst == ni) thisend++;
881 		if (thisend == thatend) continue;
882 
883 		/* if nodeinst motion stays within port area, ignore the arcinst */
884 		if ((ai->userbits&CANTSLIDE) == 0 &&
885 			db_stillinport(ai, thisend, ai->end[thisend].xpos, ai->end[thisend].ypos))
886 				continue;
887 
888 		if (((CHANGE *)ni->changeaddr)->changetype == NODEINSTNEW) ox = oy = 0; else
889 		{
890 			ox = (((CHANGE *)ni->changeaddr)->p1 + ((CHANGE *)ni->changeaddr)->p3) / 2;
891 			oy = (((CHANGE *)ni->changeaddr)->p2 + ((CHANGE *)ni->changeaddr)->p4) / 2;
892 			cla_adjustmatrix(ni, ai->end[thisend].portarcinst->proto, trans);
893 		}
894 
895 		/* figure out the new location of this arcinst connection */
896 		xform(ai->end[thisend].xpos-ox, ai->end[thisend].ypos-oy,
897 			&ax[thisend], &ay[thisend], trans);
898 
899 		/* make sure the arc end is still in the port */
900 		(void)needstaticpolygon(&poly, 4, db_cluster);
901 		shapeportpoly(ai->end[thisend].nodeinst, ai->end[thisend].portarcinst->proto,
902 			poly, FALSE);
903 		if (!isinside(ax[thisend], ay[thisend], poly))
904 		{
905 			getbbox(poly, &lx, &hx, &ly, &hy);
906 			if (ay[thisend] >= ly && ay[thisend] <= hy)
907 			{
908 				/* extend arc horizontally to fit in port */
909 				if (ax[thisend] < lx) ax[thisend] = lx; else
910 					if (ax[thisend] > hx) ax[thisend] = hx;
911 			} else if (ax[thisend] >= lx && ax[thisend] <= hx)
912 			{
913 				/* extend arc vertically to fit in port */
914 				if (ay[thisend] < ly) ay[thisend] = ly; else
915 					if (ay[thisend] > hy) ay[thisend] = hy;
916 			} else
917 			{
918 				/* extend arc arbitrarily to fit in port */
919 				closestpoint(poly, &ax[thisend], &ay[thisend]);
920 			}
921 		}
922 
923 		/* get other end of arcinst and its position */
924 		ono = ai->end[thatend].nodeinst;
925 		ax[thatend] = ai->end[thatend].xpos;
926 		ay[thatend] = ai->end[thatend].ypos;
927 #ifdef	CLA_DEBUG
928 		if (cla_conlaydebug)
929 		{
930 			ttyputmsg(M_("Modify flexible arc %s from %s to %s"),
931 				describearcinst(ai), describenodeinst(ni), describenodeinst(ono));
932 		}
933 #endif
934 
935 		/* see if other nodeinst has changed */
936 		mangle = 1;
937 		if ((ai->userbits&FIXANG) == 0) mangle = 0; else
938 		{
939 			if ((ono->userbits&NILOCKED) != 0) mangle = 0; else
940 			{
941 				if (ono->proto->primindex == 0)
942 				{
943 					if ((ono->parent->userbits&NPILOCKED) != 0) mangle = 0;
944 				} else
945 				{
946 					if ((us_useroptions&NOPRIMCHANGES) != 0 &&
947 						(ono->proto->userbits&LOCKEDPRIM) != 0) mangle = 0;
948 				}
949 			}
950 		}
951 		if (mangle != 0)
952 		{
953 			/* other nodeinst untouched, mangle it */
954 			dx = ax[thisend] - ai->end[thisend].xpos;
955 			dy = ay[thisend] - ai->end[thisend].ypos;
956 			odx = ax[thatend] - ai->end[thatend].xpos;
957 			ody = ay[thatend] - ai->end[thatend].ypos;
958 			if (ai->end[thisend].xpos == ai->end[thatend].xpos)
959 			{
960 				/* null arcinst must not be explicitly horizontal */
961 				if (ai->end[thisend].ypos != ai->end[thatend].ypos ||
962 					((ai->userbits&AANGLE) >> AANGLESH) == 90 ||
963 						((ai->userbits&AANGLE) >> AANGLESH) == 270)
964 				{
965 					/* vertical arcinst: see if it really moved in X */
966 					if (dx == odx) dx = odx = 0;
967 
968 					/* move horizontal, shrink vertical */
969 					ax[thatend] += dx-odx;
970 
971 					/* see if next nodeinst need not be moved */
972 					if (dx != odx && (ai->userbits&CANTSLIDE) == 0 &&
973 						db_stillinport(ai, thatend, ax[thatend], ay[thatend]))
974 							dx = odx = 0;
975 
976 					/* if other node already moved, don't move it any more */
977 					if (ono->changed >= cla_changeclock) dx = odx = 0;
978 
979 #ifdef	CLA_DEBUG
980 					if (cla_conlaydebug)
981 						ttyputmsg(M_("  Moving other end of arc to (%s,%s), other node by (%s,0)"),
982 							latoa(ax[thatend], 0), latoa(ay[thatend], 0), latoa(dx-odx, 0));
983 #endif
984 					if (dx != odx)
985 					{
986 						if (cla_modifynodeinst(ono, dx-odx, 0, dx-odx, 0, 0, 0, TRUE))
987 							examinecell = TRUE;
988 					}
989 					cla_domovearcinst(ai, ax[0],ay[0], ax[1],ay[1], 1);
990 					if (dx != odx)
991 						if (cla_modnodearcs(ono, 0, 0)) examinecell = TRUE;
992 					continue;
993 				}
994 			}
995 			if (ai->end[thisend].ypos == ai->end[thatend].ypos)
996 			{
997 				/* horizontal arcinst: see if it really moved in Y */
998 				if (dy == ody) dy = ody = 0;
999 
1000 				/* shrink horizontal, move vertical */
1001 				ay[thatend] += dy-ody;
1002 
1003 				/* see if next nodeinst need not be moved */
1004 				if (dy != ody && (ai->userbits&CANTSLIDE) == 0 &&
1005 					db_stillinport(ai, thatend, ax[thatend], ay[thatend]))
1006 						dy = ody = 0;
1007 
1008 				/* if other node already moved, don't move it any more */
1009 				if (ono->changed >= cla_changeclock) dx = odx = 0;
1010 
1011 #ifdef	CLA_DEBUG
1012 				if (cla_conlaydebug)
1013 					ttyputmsg(M_("  Moving other end of arc to (%s,%s), other node by (0,%s)"),
1014 						latoa(ax[thatend], 0), latoa(ay[thatend], 0), latoa(dy-ody, 0));
1015 #endif
1016 				if (dy != ody)
1017 				{
1018 					if (cla_modifynodeinst(ono, 0, dy-ody, 0, dy-ody, 0, 0, TRUE))
1019 						examinecell = TRUE;
1020 				}
1021 				cla_domovearcinst(ai, ax[0],ay[0], ax[1],ay[1], 1);
1022 				if (dy != ody) if (cla_modnodearcs(ono, 0, 0)) examinecell = TRUE;
1023 				continue;
1024 			}
1025 
1026 			/***** THIS CODE HANDLES ALL-ANGLE RIGIDITY WITH THE FIXED-ANGLE CONSTRAINT *****/
1027 
1028 			/* special code to handle nonorthogonal fixed-angles */
1029 			cla_nonorthogfixang(ai, thisend, thatend, ono, ax, ay);
1030 			dx = ax[thatend] - ai->end[thatend].xpos;
1031 			dy = ay[thatend] - ai->end[thatend].ypos;
1032 
1033 			/* change the arc */
1034 			cla_updatearc(ai, ax[0],ay[0], ax[1],ay[1], 1);
1035 
1036 			/* if other node already moved, don't move it any more */
1037 			if (ono->changed >= cla_changeclock) dx = dy = 0;
1038 
1039 			if (dx != 0 || dy != 0)
1040 			{
1041 				if (cla_modifynodeinst(ono, dx, dy, dx, dy, 0, 0, TRUE))
1042 					examinecell = TRUE;
1043 				if (cla_modnodearcs(ono, 0, 0)) examinecell = TRUE;
1044 			}
1045 			continue;
1046 		}
1047 
1048 		/* other node has changed or arc is funny, just use its position */
1049 #ifdef	CLA_DEBUG
1050 		if (cla_conlaydebug)
1051 			ttyputmsg(M_("  ! Moving arc %s to (%s,%s)-(%s,%s)"), describearcinst(ai),
1052 				latoa(ax[0], 0), latoa(ay[0], 0), latoa(ax[1], 0), latoa(ay[1], 0));
1053 #endif
1054 		cla_domovearcinst(ai, ax[0],ay[0], ax[1],ay[1], 1);
1055 	}
1056 	efree((CHAR *)arclist);
1057 	return(examinecell);
1058 }
1059 
1060 /*
1061  * routine to determine the motion of a nonorthogonal arcinst "ai" given that
1062  * its "thisend" end has moved to (ax[thisend],ay[thisend]) and its other end must be
1063  * determined in (ax[thatend],ay[thatend]).  The node on the other end is "ono".
1064  */
cla_nonorthogfixang(ARCINST * ai,INTBIG thisend,INTBIG thatend,NODEINST * ono,INTBIG ax[2],INTBIG ay[2])1065 void cla_nonorthogfixang(ARCINST *ai, INTBIG thisend, INTBIG thatend, NODEINST *ono,
1066 	INTBIG ax[2], INTBIG ay[2])
1067 {
1068 	REGISTER PORTARCINST *pi;
1069 	REGISTER ARCINST *oai, *bestai;
1070 	REGISTER INTBIG bestdist;
1071 	INTBIG ix, iy;
1072 
1073 	/* look for longest other arc on "ono" to determine proper end position */
1074 	bestdist = -1;
1075 	for(pi = ono->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
1076 	{
1077 		oai = pi->conarcinst;
1078 		if (oai == ai) continue;
1079 		if (oai->length < bestdist) continue;
1080 		bestdist = oai->length;
1081 		bestai = oai;
1082 	}
1083 
1084 	/* if no other arcs, allow that end to move the same as this end */
1085 	if (bestdist < 0)
1086 	{
1087 		ax[thatend] += ax[thisend] - ai->end[thisend].xpos;
1088 		ay[thatend] += ay[thisend] - ai->end[thisend].ypos;
1089 		return;
1090 	}
1091 
1092 	/* compute intersection of arc "bestai" with new moved arc "ai" */
1093 	if (intersect(ax[thisend],ay[thisend], ((ai->userbits&AANGLE) >> AANGLESH) * 10,
1094 		bestai->end[0].xpos, bestai->end[0].ypos,
1095 			((bestai->userbits&AANGLE) >> AANGLESH) * 10, &ix, &iy) < 0)
1096 	{
1097 		ax[thatend] += ax[thisend] - ai->end[thisend].xpos;
1098 		ay[thatend] += ay[thisend] - ai->end[thisend].ypos;
1099 		return;
1100 	}
1101 	ax[thatend] = ix;
1102 	ay[thatend] = iy;
1103 }
1104 
1105 /*
1106  * routine to ensure that arcinst "ai" is still connected properly at each
1107  * end.  If it is not, it must be jogged or adjusted.  The nature of the
1108  * arcinst is in "arctyp" (0 for rigid, 1 for flexible)
1109  */
cla_ensurearcinst(ARCINST * ai,INTBIG arctyp)1110 void cla_ensurearcinst(ARCINST *ai, INTBIG arctyp)
1111 {
1112 	REGISTER BOOLEAN inside0, inside1;
1113 	INTBIG lx0, lx1, hx0, hx1, ly0, ly1, hy0, hy1, fx, fy, tx, ty;
1114 	static POLYGON *poly0 = NOPOLYGON, *poly1 = NOPOLYGON;
1115 
1116 	/* if nothing is outside port, quit */
1117 	inside0 = db_stillinport(ai, 0, ai->end[0].xpos, ai->end[0].ypos);
1118 	inside1 = db_stillinport(ai, 1, ai->end[1].xpos, ai->end[1].ypos);
1119 	if (inside0 && inside1) return;
1120 
1121 	/* make sure there is a polygon */
1122 	(void)needstaticpolygon(&poly0, 4, cla_constraint->cluster);
1123 	(void)needstaticpolygon(&poly1, 4, cla_constraint->cluster);
1124 
1125 	/* get area of the ports */
1126 	shapeportpoly(ai->end[0].nodeinst, ai->end[0].portarcinst->proto, poly0, FALSE);
1127 	shapeportpoly(ai->end[1].nodeinst, ai->end[1].portarcinst->proto, poly1, FALSE);
1128 
1129 	/* if arcinst is not fixed-angle, run it directly to the port centers */
1130 	if ((ai->userbits&FIXANG) == 0)
1131 	{
1132 		getcenter(poly0, &fx, &fy);
1133 		getcenter(poly1, &tx, &ty);
1134 #ifdef	CLA_DEBUG
1135 		if (cla_conlaydebug)
1136 			ttyputmsg(M_("Ensuring rigid arc %s"), describearcinst(ai));
1137 #endif
1138 		cla_domovearcinst(ai, fx, fy, tx, ty, arctyp);
1139 		return;
1140 	}
1141 
1142 	/* get bounding boxes of polygons */
1143 	getbbox(poly0, &lx0, &hx0, &ly0, &hy0);
1144 	getbbox(poly1, &lx1, &hx1, &ly1, &hy1);
1145 
1146 	/* if manhattan path runs between the ports, adjust the arcinst */
1147 	if (lx0 <= hx1 && lx1 <= hx0)
1148 	{
1149 		/* arcinst runs vertically */
1150 		tx = fx = (maxi(lx0,lx1) + mini(hx0,hx1)) / 2;
1151 		fy = (ly0+hy0) / 2;   ty = (ly1+hy1) / 2;
1152 		closestpoint(poly0, &fx, &fy);
1153 		closestpoint(poly1, &tx, &ty);
1154 #ifdef	CLA_DEBUG
1155 		if (cla_conlaydebug)
1156 			ttyputmsg(M_("Ensuring manhattan arc %s"), describearcinst(ai));
1157 #endif
1158 		cla_domovearcinst(ai, fx, fy, tx, ty, arctyp);
1159 		return;
1160 	}
1161 	if (ly0 <= hy1 && ly1 <= hy0)
1162 	{
1163 		/* arcinst runs horizontally */
1164 		ty = fy = (maxi(ly0,ly1) + mini(hy0,hy1)) / 2;
1165 		fx = (lx0+hx0) / 2;   tx = (lx1+hx1) / 2;
1166 		closestpoint(poly0, &fx, &fy);
1167 		closestpoint(poly1, &tx, &ty);
1168 #ifdef	CLA_DEBUG
1169 		if (cla_conlaydebug)
1170 			ttyputmsg(M_("Ensuring manhattan arc %s"), describearcinst(ai));
1171 #endif
1172 		cla_domovearcinst(ai, fx, fy, tx, ty, arctyp);
1173 		return;
1174 	}
1175 
1176 	/* give up and jog the arcinst */
1177 	getcenter(poly0, &fx, &fy);
1178 	getcenter(poly1, &tx, &ty);
1179 	cla_domovearcinst(ai, fx, fy, tx, ty, arctyp);
1180 }
1181 
1182 /*
1183  * routine to update arc "ai" so that its ends run from (fx,fy) to (tx,ty).
1184  * The type of the arc is in "arctyp" for marking the change.
1185  */
cla_updatearc(ARCINST * ai,INTBIG fx,INTBIG fy,INTBIG tx,INTBIG ty,INTBIG arctyp)1186 void cla_updatearc(ARCINST *ai, INTBIG fx, INTBIG fy, INTBIG tx, INTBIG ty, INTBIG arctyp)
1187 {
1188 	REGISTER INTBIG oldxA, oldyA, oldxB, oldyB, oldlen;
1189 
1190 	(void)db_change((INTBIG)ai, OBJECTSTART, VARCINST, 0, 0, 0, 0, 0);
1191 
1192 	/* save old arc state */
1193 	oldxA = ai->end[0].xpos;   oldyA = ai->end[0].ypos;
1194 	oldxB = ai->end[1].xpos;   oldyB = ai->end[1].ypos;
1195 	oldlen = ai->length;
1196 
1197 	/* set the proper arcinst position */
1198 #ifdef CLA_DEBUG
1199 	if (cla_conlaydebug)
1200 		ttyputmsg(M_("Change arc %s from (%s,%s)-(%s,%s) to (%s,%s)-(%s,%s)"),
1201 			describearcinst(ai), latoa(ai->end[0].xpos, 0), latoa(ai->end[0].ypos, 0),
1202 				latoa(ai->end[1].xpos, 0), latoa(ai->end[1].ypos, 0),
1203 					latoa(fx, 0), latoa(fy, 0), latoa(tx, 0), latoa(ty, 0));
1204 #endif
1205 	ai->end[0].xpos = fx;
1206 	ai->end[0].ypos = fy;
1207 	ai->end[1].xpos = tx;
1208 	ai->end[1].ypos = ty;
1209 	ai->length = computedistance(fx,fy, tx,ty);
1210 	determineangle(ai);
1211 	(void)setshrinkvalue(ai, TRUE);
1212 	updategeom(ai->geom, ai->parent);
1213 
1214 	/* see if the arc has changed before */
1215 	if (((CHANGE *)ai->changeaddr) == NOCHANGE)
1216 	{
1217 		/* announce new arcinst change */
1218 		ai->changeaddr = (CHAR *)db_change((INTBIG)ai, ARCINSTMOD, oldxA,
1219 			oldyA, oldxB, oldyB, ai->width, oldlen);
1220 		ai->changed = cla_changeclock + arctyp;
1221 	}
1222 	(void)db_change((INTBIG)ai, OBJECTEND, VARCINST, 0, 0, 0, 0, 0);
1223 }
1224 
cla_domovearcinst(ARCINST * ai,INTBIG fx,INTBIG fy,INTBIG tx,INTBIG ty,INTBIG arctyp)1225 void cla_domovearcinst(ARCINST *ai, INTBIG fx, INTBIG fy, INTBIG tx, INTBIG ty, INTBIG arctyp)
1226 {
1227 	REGISTER NODEPROTO *np, *pnt;
1228 	REGISTER ARCPROTO *ap;
1229 	REGISTER ARCINST *ar1, *ar2, *ar3;
1230 	REGISTER PORTPROTO *pp, *fpt, *tpt;
1231 	REGISTER NODEINST *no1, *no2, *fno, *tno;
1232 	REGISTER INTBIG oldxA, oldyA, oldxB, oldyB, wid, oldbits, lx, hx, ly, hy;
1233 	INTBIG psx, psy;
1234 
1235 	/* check for null arcinst motion */
1236 	if (fx == ai->end[0].xpos && fy == ai->end[0].ypos &&
1237 		tx == ai->end[1].xpos && ty == ai->end[1].ypos)
1238 	{
1239 		/* only ignore null motion on fixed-angle requests */
1240 		if (arctyp != 0) return;
1241 	}
1242 
1243 	/* if the angle is the same or doesn't need to be, simply make the change */
1244 	if ((ai->userbits&FIXANG) == 0 ||
1245 		((ai->userbits&FIXED) != 0 && ai->changed != cla_changeclock-1) ||
1246 		ai->changed == cla_changeclock-2 ||
1247 		(fx == tx && fy == ty) ||
1248 		((((INTBIG)ai->userbits&AANGLE) >> AANGLESH)*10)%1800 == figureangle(fx, fy, tx, ty)%1800)
1249 	{
1250 		cla_updatearc(ai, fx, fy, tx, ty, arctyp);
1251 		return;
1252 	}
1253 #ifdef	CLA_DEBUG
1254 	if (cla_conlaydebug)
1255 	{
1256 		ttyputmsg(M_("Jogging arc %s (0%o) to run from (%s,%s) to (%s,%s)"),
1257 			describearcinst(ai), ai, latoa(fx, 0), latoa(fy, 0), latoa(tx, 0), latoa(ty, 0));
1258 	}
1259 #endif
1260 
1261 	/* manhattan arcinst becomes nonmanhattan: remember facts about it */
1262 	fno = ai->end[0].nodeinst;   fpt = ai->end[0].portarcinst->proto;
1263 	tno = ai->end[1].nodeinst;   tpt = ai->end[1].portarcinst->proto;
1264 	ap = ai->proto;   pnt = ai->parent;   wid = ai->width;
1265 	oldbits = ai->userbits;
1266 
1267 	/* figure out what nodeinst proto connects these arcs */
1268 	np = getpinproto(ap);
1269 	defaultnodesize(np, &psx, &psy);
1270 
1271 	/* replace it with three arcs and two nodes */
1272 	if (ai->end[0].xpos == ai->end[1].xpos)
1273 	{
1274 		/* arcinst was vertical */
1275 		oldyA = oldyB = (ty+fy) / 2;
1276 		oldxA = fx;   oldxB = tx;
1277 		lx = oldxB - psx/2; hx = lx + psx;
1278 		ly = oldyB - psy/2; hy = ly + psy;
1279 		no1 = db_newnodeinst(np, lx, hx, ly, hy, 0, 0, pnt);
1280 		lx = oldxA - psx/2; hx = lx + psx;
1281 		ly = oldyA - psy/2; hy = ly + psy;
1282 		no2 = db_newnodeinst(np, lx, hx, ly, hy, 0, 0, pnt);
1283 	} else
1284 	{
1285 		/* assume horizontal arcinst */
1286 		oldyA = fy;   oldyB = ty;
1287 		oldxA = oldxB = (tx+fx) / 2;
1288 		lx = oldxB - psx/2; hx = lx + psx;
1289 		ly = oldyB - psy/2; hy = ly + psy;
1290 		no1 = db_newnodeinst(np, lx, hx, ly, hy, 0, 0, pnt);
1291 		lx = oldxA - psx/2; hx = lx + psx;
1292 		ly = oldyA - psy/2; hy = ly + psy;
1293 		no2 = db_newnodeinst(np, lx, hx, ly, hy, 0, 0, pnt);
1294 	}
1295 	if (no1 == NONODEINST || no2 == NONODEINST)
1296 	{
1297 		ttyputerr(_("Problem creating jog pins"));
1298 		return;
1299 	}
1300 	(void)db_change((INTBIG)no1, OBJECTEND, VNODEINST, 0, 0, 0, 0, 0);
1301 	(void)db_change((INTBIG)no2, OBJECTEND, VNODEINST, 0, 0, 0, 0, 0);
1302 	pp = np->firstportproto;
1303 	ar1 = db_newarcinst(ap, wid, oldbits, fno, fpt, fx,fy, no2, pp, oldxA,oldyA, pnt);
1304 	if ((oldbits&ISNEGATED) != 0) oldbits &= ~ISNEGATED;
1305 	ar2 = db_newarcinst(ap, wid, oldbits, no2, pp, oldxA,oldyA, no1, pp, oldxB,oldyB, pnt);
1306 	ar3 = db_newarcinst(ap, wid, oldbits, no1, pp, oldxB,oldyB, tno, tpt, tx,ty, pnt);
1307 	if (ar1 == NOARCINST || ar2 == NOARCINST || ar3 == NOARCINST)
1308 	{
1309 		ttyputerr(_("Problem creating jog arcs"));
1310 		return;
1311 	}
1312 	(void)copyvars((INTBIG)ai, VARCINST, (INTBIG)ar2, VARCINST, FALSE);
1313 	(void)db_change((INTBIG)ar1, OBJECTEND, VARCINST, 0, 0, 0, 0, 0);
1314 	(void)db_change((INTBIG)ar2, OBJECTEND, VARCINST, 0, 0, 0, 0, 0);
1315 	(void)db_change((INTBIG)ar3, OBJECTEND, VARCINST, 0, 0, 0, 0, 0);
1316 	ar1->changed = cla_changeclock + arctyp;
1317 	ar2->changed = cla_changeclock + arctyp;
1318 	ar3->changed = cla_changeclock + arctyp;
1319 
1320 	/* now kill the arcinst */
1321 	(void)db_change((INTBIG)ai, OBJECTSTART, VARCINST, 0, 0, 0, 0, 0);
1322 	if ((CHANGE *)ai->changeaddr != NOCHANGE)
1323 	{
1324 		ai->end[0].xpos = ((CHANGE *)ai->changeaddr)->p1;
1325 		ai->end[0].ypos = ((CHANGE *)ai->changeaddr)->p2;
1326 		ai->end[1].xpos = ((CHANGE *)ai->changeaddr)->p3;
1327 		ai->end[1].ypos = ((CHANGE *)ai->changeaddr)->p4;
1328 		ai->length = computedistance(ai->end[0].xpos, ai->end[0].ypos,
1329 			ai->end[1].xpos, ai->end[1].ypos);
1330 		ai->width = ((CHANGE *)ai->changeaddr)->p5;
1331 		determineangle(ai);
1332 	}
1333 	db_killarcinst(ai);
1334 }
1335 
1336 /*
1337  * routine to adjust the transformation matrix "trans" by placing translation
1338  * information for nodeinst "ni", port "pp".
1339  *
1340  * there are only two types of nodeinst changes: internal and external.
1341  * The internal changes are scaling and port motion changes that
1342  * are usually caused by other changes within the cell.  The external
1343  * changes are rotation and transposition.  These two changes never
1344  * occur at the same time.  There is also translation change that
1345  * can occur at any time and is of no importance here.  What is
1346  * important is that the transformation matrix "trans" handles
1347  * the external changes and internal changes.  External changes are already
1348  * set by the "makeangle" subroutine and internal changes are
1349  * built into the matrix here.
1350  */
cla_adjustmatrix(NODEINST * ni,PORTPROTO * pp,XARRAY trans)1351 void cla_adjustmatrix(NODEINST *ni, PORTPROTO *pp, XARRAY trans)
1352 {
1353 	REGISTER INTBIG ox, oy;
1354 	INTBIG onox, onoy, dx, dy;
1355 
1356 	if (((CHANGE *)ni->changeaddr)->p5 == ni->rotation &&
1357 		((CHANGE *)ni->changeaddr)->p6 == ni->transpose)
1358 	{
1359 		/* nodeinst did not rotate: adjust for port motion */
1360 		cla_oldportposition(ni, pp, &onox, &onoy);
1361 		portposition(ni, pp, &dx, &dy);
1362 		ox = (((CHANGE *)ni->changeaddr)->p1 + ((CHANGE *)ni->changeaddr)->p3) / 2;
1363 		oy = (((CHANGE *)ni->changeaddr)->p2 + ((CHANGE *)ni->changeaddr)->p4) / 2;
1364 #ifdef	CLA_DEBUG
1365 		if (cla_conlaydebug)
1366 		{
1367 			ttyputmsg(M_("Node %s port %s was at (%s,%s), is at (%s,%s)"), describenodeinst(ni),
1368 				pp->protoname, latoa(onox, 0), latoa(onoy, 0), latoa(dx, 0), latoa(dy, 0));
1369 			ttyputmsg(M_("    Node moved (%s,%s)"), latoa(ox, 0), latoa(oy, 0));
1370 		}
1371 #endif
1372 		trans[2][0] = dx - onox + ox;   trans[2][1] = dy - onoy + oy;
1373 	} else
1374 	{
1375 		/* nodeinst rotated: adjust for nodeinst motion */
1376 		trans[2][0] = (ni->lowx + ni->highx) / 2;
1377 		trans[2][1] = (ni->lowy + ni->highy) / 2;
1378 	}
1379 }
1380 
1381 /*
1382  * routine to compute the position of portproto "pp" on nodeinst "ni" and
1383  * place the center of the area in the parameters "x" and "y".  The position
1384  * is the "old" position, as determined by any changes that may have occured
1385  * to the nodeinst (and any sub-nodes).
1386  */
cla_oldportposition(NODEINST * ni,PORTPROTO * pp,INTBIG * x,INTBIG * y)1387 void cla_oldportposition(NODEINST *ni, PORTPROTO *pp, INTBIG *x, INTBIG *y)
1388 {
1389 	REGISTER INTBIG lox, hix, loy, hiy;
1390 	XARRAY localtran, subrot, temptr;
1391 	static POLYGON *poly = NOPOLYGON;
1392 
1393 	/* make sure there is a polygon */
1394 	(void)needstaticpolygon(&poly, 4, cla_constraint->cluster);
1395 
1396 	/* descend to the primitive node */
1397 	cla_makeoldrot(ni, subrot);
1398 	while (ni->proto->primindex == 0)
1399 	{
1400 		cla_makeoldtrans(ni, localtran);
1401 		transmult(localtran, subrot, temptr);
1402 		if (((CHANGE *)pp->changeaddr) != NOCHANGE &&
1403 			((CHANGE *)pp->changeaddr)->changetype == PORTPROTOMOD)
1404 		{
1405 			/* special code for moved port prototypes */
1406 			ni = (NODEINST *)((CHANGE *)pp->changeaddr)->p1;
1407 			pp = (PORTPROTO *)((CHANGE *)pp->changeaddr)->p2;
1408 		} else
1409 		{
1410 			ni = pp->subnodeinst;
1411 			pp = pp->subportproto;
1412 		}
1413 		cla_makeoldrot(ni, localtran);
1414 		transmult(localtran, temptr, subrot);
1415 	}
1416 
1417 	/* save the actual extents of the nodeinst */
1418 	lox = ni->lowx;   hix = ni->highx;
1419 	loy = ni->lowy;   hiy = ni->highy;
1420 
1421 	/* if the node changed, reset it temporarily */
1422 	if (((CHANGE *)ni->changeaddr) != NOCHANGE &&
1423 		((CHANGE *)ni->changeaddr)->changetype == NODEINSTMOD)
1424 	{
1425 		ni->lowx = ((CHANGE *)ni->changeaddr)->p1;
1426 		ni->highx = ((CHANGE *)ni->changeaddr)->p3;
1427 		ni->lowy = ((CHANGE *)ni->changeaddr)->p2;
1428 		ni->highy = ((CHANGE *)ni->changeaddr)->p4;
1429 	}
1430 
1431 	/* now get the polygon describing the port */
1432 	shapetransportpoly(ni, pp, poly, subrot);
1433 
1434 	/* reset the bounds of the nodeinst */
1435 	ni->lowx = lox;   ni->highx = hix;
1436 	ni->lowy = loy;   ni->highy = hiy;
1437 
1438 	/* compute the center of the port */
1439 	getcenter(poly, x, y);
1440 }
1441 
cla_makeoldrot(NODEINST * ni,XARRAY trans)1442 void cla_makeoldrot(NODEINST *ni, XARRAY trans)
1443 {
1444 	REGISTER INTBIG nlx, nly, nhx, nhy;
1445 	REGISTER INTSML nr, nt;
1446 
1447 	/* save values */
1448 	nlx = ni->lowx;         nly = ni->lowy;
1449 	nhx = ni->highx;        nhy = ni->highy;
1450 	nr  = ni->rotation;     nt  = ni->transpose;
1451 
1452 	/* set to previous values if they changed */
1453 	if (((CHANGE *)ni->changeaddr) != NOCHANGE &&
1454 		((CHANGE *)ni->changeaddr)->changetype == NODEINSTMOD)
1455 	{
1456 		ni->lowx      = ((CHANGE *)ni->changeaddr)->p1;
1457 		ni->lowy      = ((CHANGE *)ni->changeaddr)->p2;
1458 		ni->highx     = ((CHANGE *)ni->changeaddr)->p3;
1459 		ni->highy     = ((CHANGE *)ni->changeaddr)->p4;
1460 		ni->rotation  = (INTSML)((CHANGE *)ni->changeaddr)->p5;
1461 		ni->transpose = (INTSML)((CHANGE *)ni->changeaddr)->p6;
1462 	}
1463 
1464 	/* create the former rotation matrix */
1465 	makerot(ni, trans);
1466 
1467 	/* restore values */
1468 	ni->lowx     = nlx;     ni->lowy      = nly;
1469 	ni->highx    = nhx;     ni->highy     = nhy;
1470 	ni->rotation = nr;      ni->transpose = nt;
1471 }
1472 
cla_makeoldtrans(NODEINST * ni,XARRAY trans)1473 void cla_makeoldtrans(NODEINST *ni, XARRAY trans)
1474 {
1475 	REGISTER INTBIG nlx, nly, nhx, nhy, ntlx, ntly, nthx, nthy;
1476 	REGISTER NODEPROTO *np;
1477 
1478 	/* save values */
1479 	np = ni->proto;
1480 	nlx = ni->lowx;      nly = ni->lowy;
1481 	nhx = ni->highx;     nhy = ni->highy;
1482 	ntlx = np->lowx;      ntly = np->lowy;
1483 	nthx = np->highx;     nthy = np->highy;
1484 
1485 	/* set to previous values if they changed */
1486 	if (((CHANGE *)ni->changeaddr) != NOCHANGE &&
1487 		((CHANGE *)ni->changeaddr)->changetype == NODEINSTMOD)
1488 	{
1489 		ni->lowx  = ((CHANGE *)ni->changeaddr)->p1;
1490 		ni->lowy  = ((CHANGE *)ni->changeaddr)->p2;
1491 		ni->highx = ((CHANGE *)ni->changeaddr)->p3;
1492 		ni->highy = ((CHANGE *)ni->changeaddr)->p4;
1493 	}
1494 	if (((CHANGE *)np->changeaddr) != NOCHANGE &&
1495 		((CHANGE *)np->changeaddr)->changetype == NODEPROTOMOD)
1496 	{
1497 		np->lowx  = ((CHANGE *)np->changeaddr)->p1;
1498 		np->highx = ((CHANGE *)np->changeaddr)->p2;
1499 		np->lowy  = ((CHANGE *)np->changeaddr)->p3;
1500 		np->highy = ((CHANGE *)np->changeaddr)->p4;
1501 	}
1502 
1503 	/* create the former translation matrix */
1504 	maketrans(ni, trans);
1505 
1506 	/* restore values */
1507 	ni->lowx  = nlx;     ni->lowy  = nly;
1508 	ni->highx = nhx;     ni->highy = nhy;
1509 	np->lowx  = ntlx;     np->lowy  = ntly;
1510 	np->highx = nthx;     np->highy = nthy;
1511 }
1512 
1513 /*
1514  * routine to re-compute the bounds of the cell "cell" (because an object
1515  * has been added or removed from it) and store these bounds in the nominal
1516  * size and the size of each instantiation of the cell.  It is also necessary
1517  * to re-position each instantiation of the cell in its proper position list.
1518  * If "forcedlook" is true, the cell is re-examined regardless of
1519  * whether its size changed.
1520  */
cla_computecell(NODEPROTO * cell,BOOLEAN forcedlook)1521 void cla_computecell(NODEPROTO *cell, BOOLEAN forcedlook)
1522 {
1523 	REGISTER NODEINST *ni, *lni;
1524 	REGISTER NODEPROTO *np, *oneparent;
1525 	INTBIG nlx, nhx, nly, nhy, offx, offy;
1526 	XARRAY trans;
1527 	REGISTER INTBIG dlx, dly, dhx, dhy, flx, fhx, fly, fhy;
1528 	REGISTER BOOLEAN mixed;
1529 	REGISTER CHANGE *c;
1530 	REGISTER LIBRARY *lib;
1531 
1532 #ifdef	CLA_DEBUG
1533 	if (cla_conlaydebug)
1534 		ttyputmsg(M_("In computecell on cell %s (fl=%ld)"),
1535 			cell->protoname, forcedlook);
1536 #endif
1537 	/* get current boundary of cell */
1538 	db_boundcell(cell, &nlx,&nhx, &nly,&nhy);
1539 
1540 	/* quit if it has not changed */
1541 	if (cell->lowx == nlx && cell->highx == nhx && cell->lowy == nly &&
1542 		cell->highy == nhy && !forcedlook) return;
1543 
1544 	/* advance the change clock */
1545 	cla_changeclock += 4;
1546 
1547 	/* get former size of cell from change information */
1548 	flx = cell->lowx;   fhx = cell->highx;
1549 	fly = cell->lowy;   fhy = cell->highy;
1550 	c = (CHANGE *)cell->changeaddr;
1551 	if (c != NOCHANGE && c->changetype == NODEPROTOMOD)
1552 	{
1553 		/* modification changes carry original size */
1554 		flx = c->p1;   fhx = c->p2;
1555 		fly = c->p3;   fhy = c->p4;
1556 	}
1557 
1558 	/* update the cell size */
1559 	cell->lowx = nlx;   cell->highx = nhx;
1560 	cell->lowy = nly;   cell->highy = nhy;
1561 	cell->changeaddr = (CHAR *)db_change((INTBIG)cell, NODEPROTOMOD, flx, fhx, fly, fhy, 0, 0);
1562 
1563 	/* see if all instances of this cell are in the same location */
1564 	mixed = FALSE;
1565 	oneparent = NONODEPROTO;
1566 	lni = NONODEINST;
1567 	for(ni = cell->firstinst; ni != NONODEINST; ni = ni->nextinst)
1568 	{
1569 		oneparent = ni->parent;
1570 		if (lni != NONODEINST) if (ni->parent != lni->parent) mixed = TRUE;
1571 		lni = ni;
1572 	}
1573 
1574 	/* if there are no constrained instances of the cell, no change */
1575 	if (oneparent == NONODEPROTO) return;
1576 
1577 	/* if all parent cells the same, make changes to the instances */
1578 	if (!mixed && !forcedlook)
1579 	{
1580 #ifdef	CLA_DEBUG
1581 		if (cla_conlaydebug)
1582 			ttyputmsg(M_("   Recomputing uniform parents of cell %s"),
1583 				describenodeproto(cell));
1584 #endif
1585 		dlx = cell->lowx - flx;   dhx = cell->highx - fhx;
1586 		dly = cell->lowy - fly;   dhy = cell->highy - fhy;
1587 		for(ni = cell->firstinst; ni != NONODEINST; ni = ni->nextinst)
1588 		{
1589 			makeangle(ni->rotation, ni->transpose, trans);
1590 			xform(dhx+dlx, dhy+dly, &offx, &offy, trans);
1591 			nlx = (dlx-dhx+offx)/2;  nhx = offx - nlx;
1592 			nly = (dly-dhy+offy)/2;  nhy = offy - nly;
1593 			if (cla_modifynodeinst(ni, nlx, nly, nhx, nhy, 0, 0, TRUE)) forcedlook = TRUE;
1594 		}
1595 		for(ni = cell->firstinst; ni != NONODEINST; ni = ni->nextinst)
1596 			if (cla_modnodearcs(ni, 0, 0)) forcedlook = TRUE;
1597 		cla_computecell(oneparent, forcedlook);
1598 		return;
1599 	}
1600 
1601 	/*
1602 	 * if instances are scattered or port motion has occured, examine
1603 	 * entire database in proper recursive order and adjust cell sizes
1604 	 */
1605 #ifdef	CLA_DEBUG
1606 	if (cla_conlaydebug)
1607 		ttyputmsg(M_("   Performing complex tree examination"));
1608 #endif
1609 	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
1610 		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
1611 			np->userbits &= ~(CELLMOD|CELLNOMOD);
1612 	cell->userbits |= CELLMOD;
1613 	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
1614 		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
1615 	{
1616 		/* only want cells with no instances as roots of trees */
1617 		if (np->firstinst != NONODEINST) continue;
1618 
1619 		/* now look recursively at the nodes in this cell */
1620 		(void)cla_lookdown(np);
1621 	}
1622 }
1623 
cla_lookdown(NODEPROTO * start)1624 BOOLEAN cla_lookdown(NODEPROTO *start)
1625 {
1626 	REGISTER NODEINST *ni;
1627 	REGISTER NODEPROTO *np;
1628 	REGISTER INTBIG dlx, dhx, dly, dhy, flx, fhx, fly, fhy;
1629 	REGISTER BOOLEAN forcedlook, foundone;
1630 	INTBIG nlx, nhx, nly, nhy, offx, offy;
1631 	XARRAY trans;
1632 
1633 	/* first look recursively to the bottom to see if this cell changed */
1634 	for(ni = start->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
1635 		if (ni->proto->primindex == 0) ni->userbits |= MARKN; else
1636 			ni->userbits &= ~MARKN;
1637 
1638 	foundone = TRUE;
1639 	while (foundone)
1640 	{
1641 		foundone = FALSE;
1642 		for(ni = start->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
1643 		{
1644 			if ((ni->userbits & MARKN) == 0) continue;
1645 			ni->userbits &= ~MARKN;
1646 			np = ni->proto;
1647 
1648 			/* ignore recursive references (showing icon in contents) */
1649 			if (isiconof(np, start)) continue;
1650 
1651 			/* if this nodeinst is to change, mark the parent cell also */
1652 			if ((np->userbits & CELLMOD) != 0) start->userbits |= CELLMOD;
1653 
1654 			/* don't look inside if the cell is certified */
1655 			if ((np->userbits & (CELLNOMOD|CELLMOD)) != 0) continue;
1656 
1657 			/* look inside nodeinst to see if it changed */
1658 			if (cla_lookdown(np)) start->userbits |= CELLMOD;
1659 			foundone = TRUE;
1660 		}
1661 	}
1662 
1663 	/* if this cell did not change, certify so and quit */
1664 	if ((start->userbits & CELLMOD) == 0)
1665 	{
1666 		start->userbits |= CELLNOMOD;
1667 		return(FALSE);
1668 	}
1669 
1670 	/* mark those nodes that must change */
1671 	for(ni = start->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
1672 	{
1673 		np = ni->proto;
1674 		ni->userbits &= ~(MARKN|TOUCHN);
1675 		if (np->primindex != 0) continue;
1676 		if (isiconof(np, start)) continue;
1677 		if ((np->userbits & CELLMOD) == 0) continue;
1678 		ni->userbits |= MARKN | TOUCHN;
1679 	}
1680 #ifdef	CLA_DEBUG
1681 	if (cla_conlaydebug)
1682 		ttyputmsg(M_("      Complex tree search at cell %s"), describenodeproto(start));
1683 #endif
1684 	/* modify the nodes in this cell that changed */
1685 	forcedlook = FALSE;
1686 	foundone = TRUE;
1687 	while (foundone)
1688 	{
1689 		foundone = FALSE;
1690 		for(ni = start->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
1691 		{
1692 			if ((ni->userbits & MARKN) == 0) continue;
1693 			ni->userbits &= ~MARKN;
1694 			np = ni->proto;
1695 
1696 			/* determine original size of cell */
1697 			if ((CHANGE *)np->changeaddr != NOCHANGE &&
1698 				((CHANGE *)np->changeaddr)->changetype == NODEPROTOMOD)
1699 			{
1700 				/* modification changes carry original size */
1701 				flx = ((CHANGE *)np->changeaddr)->p1;
1702 				fhx = ((CHANGE *)np->changeaddr)->p2;
1703 				fly = ((CHANGE *)np->changeaddr)->p3;
1704 				fhy = ((CHANGE *)np->changeaddr)->p4;
1705 			} else
1706 			{
1707 				/* creation changes have no original size: use current size */
1708 				flx = np->lowx;   fhx = np->highx;
1709 				fly = np->lowy;   fhy = np->highy;
1710 			}
1711 			if (ni->highx-ni->lowx == np->highx-np->lowx && ni->highy-ni->lowy == np->highy-np->lowy)
1712 				nlx = nhx = nly = nhy = 0; else
1713 			{
1714 				dlx = np->lowx - flx;   dhx = np->highx - fhx;
1715 				dly = np->lowy - fly;   dhy = np->highy - fhy;
1716 				makeangle(ni->rotation, ni->transpose, trans);
1717 				xform(dhx+dlx, dhy+dly, &offx, &offy, trans);
1718 				nlx = (dlx-dhx+offx)/2;  nhx = offx - nlx;
1719 				nly = (dly-dhy+offy)/2;  nhy = offy - nly;
1720 			}
1721 			if (cla_modifynodeinst(ni, nlx, nly, nhx, nhy, 0, 0, TRUE)) forcedlook = TRUE;
1722 			foundone = TRUE;
1723 		}
1724 	}
1725 
1726 	/* now change the arcs in the nodes in this cell that changed */
1727 	foundone = TRUE;
1728 	while (foundone)
1729 	{
1730 		foundone = FALSE;
1731 		for(ni = start->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
1732 		{
1733 			if ((ni->userbits & TOUCHN) == 0) continue;
1734 			ni->userbits &= ~TOUCHN;
1735 			if (cla_modnodearcs(ni, 0, 0)) forcedlook = TRUE;
1736 			foundone = TRUE;
1737 		}
1738 	}
1739 
1740 	/* now change the size of this cell */
1741 	db_boundcell(start, &nlx,&nhx, &nly,&nhy);
1742 
1743 	/* quit if it has not changed */
1744 	if (start->lowx == nlx && start->highx == nhx && start->lowy == nly &&
1745 		start->highy == nhy && !forcedlook)
1746 	{
1747 		start->userbits |= CELLNOMOD;
1748 		return(FALSE);
1749 	}
1750 
1751 	/* update the cell size */
1752 	start->changeaddr = (CHAR *)db_change((INTBIG)start, NODEPROTOMOD,
1753 		start->lowx, start->highx, start->lowy, start->highy, 0, 0);
1754 	start->lowx = nlx;  start->highx = nhx;
1755 	start->lowy = nly;  start->highy = nhy;
1756 	return(TRUE);
1757 }
1758