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