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