1 /* -*- tab-width: 4 -*-
2  *
3  * Electric(tm) VLSI Design System
4  *
5  * File: usrwindow.c
6  * User interface tool: public graphics routines
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  * info@staticfreesoft.com
30  */
31 
32 #include "global.h"
33 #include "egraphics.h"
34 #include "efunction.h"
35 #include "usr.h"
36 #include "usrtrack.h"
37 #include "tecgen.h"
38 #include <math.h>
39 
40 /***** REDRAW MODULES *****/
41 
42 #define NOREDRAW ((REDRAW *)-1)
43 
44 typedef struct Iredraw
45 {
46 	BOOLEAN          entryisnode;		/* true of object being re-drawn is node */
47 	union us_entry
48 	{
49 		NODEINST   *ni;
50 		ARCINST    *ai;
51 		PORTPROTO  *pp;
52 		void       *blind;
53 	} entryaddr;						/* object being re-drawn */
54 	struct Iredraw *nextredraw;			/* next in list */
55 } REDRAW;
56 
57 static REDRAW   *us_redrawfree = NOREDRAW;	/* free list of re-draw modules */
58 static REDRAW   *us_firstredraw = NOREDRAW;	/* first in list of active re-draw modules */
59 static REDRAW   *us_firstopaque = NOREDRAW;	/* first in list of active opaque re-draws */
60 static REDRAW   *us_firstexport = NOREDRAW;	/* first in list of export re-draws */
61 static REDRAW   *us_lastopaque = NOREDRAW;	/* last in list of active opaque re-draws */
62 static REDRAW   *us_usedredraw = NOREDRAW;	/* first in list of used re-draw modules */
63 
64 static REDRAW *us_allocredraw(void);
65 static void    us_freeredraw(REDRAW*);
66 
67 /***** 3D DISPLAY *****/
68 
69 #define FOVMAX 170.0f
70 #define FOVMIN   2.0f
71 
72 enum {ROTATEVIEW, ZOOMVIEW, PANVIEW, TWISTVIEW};
73 
74 typedef struct
75 {
76 	INTBIG    count;
77 	INTBIG    total;
78 	GRAPHICS *desc;
79 	float     depth;
80 	float    *x, *y, *z;
81 } POLY3D;
82 
83 static BOOLEAN     us_3dgatheringpolys = 0;
84 static INTBIG      us_3dpolycount;
85 static INTBIG      us_3dpolytotal = 0;
86 static POLY3D    **us_3dpolylist;
87 static WINDOWPART *us_3dwindowpart;
88 
89 /* interaction information */
90 static INTBIG      us_3dinteraction = ROTATEVIEW;
91 static INTBIG      us_3dinitialbuty;
92 static INTBIG      us_3dlastbutx, us_3dlastbuty;
93 static float       us_3dinitialfov;
94 static float       us_3dcenterx, us_3dcentery, us_3dcenterz;
95 static float       us_3dlowx, us_3dhighx, us_3dlowy, us_3dhighy, us_3dlowz, us_3dhighz;
96 
97 static BOOLEAN us_3deachdown(INTBIG x, INTBIG y);
98 static void    us_3dbuildtransform(XFORM3D *xf3);
99 static void    us_3drender(WINDOWPART *w);
100 static POLY3D *us_3dgetnextpoly(INTBIG count);
101 static int     us_3dpolydepthascending(const void *e1, const void *e2);
102 static void    us_3drotatepointaboutaxis(float *p, float theta, float *r, float *q);
103 
104 /***** MISCELLANEOUS *****/
105 
106 #define ZSHIFT       4
107 #define ZUP(x)       ((x) << ZSHIFT)
108 #define ZDN(x)       (((x) + 1) >> ZSHIFT)
109 
110 #define CROSSSIZE      3				/* size in pixels of half a cross */
111 #define BIGCROSSSIZE   5				/* size in pixels of half a big cross */
112 
113 extern GRAPHICS us_ebox;
114 
115 /* prototypes for local routines */
116 static INTBIG us_showin(WINDOWPART*, GEOM*, NODEPROTO*, XARRAY, INTBIG, INTBIG);
117 static void us_queuevicinity(WINDOWPART*, GEOM*, NODEPROTO*, XARRAY, INTBIG, INTBIG, INTBIG, INTBIG, INTBIG);
118 static void us_wanttodrawo(INTBIG, INTBIG, INTBIG, INTBIG, INTBIG, INTBIG, INTBIG, INTBIG, WINDOWPART*, GRAPHICS*, INTBIG);
119 static void us_drawextendedpolyline(POLYGON*, WINDOWPART*);
120 
121 /*
122  * Routine to free all memory associated with this module.
123  */
us_freewindowmemory(void)124 void us_freewindowmemory(void)
125 {
126 	REGISTER REDRAW *r;
127 	REGISTER POLY3D *p3;
128 	REGISTER INTBIG i;
129 
130 	while (us_redrawfree != NOREDRAW)
131 	{
132 		r = us_redrawfree;
133 		us_redrawfree = us_redrawfree->nextredraw;
134 		efree((CHAR *)r);
135 	}
136 
137 	for(i=0; i<us_3dpolytotal; i++)
138 	{
139 		p3 = us_3dpolylist[i];
140 		if (p3->total > 0)
141 		{
142 			efree((CHAR *)p3->x);
143 			efree((CHAR *)p3->y);
144 			efree((CHAR *)p3->z);
145 		}
146 		efree((CHAR *)p3);
147 	}
148 	if (us_3dpolytotal > 0)
149 		efree((CHAR *)us_3dpolylist);
150 }
151 
152 /******************** WINDOW DISPLAY ********************/
153 
154 /* routine to erase the cell in window "w" */
us_clearwindow(WINDOWPART * w)155 void us_clearwindow(WINDOWPART *w)
156 {
157 	REGISTER INTBIG newstate;
158 
159 	startobjectchange((INTBIG)w, VWINDOWPART);
160 	(void)setval((INTBIG)w, VWINDOWPART, x_("curnodeproto"), (INTBIG)NONODEPROTO, VNODEPROTO);
161 	newstate = (w->state & ~(GRIDON|WINDOWTYPE|WINDOWMODE)) | DISPWINDOW;
162 	(void)setval((INTBIG)w, VWINDOWPART, x_("state"), newstate, VINTEGER);
163 	(void)setval((INTBIG)w, VWINDOWPART, x_("buttonhandler"), (INTBIG)DEFAULTBUTTONHANDLER, VADDRESS);
164 	(void)setval((INTBIG)w, VWINDOWPART, x_("charhandler"), (INTBIG)DEFAULTCHARHANDLER, VADDRESS);
165 	(void)setval((INTBIG)w, VWINDOWPART, x_("changehandler"), (INTBIG)DEFAULTCHANGEHANDLER, VADDRESS);
166 	(void)setval((INTBIG)w, VWINDOWPART, x_("termhandler"), (INTBIG)DEFAULTTERMHANDLER, VADDRESS);
167 	(void)setval((INTBIG)w, VWINDOWPART, x_("redisphandler"), (INTBIG)DEFAULTREDISPHANDLER, VADDRESS);
168 	endobjectchange((INTBIG)w, VWINDOWPART);
169 }
170 
171 /* routine to erase the display of window "w" */
us_erasewindow(WINDOWPART * w)172 void us_erasewindow(WINDOWPART *w)
173 {
174 	static POLYGON *poly = NOPOLYGON;
175 
176 	/* get polygon */
177 	(void)needstaticpolygon(&poly, 4, us_tool->cluster);
178 
179 	maketruerectpoly(w->screenlx, w->screenhx, w->screenly, w->screenhy, poly);
180 	if ((w->state&INPLACEEDIT) != 0)
181 		xformpoly(poly, w->intocell);
182 	poly->desc = &us_ebox;
183 	poly->style = FILLEDRECT;
184 	(*us_displayroutine)(poly, w);
185 }
186 
187 /*
188  * routine to begin editing cell "cur" in the current window.  The screen
189  * extents of the cell are "lx", "hx", "ly", and "hy".  If "focusinst"
190  * is not NONODEINST, then highlight that node and port "cellinstport"
191  * (if it is not NOPORTPROTO) in the new window.  If "newframe" is true,
192  * create a window for this cell, otherwise reuse the current one.
193  */
us_switchtocell(NODEPROTO * cur,INTBIG lx,INTBIG hx,INTBIG ly,INTBIG hy,NODEINST * focusinst,PORTPROTO * cellinstport,BOOLEAN newframe,BOOLEAN pushpop,BOOLEAN exact)194 void us_switchtocell(NODEPROTO *cur, INTBIG lx, INTBIG hx, INTBIG ly, INTBIG hy,
195 	NODEINST *focusinst, PORTPROTO *cellinstport, BOOLEAN newframe, BOOLEAN pushpop, BOOLEAN exact)
196 {
197 	HIGHLIGHT newhigh;
198 	REGISTER INTBIG i, l, resethandlers, oldmode;
199 	INTBIG dummy;
200 	REGISTER VARIABLE *var;
201 	CHAR *one[1];
202 	REGISTER INTBIG oldstate;
203 	REGISTER CHAR *thisline;
204 	REGISTER EDITOR *ed;
205 	WINDOWPART *neww, *oldw;
206 	UINTBIG descript[TEXTDESCRIPTSIZE];
207 	extern GRAPHICS us_hbox;
208 	REGISTER void *infstr;
209 
210 	us_clearhighlightcount();
211 	oldw = el_curwindowpart;
212 	if (oldw == NOWINDOWPART)
213 	{
214 		/* no former window: force a new one to be created */
215 		newframe = TRUE;
216 	} else
217 	{
218 		/* if this is an explorer window, use the other partition (if there is one) */
219 		if ((oldw->state&WINDOWTYPE) == EXPLORERWINDOW)
220 		{
221 			for(neww = el_topwindowpart; neww != NOWINDOWPART; neww = neww->nextwindowpart)
222 				if (neww != oldw && neww->frame == oldw->frame) break;
223 			if (neww != NOWINDOWPART) oldw = neww;
224 		}
225 	}
226 
227 	/* start changes to the tool */
228 	startobjectchange((INTBIG)us_tool, VTOOL);
229 
230 	if (newframe)
231 	{
232 		/* just create a new window for this cell */
233 		neww = us_wantnewwindow(0);
234 		if (neww == NOWINDOWPART)
235 		{
236 			us_abortcommand(_("Cannot create new window"));
237 			return;
238 		}
239 		oldmode = oldstate = DISPWINDOW;
240 	} else
241 	{
242 		/* reuse the window */
243 		oldmode = oldw->state;
244 		if (pushpop == 0 && (oldw->state&WINDOWMODE) != 0)
245 		{
246 			/* was in a mode: turn that off */
247 			us_setwindowmode(oldw, oldw->state, oldw->state & ~WINDOWMODE);
248 		}
249 		oldstate = oldw->state;
250 		neww = oldw;
251 	}
252 	el_curwindowpart = neww;
253 
254 	if ((cur->cellview->viewstate&TEXTVIEW) != 0)
255 	{
256 		/* text window: make an editor */
257 		if (us_makeeditor(neww, describenodeproto(cur), &dummy, &dummy) == NOWINDOWPART)
258 			return;
259 
260 		/* get the text that belongs here */
261 		var = getvalkey((INTBIG)cur, VNODEPROTO, VSTRING|VISARRAY, el_cell_message_key);
262 		if (var == NOVARIABLE)
263 		{
264 			one[0] = x_("");
265 			var = setvalkey((INTBIG)cur, VNODEPROTO, el_cell_message_key, (INTBIG)one,
266 				VSTRING|VISARRAY|(1<<VLENGTHSH));
267 			if (var == NOVARIABLE) return;
268 		}
269 
270 		ed = neww->editor;
271 		ed->editobjaddr = (CHAR *)cur;
272 		ed->editobjtype = VNODEPROTO;
273 		ed->editobjqual = x_("FACET_message");
274 		ed->editobjvar = var;
275 
276 		/* load the text into the window */
277 		us_suspendgraphics(neww);
278 		l = getlength(var);
279 		for(i=0; i<l; i++)
280 		{
281 			thisline = ((CHAR **)var->addr)[i];
282 			if (i == l-1 && *thisline == 0) continue;
283 			us_addline(neww, i, thisline);
284 		}
285 		us_resumegraphics(neww);
286 
287 		/* setup for editing */
288 		neww->curnodeproto = cur;
289 		neww->changehandler = us_textcellchanges;
290 	} else
291 	{
292 		neww->curnodeproto = cur;
293 		neww->editor = NOEDITOR;
294 
295 		/* change the window type to be appropriate for editing */
296 		neww->state = (oldstate & ~WINDOWTYPE) | DISPWINDOW;
297 		resethandlers = 1;
298 		if (pushpop != 0)
299 		{
300 			if ((neww->state&WINDOWMODE) != 0 &&
301 				((oldstate&WINDOWTYPE) == DISPWINDOW))
302 			{
303 				resethandlers = 0;
304 			}
305 		}
306 
307 		/* if editing a technology-edit cell, indicate so */
308 		if ((cur->userbits&TECEDITCELL) != 0)
309 		{
310 			us_setwindowmode(neww, neww->state, neww->state | WINDOWTECEDMODE);
311 		}
312 
313 		/* adjust window if it changed from display window to something else */
314 		if ((neww->state&WINDOWTYPE) == DISPWINDOW && (oldstate&WINDOWTYPE) != DISPWINDOW)
315 		{
316 			/* became a display window: shrink the bottom and right edge */
317 			neww->usehx -= DISPLAYSLIDERSIZE;
318 			neww->usely += DISPLAYSLIDERSIZE;
319 		}
320 		if ((neww->state&WINDOWTYPE) != DISPWINDOW && (oldstate&WINDOWTYPE) == DISPWINDOW)
321 		{
322 			/* no longer a display window: shrink the bottom and right edge */
323 			neww->usehx += DISPLAYSLIDERSIZE;
324 			neww->usely -= DISPLAYSLIDERSIZE;
325 		}
326 
327 		/* adjust window if it changed from waveform window to something else */
328 		if ((neww->state&WINDOWTYPE) == WAVEFORMWINDOW && (oldstate&WINDOWTYPE) != WAVEFORMWINDOW)
329 		{
330 			/* became a waveform window: shrink the bottom and right edge */
331 			neww->uselx += DISPLAYSLIDERSIZE;
332 			neww->usely += DISPLAYSLIDERSIZE;
333 		}
334 		if ((neww->state&WINDOWTYPE) != WAVEFORMWINDOW && (oldstate&WINDOWTYPE) == WAVEFORMWINDOW)
335 		{
336 			/* no longer a waveform window: shrink the bottom and right edge */
337 			neww->uselx -= DISPLAYSLIDERSIZE;
338 			neww->usely -= DISPLAYSLIDERSIZE;
339 		}
340 
341 		if (resethandlers != 0)
342 		{
343 			neww->buttonhandler = DEFAULTBUTTONHANDLER;
344 			neww->changehandler = DEFAULTCHANGEHANDLER;
345 			neww->charhandler = DEFAULTCHARHANDLER;
346 			neww->termhandler = DEFAULTTERMHANDLER;
347 			neww->redisphandler = DEFAULTREDISPHANDLER;
348 		}
349 
350 		us_squarescreen(neww, NOWINDOWPART, FALSE, &lx, &hx, &ly, &hy, exact);
351 		neww->screenlx = lx;
352 		neww->screenhx = hx;
353 		neww->screenly = ly;
354 		neww->screenhy = hy;
355 		computewindowscale(neww);
356 
357 		/* window gets bigger: see if grid can be drawn */
358 		us_gridset(neww, neww->state);
359 
360 		if (focusinst != NONODEINST)
361 		{
362 			newhigh.status = HIGHFROM;
363 			newhigh.cell = focusinst->parent;
364 			newhigh.fromgeom = focusinst->geom;
365 			newhigh.fromport = cellinstport;
366 			newhigh.frompoint = 0;
367 			newhigh.fromvar = NOVARIABLE;
368 			newhigh.fromvarnoeval = NOVARIABLE;
369 			us_addhighlight(&newhigh);
370 		}
371 	}
372 
373 	(void)setval((INTBIG)el_curlib, VLIBRARY, x_("curnodeproto"), (INTBIG)cur, VNODEPROTO);
374 	endobjectchange((INTBIG)us_tool, VTOOL);
375 	(void)setvalkey((INTBIG)us_tool, VTOOL, us_current_window_key, (INTBIG)neww,
376 		VWINDOWPART|VDONTSAVE);
377 	us_setcellname(neww);
378 	us_setcellsize(neww);
379 	us_setgridsize(neww);
380 	if (neww->redisphandler != 0) (*neww->redisphandler)(neww);
381 	if ((neww->state&WINDOWTECEDMODE) != 0 && (oldmode&WINDOWTECEDMODE) == 0)
382 	{
383 		TDCLEAR(descript);
384 		TDSETSIZE(descript, TXTSETPOINTS(18));
385 		us_hbox.col = HIGHLIT;
386 		us_writetext(neww->uselx, neww->usehx, neww->usehy-30, neww->usehy, &us_hbox,
387 			_("TECHNOLOGY EDIT MODE:"), descript, neww, NOTECHNOLOGY);
388 		var = getval((INTBIG)us_tool, VTOOL, VSTRING, "USER_local_capg");
389 		if (var != NOVARIABLE)
390 		{
391 			infstr = initinfstr();
392 			formatinfstr(infstr, _("Use %s to make changes"), (CHAR *)var->addr);
393 			us_writetext(neww->uselx, neww->usehx, neww->usehy-60, neww->usehy-30, &us_hbox,
394 				returninfstr(infstr), descript, neww, NOTECHNOLOGY);
395 		}
396 	}
397 }
398 
399 /*
400  * Routine to switch technologies.  If "forcedtechnology" is nonzero, switch to that
401  * technology, otherwise determine technology from cell "cell".  If "always" is false,
402  * only switch if the user has selected "auto-switching".
403  */
us_ensurepropertechnology(NODEPROTO * cell,CHAR * forcedtechnology,BOOLEAN always)404 void us_ensurepropertechnology(NODEPROTO *cell, CHAR *forcedtechnology, BOOLEAN always)
405 {
406 	REGISTER USERCOM *uc;
407 	REGISTER TECHNOLOGY *tech;
408 	REGISTER WINDOWFRAME *curframe, *lastframe;
409 	REGISTER void *infstr;
410 
411 	/* if not auto-switching technologies, quit */
412 	if ((us_useroptions&AUTOSWITCHTECHNOLOGY) == 0) return;
413 
414 	/* see if cell has a technology */
415 	if (cell == NONODEPROTO) return;
416 	if ((cell->cellview->viewstate&TEXTVIEW) != 0) return;
417 	tech = cell->tech;
418 	if (tech == gen_tech) return;
419 	if ((tech->userbits&NOPRIMTECHNOLOGY) != 0) return;
420 
421 	/* switch technologies */
422 	lastframe = getwindowframe(TRUE);
423 	if (us_getmacro(x_("pmtesetup")) == NOVARIABLE) return;
424 	infstr = initinfstr();
425 	formatinfstr(infstr, x_("pmtesetup \"%s\""), us_techname(cell));
426 	uc = us_makecommand(returninfstr(infstr));
427 	if (uc != NOUSERCOM)
428 	{
429 		us_execute(uc, FALSE, FALSE, FALSE);
430 		us_freeusercom(uc);
431 	}
432 	curframe = getwindowframe(TRUE);
433 	if (curframe != lastframe && lastframe != NOWINDOWFRAME)
434 		bringwindowtofront(lastframe);
435 }
436 
437 /*
438  * routine to initialize for changes to the screen
439  */
us_beginchanges(void)440 void us_beginchanges(void)
441 {
442 }
443 
444 /*
445  * routine to finish making changes to the screen.  If "which" is NOWINDOWPART,
446  * changes are made to all windows.  Otherwise, "which" is the specific
447  * window to update.
448  */
us_endchanges(WINDOWPART * which)449 void us_endchanges(WINDOWPART *which)
450 {
451 	REGISTER WINDOWPART *w;
452 	XARRAY rottrans;
453 	REGISTER REDRAW *r, *nextr;
454 	REGISTER NODEINST *ni;
455 	REGISTER ARCINST *ai;
456 	REGISTER PORTPROTO *pp;
457 	REGISTER INTBIG stopped;
458 	REGISTER INTBIG ret;
459 
460 	stopped = el_pleasestop;
461 	for(r = us_firstredraw; r != NOREDRAW; r = nextr)
462 	{
463 		/* remember the next redraw module and queue this one for deletion */
464 		nextr = r->nextredraw;
465 		r->nextredraw = us_usedredraw;
466 		us_usedredraw = r;
467 		if (stopped != 0) continue;
468 
469 		if (r->entryisnode)
470 		{
471 			ni = r->entryaddr.ni;
472 			if ((ni->userbits & (REWANTN|RETDONN|DEADN)) == REWANTN)
473 			{
474 				if (which != NOWINDOWPART)
475 				{
476 					if (ni->rotation == 0 && ni->transpose == 0)
477 					{
478 						ret = us_showin(which, ni->geom, ni->parent, el_matid, LAYERA, 1);
479 					} else
480 					{
481 						makerot(ni, rottrans);
482 						ret = us_showin(which, ni->geom, ni->parent, rottrans, LAYERA, 1);
483 					}
484 					if (ret < 0) stopped = 1; else
485 						if (ret & 2) us_queueopaque(ni->geom, FALSE);
486 				} else
487 				{
488 					for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
489 					{
490 						if ((w->state&WINDOWTYPE) != DISPWINDOW &&
491 							(w->state&WINDOWTYPE) != DISP3DWINDOW) continue;
492 						if (ni->rotation == 0 && ni->transpose == 0)
493 						{
494 							ret = us_showin(w, ni->geom, ni->parent, el_matid, LAYERA, 1);
495 						} else
496 						{
497 							makerot(ni, rottrans);
498 							ret = us_showin(w, ni->geom, ni->parent, rottrans, LAYERA, 1);
499 						}
500 						if (ret < 0) { stopped = 1;   break; }
501 						if (ret & 2) us_queueopaque(ni->geom, FALSE);
502 					}
503 				}
504 				ni->userbits |= RETDONN;
505 			}
506 		} else
507 		{
508 			ai = r->entryaddr.ai;
509 			if ((ai->userbits & (RETDONA|DEADA)) == 0)
510 			{
511 				if (which != NOWINDOWPART)
512 				{
513 					ret = us_showin(which, ai->geom, ai->parent, el_matid, LAYERA, 1);
514 					if (ret < 0) stopped = 1; else
515 						if (ret & 2) us_queueopaque(ai->geom, FALSE);
516 				} else
517 				{
518 					for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
519 					{
520 						if ((w->state&WINDOWTYPE) != DISPWINDOW &&
521 							(w->state&WINDOWTYPE) != DISP3DWINDOW) continue;
522 						ret = us_showin(w, ai->geom, ai->parent, el_matid, LAYERA, 1);
523 						if (ret < 0) { stopped = 1;   break; }
524 						if (ret & 2) us_queueopaque(ai->geom, FALSE);
525 					}
526 				}
527 				ai->userbits |= RETDONA;
528 			}
529 		}
530 	}
531 	us_firstredraw = NOREDRAW;
532 
533 	/* now re-draw the opaque objects */
534 	for(r = us_firstopaque; r != NOREDRAW; r = nextr)
535 	{
536 		/* remember the next redraw module and queue this one for deletion */
537 		nextr = r->nextredraw;
538 		r->nextredraw = us_usedredraw;
539 		us_usedredraw = r;
540 
541 		if (stopped != 0) continue;
542 		if (r->entryisnode)
543 		{
544 			ni = r->entryaddr.ni;
545 			if ((ni->userbits & (REWANTN|REODONN|DEADN)) == REWANTN)
546 			{
547 				if (which != NOWINDOWPART)
548 				{
549 					if (ni->rotation == 0 && ni->transpose == 0)
550 					{
551 						ret = us_showin(which, ni->geom, ni->parent, el_matid, LAYERA, 2);
552 					} else
553 					{
554 						makerot(ni, rottrans);
555 						ret = us_showin(which, ni->geom, ni->parent, rottrans, LAYERA, 2);
556 					}
557 					if (ret < 0) stopped = 1;
558 				} else
559 				{
560 					for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
561 					{
562 						if ((w->state&WINDOWTYPE) != DISPWINDOW &&
563 							(w->state&WINDOWTYPE) != DISP3DWINDOW) continue;
564 						if (ni->rotation == 0 && ni->transpose == 0)
565 						{
566 							ret = us_showin(w, ni->geom, ni->parent, el_matid, LAYERA, 2);
567 						} else
568 						{
569 							makerot(ni, rottrans);
570 							ret = us_showin(w, ni->geom, ni->parent, rottrans, LAYERA, 2);
571 						}
572 						if (ret < 0) { stopped = 1;   break; }
573 					}
574 				}
575 				ni->userbits |= REODONN;
576 			}
577 		} else
578 		{
579 			ai = r->entryaddr.ai;
580 			if ((ai->userbits & (REODONA|DEADA)) == 0)
581 			{
582 				if (which != NOWINDOWPART)
583 				{
584 					ret = us_showin(which, ai->geom, ai->parent, el_matid, LAYERA, 2);
585 					if (ret < 0) stopped = 1;
586 				} else
587 				{
588 					for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
589 					{
590 						if ((w->state&WINDOWTYPE) != DISPWINDOW &&
591 							(w->state&WINDOWTYPE) != DISP3DWINDOW) continue;
592 						ret = us_showin(w, ai->geom, ai->parent, el_matid, LAYERA, 2);
593 						if (ret < 0) { stopped = 1;   break; }
594 					}
595 				}
596 				ai->userbits |= REODONA;
597 			}
598 		}
599 	}
600 
601 	us_firstopaque = NOREDRAW;
602 	us_lastopaque = NOREDRAW;
603 
604 	/* now free up all of the redraw modules */
605 	for(r = us_usedredraw; r != NOREDRAW; r = nextr)
606 	{
607 		nextr = r->nextredraw;
608 		if (r->entryisnode)
609 		{
610 			ni = r->entryaddr.ni;
611 			ni->userbits &= ~(REWANTN|RELOCLN);
612 		} else
613 		{
614 			ai = r->entryaddr.ai;
615 			ai->userbits &= ~(REWANTA|RELOCLA);
616 		}
617 		us_freeredraw(r);
618 	}
619 	us_usedredraw = NOREDRAW;
620 
621 	/* now re-draw the exports */
622 	for(r = us_firstexport; r != NOREDRAW; r = nextr)
623 	{
624 		/* remember the next redraw module and queue this one for deletion */
625 		nextr = r->nextredraw;
626 		pp = r->entryaddr.pp;
627 		us_freeredraw(r);
628 		if (stopped != 0) continue;
629 		makerot(pp->subnodeinst, rottrans);
630 
631 		if (which != NOWINDOWPART)
632 		{
633 			us_writeprotoname(pp, LAYERA, rottrans, LAYERO, el_colcelltxt, which, 0, 0, 0, 0,
634 				(us_useroptions&EXPORTLABELS)>>EXPORTLABELSSH);
635 			us_drawportprotovariables(pp, LAYERA, rottrans, which, FALSE);
636 		} else
637 		{
638 			for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
639 			{
640 				if ((w->state&WINDOWTYPE) != DISPWINDOW &&
641 					(w->state&WINDOWTYPE) != DISP3DWINDOW) continue;
642 				if (w->curnodeproto != pp->parent) continue;
643 				us_writeprotoname(pp, LAYERA, rottrans, LAYERO, el_colcelltxt, w, 0, 0, 0, 0,
644 					(us_useroptions&EXPORTLABELS)>>EXPORTLABELSSH);
645 				us_drawportprotovariables(pp, LAYERA, rottrans, w, FALSE);
646 			}
647 		}
648 	}
649 	us_firstexport = NOREDRAW;
650 
651 	/* force all changes out */
652 	flushscreen();
653 }
654 
655 /*
656  * routine to make a change to the screen (sandwiched between a "us_beginchanges"
657  * and an "us_endchanges" call.  Returns an indicator of what else needs to be
658  * drawn (negative to stop display).
659  */
us_showin(WINDOWPART * w,GEOM * object,NODEPROTO * parnt,XARRAY trans,INTBIG on,INTBIG layers)660 INTBIG us_showin(WINDOWPART *w, GEOM *object, NODEPROTO *parnt, XARRAY trans, INTBIG on,
661 	INTBIG layers)
662 {
663 	REGISTER NODEINST *ni;
664 	XARRAY localtran, subrot, subtran;
665 	REGISTER WINDOWPART *oldwin;
666 	REGISTER INTBIG moretodo, res;
667 	REGISTER INTBIG objlocal;
668 
669 	/* if the parent is the current cell in the window, draw the instance */
670 	moretodo = 0;
671 	oldwin = setvariablewindow(w);
672 	if (parnt == w->curnodeproto)
673 	{
674 		if (object->entryisnode)
675 		{
676 			ni = object->entryaddr.ni;
677 			begintraversehierarchy();
678 			res = us_drawnodeinst(ni, on, trans, layers, w);
679 			endtraversehierarchy();
680 			if (res == -2)
681 			{
682 				(void)setvariablewindow(oldwin);
683 				return(-1);
684 			}
685 			if (res < 0) res = 0;
686 			moretodo |= res;
687 		} else
688 		{
689 			res = us_drawarcinst(object->entryaddr.ai, on, trans, layers, w);
690 			if (res < 0)
691 			{
692 				(void)setvariablewindow(oldwin);
693 				return(-1);
694 			}
695 			moretodo |= res;
696 		}
697 
698 		/* if drawing the opaque layer, don't look for other things to draw */
699 		if (on && layers == 2)
700 		{
701 			(void)setvariablewindow(oldwin);
702 			return(moretodo);
703 		}
704 
705 		/* queue re-drawing of objects in vicinity of this one */
706 		if (object->entryisnode)
707 			us_queuevicinity(w, object, parnt, trans, 0, 0, 0, 0, on); else
708 				us_queuevicinity(w, object, parnt, trans, 0, 0, 0, 0, on);
709 		(void)setvariablewindow(oldwin);
710 		return(moretodo);
711 	}
712 
713 	/* look at all instances of the parent cell that are expanded */
714 	if (object->entryisnode)
715 		objlocal = object->entryaddr.ni->userbits & RELOCLN; else
716 			objlocal = object->entryaddr.ai->userbits & RELOCLA;
717 
718 	/* Steve Holmlund of Factron suggested this next statement */
719 	if (on == 0) objlocal = 0;
720 
721 	for(ni = parnt->firstinst; ni != NONODEINST; ni = ni->nextinst)
722 	{
723 		if ((ni->userbits & NEXPAND) == 0) continue;
724 		if (objlocal != 0 && (ni->userbits&(RELOCLN|REWANTN)) == 0) continue;
725 
726 		/* transform nodeinst to outer instance */
727 		maketrans(ni, localtran);
728 		transmult(trans, localtran, subrot);
729 		if (ni->rotation == 0 && ni->transpose == 0)
730 		{
731 			res = us_showin(w, object, ni->parent, subrot, on, layers);
732 			if (res < 0)
733 			{
734 				(void)setvariablewindow(oldwin);
735 				return(-1);
736 			}
737 			moretodo |= res;
738 		} else
739 		{
740 			makerot(ni, localtran);
741 			transmult(subrot, localtran, subtran);
742 			res = us_showin(w, object, ni->parent, subtran, on, layers);
743 			if (res < 0)
744 			{
745 				(void)setvariablewindow(oldwin);
746 				return(-1);
747 			}
748 			moretodo |= res;
749 		}
750 	}
751 	(void)setvariablewindow(oldwin);
752 	return(moretodo);
753 }
754 
755 /*
756  * routine to queue object "p" to be re-drawn.  If "local" is true, only
757  * draw the local instance of this object, not every instance.
758  */
us_queueredraw(GEOM * p,BOOLEAN local)759 void us_queueredraw(GEOM *p, BOOLEAN local)
760 {
761 	REGISTER REDRAW *r;
762 	REGISTER NODEINST *ni;
763 	REGISTER ARCINST *ai;
764 
765 	if (p->entryisnode)
766 	{
767 		ni = p->entryaddr.ni;
768 		ni->userbits = (ni->userbits & ~(RETDONN|REODONN|RELOCLN)) | REWANTN;
769 		if (local) ni->userbits |= RELOCLN;
770 	} else
771 	{
772 		ai = p->entryaddr.ai;
773 		ai->userbits = (ai->userbits & ~(RETDONA|REODONA|RELOCLA)) | REWANTA;
774 		if (local) ai->userbits |= RELOCLA;
775 	}
776 
777 	/* queue this object for being re-drawn */
778 	r = us_allocredraw();
779 	if (r == NOREDRAW)
780 	{
781 		ttyputnomemory();
782 		return;
783 	}
784 	r->entryisnode = p->entryisnode;
785 	r->entryaddr.blind = p->entryaddr.blind;
786 	r->nextredraw = us_firstredraw;
787 	us_firstredraw = r;
788 }
789 
790 /*
791  * Routine to remove all redraw objects that are in library "lib"
792  * (because the library has been deleted)
793  */
us_unqueueredraw(LIBRARY * lib)794 void us_unqueueredraw(LIBRARY *lib)
795 {
796 	REGISTER REDRAW *r, *nextr, *lastr;
797 	REGISTER LIBRARY *rlib;
798 
799 	lastr = NOREDRAW;
800 	for(r = us_firstredraw; r != NOREDRAW; r = nextr)
801 	{
802 		nextr = r->nextredraw;
803 		if (r->entryisnode) rlib = r->entryaddr.ni->parent->lib; else
804 			rlib = r->entryaddr.ai->parent->lib;
805 		if (rlib == lib)
806 		{
807 			if (lastr == NOREDRAW) us_firstredraw = nextr; else
808 				lastr->nextredraw = nextr;
809 			us_freeredraw(r);
810 			continue;
811 		}
812 		lastr = r;
813 	}
814 }
815 
816 /*
817  * routine to erase the display of "geom" in all windows (sandwiched between
818  * "us_beginchanges" and "us_endchanges" calls)
819  */
us_undisplayobject(GEOM * geom)820 void us_undisplayobject(GEOM *geom)
821 {
822 	REGISTER NODEINST *ni;
823 	REGISTER ARCINST *ai;
824 	REGISTER WINDOWPART *w;
825 	XARRAY rottrans;
826 
827 	if (geom->entryisnode)
828 	{
829 		ni = geom->entryaddr.ni;
830 		for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
831 		{
832 			if ((w->state&WINDOWTYPE) != DISPWINDOW &&
833 				(w->state&WINDOWTYPE) != DISP3DWINDOW) continue;
834 			if (ni->rotation == 0 && ni->transpose == 0)
835 			{
836 				(void)us_showin(w, geom, ni->parent, el_matid, LAYERN, 3);
837 			} else
838 			{
839 				makerot(ni, rottrans);
840 				(void)us_showin(w, geom, ni->parent, rottrans, LAYERN, 3);
841 			}
842 		}
843 	} else
844 	{
845 		ai = geom->entryaddr.ai;
846 		for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
847 		{
848 			if ((w->state&WINDOWTYPE) != DISPWINDOW &&
849 				(w->state&WINDOWTYPE) != DISP3DWINDOW) continue;
850 			(void)us_showin(w, geom, ai->parent, el_matid, LAYERN, 3);
851 		}
852 	}
853 }
854 
855 /*
856  * routine to queue object "p" to be re-drawn.
857  */
us_queueexport(PORTPROTO * pp)858 void us_queueexport(PORTPROTO *pp)
859 {
860 	REGISTER REDRAW *r;
861 
862 	/* queue this object for being re-drawn */
863 	r = us_allocredraw();
864 	if (r == NOREDRAW)
865 	{
866 		ttyputnomemory();
867 		return;
868 	}
869 	r->entryaddr.pp = pp;
870 	r->nextredraw = us_firstexport;
871 	us_firstexport = r;
872 }
873 
874 /******************** HELPER ROUTINES ******************/
875 
876 /*
877  * routine to queue the opaque layers of object "p" to be re-drawn.
878  * If "local" is true, only draw the local instance of this object, not
879  * every instance.
880  */
us_queueopaque(GEOM * p,BOOLEAN local)881 void us_queueopaque(GEOM *p, BOOLEAN local)
882 {
883 	REGISTER REDRAW *r;
884 	REGISTER NODEINST *ni;
885 	REGISTER NODEPROTO *np;
886 	REGISTER ARCINST *ai;
887 	REGISTER TECHNOLOGY *tech;
888 	REGISTER INTBIG i;
889 
890 	if (p->entryisnode)
891 	{
892 		ni = p->entryaddr.ni;
893 
894 		/* count displayable variables on this node */
895 		for(i = 0; i < ni->numvar; i++)
896 			if ((ni->firstvar[i].type&VDISPLAY) != 0) break;
897 
898 		/* if this nodeinst has nothing on the opaque layer, don't queue it */
899 		np = ni->proto;
900 		if (np->primindex != 0 && (np->userbits&NHASOPA) == 0 &&
901 			i >= ni->numvar && ni->firstportexpinst == NOPORTEXPINST) return;
902 
903 		/* set the nodeinst bits for opaque redraw */
904 		ni->userbits = (ni->userbits & ~REODONN) | REWANTN;
905 		if (local) ni->userbits |= RELOCLN;
906 		tech = np->tech;
907 	} else
908 	{
909 		ai = p->entryaddr.ai;
910 
911 		/* count displayable variables on this arc */
912 		for(i = 0; i < ai->numvar; i++)
913 			if ((ai->firstvar[i].type&VDISPLAY) != 0) break;
914 
915 		/* if this arcinst has nothing on the opaque layer, don't queue it */
916 		if ((ai->proto->userbits&AHASOPA) == 0 && i >= ai->numvar) return;
917 
918 		/* set the arcinst bits for opaque redraw */
919 		ai->userbits = (ai->userbits & ~REODONA) | REWANTA;
920 		if (local) ai->userbits |= RELOCLA;
921 		tech = ai->proto->tech;
922 	}
923 
924 	/* queue the object for opaque redraw */
925 	r = us_allocredraw();
926 	if (r == NOREDRAW)
927 	{
928 		ttyputnomemory();
929 		return;
930 	}
931 	r->entryisnode = p->entryisnode;
932 	r->entryaddr.blind = p->entryaddr.blind;
933 	if (tech == el_curtech)
934 	{
935 		/* in the current technology: queue redraw of this stuff first */
936 		r->nextredraw = us_firstopaque;
937 		us_firstopaque = r;
938 		if (us_lastopaque == NOREDRAW) us_lastopaque = r;
939 	} else
940 	{
941 		/* in some other technology: queue redraw of this stuff last */
942 		if (us_lastopaque != NOREDRAW) us_lastopaque->nextredraw = r;
943 		r->nextredraw = NOREDRAW;
944 		us_lastopaque = r;
945 		if (us_firstopaque == NOREDRAW) us_firstopaque = r;
946 	}
947 }
948 
949 /*
950  * routine to allocate a new redraw from the pool (if any) or memory
951  * routine returns NOREDRAW upon error
952  */
us_allocredraw(void)953 REDRAW *us_allocredraw(void)
954 {
955 	REGISTER REDRAW *r;
956 
957 	if (us_redrawfree == NOREDRAW)
958 	{
959 		r = (REDRAW *)emalloc(sizeof (REDRAW), us_tool->cluster);
960 		if (r == 0) return(NOREDRAW);
961 	} else
962 	{
963 		/* take module from free list */
964 		r = us_redrawfree;
965 		us_redrawfree = r->nextredraw;
966 	}
967 	return(r);
968 }
969 
970 /*
971  * routine to return redraw module "r" to the pool of free modules
972  */
us_freeredraw(REDRAW * r)973 void us_freeredraw(REDRAW *r)
974 {
975 	r->nextredraw = us_redrawfree;
976 	us_redrawfree = r;
977 }
978 
979 /*
980  * routine to queue for local re-draw anything in cell "parnt" that is
981  * in the vicinity of "object" with bounding box (lx-hx, ly-hy).  The objects
982  * are to be drawn on if "on" is nonzero.  The transformation matrix
983  * between the bounding box and the cell is in "trans".
984  */
us_queuevicinity(WINDOWPART * w,GEOM * object,NODEPROTO * parnt,XARRAY trans,INTBIG lx,INTBIG hx,INTBIG ly,INTBIG hy,INTBIG on)985 void us_queuevicinity(WINDOWPART *w, GEOM *object, NODEPROTO *parnt, XARRAY trans, INTBIG lx,
986 	INTBIG hx, INTBIG ly, INTBIG hy, INTBIG on)
987 {
988 	REGISTER GEOM *look;
989 	REGISTER NODEINST *ni;
990 	REGISTER ARCINST *ai;
991 	INTBIG bx, ux, by, uy, olx, ohx, oly, ohy;
992 	REGISTER BOOLEAN local;
993 	REGISTER INTBIG i, search, slop;
994 
995 	/* compute full extent of the object, including text */
996 	if (object != NOGEOM)
997 	{
998 		if (object->entryisnode)
999 		{
1000 			us_getnodebounds(object->entryaddr.ni, &lx, &hx, &ly, &hy);
1001 		} else
1002 		{
1003 			us_getarcbounds(object->entryaddr.ai, &lx, &hx, &ly, &hy);
1004 		}
1005 	}
1006 
1007 	xform(lx, ly, &bx, &by, trans);
1008 	xform(hx, hy, &ux, &uy, trans);
1009 	if (bx > ux) { i = bx;  bx = ux;  ux = i; }
1010 	if (by > uy) { i = by;  by = uy;  uy = i; }
1011 
1012 	/* extend by 1 screen pixel */
1013 	i = roundfloat(3.0f / w->scalex);
1014 	if (i <= 0) i = 1;
1015 	bx -= i;   hx += i;
1016 	by -= i;   hy += i;
1017 
1018 	/* clip search to visible area of window */
1019 	if (bx > w->screenhx || ux < w->screenlx ||
1020 		by > w->screenhy || uy < w->screenly) return;
1021 	if (bx < w->screenlx) bx = w->screenlx;
1022 	if (ux > w->screenhx) ux = w->screenhx;
1023 	if (by < w->screenly) by = w->screenly;
1024 	if (uy > w->screenhy) uy = w->screenhy;
1025 
1026 	slop = FARTEXTLIMIT * lambdaofcell(parnt);
1027 	search = initsearch(bx-slop, ux+slop, by-slop, uy+slop, parnt);
1028 	if (object == NOGEOM) local = TRUE; else local = FALSE;
1029 	for(;;)
1030 	{
1031 		if ((look = nextobject(search)) == NOGEOM) break;
1032 
1033 		/* don't re-draw the current object whose vicinity is being checked */
1034 		if (look == object) continue;
1035 
1036 		/* see if the object really intersects the redraw area */
1037 		if (look->entryisnode)
1038 			us_getnodebounds(look->entryaddr.ni, &olx, &ohx, &oly, &ohy); else
1039 				us_getarcbounds(look->entryaddr.ai, &olx, &ohx, &oly, &ohy);
1040 		if (olx > ux || ohx < bx || oly > uy || ohy < by) continue;
1041 
1042 		/* redraw all if object is going off, redraw opaque if going on */
1043 		if (on == LAYERN) us_queueredraw(look, local); else
1044 			us_queueopaque(look, local);
1045 	}
1046 
1047 	/* now queue things with "far text" */
1048 	for(ni = parnt->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
1049 	{
1050 		if ((ni->userbits&NHASFARTEXT) == 0) continue;
1051 		us_getnodebounds(ni, &olx, &ohx, &oly, &ohy);
1052 		if (olx >= ux || ohx <= bx || oly >= uy || ohy <= by) continue;
1053 		if (on == LAYERN) us_queueredraw(ni->geom, local); else
1054 			us_queueopaque(ni->geom, local);
1055 	}
1056 	for(ai = parnt->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
1057 	{
1058 		if ((ai->userbits&AHASFARTEXT) == 0) continue;
1059 		us_getarcbounds(ai, &olx, &ohx, &oly, &ohy);
1060 		if (olx >= ux || ohx <= bx || oly >= uy || ohy <= by) continue;
1061 		if (on == LAYERN) us_queueredraw(ai->geom, local); else
1062 			us_queueopaque(ai->geom, local);
1063 	}
1064 }
1065 
1066 /*
1067  * null routine for polygon display
1068  */
us_nulldisplayroutine(POLYGON * obj,WINDOWPART * w)1069 void us_nulldisplayroutine(POLYGON *obj, WINDOWPART *w)
1070 {
1071 }
1072 
1073 /*
1074  * routine to write polygon "obj" in window "w".
1075  */
us_showpoly(POLYGON * obj,WINDOWPART * w)1076 void us_showpoly(POLYGON *obj, WINDOWPART *w)
1077 {
1078 	REGISTER INTBIG i, cx, cy, rad;
1079 	REGISTER INTBIG pre, tx, ty;
1080 	INTBIG lx, ux, ly, uy, six, siy;
1081 	WINDOWPART wsc;
1082 	REGISTER GRAPHICS *gra;
1083 	static POLYGON *objc = NOPOLYGON;
1084 	INTBIG tsx, tsy;
1085 
1086 	if (us_3dgatheringpolys)
1087 	{
1088 		us_3dshowpoly(obj, w);
1089 		return;
1090 	}
1091 
1092 	/* quit if no bits to be written */
1093 	if (obj->desc->bits == LAYERN) return;
1094 
1095 	/* special case for grid display */
1096 	if (obj->style == GRIDDOTS)
1097 	{
1098 		screendrawgrid(w, obj);
1099 		return;
1100 	}
1101 
1102 	/* transform to space of this window */
1103 	if ((w->state&INPLACEEDIT) != 0) xformpoly(obj, w->outofcell);
1104 
1105 	/* now draw the polygon */
1106 	gra = obj->desc;
1107 	switch (obj->style)
1108 	{
1109 		case FILLED:		/* filled polygon */
1110 		case FILLEDRECT:	/* filled rectangle */
1111 			if (isbox(obj, &lx, &ux, &ly, &uy))
1112 			{
1113 				/* simple rectangular box: transform, clip, and draw */
1114 				if (us_makescreen(&lx, &ly, &ux, &uy, w)) break;
1115 				screendrawbox(w, lx, ux, ly, uy, gra);
1116 
1117 				/* for patterned and outlined rectangles, draw the box too */
1118 				if ((gra->colstyle&(NATURE|OUTLINEPAT)) == (PATTERNED|OUTLINEPAT))
1119 				{
1120 					screendrawline(w, lx, ly, lx, uy, gra, 0);
1121 					screendrawline(w, lx, uy, ux, uy, gra, 0);
1122 					screendrawline(w, ux, uy, ux, ly, gra, 0);
1123 					screendrawline(w, ux, ly, lx, ly, gra, 0);
1124 				}
1125 				break;
1126 			}
1127 
1128 			/* code cannot be called by multiple procesors: uses globals */
1129 			NOT_REENTRANT;
1130 
1131 			/* copy the polygon since it will be mangled when clipped */
1132 			(void)needstaticpolygon(&objc, obj->count, us_tool->cluster);
1133 			objc->count = obj->count;
1134 			objc->style = obj->style;
1135 			objc->desc = gra;
1136 			for(i=0; i<obj->count; i++)
1137 			{
1138 				objc->xv[i] = applyxscale(w, obj->xv[i]-w->screenlx) + w->uselx;
1139 				objc->yv[i] = applyyscale(w, obj->yv[i]-w->screenly) + w->usely;
1140 			}
1141 
1142 			/* clip and draw the polygon */
1143 			clippoly(objc, w->uselx, w->usehx, w->usely, w->usehy);
1144 			if (objc->count <= 1) break;
1145 			if (objc->count > 2)
1146 			{
1147 				/* always clockwise */
1148 				if (areapoly(objc) < 0.0) reversepoly(objc);
1149 				screendrawpolygon(w, objc->xv, objc->yv, objc->count, objc->desc);
1150 
1151 				/* for patterned and outlined polygons, draw the outline too */
1152 				if ((gra->colstyle&(NATURE|OUTLINEPAT)) == (PATTERNED|OUTLINEPAT))
1153 				{
1154 					for(i=0; i<objc->count; i++)
1155 					{
1156 						if (i == 0) pre = objc->count-1; else pre = i-1;
1157 						screendrawline(w, objc->xv[pre], objc->yv[pre], objc->xv[i], objc->yv[i],
1158 							objc->desc, 0);
1159 					}
1160 				}
1161 			} else screendrawline(w, objc->xv[0], objc->yv[0], objc->xv[1], objc->yv[1],
1162 				gra, 0);
1163 			break;
1164 
1165 		case CLOSEDRECT:		/* closed rectangle outline */
1166 			us_wanttodraw(obj->xv[0], obj->yv[0], obj->xv[0], obj->yv[1], w, gra, 0);
1167 			us_wanttodraw(obj->xv[0], obj->yv[1], obj->xv[1], obj->yv[1], w, gra, 0);
1168 			us_wanttodraw(obj->xv[1], obj->yv[1], obj->xv[1], obj->yv[0], w, gra, 0);
1169 			us_wanttodraw(obj->xv[1], obj->yv[0], obj->xv[0], obj->yv[0], w, gra, 0);
1170 			break;
1171 
1172 		case CROSSED:		/* polygon outline with cross */
1173 			us_wanttodraw(obj->xv[0], obj->yv[0], obj->xv[2], obj->yv[2], w, gra, 0);
1174 			us_wanttodraw(obj->xv[1], obj->yv[1], obj->xv[3], obj->yv[3], w, gra, 0);
1175 			/* FALLTHROUGH */
1176 
1177 		case CLOSED:		/* closed polygon outline */
1178 			for(i=0; i<obj->count; i++)
1179 			{
1180 				if (i == 0) pre = obj->count-1; else pre = i-1;
1181 				us_wanttodraw(obj->xv[pre], obj->yv[pre], obj->xv[i], obj->yv[i], w, gra, 0);
1182 			}
1183 			break;
1184 
1185 		case OPENED:		/* opened polygon outline */
1186 			for(i=1; i<obj->count; i++)
1187 				us_wanttodraw(obj->xv[i-1], obj->yv[i-1], obj->xv[i], obj->yv[i], w, gra, 0);
1188 			break;
1189 
1190 		case OPENEDT1:		/* opened polygon outline, dotted */
1191 			for(i=1; i<obj->count; i++)
1192 				us_wanttodraw(obj->xv[i-1], obj->yv[i-1], obj->xv[i], obj->yv[i], w, gra, 1);
1193 			break;
1194 
1195 		case OPENEDT2:		/* opened polygon outline, dashed */
1196 			for(i=1; i<obj->count; i++)
1197 				us_wanttodraw(obj->xv[i-1], obj->yv[i-1], obj->xv[i], obj->yv[i], w, gra, 2);
1198 			break;
1199 
1200 		case OPENEDT3:		/* opened polygon outline, thicker */
1201 			for(i=1; i<obj->count; i++)
1202 				us_wanttodraw(obj->xv[i-1], obj->yv[i-1], obj->xv[i], obj->yv[i], w, gra, 3);
1203 			break;
1204 
1205 		case OPENEDO1:		/* extended opened polygon outline */
1206 			us_drawextendedpolyline(obj, w);
1207 			break;
1208 
1209 		case VECTORS:		/* many lines */
1210 			if (obj->count % 2 != 0)
1211 				ttyputmsg(_("Cannot display vector with %ld vertices (must be even)"),
1212 					obj->count);
1213 			for(i=0; i<obj->count; i += 2)
1214 				us_wanttodraw(obj->xv[i], obj->yv[i], obj->xv[i+1], obj->yv[i+1], w, gra, 0);
1215 			break;
1216 
1217 		case CROSS:		/* crosses (always have one point) */
1218 		case BIGCROSS:
1219 			getcenter(obj, &six, &siy);
1220 			if (six < w->screenlx || six > w->screenhx || siy < w->screenly || siy > w->screenhy)
1221 				break;
1222 			if (obj->style == CROSS) i = CROSSSIZE; else i = BIGCROSSSIZE;
1223 			us_wanttodrawo(six, -i, siy, 0, six, i, siy, 0, w, gra, 0);
1224 			us_wanttodrawo(six, 0, siy, -i, six, 0, siy, i, w, gra, 0);
1225 			break;
1226 
1227 		case TEXTCENT:		/* text centered in box */
1228 		case TEXTTOP:		/* text below top of box */
1229 		case TEXTBOT:		/* text above bottom of box */
1230 		case TEXTLEFT:		/* text right of left edge of box */
1231 		case TEXTRIGHT:		/* text left of right edge of box */
1232 		case TEXTTOPLEFT:	/* text to lower-right of upper-left corner */
1233 		case TEXTBOTLEFT:	/* text to upper-right of lower-left corner */
1234 		case TEXTTOPRIGHT:	/* text to lower-left of upper-right corner */
1235 		case TEXTBOTRIGHT:	/* text to upper-left of lower-right corner */
1236 			getbbox(obj, &lx, &ux, &ly, &uy);
1237 			lx = applyxscale(w, lx-w->screenlx) + w->uselx;
1238 			ly = applyyscale(w, ly-w->screenly) + w->usely;
1239 			ux = applyxscale(w, ux-w->screenlx) + w->uselx;
1240 			uy = applyyscale(w, uy-w->screenly) + w->usely;
1241 			screensettextinfo(w, obj->tech, obj->textdescript);
1242 			screengettextsize(w, obj->string, &tsx, &tsy);
1243 			switch (obj->style)
1244 			{
1245 				case TEXTCENT:
1246 					tx = (lx+ux-tsx) / 2;
1247 					ty = (ly+uy-tsy) / 2;
1248 					break;
1249 				case TEXTTOP:
1250 					tx = (lx+ux-tsx) / 2;
1251 					ty = uy-tsy;
1252 					break;
1253 				case TEXTBOT:
1254 					tx = (lx+ux-tsx) / 2;
1255 					ty = ly;
1256 					break;
1257 				case TEXTLEFT:
1258 					tx = lx;
1259 					ty = (ly+uy-tsy) / 2;
1260 					break;
1261 				case TEXTRIGHT:
1262 					tx = ux-tsx;
1263 					ty = (ly+uy-tsy) / 2;
1264 					break;
1265 				case TEXTTOPLEFT:
1266 					tx = lx;
1267 					ty = uy-tsy;
1268 					break;
1269 				case TEXTBOTLEFT:
1270 					tx = lx;
1271 					ty = ly;
1272 					break;
1273 				case TEXTTOPRIGHT:
1274 					tx = ux-tsx;
1275 					ty = uy-tsy;
1276 					break;
1277 				case TEXTBOTRIGHT:
1278 					tx = ux-tsx;
1279 					ty = ly;
1280 					break;
1281 			}
1282 			if (tx > w->usehx || tx+tsx < w->uselx ||
1283 				ty > w->usehy || ty+tsy < w->usely) break;
1284 			screendrawtext(w, tx, ty, obj->string, gra);
1285 			break;
1286 
1287 		case TEXTBOX:		/* text centered and contained in box */
1288 			getbbox(obj, &lx, &ux, &ly, &uy);
1289 			if (us_makescreen(&lx, &ly, &ux, &uy, w)) break;
1290 			us_writetext(lx, ux, ly, uy, gra, obj->string, obj->textdescript, w, obj->tech);
1291 			break;
1292 
1293 		case CIRCLE:    case THICKCIRCLE:
1294 		case CIRCLEARC: case THICKCIRCLEARC:
1295 			/* must scale the window for best precision when drawing curves */
1296 			wsc.screenlx = w->screenlx;
1297 			wsc.screenly = w->screenly;
1298 			wsc.screenhx = w->screenhx;
1299 			wsc.screenhy = w->screenhy;
1300 			wsc.uselx = ZUP(w->uselx);
1301 			wsc.usely = ZUP(w->usely);
1302 			wsc.usehx = ZUP(w->usehx);
1303 			wsc.usehy = ZUP(w->usehy);
1304 			computewindowscale(&wsc);
1305 
1306 			/* code cannot be called by multiple procesors: uses globals */
1307 			NOT_REENTRANT;
1308 
1309 			/* get copy polygon */
1310 			(void)needstaticpolygon(&objc, obj->count, us_tool->cluster);
1311 
1312 			/* transform and copy the polygon */
1313 			objc->count = obj->count;
1314 			objc->style = obj->style;
1315 			for(i=0; i<obj->count; i++)
1316 			{
1317 				objc->xv[i] = applyxscale(&wsc, obj->xv[i]-wsc.screenlx) + wsc.uselx;
1318 				objc->yv[i] = applyyscale(&wsc, obj->yv[i]-wsc.screenly) + wsc.usely;
1319 			}
1320 
1321 			/* clip the circle */
1322 			cliparc(objc, wsc.uselx, wsc.usehx, wsc.usely, wsc.usehy);
1323 
1324 			/* circle outline at [0] radius to [1] */
1325 			switch (objc->style)
1326 			{
1327 				case CIRCLE:
1328 					if (objc->count != 2) break;
1329 					six = applyxscale(w, obj->xv[1]-obj->xv[0]);
1330 					siy = applyxscale(w, obj->yv[1]-obj->yv[0]);
1331 					rad = computedistance(0, 0, six, siy);
1332 					screendrawcircle(w, ZDN(objc->xv[0]), ZDN(objc->yv[0]), rad, gra);
1333 					break;
1334 				case THICKCIRCLE:
1335 					if (objc->count != 2) break;
1336 					six = applyxscale(w, obj->xv[1]-obj->xv[0]);
1337 					siy = applyxscale(w, obj->yv[1]-obj->yv[0]);
1338 					rad = computedistance(0, 0, six, siy);
1339 					screendrawthickcircle(w, ZDN(objc->xv[0]), ZDN(objc->yv[0]), rad, gra);
1340 					break;
1341 				case CIRCLEARC:
1342 					/* thin arcs at [i] points [1+i] [2+i] clockwise */
1343 					if (objc->count == 0) break;
1344 					if ((objc->count%3) != 0) break;
1345 					for (i=0; i<objc->count; i += 3)
1346 						screendrawcirclearc(w, ZDN(objc->xv[i]), ZDN(objc->yv[i]), ZDN(objc->xv[i+1]),
1347 							ZDN(objc->yv[1+i]), ZDN(objc->xv[i+2]), ZDN(objc->yv[i+2]), gra);
1348 					break;
1349 				case THICKCIRCLEARC:
1350 					/* thick arcs at [i] points [1+i] [2+i] clockwise */
1351 					if (objc->count == 0) break;
1352 					if ((objc->count%3) != 0) break;
1353 					for (i=0; i<objc->count; i += 3)
1354 						screendrawthickcirclearc(w, ZDN(objc->xv[i]), ZDN(objc->yv[i]), ZDN(objc->xv[i+1]),
1355 							ZDN(objc->yv[1+i]), ZDN(objc->xv[i+2]), ZDN(objc->yv[i+2]), gra);
1356 			}
1357 			break;
1358 
1359 		case DISC:
1360 			/* filled circle at [0] radius to [1] */
1361 			if (obj->count != 2) break;
1362 			cx = applyxscale(w, obj->xv[0]-w->screenlx) + w->uselx;
1363 			cy = applyyscale(w, obj->yv[0]-w->screenly) + w->usely;
1364 			six = applyxscale(w, obj->xv[1]-obj->xv[0]);
1365 			siy = applyxscale(w, obj->yv[1]-obj->yv[0]);
1366 			rad = computedistance(0, 0, six, siy);
1367 			if (rad == 0) break;
1368 
1369 			/* clip if completely off screen */
1370 			if (cx + rad < w->uselx || cx - rad > w->usehx ||
1371 				cy + rad < w->usely || cy - rad > w->usehy)
1372 					break;
1373 			screendrawdisc(w, cx, cy, rad, gra);
1374 			break;
1375 	}
1376 
1377 	/* transform from space of this window */
1378 	if ((w->state&INPLACEEDIT) != 0) xformpoly(obj, w->intocell);
1379 }
1380 
1381 /*
1382  * routine to clip and possibly draw a line from (fx,fy) to (tx,ty) in
1383  * window "w" with description "desc", texture "texture"
1384  */
us_wanttodraw(INTBIG fx,INTBIG fy,INTBIG tx,INTBIG ty,WINDOWPART * w,GRAPHICS * desc,INTBIG texture)1385 void us_wanttodraw(INTBIG fx, INTBIG fy, INTBIG tx, INTBIG ty, WINDOWPART *w, GRAPHICS *desc,
1386 	INTBIG texture)
1387 {
1388 	fx = applyxscale(w, fx-w->screenlx) + w->uselx;
1389 	fy = applyyscale(w, fy-w->screenly) + w->usely;
1390 	tx = applyxscale(w, tx-w->screenlx) + w->uselx;
1391 	ty = applyyscale(w, ty-w->screenly) + w->usely;
1392 	if (clipline(&fx, &fy, &tx, &ty, w->uselx, w->usehx, w->usely, w->usehy)) return;
1393 	screendrawline(w, fx, fy, tx, ty, desc, texture);
1394 }
1395 
1396 /*
1397  * routine to clip and possibly draw a line from (fx,fy) to (tx,ty) in
1398  * window "w" with description "desc", texture "texture"
1399  */
us_wanttodrawo(INTBIG fx,INTBIG fxo,INTBIG fy,INTBIG fyo,INTBIG tx,INTBIG txo,INTBIG ty,INTBIG tyo,WINDOWPART * w,GRAPHICS * desc,INTBIG texture)1400 void us_wanttodrawo(INTBIG fx, INTBIG fxo, INTBIG fy, INTBIG fyo, INTBIG tx, INTBIG txo,
1401 	INTBIG ty, INTBIG tyo, WINDOWPART *w, GRAPHICS *desc, INTBIG texture)
1402 {
1403 	fx = applyxscale(w, fx-w->screenlx) + w->uselx + fxo;
1404 	fy = applyyscale(w, fy-w->screenly) + w->usely + fyo;
1405 	tx = applyxscale(w, tx-w->screenlx) + w->uselx + txo;
1406 	ty = applyyscale(w, ty-w->screenly) + w->usely + tyo;
1407 	if (clipline(&fx, &fy, &tx, &ty, w->uselx, w->usehx, w->usely, w->usehy)) return;
1408 	screendrawline(w, fx, fy, tx, ty, desc, texture);
1409 }
1410 
1411 /*
1412  * routine to draw an opened polygon full of lines, set out by 1 pixel.  The polygon is in "obj".
1413  */
us_drawextendedpolyline(POLYGON * obj,WINDOWPART * w)1414 void us_drawextendedpolyline(POLYGON *obj, WINDOWPART *w)
1415 {
1416 	REGISTER INTBIG i;
1417 	REGISTER INTBIG x1, y1, x2, y2, x1o, y1o, x2o, y2o, centerx, centery, diff;
1418 	INTBIG lx, hx, ly, hy;
1419 
1420 	/* if polygon is a line, extension is easy */
1421 	if (isbox(obj, &lx, &hx, &ly, &hy))
1422 	{
1423 		if (lx == hx)
1424 		{
1425 			us_wanttodrawo(lx, -1, ly, 0, lx, -1, hy, 0, w, obj->desc, 0);
1426 			us_wanttodrawo(lx, 1, ly, 0, lx, 1, hy, 0, w, obj->desc, 0);
1427 			return;
1428 		}
1429 		if (ly == hy)
1430 		{
1431 			us_wanttodrawo(lx, -1, ly, 0, hx, -1, ly, 0, w, obj->desc, 0);
1432 			us_wanttodrawo(lx, 1, ly, 0, hx, 1, ly, 0, w, obj->desc, 0);
1433 			return;
1434 		}
1435 	}
1436 
1437 	if (obj->count == 3 && obj->xv[0] == obj->xv[2] && obj->yv[0] == obj->yv[2])
1438 	{
1439 		x1 = obj->xv[0];   y1 = obj->yv[0];
1440 		x2 = obj->xv[1];   y2 = obj->yv[1];
1441 		if (x1 == x2)
1442 		{
1443 			us_wanttodrawo(x1,-1, y1, 0, x2,-1, y2, 0, w, obj->desc, 0);
1444 			us_wanttodrawo(x1, 1, y1, 0, x2, 1, y2, 0, w, obj->desc, 0);
1445 			return;
1446 		}
1447 		if (y1 == y2)
1448 		{
1449 			us_wanttodrawo(x1, 0, y1,-1, x2, 0, y2,-1, w, obj->desc, 0);
1450 			us_wanttodrawo(x1, 0, y1,1, x2, 0, y2, 1, w, obj->desc, 0);
1451 			return;
1452 		}
1453 		if ((x1-x2) * (y1-y2) > 0)
1454 		{
1455 			us_wanttodrawo(x1,1, y1,-1, x2,1, y2,-1, w, obj->desc, 0);
1456 			us_wanttodrawo(x1,-1, y1,1, x2,-1, y2,1, w, obj->desc, 0);
1457 		} else
1458 		{
1459 			us_wanttodrawo(x1,1, y1,1, x2,1, y2,1, w, obj->desc, 0);
1460 			us_wanttodrawo(x1,-1, y1,-1, x2,-1, y2,-1, w, obj->desc, 0);
1461 		}
1462 		return;
1463 	}
1464 
1465 	/* do extension about polygon (and see if the polygon is a single point) */
1466 	centerx = centery = diff = 0;
1467 	for(i=0; i<obj->count; i++)
1468 	{
1469 		centerx += obj->xv[i];   centery += obj->yv[i];
1470 		if (obj->xv[i] != obj->xv[0]) diff++;
1471 		if (obj->yv[i] != obj->yv[0]) diff++;
1472 	}
1473 	centerx /= obj->count;   centery /= obj->count;
1474 
1475 	/* special case if a single point */
1476 	if (diff == 0)
1477 	{
1478 		us_wanttodrawo(centerx, -1, centery, -1, centerx, 1, centery, -1, w, obj->desc, 0);
1479 		us_wanttodrawo(centerx, 1, centery, -1, centerx, 1, centery, 1, w, obj->desc, 0);
1480 		us_wanttodrawo(centerx, 1, centery, 1, centerx, -1, centery, 1, w, obj->desc, 0);
1481 		us_wanttodrawo(centerx, -1, centery, 1, centerx, -1, centery, -1, w, obj->desc, 0);
1482 		return;
1483 	}
1484 
1485 	for(i=1; i<obj->count; i++)
1486 	{
1487 		x1 = obj->xv[i-1];   y1 = obj->yv[i-1];
1488 		x2 = obj->xv[i];     y2 = obj->yv[i];
1489 		if (x1 < centerx) x1o = -1; else
1490 			if (x1 > centerx) x1o = 1; else x1o = 0;
1491 		if (y1 < centery) y1o = -1; else
1492 			if (y1 > centery) y1o = 1; else y1o = 0;
1493 		if (x2 < centerx) x2o = -1; else
1494 			if (x2 > centerx) x2o = 1; else x2o = 0;
1495 		if (y2 < centery) y2o = -1; else
1496 			if (y2 > centery) y2o = 1; else y2o = 0;
1497 		us_wanttodrawo(x1, x1o, y1, y1o, x2, x2o, y2, y2o, w, obj->desc,
1498 			0);
1499 	}
1500 }
1501 
1502 /*
1503  * Write text in a box.  The box ranges from "lx" to "ux" in X and
1504  * from "ly" to "uy" in Y.  Draw in bit planes "desc->bits" with color
1505  * "desc->color".  Put "txt" there (or as much as will fit).  The value of
1506  * "initialfont" is the default size of text which will be reduced until it
1507  * can fit.
1508  */
us_writetext(INTBIG lx,INTBIG ux,INTBIG ly,INTBIG uy,GRAPHICS * desc,CHAR * txt,UINTBIG * initdescript,WINDOWPART * win,TECHNOLOGY * tech)1509 void us_writetext(INTBIG lx, INTBIG ux, INTBIG ly, INTBIG uy, GRAPHICS *desc, CHAR *txt,
1510 	UINTBIG *initdescript, WINDOWPART *win, TECHNOLOGY *tech)
1511 {
1512 	REGISTER INTBIG stop, save, xabssize, yabssize, abssize, newsize, oldsize;
1513 	INTBIG six, siy;
1514 	UINTBIG descript[TEXTDESCRIPTSIZE];
1515 
1516 	/* scan for a font that fits */
1517 	TDCOPY(descript, initdescript);
1518 	for(;;)
1519 	{
1520 		screensettextinfo(win, tech, descript);
1521 		screengettextsize(win, txt, &six, &siy);
1522 		if (six <= ux-lx && siy <= uy-ly) break;
1523 
1524 		oldsize = TDGETSIZE(descript);
1525 		abssize = TXTGETPOINTS(oldsize);
1526 		if (abssize != 0)
1527 		{
1528 			/* jump quickly to the proper font size */
1529 			if (six <= ux-lx) xabssize = abssize; else
1530 				xabssize = abssize * (ux-lx) / six;
1531 			if (siy <= uy-ly) yabssize = abssize; else
1532 				yabssize = abssize * (uy-ly) / siy;
1533 			newsize = mini(xabssize, yabssize);
1534 			if (newsize < 4) return;
1535 			TDSETSIZE(descript, TXTSETPOINTS(newsize));
1536 		} else
1537 		{
1538 			newsize = TXTGETQLAMBDA(TDGETSIZE(descript)) - 1;
1539 			if (newsize <= 0) return;
1540 			TDSETSIZE(descript, TXTSETQLAMBDA(newsize));
1541 		}
1542 	}
1543 
1544 	/* if the text doesn't fit in Y, quit */
1545 	if (siy > uy-ly) return;
1546 
1547 	/* truncate in X if possible */
1548 	if (six > ux-lx)
1549 	{
1550 		stop = (ux-lx) * estrlen(txt);
1551 		stop /= six;
1552 		if (stop == 0) return;
1553 		save = txt[stop];   txt[stop] = 0;
1554 		screengettextsize(win, txt, &six, &siy);
1555 	} else stop = -1;
1556 
1557 	/* draw the text */
1558 	screendrawtext(win, lx+(ux-lx-six)/2, ly+(uy-ly-siy)/2, txt, desc);
1559 	if (stop >= 0) txt[stop] = (CHAR)save;
1560 }
1561 
1562 /******************** WINDOW PANNING ********************/
1563 
1564 /*
1565  * routine to slide the contents of the current window up by "dist" lambda
1566  * units (slides down if "dist" is negative)
1567  */
us_slideup(INTBIG dist)1568 void us_slideup(INTBIG dist)
1569 {
1570 	/* save and erase highlighting */
1571 	us_pushhighlight();
1572 	us_clearhighlightcount();
1573 
1574 	/* set the new window data */
1575 	startobjectchange((INTBIG)el_curwindowpart, VWINDOWPART);
1576 	(void)setval((INTBIG)el_curwindowpart, VWINDOWPART, x_("screenly"), el_curwindowpart->screenly - dist, VINTEGER);
1577 	(void)setval((INTBIG)el_curwindowpart, VWINDOWPART, x_("screenhy"), el_curwindowpart->screenhy - dist, VINTEGER);
1578 	endobjectchange((INTBIG)el_curwindowpart, VWINDOWPART);
1579 
1580 	/* restore highlighting */
1581 	us_pophighlight(FALSE);
1582 }
1583 
1584 /*
1585  * routine to slide the current window left by "dist" lambda units
1586  * (slides right if "dist" is negative)
1587  */
us_slideleft(INTBIG dist)1588 void us_slideleft(INTBIG dist)
1589 {
1590 	/* save and erase highlighting */
1591 	us_pushhighlight();
1592 	us_clearhighlightcount();
1593 
1594 	startobjectchange((INTBIG)el_curwindowpart, VWINDOWPART);
1595 	(void)setval((INTBIG)el_curwindowpart, VWINDOWPART, x_("screenlx"), el_curwindowpart->screenlx + dist, VINTEGER);
1596 	(void)setval((INTBIG)el_curwindowpart, VWINDOWPART, x_("screenhx"), el_curwindowpart->screenhx + dist, VINTEGER);
1597 	endobjectchange((INTBIG)el_curwindowpart, VWINDOWPART);
1598 
1599 	/* restore highlighting */
1600 	us_pophighlight(FALSE);
1601 }
1602 
1603 /******************** TRANSFORMATION TO SCREEN ********************/
1604 
1605 /*
1606  * routine to convert the reference parameters (lx,ux, ly,uy)
1607  * which define a box to screen co-ordinates ready to plot.
1608  * The values are scaled to screen space of window "w" and clipped.
1609  * If the routine returns true, the box is all off the screen.
1610  */
us_makescreen(INTBIG * lx,INTBIG * ly,INTBIG * ux,INTBIG * uy,WINDOWPART * w)1611 BOOLEAN us_makescreen(INTBIG *lx, INTBIG *ly, INTBIG *ux, INTBIG *uy, WINDOWPART *w)
1612 {
1613 	/* transform to screen space */
1614 	if (*ux < w->screenlx || *lx > w->screenhx) return(TRUE);
1615 	if (*uy < w->screenly || *ly > w->screenhy) return(TRUE);
1616 	*lx = applyxscale(w, *lx-w->screenlx) + w->uselx;
1617 	*ly = applyyscale(w, *ly-w->screenly) + w->usely;
1618 	*ux = applyxscale(w, *ux-w->screenlx) + w->uselx;
1619 	*uy = applyyscale(w, *uy-w->screenly) + w->usely;
1620 
1621 	/* now clip to screen bounds */
1622 	if (*lx < w->uselx) *lx = w->uselx;
1623 	if (*ly < w->usely) *ly = w->usely;
1624 	if (*ux > w->usehx) *ux = w->usehx;
1625 	if (*uy > w->usehy) *uy = w->usehy;
1626 	return(FALSE);
1627 }
1628 
1629 /******************** 3D DISPLAY ********************/
1630 
1631 /*
1632  * Routine to setup the transformation matrix for 3D viewing.
1633  * Called once when the display is initially converted to 3D.
1634  */
us_3dsetupviewing(WINDOWPART * w)1635 void us_3dsetupviewing(WINDOWPART *w)
1636 {
1637 	REGISTER TECHNOLOGY *tech;
1638 	REGISTER INTBIG i, lambda;
1639 	float thickness, height, lowheight, highheight, scale, cx, cy, cz, sx, sy, sz;
1640 	REGISTER NODEPROTO *np;
1641 	REGISTER XFORM3D *xf3;
1642 
1643 	/* determine height range */
1644 	lowheight = 0.0;   highheight = -1.0;
1645 	for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
1646 	{
1647 		for(i=0; i<tech->layercount; i++)
1648 		{
1649 			if (get3dfactors(tech, i, &height, &thickness)) continue;
1650 			if (highheight < lowheight)
1651 			{
1652 				highheight = lowheight = height;
1653 			} else
1654 			{
1655 				if (height < lowheight) lowheight = height;
1656 				if (height > highheight) highheight = height;
1657 			}
1658 		}
1659 	}
1660 
1661 	/* setup initial camera */
1662 	np = w->curnodeproto;
1663 	if (np == NONODEPROTO) return;
1664 	cx = (np->lowx + np->highx) / 2.0f;
1665 	cy = (np->lowy + np->highy) / 2.0f;
1666 	lambda = el_curlib->lambda[el_curtech->techindex];
1667 	cz = (highheight + lowheight) / 2.0f * lambda;
1668 	sx = (float)(np->highx - np->lowx);
1669 	sy = (float)(np->highy - np->lowy);
1670 	sz = (highheight - lowheight) * lambda;
1671 	scale = (sx > sy) ? sx : sy;
1672 	scale = (sz > scale) ? sz : scale;
1673 
1674 	/* setup viewing parameters */
1675 	xf3 = &w->xf3;
1676 	xf3->fieldofview = 45.0f;
1677 	xf3->eye[0] = -0.2f;   xf3->eye[1] = 0.2f;   xf3->eye[2] = 1.0f;
1678 	vectornormalize3d(xf3->eye);
1679 	vectormultiply3d(xf3->eye, scale, xf3->eye);
1680 	xf3->eye[0] += cx;   xf3->eye[1] += cy;   xf3->eye[2] += cz;
1681 	xf3->view[0] = cx;   xf3->view[1] = cy;   xf3->view[2] = cz;
1682 	xf3->up[0] = 0.0;   xf3->up[1] = 1.0;   xf3->up[2] = 0.0;
1683 	xf3->nearplane = 0.1f;   xf3->farplane = scale * 60.0f;
1684 	xf3->screenx = (float)(w->usehx - w->uselx) / 2.0f;
1685 	xf3->screeny = (float)(w->usehy - w->usely) / 2.0f;
1686 	xf3->aspect = xf3->screenx / xf3->screeny;
1687 	us_3dbuildtransform(xf3);
1688 }
1689 
1690 /*
1691  * Routine to fill window "win".
1692  */
us_3dfillview(WINDOWPART * win)1693 void us_3dfillview(WINDOWPART *win)
1694 {
1695 	XFORM3D *xf3;
1696 	float sx, sy, sz, maxs, toeye[3];
1697 
1698 	xf3 = &win->xf3;
1699 
1700 	sx = us_3dhighx - us_3dlowx;
1701 	sy = us_3dhighy - us_3dlowy;
1702 	sz = us_3dhighz - us_3dlowz;
1703 	maxs = (sx > sy) ? sx : sy;
1704 	maxs = (sz > maxs) ? sz : maxs;
1705 
1706 	xf3->fieldofview = 45.0f;
1707 	vectorsubtract3d(xf3->eye, xf3->view, toeye);
1708 	xf3->view[0] = us_3dcenterx;  xf3->view[1] = us_3dcentery;  xf3->view[2] = us_3dcenterz;
1709 	vectoradd3d(xf3->view, toeye, xf3->eye);
1710 	xf3->nearplane = 0.1f;
1711 	xf3->farplane = maxs * 60.0f;
1712 
1713 	us_3dbuildtransform(xf3);
1714 	us_3drender(win);
1715 }
1716 
1717 /*
1718  * Routine to zoom window "win" by a factor of "z".
1719  */
us_3dzoomview(WINDOWPART * win,float z)1720 void us_3dzoomview(WINDOWPART *win, float z)
1721 {
1722 	XFORM3D *xf3;
1723 
1724 	xf3 = &win->xf3;
1725 	xf3->fieldofview = xf3->fieldofview * z;
1726 	if (xf3->fieldofview > FOVMAX) xf3->fieldofview = FOVMAX;
1727 	if (xf3->fieldofview < FOVMIN) xf3->fieldofview = FOVMIN;
1728 	us_3dbuildtransform(xf3);
1729 	us_3drender(win);
1730 }
1731 
1732 /*
1733  * Routine to pan window "win" by a factor of "x,y".
1734  */
us_3dpanview(WINDOWPART * win,INTBIG x,INTBIG y)1735 void us_3dpanview(WINDOWPART *win, INTBIG x, INTBIG y)
1736 {
1737 	XFORM3D *xf3;
1738 	float d[3], side[3], up[3], view[3], scale, e[3], offset[3];
1739 
1740 	xf3 = &win->xf3;
1741 	vectorsubtract3d(xf3->view, xf3->eye, d);
1742 	vectorcross3d(d, xf3->up, side);
1743 	vectorcross3d(d, side, up);
1744 	vectornormalize3d(side);
1745 	vectornormalize3d(up);
1746 	vectorsubtract3d(xf3->view, xf3->eye, view);
1747 	scale = vectormagnitude3d(view);
1748 	vectormultiply3d(side, scale * 0.1f * (float)x, d);
1749 	vectormultiply3d(up, scale * 0.1f * (float)y, e);
1750 	vectoradd3d(d, e, offset);
1751 	vectoradd3d(xf3->eye, offset, xf3->eye);
1752 	vectoradd3d(xf3->view, offset, xf3->view);
1753 
1754 	us_3dbuildtransform(xf3);
1755 	us_3drender(win);
1756 }
1757 
1758 /*
1759  * Routine to build the 4x4 transformation matrix in "xf3" from
1760  * the viewing parameters there.
1761  */
us_3dbuildtransform(XFORM3D * xf3)1762 void us_3dbuildtransform(XFORM3D *xf3)
1763 {
1764 	float f[3], s[3], u[3], persp[4][4], xform[4][4], trans[4][4], rot[4][4], ff;
1765 
1766 	/* build the perspective transform */
1767 	matrixid3d(persp);
1768 	if ((us_useroptions&NO3DPERSPECTIVE) == 0)
1769 	{
1770 		ff = 1.0f / (float)tan((xf3->fieldofview * EPI / 180.0f) / 2.0f);
1771 		persp[0][0] = ff / xf3->aspect;
1772 		persp[1][1] = ff;
1773 		persp[2][2] = (xf3->farplane + xf3->nearplane) / (xf3->nearplane - xf3->farplane);
1774 		persp[2][3] = 2.0f * xf3->farplane * xf3->nearplane / (xf3->nearplane - xf3->farplane);
1775 		persp[3][2] = -1.0f;
1776 		persp[3][3] = 0.0f;
1777 	} else
1778 	{
1779 		persp[0][0] = 45.0f / xf3->fieldofview;
1780 		persp[1][1] = 45.0f / xf3->fieldofview;
1781 		persp[2][2] = -1.0;
1782 	}
1783 
1784 	/* build the viewing transform */
1785 	matrixid3d(trans);
1786 	trans[0][3] = -xf3->eye[0];
1787 	trans[1][3] = -xf3->eye[1];
1788 	trans[2][3] = -xf3->eye[2];
1789 
1790 	vectorsubtract3d(xf3->view, xf3->eye, f);
1791 	vectornormalize3d(f);
1792 	vectornormalize3d(xf3->up);
1793 	vectorcross3d(f, xf3->up, s);
1794 	vectorcross3d(s, f, u);
1795 	matrixid3d(rot);
1796 	rot[0][0] = s[0];   rot[0][1] = s[1];   rot[0][2] = s[2];
1797 	rot[1][0] = u[0];   rot[1][1] = u[1];   rot[1][2] = u[2];
1798 	rot[2][0] = -f[0];  rot[2][1] = -f[1];  rot[2][2] = -f[2];
1799 	matrixmult3d(trans, rot, xform);
1800 
1801 	/* build the transformation matrix */
1802 	matrixmult3d(xform, persp, xf3->xform);
1803 }
1804 
1805 /*
1806  * Routine called at the start of drawing.
1807  */
us_3dstartdrawing(WINDOWPART * win)1808 void us_3dstartdrawing(WINDOWPART *win)
1809 {
1810 	us_3dgatheringpolys = TRUE;
1811 	us_3dpolycount = 0;
1812 	us_3dwindowpart = win;
1813 }
1814 
1815 /*
1816  * Helper routine for "us_3denddrawing()" that makes polygon depth go in ascending order
1817  */
us_3dpolydepthascending(const void * e1,const void * e2)1818 int us_3dpolydepthascending(const void *e1, const void *e2)
1819 {
1820 	REGISTER POLY3D *c1, *c2;
1821 	REGISTER float diff;
1822 
1823 	c1 = *((POLY3D **)e1);
1824 	c2 = *((POLY3D **)e2);
1825 	diff = c1->depth - c2->depth;
1826 	if (diff < 0.0) return(-1);
1827 	if (diff > 0.0) return(1);
1828 	return(0);
1829 }
1830 
1831 /*
1832  * Routine called at the end of drawing.
1833  */
us_3denddrawing(void)1834 void us_3denddrawing(void)
1835 {
1836 	REGISTER POLY3D *poly1;
1837 	REGISTER INTBIG i, j;
1838 
1839 	/* flush the opaque graphics out */
1840 	us_endchanges(NOWINDOWPART);
1841 	us_3dgatheringpolys = FALSE;
1842 
1843 	/* sort the polygons */
1844 	esort(us_3dpolylist, us_3dpolycount, sizeof (POLY3D *), us_3dpolydepthascending);
1845 
1846 	/* determine bounding volume */
1847 	for(i=0; i<us_3dpolycount; i++)
1848 	{
1849 		poly1 = us_3dpolylist[i];
1850 		for(j=0; j<poly1->count; j++)
1851 		{
1852 			if (i == 0 && j == 0)
1853 			{
1854 				us_3dlowx = us_3dhighx = poly1->x[j];
1855 				us_3dlowy = us_3dhighy = poly1->y[j];
1856 				us_3dlowz = us_3dhighz = poly1->z[j];
1857 			} else
1858 			{
1859 				if (poly1->x[j] < us_3dlowx) us_3dlowx = poly1->x[j];
1860 				if (poly1->x[j] > us_3dhighx) us_3dhighx = poly1->x[j];
1861 				if (poly1->y[j] < us_3dlowy) us_3dlowy = poly1->y[j];
1862 				if (poly1->y[j] > us_3dhighy) us_3dhighy = poly1->y[j];
1863 				if (poly1->z[j] < us_3dlowz) us_3dlowz = poly1->z[j];
1864 				if (poly1->z[j] > us_3dhighz) us_3dhighz = poly1->z[j];
1865 			}
1866 		}
1867 	}
1868 	us_3dcenterx = (us_3dlowx + us_3dhighx) / 2.0f;
1869 	us_3dcentery = (us_3dlowy + us_3dhighy) / 2.0f;
1870 	us_3dcenterz = (us_3dlowz + us_3dhighz) / 2.0f;
1871 
1872 	/* render it */
1873 	us_3dbuildtransform(&us_3dwindowpart->xf3);
1874 	us_3drender(us_3dwindowpart);
1875 }
1876 
1877 /*
1878  * Routine to re-render the polygons to window "w".
1879  */
us_3drender(WINDOWPART * w)1880 void us_3drender(WINDOWPART *w)
1881 {
1882 	REGISTER INTBIG i, j, k, passes, lambda;
1883 	INTBIG start[2], finish[2], incr[2];
1884 	REGISTER INTBIG save, isneg;
1885 	POLY3D *poly3d;
1886 	float vec[4], res[4], res2[4];
1887 	float zplane, res3[4];
1888 	static POLYGON *poly = NOPOLYGON;
1889 	REGISTER XFORM3D *xf3;
1890 
1891 	xf3 = &w->xf3;
1892 	(void)needstaticpolygon(&poly, 4, us_tool->cluster);
1893 
1894 	/* clear the screen */
1895 	us_erasewindow(w);
1896 
1897 	/* see if the view is from the back */
1898 	lambda = el_curlib->lambda[el_curtech->techindex];
1899 	for(zplane = us_3dhighz; zplane >= us_3dlowz; zplane -= lambda)
1900 	{
1901 		vec[0] = us_3dcenterx; vec[1] = us_3dcentery; vec[2] = zplane; vec[3] = 1.0;
1902 		matrixxform3d(vec, xf3->xform, res);
1903 		vec[0] = us_3dcenterx+1000.0f; vec[1] = us_3dcentery; vec[2] = zplane; vec[3] = 1.0;
1904 		matrixxform3d(vec, xf3->xform, res2);
1905 		vec[0] = us_3dcenterx; vec[1] = us_3dcentery+1000.0f; vec[2] = zplane; vec[3] = 1.0;
1906 		matrixxform3d(vec, xf3->xform, res3);
1907 		res[0] /= res[3];   res[1] /= res[3];   res[2] /= res[3];
1908 		res2[0] /= res2[3];   res2[1] /= res2[3];   res2[2] /= res2[3];
1909 		res3[0] /= res3[3];   res3[1] /= res3[3];   res3[2] /= res3[3];
1910 		vectorsubtract3d(res2, res, res2);
1911 		vectorsubtract3d(res3, res, res3);
1912 		vectorcross3d(res2, res3, res);
1913 		if (res[2] > 0) break;
1914 	}
1915 	if (zplane == us_3dhighz)
1916 	{
1917 		start[0] = 0;   finish[0] = us_3dpolycount;   incr[0] = 1;
1918 		passes = 1;
1919 	} else if (zplane < us_3dlowz)
1920 	{
1921 		start[0] = us_3dpolycount-1;   finish[0] = -1;   incr[0] = -1;
1922 		passes = 1;
1923 	} else
1924 	{
1925 		for(i=0; i<us_3dpolycount; i++)
1926 			if (us_3dpolylist[i]->depth > zplane) break;
1927 		start[0] = 0;                  finish[0] = i;	  incr[0] = 1;
1928 		start[1] = us_3dpolycount-1;   finish[1] = i-1;   incr[1] = -1;
1929 		passes = 2;
1930 	}
1931 
1932 	/* now draw it all */
1933 	for(k=0; k<passes; k++)
1934 	{
1935 		for(i=start[k]; i != finish[k]; i = i + incr[k])
1936 		{
1937 			poly3d = us_3dpolylist[i];
1938 			if (poly->limit < poly3d->count) (void)extendpolygon(poly, poly3d->count);
1939 			isneg = 0;
1940 			for(j=0; j<poly3d->count; j++)
1941 			{
1942 				vec[0] = poly3d->x[j];
1943 				vec[1] = poly3d->y[j];
1944 				vec[2] = poly3d->z[j];
1945 				vec[3] = 1.0;
1946 				matrixxform3d(vec, xf3->xform, res);
1947 				if (res[2] < 0.0)
1948 				{
1949 					isneg = 1;
1950 					break;
1951 				}
1952 				poly->xv[j] = (INTBIG)(res[0] / res[3] / 2.0 * xf3->screenx + xf3->screenx);
1953 				poly->yv[j] = (INTBIG)(res[1] / res[3] / 2.0 * xf3->screeny + xf3->screeny);
1954 				poly->xv[j] = roundfloat((poly->xv[j] - w->uselx) / w->scalex) + w->screenlx;
1955 				poly->yv[j] = roundfloat((poly->yv[j] - w->usely) / w->scaley) + w->screenly;
1956 			}
1957 			if (isneg != 0) continue;
1958 			poly->desc = poly3d->desc;
1959 			poly->count = poly3d->count;
1960 			poly->style = FILLED;
1961 			save = poly3d->desc->bits;
1962 			poly3d->desc->bits = LAYERA;
1963 			(*us_displayroutine)(poly, w);
1964 			poly3d->desc->bits = save;
1965 		}
1966 	}
1967 }
1968 
us_3dsetinteraction(INTBIG interaction)1969 void us_3dsetinteraction(INTBIG interaction)
1970 {
1971 	switch (interaction)
1972 	{
1973 		case 0: us_3dinteraction = ROTATEVIEW;  break;
1974 		case 1: us_3dinteraction = ZOOMVIEW;    break;
1975 		case 2: us_3dinteraction = PANVIEW;     break;
1976 		case 3: us_3dinteraction = TWISTVIEW;   break;
1977 	}
1978 }
1979 
1980 /*
1981  * button handler for 3D windows
1982  */
us_3dbuttonhandler(WINDOWPART * w,INTBIG but,INTBIG x,INTBIG y)1983 void us_3dbuttonhandler(WINDOWPART *w, INTBIG but, INTBIG x, INTBIG y)
1984 {
1985 	REGISTER XFORM3D *xf3;
1986 
1987 	/* changes to the mouse-wheel are handled by the user interface */
1988 	if (wheelbutton(but))
1989 	{
1990 		us_buttonhandler(w, but, x, y);
1991 		return;
1992 	}
1993 	xf3 = &w->xf3;
1994 	us_3dlastbutx = x;
1995 	us_3dlastbuty = us_3dinitialbuty = y;
1996 	switch (us_3dinteraction)
1997 	{
1998 		case ROTATEVIEW:
1999 			break;
2000 		case ZOOMVIEW:
2001 			us_3dinitialfov = xf3->fieldofview;
2002 			break;
2003 		case PANVIEW:
2004 			break;
2005 		case TWISTVIEW:
2006 			break;
2007 	}
2008 	trackcursor(FALSE, us_nullup, us_nullvoid, us_3deachdown, us_nullchar, us_nullvoid, TRACKNORMAL);
2009 }
2010 
us_3deachdown(INTBIG x,INTBIG y)2011 BOOLEAN us_3deachdown(INTBIG x, INTBIG y)
2012 {
2013 	float d[3], e[3], offset[3], side[3], up[3], view[3], scale;
2014 	float angle, sinTheta, cosTheta, toDirection[3], tempVector1[3], tempVector2[3],
2015 		tempVector3[3], rotateVector[3], toLength, newFrom[3], sinPhi, cosPhi,
2016 		newToDirection[3], rightDirection[3], dot, lastangle, cx, cy;
2017 	REGISTER XFORM3D *xf3;
2018 
2019 	xf3 = &us_3dwindowpart->xf3;
2020 	switch (us_3dinteraction)
2021 	{
2022 		case ROTATEVIEW:
2023 			if (x == us_3dlastbutx && y == us_3dlastbuty) break;
2024 
2025 			vectorsubtract3d(xf3->eye, xf3->view, toDirection);
2026 			toLength = vectormagnitude3d(toDirection);
2027 			vectornormalize3d(toDirection);
2028 
2029 			/* Calculate orthonormal up direction by Gram-Schmidt orthogonalization */
2030 			dot = vectordot3d(xf3->up, toDirection);
2031 			up[0] = xf3->up[0] - dot * toDirection[0];
2032 			up[1] = xf3->up[1] - dot * toDirection[1];
2033 			up[2] = xf3->up[2] - dot * toDirection[2];
2034 
2035 			/* orthonormal up vector vector */
2036 			vectornormalize3d(up);
2037 
2038 			/* Calculate orthonormal right vector to make up an orthonormal view frame */
2039 			vectorcross3d(up, toDirection, rightDirection);
2040 
2041 			angle = (float)(180.0f * ((float)(us_3dlastbuty - y) /
2042 				(float)(us_3dwindowpart->usehy-us_3dwindowpart->usely)/2.0f) * EPI / 180.0f);
2043 			sinTheta = (float)sin(angle);
2044 			cosTheta = (float)cos(angle);
2045 			vectormultiply3d(toDirection, cosTheta, tempVector1);
2046 			vectormultiply3d(xf3->up, sinTheta, tempVector2);
2047 			vectoradd3d(tempVector1, tempVector2, rotateVector);
2048 			vectormultiply3d(rotateVector, toLength, rotateVector);
2049 			vectoradd3d(xf3->view, rotateVector, newFrom);
2050 
2051 			/* rotate using RIGHT and new TO directions and X position of mouse */
2052 			angle = (float)(180.0f * ((float)(us_3dlastbutx - x) /
2053 				(float)(us_3dwindowpart->usehx-us_3dwindowpart->uselx)/2.0f) * EPI/180.0f);
2054 			sinPhi = (float)sin(angle);
2055 			cosPhi = (float)cos(angle);
2056 			vectorsubtract3d(newFrom, xf3->view, newToDirection);
2057 			vectornormalize3d(newToDirection);
2058 			vectormultiply3d(newToDirection, cosPhi, tempVector1);
2059 			vectormultiply3d(rightDirection, sinPhi, tempVector2);
2060 			vectoradd3d(tempVector1, tempVector2, rotateVector);
2061 			vectormultiply3d(rotateVector, toLength, rotateVector);
2062 			vectoradd3d(xf3->view, rotateVector, xf3->eye);
2063 
2064 			/* calculate new UP vector */
2065 			vectormultiply3d(xf3->up, cosTheta, tempVector2);
2066 			vectormultiply3d(toDirection, -sinTheta*cosPhi, tempVector1);
2067 			vectormultiply3d(rightDirection, sinTheta*sinPhi, tempVector3);
2068 			vectoradd3d(tempVector1, tempVector2, xf3->up);
2069 			vectoradd3d(xf3->up, tempVector3, xf3->up);
2070 
2071 			us_3dlastbutx = x;
2072 			us_3dlastbuty = y;
2073 			us_3dbuildtransform(xf3);
2074 			us_3drender(us_3dwindowpart);
2075 			break;
2076 
2077 		case ZOOMVIEW:
2078 			xf3->fieldofview = us_3dinitialfov + 0.1f * (float)(us_3dinitialbuty - y);
2079 			if (xf3->fieldofview > FOVMAX) xf3->fieldofview = FOVMAX;
2080 			if (xf3->fieldofview < FOVMIN) xf3->fieldofview = FOVMIN;
2081 			us_3dbuildtransform(xf3);
2082 			us_3drender(us_3dwindowpart);
2083 			break;
2084 
2085 		case PANVIEW:
2086 			if (x == us_3dlastbutx && y == us_3dlastbuty) break;
2087 			vectorsubtract3d(xf3->view, xf3->eye, d);
2088 			vectorcross3d(d, xf3->up, side);
2089 			vectorcross3d(d, side, up);
2090 			vectornormalize3d(side);
2091 			vectornormalize3d(up);
2092 			vectorsubtract3d(xf3->view, xf3->eye, view);
2093 			scale = vectormagnitude3d(view);
2094 			vectormultiply3d(side, scale * 0.01f * (float)(us_3dlastbutx - x), d);
2095 			vectormultiply3d(up, scale * 0.01f * (float)(y - us_3dlastbuty), e);
2096 			vectoradd3d(d, e, offset);
2097 			vectoradd3d(xf3->eye, offset, xf3->eye);
2098 			vectoradd3d(xf3->view, offset, xf3->view);
2099 
2100 			us_3dbuildtransform(xf3);
2101 			us_3drender(us_3dwindowpart);
2102 			us_3dlastbutx = x;
2103 			us_3dlastbuty = y;
2104 			break;
2105 
2106 		case TWISTVIEW:
2107 			if (x == us_3dlastbutx && y == us_3dlastbuty) break;
2108 
2109 			/* compute angle of twist */
2110 			cx = (us_3dwindowpart->usehx+us_3dwindowpart->uselx) / 2.0f;
2111 			cy = (us_3dwindowpart->usehy+us_3dwindowpart->usely) / 2.0f;
2112 			lastangle = (float)atan2(us_3dlastbuty-cy, us_3dlastbutx-cx);
2113 			angle = (float)atan2(y-cy, x-cx);
2114 			vectorsubtract3d(xf3->eye, xf3->view, toDirection);
2115 			us_3drotatepointaboutaxis(xf3->up, lastangle-angle, toDirection, tempVector1);
2116 			xf3->up[0] = tempVector1[0];
2117 			xf3->up[1] = tempVector1[1];
2118 			xf3->up[2] = tempVector1[2];
2119 			vectornormalize3d(xf3->up);
2120 
2121 			us_3dlastbutx = x;
2122 			us_3dlastbuty = y;
2123 			us_3dbuildtransform(xf3);
2124 			us_3drender(us_3dwindowpart);
2125 			break;
2126 	}
2127 	return(FALSE);
2128 }
2129 
2130 /*
2131  * Rotate a point "p" by angle "theta" around an arbitrary axis "r", returning point "q".
2132  */
us_3drotatepointaboutaxis(float * p,float theta,float * r,float * q)2133 void us_3drotatepointaboutaxis(float *p, float theta, float *r, float *q)
2134 {
2135 	float costheta, sintheta;
2136 
2137 	vectornormalize3d(r);
2138 	costheta = (float)cos(theta);
2139 	sintheta = (float)sin(theta);
2140 
2141 	q[0] = (costheta + (1.0f - costheta) * r[0] * r[0]) * p[0];
2142 	q[0] += ((1.0f - costheta) * r[0] * r[1] - r[2] * sintheta) * p[1];
2143 	q[0] += ((1.0f - costheta) * r[0] * r[2] + r[1] * sintheta) * p[2];
2144 
2145 	q[1] = ((1.0f - costheta) * r[0] * r[1] + r[2] * sintheta) * p[0];
2146 	q[1] += (costheta + (1.0f - costheta) * r[1] * r[1]) * p[1];
2147 	q[1] += ((1.0f - costheta) * r[1] * r[2] - r[0] * sintheta) * p[2];
2148 
2149 	q[2] = ((1.0f - costheta) * r[0] * r[2] - r[1] * sintheta) * p[0];
2150 	q[2] += ((1.0f - costheta) * r[1] * r[2] + r[0] * sintheta) * p[1];
2151 	q[2] += (costheta + (1.0f - costheta) * r[2] * r[2]) * p[2];
2152 }
2153 
2154 /*
2155  * Routine to draw polygon "poly" in window "w".
2156  */
us_3dshowpoly(POLYGON * poly,WINDOWPART * w)2157 void us_3dshowpoly(POLYGON *poly, WINDOWPART *w)
2158 {
2159 	REGISTER INTBIG i, previ, lambda;
2160 	float topheight, botheight;
2161 	INTBIG lx, hx, ly, hy;
2162 	float thickness, depth, topdepth, botdepth, centerdepth;
2163 	REGISTER POLY3D *poly3d;
2164 
2165 	/* ignore polygons with no color */
2166 	if (poly->desc->col == 0) return;
2167 
2168 	/* special case when drawing instance boundaries */
2169 	if (poly->desc->col == el_colcell)
2170 	{
2171 		(void)get3dfactors(el_curtech, 0, &depth, &thickness);
2172 		topdepth = depth;
2173 		topheight = topdepth;
2174 		if (isbox(poly, &lx, &hx, &ly, &hy))
2175 		{
2176 			poly3d = us_3dgetnextpoly(2);
2177 			if (poly3d == 0) return;
2178 			poly3d->x[0] = (float)lx;   poly3d->y[0] = (float)ly;   poly3d->z[0] = topheight;
2179 			poly3d->x[1] = (float)lx;   poly3d->y[1] = (float)hy;   poly3d->z[1] = topheight;
2180 			poly3d->depth = topdepth;
2181 			poly3d->desc = poly->desc;
2182 
2183 			poly3d = us_3dgetnextpoly(2);
2184 			if (poly3d == 0) return;
2185 			poly3d->x[0] = (float)lx;   poly3d->y[0] = (float)hy;   poly3d->z[0] = topheight;
2186 			poly3d->x[1] = (float)hx;   poly3d->y[1] = (float)hy;   poly3d->z[1] = topheight;
2187 			poly3d->depth = topdepth;
2188 			poly3d->desc = poly->desc;
2189 
2190 			poly3d = us_3dgetnextpoly(2);
2191 			if (poly3d == 0) return;
2192 			poly3d->x[0] = (float)hx;   poly3d->y[0] = (float)hy;   poly3d->z[0] = topheight;
2193 			poly3d->x[1] = (float)hx;   poly3d->y[1] = (float)ly;   poly3d->z[1] = topheight;
2194 			poly3d->depth = topdepth;
2195 			poly3d->desc = poly->desc;
2196 
2197 			poly3d = us_3dgetnextpoly(2);
2198 			if (poly3d == 0) return;
2199 			poly3d->x[0] = (float)hx;   poly3d->y[0] = (float)ly;   poly3d->z[0] = topheight;
2200 			poly3d->x[1] = (float)lx;   poly3d->y[1] = (float)ly;   poly3d->z[1] = topheight;
2201 			poly3d->depth = topdepth;
2202 			poly3d->desc = poly->desc;
2203 		}
2204 		return;
2205 	}
2206 
2207 	/* make sure there is a technology and a layer for this polygon */
2208 	if (poly->tech == NOTECHNOLOGY) return;
2209 	if (get3dfactors(poly->tech, poly->layer, &depth, &thickness)) return;
2210 
2211 	/* setup a 3D polygon */
2212 	lambda = el_curlib->lambda[el_curtech->techindex];
2213 	topdepth = (depth + thickness/2.0f) * lambda;
2214 	topheight = topdepth;
2215 	if (thickness != 0)
2216 	{
2217 		topdepth++;
2218 		centerdepth = depth * lambda;
2219 		botdepth = (depth - thickness/2.0f) * lambda - 1;
2220 		botheight = (depth - thickness/2.0f) * lambda;
2221 	}
2222 
2223 	/* fill the 3D polygon points */
2224 	switch (poly->style)
2225 	{
2226 		case FILLED:		/* filled polygon */
2227 		case FILLEDRECT:	/* filled rectangle */
2228 		case CLOSEDRECT:	/* closed rectangle outline */
2229 		case CLOSED:		/* closed polygon outline */
2230 		case CROSSED:		/* polygon outline with cross */
2231 			if (isbox(poly, &lx, &hx, &ly, &hy))
2232 			{
2233 				poly3d = us_3dgetnextpoly(4);
2234 				if (poly3d == 0) return;
2235 				poly3d->x[0] = (float)lx;   poly3d->y[0] = (float)ly;   poly3d->z[0] = topheight;
2236 				poly3d->x[1] = (float)lx;   poly3d->y[1] = (float)hy;   poly3d->z[1] = topheight;
2237 				poly3d->x[2] = (float)hx;   poly3d->y[2] = (float)hy;   poly3d->z[2] = topheight;
2238 				poly3d->x[3] = (float)hx;   poly3d->y[3] = (float)ly;   poly3d->z[3] = topheight;
2239 				poly3d->depth = topdepth;
2240 				poly3d->desc = poly->desc;
2241 				if (thickness != 0)
2242 				{
2243 					poly3d = us_3dgetnextpoly(4);
2244 					if (poly3d == 0) return;
2245 					poly3d->x[0] = (float)lx;   poly3d->y[0] = (float)ly;   poly3d->z[0] = botheight;
2246 					poly3d->x[1] = (float)lx;   poly3d->y[1] = (float)hy;   poly3d->z[1] = botheight;
2247 					poly3d->x[2] = (float)hx;   poly3d->y[2] = (float)hy;   poly3d->z[2] = botheight;
2248 					poly3d->x[3] = (float)hx;   poly3d->y[3] = (float)ly;   poly3d->z[3] = botheight;
2249 					poly3d->depth = botdepth;
2250 					poly3d->desc = poly->desc;
2251 
2252 					poly3d = us_3dgetnextpoly(4);
2253 					if (poly3d == 0) return;
2254 					poly3d->x[0] = (float)lx;   poly3d->y[0] = (float)ly;   poly3d->z[0] = topheight;
2255 					poly3d->x[1] = (float)lx;   poly3d->y[1] = (float)hy;   poly3d->z[1] = topheight;
2256 					poly3d->x[2] = (float)lx;   poly3d->y[2] = (float)hy;   poly3d->z[2] = botheight;
2257 					poly3d->x[3] = (float)lx;   poly3d->y[3] = (float)ly;   poly3d->z[3] = botheight;
2258 					poly3d->depth = centerdepth;
2259 					poly3d->desc = poly->desc;
2260 
2261 					poly3d = us_3dgetnextpoly(4);
2262 					if (poly3d == 0) return;
2263 					poly3d->x[0] = (float)lx;   poly3d->y[0] = (float)hy;   poly3d->z[0] = topheight;
2264 					poly3d->x[1] = (float)hx;   poly3d->y[1] = (float)hy;   poly3d->z[1] = topheight;
2265 					poly3d->x[2] = (float)hx;   poly3d->y[2] = (float)hy;   poly3d->z[2] = botheight;
2266 					poly3d->x[3] = (float)lx;   poly3d->y[3] = (float)hy;   poly3d->z[3] = botheight;
2267 					poly3d->depth = centerdepth;
2268 					poly3d->desc = poly->desc;
2269 
2270 					poly3d = us_3dgetnextpoly(4);
2271 					if (poly3d == 0) return;
2272 					poly3d->x[0] = (float)hx;   poly3d->y[0] = (float)hy;   poly3d->z[0] = topheight;
2273 					poly3d->x[1] = (float)hx;   poly3d->y[1] = (float)ly;   poly3d->z[1] = topheight;
2274 					poly3d->x[2] = (float)hx;   poly3d->y[2] = (float)ly;   poly3d->z[2] = botheight;
2275 					poly3d->x[3] = (float)hx;   poly3d->y[3] = (float)hy;   poly3d->z[3] = botheight;
2276 					poly3d->depth = centerdepth;
2277 					poly3d->desc = poly->desc;
2278 
2279 					poly3d = us_3dgetnextpoly(4);
2280 					if (poly3d == 0) return;
2281 					poly3d->x[0] = (float)hx;   poly3d->y[0] = (float)ly;   poly3d->z[0] = topheight;
2282 					poly3d->x[1] = (float)lx;   poly3d->y[1] = (float)ly;   poly3d->z[1] = topheight;
2283 					poly3d->x[2] = (float)lx;   poly3d->y[2] = (float)ly;   poly3d->z[2] = botheight;
2284 					poly3d->x[3] = (float)hx;   poly3d->y[3] = (float)ly;   poly3d->z[3] = botheight;
2285 					poly3d->depth = centerdepth;
2286 					poly3d->desc = poly->desc;
2287 				}
2288 				break;
2289 			}
2290 
2291 			/* nonmanhattan polygon: handle it as points */
2292 			poly3d = us_3dgetnextpoly(poly->count);
2293 			if (poly3d == 0) return;
2294 			for(i=0; i<poly->count; i++)
2295 			{
2296 				poly3d->x[i] = (float)poly->xv[i];
2297 				poly3d->y[i] = (float)poly->yv[i];
2298 				poly3d->z[i] = topheight;
2299 			}
2300 			poly3d->depth = topdepth;
2301 			poly3d->desc = poly->desc;
2302 			if (thickness != 0)
2303 			{
2304 				poly3d = us_3dgetnextpoly(poly->count);
2305 				if (poly3d == 0) return;
2306 				for(i=0; i<poly->count; i++)
2307 				{
2308 					poly3d->x[i] = (float)poly->xv[i];
2309 					poly3d->y[i] = (float)poly->yv[i];
2310 					poly3d->z[i] = botheight;
2311 				}
2312 				poly3d->depth = botdepth;
2313 				poly3d->desc = poly->desc;
2314 
2315 				for(i=0; i<poly->count; i++)
2316 				{
2317 					if (i == 0) previ = poly->count-1; else previ = i-1;
2318 					poly3d = us_3dgetnextpoly(4);
2319 					if (poly3d == 0) return;
2320 					poly3d->x[0] = (float)poly->xv[previ];   poly3d->y[0] = (float)poly->yv[previ];   poly3d->z[0] = topheight;
2321 					poly3d->x[1] = (float)poly->xv[i];       poly3d->y[1] = (float)poly->yv[i];       poly3d->z[1] = topheight;
2322 					poly3d->x[2] = (float)poly->xv[i];       poly3d->y[2] = (float)poly->yv[i];       poly3d->z[2] = botheight;
2323 					poly3d->x[3] = (float)poly->xv[previ];   poly3d->y[3] = (float)poly->yv[previ];   poly3d->z[3] = botheight;
2324 					poly3d->depth = centerdepth;
2325 					poly3d->desc = poly->desc;
2326 				}
2327 			}
2328 			break;
2329 	}
2330 }
2331 
us_3dgetnextpoly(INTBIG count)2332 POLY3D *us_3dgetnextpoly(INTBIG count)
2333 {
2334 	REGISTER INTBIG newtotal, i;
2335 	REGISTER POLY3D **newlist, *poly3d;
2336 
2337 	/* make sure there is room for another 3D polygon */
2338 	if (us_3dpolycount >= us_3dpolytotal)
2339 	{
2340 		newtotal = us_3dpolytotal * 2;
2341 		if (newtotal <= 0) newtotal = 50;
2342 		if (us_3dpolycount > newtotal) newtotal = us_3dpolycount;
2343 		newlist = (POLY3D **)emalloc(newtotal * (sizeof (POLY3D *)), us_tool->cluster);
2344 		if (newlist == 0) return(0);
2345 		for(i=0; i<us_3dpolytotal; i++)
2346 			newlist[i] = us_3dpolylist[i];
2347 		for(i=us_3dpolytotal; i<newtotal; i++)
2348 		{
2349 			newlist[i] = (POLY3D *)emalloc(sizeof (POLY3D), us_tool->cluster);
2350 			if (newlist[i] == 0) return(0);
2351 			newlist[i]->total = 0;
2352 		}
2353 		if (us_3dpolytotal > 0) efree((CHAR *)us_3dpolylist);
2354 		us_3dpolylist = newlist;
2355 		us_3dpolytotal = newtotal;
2356 	}
2357 	poly3d = us_3dpolylist[us_3dpolycount++];
2358 	if (poly3d->total < count)
2359 	{
2360 		if (poly3d->total > 0)
2361 		{
2362 			efree((CHAR *)poly3d->x);
2363 			efree((CHAR *)poly3d->y);
2364 			efree((CHAR *)poly3d->z);
2365 		}
2366 		poly3d->total = 0;
2367 		poly3d->x = (float *)emalloc(count * (sizeof (float)), us_tool->cluster);
2368 		if (poly3d->x == 0) return(0);
2369 		poly3d->y = (float *)emalloc(count * (sizeof (float)), us_tool->cluster);
2370 		if (poly3d->y == 0) return(0);
2371 		poly3d->z = (float *)emalloc(count * (sizeof (float)), us_tool->cluster);
2372 		if (poly3d->z == 0) return(0);
2373 		poly3d->total = count;
2374 	}
2375 	poly3d->count = count;
2376 	return(poly3d);
2377 }
2378