1 /* -*- tab-width: 4 -*-
2  *
3  * Electric(tm) VLSI Design System
4  *
5  * File: compensate.c
6  * Tool for compensating geometry
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 "config.h"
33 #if COMPENTOOL
34 
35 #include "global.h"
36 #include "egraphics.h"
37 #include "dbcontour.h"
38 #include "edialogs.h"
39 #include "tecart.h"
40 #include "tecgen.h"
41 #include "usr.h"
42 #include "compensate.h"
43 #include <math.h>
44 
45 /*********** tolerances ***********/
46 
47 /* These are tolerances that have been found useful in dealing with imperfect data */
48 
49 /*
50  * The ARCSLOP factor determines how far the ends of arcs will extend in order to
51  * make a connection to a tangent line.  If, for example, it is determined that the
52  * tangent connects to a point on the circle that is beyond the ends of the arc segment,
53  * but within this factor, then the arc is extended to the connection point.
54  */
55 #define ARCSLOP              50						/* 5 degrees slop at ends of arc */
56 
57 /*
58  * The two thresholds BESTTHRESH and WORSTTHRESH are the distances that will be accepted
59  * when gathering contour information.  At the end of a contour segment (an arc or a line),
60  * an area that is BESTTHRESH in size will be examined to find the next contour segment.
61  * If nothing is found in that area, the threshold will be expanded by a factor of 10 and
62  * tried again.  This repeats until an area that is WORSTTHRESH in size is examined, at which
63  * point the contour gathering will fail.
64  */
65 #define BESTTHRESH           0.0001		/* 0.0001 mm (1/10 micron) */
66 #define WORSTTHRESH          0.1		/* 1/10 of a millimeter */
67 
68 /*
69  * The SMALLANGLETHRESH is the threshold for arc size that is acceptable for straightening
70  * out in blending rule 2.1a.  If an arc is larger than this, it is not straightened.
71  */
72 #define SMALLANGLETHRESH     200					/* 20 degrees */
73 
74 /*
75  * The CIRCLETANGENTTHRESH is the distance that will be allowed between a circle and a tangent
76  * point on a line.  If the point is within this distance, it will be considered to be tangent
77  * to the circle.
78  */
79 #define CIRCLETANGENTTHRESH  0.0001		/* 1/10000 of a millimeter */
80 
81 /*********** debugging ***********/
82 
83 /* #define DEBDUMP 1 */		/* uncomment for debugging output */
84 
85 #ifdef DEBDUMP				/* nonzero for debugging */
86   FILE *compen_io;
87 #endif
88 
89 static void compen_debugdump(CHAR *msg, ...);
90 
91 /*********** automatic feature detection ***********/
92 
93 typedef struct
94 {
95 	INTBIG     type;			/* 0 for circle, 1 for slot */
96 	INTBIG     diameter;		/* circle diameter or slot width */
97 	INTBIG     length;			/* slot length */
98 	INTBIG     percentage;		/* circle compensation or slot endcap compensation */
99 	INTBIG     lenpercentage;	/* slot length (side) compensation */
100 } AUTOFEATURE;
101 
102 /*********** the COMPENSATION tool table ***********/
103 
104 static COMCOMP compen_setf1p = {NOKEYWORD, NOTOPLIST, NONEXTLIST, NOPARAMS,
105 	INPUTOPT, x_(" \t"), M_("metal thickness (or 'dialog' to set interactively)"), 0};
106 static COMCOMP compen_setf2p = {NOKEYWORD, NOTOPLIST, NONEXTLIST, NOPARAMS,
107 	INPUTOPT, x_(" \t"), M_("Laser Writing System edge compensation"), 0};
108 static COMCOMP compen_setf3p = {NOKEYWORD, NOTOPLIST, NONEXTLIST, NOPARAMS,
109 	INPUTOPT, x_(" \t"), M_("Global compensation"), 0};
110 static COMCOMP compen_setf4p = {NOKEYWORD, NOTOPLIST, NONEXTLIST, NOPARAMS,
111 	INPUTOPT, x_(" \t"), M_("Minimum feature size"), 0};
112 static COMCOMP compen_setp = {NOKEYWORD, NOTOPLIST, NONEXTLIST, NOPARAMS,
113 	INPUTOPT, x_(" \t"), M_("compensation percentage for the selected object(s)"), 0};
114 static KEYWORD compenopt[] =
115 {
116 	{x_("compensate"),                  0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
117 	{x_("uncompensate"),                0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
118 	{x_("detect-special-features"),     0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
119 	{x_("detect-duplicate-geometry"),   0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
120 	{x_("detect-layer-intersections"),  0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
121 	{x_("remove-percentage"),           0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
122 	{x_("next-contour-selection"),      0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
123 	{x_("prev-contour-selection"),      0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
124 	{x_("examine"),                     0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
125 	{x_("connect-points"),              0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
126 	{x_("percentage"),                  1,{&compen_setp,NOKEY,NOKEY,NOKEY,NOKEY}},
127 	{x_("global-factors"),              4,{&compen_setf1p,&compen_setf2p,&compen_setf3p,&compen_setf4p,NOKEY}},
128 	{x_("make-compensation-table"),     0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
129 	{x_("illuminate-percentages"),      0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
130 	{x_("show-contours"),               0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
131 	{x_("show-non-contours"),           0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
132 	{x_("draw-pre-compensation"),       0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
133 	TERMKEY
134 };
135 COMCOMP compen_compensatep = {compenopt, NOTOPLIST, NONEXTLIST, NOPARAMS,
136 	0, x_(" \t"), M_("Compensation action"), M_("show defaults")};
137 
138 /*********** for coloring artwork ***********/
139 
140 #define COLORS 14
141 static INTBIG compen_colors[COLORS+1] = {GREEN, CYAN, MAGENTA, YELLOW, ORANGE, PURPLE,
142 	LRED, LGREEN, LBLUE, LGRAY, DGREEN, GRAY, DRED, BROWN, 0};
143 static float compen_percentages[COLORS];
144 static GRAPHICS compen_rline = {LAYERO, RED, SOLIDC, SOLIDC,
145 	{0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,
146 	0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF}, NOVARIABLE, 0};
147 
148 /*********** additional data on contours ***********/
149 
150 typedef struct
151 {
152 	INTBIG      origsx, origsy;		/* starting coordinate (line, arc, circle) */
153 	INTBIG      origex, origey;		/* ending coordinate (line, arc) */
154 	INTBIG      lowx, highx;		/* X bounds of the element */
155 	INTBIG      lowy, highy;		/* Y bounds of the element */
156 	float       percentage;			/* compensation amount */
157 	INTBIG      explicitpercentage;	/* nonzero if percentage value was explicitly given */
158 	CHAR       *DXFlayer;			/* DXF layer */
159 } USERDATA;
160 
161 /*********** contour list associations ***********/
162 
163 #define NOCELLCONTOURS ((CELLCONTOURS *)-1)
164 
165 typedef struct Icellcontours
166 {
167 	NODEPROTO             *cell;	/* cell with a contours */
168 	CONTOUR               *contour;	/* contours associated with this cell */
169 	BOOLEAN                deleted;	/* nonzero if this has been deleted */
170 	struct Icellcontours  *next;	/* next in list */
171 } CELLCONTOURS;
172 
173 static CELLCONTOURS *compen_firstcellcontours = NOCELLCONTOURS;
174 static INTBIG         compen_deletedcellcontours = 0;
175 
176 /*********** miscellaneous information ***********/
177 
178 #define PRECOMPMAGIC	0		/* magic number (0xFEED) */
179 #define PRECOMPTYPE     1		/* type of element */
180 #define PRECOMPPERC     2		/* percentage of compensation */
181 #define PRECOMPEXPPERC  3		/* nonzero if compensation explicitly given */
182 
183 #define PRECOMPLINESX   4		/* starting X of line */
184 #define PRECOMPLINESY   5		/* starting Y of line */
185 #define PRECOMPLINEEX   6		/* ending X of line */
186 #define PRECOMPLINEEY   7		/* ending Y of line */
187 #define PRECOMPLINESIZE 8		/* size of line descriptor */
188 
189 #define PRECOMPCIRCSX   4		/* edge X of circle */
190 #define PRECOMPCIRCSY   5		/* edge Y of circle */
191 #define PRECOMPCIRCRAD  6		/* radius of circle */
192 #define PRECOMPCIRCSIZE 7		/* size of circle descriptor */
193 
194 #define PRECOMPARCSX    4		/* starting X of arc */
195 #define PRECOMPARCSY    5		/* starting Y of arc */
196 #define PRECOMPARCEX    6		/* ending X of arc */
197 #define PRECOMPARCEY    7		/* ending Y of arc */
198 #define PRECOMPARCRAD   8		/* radius of arc */
199 #define PRECOMPARCSIZE  9		/* size of arc descriptor */
200 
201 #define DEFAULTMETALTHICKNESS       0.076
202 #define DEFAULTLRSCOMPENSATION      0.0057912
203 #define DEFAULTGLOBALCOMPENSATION  95.0
204 #define DEFAULTMINIMUMSIZE          0.0505
205 
206 static TOOL      *compen_tool;
207 static INTBIG     compen_gds_layerkey;			/* key for "IO_gds_layer" */
208 static INTBIG     compen_dxf_layerkey;			/* key for "IO_dxf_layer" */
209 static INTBIG     compen_percentagekey;			/* key for "COMPEN_percentage" */
210 static INTBIG     compen_precomppositionkey;	/* key for "COMPEN_pre_compensation_position" */
211 static INTBIG     compen_metalthicknesskey;		/* key for "COMPEN_metalthickness" */
212 static INTBIG     compen_lrscompensationkey;	/* key for "COMPEN_lrscompensation" */
213 static INTBIG     compen_globalcompensationkey;	/* key for "COMPEN_globalcompensation" */
214 static INTBIG     compen_minimumsizekey;		/* key for "COMPEN_minimumsize" */
215 static INTBIG     compen_circletangentthresh;
216 
217 /*********** prototypes for local routines ***********/
218 
219 static void     compen_advanceselection(short next);
220 static INTBIG   compen_angoffset(double a1, double a2);
221 static BOOLEAN  compen_arcintersection(CONTOURELEMENT *arcconel, INTBIG x, INTBIG y, INTBIG otherx,
222 					INTBIG othery, INTBIG *ix, INTBIG *iy);
223 static BOOLEAN  compen_arctangent(CONTOURELEMENT *arcconel, INTBIG prefx, INTBIG prefy,
224 					CONTOURELEMENT *otherconel, INTBIG x, INTBIG y, INTBIG *ix, INTBIG *iy);
225 static void     compen_addchild(CONTOUR *parent, CONTOUR *child);
226 static void     compen_adjustgeometry(CONTOUR *contourlist, NODEPROTO *np);
227 static void     compen_assigndepth(CONTOUR *con, INTBIG depth);
228 static void     compen_assignpercentages(CONTOUR *contourlist, NODEPROTO *np);
229 static void     compen_cleancontours(CONTOUR *contourlist, NODEPROTO *np, BOOLEAN blend);
230 static void     compen_compensatecell(NODEPROTO *np);
231 static void     compen_connectpoints(void);
232 static INTBIG   compen_detectduplicates(NODEPROTO *np);
233 static INTBIG   compen_detectfeatures(NODEPROTO *np);
234 static void     compen_detectlayerintersections(NODEPROTO *np);
235 static void     compen_detectnoncontours(NODEPROTO *np);
236 static void     compen_dofactorsdialog(void);
237 static void     compen_drawcircle(INTBIG centerx, INTBIG centery, INTBIG x, INTBIG y, GRAPHICS *desc);
238 static void     compen_drawcirclearc(INTBIG centerx, INTBIG centery, INTBIG x1, INTBIG y1, INTBIG x2,
239 					INTBIG y2, GRAPHICS *desc);
240 static void     compen_drawcontours(NODEPROTO *np);
241 static void     compen_drawline(INTBIG x1, INTBIG y1, INTBIG x2, INTBIG y2, GRAPHICS *desc);
242 static void     compen_drawprecompensation(NODEPROTO *np, GEOM **list);
243 static void     compen_examine(void);
244 static double   compen_figureangle(INTBIG fx, INTBIG fy, INTBIG tx, INTBIG ty);
245 static void     compen_forcemeeting(CONTOUR *con, CONTOURELEMENT *firstconel, CONTOURELEMENT *secondconel,
246 					BOOLEAN blend);
247 static CONTOUR *compen_getcontourlist(NODEPROTO *np);
248 static void     compen_getglobalfactors(float *metalthickness, float *lrscompensation,
249 					float *globalcompensation, float *minimumsize, LIBRARY *lib);
250 static void     compen_illuminatepercentages(BOOLEAN showonside);
251 static void     compen_insertbridge(CONTOURELEMENT *firstconel, CONTOURELEMENT *secondconel);
252 static BOOLEAN  compen_insert15degreesegment(INTBIG linesx, INTBIG linesy, INTBIG *lineex, INTBIG *lineey,
253 					INTBIG fromx, INTBIG fromy);
254 static void     compen_insertcontour(CONTOUR *newone, CONTOUR *toplevel);
255 static BOOLEAN  compen_intersect(INTBIG x1, INTBIG y1, double fang1, INTBIG x2, INTBIG y2, double fang2,
256 					INTBIG *x, INTBIG *y);
257 static BOOLEAN  compen_isinside(CONTOUR *lower, CONTOUR *higher);
258 static void     compen_movegeometry(CONTOUR *contourlist, NODEPROTO *np);
259 static void     compen_ordercontours(CONTOUR *contourlist, NODEPROTO *np);
260 static void     compen_orientcontours(CONTOUR *contourlist, NODEPROTO *np);
261 static INTBIG   compen_pointoffarc(CONTOURELEMENT *arcconel, INTBIG x, INTBIG y);
262 static void     compen_printdistance(double px, double py, INTBIG fx, INTBIG fy, INTBIG tx, INTBIG ty);
263 static void     compen_removecellcontours(NODEPROTO *np);
264 static void     compen_setglobalfactors(float metalthickness, float lrscompensation,
265 					float globalcompensation, float minimumsize, LIBRARY *lib);
266 static void     compen_setnodecompensation(NODEINST *ni, float amt, INTBIG mode);
267 static void     compen_straighten(CONTOURELEMENT *firstconel, CONTOURELEMENT *secondconel);
268 static float    compen_truecompensation(float percentage, float metalthickness, float lrscompensation);
269 static void     compen_uncompensatecell(NODEPROTO *np);
270 
271 /******************************** CONTROL ********************************/
272 
compen_init(INTBIG * argc,CHAR1 * argv[],TOOL * thistool)273 void compen_init(INTBIG *argc, CHAR1 *argv[], TOOL *thistool)
274 {
275 	/* miscellaneous initialization during pass 3 */
276 	if (thistool == 0)
277 	{
278 		compen_percentagekey = makekey(x_("COMPEN_percentage"));
279 		compen_precomppositionkey = makekey(x_("COMPEN_pre_compensation_position"));
280 		compen_globalcompensationkey = makekey(x_("COMPEN_globalcompensation"));
281 		compen_metalthicknesskey = makekey(x_("COMPEN_metalthickness"));
282 		compen_lrscompensationkey = makekey(x_("COMPEN_lrscompensation"));
283 		compen_minimumsizekey = makekey(x_("COMPEN_minimumsize"));
284 		compen_gds_layerkey = makekey(x_("IO_gds_layer"));
285 		compen_dxf_layerkey = makekey(x_("IO_dxf_layer"));
286 		compen_circletangentthresh = scalefromdispunit((float)CIRCLETANGENTTHRESH, DISPUNITMM);
287 		return;
288 	}
289 
290 	/* ignore pass 2 */
291 	if (thistool == NOTOOL) return;
292 
293 	/* copy tool pointer during pass 1 */
294 	compen_tool = thistool;
295 }
296 
compen_set(INTBIG count,CHAR * par[])297 void compen_set(INTBIG count, CHAR *par[])
298 {
299 	REGISTER INTBIG l, i, mode;
300 	REGISTER CHAR *pp;
301 	REGISTER NODEPROTO *np;
302 	REGISTER GEOM **list;
303 	float per1, per2, per3, comp1peredge, comp2peredge, comp3peredge, amt,
304 		metalthickness, lrscompensation, globalcompensation, minimumsize;
305 
306 	l = estrlen(pp = par[0]);
307 	if (namesamen(pp, x_("compensate"), l) == 0)
308 	{
309 		np = getcurcell();
310 		if (np == NONODEPROTO)
311 		{
312 			ttyputerr(M_("No current cell"));
313 			return;
314 		}
315 		compen_compensatecell(np);
316 		return;
317 	}
318 	if (namesamen(pp, x_("connect-points"), l) == 0)
319 	{
320 		compen_connectpoints();
321 		return;
322 	}
323 	if (namesamen(pp, x_("detect-duplicate-geometry"), l) == 0 && l >= 8)
324 	{
325 		np = getcurcell();
326 		if (np == NONODEPROTO)
327 		{
328 			ttyputerr(M_("No current cell"));
329 			return;
330 		}
331 		i = compen_detectduplicates(np);
332 		if (i == 0) ttyputmsg(M_("No duplicate nodes found")); else
333 			ttyputmsg(M_("Detected %ld duplicate %s"), i, makeplural(M_("node"), i));
334 		return;
335 	}
336 	if (namesamen(pp, x_("detect-layer-intersections"), l) == 0 && l >= 8)
337 	{
338 		np = getcurcell();
339 		if (np == NONODEPROTO)
340 		{
341 			ttyputerr(M_("No current cell"));
342 			return;
343 		}
344 		compen_detectlayerintersections(np);
345 		return;
346 	}
347 	if (namesamen(pp, x_("detect-special-features"), l) == 0 && l >= 8)
348 	{
349 		np = getcurcell();
350 		if (np == NONODEPROTO)
351 		{
352 			ttyputerr(M_("No current cell"));
353 			return;
354 		}
355 		i = compen_detectfeatures(np);
356 		if (i == 0) ttyputmsg(M_("No features found")); else
357 		{
358 			compen_illuminatepercentages(TRUE);
359 			ttyputmsg(M_("Detected %ld %s"), i, makeplural(M_("feature"), i));
360 		}
361 		return;
362 	}
363 	if (namesamen(pp, x_("draw-pre-compensation"), l) == 0)
364 	{
365 		np = getcurcell();
366 		if (np == NONODEPROTO)
367 		{
368 			ttyputerr(M_("No current cell"));
369 			return;
370 		}
371 		list = us_gethighlighted(WANTNODEINST, 0, 0);
372 		compen_drawprecompensation(np, list);
373 		return;
374 	}
375 	if (namesamen(pp, x_("examine"), l) == 0 && l >= 1)
376 	{
377 		compen_examine();
378 		return;
379 	}
380 	if (namesamen(pp, x_("global-factors"), l) == 0)
381 	{
382 		if (count == 5)
383 		{
384 			metalthickness = (float)eatof(par[1]);
385 			lrscompensation = (float)eatof(par[2]);
386 			globalcompensation = (float)eatof(par[3]);
387 			minimumsize = (float)eatof(par[4]);
388 			compen_setglobalfactors(metalthickness, lrscompensation, globalcompensation, minimumsize,
389 				el_curlib);
390 		} else if (count == 2 && namesame(par[1], x_("dialog")) == 0)
391 		{
392 			compen_dofactorsdialog();
393 			return;
394 		} else if (count != 0)
395 		{
396 			ttyputusage(x_("telltool compensation global-factors ([dialog] | [METAL-THICKNESS LRS-COMPENSATION GLOBAL-COMPENSATION MINIMUM-FEATURE-SIZE])"));
397 			return;
398 		}
399 		compen_getglobalfactors(&metalthickness, &lrscompensation, &globalcompensation, &minimumsize,
400 			el_curlib);
401 		ttyputmsg(M_("Metal thickness is %g; LRS Compensation is %g; Global Compensation is %g; Minimum feature size is %g"),
402 			metalthickness, lrscompensation, globalcompensation, minimumsize);
403 		return;
404 	}
405 	if (namesamen(pp, x_("illuminate-percentages"), l) == 0)
406 	{
407 		compen_illuminatepercentages(TRUE);
408 		return;
409 	}
410 	if (namesamen(pp, x_("make-compensation-table"), l) == 0)
411 	{
412 		compen_getglobalfactors(&metalthickness, &lrscompensation, &globalcompensation, &minimumsize,
413 			el_curlib);
414 		ttyputmsg(M_("METRIC"));
415 		ttyputmsg(x_(" "));
416 		ttyputmsg(M_("Metal Thickness is %g        LRS Comp is %g (mm total), %g (mm per side)"),
417 			metalthickness, lrscompensation, lrscompensation/2.0);
418 		ttyputmsg(x_(" "));
419 		ttyputmsg(M_("ETCH    ADJUSTED 1/2 COMP        ETCH    ADJUSTED 1/2 COMP        ETCH    ADJUSTED 1/2 COMP"));
420 		ttyputmsg(M_("COMP    APPLIED PER EDGE         COMP    APPLIED PER EDGE         COMP    APPLIED PER EDGE"));
421 		for(i=165; i >= 124; i--)
422 		{
423 			per1 = (float)i;	per2 = (float)(i-42);	per3 = (float)(i-84);
424 			comp1peredge = compen_truecompensation(per1, metalthickness, lrscompensation);
425 			comp2peredge = compen_truecompensation(per2, metalthickness, lrscompensation);
426 			comp3peredge = compen_truecompensation(per3, metalthickness, lrscompensation);
427 			ttyputmsg(x_("%3g%%        %.6f             %3g%%        %.6f             %3g%%        %.6f"),
428 				per1, comp1peredge, per2, comp2peredge, per3, comp3peredge);
429 		}
430 		return;
431 	}
432 	if (namesamen(pp, x_("next-contour-selection"), l) == 0)
433 	{
434 		compen_advanceselection(1);
435 		return;
436 	}
437 	if (namesamen(pp, x_("percentage"), l) == 0 && l >= 2)
438 	{
439 		if (count > 2)
440 		{
441 			ttyputusage(x_("telltool compensation percentage [PERCENTAGE]"));
442 			return;
443 		}
444 		if (count == 1) mode = 1; else
445 		{
446 			mode = 0;
447 			amt = (float)eatof(par[1]);
448 			if (amt < 0.0)
449 			{
450 				ttyputerr(M_("Cannot set negative percentages of compensation"));
451 				return;
452 			}
453 		}
454 		list = us_gethighlighted(WANTNODEINST, 0, 0);
455 		if (list[0] == NOGEOM)
456 		{
457 			ttyputerr(M_("Select nodes before setting compensation percentages"));
458 			return;
459 		}
460 		for(i=0; list[i] != NOGEOM; i++)
461 			compen_setnodecompensation(list[i]->entryaddr.ni, amt, mode);
462 		if (mode == 0) compen_illuminatepercentages(FALSE);
463 		return;
464 	}
465 	if (namesamen(pp, x_("prev-contour-selection"), l) == 0 && l >= 2)
466 	{
467 		compen_advanceselection(0);
468 		return;
469 	}
470 	if (namesamen(pp, x_("remove-percentage"), l) == 0)
471 	{
472 		list = us_gethighlighted(WANTNODEINST, 0, 0);
473 		if (list[0] == NOGEOM)
474 		{
475 			ttyputerr(M_("Select nodes before removing compensation percentages"));
476 			return;
477 		}
478 		for(i=0; list[i] != NOGEOM; i++)
479 			compen_setnodecompensation(list[i]->entryaddr.ni, 0.0, -1);
480 		compen_illuminatepercentages(FALSE);
481 		return;
482 	}
483 	if (namesamen(pp, x_("show-contours"), l) == 0 && l >= 6)
484 	{
485 		np = getcurcell();
486 		if (np == NONODEPROTO)
487 		{
488 			ttyputerr(M_("No current cell"));
489 			return;
490 		}
491 		compen_drawcontours(np);
492 		return;
493 	}
494 	if (namesamen(pp, x_("show-non-contours"), l) == 0 && l >= 6)
495 	{
496 		np = getcurcell();
497 		if (np == NONODEPROTO)
498 		{
499 			ttyputerr(M_("No current cell"));
500 			return;
501 		}
502 		compen_detectnoncontours(np);
503 		return;
504 	}
505 	if (namesamen(pp, x_("uncompensate"), l) == 0)
506 	{
507 		np = getcurcell();
508 		if (np == NONODEPROTO)
509 		{
510 			ttyputerr(M_("No current cell"));
511 			return;
512 		}
513 		compen_uncompensatecell(np);
514 		return;
515 	}
516 	ttyputbadusage(x_("telltool compensation"));
517 }
518 
compen_done(void)519 void compen_done(void) {}
520 
compen_slice(void)521 void compen_slice(void)
522 {
523 	REGISTER CELLCONTOURS *fc, *lastfc, *nextfc;
524 	REGISTER CONTOUR *con, *nextcon;
525 	REGISTER CONTOURELEMENT *conel;
526 	REGISTER USERDATA *ud;
527 
528 	if (compen_deletedcellcontours == 0) return;
529 	lastfc = NOCELLCONTOURS;
530 	for(fc = compen_firstcellcontours; fc != NOCELLCONTOURS; fc = nextfc)
531 	{
532 		nextfc = fc->next;
533 		if (fc->deleted)
534 		{
535 			/* kill the contour information */
536 			for(con = fc->contour; con != NOCONTOUR; con = nextcon)
537 			{
538 				nextcon = con->nextcontour;
539 				for(conel = con->firstcontourelement; conel != NOCONTOURELEMENT; conel = conel->nextcontourelement)
540 				{
541 					ud = (USERDATA *)conel->userdata;
542 					if (ud->DXFlayer != 0) efree(ud->DXFlayer);
543 					efree((CHAR *)ud);
544 				}
545 				killcontour(con);
546 			}
547 
548 			/* remove from the list and deallocate */
549 			if (lastfc == NOCELLCONTOURS) compen_firstcellcontours = fc->next; else
550 				lastfc->next = fc->next;
551 			efree((CHAR *)fc);
552 			continue;
553 		}
554 		lastfc = fc;
555 	}
556 	compen_deletedcellcontours = 0;
557 }
558 
559 /******************************** DATABASE CHANGES ********************************/
560 
compen_modifynodeinst(NODEINST * ni,INTBIG olx,INTBIG oly,INTBIG ohx,INTBIG ohy,INTBIG orot,INTBIG otran)561 void compen_modifynodeinst(NODEINST *ni, INTBIG olx, INTBIG oly, INTBIG ohx, INTBIG ohy,
562 	INTBIG orot, INTBIG otran)
563 {
564 	/* if a node changes, force recaching of the contour information */
565 	compen_removecellcontours(ni->parent);
566 }
567 
compen_killobject(INTBIG addr,INTBIG type)568 void compen_killobject(INTBIG addr, INTBIG type)
569 {
570 	/* if a node is deleted, force recaching of the contour information */
571 	if (type == VNODEINST)
572 		compen_removecellcontours(((NODEINST *)addr)->parent);
573 }
574 
575 /******************************** COMMANDS ********************************/
576 
577 /*
578  * Routine to describe the currently selected object(s).
579  */
compen_examine(void)580 void compen_examine(void)
581 {
582 	REGISTER VARIABLE *var;
583 	REGISTER NODEINST *ni;
584 	REGISTER INTBIG len, cx, cy, radius;
585 	REGISTER INTBIG i, j, prev, first;
586 	INTBIG bx, by, p1x, p1y, p2x, p2y, fx, fy, tx, ty;
587 	REGISTER GEOM **list;
588 	HIGHLIGHT thishigh, otherhigh;
589 	double startoffset, endangle, rot;
590 	XARRAY trans;
591 	REGISTER void *infstr;
592 
593 	/* special case when two objects are highlighted */
594 	var = getvalkey((INTBIG)us_tool, VTOOL, VSTRING|VISARRAY, us_highlightedkey);
595 	if (var != NOVARIABLE)
596 	{
597 		len = getlength(var);
598 		if (len == 2)
599 		{
600 			if (!us_makehighlight(((CHAR **)var->addr)[0], &thishigh) &&
601 				!us_makehighlight(((CHAR **)var->addr)[1], &otherhigh))
602 			{
603 				/* describe these two objects */
604 				if ((thishigh.status&HIGHSNAP) != 0 && (otherhigh.status&HIGHSNAP) != 0)
605 				{
606 					us_getsnappoint(&thishigh, &p1x, &p1y);
607 					us_getsnappoint(&otherhigh, &p2x, &p2y);
608 					ttyputmsg(M_("Distance between snap points is %s (%s in X and %s in Y)"),
609 						latoa(computedistance(p1x,p1y, p2x,p2y), 0), latoa(labs(p2x-p1x), 0), latoa(labs(p2y-p1y), 0));
610 					return;
611 				}
612 			}
613 		}
614 	}
615 
616 	/* get what is highlighted */
617 	list = us_gethighlighted(WANTNODEINST, 0, 0);
618 	if (list[0] == NOGEOM)
619 	{
620 		ttyputerr(M_("Nothing is selected"));
621 		return;
622 	}
623 
624 	/* describe each selected object */
625 	for(i=0; list[i] != NOGEOM; i++)
626 	{
627 		if (!list[i]->entryisnode) continue;
628 		ni = list[i]->entryaddr.ni;
629 
630 		if (ni->proto->primindex == 0)
631 		{
632 			/* describe instance */
633 			infstr = initinfstr();
634 			formatinfstr(infstr, M_("Instance of %s"), describenodeinst(ni));
635 			ttyputmsg(x_("%s"), returninfstr(infstr));
636 			corneroffset(ni, ni->proto, ni->rotation, ni->transpose, &bx, &by, FALSE);
637 			bx += ni->lowx;   by += ni->lowy;
638 			ttyputmsg(M_("   at point (%s, %s)"), latoa(bx, 0), latoa(by, 0));
639 			ttyputmsg(M_("   rotation = %d"), (ni->rotation+5)/10);
640 			continue;
641 		}
642 
643 		/* describe it */
644 		infstr = initinfstr();
645 		if (ni->proto == art_openedpolygonprim || ni->proto == art_closedpolygonprim)
646 		{
647 			addstringtoinfstr(infstr, M_("Line"));
648 		} else if (ni->proto == art_circleprim || ni->proto == art_thickcircleprim)
649 		{
650 			getarcdegrees(ni, &startoffset, &endangle);
651 			if (startoffset == 0.0 && endangle == 0.0) addstringtoinfstr(infstr, M_("Circle")); else
652 				addstringtoinfstr(infstr, M_("Arc"));
653 		} else if (ni->proto == gen_invispinprim)
654 		{
655 			addstringtoinfstr(infstr, M_("Text"));
656 		} else if (ni->proto == gen_cellcenterprim)
657 		{
658 			addstringtoinfstr(infstr, M_("Cell center"));
659 		} else
660 		{
661 			addstringtoinfstr(infstr, M_("Unknown object"));
662 		}
663 
664 		var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, el_node_name_key);
665 		if (var != NOVARIABLE)
666 			formatinfstr(infstr, x_(" [%s]"), (CHAR *)var->addr);
667 		var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, compen_dxf_layerkey);
668 		if (var != NOVARIABLE)
669 			formatinfstr(infstr, M_(", DXF Layer: %s"), (CHAR *)var->addr);
670 		var = getvalkey((INTBIG)ni, VNODEINST, VINTEGER, compen_gds_layerkey);
671 		if (var != NOVARIABLE)
672 			formatinfstr(infstr, M_(", GDS Layer: %ld"), var->addr);
673 		ttyputmsg(x_("%s"), returninfstr(infstr));
674 
675 		cx = (ni->lowx + ni->highx) / 2;
676 		cy = (ni->lowy + ni->highy) / 2;
677 		if (ni->proto == art_openedpolygonprim || ni->proto == art_closedpolygonprim)
678 		{
679 			var = gettrace(ni);
680 			if (var != NOVARIABLE)
681 			{
682 				len = getlength(var);
683 				makerot(ni, trans);
684 				first = 1;
685 				for(j=0; j<len; j += 2)
686 				{
687 					if (j == 0)
688 					{
689 						if (ni->proto == art_openedpolygonprim) continue;
690 						prev = len - 2;
691 					} else prev = j - 2;
692 					xform(((INTBIG *)var->addr)[prev]+cx, ((INTBIG *)var->addr)[prev+1]+cy, &fx, &fy, trans);
693 					xform(((INTBIG *)var->addr)[j]+cx, ((INTBIG *)var->addr)[j+1]+cy, &tx, &ty, trans);
694 					if (first != 0) ttyputmsg(M_("   from point (%s, %s)"), latoa(fx, 0), latoa(fy, 0));
695 					first = 0;
696 					ttyputmsg(M_("     to point (%s, %s)"), latoa(tx, 0), latoa(ty, 0));
697 					if (ty == fy && tx == fx)
698 					{
699 						ttyputerr(M_("Domain error examining line"));
700 						break;
701 					}
702 					rot = atan2((double)(ty-fy), (double)(tx-fx));
703 					if (rot < 0.0) rot += EPI*2.0;
704 					ttyputmsg(M_("       length = %s,  angle = %g"), latoa(computedistance(fx,fy,tx,ty), 0), rot*180.0/EPI);
705 					ttyputmsg(M_("       delta X = %s,  delta Y = %s"), latoa(tx-fx, 0), latoa(ty-fy, 0));
706 				}
707 			}
708 		} else if (ni->proto == art_circleprim || ni->proto == art_thickcircleprim)
709 		{
710 			getarcdegrees(ni, &startoffset, &endangle);
711 			if (startoffset == 0.0 && endangle == 0.0)
712 			{
713 				ttyputmsg(M_("   center point (%s, %s)"), latoa(cx, 0), latoa(cy, 0));
714 				radius = (ni->highx - ni->lowx) / 2;
715 				ttyputmsg(M_("          radius = %s"), latoa(radius, 0));
716 				ttyputmsg(M_("        diameter = %s"), latoa(radius*2, 0));
717 				ttyputmsg(M_("   circumference = %s"), latoa(roundfloat((float)(radius*2.0*EPI)), 0));
718 			} else
719 			{
720 				ttyputmsg(M_("   center point (%s, %s)"), latoa(cx, 0), latoa(cy, 0));
721 				radius = (ni->highx - ni->lowx) / 2;
722 				ttyputmsg(M_("        radius = %s"), latoa(radius, 0));
723 				startoffset += ((double)ni->rotation) * EPI / 1800.0;
724 				if (ni->transpose != 0)
725 				{
726 					startoffset = 1.5 * EPI - startoffset - endangle;
727 					if (startoffset < 0.0) startoffset += EPI * 2.0;
728 				}
729 				fx = cx + rounddouble(cos(startoffset) * (double)radius);
730 				fy = cy + rounddouble(sin(startoffset) * (double)radius);
731 				tx = cx + rounddouble(cos(startoffset+endangle) * (double)radius);
732 				ty = cy + rounddouble(sin(startoffset+endangle) * (double)radius);
733 				ttyputmsg(M_("   start angle = %g, point (%s, %s)"), startoffset*180.0/EPI,
734 					latoa(fx, 0), latoa(fy, 0));
735 				ttyputmsg(M_("     end angle = %g, point (%s, %s)"), (startoffset+endangle)*180.0/EPI,
736 					latoa(tx, 0), latoa(ty, 0));
737 			}
738 		} else
739 		{
740 			ttyputmsg(M_("   center point (%s, %s)"), latoa(cx, 0), latoa(cy, 0));
741 		}
742 
743 		var = getvalkey((INTBIG)ni, VNODEINST, VFLOAT, compen_percentagekey);
744 		if (var == NOVARIABLE)
745 			ttyputmsg(M_("   No compensation value set")); else
746 				ttyputmsg(M_("   Has %g%% compensation set on it"), castfloat(var->addr));
747 	}
748 }
749 
750 /*
751  * Routine to move the selection to the next (if "next" is nonzero) or previous one
752  * in the contour.
753  */
compen_advanceselection(short next)754 void compen_advanceselection(short next)
755 {
756 	REGISTER NODEPROTO *np;
757 	REGISTER NODEINST *ni;
758 	REGISTER CONTOUR *con, *contourlist;
759 	REGISTER CONTOURELEMENT *conel, *prevconel, *thisconel;
760 	HIGHLIGHT newhigh;
761 
762 	ni = (NODEINST *)us_getobject(VNODEINST, FALSE);
763 	if (ni == NONODEINST)
764 	{
765 		ttyputerr(M_("Must select a single node before advancing to next in contour"));
766 		return;
767 	}
768 
769 	/* make sure there are contours for this cell */
770 	np = getcurcell();
771 	contourlist = compen_getcontourlist(np);
772 	if (contourlist == NOCONTOUR)
773 	{
774 		ttyputerr(M_("Sorry, no contour information can be found in this cell"));
775 		return;
776 	}
777 
778 	/* find the node in the contour list */
779 	for(con = contourlist; con != NOCONTOUR; con = con->nextcontour)
780 	{
781 		for(conel = con->firstcontourelement; conel != NOCONTOURELEMENT; conel = conel->nextcontourelement)
782 			if (conel->ni == ni) break;
783 		if (conel != NOCONTOURELEMENT) break;
784 	}
785 	if (con == NOCONTOUR)
786 	{
787 		ttyputerr(M_("This node is not in a contour"));
788 		return;
789 	}
790 
791 	if (next != 0)
792 	{
793 		/* get next contour element */
794 		for(;;)
795 		{
796 			conel = conel->nextcontourelement;
797 			if (conel == NOCONTOURELEMENT) conel = con->firstcontourelement;
798 			if (conel->ni != NONODEINST) break;
799 		}
800 	} else
801 	{
802 		/* get previous contour element */
803 		for(;;)
804 		{
805 			prevconel = NOCONTOURELEMENT;
806 			for(thisconel = con->firstcontourelement; thisconel != NOCONTOURELEMENT;
807 				thisconel = thisconel->nextcontourelement)
808 			{
809 				if (thisconel == conel) break;
810 				prevconel = thisconel;
811 			}
812 			conel = prevconel;
813 			if (prevconel == NOCONTOURELEMENT)
814 			{
815 				for(thisconel = con->firstcontourelement; thisconel != NOCONTOURELEMENT;
816 					thisconel = thisconel->nextcontourelement) conel = thisconel;
817 			}
818 
819 			if (conel->ni != NONODEINST) break;
820 		}
821 	}
822 
823 	/* highlight it */
824 	newhigh.status = HIGHFROM;
825 	newhigh.fromgeom = conel->ni->geom;
826 	newhigh.fromport = conel->ni->proto->firstportproto;
827 	newhigh.fromvar = NOVARIABLE;
828 	newhigh.fromvarnoeval = NOVARIABLE;
829 	newhigh.frompoint = 0;
830 	newhigh.cell = conel->ni->parent;
831 	us_setfind(&newhigh, 0, 0, 0, 1);
832 }
833 
compen_drawprecompensation(NODEPROTO * np,GEOM ** list)834 void compen_drawprecompensation(NODEPROTO *np, GEOM **list)
835 {
836 	REGISTER NODEINST *ni;
837 	REGISTER VARIABLE *var, *vartrace;
838 	INTBIG fx, fy, tx, ty;
839 	REGISTER INTBIG i, total, badprecomps, goodprecomps;
840 	double px, py, startoffset, endangle;
841 	REGISTER INTBIG cx, cy, oldradius, newradius, *precomparray, compamt, afx, afy, atx, aty;
842 
843 	if (list[0] == NOGEOM)
844 	{
845 		/* nothing in list: select all */
846 		for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
847 			ni->temp1 = 1;
848 	} else
849 	{
850 		/* mark only the desired nodes */
851 		for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
852 			ni->temp1 = 0;
853 		for(i=0; list[i] != NOGEOM; i++)
854 			list[i]->entryaddr.ni->temp1 = 1;
855 	}
856 	total = 0;
857 	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
858 		if (ni->temp1 != 0) total++;
859 
860 	compen_rline.col = RED;
861 	badprecomps = goodprecomps = 0;
862 	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
863 	{
864 		if (ni->temp1 == 0) continue;
865 		var = getvalkey((INTBIG)ni, VNODEINST, VINTEGER|VISARRAY, compen_precomppositionkey);
866 		if (var == NOVARIABLE) continue;
867 		precomparray = (INTBIG *)var->addr;
868 		if (precomparray[PRECOMPMAGIC] != 0xFEED)
869 		{
870 			badprecomps = 1;
871 			(void)delvalkey((INTBIG)ni, VNODEINST, compen_precomppositionkey);
872 			continue;
873 		}
874 		goodprecomps = 1;
875 		compamt = precomparray[PRECOMPPERC];
876 		switch (precomparray[PRECOMPTYPE])
877 		{
878 			case ARCSEGMENTTYPE:
879 			case REVARCSEGMENTTYPE:
880 				if (total == 1)
881 				{
882 					/* display difference information */
883 					getarcdegrees(ni, &startoffset, &endangle);
884 					if (startoffset != 0.0 || endangle != 0.0)
885 					{
886 						cx = (ni->lowx + ni->highx) / 2;   cy = (ni->lowy + ni->highy) / 2;
887 						fx = precomparray[PRECOMPARCSX];
888 						fy = precomparray[PRECOMPARCSY];
889 						tx = precomparray[PRECOMPARCEX];
890 						ty = precomparray[PRECOMPARCEY];
891 						oldradius = precomparray[PRECOMPARCRAD];
892 						ttyputmsg(M_("Arc ran from (%s,%s) to (%s,%s) with radius %s before compensation"),
893 							latoa(fx, 0), latoa(fy, 0), latoa(tx, 0), latoa(ty, 0), latoa(oldradius, 0));
894 						newradius = (ni->highx - ni->lowx) / 2;
895 						getarcendpoints(ni, startoffset, endangle, &fx, &fy, &tx, &ty);
896 						ttyputmsg(M_("        from (%s,%s) to (%s,%s) with radius %s after compensation"),
897 							latoa(fx, 0), latoa(fy, 0), latoa(tx, 0), latoa(ty, 0), latoa(newradius, 0));
898 						ttyputmsg(M_("Compensated %ld%%, radius changed by %s"), compamt,
899 							latoa(labs(newradius-oldradius), 0));
900 					}
901 				}
902 				if (precomparray[PRECOMPTYPE] == ARCSEGMENTTYPE)
903 				{
904 					compen_drawcirclearc((ni->lowx + ni->highx) / 2, (ni->lowy + ni->highy) / 2,
905 						precomparray[PRECOMPARCEX], precomparray[PRECOMPARCEY], precomparray[PRECOMPARCSX],
906 							precomparray[PRECOMPARCSY], &compen_rline);
907 				} else
908 				{
909 					compen_drawcirclearc((ni->lowx + ni->highx) / 2, (ni->lowy + ni->highy) / 2,
910 						precomparray[PRECOMPARCSX], precomparray[PRECOMPARCSY], precomparray[PRECOMPARCEX],
911 							precomparray[PRECOMPARCEY], &compen_rline);
912 				}
913 				break;
914 
915 			case CIRCLESEGMENTTYPE:
916 				cx = (ni->lowx + ni->highx) / 2;   cy = (ni->lowy + ni->highy) / 2;
917 				if (total == 1)
918 				{
919 					/* display difference information */
920 					oldradius = precomparray[PRECOMPCIRCRAD];
921 					newradius = computedistance(cx, cy, ni->highx, cy);
922 					ttyputmsg(M_("Circle had %s radius before compensation"), latoa(oldradius, 0));
923 					ttyputmsg(M_("           %s radius after compensation"), latoa(newradius, 0));
924 					ttyputmsg(M_("Compensated %ld%%, radius changed by %s"), compamt,
925 						latoa(labs(newradius-oldradius), 0));
926 				}
927 				compen_drawcircle(cx, cy, precomparray[PRECOMPCIRCSX],
928 					precomparray[PRECOMPCIRCSY], &compen_rline);
929 				break;
930 
931 			case LINESEGMENTTYPE:
932 			case BRIDGESEGMENTTYPE:
933 				if (total == 1)
934 				{
935 					/* display difference information */
936 					vartrace = gettrace(ni);
937 					if (vartrace != NOVARIABLE)
938 					{
939 						fx = precomparray[PRECOMPLINESX];
940 						fy = precomparray[PRECOMPLINESY];
941 						tx = precomparray[PRECOMPLINEEX];
942 						ty = precomparray[PRECOMPLINEEY];
943 						ttyputmsg(M_("Line ran from (%s,%s) to (%s,%s) before compensation"), latoa(fx, 0),
944 							latoa(fy, 0), latoa(tx, 0), latoa(ty, 0));
945 						cx = (ni->lowx + ni->highx) / 2;   cy = (ni->lowy + ni->highy) / 2;
946 						afx = ((INTBIG *)vartrace->addr)[0] + cx;
947 						afy = ((INTBIG *)vartrace->addr)[1] + cy;
948 						atx = ((INTBIG *)vartrace->addr)[2] + cx;
949 						aty = ((INTBIG *)vartrace->addr)[3] + cy;
950 						ttyputmsg(M_("         from (%s,%s) to (%s,%s) after compensation"), latoa(afx, 0),
951 							latoa(afy, 0), latoa(atx, 0), latoa(aty, 0));
952 						px = ((double)(fx+tx)) * 0.5;   py = ((double)(fy+ty)) * 0.5;
953 						compen_printdistance(px, py, afx, afy, atx, aty);
954 						ttyputmsg(M_("Compensated %ld%%"), compamt);
955 					}
956 				}
957 				compen_drawline(precomparray[PRECOMPLINESX], precomparray[PRECOMPLINESY], precomparray[PRECOMPLINEEX],
958 					precomparray[PRECOMPLINEEY], &compen_rline);
959 				break;
960 		}
961 	}
962 	if (badprecomps != 0)
963 	{
964 		ttyputerr(M_("Sorry, some precompensation information is obsolete"));
965 		return;
966 	}
967 	if (goodprecomps == 0)
968 		ttyputmsg(M_("No precompensation information is available"));
969 }
970 
971 /*
972  * Routine to compensate cell "uncompnp" and create a new, compensated one.
973  */
compen_compensatecell(NODEPROTO * uncompnp)974 void compen_compensatecell(NODEPROTO *uncompnp)
975 {
976 	REGISTER CONTOUR *con, *contourlist;
977 	REGISTER VARIABLE *var;
978 	REGISTER NODEINST *ni;
979 	REGISTER NODEPROTO *np;
980 	REGISTER INTBIG total;
981 	CHAR *par[2], *compname;
982 	REGISTER void *infstr;
983 
984 	/* see if this cell is already compensated */
985 	if (uncompnp->cellview == el_compview)
986 	{
987 		ttyputerr(M_("This cell is already compensated"));
988 		return;
989 	}
990 	for(ni = uncompnp->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
991 	{
992 		var = getvalkey((INTBIG)ni, VNODEINST, VINTEGER|VISARRAY, compen_precomppositionkey);
993 		if (var == NOVARIABLE) continue;
994 		ttyputerr(M_("This cell is already compensated"));
995 		(void)changecellview(uncompnp, el_compview);
996 		return;
997 	}
998 
999 	/* see if there is a compensated cell that is newer */
1000 	FOR_CELLGROUP(np, uncompnp)
1001 		if (np->cellview == el_compview && np->revisiondate > uncompnp->revisiondate)
1002 	{
1003 		par[0] = describenodeproto(np);
1004 		us_editcell(1, par);
1005 		return;
1006 	}
1007 
1008 #ifdef DEBDUMP
1009 	{
1010 		CHAR *truename;
1011 		compen_io = xcreate(x_("compen dump"), el_filetypetext, 0, &truename);
1012 	}
1013 #endif
1014 	ttyputmsg(M_("Compensating..."));
1015 
1016 	/* make a copy with the appropriate view */
1017 	infstr = initinfstr();
1018 	addstringtoinfstr(infstr, uncompnp->protoname);
1019 	addstringtoinfstr(infstr, x_("{comp}"));
1020 	compname = returninfstr(infstr);
1021 	np = getnodeproto(compname);
1022 	if (np != NONODEPROTO)
1023 	{
1024 		(void)killnodeproto(np);
1025 	}
1026 	np = copynodeproto(uncompnp, uncompnp->lib, compname, FALSE);
1027 	if (np == NONODEPROTO)
1028 	{
1029 		ttyputerr(M_("Could not create compensated view of this cell"));
1030 		return;
1031 	}
1032 
1033 	/* recompute bounds */
1034 	(*el_curconstraint->solve)(np);
1035 
1036 	/* collect contours in this cell */
1037 	contourlist = compen_getcontourlist(np);
1038 	if (contourlist == NOCONTOUR) return;
1039 
1040 	/* compute bounding boxes */
1041 	total = 0;
1042 	for(con = contourlist; con != NOCONTOUR; con = con->nextcontour)
1043 	{
1044 		getcontourbbox(con, &con->lx, &con->hx, &con->ly, &con->hy);
1045 		total++;
1046 	}
1047 
1048 	/* make sure all ends meet exactly */
1049 	compen_cleancontours(contourlist, np, FALSE);
1050 
1051 	/* make sure the contours go in the same direction */
1052 	compen_orientcontours(contourlist, np);
1053 
1054 	/* order the contours */
1055 	compen_ordercontours(contourlist, np);
1056 
1057 	/* assign percentages and compensate */
1058 	compen_assignpercentages(contourlist, np);
1059 	compen_movegeometry(contourlist, np);
1060 
1061 	/* blend endpoints correctly */
1062 	compen_cleancontours(contourlist, np, TRUE);
1063 
1064 	compen_adjustgeometry(contourlist, np);
1065 	ttyputmsg(M_("...Done Compensating"));
1066 
1067 #ifdef DEBDUMP
1068 	xclose(compen_io);
1069 #endif
1070 
1071 	/* show the compensated cell */
1072 	par[0] = describenodeproto(np);
1073 	us_editcell(1, par);
1074 }
1075 
1076 /*
1077  * Routine to uncompensate cell "np" by reverting to the old one or creating it if necessary.
1078  */
compen_uncompensatecell(NODEPROTO * np)1079 void compen_uncompensatecell(NODEPROTO *np)
1080 {
1081 	REGISTER CONTOUR *con, *contourlist;
1082 	REGISTER CONTOURELEMENT *conel;
1083 	REGISTER INTBIG lx, hx, ly, hy, radius, xc, yc, *prevdata;
1084 	INTBIG coord[6];
1085 	REGISTER VARIABLE *var;
1086 	CHAR *par[2];
1087 	REGISTER NODEPROTO *uncompnp;
1088 	REGISTER NODEINST *ni;
1089 	REGISTER INTBIG newrot, badprecomps;
1090 	double fx, fy, srot, erot, fswap;
1091 	float percentage;
1092 
1093 	/* uncompensation is easy if the original is in a different view */
1094 	if (np->cellview == el_compview)
1095 	{
1096 		FOR_CELLGROUP(uncompnp, np)
1097 			if (uncompnp->cellview != el_compview)
1098 		{
1099 			par[0] = describenodeproto(uncompnp);
1100 			us_editcell(1, par);
1101 			return;
1102 		}
1103 	}
1104 
1105 	/* see if this cell is compensated */
1106 	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
1107 	{
1108 		var = getvalkey((INTBIG)ni, VNODEINST, VINTEGER|VISARRAY, compen_precomppositionkey);
1109 		if (var != NOVARIABLE) break;
1110 	}
1111 	if (ni == NONODEINST)
1112 	{
1113 		ttyputerr(M_("Cannot uncompensate: compensation has not been done"));
1114 		return;
1115 	}
1116 	if (np->cellview != el_compview) (void)changecellview(np, el_compview);
1117 
1118 	/* make a copy of this in an uncompensated cell */
1119 	uncompnp = copynodeproto(np, np->lib, np->protoname, FALSE);
1120 	if (uncompnp == NONODEPROTO)
1121 	{
1122 		ttyputerr(M_("Could not create uncompensated view of this cell"));
1123 		return;
1124 	}
1125 	(void)changecellview(uncompnp, el_unknownview);
1126 
1127 	/* recompute bounds */
1128 	(*el_curconstraint->solve)(uncompnp);
1129 
1130 	/* collect contours in this cell */
1131 	contourlist = compen_getcontourlist(uncompnp);
1132 	if (contourlist == NOCONTOUR) return;
1133 
1134 	/* loop through every element in every contour */
1135 	badprecomps = 0;
1136 	for(con = contourlist; con != NOCONTOUR; con = con->nextcontour)
1137 	{
1138 		if (con->valid == 0) continue;
1139 		for(conel = con->firstcontourelement; conel != NOCONTOURELEMENT; conel = conel->nextcontourelement)
1140 		{
1141 			if (conel->ni == NONODEINST) continue;
1142 
1143 			/* restore previous */
1144 			if (conel->elementtype == BRIDGESEGMENTTYPE)
1145 			{
1146 				/* delete node: previous didn't exist */
1147 				startobjectchange((INTBIG)conel->ni, VNODEINST);
1148 				(void)killnodeinst(conel->ni);
1149 				conel->ni = NONODEINST;
1150 				continue;
1151 			}
1152 
1153 			var = getvalkey((INTBIG)conel->ni, VNODEINST, VINTEGER|VISARRAY, compen_precomppositionkey);
1154 			if (var == NOVARIABLE) continue;
1155 			prevdata = (INTBIG *)var->addr;
1156 			if (prevdata[PRECOMPMAGIC] != 0xFEED)
1157 			{
1158 				badprecomps = 1;
1159 				(void)delvalkey((INTBIG)conel->ni, VNODEINST, compen_precomppositionkey);
1160 				continue;
1161 			}
1162 			if (prevdata[PRECOMPTYPE] == BRIDGESEGMENTTYPE)
1163 			{
1164 				/* delete node: previous didn't exist */
1165 				startobjectchange((INTBIG)conel->ni, VNODEINST);
1166 				(void)killnodeinst(conel->ni);
1167 				conel->ni = NONODEINST;
1168 				continue;
1169 			}
1170 
1171 			/* start changes */
1172 			startobjectchange((INTBIG)conel->ni, VNODEINST);
1173 
1174 			switch (conel->elementtype)
1175 			{
1176 				case LINESEGMENTTYPE:
1177 					if (prevdata[PRECOMPTYPE] != LINESEGMENTTYPE) break;
1178 
1179 					/* create the new line node */
1180 					lx = mini(prevdata[PRECOMPLINESX], prevdata[PRECOMPLINEEX]);
1181 					hx = maxi(prevdata[PRECOMPLINESX], prevdata[PRECOMPLINEEX]);
1182 					ly = mini(prevdata[PRECOMPLINESY], prevdata[PRECOMPLINEEY]);
1183 					hy = maxi(prevdata[PRECOMPLINESY], prevdata[PRECOMPLINEEY]);
1184 					modifynodeinst(conel->ni, lx - conel->ni->lowx, ly - conel->ni->lowy,
1185 						hx - conel->ni->highx, hy - conel->ni->highy, 0, 0);
1186 
1187 					/* store line coordinates */
1188 					xc = (lx + hx) / 2;   yc = (ly + hy) / 2;
1189 					coord[0] = prevdata[PRECOMPLINESX] - xc;		coord[1] = prevdata[PRECOMPLINESY] - yc;
1190 					coord[2] = prevdata[PRECOMPLINEEX] - xc;		coord[3] = prevdata[PRECOMPLINEEY] - yc;
1191 					(void)setvalkey((INTBIG)conel->ni, VNODEINST, el_trace_key, (INTBIG)coord,
1192 						VINTEGER|VISARRAY|(4<<VLENGTHSH));
1193 
1194 					/* store compensation percentage (if not a global value) */
1195 					if (prevdata[PRECOMPEXPPERC] != 0)
1196 					{
1197 						percentage = (float)prevdata[PRECOMPPERC];
1198 						(void)setvalkey((INTBIG)conel->ni, VNODEINST, compen_percentagekey,
1199 							castint(percentage), VFLOAT);
1200 					}
1201 					break;
1202 
1203 				case CIRCLESEGMENTTYPE:
1204 					if (prevdata[PRECOMPTYPE] != CIRCLESEGMENTTYPE) break;
1205 
1206 					/* set new size according to new radius */
1207 					radius = prevdata[PRECOMPCIRCRAD];
1208 					lx = conel->cx - radius;   hx = conel->cx + radius;
1209 					ly = conel->cy - radius;   hy = conel->cy + radius;
1210 					modifynodeinst(conel->ni, lx - conel->ni->lowx, ly - conel->ni->lowy,
1211 						hx - conel->ni->highx, hy - conel->ni->highy, 0, 0);
1212 					break;
1213 
1214 				case ARCSEGMENTTYPE:
1215 				case REVARCSEGMENTTYPE:
1216 					if (prevdata[PRECOMPTYPE] != ARCSEGMENTTYPE && prevdata[PRECOMPTYPE] != REVARCSEGMENTTYPE) break;
1217 
1218 					/* set new size and arc information */
1219 					radius = prevdata[PRECOMPARCRAD];
1220 					lx = conel->cx - radius;   hx = conel->cx + radius;
1221 					ly = conel->cy - radius;   hy = conel->cy + radius;
1222 
1223 					fx = prevdata[PRECOMPARCSX] - conel->cx;   fy = prevdata[PRECOMPARCSY] - conel->cy;
1224 					if (fy == 0.0 && fx == 0.0)
1225 					{
1226 						ttyputerr(M_("Domain error uncompensating arc end 1"));
1227 						break;
1228 					}
1229 					srot = atan2(fy, fx);
1230 					if (srot < 0.0) srot += EPI*2.0;
1231 					fx = prevdata[PRECOMPARCEX] - conel->cx;   fy = prevdata[PRECOMPARCEY] - conel->cy;
1232 					if (fy == 0.0 && fx == 0.0)
1233 					{
1234 						ttyputerr(M_("Domain error uncompensating arc end 2"));
1235 						break;
1236 					}
1237 					erot = atan2(fy, fx);
1238 					if (erot < 0.0) erot += EPI*2.0;
1239 					if (prevdata[PRECOMPTYPE] == REVARCSEGMENTTYPE)
1240 					{
1241 						fswap = srot;   srot = erot;   erot = fswap;
1242 					}
1243 					erot -= srot;
1244 					if (erot < 0.0) erot += EPI*2.0;
1245 					newrot = rounddouble(srot * 1800.0 / EPI);
1246 					srot -= ((double)newrot) * EPI / 1800.0;
1247 					modifynodeinst(conel->ni, lx - conel->ni->lowx, ly - conel->ni->lowy,
1248 						hx - conel->ni->highx, hy - conel->ni->highy,
1249 							newrot - conel->ni->rotation, -conel->ni->transpose);
1250 					setarcdegrees(conel->ni, srot, erot);
1251 					break;
1252 
1253 				default:
1254 					break;
1255 			}
1256 
1257 			/* delete previous compensation data */
1258 			(void)delvalkey((INTBIG)conel->ni, VNODEINST, compen_precomppositionkey);
1259 
1260 			/* end of changes */
1261 			endobjectchange((INTBIG)conel->ni, VNODEINST);
1262 		}
1263 	}
1264 	if (badprecomps != 0)
1265 	{
1266 		ttyputerr(M_("Sorry, some precompensation information is obsolete"));
1267 		return;
1268 	}
1269 
1270 	/* show the compensated cell */
1271 	par[0] = describenodeproto(uncompnp);
1272 	us_editcell(1, par);
1273 	compen_illuminatepercentages(FALSE);
1274 }
1275 
1276 /*
1277  * Routine to automatically detect special features described in
1278  * "library:~.COMPEN_automatic_features".  It has this format:
1279  *    "circle DIAMETERinMM PERCENTAGE"
1280  *    "slot LENGTHinMM WIDTHinMM ENDCAPCOMP SIDECOMP
1281  */
compen_detectfeatures(NODEPROTO * np)1282 INTBIG compen_detectfeatures(NODEPROTO *np)
1283 {
1284 	REGISTER INTBIG len, i, autofeatures, diameter, radius2, radius4, length;
1285 	REGISTER INTBIG featuresfound;
1286 	REGISTER VARIABLE *var;
1287 	REGISTER CONTOUR *con, *contourlist;
1288 	REGISTER CONTOURELEMENT *conel, *conel1, *conel2, *conel3, *conel4;
1289 	REGISTER AUTOFEATURE *af;
1290 	REGISTER CHAR *pp, *pporig, **featurelist;
1291 	float percentage;
1292 
1293 	/* collect contours in this cell */
1294 	contourlist = compen_getcontourlist(np);
1295 	if (contourlist == NOCONTOUR) return(0);
1296 
1297 	/* get the "text" list of automatic feature descriptions */
1298 	var = getval((INTBIG)el_curlib, VLIBRARY, VSTRING | VISARRAY, x_("COMPEN_automatic_features"));
1299 	if (var == NOVARIABLE) return(0);
1300 	featurelist = (CHAR **)var->addr;
1301 	len = getlength(var);
1302 
1303 	/* make space for the "internal" list */
1304 	af = (AUTOFEATURE *)emalloc(len * (sizeof (AUTOFEATURE)), compen_tool->cluster);
1305 	if (af == 0) return(0);
1306 
1307 	/* convert text list to internal list */
1308 	autofeatures = 0;
1309 	for(i=0; i<len; i++)
1310 	{
1311 		pporig = pp = featurelist[i];
1312 		while (*pp == ' ' || *pp == '\t') pp++;
1313 		if (*pp == ';' || *pp == 0) continue;
1314 		if (namesamen(pp, x_("circle"), 6) == 0)
1315 		{
1316 			/* circle feature */
1317 			pp += 6;
1318 			while (*pp == ' ' || *pp == '\t') pp++;
1319 			af[autofeatures].type = 0;
1320 			af[autofeatures].diameter = scalefromdispunit((float)eatof(pp), DISPUNITMM);
1321 			while (*pp != ' ' && *pp != '\t' && *pp != 0) pp++;
1322 			while (*pp == ' ' || *pp == '\t') pp++;
1323 			af[autofeatures].percentage = eatoi(pp);
1324 			if (*pp != 0)
1325 			{
1326 				autofeatures++;
1327 				continue;
1328 			}
1329 		} else if (namesamen(pp, x_("slot"), 4) == 0)
1330 		{
1331 			/* slot feature */
1332 			pp += 4;
1333 			while (*pp == ' ' || *pp == '\t') pp++;
1334 			af[autofeatures].type = 1;
1335 			af[autofeatures].length = scalefromdispunit((float)eatof(pp), DISPUNITMM);
1336 			while (*pp != ' ' && *pp != '\t' && *pp != 0) pp++;
1337 			while (*pp == ' ' || *pp == '\t') pp++;
1338 
1339 			af[autofeatures].diameter = scalefromdispunit((float)eatof(pp), DISPUNITMM);
1340 			while (*pp != ' ' && *pp != '\t' && *pp != 0) pp++;
1341 			while (*pp == ' ' || *pp == '\t') pp++;
1342 
1343 			af[autofeatures].lenpercentage = eatoi(pp);
1344 			while (*pp != ' ' && *pp != '\t' && *pp != 0) pp++;
1345 			while (*pp == ' ' || *pp == '\t') pp++;
1346 
1347 			af[autofeatures].percentage = eatoi(pp);
1348 			if (*pp != 0)
1349 			{
1350 				autofeatures++;
1351 				continue;
1352 			}
1353 		}
1354 		ttyputerr(M_("Error in automatic feature compensation specification: '%s'"), pporig);
1355 		break;
1356 	}
1357 
1358 	/* now detect special coutours */
1359 	featuresfound = 0;
1360 	for(con = contourlist; con != NOCONTOUR; con = con->nextcontour)
1361 	{
1362 		if (con->valid == 0) continue;
1363 		conel = con->firstcontourelement;
1364 
1365 		if (conel->elementtype == CIRCLESEGMENTTYPE)
1366 		{
1367 			/* check for circles */
1368 			diameter = computedistance(conel->sx, conel->sy, conel->cx, conel->cy) * 2;
1369 			for(i=0; i<autofeatures; i++)
1370 			{
1371 				if (af[i].type != 0) continue;
1372 				if (diameter == af[i].diameter)
1373 				{
1374 					percentage = (float)af[i].percentage;
1375 					(void)setvalkey((INTBIG)conel->ni, VNODEINST, compen_percentagekey,
1376 						castint(percentage), VFLOAT);
1377 					featuresfound++;
1378 					break;
1379 				}
1380 			}
1381 		} else
1382 		{
1383 			/* check for slots */
1384 			i = 0;
1385 			for(; conel != NOCONTOURELEMENT; conel = conel->nextcontourelement) i++;
1386 			if (i == 4)
1387 			{
1388 				conel1 = con->firstcontourelement;
1389 				conel2 = conel1->nextcontourelement;
1390 				conel3 = conel2->nextcontourelement;
1391 				conel4 = conel3->nextcontourelement;
1392 				if (conel1->elementtype == ARCSEGMENTTYPE || conel1->elementtype == REVARCSEGMENTTYPE)
1393 				{
1394 					conel = conel1;   conel1 = conel2;   conel2 = conel3;   conel3 = conel4;   conel4 = conel;
1395 				}
1396 				if (conel1->elementtype == LINESEGMENTTYPE && conel3->elementtype == LINESEGMENTTYPE &&
1397 					(conel2->elementtype == ARCSEGMENTTYPE || conel2->elementtype == REVARCSEGMENTTYPE) &&
1398 					(conel4->elementtype == ARCSEGMENTTYPE || conel4->elementtype == REVARCSEGMENTTYPE))
1399 				{
1400 					radius2 = computedistance(conel2->sx, conel2->sy, conel2->cx, conel2->cy);
1401 					radius4 = computedistance(conel4->sx, conel4->sy, conel4->cx, conel4->cy);
1402 					length = computedistance(conel2->cx, conel2->cy, conel4->cx, conel4->cy);
1403 					length += radius2 + radius4;
1404 					for(i=0; i<autofeatures; i++)
1405 					{
1406 						if (af[i].type != 1) continue;
1407 						if (radius2 == radius4 && radius2 * 2 == af[i].diameter && length == af[i].length)
1408 						{
1409 							percentage = (float)af[i].percentage;
1410 							(void)setvalkey((INTBIG)conel1->ni, VNODEINST, compen_percentagekey,
1411 								castint(percentage), VFLOAT);
1412 							(void)setvalkey((INTBIG)conel3->ni, VNODEINST, compen_percentagekey,
1413 								castint(percentage), VFLOAT);
1414 
1415 							percentage = (float)af[i].lenpercentage;
1416 							(void)setvalkey((INTBIG)conel2->ni, VNODEINST, compen_percentagekey,
1417 								castint(percentage), VFLOAT);
1418 							(void)setvalkey((INTBIG)conel4->ni, VNODEINST, compen_percentagekey,
1419 								castint(percentage), VFLOAT);
1420 							featuresfound++;
1421 							break;
1422 						}
1423 					}
1424 				}
1425 			}
1426 		}
1427 	}
1428 
1429 	efree((CHAR *)af);
1430 	return(featuresfound);
1431 }
1432 
1433 /*
1434  * routine to examine all contours in the cell and find where dielectric crosses stainless.  At those
1435  * points, the dielectric is broken into two pieces.
1436  */
compen_detectlayerintersections(NODEPROTO * np)1437 void compen_detectlayerintersections(NODEPROTO *np)
1438 {
1439 	REGISTER CONTOUR *con, *ocon, *contourlist;
1440 	REGISTER CONTOURELEMENT *conel, *oconel;
1441 	REGISTER USERDATA *ud, *oud;
1442 	REGISTER double ang, oang;
1443 	INTBIG ix, iy;
1444 
1445 	/* collect contours in this cell */
1446 	contourlist = compen_getcontourlist(np);
1447 	if (contourlist == NOCONTOUR) return;
1448 
1449 	/* compute the bounding boxes */
1450 	for(con = contourlist; con != NOCONTOUR; con = con->nextcontour)
1451 	{
1452 		if (con->valid == 0) continue;
1453 		for(conel = con->firstcontourelement; conel != NOCONTOURELEMENT; conel = conel->nextcontourelement)
1454 		{
1455 			ud = (USERDATA *)conel->userdata;
1456 			getcontourelementbbox(conel, &ud->lowx, &ud->highx, &ud->lowy, &ud->highy);
1457 		}
1458 	}
1459 
1460 	/* examine all geometry */
1461 	for(con = contourlist; con != NOCONTOUR; con = con->nextcontour)
1462 	{
1463 		if (con->valid == 0) continue;
1464 		for(conel = con->firstcontourelement; conel != NOCONTOURELEMENT; conel = conel->nextcontourelement)
1465 		{
1466 			/* ignore if this is a circle or not dielectric layer */
1467 			if (conel->elementtype == CIRCLESEGMENTTYPE) continue;
1468 			ud = (USERDATA *)conel->userdata;
1469 			if (ud->DXFlayer == 0) continue;
1470 			if (namesame(ud->DXFlayer, x_("DIELECTRIC")) != 0) continue;
1471 
1472 			/* found piece of dielectric: see if stainless intersects it */
1473 			for(ocon = contourlist; ocon != NOCONTOUR; ocon = ocon->nextcontour)
1474 			{
1475 				if (ocon->valid == 0) continue;
1476 				for(oconel = ocon->firstcontourelement; oconel != NOCONTOURELEMENT; oconel = oconel->nextcontourelement)
1477 				{
1478 					/* ignore if this is a circle or not stainless layer */
1479 					if (oconel->elementtype == CIRCLESEGMENTTYPE) continue;
1480 					oud = (USERDATA *)oconel->userdata;
1481 					if (oud->DXFlayer == 0) continue;
1482 					if (namesame(oud->DXFlayer, x_("SST")) != 0) continue;
1483 
1484 					/* trivial intersection test */
1485 					if (ud->highx < oud->lowx || ud->lowx > oud->highx ||
1486 						ud->highy < oud->lowy || ud->lowy > oud->highy) continue;
1487 
1488 					/* more advanced test */
1489 					if (conel->elementtype == LINESEGMENTTYPE || conel->elementtype == BRIDGESEGMENTTYPE)
1490 					{
1491 						if (oconel->elementtype == LINESEGMENTTYPE || oconel->elementtype == BRIDGESEGMENTTYPE)
1492 						{
1493 							/* handle line-to-line intersection */
1494 							ang = compen_figureangle(conel->sx, conel->sy, conel->ex, conel->ey);
1495 							oang = compen_figureangle(oconel->sx, oconel->sy, oconel->ex, oconel->ey);
1496 							if (!compen_intersect(conel->sx, conel->sy, ang, oconel->sx, oconel->sy, oang, &ix, &iy))
1497 							{
1498 								if (ix >= mini(conel->sx, conel->ex) && ix <= maxi(conel->sx, conel->ex) &&
1499 									iy >= mini(conel->sy, conel->ey) && iy <= maxi(conel->sy, conel->ey) &&
1500 									ix >= mini(oconel->sx, oconel->ex) && ix <= maxi(oconel->sx, oconel->ex) &&
1501 									iy >= mini(oconel->sy, oconel->ey) && iy <= maxi(oconel->sy, oconel->ey))
1502 								{
1503 									INTBIG fx, fy;
1504 									compen_rline.col = GREEN;
1505 									if ((el_curwindowpart->state&INPLACEEDIT) != 0)
1506 										xform(ix, iy, &ix, &iy, el_curwindowpart->outofcell);
1507 									fx = applyxscale(el_curwindowpart, ix-el_curwindowpart->screenlx) + el_curwindowpart->uselx;
1508 									fy = applyyscale(el_curwindowpart, iy-el_curwindowpart->screenly) + el_curwindowpart->usely;
1509 									if (fx > el_curwindowpart->uselx+2 && fx < el_curwindowpart->usehx-2 &&
1510 										fy > el_curwindowpart->usely+2 && fy < el_curwindowpart->usehy-2)
1511 									{
1512 										screendrawline(el_curwindowpart, fx-3, fy-3, fx+3, fy+3, &compen_rline, 0);
1513 										screendrawline(el_curwindowpart, fx-3, fy+3, fx-3, fy+3, &compen_rline, 0);
1514 									}
1515 								}
1516 							}
1517 						} else
1518 						{
1519 							/* handle line-to-arc intersection */
1520 							/* EMPTY */
1521 						}
1522 					} else
1523 					{
1524 						if (oconel->elementtype == LINESEGMENTTYPE || oconel->elementtype == BRIDGESEGMENTTYPE)
1525 						{
1526 							/* handle arc-to-line intersection */
1527 							/* EMPTY */
1528 						} else
1529 						{
1530 							/* handle arc-to-arc intersection */
1531 							/* EMPTY */
1532 						}
1533 					}
1534 				}
1535 			}
1536 		}
1537 	}
1538 }
1539 
1540 /*
1541  * Routine to detect and highlight all duplicate nodes in cell "np".
1542  */
compen_detectduplicates(NODEPROTO * np)1543 INTBIG compen_detectduplicates(NODEPROTO *np)
1544 {
1545 	REGISTER NODEINST *ni, *oni;
1546 	REGISTER INTBIG cx, cy, sea, len, olen, i, ctrx, ctry;
1547 	INTBIG fx, fy, tx, ty, ofx, ofy, otx, oty;
1548 	REGISTER VARIABLE *var, *ovar;
1549 	REGISTER GEOM *geom;
1550 	XARRAY trans;
1551 	double startoffset, endangle, ostartoffset, oendangle, dx, dy;
1552 	REGISTER void *infstr;
1553 
1554 	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst) ni->temp1 = 0;
1555 
1556 	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
1557 	{
1558 		if (ni->temp1 != 0) continue;
1559 		cx = (ni->lowx + ni->highx) / 2;
1560 		cy = (ni->lowy + ni->highy) / 2;
1561 		sea = initsearch(cx, cx, cy, cy, np);
1562 		for(;;)
1563 		{
1564 			geom = nextobject(sea);
1565 			if (geom == NOGEOM) break;
1566 			if (!geom->entryisnode) continue;
1567 			oni = geom->entryaddr.ni;
1568 			if (oni == ni) continue;
1569 			if (oni->temp1 != 0) continue;
1570 			if (oni->proto != ni->proto) continue;
1571 
1572 			/* could be a duplicate: make specific tests */
1573 			if (ni->proto == art_openedpolygonprim)
1574 			{
1575 				var = gettrace(ni);
1576 				if (var == NOVARIABLE) len = 0; else
1577 					len = getlength(var);
1578 				ovar = gettrace(oni);
1579 				if (ovar == NOVARIABLE) olen = 0; else
1580 					olen = getlength(ovar);
1581 				if (len == 4 && olen == 4)
1582 				{
1583 					/* special test for colinearity */
1584 					makerot(ni, trans);
1585 					ctrx = (ni->highx + ni->lowx) / 2;
1586 					ctry = (ni->highy + ni->lowy) / 2;
1587 					xform(((INTBIG *)var->addr)[0]+ctrx, ((INTBIG *)var->addr)[1]+ctry, &fx, &fy,
1588 						trans);
1589 					xform(((INTBIG *)var->addr)[2]+ctrx, ((INTBIG *)var->addr)[3]+ctry, &tx, &ty,
1590 						trans);
1591 
1592 					makerot(oni, trans);
1593 					ctrx = (oni->highx + oni->lowx) / 2;
1594 					ctry = (oni->highy + oni->lowy) / 2;
1595 					xform(((INTBIG *)ovar->addr)[0]+ctrx, ((INTBIG *)ovar->addr)[1]+ctry, &ofx, &ofy,
1596 						trans);
1597 					xform(((INTBIG *)ovar->addr)[2]+ctrx, ((INTBIG *)ovar->addr)[3]+ctry, &otx, &oty,
1598 						trans);
1599 
1600 					/* other line must be inside bounds of current one */
1601 					if (mini(ofx, otx) < mini(fx, tx) || maxi(ofx, otx) > maxi(fx, tx)) continue;
1602 					if (mini(ofy, oty) < mini(fy, ty) || maxi(ofy, oty) > maxi(fy, ty)) continue;
1603 
1604 					/* both endpoints of other line must be on current one */
1605 					dx = (double)(tx - fx);   dy = (double)(ty - fy);
1606 					if (((double)(ofx-fx))*dy != ((double)(ofy-fy))*dx) continue;
1607 					if (((double)(otx-fx))*dy != ((double)(oty-fy))*dx) continue;
1608 
1609 					/* node "oni" is colinear with "ni" */
1610 					oni->temp1 = 1;
1611 					continue;
1612 				}
1613 			}
1614 
1615 			/* make sure size and orientation are the same */
1616 			if (oni->lowx != ni->lowx || oni->highx != ni->highx ||
1617 				oni->lowy != ni->lowy || oni->highy != ni->highy) continue;
1618 			if (oni->rotation != ni->rotation || oni->transpose != ni->transpose) continue;
1619 
1620 			if (ni->proto == art_circleprim || ni->proto == art_thickcircleprim)
1621 			{
1622 				getarcdegrees(ni, &startoffset, &endangle);
1623 				getarcdegrees(oni, &ostartoffset, &oendangle);
1624 				if (startoffset != ostartoffset || endangle != oendangle) continue;
1625 			} else if (ni->proto == art_openedpolygonprim || ni->proto == art_closedpolygonprim)
1626 			{
1627 				var = gettrace(ni);
1628 				if (var == NOVARIABLE) len = 0; else
1629 					len = getlength(var);
1630 				ovar = gettrace(oni);
1631 				if (ovar == NOVARIABLE) olen = 0; else
1632 					olen = getlength(ovar);
1633 				if (len != olen) continue;
1634 				for(i=0; i<len; i++) if (((INTBIG *)var->addr)[i] != ((INTBIG *)ovar->addr)[i]) break;
1635 				if (i < len) continue;
1636 			}
1637 
1638 			/* node "oni" is a duplicate */
1639 			oni->temp1 = 1;
1640 		}
1641 	}
1642 
1643 	/* count the number of duplicates */
1644 	i = 0;
1645 	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
1646 		if (ni->temp1 != 0) i++;
1647 	if (i > 0)
1648 	{
1649 		(void)asktool(us_tool, x_("clear"));
1650 		infstr = initinfstr();
1651 		i = 0;
1652 		for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
1653 		{
1654 			if (ni->temp1 == 0) continue;
1655 			if (i != 0) addtoinfstr(infstr, '\n');
1656 			i++;
1657 			formatinfstr(infstr, x_("CELL=%s FROM=0%lo;0%lo;0;NOBBOX"),
1658 				describenodeproto(np), (INTBIG)ni->geom, (INTBIG)ni->proto->firstportproto);
1659 		}
1660 		(void)asktool(us_tool, x_("show-multiple"), (INTBIG)returninfstr(infstr));
1661 	}
1662 	return(i);
1663 }
1664 
1665 /*
1666  * Routine to detect and highlight all nodes that are not in contours in cell "np".
1667  */
compen_detectnoncontours(NODEPROTO * np)1668 void compen_detectnoncontours(NODEPROTO *np)
1669 {
1670 	REGISTER CONTOUR *con, *contourlist;
1671 	REGISTER CONTOURELEMENT *conel;
1672 	REGISTER NODEINST *ni;
1673 	REGISTER INTBIG i;
1674 	REGISTER void *infstr;
1675 
1676 	contourlist = compen_getcontourlist(np);
1677 	if (contourlist == NOCONTOUR) return;
1678 
1679 	/* mark all nodes as "not in contour" */
1680 	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
1681 	{
1682 		ni->temp1 = 0;
1683 		if (ni->proto->primindex == 0) ni->temp1 = 1;
1684 		if (ni->proto == gen_cellcenterprim) ni->temp1 = 1;
1685 	}
1686 
1687 	/* mark nodes that are in contours */
1688 	for(con = contourlist; con != NOCONTOUR; con = con->nextcontour)
1689 	{
1690 		if (con->valid == 0) continue;
1691 		for(conel = con->firstcontourelement; conel != NOCONTOURELEMENT; conel = conel->nextcontourelement)
1692 		{
1693 			if (conel->ni == NONODEINST) continue;
1694 			conel->ni->temp1 = 1;
1695 		}
1696 	}
1697 
1698 	/* count nodes that are not in contours */
1699 	i = 0;
1700 	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
1701 		if (ni->temp1 == 0) i++;
1702 	if (i == 0)
1703 	{
1704 		ttyputmsg(M_("All geometry is in a contour"));
1705 		return;
1706 	}
1707 
1708 	(void)asktool(us_tool, x_("clear"));
1709 	infstr = initinfstr();
1710 	i = 0;
1711 	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
1712 	{
1713 		if (ni->temp1 != 0) continue;
1714 		if (i != 0) addtoinfstr(infstr, '\n');
1715 		i++;
1716 		formatinfstr(infstr, x_("CELL=%s FROM=0%lo;0%lo;0;NOBBOX"),
1717 			describenodeproto(np), (INTBIG)ni->geom, (INTBIG)ni->proto->firstportproto);
1718 	}
1719 	(void)asktool(us_tool, x_("show-multiple"), (INTBIG)returninfstr(infstr));
1720 }
1721 
1722 /*
1723  * Routine to create a line that runs between the two highlighted snap points.
1724  */
compen_connectpoints(void)1725 void compen_connectpoints(void)
1726 {
1727 	REGISTER NODEPROTO *np;
1728 	REGISTER VARIABLE *var, *vargds1, *vargds2, *vardxf1, *vardxf2;
1729 	INTBIG len, p1x, p1y, p2x, p2y, cx, cy, v[4];
1730 	HIGHLIGHT thishigh, otherhigh;
1731 	REGISTER NODEINST *ni;
1732 
1733 	np = getcurcell();
1734 	if (np == NONODEPROTO)
1735 	{
1736 		ttyputerr(M_("No current cell"));
1737 		return;
1738 	}
1739 	var = getvalkey((INTBIG)us_tool, VTOOL, VSTRING|VISARRAY, us_highlightedkey);
1740 	if (var != NOVARIABLE)
1741 	{
1742 		len = getlength(var);
1743 		if (len == 2)
1744 		{
1745 			if (!us_makehighlight(((CHAR **)var->addr)[0], &thishigh) &&
1746 				!us_makehighlight(((CHAR **)var->addr)[1], &otherhigh))
1747 			{
1748 				/* describe these two objects */
1749 				if ((thishigh.status&HIGHSNAP) != 0 && (otherhigh.status&HIGHSNAP) != 0)
1750 				{
1751 					/* get GDS and DXF information from the two sides */
1752 					if (thishigh.fromgeom == NOGEOM) vargds1 = vardxf1 = NOVARIABLE; else
1753 					{
1754 						if (thishigh.fromgeom->entryisnode)
1755 						{
1756 							vargds1 = getvalkey((INTBIG)thishigh.fromgeom->entryaddr.ni, VNODEINST,
1757 								VINTEGER, compen_gds_layerkey);
1758 							vardxf1 = getvalkey((INTBIG)thishigh.fromgeom->entryaddr.ni, VNODEINST,
1759 								VSTRING, compen_dxf_layerkey);
1760 						} else
1761 						{
1762 							vargds1 = getvalkey((INTBIG)thishigh.fromgeom->entryaddr.ai, VARCINST,
1763 								VINTEGER, compen_gds_layerkey);
1764 							vardxf1 = getvalkey((INTBIG)thishigh.fromgeom->entryaddr.ai, VARCINST,
1765 								VSTRING, compen_dxf_layerkey);
1766 						}
1767 					}
1768 					if (otherhigh.fromgeom == NOGEOM) vargds2 = vardxf2 = NOVARIABLE; else
1769 					{
1770 						if (otherhigh.fromgeom->entryisnode)
1771 						{
1772 							vargds2 = getvalkey((INTBIG)otherhigh.fromgeom->entryaddr.ni, VNODEINST,
1773 								VINTEGER, compen_gds_layerkey);
1774 							vardxf2 = getvalkey((INTBIG)otherhigh.fromgeom->entryaddr.ni, VNODEINST,
1775 								VSTRING, compen_dxf_layerkey);
1776 						} else
1777 						{
1778 							vargds2 = getvalkey((INTBIG)otherhigh.fromgeom->entryaddr.ai, VARCINST,
1779 								VINTEGER, compen_gds_layerkey);
1780 							vardxf2 = getvalkey((INTBIG)otherhigh.fromgeom->entryaddr.ai, VARCINST,
1781 								VSTRING, compen_dxf_layerkey);
1782 						}
1783 					}
1784 
1785 					/* make the line */
1786 					us_getsnappoint(&thishigh, &p1x, &p1y);
1787 					us_getsnappoint(&otherhigh, &p2x, &p2y);
1788 					ni = newnodeinst(art_openedpolygonprim, mini(p1x,p2x), maxi(p1x,p2x),
1789 						mini(p1y,p2y), maxi(p1y,p2y), 0, 0, np);
1790 					if (ni == NONODEINST)
1791 					{
1792 						ttyputerr(M_("Could not create connecting line"));
1793 						return;
1794 					}
1795 					cx = (p1x + p2x) / 2;
1796 					cy = (p1y + p2y) / 2;
1797 					v[0] = p1x - cx;   v[1] = p1y - cy;
1798 					v[2] = p2x - cx;   v[3] = p2y - cy;
1799 					(void)setvalkey((INTBIG)ni, VNODEINST, el_trace_key, (INTBIG)v,
1800 						VINTEGER|VISARRAY|(4<<VLENGTHSH));
1801 
1802 					/* set GDS if appropriate */
1803 					if (vargds1 != NOVARIABLE || vargds2 != NOVARIABLE)
1804 					{
1805 						if (vargds1 != NOVARIABLE && vargds2 != NOVARIABLE)
1806 						{
1807 							if (vargds1->addr != vargds2->addr)
1808 								ttyputmsg(M_("Warning, one side has GDS layer %ld, the other has %ld"),
1809 									vargds1->addr, vargds2->addr);
1810 						}
1811 						if (vargds1 == NOVARIABLE) vargds1 = vargds2;
1812 						(void)setvalkey((INTBIG)ni, VNODEINST, compen_gds_layerkey,
1813 							vargds1->addr, VINTEGER);
1814 					}
1815 
1816 					/* set DXF if appropriate */
1817 					if (vardxf1 != NOVARIABLE || vardxf2 != NOVARIABLE)
1818 					{
1819 						if (vardxf1 != NOVARIABLE && vardxf2 != NOVARIABLE)
1820 						{
1821 							if (estrcmp((CHAR *)vardxf1->addr, (CHAR *)vardxf2->addr) != 0)
1822 								ttyputmsg(M_("Warning, one side has DXF layer '%s', the other has '%s'"),
1823 									(CHAR *)vardxf1->addr, (CHAR *)vardxf2->addr);
1824 						}
1825 						if (vardxf1 == NOVARIABLE) vardxf1 = vardxf2;
1826 						(void)setvalkey((INTBIG)ni, VNODEINST, compen_dxf_layerkey,
1827 							vardxf1->addr, VSTRING);
1828 					}
1829 
1830 					endobjectchange((INTBIG)ni, VNODEINST);
1831 					return;
1832 				}
1833 			}
1834 		}
1835 	}
1836 	ttyputerr(M_("Must select two snap points to connect them with a line"));
1837 }
1838 
1839 /*
1840  * Routine to draw all contours in cell "np".
1841  */
compen_drawcontours(NODEPROTO * np)1842 void compen_drawcontours(NODEPROTO *np)
1843 {
1844 	static POLYGON *objc = NOPOLYGON;
1845 	REGISTER CONTOURELEMENT *conel;
1846 	REGISTER CONTOUR *con, *contourlist;
1847 	static INTBIG color = 0;
1848 
1849 	if (el_curwindowpart == NOWINDOWPART) return;
1850 	(void)needstaticpolygon(&objc, 4, compen_tool->cluster);
1851 
1852 	contourlist = compen_getcontourlist(np);
1853 	if (contourlist == NOCONTOUR) return;
1854 	for(con = contourlist; con != NOCONTOUR; con = con->nextcontour)
1855 	{
1856 		if (con->valid == 0) continue;
1857 		compen_rline.col = compen_colors[color++];
1858 		if (color >= COLORS) color = 0;
1859 		for(conel = con->firstcontourelement; conel != NOCONTOURELEMENT; conel = conel->nextcontourelement)
1860 		{
1861 			switch (conel->elementtype)
1862 			{
1863 				case CIRCLESEGMENTTYPE:
1864 					compen_drawcircle(conel->cx, conel->cy, conel->sx, conel->sy, &compen_rline);
1865 					break;
1866 				case ARCSEGMENTTYPE:
1867 				case REVARCSEGMENTTYPE:
1868 					if (conel->elementtype == REVARCSEGMENTTYPE)
1869 					{
1870 						compen_drawcirclearc(conel->cx, conel->cy, conel->sx, conel->sy, conel->ex, conel->ey, &compen_rline);
1871 					} else
1872 					{
1873 						compen_drawcirclearc(conel->cx, conel->cy, conel->ex, conel->ey, conel->sx, conel->sy, &compen_rline);
1874 					}
1875 					break;
1876 				case LINESEGMENTTYPE:
1877 				case BRIDGESEGMENTTYPE:
1878 					compen_drawline(conel->sx, conel->sy, conel->ex, conel->ey, &compen_rline);
1879 					break;
1880 			}
1881 		}
1882 	}
1883 }
1884 
1885 /*********** Compensation factors dialog ***********/
1886 
1887 static DIALOGITEM compen_factorsdialogitems[] =
1888 {
1889  /*  1 */ {0, {108,176,132,240}, BUTTON, N_("OK")},
1890  /*  2 */ {0, {108,16,132,80}, BUTTON, N_("Cancel")},
1891  /*  3 */ {0, {8,168,24,258}, EDITTEXT, x_("")},
1892  /*  4 */ {0, {8,8,24,133}, MESSAGE, N_("Metal Thickness:")},
1893  /*  5 */ {0, {32,168,48,258}, EDITTEXT, x_("")},
1894  /*  6 */ {0, {32,8,48,145}, MESSAGE, N_("LRS Compensation:")},
1895  /*  7 */ {0, {56,168,72,258}, EDITTEXT, x_("")},
1896  /*  8 */ {0, {56,8,72,160}, MESSAGE, N_("Global Compensation:")},
1897  /*  9 */ {0, {80,168,96,258}, EDITTEXT, x_("")},
1898  /* 10 */ {0, {80,8,96,165}, MESSAGE, N_("Minimum feature size:")}
1899 };
1900 static DIALOG compen_factorsdialog = {{50,75,197,343}, N_("Compensation Factors"), 0, 8, compen_factorsdialogitems, 0, 0};
1901 
1902 /* special items for the "Compensation Factors" dialog: */
1903 #define DCMF_METALTHICK    3		/* Metal Thickness (edit text) */
1904 #define DCMF_LRSCOMP       5		/* LRS Compensation (edit text) */
1905 #define DCMF_GLOBALCOMP    7		/* Global Compensation (edit text) */
1906 #define DCMF_MINFEATURE    9		/* Minimum feature size (edit text) */
1907 
compen_dofactorsdialog(void)1908 void compen_dofactorsdialog(void)
1909 {
1910 	CHAR line[256];
1911 	INTBIG itemHit;
1912 	float metalthickness, lrscompensation, globalcompensation, minimumsize;
1913 	REGISTER void *dia;
1914 
1915 	/* show the "about" dialog */
1916 	dia = DiaInitDialog(&compen_factorsdialog);
1917 	if (dia == 0) return;
1918 	compen_getglobalfactors(&metalthickness, &lrscompensation, &globalcompensation,
1919 		&minimumsize, el_curlib);
1920 	esnprintf(line, 256, x_("%g"), metalthickness);
1921 	DiaSetText(dia, DCMF_METALTHICK, line);
1922 	esnprintf(line, 256, x_("%g"), lrscompensation);
1923 	DiaSetText(dia, DCMF_LRSCOMP, line);
1924 	esnprintf(line, 256, x_("%g"), globalcompensation);
1925 	DiaSetText(dia, DCMF_GLOBALCOMP, line);
1926 	esnprintf(line, 256, x_("%g"), minimumsize);
1927 	DiaSetText(dia, DCMF_MINFEATURE, line);
1928 	for(;;)
1929 	{
1930 		itemHit = DiaNextHit(dia);
1931 		if (itemHit == OK || itemHit == CANCEL) break;
1932 	}
1933 	if (itemHit == OK)
1934 	{
1935 		metalthickness = (float)eatof(DiaGetText(dia, DCMF_METALTHICK));
1936 		lrscompensation = (float)eatof(DiaGetText(dia, DCMF_LRSCOMP));
1937 		globalcompensation = (float)eatof(DiaGetText(dia, DCMF_GLOBALCOMP));
1938 		minimumsize = (float)eatof(DiaGetText(dia, DCMF_MINFEATURE));
1939 		compen_setglobalfactors(metalthickness, lrscompensation, globalcompensation,
1940 			minimumsize, el_curlib);
1941 	}
1942 	DiaDoneDialog(dia);
1943 }
1944 
1945 /*
1946  * routine to manipulate compensation percentages on node "ni".
1947  * If "mode" is zero, set percentage to "amt".
1948  * If "mode" is positive, report percentage amount.
1949  * If "mode" is negative, remove percentage amount.
1950  */
compen_setnodecompensation(NODEINST * ni,float amt,INTBIG mode)1951 void compen_setnodecompensation(NODEINST *ni, float amt, INTBIG mode)
1952 {
1953 	REGISTER VARIABLE *var;
1954 	CHAR name[100];
1955 	double startoffset, endangle;
1956 
1957 	if (mode > 0)
1958 	{
1959 		/* get compensation percentage */
1960 		estrcpy(name, M_("Unknown"));
1961 		if (ni->proto == art_openedpolygonprim || ni->proto == art_closedpolygonprim)
1962 		{
1963 			estrcpy(name, M_("Line"));
1964 		} else if (ni->proto == art_circleprim || ni->proto == art_thickcircleprim)
1965 		{
1966 			getarcdegrees(ni, &startoffset, &endangle);
1967 			if (startoffset != 0.0 || endangle != 0.0) estrcpy(name, M_("Arc")); else
1968 				estrcpy(name, M_("Circle"));
1969 		}
1970 		var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, el_node_name_key);
1971 		if (var != NOVARIABLE)
1972 		{
1973 			estrcat(name, x_(" ["));
1974 			estrcat(name, (CHAR *)var->addr);
1975 			estrcat(name, x_("]"));
1976 		}
1977 
1978 		var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, compen_dxf_layerkey);
1979 		if (var != NOVARIABLE)
1980 		{
1981 			estrcat(name, M_(" on layer "));
1982 			estrcat(name, (CHAR *)var->addr);
1983 		}
1984 
1985 		var = getvalkey((INTBIG)ni, VNODEINST, VFLOAT, compen_percentagekey);
1986 		if (var == NOVARIABLE)
1987 			ttyputmsg(M_("%s has no compensation set on it"), name); else
1988 				ttyputmsg(M_("%s has %g%% compensation set on it"), name, castfloat(var->addr));
1989 	} else if (mode == 0)
1990 	{
1991 		/* set compensation percentage */
1992 		(void)setvalkey((INTBIG)ni, VNODEINST, compen_percentagekey, castint(amt), VFLOAT);
1993 	} else
1994 	{
1995 		/* remove compensation percentage */
1996 		if (getvalkey((INTBIG)ni, VNODEINST, VFLOAT, compen_percentagekey) != NOVARIABLE)
1997 			(void)delvalkey((INTBIG)ni, VNODEINST, compen_percentagekey);
1998 	}
1999 }
2000 
2001 /*
2002  * routine to set color information on all nodes in the current cell with compensation
2003  * percentage factors
2004  */
compen_illuminatepercentages(BOOLEAN showonside)2005 void compen_illuminatepercentages(BOOLEAN showonside)
2006 {
2007 	REGISTER NODEPROTO *np;
2008 	REGISTER NODEINST *ni;
2009 	REGISTER VARIABLE *var, *varcolor;
2010 	REGISTER INTBIG cindex, usedcolors, bestindex, position;
2011 	REGISTER INTBIG cx, cy;
2012 	UINTBIG descript[TEXTDESCRIPTSIZE];
2013 	float amt, bestvalue;
2014 	static POLYGON *objc = NOPOLYGON;
2015 	CHAR perstring[100];
2016 	INTBIG shown[COLORS];
2017 
2018 	np = getcurcell();
2019 	if (np == NONODEPROTO)
2020 	{
2021 		ttyputerr(M_("No current cell"));
2022 		return;
2023 	}
2024 
2025 	(void)needstaticpolygon(&objc, 4, compen_tool->cluster);
2026 
2027 	/* push highlighting */
2028 	us_pushhighlight();
2029 	us_clearhighlightcount();
2030 	usedcolors = 0;
2031 	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
2032 	{
2033 		/* see if there is a percentage on this node */
2034 		var = getvalkey((INTBIG)ni, VNODEINST, VFLOAT, compen_percentagekey);
2035 		varcolor = getvalkey((INTBIG)ni, VNODEINST, VINTEGER, art_colorkey);
2036 		if (var == NOVARIABLE)
2037 		{
2038 			if (varcolor != NOVARIABLE)
2039 			{
2040 				startobjectchange((INTBIG)ni, VNODEINST);
2041 				(void)delvalkey((INTBIG)ni, VNODEINST, art_colorkey);
2042 				endobjectchange((INTBIG)ni, VNODEINST);
2043 			}
2044 			continue;
2045 		}
2046 
2047 		/* see if the percentage is in the table.  Add if not */
2048 		amt = castfloat(var->addr);
2049 		for(cindex=0; cindex<usedcolors; cindex++)
2050 			if (compen_percentages[cindex] == amt) break;
2051 		if (cindex >= usedcolors && usedcolors < COLORS)
2052 		{
2053 			cindex = usedcolors++;
2054 			compen_percentages[cindex] = amt;
2055 		}
2056 
2057 		/* see if there is a color on this node */
2058 		if (varcolor == NOVARIABLE)
2059 		{
2060 			/* none: add it */
2061 			startobjectchange((INTBIG)ni, VNODEINST);
2062 			setvalkey((INTBIG)ni, VNODEINST, art_colorkey, compen_colors[cindex], VINTEGER);
2063 			endobjectchange((INTBIG)ni, VNODEINST);
2064 		} else
2065 		{
2066 			/* see if the color is correct */
2067 			if (cindex < usedcolors && compen_colors[cindex] != varcolor->addr)
2068 			{
2069 				startobjectchange((INTBIG)ni, VNODEINST);
2070 				setvalkey((INTBIG)ni, VNODEINST, art_colorkey, compen_colors[cindex], VINTEGER);
2071 				endobjectchange((INTBIG)ni, VNODEINST);
2072 			}
2073 		}
2074 	}
2075 
2076 	/* illustrate the percentage colors */
2077 	if (showonside)
2078 	{
2079 		TDCLEAR(descript);
2080 		TDSETSIZE(descript, TXTSETPOINTS(12));
2081 		screensettextinfo(el_curwindowpart, NOTECHNOLOGY, descript);
2082 		for(cindex=0; cindex<usedcolors; cindex++) shown[cindex] = 0;
2083 		for(position=0; ; position++)
2084 		{
2085 			bestindex = -1;
2086 			bestvalue = 100000.0;
2087 			for(cindex = 0; cindex < usedcolors; cindex++)
2088 			{
2089 				if (shown[cindex] != 0) continue;
2090 				if (compen_percentages[cindex] > bestvalue) continue;
2091 				bestvalue = compen_percentages[cindex];
2092 				bestindex = cindex;
2093 			}
2094 			if (bestindex < 0) break;
2095 
2096 			shown[bestindex] = 1;
2097 			compen_rline.col = compen_colors[bestindex];
2098 			cx = el_curwindowpart->uselx;
2099 			cy = el_curwindowpart->usely + (el_curwindowpart->usehy-el_curwindowpart->usely) / usedcolors * position;
2100 			esnprintf(perstring, 100, x_("%g%%"), compen_percentages[bestindex]);
2101 			screendrawtext(el_curwindowpart, cx, cy, perstring, &compen_rline);
2102 		}
2103 	}
2104 
2105 	/* restore highlighting */
2106 	us_pophighlight(FALSE);
2107 }
2108 
2109 /*
2110  * Routine to get global factors "metalthickness", "lrscompensation", "globalcompensation", and
2111  * "minimumsize" from library "lib".
2112  */
compen_getglobalfactors(float * metalthickness,float * lrscompensation,float * globalcompensation,float * minimumsize,LIBRARY * lib)2113 void compen_getglobalfactors(float *metalthickness, float *lrscompensation, float *globalcompensation,
2114 	float *minimumsize, LIBRARY *lib)
2115 {
2116 	REGISTER VARIABLE *var;
2117 
2118 	var = getvalkey((INTBIG)lib, VLIBRARY, VFLOAT, compen_metalthicknesskey);
2119 	if (var == NOVARIABLE) *metalthickness = (float)DEFAULTMETALTHICKNESS; else
2120 		*metalthickness = castfloat(var->addr);
2121 	var = getvalkey((INTBIG)lib, VLIBRARY, VFLOAT, compen_lrscompensationkey);
2122 	if (var == NOVARIABLE) *lrscompensation = (float)DEFAULTLRSCOMPENSATION; else
2123 		*lrscompensation = castfloat(var->addr);
2124 	var = getvalkey((INTBIG)lib, VLIBRARY, VFLOAT, compen_globalcompensationkey);
2125 	if (var == NOVARIABLE) *globalcompensation = (float)DEFAULTGLOBALCOMPENSATION; else
2126 		*globalcompensation = castfloat(var->addr);
2127 	var = getvalkey((INTBIG)lib, VLIBRARY, VFLOAT, compen_minimumsizekey);
2128 	if (var == NOVARIABLE) *minimumsize = (float)DEFAULTMINIMUMSIZE; else
2129 		*minimumsize = castfloat(var->addr);
2130 }
2131 
2132 /*
2133  * Routine to set global factors "metalthickness", "lrscompensation", "globalcompensation", and
2134  * "minimumsize" onto library "lib".
2135  */
compen_setglobalfactors(float metalthickness,float lrscompensation,float globalcompensation,float minimumsize,LIBRARY * lib)2136 void compen_setglobalfactors(float metalthickness, float lrscompensation, float globalcompensation,
2137 	float minimumsize, LIBRARY *lib)
2138 {
2139 	setvalkey((INTBIG)lib, VLIBRARY, compen_metalthicknesskey,
2140 		(INTBIG)castint(metalthickness), VFLOAT);
2141 	setvalkey((INTBIG)lib, VLIBRARY, compen_lrscompensationkey,
2142 		(INTBIG)castint(lrscompensation), VFLOAT);
2143 	setvalkey((INTBIG)lib, VLIBRARY, compen_globalcompensationkey,
2144 		(INTBIG)castint(globalcompensation), VFLOAT);
2145 	setvalkey((INTBIG)lib, VLIBRARY, compen_minimumsizekey,
2146 		(INTBIG)castint(minimumsize), VFLOAT);
2147 }
2148 
2149 /******************************** ACTUAL COMPENSATION ********************************/
2150 
2151 /*
2152  * routine to examine all of the node instances in the cell and assign any compensation
2153  * percentages to the appropriate contour elements in the data structure.  Also warns the user
2154  * of any nodes with compensation percentages that are not in a contour data structure.
2155  */
compen_assignpercentages(CONTOUR * contourlist,NODEPROTO * np)2156 void compen_assignpercentages(CONTOUR *contourlist, NODEPROTO *np)
2157 {
2158 	REGISTER NODEINST *ni;
2159 	REGISTER INTBIG total;
2160 	BOOLEAN first;
2161 	REGISTER VARIABLE *var;
2162 	REGISTER CONTOUR *con;
2163 	REGISTER CONTOURELEMENT *conel;
2164 	REGISTER USERDATA *ud;
2165 	float metalthickness, lrscompensation, globalcompensation, minimumsize;
2166 	REGISTER void *infstr;
2167 
2168 	/* get global compensation */
2169 	compen_getglobalfactors(&metalthickness, &lrscompensation, &globalcompensation, &minimumsize,
2170 		np->lib);
2171 
2172 	/* mark all nodes that have compensation percentages */
2173 	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
2174 	{
2175 		ni->temp1 = 0;
2176 		var = getvalkey((INTBIG)ni, VNODEINST, VFLOAT, compen_percentagekey);
2177 		if (var != NOVARIABLE) ni->temp1 = 1;
2178 	}
2179 
2180 	/* unmark those nodes that appear in contours */
2181 	for(con = contourlist; con != NOCONTOUR; con = con->nextcontour)
2182 	{
2183 		if (con->valid == 0) continue;
2184 		for(conel = con->firstcontourelement; conel != NOCONTOURELEMENT; conel = conel->nextcontourelement)
2185 		{
2186 			ud = (USERDATA *)conel->userdata;
2187 			ud->percentage = globalcompensation;
2188 			ud->explicitpercentage = 0;
2189 			if (conel->ni != NONODEINST)
2190 			{
2191 				var = getvalkey((INTBIG)conel->ni, VNODEINST, VFLOAT, compen_percentagekey);
2192 				if (var != NOVARIABLE)
2193 				{
2194 					ud->percentage = castfloat(var->addr);
2195 					ud->explicitpercentage = 1;
2196 					conel->ni->temp1 = 0;
2197 				}
2198 			}
2199 		}
2200 	}
2201 
2202 	/* warn about nodes that are not in contours */
2203 	total = 0;
2204 	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
2205 		if (ni->temp1 != 0) total++;
2206 	if (total != 0)
2207 	{
2208 		ttyputerr(M_("%ld node(s) with compensation information are not in contours"), total);
2209 		(void)asktool(us_tool, x_("clear"));
2210 		infstr = initinfstr();
2211 		first = FALSE;
2212 		for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
2213 		{
2214 			if (ni->temp1 == 0) continue;
2215 			if (first) addtoinfstr(infstr, '\n');
2216 			first = TRUE;
2217 			formatinfstr(infstr, x_("CELL=%s FROM=0%lo;0%lo;0;NOBBOX"),
2218 				describenodeproto(np), (INTBIG)ni->geom, (INTBIG)ni->proto->firstportproto);
2219 		}
2220 		(void)asktool(us_tool, x_("show-multiple"), (INTBIG)returninfstr(infstr));
2221 	}
2222 }
2223 
2224 /*
2225  * routine to do the actual compensation to the geometry in the contours.
2226  */
compen_movegeometry(CONTOUR * contourlist,NODEPROTO * np)2227 void compen_movegeometry(CONTOUR *contourlist, NODEPROTO *np)
2228 {
2229 	REGISTER CONTOUR *con;
2230 	REGISTER CONTOURELEMENT *conel;
2231 	REGISTER INTBIG orthosine, orthocosine, distance, radius, closex, closey, radius1, radius2,
2232 		testx1, testy1, testx2, testy2;
2233 	INTBIG x1, y1, x2, y2;
2234 	REGISTER INTBIG ang, orthoang;
2235 	REGISTER USERDATA *ud;
2236 	double dang, dorthoang, dorthosin, dorthocos, dstartsine, dstartcosine, dendsine, dendcosine;
2237 	float metalthickness, lrscompensation, globalcompensation, minimumsize, truecomp;
2238 
2239 	/* get global factors */
2240 	compen_getglobalfactors(&metalthickness, &lrscompensation, &globalcompensation, &minimumsize,
2241 		np->lib);
2242 
2243 	/* loop through every element in every contour */
2244 	for(con = contourlist; con != NOCONTOUR; con = con->nextcontour)
2245 	{
2246 		if (con->valid == 0) continue;
2247 		for(conel = con->firstcontourelement; conel != NOCONTOURELEMENT; conel = conel->nextcontourelement)
2248 		{
2249 			/* remember original positions */
2250 			ud = (USERDATA *)conel->userdata;
2251 			ud->origsx = conel->sx;
2252 			ud->origsy = conel->sy;
2253 			ud->origex = conel->ex;
2254 			ud->origey = conel->ey;
2255 
2256 			if (ud->percentage < 0.0) continue;
2257 
2258 			/* get the actual distance to compensate from the percentage */
2259 			truecomp = compen_truecompensation(ud->percentage, metalthickness, lrscompensation);
2260 			distance = scalefromdispunit(truecomp, DISPUNITMM);
2261 			switch (conel->elementtype)
2262 			{
2263 				case LINESEGMENTTYPE:
2264 				case BRIDGESEGMENTTYPE:
2265 					/* compute the angle that is perpendicular to the line in the direction of compensation */
2266 					dang = compen_figureangle(conel->sx, conel->sy, conel->ex, conel->ey);
2267 					if ((con->depth&1) != 0)
2268 					{
2269 						dorthoang = dang - EPI/2.0;
2270 						if (dorthoang < 0.0) dorthoang += EPI*2.0;
2271 					} else
2272 					{
2273 						dorthoang = dang + EPI/2.0;
2274 						if (dorthoang > EPI*2.0) dorthoang -= EPI*2.0;
2275 					}
2276 
2277 					/* move the endpoints by the appropriate distance in the compensation direction */
2278 					dorthosin = sin(dorthoang);   dorthocos = cos(dorthoang);
2279 					conel->sx = conel->sx + rounddouble(dorthocos * (double)distance);
2280 					conel->sy = conel->sy + rounddouble(dorthosin * (double)distance);
2281 					conel->ex = conel->ex + rounddouble(dorthocos * (double)distance);
2282 					conel->ey = conel->ey + rounddouble(dorthosin * (double)distance);
2283 					break;
2284 
2285 				case ARCSEGMENTTYPE:
2286 				case REVARCSEGMENTTYPE:
2287 					/* get the first line segment that approximates this arc */
2288 					initcontoursegmentgeneration(conel);
2289 					(void)nextcontoursegmentgeneration(&x1, &y1, &x2, &y2);
2290 
2291 					/* compute the angle that is perpendicular to the approximating line */
2292 					ang = figureangle(x1, y1, x2, y2);		/* OK to do this in tenth-degree approximations */
2293 					if ((con->depth&1) != 0) orthoang = (ang+2700) % 3600; else
2294 						 orthoang = (ang+900) % 3600;
2295 
2296 					/*
2297 					 * move the starting point by the appropriate distance in the compensation direction.
2298 					 * this is only an approximate to the compensated starting point since it is derived
2299 					 * from an approximating line segment
2300 					 */
2301 					orthosine = sine(orthoang);   orthocosine = cosine(orthoang);
2302 					closex = x1 + mult(orthocosine, distance);
2303 					closey = y1 + mult(orthosine, distance);
2304 
2305 					/* now compute the arc radius and the two possible changes to it */
2306 					radius = computedistance(conel->cx, conel->cy, conel->sx, conel->sy);
2307 					radius1 = radius + distance;
2308 					radius2 = radius - distance;
2309 
2310 					/* compute the precise starting point location for the two possible radius changes */
2311 					dang = compen_figureangle(conel->cx, conel->cy, conel->sx, conel->sy);
2312 					dstartsine = sin(dang);   dstartcosine = cos(dang);
2313 					dang = compen_figureangle(conel->cx, conel->cy, conel->ex, conel->ey);
2314 					dendsine = sin(dang);   dendcosine = cos(dang);
2315 					testx1 = conel->cx + rounddouble(dstartcosine * (double)radius1);
2316 					testy1 = conel->cy + rounddouble(dstartsine * (double)radius1);
2317 					testx2 = conel->cx + rounddouble(dstartcosine * (double)radius2);
2318 					testy2 = conel->cy + rounddouble(dstartsine * (double)radius2);
2319 
2320 					/* see which starting point is closest to the approximate one */
2321 					if (computedistance(closex,closey, testx1,testy1) <
2322 						computedistance(closex,closey, testx2,testy2))
2323 					{
2324 						/* first radius is correct */
2325 						conel->sx = testx1;
2326 						conel->sy = testy1;
2327 						conel->ex = conel->cx + rounddouble(dendcosine * (double)radius1);
2328 						conel->ey = conel->cy + rounddouble(dendsine * (double)radius1);
2329 					} else
2330 					{
2331 						/* second radius is correct */
2332 						conel->sx = testx2;
2333 						conel->sy = testy2;
2334 						conel->ex = conel->cx + rounddouble(dendcosine * (double)radius2);
2335 						conel->ey = conel->cy + rounddouble(dendsine * (double)radius2);
2336 					}
2337 					break;
2338 
2339 				case CIRCLESEGMENTTYPE:
2340 					/* compute the radius of the circle */
2341 					radius = computedistance(conel->cx, conel->cy, conel->sx, conel->sy);
2342 
2343 					/* adjust the radius by the proper amount */
2344 					if ((con->depth&1) != 0) radius -= distance; else
2345 						radius += distance;
2346 
2347 					/* set the new compensated starting point */
2348 					conel->sx = conel->cx + radius;
2349 					conel->sy = conel->cy;
2350 					break;
2351 			}
2352 		}
2353 	}
2354 }
2355 
2356 /*
2357  * Routine to adjust the actual nodes to match the compensated information in cell "np"
2358  */
compen_adjustgeometry(CONTOUR * contourlist,NODEPROTO * np)2359 void compen_adjustgeometry(CONTOUR *contourlist, NODEPROTO *np)
2360 {
2361 	REGISTER CONTOUR *con;
2362 	REGISTER CONTOURELEMENT *conel;
2363 	INTBIG lx, hx, ly, hy;
2364 	REGISTER INTBIG radius, xc, yc;
2365 	INTBIG coord[10];
2366 	REGISTER INTBIG newrot;
2367 	double fx, fy, srot, erot, fswap;
2368 	REGISTER NODEINST *ni, *nextni;
2369 	REGISTER USERDATA *ud;
2370 
2371 	/* mark and delete all nodes that created lines */
2372 	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
2373 		ni->temp1 = 0;
2374 	for(con = contourlist; con != NOCONTOUR; con = con->nextcontour)
2375 	{
2376 		if (con->valid == 0) continue;
2377 		for(conel = con->firstcontourelement; conel != NOCONTOURELEMENT; conel = conel->nextcontourelement)
2378 			if (conel->ni != NONODEINST && conel->elementtype == LINESEGMENTTYPE)
2379 		{
2380 			conel->ni->temp1 = 1;
2381 			conel->ni = NONODEINST;
2382 		}
2383 	}
2384 	for(ni = np->firstnodeinst; ni != NONODEINST; ni = nextni)
2385 	{
2386 		nextni = ni->nextnodeinst;
2387 		if (ni->temp1 == 0) continue;
2388 		startobjectchange((INTBIG)ni, VNODEINST);
2389 		(void)killnodeinst(ni);
2390 	}
2391 
2392 	/* loop through every element in every contour */
2393 	for(con = contourlist; con != NOCONTOUR; con = con->nextcontour)
2394 	{
2395 		if (con->valid == 0) continue;
2396 		for(conel = con->firstcontourelement; conel != NOCONTOURELEMENT; conel = conel->nextcontourelement)
2397 		{
2398 			if (conel->ni == NONODEINST)
2399 			{
2400 				if (conel->elementtype != LINESEGMENTTYPE && conel->elementtype != BRIDGESEGMENTTYPE)
2401 				{
2402 					ttyputmsg(M_("Error adjusting nonline segment with no original geometry"));
2403 					continue;
2404 				}
2405 
2406 				/* create the new line node */
2407 				lx = mini(conel->sx, conel->ex);   hx = maxi(conel->sx, conel->ex);
2408 				ly = mini(conel->sy, conel->ey);   hy = maxi(conel->sy, conel->ey);
2409 				xc  = (lx + hx) / 2;	yc = (ly + hy) / 2;
2410 				conel->ni = newnodeinst(art_openedpolygonprim, lx, hx, ly, hy, 0, 0, np);
2411 				if (conel->ni == NONODEINST) return;
2412 
2413 				/* preserve original information */
2414 				ud = (USERDATA *)conel->userdata;
2415 				coord[PRECOMPMAGIC] = 0xFEED;
2416 				coord[PRECOMPTYPE] = conel->elementtype;
2417 				coord[PRECOMPPERC] = (INTBIG)ud->percentage;
2418 				coord[PRECOMPEXPPERC] = ud->explicitpercentage;
2419 				coord[PRECOMPLINESX] = ud->origsx;
2420 				coord[PRECOMPLINESY] = ud->origsy;
2421 				coord[PRECOMPLINEEX] = ud->origex;
2422 				coord[PRECOMPLINEEY] = ud->origey;
2423 				(void)setvalkey((INTBIG)conel->ni, VNODEINST, compen_precomppositionkey, (INTBIG)coord,
2424 					VINTEGER|VISARRAY|(PRECOMPLINESIZE<<VLENGTHSH));
2425 
2426 				/* store line coordinates */
2427 				coord[0] = conel->sx - xc;		coord[1] = conel->sy - yc;
2428 				coord[2] = conel->ex - xc;		coord[3] = conel->ey - yc;
2429 				(void)setvalkey((INTBIG)conel->ni, VNODEINST, el_trace_key, (INTBIG)coord,
2430 					VINTEGER|VISARRAY|(4<<VLENGTHSH));
2431 
2432 				if (ud->DXFlayer != 0)
2433 					(void)setvalkey((INTBIG)conel->ni, VNODEINST, compen_dxf_layerkey, (INTBIG)ud->DXFlayer, VSTRING);
2434 
2435 				/* assign new GDS layer if this is hole contour */
2436 				if ((con->depth&1) != 0)
2437 					setvalkey((INTBIG)conel->ni, VNODEINST, compen_gds_layerkey, 2, VINTEGER);
2438 
2439 				/* end of changes */
2440 				endobjectchange((INTBIG)conel->ni, VNODEINST);
2441 				continue;
2442 			}
2443 
2444 			switch (conel->elementtype)
2445 			{
2446 				case CIRCLESEGMENTTYPE:
2447 					/* start changes */
2448 					startobjectchange((INTBIG)conel->ni, VNODEINST);
2449 
2450 					/* preserve original information */
2451 					ud = (USERDATA *)conel->userdata;
2452 					coord[PRECOMPMAGIC] = 0xFEED;
2453 					coord[PRECOMPTYPE] = CIRCLESEGMENTTYPE;
2454 					coord[PRECOMPPERC] = (INTBIG)ud->percentage;
2455 					coord[PRECOMPEXPPERC] = ud->explicitpercentage;
2456 					coord[PRECOMPCIRCSX] = ud->origsx;
2457 					coord[PRECOMPCIRCSY] = ud->origsy;
2458 					coord[PRECOMPCIRCRAD] = computedistance(conel->cx, conel->cy, ud->origsx, ud->origsy);
2459 					(void)setvalkey((INTBIG)conel->ni, VNODEINST, compen_precomppositionkey, (INTBIG)coord,
2460 						VINTEGER|VISARRAY|(PRECOMPCIRCSIZE<<VLENGTHSH));
2461 
2462 					/* set new size according to new radius */
2463 					radius = computedistance(conel->cx, conel->cy, conel->sx, conel->sy);
2464 					lx = conel->cx - radius;   hx = conel->cx + radius;
2465 					ly = conel->cy - radius;   hy = conel->cy + radius;
2466 					modifynodeinst(conel->ni, lx - conel->ni->lowx, ly - conel->ni->lowy,
2467 						hx - conel->ni->highx, hy - conel->ni->highy, 0, 0);
2468 
2469 					/* remove compensation percentage color coding if it is there */
2470 					if (getvalkey((INTBIG)conel->ni, VNODEINST, VINTEGER, art_colorkey) != NOVARIABLE)
2471 						(void)delvalkey((INTBIG)conel->ni, VNODEINST, art_colorkey);
2472 
2473 					/* assign new GDS layer if this is hole contour */
2474 					if ((con->depth&1) != 0)
2475 						setvalkey((INTBIG)conel->ni, VNODEINST, compen_gds_layerkey, 2, VINTEGER);
2476 
2477 					/* end of changes */
2478 					endobjectchange((INTBIG)conel->ni, VNODEINST);
2479 					break;
2480 
2481 				case ARCSEGMENTTYPE:
2482 				case REVARCSEGMENTTYPE:
2483 					/* start changes */
2484 					startobjectchange((INTBIG)conel->ni, VNODEINST);
2485 
2486 					/* preserve original information */
2487 					ud = (USERDATA *)conel->userdata;
2488 					coord[PRECOMPMAGIC] = 0xFEED;
2489 					coord[PRECOMPTYPE] = conel->elementtype;
2490 					coord[PRECOMPPERC] = (INTBIG)ud->percentage;
2491 					coord[PRECOMPEXPPERC] = ud->explicitpercentage;
2492 					coord[PRECOMPARCSX] = ud->origsx;
2493 					coord[PRECOMPARCSY] = ud->origsy;
2494 					coord[PRECOMPARCEX] = ud->origex;
2495 					coord[PRECOMPARCEY] = ud->origey;
2496 					coord[PRECOMPARCRAD] = computedistance(conel->cx, conel->cy, ud->origsx, ud->origsy);
2497 					(void)setvalkey((INTBIG)conel->ni, VNODEINST, compen_precomppositionkey, (INTBIG)coord,
2498 						VINTEGER|VISARRAY|(PRECOMPARCSIZE<<VLENGTHSH));
2499 
2500 					/* set new size and arc information */
2501 					radius = computedistance(conel->cx, conel->cy, conel->sx, conel->sy);
2502 					lx = conel->cx - radius;   hx = conel->cx + radius;
2503 					ly = conel->cy - radius;   hy = conel->cy + radius;
2504 					fx = conel->sx - conel->cx;   fy = conel->sy - conel->cy;
2505 					if (fy == 0.0 && fx == 0.0)
2506 					{
2507 						ttyputerr(M_("Domain error compensating arc end 1"));
2508 						break;
2509 					}
2510 					srot = atan2(fy, fx);
2511 					if (srot < 0.0) srot += EPI*2.0;
2512 					fx = conel->ex - conel->cx;   fy = conel->ey - conel->cy;
2513 					if (fy == 0.0 && fx == 0.0)
2514 					{
2515 						ttyputerr(M_("Domain error compensating arc end 2"));
2516 						break;
2517 					}
2518 					erot = atan2(fy, fx);
2519 					if (erot < 0.0) erot += EPI*2.0;
2520 					if (conel->elementtype == REVARCSEGMENTTYPE)
2521 					{
2522 						fswap = srot;   srot = erot;   erot = fswap;
2523 					}
2524 					erot -= srot;
2525 					if (erot < 0.0) erot += EPI*2.0;
2526 					newrot = rounddouble(srot * 1800.0 / EPI);
2527 					srot -= ((double)newrot) * EPI / 1800.0;
2528 					modifynodeinst(conel->ni, lx - conel->ni->lowx, ly - conel->ni->lowy,
2529 						hx - conel->ni->highx, hy - conel->ni->highy,
2530 							newrot - conel->ni->rotation, -conel->ni->transpose);
2531 					setarcdegrees(conel->ni, srot, erot);
2532 
2533 					/* remove compensation percentage color coding if it is there */
2534 					if (getvalkey((INTBIG)conel->ni, VNODEINST, VINTEGER, art_colorkey) != NOVARIABLE)
2535 						(void)delvalkey((INTBIG)conel->ni, VNODEINST, art_colorkey);
2536 
2537 					/* assign new GDS layer if this is hole contour */
2538 					if ((con->depth&1) != 0)
2539 						setvalkey((INTBIG)conel->ni, VNODEINST, compen_gds_layerkey, 2, VINTEGER);
2540 
2541 					/* end of changes */
2542 					endobjectchange((INTBIG)conel->ni, VNODEINST);
2543 					break;
2544 
2545 				default:
2546 					break;
2547 			}
2548 		}
2549 	}
2550 }
2551 
2552 /******************************** CONTOUR CLEANING/BLENDING ********************************/
2553 
2554 /*
2555  * routine to clean-up the contours by adjusting the geometry.  If "blend" is true, blend endpoints
2556  * precisely.  Otherwise, simply insert bridge-line segments where the ends don't meet.
2557  */
compen_cleancontours(CONTOUR * contourlist,NODEPROTO * np,BOOLEAN blend)2558 void compen_cleancontours(CONTOUR *contourlist, NODEPROTO *np, BOOLEAN blend)
2559 {
2560 	REGISTER CONTOUR *con;
2561 	REGISTER CONTOURELEMENT *conel, *lastconel, *nextconel;
2562 
2563 	if (!blend) compen_debugdump(M_("CLEANING QUICKLY")); else
2564 		compen_debugdump(M_("\nBLENDING PROPERLY"));
2565 
2566 	for(con = contourlist; con != NOCONTOUR; con = con->nextcontour)
2567 	{
2568 		if (con->valid == 0) continue;
2569 
2570 		/* remove bridge segments */
2571 		lastconel = NOCONTOURELEMENT;
2572 		for(conel = con->firstcontourelement; conel != NOCONTOURELEMENT; conel = nextconel)
2573 		{
2574 			nextconel = conel->nextcontourelement;
2575 			if (conel->elementtype == BRIDGESEGMENTTYPE)
2576 			{
2577 				if (lastconel == NOCONTOURELEMENT) con->firstcontourelement = nextconel; else
2578 					lastconel->nextcontourelement = nextconel;
2579 				efree((CHAR *)conel);
2580 			} else lastconel = conel;
2581 		}
2582 		con->lastcontourelement = lastconel;
2583 
2584 		/* force all segments to meet */
2585 		for(conel = con->firstcontourelement; conel != NOCONTOURELEMENT; conel = nextconel)
2586 		{
2587 			nextconel = conel->nextcontourelement;
2588 			if (conel != con->firstcontourelement)
2589 				compen_forcemeeting(con, lastconel, conel, blend);
2590 			lastconel = conel;
2591 		}
2592 		compen_forcemeeting(con, lastconel, con->firstcontourelement, blend);
2593 	}
2594 }
2595 
2596 /*
2597  * routine to clean-up two contour elements "firstconel" and "secondconel" on contour "con" by adjusting
2598  * their geometry.  If "blend" is true, blend endpoints precisely.  Otherwise, simply insert
2599  * bridge-line segments where the ends don't meet.
2600  */
compen_forcemeeting(CONTOUR * con,CONTOURELEMENT * firstconel,CONTOURELEMENT * secondconel,BOOLEAN blend)2601 void compen_forcemeeting(CONTOUR *con, CONTOURELEMENT *firstconel, CONTOURELEMENT *secondconel, BOOLEAN blend)
2602 {
2603 	REGISTER INTBIG a1, a2, impliedintersection, icount;
2604 	double da1, da2, dang;
2605 	INTBIG ix, iy, ix1, iy1, ix2, iy2;
2606 	REGISTER INTBIG arcrad, newix, newiy;
2607 	REGISTER USERDATA *ud1, *ud2;
2608 	CHAR *firstname, *secondname;
2609 
2610 	firstname = secondname = x_("");
2611 #ifdef DEBDUMP
2612 	if (firstconel->ni != NONODEINST)
2613 	{
2614 		REGISTER VARIABLE *var;
2615 		var = getvalkey((INTBIG)firstconel->ni, VNODEINST, VSTRING, el_node_name_key);
2616 		if (var != NOVARIABLE) firstname = (CHAR *)var->addr;
2617 	}
2618 	if (secondconel->ni != NONODEINST)
2619 	{
2620 		REGISTER VARIABLE *var;
2621 		var = getvalkey((INTBIG)secondconel->ni, VNODEINST, VSTRING, el_node_name_key);
2622 		if (var != NOVARIABLE) secondname = (CHAR *)var->addr;
2623 	}
2624 #endif
2625 
2626 	/* cannot make circles meet anything else */
2627 	if (firstconel->elementtype == CIRCLESEGMENTTYPE ||
2628 		secondconel->elementtype == CIRCLESEGMENTTYPE) return;
2629 
2630 	/* if they already meet, exit */
2631 	if (firstconel->ex == secondconel->sx && firstconel->ey == secondconel->sy) return;
2632 
2633 	/* if not blending, simply insert a bridge line segment */
2634 	if (!blend)
2635 	{
2636 		compen_insertbridge(firstconel, secondconel);
2637 		return;
2638 	}
2639 
2640 	/*
2641 	 * see if there is an implied intersection point.
2642 	 * If segments meet with more than 5 degrees difference, blending occurs at implied point
2643 	 * OK to do these in tenth-degree approximations
2644 	 */
2645 	impliedintersection = 0;
2646 	if (firstconel->elementtype == LINESEGMENTTYPE)
2647 	{
2648 		if (firstconel->sx == firstconel->ex && firstconel->sy == firstconel->ey)
2649 		{
2650 			a1 = 0;
2651 			impliedintersection = 1;
2652 		} else
2653 			a1 = figureangle(firstconel->sx, firstconel->sy, firstconel->ex, firstconel->ey);
2654 	} else if (firstconel->elementtype == ARCSEGMENTTYPE ||
2655 		firstconel->elementtype == REVARCSEGMENTTYPE)
2656 	{
2657 		a1 = (figureangle(firstconel->cx, firstconel->cy, firstconel->ex, firstconel->ey) + 900) % 3600;
2658 	}
2659 	if (secondconel->elementtype == LINESEGMENTTYPE)
2660 	{
2661 		if (secondconel->sx == secondconel->ex && secondconel->sy == secondconel->ey)
2662 		{
2663 			a2 = 0;
2664 			impliedintersection = 1;
2665 		} else
2666 			a2 = figureangle(secondconel->ex, secondconel->ey, secondconel->sx, secondconel->sy);
2667 	} else if (secondconel->elementtype == ARCSEGMENTTYPE ||
2668 		secondconel->elementtype == REVARCSEGMENTTYPE)
2669 	{
2670 		a2 = (figureangle(secondconel->cx, secondconel->cy, secondconel->sx, secondconel->sy) + 900) % 3600;
2671 	}
2672 	a1 %= 1800;   a2 %= 1800;
2673 	if (abs(a1-a2) > 50) impliedintersection = 1;
2674 
2675 	/* handle line-to-line blending (rule 1.0) */
2676 	if (firstconel->elementtype == LINESEGMENTTYPE && secondconel->elementtype == LINESEGMENTTYPE)
2677 	{
2678 		/* see if the lines are parallel */
2679 		da1 = compen_figureangle(firstconel->ex, firstconel->ey, firstconel->sx, firstconel->sy);
2680 		da2 = compen_figureangle(secondconel->sx, secondconel->sy, secondconel->ex, secondconel->ey);
2681 		if (!compen_intersect(firstconel->ex, firstconel->ey, da1, secondconel->sx, secondconel->sy, da2, &ix, &iy))
2682 		{
2683 			/* lines are not parallel: adjust the segments to meet at the intersection point */
2684 			compen_debugdump(M_("Line [%s] from (%s,%s) to (%s,%s) and line [%s] from (%s,%s) to (%s,%s)"),
2685 				firstname, latoa(firstconel->sx, 0), latoa(firstconel->sy, 0), latoa(firstconel->ex, 0),
2686 					latoa(firstconel->ey, 0), secondname, latoa(secondconel->sx, 0), latoa(secondconel->sy, 0),
2687 						latoa(secondconel->ex, 0), latoa(secondconel->ey, 0));
2688 			compen_debugdump(M_("  meet at (%s,%s)"), latoa(ix, 0), latoa(iy, 0));
2689 			firstconel->ex = ix;
2690 			firstconel->ey = iy;
2691 			secondconel->sx = ix;
2692 			secondconel->sy = iy;
2693 			return;
2694 		}
2695 
2696 		compen_debugdump(M_("Parallel lines [%s] from (%s,%s) to (%s,%s) and [%s] from (%s,%s) to (%s,%s)!"),
2697 			firstname, latoa(firstconel->sx, 0), latoa(firstconel->sy, 0), latoa(firstconel->ex, 0),
2698 				latoa(firstconel->ey, 0), secondname, latoa(secondconel->sx, 0), latoa(secondconel->sy, 0),
2699 					latoa(secondconel->ex, 0), latoa(secondconel->ey, 0));
2700 
2701 		/* lines are parallel: must insert a bridge segment */
2702 		/* try bridge segment 15 degrees from end of first segment to the intersection of the second segment */
2703 		dang = da1 + 15.0/180.0*EPI;   if (dang > EPI*2.0) dang -= EPI*2.0;
2704 		(void)compen_intersect(firstconel->ex, firstconel->ey, dang, secondconel->sx, secondconel->sy, da2, &ix, &iy);
2705 		if (ix >= mini(secondconel->sx, secondconel->ex) && ix <= maxi(secondconel->sx, secondconel->ex) &&
2706 			iy >= mini(secondconel->sy, secondconel->ey) && iy <= maxi(secondconel->sy, secondconel->ey))
2707 		{
2708 			secondconel->sx = ix;
2709 			secondconel->sy = iy;
2710 			compen_insertbridge(firstconel, secondconel);
2711 			return;
2712 		}
2713 
2714 		/* try bridge segment 15 degrees in the other direction */
2715 		dang = da1 - 15.0/180.0*EPI;   if (dang < 0.0) dang += EPI*2.0;
2716 		(void)compen_intersect(firstconel->ex, firstconel->ey, dang, secondconel->sx, secondconel->sy, da2, &ix, &iy);
2717 		if (ix >= mini(secondconel->sx, secondconel->ex) && ix <= maxi(secondconel->sx, secondconel->ex) &&
2718 			iy >= mini(secondconel->sy, secondconel->ey) && iy <= maxi(secondconel->sy, secondconel->ey))
2719 		{
2720 			secondconel->sx = ix;
2721 			secondconel->sy = iy;
2722 			compen_insertbridge(firstconel, secondconel);
2723 			return;
2724 		}
2725 
2726 		/* can't get 15 degree slope to work: just insert the bridge */
2727 		compen_insertbridge(firstconel, secondconel);
2728 		return;
2729 	}
2730 
2731 	/* handle line-to-arc blending (rule 2.0) */
2732 	if ((firstconel->elementtype == ARCSEGMENTTYPE || firstconel->elementtype == REVARCSEGMENTTYPE) &&
2733 		secondconel->elementtype == LINESEGMENTTYPE)
2734 	{
2735 		compen_debugdump(M_("Arc [%s] from (%s,%s) to (%s,%s) and line [%s] from (%s,%s) to (%s,%s)"), firstname,
2736 			latoa(firstconel->sx, 0), latoa(firstconel->sy, 0), latoa(firstconel->ex, 0), latoa(firstconel->ey, 0),
2737 				secondname, latoa(secondconel->sx, 0), latoa(secondconel->sy, 0), latoa(secondconel->ex, 0),
2738 					latoa(secondconel->ey, 0));
2739 		if (impliedintersection != 0)
2740 		{
2741 			icount = circlelineintersection(firstconel->cx, firstconel->cy, firstconel->sx,
2742 				firstconel->sy, secondconel->sx, secondconel->sy, secondconel->ex,
2743 					secondconel->ey, &ix1, &iy1, &ix2, &iy2, compen_circletangentthresh);
2744 
2745 			/* if both points are on the circle: choose the closest */
2746 			if (icount == 2)
2747 			{
2748 				if (computedistance(secondconel->sx, secondconel->sy, ix1, iy1) >
2749 					computedistance(secondconel->sx, secondconel->sy, ix2, iy2))
2750 				{
2751 					ix1 = ix2;   iy1 = iy2;
2752 				}
2753 			}
2754 			if (icount > 0)
2755 			{
2756 				compen_debugdump(M_("   Rule 2.3 applies for explicit intersection at (%s,%s)"),
2757 					latoa(ix1, 0), latoa(iy1, 0));
2758 				secondconel->sx = ix1;   secondconel->sy = iy1;
2759 				firstconel->ex = ix1;    firstconel->ey = iy1;
2760 				return;
2761 			}
2762 		}
2763 
2764 		/* see if the line intersects the arc (rule 2.2/2.3) */
2765 		if (compen_arcintersection(firstconel, secondconel->sx, secondconel->sy, secondconel->ex, secondconel->ey,
2766 			&ix, &iy))
2767 		{
2768 			/* see if it is rule 2.2 or 2.3 */
2769 			ud1 = (USERDATA *)firstconel->userdata;
2770 			ud2 = (USERDATA *)secondconel->userdata;
2771 			if (fabs(ud1->percentage - ud2->percentage) > 5.0)
2772 			{
2773 				if (compen_insert15degreesegment(secondconel->ex, secondconel->ey, &secondconel->sx, &secondconel->sy,
2774 					firstconel->ex, firstconel->ey))
2775 				{
2776 					compen_insertbridge(firstconel, secondconel);
2777 					compen_debugdump(M_("   Rule 2.2 applies"));
2778 					return;
2779 				}
2780 				compen_debugdump(M_("   Rule 2.2 should be used but cannot"));
2781 			}
2782 			compen_debugdump(M_("   Rule 2.3 applies, intersection at (%s,%s)"), latoa(ix, 0), latoa(iy, 0));
2783 			secondconel->sx = ix;   secondconel->sy = iy;
2784 			firstconel->ex = ix;    firstconel->ey = iy;
2785 			return;
2786 		}
2787 
2788 		/* line does not intersect arc: extend arc tangent to line (rule 2.1) */
2789 		if (compen_arctangent(firstconel, firstconel->ex, firstconel->ey, secondconel, secondconel->sx, secondconel->sy,
2790 			&ix, &iy))
2791 		{
2792 			firstconel->ex = ix;    firstconel->ey = iy;
2793 			compen_debugdump(M_("   Rule 2.1 applies, intersection at (%s,%s)"), latoa(ix, 0), latoa(iy, 0));
2794 			compen_insertbridge(firstconel, secondconel);
2795 			return;
2796 		}
2797 
2798 		/* failure to implement rule 2: replace smaller arc with straight line */
2799 		compen_debugdump(M_("   Rule 2.1a applies (straightening)"));
2800 		compen_straighten(firstconel, secondconel);
2801 		return;
2802 	}
2803 	if (firstconel->elementtype == LINESEGMENTTYPE &&
2804 		(secondconel->elementtype == ARCSEGMENTTYPE || secondconel->elementtype == REVARCSEGMENTTYPE))
2805 	{
2806 		compen_debugdump(M_("Arc [%s] from (%s,%s) to (%s,%s) and line [%s] from (%s,%s) to (%s,%s)"), secondname,
2807 			latoa(secondconel->sx, 0), latoa(secondconel->sy, 0), latoa(secondconel->ex, 0), latoa(secondconel->ey, 0),
2808 				firstname, latoa(firstconel->sx, 0), latoa(firstconel->sy, 0), latoa(firstconel->ex, 0),
2809 					latoa(firstconel->ey, 0));
2810 		if (impliedintersection != 0)
2811 		{
2812 			icount = circlelineintersection(secondconel->cx, secondconel->cy, secondconel->sx,
2813 				secondconel->sy, firstconel->ex, firstconel->ey, firstconel->sx,
2814 					firstconel->sy, &ix1, &iy1, &ix2, &iy2, compen_circletangentthresh);
2815 
2816 			/* if both points are on the circle: choose the closest */
2817 			if (icount == 2)
2818 			{
2819 				if (computedistance(firstconel->ex, firstconel->ey, ix1, iy1) >
2820 					computedistance(firstconel->ex, firstconel->ey, ix2, iy2))
2821 				{
2822 					ix1 = ix2;   iy1 = iy2;
2823 				}
2824 			}
2825 			if (icount > 0)
2826 			{
2827 				compen_debugdump(M_("   Rule 2.3 applies for explicit intersection at (%s,%s)"),
2828 					latoa(ix1, 0), latoa(iy1, 0));
2829 				secondconel->sx = ix1;   secondconel->sy = iy1;
2830 				firstconel->ex = ix1;    firstconel->ey = iy1;
2831 				return;
2832 			}
2833 		}
2834 
2835 		/* see if the line intersects the arc (rule 2.2/2.3) */
2836 		if (compen_arcintersection(secondconel, firstconel->ex, firstconel->ey, firstconel->sx, firstconel->sy,
2837 			&ix, &iy))
2838 		{
2839 			/* see if it is rule 2.2 or 2.3 */
2840 			ud1 = (USERDATA *)firstconel->userdata;
2841 			ud2 = (USERDATA *)secondconel->userdata;
2842 			if (fabs(ud1->percentage - ud2->percentage) > 5.0)
2843 			{
2844 				/* use bridge segment 15 degrees from end of arc to the intersection of the line */
2845 				if (compen_insert15degreesegment(firstconel->sx, firstconel->sy, &firstconel->ex, &firstconel->ey,
2846 					secondconel->sx, secondconel->sy))
2847 				{
2848 					compen_insertbridge(firstconel, secondconel);
2849 					compen_debugdump(M_("   Rule 2.2 applies"));
2850 					return;
2851 				}
2852 				compen_debugdump(M_("   Rule 2.2 should be used but cannot"));
2853 			}
2854 			compen_debugdump(M_("   Rule 2.3 applies, intersection at (%s,%s)"), latoa(ix, 0), latoa(iy, 0));
2855 			secondconel->sx = ix;   secondconel->sy = iy;
2856 			firstconel->ex = ix;    firstconel->ey = iy;
2857 			return;
2858 		}
2859 
2860 		/* line does not intersect arc: extend arc tangent to line (rule 2.1) */
2861 		if (compen_arctangent(secondconel, secondconel->sx, secondconel->sy, firstconel, firstconel->ex, firstconel->ey,
2862 			&ix, &iy))
2863 		{
2864 			compen_debugdump(M_("   Rule 2.1 applies, intersection at (%s,%s)"), latoa(ix, 0), latoa(iy, 0));
2865 			secondconel->sx = ix;   secondconel->sy = iy;
2866 			compen_insertbridge(firstconel, secondconel);
2867 			return;
2868 		}
2869 
2870 		/* failure to implement rule 2: replace smaller arc with straight line */
2871 		compen_debugdump(M_("   Rule 2.1a applies (straightening)"));
2872 		compen_straighten(firstconel, secondconel);
2873 		return;
2874 	}
2875 
2876 	/* handle arc-to-arc blending (rule 3.0) */
2877 	if ((firstconel->elementtype == ARCSEGMENTTYPE || firstconel->elementtype == REVARCSEGMENTTYPE) &&
2878 		(secondconel->elementtype == ARCSEGMENTTYPE || secondconel->elementtype == REVARCSEGMENTTYPE))
2879 	{
2880 		compen_debugdump(M_("Arc [%s] from (%s,%s) to (%s,%s) and Arc [%s] from (%s,%s) to (%s,%s)"), secondname,
2881 			latoa(secondconel->sx, 0), latoa(secondconel->sy, 0), latoa(secondconel->ex, 0), latoa(secondconel->ey, 0),
2882 				firstname, latoa(firstconel->sx, 0), latoa(firstconel->sy, 0), latoa(firstconel->ex, 0),
2883 					latoa(firstconel->ey, 0));
2884 		if (firstconel->elementtype == secondconel->elementtype)
2885 		{
2886 			/* curvature is the same: use rules 3.0 */
2887 			/* draw from first endpoint to tangent on second arc */
2888 			if (compen_arctangent(secondconel, secondconel->sx, secondconel->sy, firstconel, firstconel->ex, firstconel->ey,
2889 				&ix, &iy))
2890 			{
2891 				compen_debugdump(M_("   Rule 3.0 applies, intersection at (%s,%s)"), latoa(ix, 0), latoa(iy, 0));
2892 				secondconel->sx = ix;   secondconel->sy = iy;
2893 				compen_insertbridge(firstconel, secondconel);
2894 				return;
2895 			}
2896 
2897 			/* draw from second endpoint to tangent on first arc */
2898 			if (compen_arctangent(firstconel, firstconel->ex, firstconel->ey, secondconel, secondconel->sx, secondconel->sy,
2899 				&ix, &iy))
2900 			{
2901 				compen_debugdump(M_("   Rule 3.0 applies, intersection at (%s,%s)"), latoa(ix, 0), latoa(iy, 0));
2902 				firstconel->ex = ix;    firstconel->ey = iy;
2903 				compen_insertbridge(firstconel, secondconel);
2904 				return;
2905 			}
2906 
2907 			/* failure to implement rule 3.0: replace smaller arc with straight line */
2908 			compen_debugdump(M_("   Rule 3.0 fails (straightening)"));
2909 			compen_straighten(firstconel, secondconel);
2910 			return;
2911 		} else
2912 		{
2913 			/* curvature is different: use rules 3.0a and 3.0b */
2914 			arcrad = computedistance(firstconel->cx, firstconel->cy, firstconel->sx, firstconel->sy);
2915 			ix = (firstconel->ex + secondconel->sx) / 2;
2916 			iy = (firstconel->ey + secondconel->sy) / 2;
2917 			dang = compen_figureangle(firstconel->ex, firstconel->ey, secondconel->sx, secondconel->sy);
2918 			dang += EPI / 2.0;   if (dang > EPI*2.0) dang -= EPI*2.0;
2919 			newix = ix + rounddouble(cos(dang) * (double)arcrad);
2920 			newiy = iy + rounddouble(sin(dang) * (double)arcrad);
2921 
2922 			/* see if each arc has an intersection perpendicular to the midpoint (rule 3.0a) */
2923 			if (compen_arcintersection(firstconel, ix, iy, newix, newiy, &ix1, &iy1) &&
2924 				compen_arcintersection(secondconel, ix, iy, newix, newiy, &ix2, &iy2))
2925 			{
2926 				compen_debugdump(M_("   Rule 3.0a applies, intersection from (%s,%s) to (%s,%s)"),
2927 					latoa(ix1, 0), latoa(iy1, 0), latoa(ix2, 0), latoa(iy2, 0));
2928 				firstconel->ex = ix1;    firstconel->ey = iy1;
2929 				secondconel->ex = ix2;   secondconel->ey = iy2;
2930 				compen_insertbridge(firstconel, secondconel);
2931 				return;
2932 			}
2933 
2934 			/* use tangent points (rule 3.0b) */
2935 			if (compen_arctangent(firstconel, firstconel->ex, firstconel->ey, NOCONTOURELEMENT, ix, iy, &ix1, &iy1) &&
2936 				compen_arctangent(secondconel, secondconel->sx, secondconel->sy, NOCONTOURELEMENT, ix, iy, &ix2, &iy2))
2937 			{
2938 				compen_debugdump(M_("   Rule 3.0b applies, intersection from (%s,%s) to (%s,%s)"),
2939 					latoa(ix1, 0), latoa(iy1, 0), latoa(ix2, 0), latoa(iy2, 0));
2940 				firstconel->ex = ix1;    firstconel->ey = iy1;
2941 				secondconel->ex = ix2;   secondconel->ey = iy2;
2942 				compen_insertbridge(firstconel, secondconel);
2943 				return;
2944 			}
2945 
2946 			/* cannot figure it out: just insert the bridge */
2947 			compen_debugdump(M_("   No 3.0a/b Rule applies!"));
2948 			compen_insertbridge(firstconel, secondconel);
2949 			return;
2950 		}
2951 	}
2952 
2953 	/* this should never happen!!! */
2954 	compen_insertbridge(firstconel, secondconel);
2955 }
2956 
2957 /*
2958  * routine to see if a segment can be inserted between point (fromx,fromy) and the line segment (linesx,linesy) to
2959  * (*lineex,*lineey).  The segment must be 15 degrees offset from the line segment and must fall on the segment.
2960  * If such a line is possible, then the point (*lineex,*lineey) is adjusted to be at that point and the routine
2961  * returns true.
2962  */
compen_insert15degreesegment(INTBIG linesx,INTBIG linesy,INTBIG * lineex,INTBIG * lineey,INTBIG fromx,INTBIG fromy)2963 BOOLEAN compen_insert15degreesegment(INTBIG linesx, INTBIG linesy, INTBIG *lineex, INTBIG *lineey, INTBIG fromx, INTBIG fromy)
2964 {
2965 	double a1, ang;
2966 	INTBIG ix, iy;
2967 
2968 	a1 = compen_figureangle(*lineex, *lineey, linesx, linesy);
2969 	ang = a1 + 15.0/180.0*EPI;
2970 	if (ang > EPI*2.0) ang -= EPI*2.0;
2971 	(void)compen_intersect(fromx, fromy, ang, *lineex, *lineey, a1, &ix, &iy);
2972 	if (ix >= mini(linesx, *lineex) && ix <= maxi(linesx, *lineex) &&
2973 		iy >= mini(linesy, *lineey) && iy <= maxi(linesy, *lineey))
2974 	{
2975 		*lineex = ix;
2976 		*lineey = iy;
2977 		return(TRUE);
2978 	}
2979 
2980 	ang = a1 - 15.0/180.0*EPI;
2981 	if (ang < 0.0) ang += EPI*2.0;
2982 	(void)compen_intersect(fromx, fromy, ang, *lineex, *lineey, a1, &ix, &iy);
2983 	if (ix >= mini(linesx, *lineex) && ix <= maxi(linesx, *lineex) &&
2984 		iy >= mini(linesy, *lineey) && iy <= maxi(linesy, *lineey))
2985 	{
2986 		*lineex = ix;
2987 		*lineey = iy;
2988 		return(TRUE);
2989 	}
2990 	return(FALSE);
2991 }
2992 
2993 
2994 /*
2995  * routine to straighten out one of the curved segments "firstconel" or "secondconel" to implement
2996  * rule 2.1a (when the curves are too small to be blended properly).
2997  */
compen_straighten(CONTOURELEMENT * firstconel,CONTOURELEMENT * secondconel)2998 void compen_straighten(CONTOURELEMENT *firstconel, CONTOURELEMENT *secondconel)
2999 {
3000 	REGISTER INTBIG ang1, ang2;
3001 	double startoffset, endangle;
3002 
3003 	/* get the angles of the two segments */
3004 	ang1 = ang2 = SMALLANGLETHRESH+1;
3005 	if (firstconel->elementtype == ARCSEGMENTTYPE || firstconel->elementtype == REVARCSEGMENTTYPE)
3006 	{
3007 		getarcdegrees(firstconel->ni, &startoffset, &endangle);
3008 		if (startoffset != 0.0 || endangle != 0.0)
3009 			ang1 = rounddouble(startoffset + endangle);
3010 	}
3011 	if (secondconel->elementtype == ARCSEGMENTTYPE || secondconel->elementtype == REVARCSEGMENTTYPE)
3012 	{
3013 		getarcdegrees(secondconel->ni, &startoffset, &endangle);
3014 		if (startoffset != 0.0 || endangle != 0.0)
3015 			ang2 = rounddouble(startoffset + endangle);
3016 	}
3017 
3018 	/* if both are small angle arcs, choose the smaller one */
3019 	if (ang1 <= SMALLANGLETHRESH && ang2 <= SMALLANGLETHRESH)
3020 	{
3021 		if (ang1 < ang2) ang2 = SMALLANGLETHRESH+1; else
3022 			ang1 = SMALLANGLETHRESH+1;
3023 	}
3024 
3025 	/* straighten out the smaller angle arc */
3026 	if (ang1 <= SMALLANGLETHRESH)
3027 	{
3028 		/* straighten out first element and make it join first */
3029 		firstconel->elementtype = LINESEGMENTTYPE;
3030 		firstconel->ex = secondconel->sx;
3031 		firstconel->ey = secondconel->sy;
3032 		return;
3033 	}
3034 	if (ang2 <= SMALLANGLETHRESH)
3035 	{
3036 		/* straighten out second element and make it join first */
3037 		secondconel->elementtype = LINESEGMENTTYPE;
3038 		secondconel->sx = firstconel->ex;
3039 		secondconel->sy = firstconel->ey;
3040 		return;
3041 	}
3042 
3043 	/* no small arcs: just insert a straight line */
3044 	compen_insertbridge(firstconel, secondconel);
3045 }
3046 
3047 /*
3048  * routine to determine whether the line from (x,y) to (otherx,othery) intersects the arc element
3049  * "arcconel".  If it does, the routine returns true and sets the intersection point to (ix,iy).
3050  * The first coordinate of the line (x,y) is presumed to be the closest to the desired arc
3051  * intersection point.
3052  */
compen_arcintersection(CONTOURELEMENT * arcconel,INTBIG x,INTBIG y,INTBIG otherx,INTBIG othery,INTBIG * ix,INTBIG * iy)3053 BOOLEAN compen_arcintersection(CONTOURELEMENT *arcconel, INTBIG x, INTBIG y, INTBIG otherx, INTBIG othery,
3054 	INTBIG *ix, INTBIG *iy)
3055 {
3056 	REGISTER INTBIG icount, off1, off2;
3057 	INTBIG ix1, iy1, ix2, iy2;
3058 
3059 	icount = circlelineintersection(arcconel->cx, arcconel->cy, arcconel->sx, arcconel->sy,
3060 		x, y, otherx, othery, &ix1, &iy1, &ix2, &iy2, compen_circletangentthresh);
3061 
3062 	/* eliminate points that are not on the arc (unless both are off) */
3063 	if (icount == 2)
3064 	{
3065 		off1 = compen_pointoffarc(arcconel, ix1, iy1);
3066 		off2 = compen_pointoffarc(arcconel, ix2, iy2);
3067 		if (off1 == 0 && off2 != 0)
3068 		{
3069 			icount = 1;
3070 		} else if (off1 != 0 && off2 == 0)
3071 		{
3072 			icount = 1;
3073 			ix1 = ix2;   iy1 = iy2;
3074 		}
3075 	}
3076 
3077 	/* if both points are on the arc: choose the closest */
3078 	if (icount == 2)
3079 	{
3080 		if (computedistance(x, y, ix1, iy1) > computedistance(x, y, ix2, iy2))
3081 		{
3082 			ix1 = ix2;   iy1 = iy2;
3083 		}
3084 	}
3085 
3086 	/* if there is an intersection point, return it */
3087 	if (icount >= 1)
3088 	{
3089 		*ix = ix1;
3090 		*iy = iy1;
3091 		return(TRUE);
3092 	}
3093 	return(FALSE);
3094 }
3095 
3096 /*
3097  * routine to find the tangent point(s) on an arc that connect to a given point.
3098  * The arc is "arcconel" and the end of the arc that *SHOULD* be close to the tangents is
3099  * (prefx, prefy).  The point is (x,y) and it may be on arc "otherconel" (if it is not
3100  * NOCONTOURELEMENT). If a tangent is found, it is put in (ix,iy) and the routine returns true.
3101  */
compen_arctangent(CONTOURELEMENT * arcconel,INTBIG prefx,INTBIG prefy,CONTOURELEMENT * otherconel,INTBIG x,INTBIG y,INTBIG * ix,INTBIG * iy)3102 BOOLEAN compen_arctangent(CONTOURELEMENT *arcconel, INTBIG prefx, INTBIG prefy,
3103 	CONTOURELEMENT *otherconel, INTBIG x, INTBIG y, INTBIG *ix, INTBIG *iy)
3104 {
3105 	INTBIG ix1, iy1, ix2, iy2, x1, y1, x2, y2;
3106 	REGISTER INTBIG pt1offarc, pt2offarc, ang, angt1, angt2, diff1, diff2;
3107 
3108 	if (circletangents(x, y, arcconel->cx, arcconel->cy, arcconel->sx, arcconel->sy, &ix1, &iy1, &ix2, &iy2))
3109 		return(FALSE);
3110 	pt1offarc = compen_pointoffarc(arcconel, ix1, iy1);
3111 	pt2offarc = compen_pointoffarc(arcconel, ix2, iy2);
3112 
3113 	/* decide which tangent to use if both are possible */
3114 	if (pt1offarc == 0 && pt2offarc == 0)
3115 	{
3116 		/* use minimum distance to distinguish the proper tangent */
3117 		if (computedistance(prefx, prefy, ix1, iy1) > computedistance(prefx, prefy, ix2, iy2))
3118 			pt1offarc = 1; else
3119 				pt2offarc = 1;
3120 
3121 		/* if there is a contour element on the point, make sure the tangent is in the right direction */
3122 		if (otherconel != NOCONTOURELEMENT)
3123 		{
3124 			initcontoursegmentgeneration(otherconel);
3125 			(void)nextcontoursegmentgeneration(&x1, &y1, &x2, &y2);
3126 			if (x1 != x || y1 != y)
3127 			{
3128 				for(;;)
3129 				{
3130 					if (nextcontoursegmentgeneration(&x2, &y2, &x1, &y1)) break;
3131 				}
3132 			}
3133 
3134 			/* check angle about (x,y) between (x2,y2) and intersection points */
3135 			if ((x != x2 || y != y2) && (x != ix1 || y != iy1) && (x != ix2 || y != iy2))
3136 			{
3137 				ang = figureangle(x, y, x2, y2);
3138 				angt1 = figureangle(x, y, ix1, iy1);
3139 				angt2 = figureangle(x, y, ix2, iy2);
3140 				diff1 = abs(angt1-ang);   if (diff1 > 1800) diff1 = 3600 - diff1;
3141 				diff2 = abs(angt2-ang);   if (diff2 > 1800) diff2 = 3600 - diff2;
3142 				if (diff1 > diff2) { pt1offarc = 0; pt2offarc = 1; } else
3143 					{ pt1offarc = 1; pt2offarc = 0; }
3144 			}
3145 		}
3146 	}
3147 
3148 	/* see if either tangent can be used if both are off of the arc */
3149 	if (pt1offarc != 0 && pt2offarc != 0)
3150 	{
3151 		if (pt1offarc < pt2offarc)
3152 		{
3153 			if (pt1offarc < ARCSLOP) pt1offarc = 0;
3154 		} else
3155 		{
3156 			if (pt2offarc < ARCSLOP) pt2offarc = 0;
3157 		}
3158 	}
3159 
3160 	/* return the selected tangent */
3161 	if (pt1offarc == 0)
3162 	{
3163 		*ix = ix1;
3164 		*iy = iy1;
3165 		return(TRUE);
3166 	}
3167 	if (pt2offarc == 0)
3168 	{
3169 		*ix = ix2;
3170 		*iy = iy2;
3171 		return(TRUE);
3172 	}
3173 
3174 	/* failure to find a tangent */
3175 	return(FALSE);
3176 }
3177 
3178 /*
3179  * routine to insert a bridge segment between contour elements "firstconel" and "secondconel".
3180  */
compen_insertbridge(CONTOURELEMENT * firstconel,CONTOURELEMENT * secondconel)3181 void compen_insertbridge(CONTOURELEMENT *firstconel, CONTOURELEMENT *secondconel)
3182 {
3183 	REGISTER CONTOURELEMENT *bridgeconel, *swapconel;
3184 	REGISTER USERDATA *ud, *ud1, *ud2;
3185 
3186 	/* make sure the two elements are in the right order */
3187 	if (secondconel->nextcontourelement == firstconel ||
3188 		(firstconel->nextcontourelement != secondconel && secondconel->nextcontourelement == NOCONTOURELEMENT))
3189 	{
3190 		swapconel = firstconel;   firstconel = secondconel;   secondconel = swapconel;
3191 	}
3192 
3193 	/* stop if no bridge needed */
3194 	if (firstconel->ex == secondconel->sx && firstconel->ey == secondconel->sy) return;
3195 
3196 	/* create the bridge element */
3197 	bridgeconel = (CONTOURELEMENT *)emalloc(sizeof (CONTOURELEMENT), compen_tool->cluster);
3198 	if (bridgeconel == 0) return;
3199 	ud = (USERDATA *)emalloc(sizeof (USERDATA), compen_tool->cluster);
3200 	if (ud == 0) return;
3201 	bridgeconel->userdata = (INTBIG)ud;
3202 	bridgeconel->elementtype = BRIDGESEGMENTTYPE;
3203 	bridgeconel->ni = NONODEINST;
3204 	ud->origsx = bridgeconel->sx = firstconel->ex;
3205 	ud->origsy = bridgeconel->sy = firstconel->ey;
3206 	ud->origex = bridgeconel->ex = secondconel->sx;
3207 	ud->origey = bridgeconel->ey = secondconel->sy;
3208 	ud->DXFlayer = 0;
3209 	ud->percentage = -1.0;
3210 	ud->lowx = ud->highx = 0;
3211 	ud->lowy = ud->highy = 0;
3212 	ud1 = (USERDATA *)firstconel->userdata;
3213 	ud2 = (USERDATA *)secondconel->userdata;
3214 	if (ud1->DXFlayer != 0)
3215 		(void)allocstring(&ud->DXFlayer, ud1->DXFlayer, compen_tool->cluster); else
3216 			if (ud2->DXFlayer != 0)
3217 				(void)allocstring(&ud->DXFlayer, ud2->DXFlayer, compen_tool->cluster);
3218 	bridgeconel->nextcontourelement = firstconel->nextcontourelement;
3219 	firstconel->nextcontourelement = bridgeconel;
3220 	compen_debugdump(M_("Inserting bridge from (%s,%s) to (%s,%s)"), latoa(bridgeconel->sx, 0),
3221 		latoa(bridgeconel->sy, 0), latoa(bridgeconel->ex, 0), latoa(bridgeconel->ey, 0));
3222 }
3223 
3224 /******************************** CONTOUR NESTING DETERMINATION ********************************/
3225 
3226 /*
3227  * routine to examine each contour and create a parent/child tree that indicates nexting.
3228  * From this tree, the depth field is determined for each contour.
3229  */
compen_ordercontours(CONTOUR * contourlist,NODEPROTO * np)3230 void compen_ordercontours(CONTOUR *contourlist, NODEPROTO *np)
3231 {
3232 	REGISTER CONTOUR *con;
3233 	CONTOUR toplevel;
3234 	REGISTER INTBIG i;
3235 
3236 	/* initialize the tree, including the static top-level */
3237 	for(con = contourlist; con != NOCONTOUR; con = con->nextcontour) con->childcount = 0;
3238 	toplevel.childtotal = 0;
3239 	toplevel.childcount = 0;
3240 
3241 	/* look at every contour and place it in the tree */
3242 	for(con = contourlist; con != NOCONTOUR; con = con->nextcontour)
3243 	{
3244 		if (con->valid == 0) continue;
3245 		compen_insertcontour(con, &toplevel);
3246 	}
3247 
3248 	/* now compute depth factors */
3249 	for(i=0; i<toplevel.childcount; i++)
3250 		compen_assigndepth(toplevel.children[i], 0);
3251 }
3252 
3253 /*
3254  * routine to recursively assign contour depth from the nesting tree.
3255  */
compen_assigndepth(CONTOUR * con,INTBIG depth)3256 void compen_assigndepth(CONTOUR *con, INTBIG depth)
3257 {
3258 	REGISTER INTBIG i;
3259 
3260 	con->depth = (INTSML)depth;
3261 	for(i=0; i<con->childcount; i++)
3262 		compen_assigndepth(con->children[i], depth+1);
3263 }
3264 
3265 /*
3266  * routine to return true if contour "lower" is inside of contour "higher"
3267  */
compen_isinside(CONTOUR * lower,CONTOUR * higher)3268 BOOLEAN compen_isinside(CONTOUR *lower, CONTOUR *higher)
3269 {
3270 	REGISTER INTBIG x, y, rad, dist;
3271 	INTBIG x1, y1, x2, y2;
3272 	REGISTER CONTOURELEMENT *conel;
3273 	REGISTER INTBIG angles, ang, lastp, tang, thisp;
3274 
3275 	/* trivial reject if bounding boxes don't overlap */
3276 	if (lower->hx < higher->lx || lower->lx > higher->hx ||
3277 		lower->hy < higher->ly || lower->ly > higher->hy) return(FALSE);
3278 
3279 	/* general polygon containment by summing angles to vertices */
3280 	x = lower->firstcontourelement->sx;
3281 	y = lower->firstcontourelement->sy;
3282 
3283 	/* special case if contour is a circle */
3284 	conel = higher->firstcontourelement;
3285 	if (conel->elementtype == CIRCLESEGMENTTYPE)
3286 	{
3287 		rad = computedistance(conel->cx, conel->cy, conel->sx, conel->sy);
3288 		dist = computedistance(conel->cx, conel->cy, x, y);
3289 		if (dist <= rad) return(TRUE);
3290 		return(FALSE);
3291 	}
3292 
3293 	ang = 0;
3294 	angles = 0;
3295 	lastp = figureangle(x, y, higher->firstcontourelement->sx, higher->firstcontourelement->sy);
3296 	for(conel = higher->firstcontourelement; conel != NOCONTOURELEMENT; conel = conel->nextcontourelement)
3297 	{
3298 		if (conel->elementtype == ARCSEGMENTTYPE || conel->elementtype == REVARCSEGMENTTYPE)
3299 		{
3300 			initcontoursegmentgeneration(conel);
3301 			for(;;)
3302 			{
3303 				if (nextcontoursegmentgeneration(&x1, &y1, &x2, &y2)) break;
3304 				thisp = figureangle(x, y, x2, y2);
3305 				tang = lastp - thisp;
3306 				if (tang < -1800) tang += 3600;
3307 				if (tang > 1800) tang -= 3600;
3308 				ang += tang;
3309 				lastp = thisp;
3310 				angles++;
3311 			}
3312 		} else
3313 		{
3314 			thisp = figureangle(x, y, conel->ex, conel->ey);
3315 			tang = lastp - thisp;
3316 			if (tang < -1800) tang += 3600;
3317 			if (tang > 1800) tang -= 3600;
3318 			ang += tang;
3319 			lastp = thisp;
3320 			angles++;
3321 		}
3322 	}
3323 
3324 	if (abs(ang) <= angles) return(FALSE);
3325 	return(TRUE);
3326 }
3327 
3328 /*
3329  * routine to recursively insert contour "newone" into the nesting tree
3330  * that starts at contour "thislevel".
3331  */
compen_insertcontour(CONTOUR * newone,CONTOUR * thislevel)3332 void compen_insertcontour(CONTOUR *newone, CONTOUR *thislevel)
3333 {
3334 	REGISTER INTBIG i, j, oldtotal;
3335 
3336 	/* see if anything at this level is inside of the contour */
3337 	for(i=0; i<thislevel->childcount; i++)
3338 		if (compen_isinside(thislevel->children[i], newone)) break;
3339 	if (i < thislevel->childcount)
3340 	{
3341 		/* contour encloses something at this level: split the level */
3342 		oldtotal = thislevel->childcount;
3343 		thislevel->childcount = 0;
3344 		for(j=0; j<oldtotal; j++)
3345 		{
3346 			if (compen_isinside(thislevel->children[j], newone))
3347 				compen_addchild(newone, thislevel->children[j]); else
3348 					compen_addchild(thislevel, thislevel->children[j]);
3349 		}
3350 	}
3351 
3352 	/* see if this contour is inside any on this level */
3353 	for(i=0; i<thislevel->childcount; i++)
3354 	{
3355 		if (compen_isinside(newone, thislevel->children[i]))
3356 		{
3357 			compen_insertcontour(newone, thislevel->children[i]);
3358 			return;
3359 		}
3360 	}
3361 
3362 	/* not inside of these, add to the list */
3363 	compen_addchild(thislevel, newone);
3364 }
3365 
3366 /*
3367  * routine to insert contour "child" into parent contour "parent".
3368  */
compen_addchild(CONTOUR * parent,CONTOUR * child)3369 void compen_addchild(CONTOUR *parent, CONTOUR *child)
3370 {
3371 	REGISTER INTBIG i, newtotal;
3372 	REGISTER CONTOUR **newchildren;
3373 
3374 	if (parent->childcount >= parent->childtotal)
3375 	{
3376 		newtotal = parent->childcount+5;
3377 		newchildren = (CONTOUR **)emalloc(newtotal * (sizeof (CONTOUR *)),
3378 			compen_tool->cluster);
3379 		if (newchildren == 0) return;
3380 		for(i=0; i<parent->childcount; i++) newchildren[i] = parent->children[i];
3381 		if (parent->childtotal > 0) efree((CHAR *)parent->children);
3382 		parent->children = newchildren;
3383 		parent->childtotal = (INTSML)newtotal;
3384 	}
3385 	parent->children[parent->childcount++] = child;
3386 }
3387 
3388 /******************************** CONTOUR ORIENTATION ********************************/
3389 
3390 #define SCALEFACTOR 2000
3391 
3392 /*
3393  * routine to make sure all contours run clockwise.
3394  */
compen_orientcontours(CONTOUR * contourlist,NODEPROTO * np)3395 void compen_orientcontours(CONTOUR *contourlist, NODEPROTO *np)
3396 {
3397 	REGISTER CONTOUR *con;
3398 	REGISTER CONTOURELEMENT *conel, *lastconel, *nextconel;
3399 	REGISTER INTBIG swap, xd, yd, xfactor, yfactor, area;
3400 	INTBIG x1, y1, x2, y2;
3401 
3402 	for(con = contourlist; con != NOCONTOUR; con = con->nextcontour)
3403 	{
3404 		if (con->valid == 0) continue;
3405 		if (con->hx-con->lx < SCALEFACTOR) xfactor = 1; else xfactor = (con->hx-con->lx) / SCALEFACTOR;
3406 		if (con->hy-con->ly < SCALEFACTOR) yfactor = 1; else yfactor = (con->hy-con->ly) / SCALEFACTOR;
3407 		area = 0;
3408 		for(conel = con->firstcontourelement; conel != NOCONTOURELEMENT; conel = conel->nextcontourelement)
3409 		{
3410 			if (conel->elementtype == ARCSEGMENTTYPE || conel->elementtype == REVARCSEGMENTTYPE)
3411 			{
3412 				initcontoursegmentgeneration(conel);
3413 				for(;;)
3414 				{
3415 					if (nextcontoursegmentgeneration(&x1, &y1, &x2, &y2)) break;
3416 					x1 /= xfactor;   y1 /= yfactor;
3417 					x2 /= xfactor;   y2 /= yfactor;
3418 					xd = x2 - x1;    yd = y2 + y1;
3419 					area += xd * yd / 2;
3420 				}
3421 			} else if (conel->elementtype == BRIDGESEGMENTTYPE || conel->elementtype == LINESEGMENTTYPE)
3422 			{
3423 				xd = conel->ex/xfactor - conel->sx/xfactor;
3424 				yd = conel->ey/yfactor + conel->sy/yfactor;
3425 				area += xd * yd / 2;
3426 			}
3427 		}
3428 
3429 		/* reverse the contour if the area is negative */
3430 		if (area < 0)
3431 		{
3432 			lastconel = NOCONTOURELEMENT;
3433 			for(conel = con->firstcontourelement; conel != NOCONTOURELEMENT; conel = nextconel)
3434 			{
3435 				nextconel = conel->nextcontourelement;
3436 				conel->nextcontourelement = lastconel;
3437 				lastconel = conel;
3438 				switch (conel->elementtype)
3439 				{
3440 					case LINESEGMENTTYPE:
3441 					case BRIDGESEGMENTTYPE:
3442 					case ARCSEGMENTTYPE:
3443 					case REVARCSEGMENTTYPE:
3444 						swap = conel->sx;    conel->sx = conel->ex;    conel->ex = swap;
3445 						swap = conel->sy;    conel->sy = conel->ey;    conel->ey = swap;
3446 						if (conel->elementtype == ARCSEGMENTTYPE) conel->elementtype = REVARCSEGMENTTYPE; else
3447 							if (conel->elementtype == REVARCSEGMENTTYPE) conel->elementtype = ARCSEGMENTTYPE;
3448 						break;
3449 					default:
3450 						break;
3451 				}
3452 			}
3453 			con->firstcontourelement = lastconel;
3454 		}
3455 	}
3456 }
3457 
3458 /******************************** SUPPORT ********************************/
3459 
3460 /*
3461  * Routine to print the distance from (px,py) to the nearest point on the line
3462  * from (fx,fy) to (tx,ty).
3463  */
compen_printdistance(double px,double py,INTBIG fx,INTBIG fy,INTBIG tx,INTBIG ty)3464 void compen_printdistance(double px, double py, INTBIG fx, INTBIG fy, INTBIG tx, INTBIG ty)
3465 {
3466 	double lineangle, perpangle, fa1, fb1, fc1, fa2, fb2, fc2, fswap, ix, iy, dx, dy, dist;
3467 
3468 	/* determine angle of line from (fx,fy) to (tx,ty) */
3469 	if (ty == fy && tx == fx)
3470 	{
3471 		ttyputerr(M_("Domain error examining distance"));
3472 		return;
3473 	}
3474 	lineangle = atan2((double)(ty-fy), (double)(tx-fx));
3475 	if (lineangle < 0.0) lineangle += EPI*2.0;
3476 
3477 	/* determine perpendicular angle */
3478 	perpangle = lineangle + EPI / 2.0;
3479 	if (perpangle > EPI*2.0) perpangle -= EPI*2.0;
3480 
3481 	fa1 = sin(lineangle);   fb1 = -cos(lineangle);
3482 	fc1 = -fa1 * ((double)fx) - fb1 * ((double)fy);
3483 	fa2 = sin(perpangle);   fb2 = -cos(perpangle);
3484 	fc2 = -fa2 * px - fb2 * py;
3485 	if (fabs(fa1) < fabs(fa2))
3486 	{
3487 		fswap = fa1;   fa1 = fa2;   fa2 = fswap;
3488 		fswap = fb1;   fb1 = fb2;   fb2 = fswap;
3489 		fswap = fc1;   fc1 = fc2;   fc2 = fswap;
3490 	}
3491 	iy = (fa2 * fc1 / fa1 - fc2) / (fb2 - fa2*fb1/fa1);
3492 	ix = (-fb1 * iy - fc1) / fa1;
3493 	dx = ix - px;   dy = iy - py;
3494 	dist = sqrt(dx*dx + dy*dy);
3495 	ttyputmsg(M_("         line moved by %s"), latoa(rounddouble(dist), 0));
3496 }
3497 
compen_drawline(INTBIG x1,INTBIG y1,INTBIG x2,INTBIG y2,GRAPHICS * desc)3498 void compen_drawline(INTBIG x1, INTBIG y1, INTBIG x2, INTBIG y2, GRAPHICS *desc)
3499 {
3500 	static POLYGON *poly = NOPOLYGON;
3501 
3502 	/* get polygon */
3503 	(void)needstaticpolygon(&poly, 2, us_tool->cluster);
3504 
3505 	poly->xv[0] = x1;        poly->yv[0] = y1;
3506 	poly->xv[1] = x2;        poly->yv[1] = y2;
3507 	poly->count = 2;
3508 	poly->style = OPENED;
3509 	poly->desc = desc;
3510 	us_showpoly(poly, el_curwindowpart);
3511 }
3512 
compen_drawcircle(INTBIG centerx,INTBIG centery,INTBIG x,INTBIG y,GRAPHICS * desc)3513 void compen_drawcircle(INTBIG centerx, INTBIG centery, INTBIG x, INTBIG y, GRAPHICS *desc)
3514 {
3515 	static POLYGON *poly = NOPOLYGON;
3516 
3517 	/* get polygon */
3518 	(void)needstaticpolygon(&poly, 2, us_tool->cluster);
3519 
3520 	poly->xv[0] = centerx;   poly->yv[0] = centery;
3521 	poly->xv[1] = x;         poly->yv[1] = y;
3522 	poly->count = 2;
3523 	poly->style = CIRCLE;
3524 	poly->desc = desc;
3525 	us_showpoly(poly, el_curwindowpart);
3526 }
3527 
compen_drawcirclearc(INTBIG centerx,INTBIG centery,INTBIG x1,INTBIG y1,INTBIG x2,INTBIG y2,GRAPHICS * desc)3528 void compen_drawcirclearc(INTBIG centerx, INTBIG centery, INTBIG x1, INTBIG y1, INTBIG x2, INTBIG y2, GRAPHICS *desc)
3529 {
3530 	static POLYGON *poly = NOPOLYGON;
3531 
3532 	/* get polygon */
3533 	(void)needstaticpolygon(&poly, 3, us_tool->cluster);
3534 
3535 	poly->xv[0] = centerx;   poly->yv[0] = centery;
3536 	poly->xv[1] = x1;        poly->yv[1] = y1;
3537 	poly->xv[2] = x2;        poly->yv[2] = y2;
3538 	poly->count = 3;
3539 	poly->style = CIRCLEARC;
3540 	poly->desc = desc;
3541 	us_showpoly(poly, el_curwindowpart);
3542 }
3543 
3544 /*
3545  * routine to determine the actual compensation amount given the percentage (and
3546  * making use of the two globals: metal thickness and LRS compensation adjustment).
3547  */
compen_truecompensation(float percentage,float metalthickness,float lrscompensation)3548 float compen_truecompensation(float percentage, float metalthickness, float lrscompensation)
3549 {
3550 	return(metalthickness * percentage / 200.0f - lrscompensation/2.0f);
3551 }
3552 
3553 /*
3554  * Routine to return zero if point (x,y) is on the arc segment "arcconel".
3555  * Returns the angulur distance off of the arc if not.
3556  */
compen_pointoffarc(CONTOURELEMENT * arcconel,INTBIG x,INTBIG y)3557 INTBIG compen_pointoffarc(CONTOURELEMENT *arcconel, INTBIG x, INTBIG y)
3558 {
3559 	double as, ae, a;
3560 
3561 	as = compen_figureangle(arcconel->cx, arcconel->cy, arcconel->sx, arcconel->sy) * 1800.0 / EPI;
3562 	ae = compen_figureangle(arcconel->cx, arcconel->cy, arcconel->ex, arcconel->ey) * 1800.0 / EPI;
3563 	a = compen_figureangle(arcconel->cx, arcconel->cy, x, y) * 1800.0 / EPI;
3564 	if (arcconel->elementtype == ARCSEGMENTTYPE)
3565 	{
3566 		if (ae > as)
3567 		{
3568 			if (a >= as && a <= ae) return(0);
3569 			return(mini(compen_angoffset(a,as), compen_angoffset(a,ae)));
3570 		}
3571 		if (a >= as || a <= ae) return(0);
3572 		return(mini(compen_angoffset(a,as), compen_angoffset(a,ae)));
3573 	}
3574 
3575 	if (as > ae)
3576 	{
3577 		if (a >= ae && a <= as) return(0);
3578 		return(mini(compen_angoffset(a,as), compen_angoffset(a,ae)));
3579 	}
3580 	if (a >= ae || a <= as) return(0);
3581 	return(mini(compen_angoffset(a,as), compen_angoffset(a,ae)));
3582 }
3583 
compen_angoffset(double a1,double a2)3584 INTBIG compen_angoffset(double a1, double a2)
3585 {
3586 	REGISTER double dist;
3587 
3588 	dist = fabs(a1 - a2);
3589 	if (dist > 1800.0) dist -= 3600.0;
3590 	return(rounddouble(fabs(dist)));
3591 }
3592 
compen_figureangle(INTBIG fx,INTBIG fy,INTBIG tx,INTBIG ty)3593 double compen_figureangle(INTBIG fx, INTBIG fy, INTBIG tx, INTBIG ty)
3594 {
3595 	double ang;
3596 
3597 	if (ty == fy && tx == fx)
3598 	{
3599 		ttyputerr(M_("Domain error computing angle"));
3600 		return(0.0);
3601 	}
3602 	ang = atan2((double)ty-fy, (double)tx-fx);
3603 	if (ang < 0.0) ang += EPI*2.0;
3604 	return(ang);
3605 }
3606 
compen_intersect(INTBIG x1,INTBIG y1,double fang1,INTBIG x2,INTBIG y2,double fang2,INTBIG * x,INTBIG * y)3607 BOOLEAN compen_intersect(INTBIG x1, INTBIG y1, double fang1, INTBIG x2, INTBIG y2, double fang2, INTBIG *x, INTBIG *y)
3608 {
3609 	double fa1, fb1, fc1, fa2, fb2, fc2, fswap, fy;
3610 
3611 	/* cannot handle lines if they are at the same angle */
3612 	if (fang1 == fang2) return(TRUE);
3613 
3614 	/* also at the same angle if off by 180 degrees */
3615 	if (fang1 < fang2)
3616 	{
3617 		if (fang1 + EPI == fang2) return(TRUE);
3618 	} else
3619 	{
3620 		if (fang1 - EPI == fang2) return(TRUE);
3621 	}
3622 
3623 	fa1 = sin(fang1);   fb1 = -cos(fang1);
3624 	fc1 = -fa1 * ((double)x1) - fb1 * ((double)y1);
3625 	fa2 = sin(fang2);   fb2 = -cos(fang2);
3626 	fc2 = -fa2 * ((double)x2) - fb2 * ((double)y2);
3627 	if (fabs(fa1) < fabs(fa2))
3628 	{
3629 		fswap = fa1;   fa1 = fa2;   fa2 = fswap;
3630 		fswap = fb1;   fb1 = fb2;   fb2 = fswap;
3631 		fswap = fc1;   fc1 = fc2;   fc2 = fswap;
3632 	}
3633 	fy = (fa2 * fc1 / fa1 - fc2) / (fb2 - fa2*fb1/fa1);
3634 	*y = rounddouble(fy);
3635 	*x = rounddouble((-fb1 * fy - fc1) / fa1);
3636 	return(FALSE);
3637 }
3638 
3639 /*
3640  * Routine to queue all contours on cell "np" for deletion.
3641  */
compen_removecellcontours(NODEPROTO * np)3642 void compen_removecellcontours(NODEPROTO *np)
3643 {
3644 	REGISTER CELLCONTOURS *fc;
3645 
3646 	for(fc = compen_firstcellcontours; fc != NOCELLCONTOURS; fc = fc->next)
3647 		if (fc->cell == np && !fc->deleted) break;
3648 	if (fc == NOCELLCONTOURS) return;
3649 
3650 	/* mark this as deleted and queue cleanup */
3651 	fc->deleted = TRUE;
3652 	compen_deletedcellcontours = 1;
3653 }
3654 
3655 /*
3656  * routine to get the list of contours currently stored on "np".  If there
3657  * is nothing stored, compute it and store it.
3658  * Returns NOCONTOUR if there is no list and no contours.
3659  */
compen_getcontourlist(NODEPROTO * np)3660 CONTOUR *compen_getcontourlist(NODEPROTO *np)
3661 {
3662 	REGISTER USERDATA *ud;
3663 	REGISTER CONTOUR *con, *contourlist;
3664 	REGISTER CONTOURELEMENT *conel;
3665 	REGISTER INTBIG total, bestthresh, worstthresh;
3666 	REGISTER CELLCONTOURS *fc;
3667 	REGISTER VARIABLE *var;
3668 
3669 	/* see if the data is already there */
3670 	for(fc = compen_firstcellcontours; fc != NOCELLCONTOURS; fc = fc->next)
3671 		if (fc->cell == np && !fc->deleted) return(fc->contour);
3672 
3673 	/* gather the contours in this cell */
3674 	ttyputmsg(M_("Gathering contours..."));
3675 	bestthresh = scalefromdispunit((float)BESTTHRESH, DISPUNITMM);
3676 	worstthresh = scalefromdispunit((float)WORSTTHRESH, DISPUNITMM);
3677 	contourlist = gathercontours(np, 0, bestthresh, worstthresh);
3678 
3679 	/* count 'em */
3680 	total = 0;
3681 	for(con = contourlist; con != NOCONTOUR; con = con->nextcontour)
3682 		if (con->valid != 0) total++;
3683 	ttyputmsg(M_("...Done gathering, found %ld contours"), total);
3684 
3685 	/* add user data to each contour element */
3686 	for(con = contourlist; con != NOCONTOUR; con = con->nextcontour)
3687 	{
3688 		con->userdata = 0;
3689 		for(conel = con->firstcontourelement; conel != NOCONTOURELEMENT; conel = conel->nextcontourelement)
3690 		{
3691 			ud = (USERDATA *)emalloc(sizeof (USERDATA), compen_tool->cluster);
3692 			if (ud == 0) return(0);
3693 			conel->userdata = (INTBIG)ud;
3694 			ud->DXFlayer = 0;
3695 			if (conel->ni != NONODEINST)
3696 			{
3697 				var = getvalkey((INTBIG)conel->ni, VNODEINST, VSTRING, compen_dxf_layerkey);
3698 				if (var != NOVARIABLE)
3699 				{
3700 					(void)allocstring(&ud->DXFlayer, (CHAR *)var->addr, compen_tool->cluster);
3701 				}
3702 			}
3703 		}
3704 	}
3705 
3706 	/* store the list on the cell */
3707 	if (contourlist != NOCONTOUR)
3708 	{
3709 		fc = (CELLCONTOURS *)emalloc(sizeof (CELLCONTOURS), compen_tool->cluster);
3710 		if (fc == 0) return(NOCONTOUR);
3711 		fc->cell = np;
3712 		fc->contour = contourlist;
3713 		fc->deleted = FALSE;
3714 		fc->next = compen_firstcellcontours;
3715 		compen_firstcellcontours = fc;
3716 
3717 		/* turn on the tool so that it can track changes to the cell and force contour upgrades */
3718 		toolturnon(compen_tool);
3719 	}
3720 
3721 	/* return the list */
3722 	return(contourlist);
3723 }
3724 
compen_debugdump(CHAR * msg,...)3725 void compen_debugdump(CHAR *msg, ...)
3726 {
3727 #ifdef DEBDUMP
3728 	va_list ap;
3729 	CHAR line[256];
3730 
3731 	var_start(ap, msg);
3732 	evsnprintf(line, 256, msg, ap);
3733 	va_end(ap);
3734 	xprintf(compen_io, x_("%s\n"), line);
3735 #endif
3736 }
3737 
3738 #endif  /* COMPENTOOL - at top */
3739