1 /* -*- tab-width: 4 -*-
2 *
3 * Electric(tm) VLSI Design System
4 *
5 * File: conlin.c
6 * Linear inequality 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 "database.h"
33 #include "conlin.h"
34
35 /* command completion table for this constraint solver */
36 static COMCOMP cli_linconsp = {NOKEYWORD, topofcells, nextcells, NOPARAMS,
37 INPUTOPT, x_(" \t"), M_("cell to solve for linear inequalities"), 0};
38 static KEYWORD linconiopt[] =
39 {
40 {x_("on"), 0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
41 {x_("off"), 0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
42 TERMKEY
43 };
44 static COMCOMP cli_linconip = {linconiopt, NOTOPLIST, NONEXTLIST, NOPARAMS,
45 INPUTOPT, x_(" \t"), M_("mode that assumes manhattan wires"), 0};
46 static KEYWORD lincontopt[] =
47 {
48 {x_("go"), 0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
49 {x_("name-nodes"), 0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
50 {x_("highlight-equivalent"), 0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
51 TERMKEY
52 };
53 static COMCOMP cli_lincontp = {lincontopt, NOTOPLIST, NONEXTLIST, NOPARAMS,
54 INPUTOPT, x_(" \t"), M_("text-equivalent mode"), 0};
55 static KEYWORD linconopt[] =
56 {
57 {x_("solve"), 1,{&cli_linconsp,NOKEY,NOKEY,NOKEY,NOKEY}},
58 {x_("debug-toggle"), 0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
59 {x_("implicit-manhattan"), 1,{&cli_linconip,NOKEY,NOKEY,NOKEY,NOKEY}},
60 {x_("text-equivalence"), 1,{&cli_lincontp,NOKEY,NOKEY,NOKEY,NOKEY}},
61 TERMKEY
62 };
63 COMCOMP cli_linconp = {linconopt, NOTOPLIST, NONEXTLIST, NOPARAMS,
64 0, x_(" \t"), M_("linear constraint system option"), 0};
65
66 /* global data in linear constraints */
67 CONSTRAINT *cli_constraint; /* the constraint object for this solver */
68 BOOLEAN cli_manhattan; /* true to assume Manhattan constraints */
69 INTBIG cli_properties_key; /* variable key for "CONLIN_properties" */
70 NODEPROTO *cli_curcell; /* the current cell being equated to text */
71 BOOLEAN cli_ownchanges; /* true if changes are internally generated */
72 INTBIG cli_textlines; /* number of declaration/connection lines */
73 BOOLEAN cli_texton; /* true if text/graphics system is on */
74 static CHAR *cli_linconops[] = {x_(">="), x_("=="), x_("<=")};
75
76 static BOOLEAN cli_conlindebug;
77 static BOOLEAN cli_didsolve;
78
79 #define NOCONNODE ((CONNODE *)-1)
80 typedef struct Iconnode
81 {
82 NODEINST *realn; /* actual node that is constrained */
83 INTBIG value; /* value set on this node */
84 BOOLEAN solved; /* flag for marking this node as solved */
85 BOOLEAN loopflag; /* flag for solving loops */
86 struct Iconarc *firstconarc;/* head of list of constraints on this node */
87 struct Iconnode *nextconnode;/* next in list of constraint nodes */
88 } CONNODE;
89
90 #define NOCONARC ((CONARC *)-1)
91 typedef struct Iconarc
92 {
93 ARCINST *reala; /* actual arc that is constrained */
94 CONNODE *other; /* other node being constrained */
95 INTBIG oper; /* operator of the constraint */
96 INTBIG value; /* value of the constraint */
97 struct Iconarc *nextconarc; /* next constraint in linked list */
98 } CONARC;
99
100 static CONARC *cli_conarcfree; /* list of free constraint arc modules */
101 static CONNODE *cli_connodefree;/* list of free constraint node modules */
102
103 /* references to routines in "conlay.c"*/
104 extern void cla_adjustmatrix(NODEINST*, PORTPROTO*, XARRAY);
105 extern void cla_oldportposition(NODEINST*, PORTPROTO*, INTBIG*, INTBIG*);
106
107 /* prototypes for local routines */
108 static CONARC *cli_allocconarc(void);
109 static void cli_freeconarc(CONARC*);
110 static CONNODE *cli_allocconnode(void);
111 static void cli_freeconnode(CONNODE*);
112 static void cli_dosolve(NODEPROTO*);
113 static BOOLEAN cli_buildconstraints(NODEPROTO*, CONNODE**, BOOLEAN, BOOLEAN);
114 static void cli_addconstraint(ARCINST*, CONNODE**, INTBIG, INTBIG, INTBIG, INTBIG, BOOLEAN);
115 static void cli_adjustconstraints(CONNODE*, BOOLEAN);
116 static BOOLEAN cli_solveconstraints(CONNODE*, BOOLEAN);
117 static BOOLEAN cli_satisfy(CONNODE*, CONARC*);
118 static void cli_applyconstraints(NODEPROTO*, CONNODE*, BOOLEAN);
119 static void cli_deleteconstraints(CONNODE*);
120 static void cli_printconstraints(CONNODE*, CHAR*, CHAR*);
121 static void cli_nameallnodes(NODEPROTO*);
122
123 /****************************** ALLOCATION ******************************/
124
125 /*
126 * routine to allocate a constraint arc module from the pool (if any) or memory
127 */
cli_allocconarc(void)128 CONARC *cli_allocconarc(void)
129 {
130 REGISTER CONARC *c;
131
132 if (cli_conarcfree == NOCONARC)
133 {
134 c = (CONARC *)emalloc(sizeof (CONARC), cli_constraint->cluster);
135 if (c == 0) return(NOCONARC);
136 } else
137 {
138 c = cli_conarcfree;
139 cli_conarcfree = (CONARC *)c->nextconarc;
140 }
141 return(c);
142 }
143
144 /*
145 * routine to return constraint arc module "c" to the pool of free modules
146 */
cli_freeconarc(CONARC * c)147 void cli_freeconarc(CONARC *c)
148 {
149 c->nextconarc = cli_conarcfree;
150 cli_conarcfree = c;
151 }
152
153 /*
154 * routine to allocate a constraint node module from the pool (if any) or memory
155 */
cli_allocconnode(void)156 CONNODE *cli_allocconnode(void)
157 {
158 REGISTER CONNODE *c;
159
160 if (cli_connodefree == NOCONNODE)
161 {
162 c = (CONNODE *)emalloc(sizeof (CONNODE), cli_constraint->cluster);
163 if (c == 0) return(NOCONNODE);
164 } else
165 {
166 c = cli_connodefree;
167 cli_connodefree = (CONNODE *)c->nextconnode;
168 }
169 return(c);
170 }
171
172 /*
173 * routine to return constraint node module "c" to the pool of free modules
174 */
cli_freeconnode(CONNODE * c)175 void cli_freeconnode(CONNODE *c)
176 {
177 c->nextconnode = cli_connodefree;
178 cli_connodefree = c;
179 }
180
181 /****************************** DATABASE HOOKS ******************************/
182
cli_linconinit(CONSTRAINT * con)183 void cli_linconinit(CONSTRAINT *con)
184 {
185 if (con != NOCONSTRAINT) cli_constraint = con; else
186 {
187 /* only function during pass 2 of initialization */
188 cli_conarcfree = NOCONARC;
189 cli_connodefree = NOCONNODE;
190 cli_conlindebug = FALSE;
191 cli_didsolve = FALSE;
192 cli_curcell = NONODEPROTO;
193 cli_manhattan = TRUE;
194
195 /* get the primary key for the properties variable */
196 cli_properties_key = makekey(x_("CONLIN_properties"));
197
198 /* initialize the text/graphics equivalence system */
199 cli_ownchanges = FALSE;
200 cli_texton = FALSE;
201 }
202 }
203
cli_linconterm(void)204 void cli_linconterm(void) {}
205
cli_linconsetmode(INTBIG count,CHAR * par[])206 void cli_linconsetmode(INTBIG count, CHAR *par[])
207 {
208 REGISTER INTBIG l;
209 REGISTER CHAR *pp;
210 REGISTER NODEPROTO *np;
211 REGISTER GEOM *geom;
212
213 if (count == 0)
214 {
215 ttyputusage(x_("constraint tell linear OPTION"));
216 return;
217 }
218
219 if (el_curconstraint != cli_constraint)
220 {
221 ttyputerr(M_("Must first switch to this solver with 'constraint use'"));
222 return;
223 }
224
225 l = estrlen(pp = par[0]);
226
227 if (namesamen(pp, x_("debug-toggle"), l) == 0 && l >= 1)
228 {
229 cli_conlindebug = !cli_conlindebug;
230 if (cli_conlindebug) ttyputverbose(M_("Linear constraint debugging on")); else
231 ttyputverbose(M_("Linear constraint debugging off"));
232 return;
233 }
234
235 if (namesamen(pp, x_("text-equivalence"), l) == 0 && l >= 1)
236 {
237 if (count < 2)
238 {
239 ttyputusage(x_("constraint tell linear text-equivalence OPTION"));
240 return;
241 }
242 l = estrlen(pp = par[1]);
243 if (namesamen(pp, x_("name-nodes"), l) == 0 && l >= 1)
244 {
245 np = getcurcell();
246 if (np == NONODEPROTO)
247 {
248 ttyputerr(M_("Must have a current cell to generate names"));
249 return;
250 }
251 cli_nameallnodes(np);
252 return;
253 }
254 if (namesamen(pp, x_("go"), l) == 0 && l >= 1)
255 {
256 np = getcurcell();
257 if (np == NONODEPROTO) ttyputverbose(M_("Enabling text/graphics association")); else
258 {
259 if (cli_texton)
260 {
261 if (np == cli_curcell)
262 ttyputverbose(M_("Reevaluating textual description")); else
263 ttyputverbose(M_("Switching association to cell %s"), describenodeproto(np));
264 } else ttyputverbose(M_("Text window will mirror cell %s"), describenodeproto(np));
265 }
266 cli_maketextcell(np);
267 return;
268 }
269 if (namesamen(pp, x_("highlight-equivalent"), l) == 0 && l >= 1)
270 {
271 geom = (GEOM *)asktool(us_tool, x_("get-object"));
272 if (geom == NOGEOM)
273 {
274 ttyputerr(M_("Find a single object first"));
275 return;
276 }
277 cli_highlightequivalent(geom);
278 return;
279 }
280 ttyputbadusage(x_("constraint tell linear text-equivalence"));
281 return;
282 }
283
284 if (namesamen(pp, x_("implicit-manhattan"), l) == 0 && l >= 1)
285 {
286 if (count < 2) l = estrlen(pp = x_("X")); else l = estrlen(pp = par[1]);
287 if (namesame(pp, x_("on")) == 0)
288 {
289 cli_manhattan = TRUE;
290 ttyputverbose(M_("Manhattan arcs will be kept that way"));
291 return;
292 }
293 if (namesamen(pp, x_("off"), l) == 0 && l >= 2)
294 {
295 cli_manhattan = FALSE;
296 ttyputverbose(M_("No assumptions will be made about Manhattan arcs"));
297 return;
298 }
299 ttyputusage(x_("constraint tell linear implicit-manhattan (on|off)"));
300 return;
301 }
302
303 if (namesamen(pp, x_("solve"), l) == 0 && l >= 1)
304 {
305 if (count < 2)
306 {
307 np = getcurcell();
308 if (np == NONODEPROTO)
309 {
310 ttyputerr(M_("No cell specified and no current cell"));
311 return;
312 }
313 } else
314 {
315 np = getnodeproto(par[1]);
316 if (np == NONODEPROTO)
317 {
318 ttyputerr(M_("No cell named %s"), par[1]);
319 return;
320 }
321 if (np->primindex != 0)
322 {
323 ttyputerr(M_("Must solve cells, not primitives"));
324 return;
325 }
326 }
327
328 /* do constraint work */
329 cli_solvecell(np, TRUE, TRUE);
330 cli_didsolve = TRUE;
331 return;
332 }
333 ttyputbadusage(x_("constraint tell linear"));
334 }
335
336 /*
337 * the valid "command"s are:
338 * "describearc" return a string that describes constraints on arc in "arg1".
339 */
cli_linconrequest(CHAR * command,INTBIG arg1)340 INTBIG cli_linconrequest(CHAR *command, INTBIG arg1)
341 {
342 REGISTER INTBIG i, len, l;
343 REGISTER VARIABLE *var;
344 REGISTER LINCON *conptr;
345 REGISTER void *infstr;
346
347 l = estrlen(command);
348
349 if (namesamen(command, x_("describearc"), l) == 0)
350 {
351 infstr = initinfstr();
352 var = getvalkey(arg1, VARCINST, VINTEGER|VISARRAY, cli_properties_key);
353 if (var != NOVARIABLE)
354 {
355 len = getlength(var) / LINCONSIZE;
356 for(i=0; i<len; i++)
357 {
358 conptr = &((LINCON *)var->addr)[i];
359 if (i != 0) addstringtoinfstr(infstr, x_(", "));
360 addtoinfstr(infstr, (CHAR)(conptr->variable<=1 ? 'X' : 'Y'));
361 addstringtoinfstr(infstr, cli_linconops[conptr->oper]);
362 addstringtoinfstr(infstr, frtoa(conptr->value));
363 }
364 }
365 return((INTBIG)returninfstr(infstr));
366 }
367 return(0);
368 }
369
cli_linconsolve(NODEPROTO * np)370 void cli_linconsolve(NODEPROTO *np)
371 {
372 REGISTER CHANGECELL *cc;
373 REGISTER CHANGEBATCH *curbatch;
374
375 if (np != NONODEPROTO)
376 {
377 cli_dosolve(np);
378 return;
379 }
380
381 cli_eq_solve();
382
383 curbatch = db_getcurrentbatch();
384 if (curbatch != NOCHANGEBATCH)
385 for(cc = curbatch->firstchangecell; cc != NOCHANGECELL; cc = cc->nextchangecell)
386 cli_dosolve(cc->changecell);
387 cli_didsolve = FALSE;
388 }
389
cli_dosolve(NODEPROTO * cell)390 void cli_dosolve(NODEPROTO *cell)
391 {
392 INTBIG nlx, nhx, nly, nhy;
393 REGISTER NODEINST *ni;
394 REGISTER INTBIG dlx, dhx, dly, dhy;
395
396 if (cli_conlindebug)
397 ttyputmsg(M_("Re-checking linear constraints for cell %s"), describenodeproto(cell));
398 if (!cli_didsolve) cli_solvecell(cell, FALSE, FALSE);
399
400 /* get current boundary of cell */
401 db_boundcell(cell, &nlx,&nhx, &nly,&nhy);
402 dlx = nlx - cell->lowx; dhx = nhx - cell->highx;
403 dly = nly - cell->lowy; dhy = nhy - cell->highy;
404
405 /* quit if it has not changed */
406 if (dlx == 0 && dhx == 0 && dly == 0 && dhy == 0) return;
407
408 /* update the cell size */
409 cell->changeaddr = (CHAR *)db_change((INTBIG)cell, NODEPROTOMOD,
410 cell->lowx, cell->lowy, cell->highx, cell->highy, 0, 0);
411 cell->lowx = nlx; cell->highx = nhx;
412 cell->lowy = nly; cell->highy = nhy;
413
414 /* now update instances of the cell */
415 for(ni = cell->firstinst; ni != NONODEINST; ni = ni->nextinst)
416 {
417 (void)db_change((INTBIG)ni, OBJECTSTART, VNODEINST, 0, 0, 0, 0, 0);
418 cli_linconmodifynodeinst(ni, dlx, dly, dhx, dhy, 0, 0);
419 (void)db_change((INTBIG)ni, OBJECTEND, VNODEINST, 0, 0, 0, 0, 0);
420 }
421 }
422
cli_linconnewobject(INTBIG addr,INTBIG type)423 void cli_linconnewobject(INTBIG addr, INTBIG type)
424 {
425 REGISTER ARCINST *ai;
426
427 switch (type&VTYPE)
428 {
429 case VNODEINST: cli_eq_newnode((NODEINST *)addr); break;
430 case VARCINST:
431 ai = (ARCINST *)addr;
432 if (getvalkey((INTBIG)ai, VARCINST, VINTEGER|VISARRAY, cli_properties_key) != NOVARIABLE)
433 {
434 db_setchangecell(ai->parent);
435 db_forcehierarchicalanalysis(ai->parent);
436 }
437 cli_eq_newarc(ai);
438 break;
439 case VPORTPROTO: cli_eq_newport((PORTPROTO *)addr); break;
440 }
441 }
442
cli_linconkillobject(INTBIG addr,INTBIG type)443 void cli_linconkillobject(INTBIG addr, INTBIG type)
444 {
445 REGISTER ARCINST *ai;
446
447 switch (type&VTYPE)
448 {
449 case VNODEINST: cli_eq_killnode((NODEINST *)addr); break;
450 case VARCINST:
451 ai = (ARCINST *)addr;
452 if (getvalkey((INTBIG)ai, VARCINST, VINTEGER|VISARRAY, cli_properties_key) != NOVARIABLE)
453 {
454 db_setchangecell(ai->parent);
455 db_forcehierarchicalanalysis(ai->parent);
456 }
457 cli_eq_killarc(ai);
458 break;
459 case VPORTPROTO: cli_eq_killport((PORTPROTO *)addr); break;
460 }
461 }
462
cli_linconmodifynodeinst(NODEINST * ni,INTBIG dlx,INTBIG dly,INTBIG dhx,INTBIG dhy,INTBIG drot,INTBIG dtrans)463 void cli_linconmodifynodeinst(NODEINST *ni, INTBIG dlx, INTBIG dly, INTBIG dhx, INTBIG dhy,
464 INTBIG drot, INTBIG dtrans)
465 {
466 REGISTER INTSML oldang, oldtrans;
467 REGISTER INTBIG oldlx, oldhx, oldly, oldhy, ox, oy, oldxA, oldyA, oldxB, oldyB, oldlen,
468 thisend, thatend;
469 INTBIG ax[2], ay[2], onox, onoy, nox, noy;
470 XARRAY trans;
471 REGISTER PORTARCINST *pi;
472 REGISTER ARCINST *ai;
473 REGISTER CHANGE *cha;
474
475 /* if simple rotation on transposed nodeinst, reverse rotation */
476 if (ni->transpose != 0 && dtrans == 0) drot = (3600 - drot) % 3600;
477
478 /* make changes to the nodeinst */
479 oldang = ni->rotation; ni->rotation = (INTSML)((ni->rotation + drot) % 3600);
480 oldtrans = ni->transpose; ni->transpose = (INTSML)((ni->transpose + dtrans) & 1);
481 oldlx = ni->lowx; ni->lowx += dlx;
482 oldhx = ni->highx; ni->highx += dhx;
483 oldly = ni->lowy; ni->lowy += dly;
484 oldhy = ni->highy; ni->highy += dhy;
485 updategeom(ni->geom, ni->parent);
486
487 /* mark that this nodeinst has changed */
488 ni->changeaddr = (CHAR *)db_change((INTBIG)ni, NODEINSTMOD, oldlx, oldly,
489 oldhx, oldhy, oldang, oldtrans);
490
491 /* look at all of the arcs on this nodeinst */
492 for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
493 {
494 ai = pi->conarcinst;
495
496 /* figure where each end of the arcinst is, ignore internal arcs */
497 thisend = thatend = 0;
498 if (ai->end[0].nodeinst == ni) thatend++;
499 if (ai->end[1].nodeinst == ni) thisend++;
500 if (thisend == thatend)
501 {
502 /* arc is internal: compute old center of nodeinst */
503 cha = (CHANGE *)ni->changeaddr;
504 ox = (cha->p1 + cha->p3) / 2;
505 oy = (cha->p2 + cha->p4) / 2;
506
507 /* prepare transformation matrix */
508 makeangle(drot, dtrans, trans);
509
510 /* determine the new ends of the arcinst */
511 cla_adjustmatrix(ni, ai->end[0].portarcinst->proto, trans);
512 xform(ai->end[0].xpos-ox, ai->end[0].ypos-oy, &ax[0], &ay[0], trans);
513 cla_adjustmatrix(ni, ai->end[1].portarcinst->proto, trans);
514 xform(ai->end[1].xpos-ox, ai->end[1].ypos-oy, &ax[1], &ay[1], trans);
515 } else
516 {
517 /* external arc: compute its position on modified nodeinst */
518 cla_oldportposition(ni,ai->end[thisend].portarcinst->proto,&onox,&onoy);
519 portposition(ni, ai->end[thisend].portarcinst->proto, &nox, &noy);
520 ax[thisend] = ai->end[thisend].xpos + nox - onox;
521 ay[thisend] = ai->end[thisend].ypos + noy - onoy;
522
523 /* set position of other end of arcinst */
524 ax[thatend] = ai->end[thatend].xpos;
525 ay[thatend] = ai->end[thatend].ypos;
526 }
527
528 /* check for null arcinst motion */
529 if (ax[0] == ai->end[0].xpos && ay[0] == ai->end[0].ypos &&
530 ax[1] == ai->end[1].xpos && ay[1] == ai->end[1].ypos) continue;
531
532 /* demand constraint satisfaction if arc with properties moved */
533 if (getvalkey((INTBIG)ai, VARCINST, VINTEGER|VISARRAY, cli_properties_key) != NOVARIABLE)
534 {
535 db_setchangecell(ai->parent);
536 db_forcehierarchicalanalysis(ai->parent);
537 }
538
539 (void)db_change((INTBIG)ai, OBJECTSTART, VARCINST, 0, 0, 0, 0, 0);
540
541 /* save old state */
542 oldxA = ai->end[0].xpos; oldyA = ai->end[0].ypos;
543 oldxB = ai->end[1].xpos; oldyB = ai->end[1].ypos;
544 oldlen = ai->length;
545
546 /* set the proper arcinst position */
547 ai->end[0].xpos = ax[0];
548 ai->end[0].ypos = ay[0];
549 ai->end[1].xpos = ax[1];
550 ai->end[1].ypos = ay[1];
551 ai->length = computedistance(ax[0],ay[0], ax[1],ay[1]);
552 determineangle(ai);
553 updategeom(ai->geom, ai->parent);
554
555 /* announce new arcinst change */
556 ai->changeaddr = (CHAR *)db_change((INTBIG)ai, ARCINSTMOD, oldxA,
557 oldyA, oldxB, oldyB, ai->width, oldlen);
558 (void)db_change((INTBIG)ai, OBJECTEND, VARCINST, 0, 0, 0, 0, 0);
559 cli_eq_modarc(ai);
560 }
561 cli_eq_modnode(ni);
562 }
563
cli_linconmodifynodeinsts(INTBIG count,NODEINST ** ni,INTBIG * dlx,INTBIG * dly,INTBIG * dhx,INTBIG * dhy,INTBIG * drot,INTBIG * dtrans)564 void cli_linconmodifynodeinsts(INTBIG count, NODEINST **ni, INTBIG *dlx, INTBIG *dly,
565 INTBIG *dhx, INTBIG *dhy, INTBIG *drot, INTBIG *dtrans)
566 {
567 REGISTER INTBIG i;
568
569 for(i=0; i<count; i++)
570 cli_linconmodifynodeinst(ni[i], dlx[i], dly[i], dhx[i], dhy[i], drot[i], dtrans[i]);
571 }
572
cli_linconmodifyarcinst(ARCINST * ai,INTBIG oldx0,INTBIG oldy0,INTBIG oldx1,INTBIG oldy1,INTBIG oldwid,INTBIG oldlen)573 void cli_linconmodifyarcinst(ARCINST *ai, INTBIG oldx0, INTBIG oldy0, INTBIG oldx1, INTBIG oldy1, INTBIG oldwid, INTBIG oldlen)
574 {
575 cli_eq_modarc(ai);
576 }
577
578 /*
579 * routine to accept a constraint property in "changedata" for arcinst "ai".
580 * The value of "changetype" determines what to do: 0 means set the new property,
581 * 1 means add the new property, 2 means remove all properties, 3 means print all
582 * properties. The constraint is of the form "VARIABLE OPERATOR VALUE" where:
583 * VARIABLE = "x" | "y"
584 * OPERATOR = "==" | ">=" | "<="
585 * VALUE = numeric constant in WHOLE units
586 * the routine returns nonzero if the change is ineffective or impossible
587 */
cli_linconsetobject(INTBIG addr,INTBIG type,INTBIG changetype,INTBIG changedata)588 BOOLEAN cli_linconsetobject(INTBIG addr, INTBIG type, INTBIG changetype, INTBIG changedata)
589 {
590 REGISTER ARCINST *ai;
591 REGISTER CHAR *str, *pack, *pt;
592 REGISTER BOOLEAN error;
593 REGISTER INTBIG variable, value, len, i, oper;
594 REGISTER LINCON *conptr;
595 REGISTER VARIABLE *var;
596
597 if ((type&VTYPE) != VARCINST) return(TRUE);
598 ai = (ARCINST *)addr;
599
600 /* handle printing of constraints */
601 if (changetype == 3)
602 {
603 var = getvalkey((INTBIG)ai, VARCINST, VINTEGER|VISARRAY, cli_properties_key);
604 if (var == NOVARIABLE) ttyputmsg(M_("No linear constraint properties")); else
605 {
606 len = getlength(var) / LINCONSIZE;
607 ttyputmsg(M_("%ld %s on this arc:"), len, makeplural(M_("constraint"), len));
608 for(i=0; i<len; i++)
609 {
610 conptr = &((LINCON *)var->addr)[i];
611 ttyputmsg(M_("Constraint %ld: %s %s %s"), i+1, (conptr->variable<=1 ? x_("x") : x_("y")),
612 cli_linconops[conptr->oper], frtoa(conptr->value));
613 }
614 }
615 return(FALSE);
616 }
617
618 /* handle deletion of constraints */
619 if (changetype == 2)
620 {
621 var = getvalkey((INTBIG)ai, VARCINST, VINTEGER|VISARRAY, cli_properties_key);
622 if (var == NOVARIABLE)
623 {
624 ttyputerr(M_("No constraints on this arc"));
625 return(TRUE);
626 }
627 len = getlength(var) / LINCONSIZE;
628 (void)delvalkey((INTBIG)ai, VARCINST, cli_properties_key);
629 cli_eq_modarc(ai);
630 db_setchangecell(ai->parent);
631 db_forcehierarchicalanalysis(ai->parent);
632 return(FALSE);
633 }
634
635 /* adding a new constraint: parse it */
636 str = (CHAR *)changedata;
637
638 /* eliminate blanks in the string */
639 for(pack = pt = str; *pt != 0; pt++) if (*pt != ' ') *pack++ = *pt;
640 *pack = 0;
641
642 /* make sure the constraint string is valid */
643 error = FALSE;
644 pt = str;
645
646 /* get the variable */
647 if (namesamen(pt, x_("x"), 1) == 0)
648 {
649 if (ai->end[0].xpos < ai->end[1].xpos) variable = 1; else
650 variable = 0;
651 } else if (namesamen(pt, x_("y"), 1) == 0)
652 {
653 if (ai->end[0].ypos < ai->end[1].ypos) variable = 3; else
654 variable = 2;
655 } else
656 {
657 ttyputerr(M_("Cannot parse variable (should be 'X' or 'Y')"));
658 error = TRUE;
659 }
660 pt++;
661
662 /* get the operator */
663 for(i=0; i<3; i++)
664 if (namesamen(pt, cli_linconops[i], estrlen(cli_linconops[i])) == 0)
665 break;
666 if (i < 3)
667 {
668 oper = i;
669 pt += estrlen(cli_linconops[i]);
670 } else
671 {
672 ttyputerr(M_("Cannot parse operator (must be '>=', '==', or '<=')"));
673 error = TRUE;
674 }
675
676 /* get the value */
677 if (!error && isanumber(pt))
678 {
679 value = atofr(pt);
680 if (value < 0)
681 {
682 ttyputerr(M_("Values must be positive"));
683 error = TRUE;
684 }
685 } else
686 {
687 ttyputerr(M_("Invalid number on right (%s)"), pt);
688 error = TRUE;
689 }
690
691 if (error)
692 {
693 ttyputmsg(M_("Cannot accept linear constraint '%s'"), str);
694 return(TRUE);
695 }
696
697 db_setchangecell(ai->parent);
698 db_forcehierarchicalanalysis(ai->parent);
699 if (cli_addarcconstraint(ai, variable, oper, value, changetype)) return(TRUE);
700 if (changetype == 0) ttyputverbose(M_("Constraint set")); else
701 ttyputverbose(M_("Constraint added"));
702 return(FALSE);
703 }
704
705 /*
706 * routine to add a constraint to arc "ai". The constraint is on variable
707 * "variable" (0=left, 1=right, 2=down, 3=up), operation "oper" (0 for >=,
708 * 1 for ==, 2 for <=) and has a value of "value". If "add" is nonzero, add
709 * this constraint, otherwise replace existing constraints. Returns true
710 * if there is an error.
711 */
cli_addarcconstraint(ARCINST * ai,INTBIG variable,INTBIG oper,INTBIG value,INTBIG add)712 BOOLEAN cli_addarcconstraint(ARCINST *ai, INTBIG variable, INTBIG oper, INTBIG value,
713 INTBIG add)
714 {
715 REGISTER VARIABLE *var;
716 LINCON thiscon;
717 REGISTER LINCON *conptr, *conlist;
718 REGISTER INTBIG len, i;
719
720 var = getvalkey((INTBIG)ai, VARCINST, VINTEGER|VISARRAY, cli_properties_key);
721 if (var == NOVARIABLE || add == 0)
722 {
723 thiscon.variable = variable;
724 thiscon.oper = oper;
725 thiscon.value = value;
726 (void)setvalkey((INTBIG)ai, VARCINST, cli_properties_key, (INTBIG)&thiscon,
727 VINTEGER|VISARRAY|(LINCONSIZE<<VLENGTHSH));
728 cli_eq_modarc(ai);
729 return(FALSE);
730 }
731
732 len = getlength(var) / LINCONSIZE;
733 conlist = (LINCON *)emalloc(((len+1) * (sizeof (LINCON))), el_tempcluster);
734 if (conlist == 0)
735 {
736 ttyputnomemory();
737 return(TRUE);
738 }
739 for(i=0; i<len; i++)
740 {
741 conptr = &((LINCON *)var->addr)[i];
742 conlist[i].variable = conptr->variable;
743 conlist[i].oper = conptr->oper;
744 conlist[i].value = conptr->value;
745 }
746 conlist[len].variable = variable;
747 conlist[len].oper = oper;
748 conlist[len].value = value;
749 (void)setvalkey((INTBIG)ai, VARCINST, cli_properties_key, (INTBIG)conlist,
750 VINTEGER|VISARRAY|((LINCONSIZE*(len+1))<<VLENGTHSH));
751 cli_eq_modarc(ai);
752 efree((CHAR *)conlist);
753 return(FALSE);
754 }
755
756 /*
757 * routine to delete a constraint from arc "ai". The constraint is on variable
758 * "variable" (0=left, 1=right, 2=down, 3=up), operation "oper" (0 for >=,
759 * 1 for ==, 2 for <=) and has a value of "value".
760 */
cli_deletearcconstraint(ARCINST * ai,INTBIG variable,INTBIG oper,INTBIG value)761 void cli_deletearcconstraint(ARCINST *ai, INTBIG variable, INTBIG oper, INTBIG value)
762 {
763 REGISTER VARIABLE *var;
764 REGISTER LINCON *conptr, *conlist;
765 REGISTER INTBIG len, i, j, k;
766
767 var = getvalkey((INTBIG)ai, VARCINST, VINTEGER|VISARRAY, cli_properties_key);
768 if (var == NOVARIABLE) return;
769
770 /* search for the constraint */
771 len = getlength(var) / LINCONSIZE;
772 for(i=0; i<len; i++)
773 {
774 conptr = &((LINCON *)var->addr)[i];
775 if (conptr->variable != variable) continue;
776 if (conptr->oper != oper) continue;
777 if (conptr->value != value) continue;
778 break;
779 }
780 if (i >= len) return;
781
782 /* if this is the only constraint, delete the variable */
783 if (len == 1)
784 {
785 (void)delvalkey((INTBIG)ai, VARCINST, cli_properties_key);
786 cli_eq_modarc(ai);
787 return;
788 }
789
790 conlist = (LINCON *)emalloc(((len-1) * (sizeof (LINCON))), el_tempcluster);
791 if (conlist == 0)
792 {
793 ttyputnomemory();
794 return;
795 }
796 k = 0;
797 for(j=0; j<len; j++)
798 {
799 if (j == i) continue;
800 conptr = &((LINCON *)var->addr)[j];
801 conlist[k].variable = conptr->variable;
802 conlist[k].oper = conptr->oper;
803 conlist[k].value = conptr->value;
804 k++;
805 }
806 (void)setvalkey((INTBIG)ai, VARCINST, cli_properties_key, (INTBIG)conlist,
807 VINTEGER|VISARRAY|((LINCONSIZE*(len-1))<<VLENGTHSH));
808 efree((CHAR *)conlist);
809 cli_eq_modarc(ai);
810 }
811
cli_linconmodifyportproto(PORTPROTO * pp,NODEINST * oni,PORTPROTO * opp)812 void cli_linconmodifyportproto(PORTPROTO *pp, NODEINST *oni, PORTPROTO *opp)
813 {
814 cli_eq_modport(pp);
815 }
816
cli_linconmodifynodeproto(NODEPROTO * np)817 void cli_linconmodifynodeproto(NODEPROTO *np) {}
cli_linconmodifydescript(INTBIG addr,INTBIG type,INTBIG key,UINTBIG * descript)818 void cli_linconmodifydescript(INTBIG addr, INTBIG type, INTBIG key, UINTBIG *descript) {}
cli_linconnewlib(LIBRARY * lib)819 void cli_linconnewlib(LIBRARY *lib) {}
cli_linconkilllib(LIBRARY * lib)820 void cli_linconkilllib(LIBRARY *lib) {}
821
cli_linconnewvariable(INTBIG addr,INTBIG type,INTBIG key,INTBIG newtype)822 void cli_linconnewvariable(INTBIG addr, INTBIG type, INTBIG key, INTBIG newtype)
823 {
824 if ((newtype&VCREF) != 0) return;
825 if (type == VARCINST)
826 {
827 if (key == cli_properties_key)
828 {
829 /* mark the parent of this arc as changed */
830 db_setchangecell(((ARCINST *)addr)->parent);
831 db_forcehierarchicalanalysis(((ARCINST *)addr)->parent);
832 }
833 }
834 cli_eq_newvar(addr, type, key);
835 }
836
cli_linconkillvariable(INTBIG addr,INTBIG type,INTBIG key,INTBIG oldaddr,INTBIG oldtype,UINTBIG * olddescript)837 void cli_linconkillvariable(INTBIG addr, INTBIG type, INTBIG key, INTBIG oldaddr,
838 INTBIG oldtype, UINTBIG *olddescript)
839 {
840 if ((oldtype&VCREF) != 0) return;
841 if ((type&VTYPE) == VARCINST)
842 {
843 if (key == cli_properties_key)
844 {
845 /* mark the parent of this arc as changed */
846 db_setchangecell(((ARCINST *)addr)->parent);
847 db_forcehierarchicalanalysis(((ARCINST *)addr)->parent);
848 }
849 }
850 cli_eq_killvar(addr, type, key);
851 }
852
cli_linconmodifyvariable(INTBIG addr,INTBIG type,INTBIG key,INTBIG stype,INTBIG aindex,INTBIG value)853 void cli_linconmodifyvariable(INTBIG addr, INTBIG type, INTBIG key, INTBIG stype, INTBIG aindex,
854 INTBIG value)
855 {
856 if (type == VARCINST)
857 {
858 if (key == cli_properties_key)
859 {
860 /* mark the parent of this arc as changed */
861 db_setchangecell(((ARCINST *)addr)->parent);
862 db_forcehierarchicalanalysis(((ARCINST *)addr)->parent);
863 }
864 }
865 }
866
cli_linconinsertvariable(INTBIG addr,INTBIG type,INTBIG key,INTBIG aindex)867 void cli_linconinsertvariable(INTBIG addr, INTBIG type, INTBIG key, INTBIG aindex) {}
868
cli_lincondeletevariable(INTBIG addr,INTBIG type,INTBIG key,INTBIG aindex,INTBIG oldval)869 void cli_lincondeletevariable(INTBIG addr, INTBIG type, INTBIG key, INTBIG aindex,
870 INTBIG oldval) {}
871
cli_linconsetvariable(void)872 void cli_linconsetvariable(void) {}
873
874 /***************************** CONSTRAINT SOLVING *****************************/
875
876 /*
877 * routine to solve the linear constraints in cell "np". If "minimum" is
878 * true, re-solve for minimum spacing even if the network is satisfied.
879 * If "noanchors" is true, do not presume anchoring of the components that
880 * just changed
881 */
cli_solvecell(NODEPROTO * np,BOOLEAN minimum,BOOLEAN noanchors)882 void cli_solvecell(NODEPROTO *np, BOOLEAN minimum, BOOLEAN noanchors)
883 {
884 CONNODE *firstx, *firsty;
885 REGISTER BOOLEAN failure, xnotsolved, ynotsolved;
886
887 /* build constraint network in both axes */
888 xnotsolved = cli_buildconstraints(np, &firstx, FALSE, noanchors);
889 ynotsolved = cli_buildconstraints(np, &firsty, TRUE, noanchors);
890
891 /* quit now if all constraints are solved */
892 if (!xnotsolved && !ynotsolved && !minimum)
893 {
894 if (cli_conlindebug) ttyputmsg(M_("All constraints are met"));
895 cli_deleteconstraints(firstx);
896 cli_deleteconstraints(firsty);
897 return;
898 }
899
900 /* adjust constraint network for node center offsets */
901 cli_adjustconstraints(firstx, FALSE);
902 cli_adjustconstraints(firsty, TRUE);
903
904 if (cli_conlindebug)
905 {
906 cli_printconstraints(firstx, M_("Horizontal constraints:"), x_("X"));
907 if (!xnotsolved) ttyputmsg(M_("HORIZONTAL CONSTRAINTS ARE ALL MET"));
908 cli_printconstraints(firsty, M_("Vertical constraints:"), x_("Y"));
909 if (!ynotsolved) ttyputmsg(M_("VERTICAL CONSTRAINTS ARE ALL MET"));
910 }
911
912 /* solve the networks and determine the success */
913 failure = FALSE;
914 if (xnotsolved || minimum)
915 {
916 if (cli_conlindebug) ttyputmsg(M_("Solving X constraints:"));
917 if (cli_solveconstraints(firstx, minimum))
918 {
919 ttyputmsg(M_("X constraint network cannot be solved!"));
920 failure = TRUE;
921 }
922 }
923 if (ynotsolved || minimum)
924 {
925 if (cli_conlindebug) ttyputmsg(M_("Solving Y constraints:"));
926 if (cli_solveconstraints(firsty, minimum))
927 {
928 ttyputmsg(M_("Y constraint network cannot be solved!"));
929 failure = TRUE;
930 }
931 }
932
933 /* if networks are solved properly, apply them to the circuit */
934 if (!failure)
935 {
936 /* make the changes */
937 if (xnotsolved || minimum) cli_applyconstraints(np, firstx, FALSE);
938 if (ynotsolved || minimum) cli_applyconstraints(np, firsty, TRUE);
939 }
940
941 /* erase the constraint networks */
942 cli_deleteconstraints(firstx);
943 cli_deleteconstraints(firsty);
944 }
945
946 /*
947 * routine to convert the constraints found in cell "np" into a network of
948 * internal constraint nodes headed by "first". If "axis" is false, build
949 * the X constraints, otherwise build the Y constraints. If "noanchors" is
950 * true, do not presume presolution of changed nodes. The routine returns
951 * false if the constraints are already met.
952 */
cli_buildconstraints(NODEPROTO * np,CONNODE ** first,BOOLEAN axis,BOOLEAN noanchors)953 BOOLEAN cli_buildconstraints(NODEPROTO *np, CONNODE **first, BOOLEAN axis, BOOLEAN noanchors)
954 {
955 REGISTER VARIABLE *var;
956 REGISTER ARCINST *ai;
957 REGISTER INTBIG i, rev, len, opf, opt, gothor, gotver;
958 BOOLEAN notsolved;
959 REGISTER INTBIG vaf, vat, lambda;
960 REGISTER LINCON *conptr;
961 REGISTER NODEINST *ni;
962 REGISTER CONNODE *cn;
963
964 /* get the value of lambda */
965 lambda = lambdaofcell(np);
966
967 /* mark all real nodes as un-converted to constraint nodes */
968 for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
969 ni->temp1 = -1;
970
971 /* initialize the list of constraint nodes */
972 *first = NOCONNODE;
973
974 /* look at all constraints in the cell */
975 notsolved = FALSE;
976 for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
977 {
978 var = getvalkey((INTBIG)ai, VARCINST, VINTEGER|VISARRAY, cli_properties_key);
979 if (var == NOVARIABLE) len = 0; else len = getlength(var) / LINCONSIZE;
980 gothor = gotver = 0;
981 for(i=0; i<len; i++)
982 {
983 conptr = &((LINCON *)var->addr)[i];
984
985 /* place "from" and "to" nodes in the correct orientation */
986 switch (conptr->variable)
987 {
988 case CLLEFT:
989 gothor++;
990 if (axis) continue;
991 rev = 1;
992 break;
993 case CLRIGHT:
994 gothor++;
995 if (axis) continue;
996 rev = 0;
997 break;
998 case CLDOWN:
999 gotver++;
1000 if (!axis) continue;
1001 rev = 1;
1002 break;
1003 case CLUP:
1004 gotver++;
1005 if (!axis) continue;
1006 rev = 0;
1007 break;
1008 }
1009
1010 if (rev != 0)
1011 {
1012 opt = conptr->oper; vat = conptr->value*lambda/WHOLE;
1013 opf = 2 - conptr->oper; vaf = -vat;
1014 } else
1015 {
1016 opf = conptr->oper; vaf = conptr->value*lambda/WHOLE;
1017 opt = 2 - conptr->oper; vat = -vaf;
1018 }
1019
1020 /* place the constraint in the list */
1021 cli_addconstraint(ai, first, opf, vaf, opt, vat, noanchors);
1022
1023 /* see if this constraint is already met */
1024 switch (conptr->variable)
1025 {
1026 case CLLEFT:
1027 switch (conptr->oper)
1028 {
1029 case CLEQUALS:
1030 if (ai->end[0].xpos - ai->end[1].xpos != vat) notsolved = TRUE;
1031 break;
1032 case CLGEQUALS:
1033 if (ai->end[0].xpos - ai->end[1].xpos < vat) notsolved = TRUE;
1034 break;
1035 case CLLEQUALS:
1036 if (ai->end[0].xpos - ai->end[1].xpos > vat) notsolved = TRUE;
1037 break;
1038 }
1039 break;
1040
1041 case CLRIGHT:
1042 switch (conptr->oper)
1043 {
1044 case CLEQUALS:
1045 if (ai->end[1].xpos - ai->end[0].xpos != vaf) notsolved = TRUE;
1046 break;
1047 case CLGEQUALS:
1048 if (ai->end[1].xpos - ai->end[0].xpos < vaf) notsolved = TRUE;
1049 break;
1050 case CLLEQUALS:
1051 if (ai->end[1].xpos - ai->end[0].xpos > vaf) notsolved = TRUE;
1052 break;
1053 }
1054 break;
1055
1056 case CLDOWN:
1057 switch (conptr->oper)
1058 {
1059 case CLEQUALS:
1060 if (ai->end[0].ypos - ai->end[1].ypos != vat) notsolved = TRUE;
1061 break;
1062 case CLGEQUALS:
1063 if (ai->end[0].ypos - ai->end[1].ypos < vat) notsolved = TRUE;
1064 break;
1065 case CLLEQUALS:
1066 if (ai->end[0].ypos - ai->end[1].ypos > vat) notsolved = TRUE;
1067 break;
1068 }
1069 break;
1070
1071 case CLUP:
1072 switch (conptr->oper)
1073 {
1074 case CLEQUALS:
1075 if (ai->end[1].ypos - ai->end[0].ypos != vaf) notsolved = TRUE;
1076 break;
1077 case CLGEQUALS:
1078 if (ai->end[1].ypos - ai->end[0].ypos < vaf) notsolved = TRUE;
1079 break;
1080 case CLLEQUALS:
1081 if (ai->end[1].ypos - ai->end[0].ypos > vaf) notsolved = TRUE;
1082 break;
1083 }
1084 break;
1085 }
1086 }
1087
1088 /* if Manhattan mode set, add implicit constraints */
1089 if (cli_manhattan)
1090 {
1091 if (!axis)
1092 {
1093 /* handle vertical arcs */
1094 if (gothor == 0 && gotver != 0)
1095 {
1096 cli_addconstraint(ai, first, CLEQUALS, 0, CLEQUALS, 0, noanchors);
1097 if (ai->end[0].xpos != ai->end[1].xpos) notsolved = TRUE;
1098 }
1099 } else
1100 {
1101 /* handle horizontal arcs */
1102 if (gotver == 0 && gothor != 0)
1103 {
1104 cli_addconstraint(ai, first, CLEQUALS, 0, CLEQUALS, 0, noanchors);
1105 if (ai->end[0].ypos != ai->end[1].ypos) notsolved = TRUE;
1106 }
1107 }
1108 }
1109 }
1110
1111 /* now assign proper values to each node */
1112 for(cn = *first; cn != NOCONNODE; cn = cn->nextconnode)
1113 if (!axis) cn->value = (cn->realn->highx + cn->realn->lowx) / 2; else
1114 cn->value = (cn->realn->highy + cn->realn->lowy) / 2;
1115
1116 return(notsolved);
1117 }
1118
1119 /*
1120 * routine to add a constraint between the nodes connected by arc "ai". The
1121 * constraint is added to the list "first" and has operator "opf", value "vaf"
1122 * on the from end (0) and operator "opt", value "vat" on the to end (1).
1123 * If "noanchors" is true, do not presume anchoring and presolution of
1124 * nodes.
1125 */
cli_addconstraint(ARCINST * ai,CONNODE ** first,INTBIG opf,INTBIG vaf,INTBIG opt,INTBIG vat,BOOLEAN noanchors)1126 void cli_addconstraint(ARCINST *ai, CONNODE **first, INTBIG opf, INTBIG vaf, INTBIG opt,
1127 INTBIG vat, BOOLEAN noanchors)
1128 {
1129 REGISTER CONARC *caf, *cat;
1130 REGISTER CONNODE *cnf, *cnt;
1131 REGISTER CHANGE *ch;
1132 REGISTER CHANGEBATCH *curbatch;
1133
1134 /* store this constraint as two constraint arcs */
1135 caf = cli_allocconarc();
1136 cat = cli_allocconarc();
1137 if (caf == NOCONARC || cat == NOCONARC)
1138 {
1139 ttyputnomemory();
1140 return;
1141 }
1142
1143 /* get current change batch */
1144 curbatch = db_getcurrentbatch();
1145
1146 /* get the "from" constraint node */
1147 cnf = (CONNODE *)ai->end[0].nodeinst->temp1;
1148 if (cnf == NOCONNODE)
1149 {
1150 cnf = cli_allocconnode();
1151 if (cnf == NOCONNODE)
1152 {
1153 ttyputnomemory();
1154 return;
1155 }
1156 cnf->realn = ai->end[0].nodeinst;
1157 cnf->solved = FALSE;
1158 if (!noanchors)
1159 {
1160 if (curbatch != NOCHANGEBATCH)
1161 for(ch = curbatch->changehead; ch != NOCHANGE; ch = ch->nextchange)
1162 if (ch->changetype == NODEINSTMOD && (NODEINST *)ch->entryaddr == cnf->realn)
1163 cnf->solved = TRUE;
1164 }
1165 cnf->firstconarc = NOCONARC;
1166 cnf->nextconnode = *first;
1167 *first = cnf;
1168 ai->end[0].nodeinst->temp1 = (INTBIG)cnf;
1169 }
1170
1171 /* get the "to" constraint node */
1172 cnt = (CONNODE *)ai->end[1].nodeinst->temp1;
1173 if (cnt == NOCONNODE)
1174 {
1175 cnt = cli_allocconnode();
1176 if (cnt == NOCONNODE)
1177 {
1178 ttyputnomemory();
1179 return;
1180 }
1181 cnt->realn = ai->end[1].nodeinst;
1182 cnt->solved = FALSE;
1183 if (!noanchors)
1184 {
1185 if (curbatch != NOCHANGEBATCH)
1186 for(ch = curbatch->changehead; ch != NOCHANGE; ch = ch->nextchange)
1187 if (ch->changetype == NODEINSTMOD && (NODEINST *)ch->entryaddr == cnt->realn)
1188 cnt->solved = TRUE;
1189 }
1190 cnt->firstconarc = NOCONARC;
1191 cnt->nextconnode = *first;
1192 *first = cnt;
1193 ai->end[1].nodeinst->temp1 = (INTBIG)cnt;
1194 }
1195
1196 /* append constraint arc "cat" to constraint node "cnt" */
1197 cat->nextconarc = cnt->firstconarc;
1198 cnt->firstconarc = cat;
1199 cat->other = cnf;
1200
1201 /* append constraint arc "caf" to constraint node "cnf" */
1202 caf->nextconarc = cnf->firstconarc;
1203 cnf->firstconarc = caf;
1204 caf->other = cnt;
1205
1206 caf->oper = opf;
1207 caf->value = vaf;
1208 cat->oper = opt;
1209 cat->value = vat;
1210 cat->reala = caf->reala = ai;
1211 }
1212
cli_adjustconstraints(CONNODE * first,BOOLEAN axis)1213 void cli_adjustconstraints(CONNODE *first, BOOLEAN axis)
1214 {
1215 REGISTER CONNODE *cn;
1216 REGISTER CONARC *ca;
1217 REGISTER ARCINST *ai;
1218 REGISTER NODEINST *nif, *nit;
1219 REGISTER INTBIG centerf, centert, diff;
1220 REGISTER INTBIG fromend, toend;
1221
1222 for(cn = first; cn != NOCONNODE; cn = cn->nextconnode)
1223 {
1224 for(ca = cn->firstconarc; ca != NOCONARC; ca = ca->nextconarc)
1225 {
1226 ai = ca->reala;
1227 if (ai->end[0].nodeinst == cn->realn)
1228 {
1229 fromend = 0; toend = 1;
1230 } else
1231 {
1232 fromend = 1; toend = 0;
1233 }
1234 nif = cn->realn; nit = ca->other->realn;
1235
1236 /* adjust constraint to get arc to node center */
1237 if (!axis)
1238 {
1239 /* adjust for X axis constraints */
1240 centerf = (nif->lowx + nif->highx) / 2;
1241 centert = (nit->lowx + nit->highx) / 2;
1242 if (centerf != ai->end[fromend].xpos)
1243 {
1244 diff = ai->end[fromend].xpos - centerf;
1245 ca->value += diff;
1246 }
1247 if (centert != ai->end[toend].xpos)
1248 {
1249 diff = centert - ai->end[toend].xpos;
1250 ca->value += diff;
1251 }
1252 } else
1253 {
1254 /* adjust for Y axis constraints */
1255 centerf = (nif->lowy + nif->highy) / 2;
1256 centert = (nit->lowy + nit->highy) / 2;
1257 if (centerf != ai->end[fromend].ypos)
1258 {
1259 diff = ai->end[fromend].ypos - centerf;
1260 ca->value += diff;
1261 }
1262 if (centert != ai->end[toend].ypos)
1263 {
1264 diff = centert - ai->end[toend].ypos;
1265 ca->value += diff;
1266 }
1267 }
1268 }
1269 }
1270 }
1271
1272 /*
1273 * routine to solve the constraint network in "first". If "minimum" is true,
1274 * reduce to minimum distances (rahter than allowing larger values)
1275 */
cli_solveconstraints(CONNODE * first,BOOLEAN minimum)1276 BOOLEAN cli_solveconstraints(CONNODE *first, BOOLEAN minimum)
1277 {
1278 REGISTER CONNODE *cn, *cno;
1279 REGISTER CONARC *ca;
1280 REGISTER BOOLEAN satisfied;
1281
1282 /* if there are no constraints, all is well */
1283 if (first == NOCONNODE) return(FALSE);
1284
1285 /* fill in the values */
1286 for (;;)
1287 {
1288 /* choose a node that is solved and constrains to one that is not */
1289 for(cn = first; cn != NOCONNODE; cn = cn->nextconnode)
1290 {
1291 if (!cn->solved) continue;
1292 for(ca = cn->firstconarc; ca != NOCONARC; ca = ca->nextconarc)
1293 if (!ca->other->solved) break;
1294 if (ca != NOCONARC) break;
1295 }
1296
1297 /* if no constraints relate solved to unsolved, take any unsolved */
1298 if (cn == NOCONNODE)
1299 {
1300 for(cn = first; cn != NOCONNODE; cn = cn->nextconnode)
1301 if (!cn->solved) break;
1302 if (cn == NOCONNODE) break;
1303
1304 /* mark this constraint node as solved */
1305 cn->solved = TRUE;
1306 }
1307
1308 if (cli_conlindebug) ttyputmsg(M_(" Working from node %s at %ld"),
1309 describenodeinst(cn->realn), cn->value);
1310
1311 /* check all constraint arcs to other nodes */
1312 for(ca = cn->firstconarc; ca != NOCONARC; ca = ca->nextconarc)
1313 {
1314 /* see if other node meets the constraint */
1315 satisfied = FALSE;
1316 switch (ca->oper)
1317 {
1318 case CLEQUALS:
1319 if (ca->other->value == cn->value + ca->value) satisfied = TRUE;
1320 break;
1321 case CLGEQUALS:
1322 if (ca->other->value >= cn->value + ca->value) satisfied = TRUE;
1323 break;
1324 case CLLEQUALS:
1325 if (ca->other->value <= cn->value + ca->value) satisfied = TRUE;
1326 break;
1327 }
1328
1329 /* if the constraint meets perfectly, ignore it */
1330 if (ca->other->value == cn->value + ca->value)
1331 {
1332 if (cli_conlindebug)
1333 ttyputmsg(M_(" Constraint to %s is met perfectly"),
1334 describenodeinst(ca->other->realn));
1335 ca->other->solved = TRUE;
1336 continue;
1337 }
1338
1339 /* if the constraint is ok and doesn't have to be exact, ignore it */
1340 if (satisfied && !minimum)
1341 {
1342 if (cli_conlindebug)
1343 ttyputmsg(M_(" Constraint to %s is met adequately"),
1344 describenodeinst(ca->other->realn));
1345 ca->other->solved = TRUE;
1346 continue;
1347 }
1348
1349 /* if other node is unsolved, set a value on it */
1350 if (!ca->other->solved)
1351 {
1352 ca->other->value = cn->value + ca->value;
1353 if (cli_conlindebug)
1354 ttyputmsg(M_(" Constraint to unsolved %s adjusts it to %ld"),
1355 describenodeinst(ca->other->realn), ca->other->value);
1356 ca->other->solved = TRUE;
1357 continue;
1358 }
1359
1360 /* other node solved: solve for this node's value */
1361 for(cno = first; cno != NOCONNODE; cno = cno->nextconnode)
1362 cno->loopflag = FALSE;
1363 cn->loopflag = TRUE;
1364 if (cli_conlindebug)
1365 ttyputmsg(M_(" Constraint to solved %s takes some work:"),
1366 describenodeinst(ca->other->realn));
1367 if (cli_satisfy(cn, ca)) return(TRUE);
1368 }
1369 }
1370 return(FALSE);
1371 }
1372
1373 /*
1374 * routine to iteratively force constraint arc "ca" on constrant node "cn"
1375 * to be satisfied
1376 */
cli_satisfy(CONNODE * cn,CONARC * ca)1377 BOOLEAN cli_satisfy(CONNODE *cn, CONARC *ca)
1378 {
1379 REGISTER CONNODE *cno;
1380 REGISTER CONARC *cao;
1381 REGISTER BOOLEAN satisfied;
1382
1383 /* get the constraint node on the other end */
1384 cno = ca->other;
1385 cno->loopflag = TRUE;
1386
1387 /* set new value for that constraint node */
1388 cno->value = cn->value + ca->value;
1389 if (cli_conlindebug) ttyputmsg(M_(" Setting %s to %ld"),
1390 describenodeinst(cno->realn), cno->value);
1391
1392 /* see if that constraint node is now satisfied */
1393 for(cao=cno->firstconarc; cao!=NOCONARC; cao=cao->nextconarc)
1394 {
1395 if (!cao->other->solved) continue;
1396 satisfied = FALSE;
1397 switch (cao->oper)
1398 {
1399 case CLEQUALS:
1400 if (cao->other->value == cno->value + cao->value) satisfied = TRUE;
1401 break;
1402 case CLGEQUALS:
1403 if (cao->other->value >= cno->value + cao->value) satisfied = TRUE;
1404 break;
1405 case CLLEQUALS:
1406 if (cao->other->value <= cno->value + cao->value) satisfied = TRUE;
1407 break;
1408 }
1409 if (!satisfied)
1410 {
1411 /* if this is a loop, quit now */
1412 if (cao->other->loopflag) return(TRUE);
1413
1414 /* attempt to recursively satisfy the constraint */
1415 if (cli_satisfy(cno, cao)) return(TRUE);
1416 }
1417 }
1418 return(FALSE);
1419 }
1420
cli_applyconstraints(NODEPROTO * np,CONNODE * first,BOOLEAN axis)1421 void cli_applyconstraints(NODEPROTO *np, CONNODE *first, BOOLEAN axis)
1422 {
1423 REGISTER CONNODE *cn;
1424 REGISTER ARCINST *ai;
1425 REGISTER NODEINST *ni;
1426 REGISTER PORTARCINST *pi;
1427 REGISTER INTBIG lowestcon, lowestcir, center, adjust, oldlx, oldhx,
1428 oldly, oldhy, oldxA, oldyA, oldxB, oldyB, oldlen;
1429
1430 /* if there are no constraints, circuit is solved */
1431 if (first == NOCONNODE) return;
1432
1433 /* look for the lowest value in the constraints and the circuit */
1434 lowestcon = lowestcir = 0;
1435
1436 /* mark this cell as changed so its size will be adjusted */
1437 db_setchangecell(first->realn->parent);
1438
1439 /* now adjust the nodes in the circuit */
1440 for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
1441 ai->temp1 = 0;
1442 for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
1443 ni->temp1 = 0;
1444 for(cn = first; cn != NOCONNODE; cn = cn->nextconnode)
1445 {
1446 ni = cn->realn;
1447 if (!axis) center = (ni->lowx + ni->highx) / 2; else
1448 center = (ni->lowy + ni->highy) / 2;
1449 adjust = (cn->value - lowestcon) - (center - lowestcir);
1450 if (adjust == 0) continue;
1451 ni->temp1 = adjust;
1452 oldlx = ni->lowx; oldhx = ni->highx;
1453 oldly = ni->lowy; oldhy = ni->highy;
1454 (void)db_change((INTBIG)ni, OBJECTSTART, VNODEINST, 0, 0, 0, 0, 0);
1455 if (!axis)
1456 {
1457 ni->lowx += adjust; ni->highx += adjust;
1458 } else
1459 {
1460 ni->lowy += adjust; ni->highy += adjust;
1461 }
1462 updategeom(ni->geom, ni->parent);
1463 ni->changeaddr = (CHAR *)db_change((INTBIG)ni, NODEINSTMOD, oldlx,
1464 oldly, oldhx, oldhy, ni->rotation, ni->transpose);
1465 (void)db_change((INTBIG)ni, OBJECTEND, VNODEINST, 0, 0, 0, 0, 0);
1466 cli_eq_modnode(ni);
1467 for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
1468 pi->conarcinst->temp1 = 1;
1469 }
1470
1471 /* finally adjust the arcs in the circuit */
1472 for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
1473 {
1474 if (ai->temp1 == 0) continue;
1475 oldxA = ai->end[0].xpos; oldyA = ai->end[0].ypos;
1476 oldxB = ai->end[1].xpos; oldyB = ai->end[1].ypos;
1477 oldlen = ai->length;
1478 (void)db_change((INTBIG)ai, OBJECTSTART, VARCINST, 0, 0, 0, 0, 0);
1479 if (!axis)
1480 {
1481 ai->end[0].xpos += ai->end[0].nodeinst->temp1;
1482 ai->end[1].xpos += ai->end[1].nodeinst->temp1;
1483 } else
1484 {
1485 ai->end[0].ypos += ai->end[0].nodeinst->temp1;
1486 ai->end[1].ypos += ai->end[1].nodeinst->temp1;
1487 }
1488 determineangle(ai);
1489 updategeom(ai->geom, ai->parent);
1490 ai->length = computedistance(ai->end[0].xpos,ai->end[0].ypos,
1491 ai->end[1].xpos,ai->end[1].ypos);
1492 ai->changeaddr = (CHAR *)db_change((INTBIG)ai, ARCINSTMOD, oldxA,
1493 oldyA, oldxB, oldyB, ai->width, oldlen);
1494 (void)db_change((INTBIG)ai, OBJECTEND, VARCINST, 0, 0, 0, 0, 0);
1495 cli_eq_modarc(ai);
1496 }
1497 }
1498
cli_deleteconstraints(CONNODE * first)1499 void cli_deleteconstraints(CONNODE *first)
1500 {
1501 REGISTER CONNODE *cn, *nextcn;
1502 REGISTER CONARC *ca, *nextca;
1503
1504 for(cn = first; cn != NOCONNODE; cn = nextcn)
1505 {
1506 for(ca = cn->firstconarc; ca != NOCONARC; ca = nextca)
1507 {
1508 nextca = ca->nextconarc;
1509 cli_freeconarc(ca);
1510 }
1511 nextcn = cn->nextconnode;
1512 cli_freeconnode(cn);
1513 }
1514 }
1515
cli_printconstraints(CONNODE * first,CHAR * message,CHAR * axis)1516 void cli_printconstraints(CONNODE *first, CHAR *message, CHAR *axis)
1517 {
1518 REGISTER CONNODE *cn;
1519 REGISTER CONARC *ca;
1520
1521 ttyputmsg(x_("%s"), message);
1522 for(cn = first; cn != NOCONNODE; cn = cn->nextconnode)
1523 {
1524 ttyputmsg(M_(" Node %s at %ld anchored=%d has these constraints:"),
1525 describenodeinst(cn->realn), cn->value, cn->solved);
1526 for(ca = cn->firstconarc; ca != NOCONARC; ca = ca->nextconarc)
1527 ttyputmsg(M_(" to node %s (%s %s %ld)"), describenodeinst(ca->other->realn), axis,
1528 cli_linconops[ca->oper], ca->value);
1529 }
1530 }
1531
1532 /*
1533 * routine to name all of the nodes in cell "np"
1534 */
1535 #define NOABBREV ((ABBREV *)-1)
1536
1537 typedef struct Iabbrev
1538 {
1539 CHAR *name;
1540 INTBIG aindex;
1541 struct Iabbrev *nextabbrev;
1542 } ABBREV;
1543
cli_nameallnodes(NODEPROTO * np)1544 void cli_nameallnodes(NODEPROTO *np)
1545 {
1546 REGISTER NODEINST *ni;
1547 REGISTER CHAR *pt;
1548 REGISTER BOOLEAN upper;
1549 REGISTER VARIABLE *var;
1550 REGISTER ABBREV *firstabbrev, *a, *nexta;
1551 CHAR line[20], *newname;
1552 REGISTER void *infstr;
1553
1554 firstabbrev = NOABBREV;
1555 for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
1556 {
1557 /* ignore this if it has a name */
1558 if (getvalkey((INTBIG)ni, VNODEINST, VSTRING, el_node_name_key) != NOVARIABLE) continue;
1559
1560 if (ni->proto->primindex == 0) upper = TRUE; else upper = FALSE;
1561 pt = makeabbrev(ni->proto->protoname, upper);
1562
1563 /* if the name is null, assign a name */
1564 if (estrlen(pt) == 0)
1565 {
1566 if (ni->proto->primindex == 0) pt = M_("CELL"); else pt = M_("node");
1567 }
1568
1569 /* find an abbreviation module for this */
1570 for(a = firstabbrev; a != NOABBREV; a = a->nextabbrev)
1571 if (namesame(a->name, pt) == 0) break;
1572 if (a == NOABBREV)
1573 {
1574 a = (ABBREV *)emalloc(sizeof (ABBREV), el_tempcluster);
1575 if (a == 0) break;
1576 (void)allocstring(&a->name, pt, el_tempcluster);
1577 a->nextabbrev = firstabbrev;
1578 firstabbrev = a;
1579 a->aindex = 1;
1580 }
1581
1582 /* assign a name to the node */
1583 infstr = initinfstr();
1584 addstringtoinfstr(infstr, a->name);
1585 (void)esnprintf(line, 20, x_("%ld"), a->aindex++);
1586 addstringtoinfstr(infstr, line);
1587 allocstring(&newname, returninfstr(infstr), el_tempcluster);
1588 var = setvalkey((INTBIG)ni, VNODEINST, el_node_name_key, (INTBIG)newname, VSTRING|VDISPLAY);
1589 efree(newname);
1590 if (var != NOVARIABLE)
1591 defaulttextsize(3, var->textdescript);
1592 }
1593
1594 /* free the abbreviation keys */
1595 for(a = firstabbrev; a != NOABBREV; a = nexta)
1596 {
1597 nexta = a->nextabbrev;
1598 efree(a->name);
1599 efree((CHAR *)a);
1600 }
1601 }
1602