1 /* -*- tab-width: 4 -*-
2 *
3 * Electric(tm) VLSI Design System
4 *
5 * File: usrarc.c
6 * User interface tool: simple arc routing
7 * Written by: Steven M. Rubin, Static Free Software
8 *
9 * Copyright (c) 2000 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 * support@staticfreesoft.com
30 */
31
32 #include "global.h"
33 #include "egraphics.h"
34 #include "usr.h"
35 #include "efunction.h"
36 #include "tecschem.h"
37 #include "tecgen.h"
38
39 /* prototypes for local routines */
40 static TECHNOLOGY *us_gettech(NODEINST*, PORTPROTO*);
41 static ARCINST *us_directarcinst(NODEINST*, PORTPROTO*, ARCPROTO*, NODEINST*, PORTPROTO*, ARCPROTO*,
42 NODEPROTO*, INTBIG, INTBIG, INTBIG, INTBIG, NODEINST**, INTBIG, INTBIG*, BOOLEAN);
43 static ARCINST *us_onebend(NODEINST*, PORTPROTO*, ARCPROTO*, NODEINST*, PORTPROTO*, ARCPROTO*,
44 NODEPROTO*, INTBIG, INTBIG, INTBIG, INTBIG, ARCINST**, NODEINST**, INTBIG*, BOOLEAN);
45 static ARCINST *us_twobend(NODEINST*, PORTPROTO*, ARCPROTO*, NODEINST*, PORTPROTO*, ARCPROTO*,
46 NODEPROTO*, INTBIG, INTBIG, INTBIG, INTBIG, ARCINST**, ARCINST**, NODEINST**, NODEINST**,
47 INTBIG*, BOOLEAN);
48 static INTBIG us_portdistance(NODEINST*, PORTPROTO*, INTBIG, INTBIG, INTBIG*, INTBIG*, INTBIG, BOOLEAN);
49 static BOOLEAN us_fitportangle(NODEINST*, PORTPROTO*, INTBIG, INTBIG);
50 static INTBIG us_figurearcwidth(ARCPROTO *typ, INTBIG wid, NODEINST *ni1, PORTPROTO *pp1,
51 INTBIG ox1, INTBIG oy1, NODEINST *ni2, PORTPROTO *pp2, INTBIG ox2, INTBIG oy2);
52 static NODEPROTO *us_findconnectingpin(ARCPROTO *ap, GEOM *geom, PORTPROTO *pp);
53 static void us_getproperconsize(NODEPROTO *con, ARCPROTO *fromap, INTBIG fwid,
54 ARCPROTO *toap, INTBIG twid, INTBIG *pxs, INTBIG *pys);
55 static BOOLEAN us_breakarcinsertnode(ARCINST *ai, NODEINST *ni, ARCINST **ai1, ARCINST **ai2);
56
57 /*
58 * routine to run an arcinst from "fromgeom" to "togeom". The prefered
59 * ports to use on these objects is "frompp" and "topp" (presuming that
60 * "fromgeom" and "togeom" are nodeinsts and "frompp" and "topp" are
61 * not NOPORTPROTO). The prefered layer for the arc is "typ" but
62 * this may be overridden if other layers are possible and the prefered
63 * layer isn't. The prefered location for the arcinst is closest to
64 * (prefx, prefy) if there is a choice. If "nozigzag" is true, do not
65 * make 3-arc connections (only 1 or 2). If the arcinst is run, the
66 * routine returns its address (if two or three arcs are created, their
67 * addresses are returned in "alt1" and "alt2", if contacts or pins are created,
68 * their addresses are returned in "con1" and "con2"). Otherwise, it issues an
69 * error and returns NOARCINST. The required angle increment for the
70 * arcs is "ang" tenth-degrees (900 for manhattan geometry). The number of
71 * arcs created is reported if "report" is TRUE.
72 */
aconnect(GEOM * fromgeom,PORTPROTO * frompp,GEOM * togeom,PORTPROTO * topp,ARCPROTO * typ,INTBIG prefx,INTBIG prefy,ARCINST ** alt1,ARCINST ** alt2,NODEINST ** con1,NODEINST ** con2,INTBIG ang,BOOLEAN nozigzag,BOOLEAN report)73 ARCINST *aconnect(GEOM *fromgeom, PORTPROTO *frompp, GEOM *togeom, PORTPROTO *topp,
74 ARCPROTO *typ, INTBIG prefx, INTBIG prefy,
75 ARCINST **alt1, ARCINST **alt2, NODEINST **con1, NODEINST **con2,
76 INTBIG ang, BOOLEAN nozigzag, BOOLEAN report)
77 {
78 REGISTER NODEINST *fromnodeinst, *tonodeinst, *ni;
79 REGISTER NODEPROTO *con;
80 REGISTER ARCINST *ai, *newai, *ai1, *ai2;
81 REGISTER PORTPROTO *fpt, *tpt, *pp;
82 REGISTER ARCPROTO *ap, *fap, *tap;
83 REGISTER TECHNOLOGY *tech, *tech1, *tech2;
84 REGISTER INTBIG j, t, ans, gent;
85 REGISTER INTBIG i, bestdist, bestx, besty, usecontact;
86 INTBIG x, y;
87 CHAR *result[2];
88 extern COMCOMP us_noyesp;
89 REGISTER void *infstr;
90
91 /* error check */
92 if (fromgeom == togeom && !fromgeom->entryisnode)
93 {
94 us_abortcommand(_("Cannot run from arc to itself"));
95 return(NOARCINST);
96 }
97
98 /* handle special case of an arcinst connecting to a cell */
99 if (!fromgeom->entryisnode || !togeom->entryisnode)
100 {
101 if (!fromgeom->entryisnode) ai = fromgeom->entryaddr.ai; else
102 ai = togeom->entryaddr.ai;
103 ni = NONODEINST;
104 if (fromgeom->entryisnode) { ni = fromgeom->entryaddr.ni; pp = frompp; }
105 if (togeom->entryisnode) { ni = togeom->entryaddr.ni; pp = topp; }
106
107 if (ni != NONODEINST && ni->proto->primindex == 0)
108 {
109 /* found cell connected to arcinst: search for closest portinst */
110 if (pp == NOPORTPROTO)
111 {
112 bestdist = MAXINTBIG;
113 for(fpt = ni->proto->firstportproto; fpt != NOPORTPROTO; fpt = fpt->nextportproto)
114 {
115 /* see if the arcinst can connect at this portinst */
116 for(i=0; fpt->connects[i] != NOARCPROTO; i++)
117 if (fpt->connects[i] == ai->proto) break;
118 if (fpt->connects[i] == NOARCPROTO) continue;
119
120 /* compute position for closest applicable portinst */
121 i = us_portdistance(ni, fpt, prefx, prefy, &x, &y,
122 defaultarcwidth(typ) - arcprotowidthoffset(typ), FALSE);
123 if (i > bestdist) continue;
124 bestdist = i; bestx = x; besty = y;
125 }
126
127 /* adjust prefered position to closest port (manhattan only!!!) */
128 if (bestdist < MAXINTBIG)
129 {
130 if (ai->end[0].xpos == ai->end[1].xpos) prefy = besty;
131 if (ai->end[0].ypos == ai->end[1].ypos) prefx = bestx;
132 }
133 }
134 }
135 }
136
137 /* if two arcs are selected and they cross, make only 1 contact */
138 fromnodeinst = NONODEINST;
139 if (!fromgeom->entryisnode && !togeom->entryisnode)
140 {
141 ai1 = fromgeom->entryaddr.ai;
142 ai2 = togeom->entryaddr.ai;
143 if (segintersect(ai1->end[0].xpos, ai1->end[0].ypos, ai1->end[1].xpos, ai1->end[1].ypos,
144 ai2->end[0].xpos, ai2->end[0].ypos, ai2->end[1].xpos, ai2->end[1].ypos, &x, &y))
145 {
146 /* they intersect */
147 fromnodeinst = us_getnodeonarcinst(&fromgeom, &frompp, togeom, topp, x, y, 0);
148 if (fromnodeinst == NONODEINST)
149 {
150 us_abortcommand(_("Cannot create splitting pin"));
151 return(NOARCINST);
152 }
153 if (us_breakarcinsertnode(ai2, fromnodeinst, alt1, alt2)) return(NOARCINST);
154 ai = *alt2;
155 *alt2 = NOARCINST;
156 *con1 = fromnodeinst;
157 return(ai);
158 }
159 }
160
161 /* get actual nodes for each end of connection */
162 if (fromnodeinst == NONODEINST)
163 {
164 fromnodeinst = us_getnodeonarcinst(&fromgeom, &frompp, togeom, topp, prefx, prefy, 0);
165 if (fromnodeinst == NONODEINST)
166 {
167 us_abortcommand(_("Cannot create splitting pin"));
168 return(NOARCINST);
169 }
170
171 tonodeinst = us_getnodeonarcinst(&togeom, &topp, fromgeom, frompp, prefx, prefy, 0);
172 if (tonodeinst == NONODEINST)
173 {
174 us_abortcommand(_("Cannot create splitting pin"));
175 return(NOARCINST);
176 }
177 }
178
179 /* default to single port on one-port nodeinsts */
180 if (frompp == NOPORTPROTO)
181 if ((fpt = fromnodeinst->proto->firstportproto) != NOPORTPROTO)
182 if (fpt->nextportproto == NOPORTPROTO) frompp = fpt;
183 if (topp == NOPORTPROTO)
184 if ((tpt = tonodeinst->proto->firstportproto) != NOPORTPROTO)
185 if (tpt->nextportproto == NOPORTPROTO) topp = tpt;
186
187 /* sillyness check */
188 if (fromnodeinst == tonodeinst && frompp == topp)
189 {
190 infstr = initinfstr();
191 formatinfstr(infstr, _("Are you sure you want to run an arc from node %s to itself?"),
192 describenodeinst(fromnodeinst));
193 ans = ttygetparam(returninfstr(infstr), &us_noyesp, 1, result);
194 if (ans <= 0 || (result[0][0] != 'y' && result[0][0] != 'Y')) return(NOARCINST);
195 }
196
197 /* reset all arcproto connection bits */
198 for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
199 for(ap = tech->firstarcproto; ap != NOARCPROTO; ap = ap->nextarcproto)
200 ap->userbits &= ~CANCONNECT;
201
202 /* set bits in arc prototypes that can make the connection */
203 t = gent = 0;
204 for(fpt = fromnodeinst->proto->firstportproto; fpt !=NOPORTPROTO; fpt = fpt->nextportproto)
205 {
206 if (frompp != NOPORTPROTO && frompp != fpt) continue;
207 for (tpt = tonodeinst->proto->firstportproto; tpt != NOPORTPROTO; tpt = tpt->nextportproto)
208 {
209 if (topp != NOPORTPROTO && topp != tpt) continue;
210
211 /* set those bits common to both "fpt" and "tpt" */
212 for(i=0; fpt->connects[i] != NOARCPROTO; i++)
213 {
214 for(j=0; tpt->connects[j] != NOARCPROTO; j++)
215 {
216 if (tpt->connects[j] != fpt->connects[i]) continue;
217 fpt->connects[i]->userbits |= CANCONNECT;
218 if (fpt->connects[i]->tech == gen_tech) gent++; else t++;
219 break;
220 }
221 }
222 }
223 }
224
225 /* if no common ports, look for a contact */
226 con = NONODEPROTO;
227 usecontact = 0;
228 if (t == 0)
229 {
230 tech = fromnodeinst->proto->tech;
231 if (tech != gen_tech)
232 {
233 for(con = tech->firstnodeproto; con != NONODEPROTO; con = con->nextnodeproto)
234 {
235 if (((con->userbits&NFUNCTION) >> NFUNCTIONSH) != NPCONTACT) continue;
236 pp = con->firstportproto;
237 for(fpt = fromnodeinst->proto->firstportproto; fpt != NOPORTPROTO; fpt = fpt->nextportproto)
238 {
239 if (frompp != NOPORTPROTO && frompp != fpt) continue;
240 for(i=0; pp->connects[i] != NOARCPROTO; i++)
241 {
242 if (pp->connects[i]->tech == gen_tech) continue;
243 for(j=0; fpt->connects[j] != NOARCPROTO; j++)
244 if (fpt->connects[j] == pp->connects[i]) break;
245 if (fpt->connects[j] != NOARCPROTO) break;
246 }
247 if (pp->connects[i] != NOARCPROTO) break;
248 }
249 if (fpt == NOPORTPROTO) continue;
250 fap = fpt->connects[j];
251
252 for (tpt = tonodeinst->proto->firstportproto; tpt != NOPORTPROTO; tpt = tpt->nextportproto)
253 {
254 if (topp != NOPORTPROTO && topp != tpt) continue;
255 for(i=0; pp->connects[i] != NOARCPROTO; i++)
256 {
257 if (pp->connects[i]->tech == gen_tech) continue;
258 for(j=0; tpt->connects[j] != NOARCPROTO; j++)
259 if (tpt->connects[j] == pp->connects[i]) break;
260 if (tpt->connects[j] != NOARCPROTO) break;
261 }
262 if (pp->connects[i] != NOARCPROTO) break;
263 }
264 if (tpt == NOPORTPROTO) continue;
265 tap = tpt->connects[j];
266
267 /* this contact connects */
268 t = 1;
269 usecontact = 1;
270 break;
271 }
272 }
273 }
274
275 /* if no common ports, don't run the arcinst */
276 if (t+gent == 0)
277 {
278 us_abortcommand(_("Cannot find arc that connects %s to %s"), geomname(fromgeom),
279 geomname(togeom));
280 return(NOARCINST);
281 }
282
283 /* if a contact must be used, try it */
284 if (usecontact != 0)
285 {
286 newai = us_makeconnection(fromnodeinst, frompp, fap, tonodeinst, topp, tap,
287 con, prefx, prefy, alt1, alt2, con1, con2, ang, nozigzag, 0, report);
288 if (newai != NOARCINST) return(newai);
289 }
290
291 /* see if the default arc prototype can be used */
292 if ((typ->userbits&CANCONNECT) != 0)
293 {
294 newai = us_makeconnection(fromnodeinst, frompp, typ, tonodeinst, topp, typ,
295 getpinproto(typ), prefx, prefy, alt1, alt2, con1, con2, ang, nozigzag, 0, report);
296 if (newai != NOARCINST) return(newai);
297 }
298
299 /* default arc prototype cannot be used: try others in this technology */
300 for(ap = el_curtech->firstarcproto; ap != NOARCPROTO; ap = ap->nextarcproto)
301 {
302 if ((ap->userbits&CANCONNECT) == 0) continue;
303 if (ap == typ) continue;
304 newai = us_makeconnection(fromnodeinst, frompp, ap, tonodeinst, topp, ap,
305 getpinproto(ap), prefx, prefy, alt1, alt2, con1, con2, ang, nozigzag, 0, report);
306 if (newai != NOARCINST) return(newai);
307 }
308
309 /* current technology bad: see if this is a cross-technology connect */
310 tech1 = us_gettech(fromnodeinst, frompp);
311 tech2 = us_gettech(tonodeinst, topp);
312 if (tech1 == tech2)
313 {
314 /* if current technology not that of the two nodes, check it */
315 if (tech1 != el_curtech)
316 for(ap = tech1->firstarcproto; ap != NOARCPROTO; ap = ap->nextarcproto)
317 {
318 if ((ap->userbits&CANCONNECT) == 0) continue;
319 if (ap == typ) continue;
320 newai = us_makeconnection(fromnodeinst, frompp, ap, tonodeinst, topp, ap,
321 getpinproto(ap), prefx, prefy, alt1, alt2, con1, con2, ang, nozigzag, 0, report);
322 if (newai != NOARCINST) return(newai);
323 }
324 } else
325 {
326 /* current technology bad: try other technologies */
327 for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
328 if (tech != el_curtech)
329 for(ap = tech->firstarcproto; ap != NOARCPROTO; ap = ap->nextarcproto)
330 {
331 if ((ap->userbits&CANCONNECT) == 0) continue;
332 newai = us_makeconnection(fromnodeinst, frompp, ap, tonodeinst, topp, ap,
333 getpinproto(ap), prefx, prefy, alt1, alt2, con1, con2, ang, nozigzag, 0, report);
334 if (newai != NOARCINST) return(newai);
335 }
336 }
337 us_abortcommand(_("There is no way to connect these objects"));
338 return(NOARCINST);
339 }
340
341 /*
342 * routine to return the technology associated with node "ni", port "pp".
343 */
us_gettech(NODEINST * ni,PORTPROTO * pp)344 TECHNOLOGY *us_gettech(NODEINST *ni, PORTPROTO *pp)
345 {
346 for(;;)
347 {
348 if (ni->proto->primindex != 0) break;
349 if (pp == NOPORTPROTO) break;
350 ni = pp->subnodeinst;
351 pp = pp->subportproto;
352 }
353 return(ni->proto->tech);
354 }
355
356 /*
357 * routine to try to run an arcinst from nodeinst "fromnodeinst" to nodeinst
358 * "tonodeinst". The default port prototypes to use are in "fromportproto" and
359 * "toportproto". The arcs to use are "fromarcproto" and "toarcproto". The
360 * connecting pin (if necessary) is "con". If it can be done, the arcinst is
361 * created and the routine returns its address, otherwise it returns
362 * NOARCINST. If two or three arcs are created, their addresses are returned
363 * in "alt1" and "alt2". If contacts or pins are created, their addresses are
364 * returned in "con1" and "con2". The preferred arcinst runs closest to (prefx, prefy).
365 * The most direct route will be traveled, but an additional nodeinst or two
366 * may have to be created. The angle increment for the arcs is "ang".
367 * If "nozigzag" is nonzero, disallow 3-arc connections (only 1 or 2). The number of
368 * arcs created is reported if "report" is TRUE.
369 */
us_makeconnection(NODEINST * fromnodeinst,PORTPROTO * fromportproto,ARCPROTO * fromarcproto,NODEINST * tonodeinst,PORTPROTO * toportproto,ARCPROTO * toarcproto,NODEPROTO * con,INTBIG prefx,INTBIG prefy,ARCINST ** alt1,ARCINST ** alt2,NODEINST ** con1,NODEINST ** con2,INTBIG ang,BOOLEAN nozigzag,INTBIG * fakecoords,BOOLEAN report)370 ARCINST *us_makeconnection(NODEINST *fromnodeinst, PORTPROTO *fromportproto, ARCPROTO *fromarcproto,
371 NODEINST *tonodeinst, PORTPROTO *toportproto, ARCPROTO *toarcproto, NODEPROTO *con, INTBIG prefx,
372 INTBIG prefy, ARCINST **alt1, ARCINST **alt2, NODEINST **con1, NODEINST **con2,
373 INTBIG ang, BOOLEAN nozigzag, INTBIG *fakecoords, BOOLEAN report)
374 {
375 REGISTER PORTPROTO *fpt, *tpt;
376 REGISTER INTBIG bestdist, dist, fwid, twid, w;
377 REGISTER INTBIG i;
378 REGISTER ARCINST *newai;
379 INTBIG x, y, hx, hy, lx, ly;
380
381 /* see if the cursor is near a port on a cell */
382 bestdist = MAXINTBIG;
383 if (fromportproto == NOPORTPROTO && fromnodeinst->proto->primindex == 0)
384 for(fpt = fromnodeinst->proto->firstportproto; fpt != NOPORTPROTO; fpt = fpt->nextportproto)
385 {
386 for(i=0; fpt->connects[i] != NOARCPROTO; i++)
387 if (fromarcproto == fpt->connects[i]) break;
388 if (fpt->connects[i] == NOARCPROTO) continue;
389
390 /* portproto may be valid: compute distance to it */
391 us_portposition(fromnodeinst, fpt, &x, &y);
392 dist = abs(x-prefx) + abs(y-prefy);
393 if (dist > bestdist) continue;
394 bestdist = dist; fromportproto = fpt;
395 }
396 if (toportproto == NOPORTPROTO && tonodeinst->proto->primindex == 0)
397 for(tpt = tonodeinst->proto->firstportproto; tpt != NOPORTPROTO; tpt = tpt->nextportproto)
398 {
399 for(i=0; tpt->connects[i] != NOARCPROTO; i++)
400 if (toarcproto == tpt->connects[i]) break;
401 if (tpt->connects[i] == NOARCPROTO) continue;
402
403 /* portproto may be valid: compute distance to it */
404 us_portposition(tonodeinst, tpt, &x, &y);
405 dist = abs(x-prefx) + abs(y-prefy);
406 if (dist > bestdist) continue;
407 bestdist = dist; toportproto = tpt;
408 }
409 if (fromportproto == NOPORTPROTO || toportproto == NOPORTPROTO) return(NOARCINST);
410
411 /* determine the width of this arc from others on the nodes */
412 fwid = defaultarcwidth(fromarcproto);
413 w = us_widestarcinst(fromarcproto, fromnodeinst, fromportproto);
414 if (w > fwid) fwid = w;
415 twid = defaultarcwidth(toarcproto);
416 w = us_widestarcinst(toarcproto, tonodeinst, toportproto);
417 if (w > twid) twid = w;
418
419 /* now first check for direct connection */
420 *con1 = *con2 = NONODEINST;
421 newai = us_directarcinst(fromnodeinst, fromportproto, fromarcproto,
422 tonodeinst, toportproto, toarcproto, con,
423 prefx, prefy, fwid, twid, con1, ang, fakecoords, report);
424 if (newai != NOARCINST)
425 {
426 *alt1 = *alt2 = NOARCINST;
427 return(newai);
428 }
429
430 /* now try a zig-zag if Manhattan or diagonal */
431 if ((ang == 450 || ang == 900) && !nozigzag)
432 {
433 /*
434 * check location of cursor with respect to fromnode and tonode.
435 * There are nine possibilities. Each implies a specific routing
436 * request (cases where cursor is lined up horizontally or vertically
437 * with either fromnode or tonode are not included as they default to
438 * L-connections)
439 * 1 2 3
440 * * [fromnode]
441 * 4 5 6
442 * * [tonode]
443 * 7 8 9
444 */
445 us_portposition(fromnodeinst, fromportproto, &lx, &ly);
446 us_portposition(tonodeinst, toportproto, &hx, &hy);
447 if (hx < lx)
448 {
449 x = lx; lx = hx; hx = x;
450 }
451 if (hy < ly)
452 {
453 y = ly; ly = hy; hy = y;
454 }
455
456 /* if cursor location is in 2, 4, 5, 6, or 8, try two-bend connection */
457 if ((prefx > lx && prefx < hx) || (prefy > ly && prefy < hy))
458 {
459 newai = us_twobend(fromnodeinst, fromportproto, fromarcproto, tonodeinst,
460 toportproto, toarcproto, con, prefx, prefy, fwid, twid,
461 alt1, alt2, con1, con2, fakecoords, report);
462 if (newai != NOARCINST) return(newai);
463 }
464 }
465
466 /* next check for a one-bend connection */
467 newai = us_onebend(fromnodeinst, fromportproto, fromarcproto, tonodeinst, toportproto,
468 toarcproto, con, prefx, prefy, fwid, twid, alt1, con1, fakecoords, report);
469 if (newai != NOARCINST)
470 {
471 *alt2 = NOARCINST;
472 return(newai);
473 }
474
475 /* finally check for any zig-zag connection */
476 newai = us_twobend(fromnodeinst, fromportproto, fromarcproto, tonodeinst, toportproto,
477 toarcproto, con, prefx, prefy, fwid, twid, alt1, alt2, con1, con2, fakecoords, report);
478 if (newai != NOARCINST) return(newai);
479
480 /* give up */
481 return(NOARCINST);
482 }
483
484 /*
485 * routine to check for direct connection from nodeinst "fromnodeinst", port "fromppt",
486 * arc "fromarcproto" to nodeinst "tonodeinst", port "toppt", arc "toarcproto" (with
487 * a possible via of type "con"). and create an arc that is "wid" wide if possible.
488 * It is prefered that the arcinst be close to (prefx, prefy). If "fakecoords" is
489 * zero, create the arc if possible. If "fakecoords" is nonzero, do not create an arc,
490 * but just store the coordinates of the two endpoints in the four integers there (and
491 * return any non-NOARCINST value). If the arcinst is created, the routine
492 * returns its address, otherwise it returns NOARCINST. The angle increment
493 * for the arc is "ang" tenth-degrees (900 for manhattan geometry).
494 */
us_directarcinst(NODEINST * fromnodeinst,PORTPROTO * fromppt,ARCPROTO * fromarcproto,NODEINST * tonodeinst,PORTPROTO * toppt,ARCPROTO * toarcproto,NODEPROTO * con,INTBIG prefx,INTBIG prefy,INTBIG fwid,INTBIG twid,NODEINST ** con1,INTBIG ang,INTBIG * fakecoords,BOOLEAN report)495 ARCINST *us_directarcinst(NODEINST *fromnodeinst, PORTPROTO *fromppt, ARCPROTO *fromarcproto,
496 NODEINST *tonodeinst, PORTPROTO *toppt, ARCPROTO *toarcproto, NODEPROTO *con,
497 INTBIG prefx, INTBIG prefy, INTBIG fwid, INTBIG twid, NODEINST **con1, INTBIG ang,
498 INTBIG *fakecoords, BOOLEAN report)
499 {
500 REGISTER PORTPROTO *fpt, *tpt, *fstart, *tstart;
501 REGISTER ARCINST *ai;
502 REGISTER NODEINST *ni;
503 PORTPROTO *fromportproto, *toportproto;
504 REGISTER INTBIG i, j, trange, frange, bad, gotpath;
505 REGISTER INTBIG edgealignment, alignment;
506 INTBIG bestdist, dist, bestpdist, pdist, bestfx,bestfy, besttx,bestty, otheralign, fpx, fpy,
507 tpx, tpy, flx, fly, fhx, fhy, tlx, tly, thx, thy, wid, frwid, trwid, x, y, bestxi, bestyi,
508 pxs, pys, lx, hx, ly, hy;
509 static POLYGON *fpoly = NOPOLYGON, *tpoly = NOPOLYGON;
510
511 /* get polygons */
512 (void)needstaticpolygon(&fpoly, 4, us_tool->cluster);
513 (void)needstaticpolygon(&tpoly, 4, us_tool->cluster);
514
515 /* determine true width */
516 fwid = us_figurearcwidth(fromarcproto, fwid, fromnodeinst, fromppt, prefx, prefy, tonodeinst, toppt, prefx, prefy);
517 frwid = fwid - arcprotowidthoffset(fromarcproto);
518 twid = us_figurearcwidth(toarcproto, twid, fromnodeinst, fromppt, prefx, prefy, tonodeinst, toppt, prefx, prefy);
519 trwid = twid - arcprotowidthoffset(toarcproto);
520 wid = maxi(fwid, twid);
521
522 /* assume default prefered positions for port locations */
523 tpx = (tonodeinst->highx + tonodeinst->lowx) / 2;
524 tpy = (tonodeinst->highy + tonodeinst->lowy) / 2;
525 fpx = (fromnodeinst->highx + fromnodeinst->lowx) / 2;
526 fpy = (fromnodeinst->highy + fromnodeinst->lowy) / 2;
527
528 /* precompute better positions if ports were specified */
529 if (toppt != NOPORTPROTO)
530 {
531 us_portposition(tonodeinst, toppt, &tpx, &tpy);
532 tpoly->xv[0] = prefx; tpoly->yv[0] = prefy; tpoly->count = 1;
533 shapeportpoly(tonodeinst, toppt, tpoly, TRUE);
534 reduceportpoly(tpoly, tonodeinst, toppt, trwid, -1);
535 if ((toppt->userbits&PORTISOLATED) != 0)
536 {
537 /* use prefered location on isolated ports */
538 tpx = prefx; tpy = prefy;
539 closestpoint(tpoly, &tpx, &tpy);
540 }
541 tstart = toppt;
542 } else tstart = tonodeinst->proto->firstportproto;
543 if (fromppt != NOPORTPROTO)
544 {
545 us_portposition(fromnodeinst, fromppt, &fpx, &fpy);
546 fpoly->xv[0] = prefx; fpoly->yv[0] = prefy; fpoly->count = 1;
547 shapeportpoly(fromnodeinst, fromppt, fpoly, TRUE);
548 reduceportpoly(fpoly, fromnodeinst, fromppt, frwid, -1);
549 if ((fromppt->userbits&PORTISOLATED) != 0)
550 {
551 /* use prefered location on isolated ports */
552 fpx = prefx; fpy = prefy;
553 closestpoint(fpoly, &fpx, &fpy);
554 }
555 fstart = fromppt;
556 } else fstart = fromnodeinst->proto->firstportproto;
557
558 /* check again to make sure the ports align */
559 if (toppt != NOPORTPROTO)
560 {
561 tpx = fpx; tpy = fpy;
562 closestpoint(tpoly, &tpx, &tpy);
563 }
564 if (fromppt != NOPORTPROTO)
565 {
566 fpx = tpx; fpy = tpy;
567 closestpoint(fpoly, &fpx, &fpy);
568 }
569 if (toppt != NOPORTPROTO)
570 {
571 tpx = fpx; tpy = fpy;
572 closestpoint(tpoly, &tpx, &tpy);
573 }
574
575 /* search every possible port on the "from" node */
576 bestdist = bestpdist = MAXINTBIG;
577 for(fpt = fstart; fpt != NOPORTPROTO; fpt = fpt->nextportproto)
578 {
579 if (fromppt != NOPORTPROTO && fromppt != fpt) break;
580
581 /* see if the port has an opening for this type of arcinst */
582 for(i=0; fpt->connects[i] != NOARCPROTO; i++)
583 if (fromarcproto == fpt->connects[i]) break;
584 if (fpt->connects[i] == NOARCPROTO) continue;
585
586 /* potential port: get information about its position if not already known */
587 if (fromppt == NOPORTPROTO)
588 {
589 fpoly->xv[0] = prefx; fpoly->yv[0] = prefy; fpoly->count = 1;
590 shapeportpoly(fromnodeinst, fpt, fpoly, TRUE);
591
592 /* handle arc width offset from node edge */
593 reduceportpoly(fpoly, fromnodeinst, fpt, frwid, -1);
594
595 /* find single closest point */
596 fpx = tpx; fpy = tpy;
597 closestpoint(fpoly, &fpx, &fpy);
598 }
599
600 /* correlate with every possible port on the "to" node */
601 for(tpt = tstart; tpt != NOPORTPROTO; tpt = tpt->nextportproto)
602 {
603 if (toppt != NOPORTPROTO && toppt != tpt) break;
604
605 /* silly to run from port to itself when ports are unspecified */
606 if (fromnodeinst == tonodeinst && fpt == tpt &&
607 (fromppt == NOPORTPROTO || toppt == NOPORTPROTO)) continue;
608
609 /* see if the port has an opening for this type of arcinst */
610 for(i=0; tpt->connects[i] != NOARCPROTO; i++)
611 if (toarcproto == tpt->connects[i]) break;
612 if (tpt->connects[i] == NOARCPROTO) continue;
613
614 /* get the shape of the "to" port if not already done */
615 if (toppt == NOPORTPROTO)
616 {
617 tpoly->xv[0] = prefx; tpoly->yv[0] = prefy; tpoly->count = 1;
618 shapeportpoly(tonodeinst, tpt, tpoly, TRUE);
619
620 /* handle arc width offset from node edge */
621 reduceportpoly(tpoly, tonodeinst, tpt, trwid, -1);
622
623 /* find single closest point */
624 tpx = fpx; tpy = fpy;
625 closestpoint(tpoly, &tpx, &tpy);
626 }
627
628 /* check directionality of ports */
629 trange = ((tpt->userbits&PORTARANGE) >> PORTARANGESH) * 10;
630 frange = ((fpt->userbits&PORTARANGE) >> PORTARANGESH) * 10;
631
632 /* ignore range calculations for serpentine transistors */
633 if ((tonodeinst->proto->userbits&HOLDSTRACE) != 0 &&
634 gettrace(tonodeinst) != NOVARIABLE)
635 trange = 1800;
636 if ((fromnodeinst->proto->userbits&HOLDSTRACE) != 0 &&
637 gettrace(fromnodeinst) != NOVARIABLE)
638 frange = 1800;
639
640 /* make sure ranges are acceptable */
641 if (trange != 1800 || frange != 1800)
642 {
643 /* determine angle between port centers */
644 bad = 0;
645 if (fpx != tpx || fpy != tpy)
646 {
647 /* see if the angle is permitted */
648 i = figureangle(fpx, fpy, tpx, tpy);
649 if (us_fitportangle(fromnodeinst, fpt, i, frange)) bad = 1; else
650 if (us_fitportangle(tonodeinst, tpt, i+1800, trange))
651 bad = 1;
652 }
653
654 /* special case for ports that overlap */
655 if (bad != 0)
656 {
657 flx = (fromnodeinst->lowx+fromnodeinst->highx)/2;
658 fly = (fromnodeinst->lowy+fromnodeinst->highy)/2;
659 fhx = (tonodeinst->lowx+tonodeinst->highx)/2;
660 fhy = (tonodeinst->lowy+tonodeinst->highy)/2;
661 if (flx != fhx || fly != fhy)
662 {
663 j = figureangle(flx, fly, fhx, fhy);
664 if ((j+1800)%3600 == i &&
665 !us_fitportangle(fromnodeinst, fpt, j, frange) &&
666 !us_fitportangle(tonodeinst, tpt, j+1800, trange))
667 bad = 0;
668 }
669 }
670 if (bad != 0) continue;
671 }
672
673 /* see if an arc can connect at this angle */
674 if (ang == 0)
675 {
676 /* no angle restrictions: simply use the chosen locations */
677 gotpath = 1;
678 } else
679 {
680 /* if manhattan is possible, test it first */
681 gotpath = 0;
682 if (900 / ang * ang == 900)
683 {
684 /* manhattan angle restriction: check directly */
685 if (tpx == fpx || tpy == fpy) gotpath = 1;
686 }
687 if (gotpath == 0 && ang != 900)
688 {
689 flx = fhx = fpx; fly = fhy = fpy;
690 tlx = thx = tpx; tly = thy = tpy;
691
692 /* arbitrary angle restrictions: try all angle possibilities */
693 for(i=0; i<3600; i += ang)
694 if (arcconnects(i, flx,fhx,fly,fhy, tlx,thx,tly,thy, &fpx,&fpy, &tpx,&tpy))
695 {
696 gotpath = 1;
697 break;
698 }
699 }
700 }
701 if (gotpath == 0) continue;
702
703 /* for manhattan arcs, adjust if edge alignment requested */
704 if (fpx == tpx)
705 {
706 /* arcinst runs vertically */
707 getbbox(fpoly, &flx, &fhx, &fly, &fhy);
708 getbbox(tpoly, &tlx, &thx, &tly, &thy);
709 if (us_edgealignment_ratio != 0 && flx != fhx && tlx != thx)
710 {
711 /* make the arc edge align */
712 edgealignment = muldiv(us_edgealignment_ratio, WHOLE, el_curlib->lambda[el_curtech->techindex]);
713 x = us_alignvalue(fpx - frwid/2, edgealignment, &otheralign) + frwid/2;
714 otheralign += frwid/2;
715 if (x <= mini(fhx,thx) && x >= maxi(flx,tlx)) tpx = fpx = x; else
716 if (otheralign <= mini(fhx,thx) && otheralign >= maxi(flx,tlx))
717 fpx = tpx = otheralign;
718
719 /* try to align the ends */
720 y = us_alignvalue(tpy+trwid/2, edgealignment, &otheralign) - trwid/2;
721 otheralign -= trwid/2;
722 if (isinside(tpx, y, tpoly)) tpy = y; else
723 if (isinside(tpx, otheralign, tpoly)) tpy = otheralign;
724 y = us_alignvalue(fpy+frwid/2, edgealignment, &otheralign) - frwid/2;
725 otheralign -= frwid/2;
726 if (isinside(fpx, y, fpoly)) fpy = y; else
727 if (isinside(fpx, otheralign, fpoly)) fpy = otheralign;
728 }
729 } else if (fpy == tpy)
730 {
731 /* arcinst runs horizontally */
732 getbbox(fpoly, &flx, &fhx, &fly, &fhy);
733 getbbox(tpoly, &tlx, &thx, &tly, &thy);
734 if (us_edgealignment_ratio != 0 && fly != fhy && tly != thy)
735 {
736 /* make the arc edge align */
737 edgealignment = muldiv(us_edgealignment_ratio, WHOLE, el_curlib->lambda[el_curtech->techindex]);
738 y = us_alignvalue(tpy - trwid/2, edgealignment, &otheralign) + trwid/2;
739 otheralign += trwid/2;
740 if (y <= mini(fhy,thy) && y >= maxi(fly,tly)) tpy = fpy = y; else
741 if (otheralign <= mini(fhy,thy) && otheralign >= maxi(fly,tly))
742 tpy = fpy = otheralign;
743
744 /* try to align the ends */
745 x = us_alignvalue(tpx+trwid/2, edgealignment, &otheralign) - trwid/2;
746 otheralign -= trwid/2;
747 if (isinside(x, tpy, tpoly)) tpx = x; else
748 if (isinside(otheralign, tpy, tpoly)) tpx = otheralign;
749 x = us_alignvalue(fpx+frwid/2, edgealignment, &otheralign) - frwid/2;
750 otheralign -= frwid/2;
751 if (isinside(fpx, x, fpoly)) fpx = x; else
752 if (isinside(otheralign, fpy, fpoly)) fpx = otheralign;
753 }
754 }
755
756 /* if this path is longer than another, forget it */
757 dist = abs(fpx-tpx) + abs(fpy-tpy);
758 if (dist > bestdist) continue;
759
760 /* if this path is same as another check prefered position */
761 pdist = (fpx==tpx) ? abs(prefx-fpx) : abs(prefy-fpy);
762 if (dist == bestdist && pdist > bestpdist) continue;
763
764 /* this is best: remember it */
765 bestdist = dist; bestpdist = pdist;
766 fromportproto = fpt; toportproto = tpt;
767 bestfx = fpx; bestfy = fpy;
768 besttx = tpx; bestty = tpy;
769 }
770 }
771
772 if (bestdist < MAXINTBIG)
773 {
774 if (fakecoords == 0)
775 {
776 if (fromarcproto != toarcproto)
777 {
778 /* create the intermediate nodeinst */
779 alignment = muldiv(us_alignment_ratio, el_curlib->lambda[el_curtech->techindex], WHOLE);
780 if (bestfx == besttx)
781 {
782 bestxi = bestfx;
783 bestyi = us_alignvalue(prefy, alignment, &otheralign);
784 } else if (bestfy == bestty)
785 {
786 bestxi = us_alignvalue(prefx, alignment, &otheralign);
787 bestyi = bestfy;
788 } else
789 {
790 bestxi = prefx;
791 bestyi = prefy;
792 }
793 us_getproperconsize(con, fromarcproto, fwid, toarcproto, twid, &pxs, &pys);
794 lx = bestxi - pxs/2; hx = lx + pxs;
795 ly = bestyi - pys/2; hy = ly + pys;
796 *con1 = ni = newnodeinst(con, lx, hx, ly, hy, 0, 0, fromnodeinst->parent);
797 if (ni == NONODEINST)
798 {
799 us_abortcommand(_("Cannot create connecting contact"));
800 return(NOARCINST);
801 }
802 endobjectchange((INTBIG)ni, VNODEINST);
803
804 ai = us_runarcinst(fromnodeinst, fromportproto, bestfx, bestfy,
805 ni, con->firstportproto, bestxi, bestyi, fromarcproto, fwid);
806 if (ai == NOARCINST) return(NOARCINST);
807
808 ai = us_runarcinst(ni, con->firstportproto, bestxi, bestyi,
809 tonodeinst, toportproto, besttx, bestty, toarcproto, twid);
810 if (ai == NOARCINST) return(NOARCINST);
811 if (report)
812 us_reportarcscreated(0, 1, fromarcproto->protoname, 1, toarcproto->protoname);
813 } else
814 {
815 ai = us_runarcinst(fromnodeinst, fromportproto, bestfx, bestfy,
816 tonodeinst, toportproto, besttx, bestty, fromarcproto, wid);
817 if (ai == NOARCINST) return(NOARCINST);
818 if (report)
819 us_reportarcscreated(0, 1, fromarcproto->protoname, 0, x_(""));
820 }
821 return(ai);
822 } else
823 {
824 fakecoords[0] = bestfx;
825 fakecoords[1] = bestfy;
826 fakecoords[2] = besttx;
827 fakecoords[3] = bestty;
828 return((ARCINST *)1);
829 }
830 }
831
832 /* give up */
833 return(NOARCINST);
834 }
835
836 /*
837 * Routine to report that activity "who" created "numarcs1" arcs of type "type1" and "numarcs2"
838 * arcs of type "type2". If "who" is zero, ignore that part of the message. If "numarcs2" is zero,
839 * ignore that part of the message. If "type1" is zero, ignore that part of the message.
840 * Also plays a sound appropriate to the number of arcs created.
841 */
us_reportarcscreated(CHAR * who,INTBIG numarcs1,CHAR * type1,INTBIG numarcs2,CHAR * type2)842 void us_reportarcscreated(CHAR *who, INTBIG numarcs1, CHAR *type1, INTBIG numarcs2, CHAR *type2)
843 {
844 REGISTER void *infstr;
845 REGISTER INTBIG totalcreated;
846
847 infstr = initinfstr();
848 if (who != 0) formatinfstr(infstr, x_("%s: "), who);
849 if (numarcs2 > 0)
850 {
851 formatinfstr(infstr, _("Created %ld %s and %ld %s %s"), numarcs1, type1, numarcs2, type2,
852 makeplural(x_("arc"), numarcs2));
853 } else
854 {
855 if (type1 == 0)
856 {
857 formatinfstr(infstr, _("Created %ld %s"), numarcs1, makeplural(x_("arc"), numarcs1));
858 } else
859 {
860 formatinfstr(infstr, _("Created %ld %s %s"), numarcs1, type1, makeplural(x_("arc"), numarcs1));
861 }
862 }
863 ttyputmsg(x_("%s"), returninfstr(infstr));
864 totalcreated = numarcs1 + numarcs2;
865 if (totalcreated == 1)
866 {
867 ttybeep(SOUNDCLICK, TRUE);
868 } else if (totalcreated == 2)
869 {
870 ttybeep(SOUNDCLICK, TRUE);
871 ttybeep(SOUNDCLICK, TRUE);
872 } else if (totalcreated >= 3)
873 {
874 ttybeep(SOUNDCLICK, TRUE);
875 ttybeep(SOUNDCLICK, TRUE);
876 ttybeep(SOUNDCLICK, TRUE);
877 }
878 }
879
880 /*
881 * routine to check for a one-bend connection running from nodeinst
882 * "fromnodeinst" to nodeinst "tonodeinst" on arcproto "typ" and create two
883 * arcs that are "wid" wide if possible. It is prefered that the bend pass
884 * close to (prefx, prefy) and is on portproto "fromppt" (if "fromppt" is not
885 * NOPORTPROTO) on the FROM nodeinst and on portproto "toppt" (if "toppt" is
886 * not NOPORTPROTO) on the TO nodeinst. If "fakecoords" is zero, create the arc
887 * if possible. If "fakecoords" is nonzero, do not create an arc, but just store
888 * the coordinates of the two endpoints in the four integers there (and return any
889 * non-NOARCINST value). If the arcinst is created, the
890 * routine returns its address and the second arc's address is returned in
891 * "alt". If no arc can be created, the routine returns NOARCINST.
892 */
us_onebend(NODEINST * fromnodeinst,PORTPROTO * fromppt,ARCPROTO * fromarcproto,NODEINST * tonodeinst,PORTPROTO * toppt,ARCPROTO * toarcproto,NODEPROTO * con,INTBIG prefx,INTBIG prefy,INTBIG fwid,INTBIG twid,ARCINST ** alt,NODEINST ** con1,INTBIG * fakecoords,BOOLEAN report)893 ARCINST *us_onebend(NODEINST *fromnodeinst, PORTPROTO *fromppt, ARCPROTO *fromarcproto,
894 NODEINST *tonodeinst, PORTPROTO *toppt, ARCPROTO *toarcproto, NODEPROTO *con,
895 INTBIG prefx, INTBIG prefy, INTBIG fwid, INTBIG twid, ARCINST **alt, NODEINST **con1,
896 INTBIG *fakecoords, BOOLEAN report)
897 {
898 REGISTER NODEINST *ni;
899 REGISTER PORTPROTO *fpt, *tpt;
900 PORTPROTO *fromportproto, *toportproto;
901 REGISTER INTBIG i, bad, frange, trange;
902 REGISTER INTBIG lx, hx, ly, hy, frwid, trwid, edgealignment;
903 REGISTER ARCINST *ai1, *ai2;
904 static POLYGON *poly = NOPOLYGON;
905 INTBIG fx, fy, tx, ty, xi, yi, bestxi, bestyi, bestdist, dist,
906 bestfx, bestfy, besttx, bestty, altxi, altyi, otheralign, pxs, pys;
907 BOOLEAN found;
908
909 /* get polygon */
910 (void)needstaticpolygon(&poly, 4, us_tool->cluster);
911
912 /* determine true width */
913 fwid = us_figurearcwidth(fromarcproto, fwid, fromnodeinst, fromppt, prefx, prefy, tonodeinst, toppt, prefx, prefy);
914 frwid = fwid - arcprotowidthoffset(fromarcproto);
915 twid = us_figurearcwidth(toarcproto, twid, fromnodeinst, fromppt, prefx, prefy, tonodeinst, toppt, prefx, prefy);
916 trwid = twid - arcprotowidthoffset(toarcproto);
917
918 found = FALSE; bestdist = MAXINTBIG;
919 for(fpt = fromnodeinst->proto->firstportproto; fpt != NOPORTPROTO; fpt = fpt->nextportproto)
920 {
921 if (fromppt != NOPORTPROTO && fromppt != fpt) continue;
922
923 /* see if the port has an opening for this type of arcinst */
924 for(i=0; fpt->connects[i] != NOARCPROTO; i++)
925 if (fromarcproto == fpt->connects[i]) break;
926 if (fpt->connects[i] == NOARCPROTO) continue;
927
928 /* potential port: get information about its position */
929 if ((fpt->userbits&PORTISOLATED) != 0)
930 {
931 (void)us_portdistance(fromnodeinst, fpt, prefx, prefy, &fx, &fy, frwid, TRUE);
932 } else
933 {
934 us_portposition(fromnodeinst, fpt, &fx, &fy);
935 }
936
937 for(tpt = tonodeinst->proto->firstportproto; tpt != NOPORTPROTO; tpt = tpt->nextportproto)
938 {
939 if (toppt != NOPORTPROTO && toppt != tpt) continue;
940
941 /* should not run arcinst from port to itself */
942 if (fromnodeinst == tonodeinst && fpt == tpt) continue;
943
944 /* see if the port has an opening for this type of arcinst */
945 for(i=0; tpt->connects[i] != NOARCPROTO; i++)
946 if (toarcproto == tpt->connects[i]) break;
947 if (tpt->connects[i] == NOARCPROTO) continue;
948
949 /* potential portinst: get information about its position */
950 if ((tpt->userbits&PORTISOLATED) != 0)
951 {
952 (void)us_portdistance(tonodeinst, tpt, prefx, prefy, &tx, &ty, trwid, TRUE);
953 } else
954 {
955 us_portposition(tonodeinst, tpt, &tx, &ty);
956 }
957
958 if (abs(tx-prefx) + abs(fy-prefy) < abs(fx-prefx) + abs(ty-prefy))
959 {
960 xi = tx; yi = fy; altxi = fx; altyi = ty;
961 } else
962 {
963 xi = fx; yi = ty; altxi = tx; altyi = fy;
964 }
965
966 /* see if port angles are correct */
967 bad = 0;
968 trange = ((tpt->userbits&PORTARANGE) >> PORTARANGESH) * 10;
969 if (trange != 1800)
970 {
971 if (tx == xi && ty == yi) bad++; else
972 {
973 i = figureangle(tx, ty, xi, yi);
974 if (us_fitportangle(tonodeinst, tpt, i, trange)) bad++;
975 }
976 }
977 frange = ((fpt->userbits&PORTARANGE) >> PORTARANGESH) * 10;
978 if (frange != 1800)
979 {
980 if (fx == xi && fy == yi) bad++; else
981 {
982 i = figureangle(fx, fy, xi, yi);
983 if (us_fitportangle(fromnodeinst, fpt, i, frange)) bad++;
984 }
985 }
986
987 /* if port angles are wrong, try the other inflexion point */
988 if (bad != 0)
989 {
990 if (tx == altxi && ty == altyi) continue;
991 i = figureangle(tx, ty, altxi, altyi);
992 if (us_fitportangle(tonodeinst, tpt, i, trange)) continue;
993 if (fx == altxi && fy == altyi) continue;
994 i = figureangle(fx, fy, altxi, altyi);
995 if (us_fitportangle(fromnodeinst, fpt, i, frange)) continue;
996 xi = altxi; yi = altyi;
997 }
998
999 /* see if this path is better than any previous ones */
1000 dist = abs(fx-tx) + abs(fy-ty);
1001 if (dist > bestdist) continue;
1002
1003 /* select this path */
1004 found = TRUE; bestdist = dist;
1005 fromportproto = fpt; toportproto = tpt;
1006 bestxi = xi; bestyi = yi;
1007 bestfx = fx; bestfy = fy;
1008 besttx = tx; bestty = ty;
1009 }
1010 }
1011
1012 /* make one-bend arcinst */
1013 if (found)
1014 {
1015 /* handle edge alignment */
1016 if (us_edgealignment_ratio != 0)
1017 {
1018 edgealignment = muldiv(us_edgealignment_ratio, WHOLE, el_curlib->lambda[el_curtech->techindex]);
1019 if (bestfx == bestxi)
1020 {
1021 /* see if "bestxi" and "bestfx" can be aligned */
1022 i = us_alignvalue(bestfx - frwid/2, edgealignment, &otheralign) + frwid/2;
1023 otheralign += frwid/2;
1024 shapeportpoly(fromnodeinst, fromportproto, poly, FALSE);
1025 if (isinside(i, bestfy, poly)) bestfx = bestxi = i; else
1026 if (isinside(otheralign, bestfy, poly)) bestfx = bestxi = otheralign;
1027
1028 /* see if "bestyi" and "bestty" can be aligned */
1029 i = us_alignvalue(bestty - trwid/2, edgealignment, &otheralign) + trwid/2;
1030 otheralign += trwid/2;
1031 shapeportpoly(tonodeinst, toportproto, poly, FALSE);
1032 if (isinside(besttx, i, poly)) bestty = bestyi = i; else
1033 if (isinside(besttx, otheralign, poly)) bestty = bestyi = otheralign;
1034 } else if (bestfy == bestyi)
1035 {
1036 /* see if "bestyi" and "bestfy" can be aligned */
1037 i = us_alignvalue(bestfy - frwid/2, edgealignment, &otheralign) + frwid/2;
1038 otheralign += frwid/2;
1039 shapeportpoly(fromnodeinst, fromportproto, poly, FALSE);
1040 if (isinside(bestfx, i, poly)) bestfy = bestyi = i; else
1041 if (isinside(bestfx, otheralign, poly)) bestfy = bestyi = otheralign;
1042
1043 /* see if "bestxi" and "besttx" can be aligned */
1044 i = us_alignvalue(besttx - trwid/2, edgealignment, &otheralign) + trwid/2;
1045 otheralign += trwid/2;
1046 shapeportpoly(tonodeinst, toportproto, poly, FALSE);
1047 if (isinside(i, bestty, poly)) besttx = bestxi = i; else
1048 if (isinside(otheralign, bestty, poly)) besttx = bestxi = otheralign;
1049 }
1050 }
1051
1052 /* run the connecting arcs */
1053 if (fakecoords == 0)
1054 {
1055 /* create the intermediate nodeinst */
1056 us_getproperconsize(con, fromarcproto, fwid, toarcproto, twid, &pxs, &pys);
1057 lx = bestxi - pxs/2; hx = lx + pxs;
1058 ly = bestyi - pys/2; hy = ly + pys;
1059 *con1 = ni = newnodeinst(con, lx, hx, ly, hy, 0, 0, fromnodeinst->parent);
1060 if (ni == NONODEINST)
1061 {
1062 us_abortcommand(_("Cannot create connecting pin"));
1063 return(NOARCINST);
1064 }
1065 endobjectchange((INTBIG)ni, VNODEINST);
1066
1067 fpt = con->firstportproto;
1068 ai1 = us_runarcinst(fromnodeinst, fromportproto, bestfx, bestfy, ni, fpt,
1069 bestxi, bestyi, fromarcproto, fwid);
1070 if (ai1 == NOARCINST) return(NOARCINST);
1071 ai2 = us_runarcinst(ni, fpt, bestxi, bestyi, tonodeinst, toportproto,
1072 besttx, bestty, toarcproto, twid);
1073 if (ai2 == NOARCINST) return(NOARCINST);
1074 if (report)
1075 {
1076 if (fromarcproto == toarcproto)
1077 {
1078 us_reportarcscreated(0, 2, fromarcproto->protoname, 0, x_(""));
1079 } else
1080 {
1081 us_reportarcscreated(0, 1, fromarcproto->protoname, 1, toarcproto->protoname);
1082 }
1083 }
1084 if (abs(bestfx-bestxi) + abs(bestfy-bestyi) >
1085 abs(bestxi-besttx) + abs(bestyi-bestty))
1086 {
1087 *alt = ai2;
1088 return(ai1);
1089 } else
1090 {
1091 *alt = ai1;
1092 return(ai2);
1093 }
1094 } else
1095 {
1096 fakecoords[0] = bestfx;
1097 fakecoords[1] = bestfy;
1098 fakecoords[2] = bestxi;
1099 fakecoords[3] = bestyi;
1100 fakecoords[4] = besttx;
1101 fakecoords[5] = bestty;
1102 *alt = (ARCINST *)1;
1103 return((ARCINST *)1);
1104 }
1105 }
1106
1107 /* give up */
1108 return(NOARCINST);
1109 }
1110
1111 /*
1112 * routine to check for a two-bend connection running from nodeinst
1113 * "fromnodeinst" to nodeinst "tonodeinst" on arcproto "typ" and create two
1114 * arcs that are "wid" wide if possible. It is prefered that the jog pass
1115 * close to (prefx, prefy) and is on portproto "fromppt" (if "fromppt" is not
1116 * NOPORTPROTO) on the FROM nodeinst and on portproto "toppt" (if "toppt" is
1117 * not NOPORTPROTO) on the TO nodeinst. If "fakecoords" is zero, create the arc
1118 * if possible. If "fakecoords" is nonzero, do not create an arc, but just store
1119 * the coordinates of the two endpoints in the four integers there (and return any
1120 * non-NOARCINST value). If the arcinst is created, the
1121 * routine returns its address and the other two arcs are returned in "alt1"
1122 * and "alt2" and contacts or pins are returned in "con1" and "con2". If no arc
1123 * can be created, the routine returns NOARCINST.
1124 */
us_twobend(NODEINST * fromnodeinst,PORTPROTO * fromppt,ARCPROTO * fromarcproto,NODEINST * tonodeinst,PORTPROTO * toppt,ARCPROTO * toarcproto,NODEPROTO * con,INTBIG prefx,INTBIG prefy,INTBIG fwid,INTBIG twid,ARCINST ** alt1,ARCINST ** alt2,NODEINST ** con1,NODEINST ** con2,INTBIG * fakecoords,BOOLEAN report)1125 ARCINST *us_twobend(NODEINST *fromnodeinst, PORTPROTO *fromppt, ARCPROTO *fromarcproto,
1126 NODEINST *tonodeinst, PORTPROTO *toppt, ARCPROTO *toarcproto, NODEPROTO *con,
1127 INTBIG prefx, INTBIG prefy, INTBIG fwid, INTBIG twid,
1128 ARCINST **alt1, ARCINST **alt2, NODEINST **con1, NODEINST **con2,
1129 INTBIG *fakecoords, BOOLEAN report)
1130 {
1131 REGISTER NODEINST *ni1, *ni2;
1132 REGISTER NODEPROTO *fpin;
1133 REGISTER PORTPROTO *fpt, *tpt;
1134 PORTPROTO *fromportproto, *toportproto;
1135 REGISTER INTBIG i, lx, hx, ly, hy, frwid, trwid;
1136 REGISTER ARCINST *ai;
1137 REGISTER BOOLEAN horizontaljog;
1138 INTBIG fx, fy, tx, ty, xi1, yi1, xi2, yi2, bestdist, dist,
1139 bestfx, bestfy, besttx, bestty, fpxs, fpys, tpxs, tpys;
1140
1141 /* determine true width */
1142 fwid = us_figurearcwidth(fromarcproto, fwid, fromnodeinst, fromppt, prefx, prefy, tonodeinst, toppt, prefx, prefy);
1143 frwid = fwid - arcprotowidthoffset(fromarcproto);
1144 twid = us_figurearcwidth(toarcproto, twid, fromnodeinst, fromppt, prefx, prefy, tonodeinst, toppt, prefx, prefy);
1145 trwid = twid - arcprotowidthoffset(toarcproto);
1146
1147 /* find the "from" port */
1148 bestdist = MAXINTBIG; fromportproto = NOPORTPROTO;
1149 for(fpt = fromnodeinst->proto->firstportproto; fpt != NOPORTPROTO; fpt = fpt->nextportproto)
1150 {
1151 if (fromppt != NOPORTPROTO && fromppt != fpt) continue;
1152
1153 /* see if the port has an opening for this type of arcinst */
1154 for(i=0; fpt->connects[i] != NOARCPROTO; i++)
1155 if (fromarcproto == fpt->connects[i]) break;
1156 if (fpt->connects[i] == NOARCPROTO) continue;
1157
1158 /* potential portinst: get information about its position */
1159 (void)us_portdistance(fromnodeinst, fpt, prefx, prefy, &fx, &fy, frwid, TRUE);
1160
1161 dist = abs(fx-prefx) + abs(fy-prefy);
1162 if (dist > bestdist) continue;
1163 fromportproto = fpt; bestdist = dist;
1164 bestfx = fx; bestfy = fy;
1165 }
1166 if (fromportproto == NOPORTPROTO) return(NOARCINST);
1167
1168 /* find the "to" port */
1169 bestdist = MAXINTBIG; toportproto = NOPORTPROTO;
1170 for(tpt = tonodeinst->proto->firstportproto; tpt != NOPORTPROTO; tpt = tpt->nextportproto)
1171 {
1172 if (toppt != NOPORTPROTO && toppt != tpt) continue;
1173
1174 /* see if the port has an opening for this type of arcinst */
1175 for(i=0; tpt->connects[i] != NOARCPROTO; i++)
1176 if (toarcproto == tpt->connects[i]) break;
1177 if (tpt->connects[i] == NOARCPROTO) continue;
1178
1179 /* potential portinst: get information about its position */
1180 (void)us_portdistance(tonodeinst, tpt, prefx, prefy, &tx, &ty, trwid, TRUE);
1181
1182 dist = abs(tx-prefx) + abs(ty-prefy);
1183 if (dist > bestdist) continue;
1184 toportproto = tpt; bestdist = dist;
1185 besttx = tx; bestty = ty;
1186 }
1187 if (toportproto == NOPORTPROTO) return(NOARCINST);
1188
1189 /*
1190 * figure out whether the jog will run horizontally or vertically.
1191 * Use directionality constraints if they exist
1192 */
1193 if (((fromportproto->userbits&PORTARANGE) >> PORTARANGESH) != 180)
1194 {
1195 i = us_bottomrecurse(fromnodeinst, fromportproto);
1196 } else if (((toportproto->userbits&PORTARANGE) >> PORTARANGESH) != 180)
1197 {
1198 i = us_bottomrecurse(tonodeinst, toportproto);
1199 } else if ((prefy > bestfy && prefy > bestty) || (prefy < bestfy && prefy < bestty))
1200 {
1201 /* jog is horizontal if prefy is above or below both ports */
1202 i = 900;
1203 } else if ((prefx > bestfx && prefx > besttx) || (prefx < bestfx && prefx < besttx))
1204 {
1205 /* jog is vertical if prefx is to right or left of both ports */
1206 i = 0;
1207 } else
1208 {
1209 /* if area between nodes is wider than tall, jog is vertical */
1210 if (abs(bestfx-besttx) > abs(bestfy-bestty)) i = 0; else i = 900;
1211 }
1212 i = (i+450) % 1800; if (i < 0) i += 1800;
1213 gridalign(&prefx, &prefx, 1, fromnodeinst->parent);
1214 if (i < 900)
1215 {
1216 if (bestty == bestfy) horizontaljog = TRUE; else
1217 horizontaljog = FALSE;
1218 } else
1219 {
1220 if (besttx == bestfx) horizontaljog = FALSE; else
1221 horizontaljog = TRUE;
1222 }
1223 if (horizontaljog)
1224 {
1225 xi1 = bestfx; xi2 = besttx;
1226 yi1 = yi2 = prefy;
1227 } else
1228 {
1229 xi1 = xi2 = prefx;
1230 yi1 = bestfy; yi2 = bestty;
1231 }
1232
1233 /* figure out what primitive nodeproto connects these arcs */
1234 fpin = getpinproto(fromarcproto);
1235 if (fpin == NONODEPROTO)
1236 {
1237 us_abortcommand(_("No pin for %s arcs"), describearcproto(fromarcproto));
1238 return(NOARCINST);
1239 }
1240 defaultnodesize(fpin, &fpxs, &fpys);
1241 us_getproperconsize(con, fromarcproto, fwid, toarcproto, twid, &tpxs, &tpys);
1242
1243 /* run the arcs */
1244 if (fakecoords == 0)
1245 {
1246 /* create the intermediate nodeinsts */
1247 lx = xi1 - fpxs/2; hx = lx + fpxs;
1248 ly = yi1 - fpys/2; hy = ly + fpys;
1249 *con1 = ni1 = newnodeinst(fpin, lx,hx, ly,hy, 0, 0, fromnodeinst->parent);
1250 if (ni1 == NONODEINST)
1251 {
1252 us_abortcommand(_("Cannot create first connecting pin"));
1253 return(NOARCINST);
1254 }
1255 endobjectchange((INTBIG)ni1, VNODEINST);
1256 lx = xi2 - tpxs/2; hx = lx + tpxs;
1257 ly = yi2 - tpys/2; hy = ly + tpys;
1258 *con2 = ni2 = newnodeinst(con, lx,hx, ly,hy, 0, 0, fromnodeinst->parent);
1259 if (ni2 == NONODEINST)
1260 {
1261 us_abortcommand(_("Cannot create second connecting pin"));
1262 return(NOARCINST);
1263 }
1264 endobjectchange((INTBIG)ni2, VNODEINST);
1265
1266 *alt1 = us_runarcinst(fromnodeinst, fromportproto, bestfx, bestfy, ni1,
1267 fpin->firstportproto, xi1,yi1, fromarcproto, fwid);
1268 if (*alt1 == NOARCINST) return(NOARCINST);
1269 ai = us_runarcinst(ni1,fpin->firstportproto, xi1,yi1, ni2,con->firstportproto,
1270 xi2,yi2, fromarcproto, fwid);
1271 if (ai == NOARCINST) return(NOARCINST);
1272 *alt2 = us_runarcinst(ni2,con->firstportproto, xi2,yi2, tonodeinst,
1273 toportproto, besttx, bestty, toarcproto, twid);
1274 if (*alt2 == NOARCINST) return(NOARCINST);
1275 if (report)
1276 {
1277 if (fromarcproto == toarcproto)
1278 {
1279 us_reportarcscreated(0, 3, fromarcproto->protoname, 0, x_(""));
1280 } else
1281 {
1282 us_reportarcscreated(0, 2, fromarcproto->protoname, 1, toarcproto->protoname);
1283 }
1284 }
1285 return(ai);
1286 }
1287
1288 /* record the fake */
1289 fakecoords[0] = bestfx;
1290 fakecoords[1] = bestfy;
1291 fakecoords[2] = xi1;
1292 fakecoords[3] = yi1;
1293 fakecoords[4] = xi2;
1294 fakecoords[5] = yi2;
1295 fakecoords[6] = besttx;
1296 fakecoords[7] = bestty;
1297 *alt1 = (ARCINST *)1;
1298 *alt2 = (ARCINST *)1;
1299 return((ARCINST *)1);
1300 }
1301
1302 /*
1303 * run an arcinst from portproto "fromportproto" of nodeinst "fromnodeinst" at
1304 * (fromx,fromy) to portproto "toportproto" of nodeinst "tonodeinst" at
1305 * (tox,toy). The type of the arcinst is "ap" and the width is "wid". The
1306 * routine returns the address of the newly created arcinst (NOARCINST on
1307 * error).
1308 */
us_runarcinst(NODEINST * fromnodeinst,PORTPROTO * fromportproto,INTBIG fromx,INTBIG fromy,NODEINST * tonodeinst,PORTPROTO * toportproto,INTBIG tox,INTBIG toy,ARCPROTO * ap,INTBIG wid)1309 ARCINST *us_runarcinst(NODEINST *fromnodeinst, PORTPROTO *fromportproto, INTBIG fromx, INTBIG fromy,
1310 NODEINST *tonodeinst, PORTPROTO *toportproto, INTBIG tox, INTBIG toy, ARCPROTO *ap, INTBIG wid)
1311 {
1312 REGISTER ARCINST *ai;
1313 REGISTER INTBIG bits;
1314
1315 /* see if nodes need to be undrawn to account for "Steiner Point" changes */
1316 if ((fromnodeinst->proto->userbits&WIPEON1OR2) != 0)
1317 startobjectchange((INTBIG)fromnodeinst, VNODEINST);
1318 if ((tonodeinst->proto->userbits&WIPEON1OR2) != 0)
1319 startobjectchange((INTBIG)tonodeinst, VNODEINST);
1320
1321 /* create the arcinst */
1322 bits = us_makearcuserbits(ap);
1323 ai = newarcinst(ap, wid, bits, fromnodeinst, fromportproto, fromx,fromy,
1324 tonodeinst, toportproto, tox, toy, fromnodeinst->parent);
1325 if (ai == NOARCINST)
1326 {
1327 us_abortcommand(_("Problem creating the arc"));
1328 return(NOARCINST);
1329 }
1330 ai->changed = 0;
1331 endobjectchange((INTBIG)ai, VARCINST);
1332 us_setarcproto(ap, FALSE);
1333
1334 /* see if nodes need to be redrawn to account for "Steiner Point" changes */
1335 if ((fromnodeinst->proto->userbits&WIPEON1OR2) != 0)
1336 endobjectchange((INTBIG)fromnodeinst, VNODEINST);
1337 if ((tonodeinst->proto->userbits&WIPEON1OR2) != 0)
1338 endobjectchange((INTBIG)tonodeinst, VNODEINST);
1339 return(ai);
1340 }
1341
1342 /*
1343 * routine to find the width of the widest arcinst of type "ap" connected
1344 * to any port of nodeinst "ni" (if "ni" is primitive) or to port "por" of
1345 * nodeinst "ni" (if "ni" is complex).
1346 */
us_widestarcinst(ARCPROTO * ap,NODEINST * ni,PORTPROTO * por)1347 INTBIG us_widestarcinst(ARCPROTO *ap, NODEINST *ni, PORTPROTO *por)
1348 {
1349 REGISTER INTBIG wid, pindex;
1350 REGISTER PORTARCINST *pi;
1351
1352 /* look at all arcs on the nodeinst */
1353 wid = 0;
1354 pindex = ni->proto->primindex;
1355 if (por == NOPORTPROTO) pindex = 1;
1356 for(;;)
1357 {
1358 for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
1359 {
1360 if (pindex == 0 && pi->proto != por) continue;
1361 if (ap != NOARCPROTO && pi->conarcinst->proto != ap) continue;
1362 if (pi->conarcinst->width > wid) wid = pi->conarcinst->width;
1363 }
1364
1365 /* descend to the next level in the hierarchy */
1366 if (pindex != 0) break;
1367 ni = por->subnodeinst;
1368 pindex = ni->proto->primindex;
1369 por = por->subportproto;
1370 }
1371 return(wid);
1372 }
1373
1374 /*
1375 * Routine to determine the proper width of arc of type "typ" with width "wid" and running from node
1376 * "ni1", port "pp1" to node "ni2", port "pp2". Oversize nodes are considered when sizing the arc.
1377 */
us_stretchtonodes(ARCPROTO * typ,INTBIG wid,NODEINST * ni1,PORTPROTO * pp1,INTBIG otherx,INTBIG othery)1378 INTBIG us_stretchtonodes(ARCPROTO *typ, INTBIG wid, NODEINST *ni1, PORTPROTO *pp1, INTBIG otherx, INTBIG othery)
1379 {
1380 #if 0 /* this turns out to be a bad idea. So it is turned off. */
1381 REGISTER INTBIG xstretch, ystretch, i, rot, cx, cy;
1382 INTBIG ni1x, ni1y;
1383
1384 /* see if node 1 is stretched */
1385 xstretch = (ni1->highx - ni1->lowx) - (ni1->proto->highx - ni1->proto->lowx);
1386 ystretch = (ni1->highy - ni1->lowy) - (ni1->proto->highy - ni1->proto->lowy);
1387 if (xstretch > 0 || ystretch > 0)
1388 {
1389 rot = (ni1->rotation + 900 * ni1->transpose) % 3600;
1390 switch (rot)
1391 {
1392 case 0:
1393 case 1800:
1394 break;
1395 case 900:
1396 case 2700:
1397 i = xstretch; xstretch = ystretch; ystretch = i;
1398 break;
1399 default:
1400 return(wid);
1401 }
1402 us_portposition(ni1, pp1, &ni1x, &ni1y);
1403 cx = (ni1->lowx + ni1->highx) / 2;
1404 cy = (ni1->lowy + ni1->highy) / 2;
1405 if (ni1x == cx && ni1y == cy)
1406 {
1407 if (abs(ni1x-otherx) > abs(ni1y-othery))
1408 {
1409 /* horizontal wire: see if Y stretch allows growth */
1410 if (ystretch > wid - typ->nominalwidth) wid = ystretch + typ->nominalwidth;
1411 } else
1412 {
1413 /* vertical wire: see if X stretch allows growth */
1414 if (xstretch > wid - typ->nominalwidth) wid = xstretch + typ->nominalwidth;
1415 }
1416 } else
1417 {
1418 if (abs(ni1x-cx) > abs(ni1y-cy))
1419 {
1420 /* horizontal wire: see if Y stretch allows growth */
1421 if (ystretch > wid - typ->nominalwidth) wid = ystretch + typ->nominalwidth;
1422 } else
1423 {
1424 /* vertical wire: see if X stretch allows growth */
1425 if (xstretch > wid - typ->nominalwidth) wid = xstretch + typ->nominalwidth;
1426 }
1427 }
1428 }
1429 #endif
1430 return(wid);
1431 }
1432
1433 /*
1434 * Routine to determine the width to use for arc "typ", given a default width of "wid", that it
1435 * runs from "ni1/pp1" towards (ox1,oy1) and to "ni2/pp2" towards (ox2,oy2). If either end is
1436 * a pin, the width calculation is not performed for it.
1437 */
us_figurearcwidth(ARCPROTO * typ,INTBIG wid,NODEINST * ni1,PORTPROTO * pp1,INTBIG ox1,INTBIG oy1,NODEINST * ni2,PORTPROTO * pp2,INTBIG ox2,INTBIG oy2)1438 INTBIG us_figurearcwidth(ARCPROTO *typ, INTBIG wid, NODEINST *ni1, PORTPROTO *pp1, INTBIG ox1, INTBIG oy1,
1439 NODEINST *ni2, PORTPROTO *pp2, INTBIG ox2, INTBIG oy2)
1440 {
1441 INTBIG wid1, wid2, stretchwid, stretch1, stretch2, fun;
1442 REGISTER NODEINST *rni;
1443 REGISTER PORTPROTO *rpp;
1444
1445 stretch1 = stretch2 = 1;
1446
1447 /* see if node 1 is a pin */
1448 rni = ni1; rpp = pp1;
1449 while (rni->proto->primindex == 0)
1450 {
1451 rni = rpp->subnodeinst;
1452 rpp = rpp->subportproto;
1453 }
1454 fun = nodefunction(rni);
1455 if (fun == NPPIN) stretch1 = 0;
1456
1457 /* see if node 2 is a pin */
1458 rni = ni2; rpp = pp2;
1459 while (rni->proto->primindex == 0)
1460 {
1461 rni = rpp->subnodeinst;
1462 rpp = rpp->subportproto;
1463 }
1464 fun = nodefunction(rni);
1465 if (fun == NPPIN) stretch2 = 0;
1466
1467 if (stretch1 == 0 && stretch2 == 0) return(wid);
1468
1469 wid1 = us_stretchtonodes(typ, wid, ni1, pp1, ox1, oy1);
1470 wid2 = us_stretchtonodes(typ, wid, ni2, pp2, ox2, oy2);
1471 if (stretch1 == 0) wid1 = wid2;
1472 if (stretch2 == 0) wid2 = wid1;
1473 stretchwid = mini(wid1, wid2);
1474 if (stretchwid > wid) wid = stretchwid;
1475 return(wid);
1476 }
1477
1478 /*
1479 * routine to determine the nodeinst to be used when connecting the geometry
1480 * module pointed to by "ipos" and the geometry module in "othergeom".
1481 * The port prototype on the other object ("othergeom") is in "otherport" and
1482 * the port prototype on the object in "ipos" is in "ipp". The prefered site
1483 * of connection is in (prefx, prefy). If the first module (ipos) is an
1484 * arcinst, it may be split into two arcs and a nodeinst in which case that
1485 * nodeinst will be returned and the address of the geometry module will be
1486 * changed to point to that nodeinst. If "fake" is nonzero, the newly created
1487 * node will be a fake one (not really created) for the purposes of determining
1488 * an intended connection only.
1489 */
us_getnodeonarcinst(GEOM ** ipos,PORTPROTO ** ipp,GEOM * othergeom,PORTPROTO * otherport,INTBIG prefx,INTBIG prefy,INTBIG fake)1490 NODEINST *us_getnodeonarcinst(GEOM **ipos, PORTPROTO **ipp, GEOM *othergeom,
1491 PORTPROTO *otherport, INTBIG prefx, INTBIG prefy, INTBIG fake)
1492 {
1493 REGISTER ARCINST *ai, *oar;
1494 ARCINST *ai1, *ai2;
1495 REGISTER ARCPROTO *ap;
1496 REGISTER NODEINST *ni;
1497 REGISTER NODEPROTO *np;
1498 REGISTER GEOM *geom;
1499 static NODEINST node;
1500 REGISTER INTBIG w, wid, owid, lx, hx, ly, hy, delta, fun;
1501 INTBIG pxs, pys;
1502
1503 /* get the actual geometry modules */
1504 geom = *ipos;
1505
1506 /* if the module is a nodeinst, return it */
1507 if (geom->entryisnode) return(geom->entryaddr.ni);
1508
1509 /* if the other module is a node use it to determine the break point */
1510 if (othergeom->entryisnode)
1511 {
1512 ni = othergeom->entryaddr.ni;
1513
1514 /* if the other module has a port specified, use port center as break point */
1515 if (othergeom->entryisnode && otherport != NOPORTPROTO)
1516 {
1517 portposition(ni, otherport, &prefx, &prefy);
1518 } else
1519 {
1520 /* if the other module is a primitive node, use center as break point */
1521 if (ni->proto->primindex != 0)
1522 {
1523 prefx = (ni->lowx + ni->highx) / 2;
1524 prefy = (ni->lowy + ni->highy) / 2;
1525 }
1526 }
1527 }
1528
1529 /* find point on this arcinst closest to break point */
1530 ai = geom->entryaddr.ai;
1531 if (ai->end[0].xpos == ai->end[1].xpos)
1532 {
1533 /* vertical arcinst */
1534 if (!othergeom->entryisnode)
1535 {
1536 /* if two arcs are perpendicular, find point of intersection */
1537 oar = othergeom->entryaddr.ai;
1538 if (oar->end[0].ypos == oar->end[1].ypos) prefy = oar->end[0].ypos;
1539 }
1540 } else if (ai->end[0].ypos == ai->end[1].ypos)
1541 {
1542 /* horizontal arcinst */
1543 if (!othergeom->entryisnode)
1544 {
1545 /* if two arcs are perpendicular, find point of intersection */
1546 oar = othergeom->entryaddr.ai;
1547 if (oar->end[0].xpos == oar->end[1].xpos) prefx = oar->end[0].xpos;
1548 }
1549 }
1550
1551 /* adjust to closest point */
1552 (void)closestpointtosegment(ai->end[0].xpos, ai->end[0].ypos,
1553 ai->end[1].xpos, ai->end[1].ypos, &prefx, &prefy);
1554 if (prefx == ai->end[0].xpos && prefy == ai->end[0].ypos)
1555 {
1556 *ipp = ai->end[0].portarcinst->proto;
1557 *ipos = ai->end[0].nodeinst->geom;
1558 return(ai->end[0].nodeinst);
1559 }
1560 if (prefx == ai->end[1].xpos && prefy == ai->end[1].ypos)
1561 {
1562 *ipp = ai->end[1].portarcinst->proto;
1563 *ipos = ai->end[1].nodeinst->geom;
1564 return(ai->end[1].nodeinst);
1565 }
1566
1567 /* create the splitting pin */
1568 ap = ai->proto;
1569 np = us_findconnectingpin(ap, othergeom, otherport);
1570 if (np == NONODEPROTO) return(NONODEINST);
1571
1572 /* determine width of other arc */
1573 wid = ai->width;
1574 us_getproperconsize(np, ap, wid, ap, wid, &pxs, &pys);
1575
1576 /* make contact asymmetric if other arc is wider */
1577 fun = (np->userbits&NFUNCTION) >> NFUNCTIONSH;
1578 if (fun != NPPIN)
1579 {
1580 owid = wid;
1581 if (othergeom->entryisnode)
1582 {
1583 w = us_widestarcinst(ap, othergeom->entryaddr.ni, otherport);
1584 if (w > owid) owid = w;
1585 } else
1586 {
1587 if (othergeom->entryaddr.ai->width > owid)
1588 owid = othergeom->entryaddr.ai->width;
1589 }
1590 if (owid > ai->width)
1591 {
1592 delta = owid - ai->width;
1593 if (ai->end[0].xpos == ai->end[1].xpos)
1594 {
1595 /* vertical arc, make contact larger in Y */
1596 pys += delta;
1597 } else if (ai->end[0].ypos == ai->end[1].ypos)
1598 {
1599 /* horizontal arc, make contact larger in x */
1600 pxs += delta;
1601 }
1602 }
1603 }
1604
1605 /* create the contact */
1606 lx = prefx - pxs/2; hx = lx + pxs;
1607 ly = prefy - pys/2; hy = ly + pys;
1608 if (fake != 0)
1609 {
1610 ni = &node; initdummynode(ni);
1611 ni->lowx = lx; ni->highx = hx;
1612 ni->lowy = ly; ni->highy = hy;
1613 ni->proto = np;
1614 } else
1615 {
1616 ni = newnodeinst(np, lx, hx, ly, hy, 0, 0, ai->parent);
1617 if (ni == NONODEINST)
1618 {
1619 ttyputerr(_("Cannot create splitting pin"));
1620 return(NONODEINST);
1621 }
1622 endobjectchange((INTBIG)ni, VNODEINST);
1623
1624 /* create the two new arcinsts */
1625 (void)us_breakarcinsertnode(ai, ni, &ai1, &ai2);
1626 }
1627
1628 /* return pointers to the splitting pin */
1629 *ipos = ni->geom;
1630 *ipp = ni->proto->firstportproto;
1631 return(ni);
1632 }
1633
1634 /*
1635 * Routine to break arc "ai" at node "ni". Sets the two new halves in "ai1" and "ai2".
1636 * Returns TRUE on error.
1637 */
us_breakarcinsertnode(ARCINST * ai,NODEINST * ni,ARCINST ** ai1,ARCINST ** ai2)1638 BOOLEAN us_breakarcinsertnode(ARCINST *ai, NODEINST *ni, ARCINST **ai1, ARCINST **ai2)
1639 {
1640 REGISTER NODEINST *fno, *tno;
1641 REGISTER ARCINST *mainarc;
1642 REGISTER PORTPROTO *fpt, *tpt, *pp;
1643 REGISTER NODEPROTO *parent;
1644 REGISTER INTBIG fendx, fendy, tendx, tendy, bits1, bits2, wid;
1645 INTBIG x, y;
1646 REGISTER ARCPROTO *ap;
1647
1648 fno = ai->end[0].nodeinst; fpt = ai->end[0].portarcinst->proto;
1649 tno = ai->end[1].nodeinst; tpt = ai->end[1].portarcinst->proto;
1650 fendx = ai->end[0].xpos; fendy = ai->end[0].ypos;
1651 tendx = ai->end[1].xpos; tendy = ai->end[1].ypos;
1652 ap = ai->proto; wid = ai->width; parent = ai->parent;
1653 pp = ni->proto->firstportproto;
1654 portposition(ni, pp, &x, &y);
1655 bits1 = bits2 = ai->userbits;
1656 if ((bits1&ISNEGATED) != 0)
1657 {
1658 if ((bits1&REVERSEEND) == 0) bits2 &= ~ISNEGATED; else
1659 bits1 &= ~ISNEGATED;
1660 }
1661 if (figureangle(fendx, fendy, x, y) != figureangle(x, y, tendx, tendy))
1662 {
1663 bits1 &= ~FIXANG;
1664 bits2 &= ~FIXANG;
1665 }
1666
1667 /* create the two new arcinsts */
1668 *ai1 = newarcinst(ap, wid, bits1, fno, fpt, fendx, fendy, ni, pp, x, y, parent);
1669 *ai2 = newarcinst(ap, wid, bits2, ni, pp, x, y, tno, tpt, tendx, tendy, parent);
1670 if (*ai1 == NOARCINST || *ai2 == NOARCINST)
1671 {
1672 ttyputerr(_("Error creating the split arc parts"));
1673 return(TRUE);
1674 }
1675
1676 /* figure out on which half of the arc to place former arc information */
1677 mainarc = *ai1;
1678 if (fno->proto->primindex == 0 && tno->proto->primindex != 0)
1679 mainarc = *ai2; else
1680 {
1681 if (computedistance(fendx, fendy, x, y) <
1682 computedistance(tendx, tendy, x, y)) mainarc = *ai2;
1683 }
1684
1685 (void)copyvars((INTBIG)ai, VARCINST, (INTBIG)mainarc, VARCINST, FALSE);
1686 endobjectchange((INTBIG)*ai1, VARCINST);
1687 endobjectchange((INTBIG)*ai2, VARCINST);
1688
1689 /* delete the old arcinst */
1690 startobjectchange((INTBIG)ai, VARCINST);
1691 if (killarcinst(ai))
1692 {
1693 ttyputerr(_("Error deleting original arc"));
1694 return(TRUE);
1695 }
1696 return(FALSE);
1697 }
1698
1699 /*
1700 * Routine to find the connecting node that can join arcs of type "ap" with
1701 * an object "geom" (which may be a node with port "pp"). Returns NONODEPROTO
1702 * if no connecting node can be found.
1703 */
us_findconnectingpin(ARCPROTO * ap,GEOM * geom,PORTPROTO * pp)1704 NODEPROTO *us_findconnectingpin(ARCPROTO *ap, GEOM *geom, PORTPROTO *pp)
1705 {
1706 REGISTER NODEPROTO *np, *niproto;
1707 REGISTER ARCINST *ai;
1708 REGISTER PORTPROTO *firstpp;
1709 REGISTER TECHNOLOGY *tech;
1710 REGISTER INTBIG fun, i, j;
1711
1712 /* first presume the pin that connects this type of arc */
1713 np = getpinproto(ap);
1714
1715 /* if there is no other object, use this pin */
1716 if (geom == NOGEOM) return(np);
1717
1718 /* ensure that it connects to this */
1719 if (geom->entryisnode)
1720 {
1721 if (pp == NOPORTPROTO)
1722 {
1723 niproto = geom->entryaddr.ni->proto;
1724 pp = niproto->firstportproto;
1725 if (pp == NOPORTPROTO || pp->nextportproto != NOPORTPROTO)
1726 return(np);
1727 }
1728 for(i=0; pp->connects[i] != NOARCPROTO; i++)
1729 {
1730 if (pp->connects[i] == ap) break;
1731
1732 /* special case: bus arc can connect to a node with a wire-pin port */
1733 if (ap == sch_busarc && pp->connects[i] == sch_wirearc) break;
1734 }
1735 if (pp->connects[i] == NOARCPROTO) np = NONODEPROTO;
1736 } else
1737 {
1738 ai = geom->entryaddr.ai;
1739 if (ai->proto != ap) np = NONODEPROTO;
1740 }
1741 if (np == NONODEPROTO)
1742 {
1743 /* doesn't connect: look for a contact */
1744 tech = ap->tech;
1745 for(np = tech->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
1746 {
1747 fun = (np->userbits&NFUNCTION) >> NFUNCTIONSH;
1748 if (fun != NPCONTACT) continue;
1749 firstpp = np->firstportproto;
1750
1751 /* make sure the original arc connects to this contact */
1752 for(i=0; firstpp->connects[i] != NOARCPROTO; i++)
1753 if (firstpp->connects[i] == ap) break;
1754 if (firstpp->connects[i] == NOARCPROTO) continue;
1755
1756 /* make sure the other object connects to this contact */
1757 for(i=0; firstpp->connects[i] != NOARCPROTO; i++)
1758 {
1759 if (firstpp->connects[i]->tech != tech) continue;
1760 if (geom->entryisnode)
1761 {
1762 for(j=0; pp->connects[j] != NOARCPROTO; j++)
1763 if (pp->connects[j] == firstpp->connects[i]) break;
1764 if (pp->connects[j] != NOARCPROTO) break;
1765 } else
1766 {
1767 ai = geom->entryaddr.ai;
1768 if (ai->proto == firstpp->connects[i]) break;
1769 }
1770 }
1771 if (firstpp->connects[i] != NOARCPROTO) break;
1772 }
1773 }
1774 return(np);
1775 }
1776
1777 /*
1778 * routine to report the distance of point (prefx,prefy) to port "pt" of node
1779 * instance "ni". The closest point on the polygon is returned in (x,y),
1780 * given that the port will connect to an arc with width "wid". If "purpose"
1781 * is true, a new sub-port location within the port is desired from
1782 * the "shapeportpoly" routine. Euclidean distance is not always used, but
1783 * at least the metric is consistent with itself.
1784 */
us_portdistance(NODEINST * ni,PORTPROTO * pt,INTBIG prefx,INTBIG prefy,INTBIG * x,INTBIG * y,INTBIG wid,BOOLEAN purpose)1785 INTBIG us_portdistance(NODEINST *ni, PORTPROTO *pt, INTBIG prefx, INTBIG prefy, INTBIG *x,
1786 INTBIG *y, INTBIG wid, BOOLEAN purpose)
1787 {
1788 static POLYGON *poly = NOPOLYGON;
1789 REGISTER INTBIG i, j;
1790 REGISTER INTBIG bestdist, px, py, nx, ny;
1791
1792 /* get polygon */
1793 (void)needstaticpolygon(&poly, 4, us_tool->cluster);
1794
1795 /* specify prefered location of new port */
1796 poly->xv[0] = prefx; poly->yv[0] = prefy; poly->count = 1;
1797 shapeportpoly(ni, pt, poly, purpose);
1798 switch (poly->style)
1799 {
1800 case FILLED:
1801 /* reduce the port area to the proper amount for this arc */
1802 reduceportpoly(poly, ni, pt, wid, -1);
1803
1804 /* for filled polygon, see if point is inside */
1805 if (isinside(prefx, prefy, poly))
1806 {
1807 *x = prefx; *y = prefy;
1808 return(0);
1809 }
1810
1811 *x = prefx; *y = prefy;
1812 closestpoint(poly, x, y);
1813 return(0);
1814
1815 case OPENED:
1816 case CLOSED:
1817 /* for OPENED/CLOSED polygons look for proximity to a vertex */
1818 bestdist = abs(poly->xv[0] - prefx) + abs(poly->yv[0] - prefy);
1819 *x = poly->xv[0]; *y = poly->yv[0];
1820 for(j=1; j<poly->count; j++)
1821 {
1822 i = abs(poly->xv[j] - prefx) + abs(poly->yv[j] - prefy);
1823 if (i < bestdist)
1824 {
1825 bestdist = i;
1826 *x = poly->xv[j]; *y = poly->yv[j];
1827 }
1828 }
1829
1830 /* additionally, look for proximity to an edge */
1831 for(j=0; j<poly->count; j++)
1832 {
1833 if (j == 0)
1834 {
1835 if (poly->style == OPENED) continue;
1836 px = poly->xv[poly->count-1];
1837 py = poly->yv[poly->count-1];
1838 } else
1839 {
1840 px = poly->xv[j-1];
1841 py = poly->yv[j-1];
1842 }
1843 nx = poly->xv[j];
1844 ny = poly->yv[j];
1845
1846 /* handle vertical line that is perpendicular to point */
1847 if (px == nx && maxi(py, ny) >= prefy && mini(py, ny) <= prefy &&
1848 abs(px - prefx) < bestdist)
1849 {
1850 bestdist = i;
1851 *x = px;
1852 *y = prefy;
1853 }
1854
1855 /* handle horizontal line that is perpendicular to point */
1856 if (py == ny && maxi(px, nx) >= prefx && mini(px, nx) <= prefx &&
1857 abs(py - prefy) < bestdist)
1858 {
1859 bestdist = i;
1860 *x = prefx;
1861 *y = py;
1862 }
1863 }
1864 return(bestdist);
1865 }
1866
1867 /* bogus answer for unusual shapes!!! */
1868 return(0);
1869 }
1870
1871 /*
1872 * Routine to determine the size of contact "con", used when connecting arcs "fromap" that
1873 * are "fwid" wide and "toap" that are "twid" wide. The size is put in (pxs,pys).
1874 */
us_getproperconsize(NODEPROTO * con,ARCPROTO * fromap,INTBIG fwid,ARCPROTO * toap,INTBIG twid,INTBIG * pxs,INTBIG * pys)1875 void us_getproperconsize(NODEPROTO *con, ARCPROTO *fromap, INTBIG fwid,
1876 ARCPROTO *toap, INTBIG twid, INTBIG *pxs, INTBIG *pys)
1877 {
1878 REGISTER NODEINST *ni;
1879 REGISTER ARCINST *ai;
1880 REGISTER INTBIG truefwid, truetwid, fromlayer, tolayer, tot, i, fromdiff, todiff,
1881 size, growth;
1882 INTBIG lx, hx, ly, hy;
1883 NODEINST node;
1884 ARCINST arc;
1885 static POLYGON *poly = NOPOLYGON;
1886
1887 /* get polygon */
1888 (void)needstaticpolygon(&poly, 4, us_tool->cluster);
1889
1890 /* determine width and layer of each arc */
1891 ai = &arc; initdummyarc(ai);
1892 ai->proto = fromap;
1893 ai->width = fromap->nominalwidth;
1894 ai->end[0].xpos = -1000000; ai->end[0].ypos = 0;
1895 ai->end[1].xpos = 1000000; ai->end[1].ypos = 0;
1896 tot = arcpolys(ai, NOWINDOWPART);
1897 shapearcpoly(ai, 0, poly);
1898 fromlayer = poly->layer;
1899 truefwid = fwid - arcprotowidthoffset(fromap);
1900
1901 ai->proto = toap;
1902 ai->width = toap->nominalwidth;
1903 ai->end[0].xpos = -1000000; ai->end[0].ypos = 0;
1904 ai->end[1].xpos = 1000000; ai->end[1].ypos = 0;
1905 tot = arcpolys(ai, NOWINDOWPART);
1906 shapearcpoly(ai, 0, poly);
1907 tolayer = poly->layer;
1908 truetwid = twid - arcprotowidthoffset(toap);
1909
1910 ni = &node; initdummynode(ni);
1911 ni->proto = con;
1912 ni->lowx = con->lowx; ni->highx = con->highx;
1913 ni->lowy = con->lowy; ni->highy = con->highy;
1914 tot = nodepolys(ni, 0, NOWINDOWPART);
1915 fromdiff = todiff = 0;
1916 for(i=0; i<tot; i++)
1917 {
1918 shapenodepoly(ni, i, poly);
1919 if (poly->layer == fromlayer)
1920 {
1921 getbbox(poly, &lx, &hx, &ly, &hy);
1922 size = maxi(hx-lx, hy-ly);
1923 fromdiff = truefwid - size;
1924 }
1925 if (poly->layer == tolayer)
1926 {
1927 getbbox(poly, &lx, &hx, &ly, &hy);
1928 size = maxi(hx-lx, hy-ly);
1929 todiff = truetwid - size;
1930 }
1931 }
1932 defaultnodesize(con, pxs, pys);
1933 growth = maxi(fromdiff, todiff);
1934 if (growth > 0)
1935 {
1936 *pxs += growth;
1937 *pys += growth;
1938 }
1939 }
1940
1941 /*
1942 * Routine to find the center of node "ni", port "pp" and place it in (x,y).
1943 * Adjusts the coordinate to account for grid alignment.
1944 */
us_portposition(NODEINST * ni,PORTPROTO * pp,INTBIG * x,INTBIG * y)1945 void us_portposition(NODEINST *ni, PORTPROTO *pp, INTBIG *x, INTBIG *y)
1946 {
1947 INTBIG ax, ay;
1948 static POLYGON *poly = NOPOLYGON;
1949
1950 /* get polygon */
1951 (void)needstaticpolygon(&poly, 4, us_tool->cluster);
1952 portposition(ni, pp, x, y);
1953 ax = *x; ay = *y;
1954 gridalign(&ax, &ay, 1, ni->parent);
1955 if (ax != *x || ay != *y)
1956 {
1957 shapeportpoly(ni, pp, poly, FALSE);
1958 if (isinside(ax, ay, poly))
1959 {
1960 *x = ax;
1961 *y = ay;
1962 }
1963 }
1964 }
1965
1966 /*
1967 * routine to determine whether port "pp" of node "ni" can connect to an
1968 * arc at angle "angle" within range "range". Returns true if the
1969 * connection cannot be made.
1970 */
us_fitportangle(NODEINST * ni,PORTPROTO * pp,INTBIG angle,INTBIG range)1971 BOOLEAN us_fitportangle(NODEINST *ni, PORTPROTO *pp, INTBIG angle, INTBIG range)
1972 {
1973 REGISTER INTBIG j;
1974
1975 j = us_bottomrecurse(ni, pp);
1976 j = (j - angle) % 3600; if (j < 0) j += 3600;
1977 if (j > 1800) j = 3600 - j;
1978 if (j > range) return(TRUE);
1979 return(FALSE);
1980 }
1981
1982 /*
1983 * routine to recurse to the bottom (most primitive node) of a port and
1984 * compute the port orientation from the bottom up.
1985 */
us_bottomrecurse(NODEINST * ni,PORTPROTO * pp)1986 INTBIG us_bottomrecurse(NODEINST *ni, PORTPROTO *pp)
1987 {
1988 REGISTER INTBIG k;
1989
1990 if (ni->proto->primindex == 0)
1991 k = us_bottomrecurse(pp->subnodeinst, pp->subportproto); else
1992 k = ((pp->userbits&PORTANGLE) >> PORTANGLESH) * 10;
1993 k += ni->rotation;
1994 if (ni->transpose != 0) k = 2700 - k;
1995 return(k);
1996 }
1997
1998 /*
1999 * Routine to figure out the proper "cursor location" given that arcs are to be
2000 * drawn from "fromgeom/fromport" to "togeom/toport" and that the cursor is at
2001 * (xcur,ycur). Changes (xcur,ycur) to be the proper location.
2002 */
us_figuredrawpath(GEOM * fromgeom,PORTPROTO * fromport,GEOM * togeom,PORTPROTO * toport,INTBIG * xcur,INTBIG * ycur)2003 BOOLEAN us_figuredrawpath(GEOM *fromgeom, PORTPROTO *fromport, GEOM *togeom, PORTPROTO *toport,
2004 INTBIG *xcur, INTBIG *ycur)
2005 {
2006 static POLYGON *poly = NOPOLYGON, *poly2 = NOPOLYGON;
2007 REGISTER NODEINST *tonode, *fromnode;
2008 INTBIG flx, fhx, fly, fhy, tlx, tly, thx, thy, lx, hx, ly, hy, overx[2], overy[2];
2009 REGISTER INTBIG k, overcount, fx, fy, tx, ty, c1x, c1y, c2x, c2y,
2010 c1dist, c2dist, pxs, pys, dist, ox, oy, x, y;
2011
2012 /* get polygon */
2013 (void)needstaticpolygon(&poly, 4, us_tool->cluster);
2014 (void)needstaticpolygon(&poly2, 4, us_tool->cluster);
2015
2016 if (!fromgeom->entryisnode || fromport == NOPORTPROTO)
2017 {
2018 us_abortcommand(_("Cannot connect to %s: it has no ports"),
2019 geomname(fromgeom));
2020 return(TRUE);
2021 }
2022 fromnode = fromgeom->entryaddr.ni;
2023
2024 ox = *xcur; oy = *ycur;
2025 gridalign(xcur, ycur, 1, fromnode->parent);
2026
2027 /* user dragged over another object: connect them */
2028 tonode = us_getnodeonarcinst(&togeom, &toport, fromgeom,
2029 fromport, *xcur, *ycur, 1);
2030 if (tonode == NONODEINST)
2031 {
2032 us_abortcommand(_("Cannot find a way to connect these objects"));
2033 return(TRUE);
2034 }
2035 if (toport == NOPORTPROTO)
2036 {
2037 us_abortcommand(_("Cannot connect to %s: it has no ports"),
2038 geomname(togeom));
2039 return(TRUE);
2040 }
2041
2042 /* change cursor according to port angle */
2043 shapeportpoly(fromnode, fromport, poly2, FALSE);
2044 getbbox(poly2, &flx, &fhx, &fly, &fhy);
2045 shapeportpoly(tonode, toport, poly2, FALSE);
2046 getbbox(poly2, &tlx, &thx, &tly, &thy);
2047 lx = mini(flx, tlx);
2048 hx = maxi(fhx, thx);
2049 ly = mini(fly, tly);
2050 hy = maxi(fhy, thy);
2051 dist = maxi(hx-lx, hy-ly);
2052 overcount = 0;
2053 if (((fromport->userbits&PORTARANGE) >> PORTARANGESH) != 180)
2054 {
2055 k = us_bottomrecurse(fromnode, fromport) % 3600;
2056 if (k < 0) k += 3600;
2057 overx[overcount] = (flx+fhx)/2 + mult(cosine(k), dist);
2058 overy[overcount] = (fly+fhy)/2 + mult(sine(k), dist);
2059 overcount++;
2060 }
2061 if (((toport->userbits&PORTARANGE) >> PORTARANGESH) != 180)
2062 {
2063 k = us_bottomrecurse(tonode, toport) % 3600;
2064 if (k < 0) k += 3600;
2065 overx[overcount] = (tlx+thx)/2 + mult(cosine(k), dist);
2066 overy[overcount] = (tly+thy)/2 + mult(sine(k), dist);
2067 overcount++;
2068 }
2069 if (overcount == 2)
2070 {
2071 pxs = (overx[0] + overx[1]) / 2;
2072 pys = (overy[0] + overy[1]) / 2;
2073 } else
2074 {
2075 pxs = (flx + fhx) / 2;
2076 pys = (tly + thy) / 2;
2077 if (overcount > 0)
2078 {
2079 pxs = overx[0]; pys = overy[0];
2080 } else
2081 {
2082 /* use slight cursor differences to pick a corner */
2083 fx = (flx+fhx)/2;
2084 fy = (fly+fhy)/2;
2085 tx = (tlx+thx)/2;
2086 ty = (tly+thy)/2;
2087 c1x = fx; c1y = ty;
2088 c2x = tx; c2y = fy;
2089 c1dist = computedistance(ox, oy, c1x, c1y) +
2090 computedistance(c1x, c1y, fx, fy);
2091 c2dist = computedistance(ox, oy, c2x, c2y) +
2092 computedistance(c2x, c2y, fx, fy);
2093 if (c1dist < c2dist)
2094 {
2095 pxs = c1x; pys = c1y;
2096 } else
2097 {
2098 pxs = c2x; pys = c2y;
2099 }
2100 }
2101
2102 /* set coordinate in the center if the ports overlap in either axis */
2103 if (fromport != NOPORTPROTO)
2104 {
2105 shapeportpoly(fromnode, fromport, poly, FALSE);
2106 getbbox(poly, &flx, &fhx, &fly, &fhy);
2107 shapeportpoly(tonode, toport, poly, FALSE);
2108 getbbox(poly, &tlx, &thx, &tly, &thy);
2109 lx = maxi(flx, tlx); hx = mini(fhx, thx);
2110 ly = maxi(fly, tly); hy = mini(fhy, thy);
2111 if (lx <= hx || ly <= hy)
2112 {
2113 pxs = (lx + hx) / 2;
2114 pys = (ly + hy) / 2;
2115 }
2116 }
2117 }
2118
2119 /* isolated ports simply use cursor location */
2120 if ((fromport != NOPORTPROTO &&
2121 (fromport->userbits&PORTISOLATED) != 0) ||
2122 (toport->userbits&PORTISOLATED) != 0)
2123 {
2124 us_portposition(fromnode, fromport, &lx, &ly);
2125 us_portposition(tonode, toport, &hx, &hy);
2126 if (hx < lx)
2127 {
2128 x = lx; lx = hx; hx = x;
2129 }
2130 if (hy < ly)
2131 {
2132 y = ly; ly = hy; hy = y;
2133 }
2134
2135 /* if cursor location is within range of ports, use it */
2136 if (*xcur >= lx && *xcur <= hx) pxs = *xcur;
2137 if (*ycur >= ly && *ycur <= hy) pys = *ycur;
2138 }
2139 *xcur = pxs;
2140 *ycur = pys;
2141 return(FALSE);
2142 }
2143