Default STEP export to board only
[geda-pcb/pcjc2/v2.git] / src / thermal.c
blob0e241034bb23c4ac6984cc0dbd708e2344f15cd1
1 /*!
2 * \file src/thermal.c
4 * \brief Negative thermal finger polygons.
6 * Thermals are normal lines on the layout
8 * The only thing unique about them is that they have the USETHERMALFLAG
9 * set so that they can be identified as thermals.
11 * It is handy for pcb to automatically make adjustments to the thermals
12 * when the user performs certain operations, and the functions in
13 * thermal.h help implement that.
15 * \author This file, thermal.c was written by and is
16 * (C) Copyright 2006, harry eaton
18 * <hr>
20 * <h1><b>Copyright.</b></h1>\n
22 * PCB, interactive printed circuit board design
24 * Copyright (C) 1994,1995,1996,2004,2006 Thomas Nau
26 * This program is free software; you can redistribute it and/or modify
27 * it under the terms of the GNU General Public License as published by
28 * the Free Software Foundation; either version 2 of the License, or
29 * (at your option) any later version.
31 * This program is distributed in the hope that it will be useful,
32 * but WITHOUT ANY WARRANTY; without even the implied warranty of
33 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
34 * GNU General Public License for more details.
36 * You should have received a copy of the GNU General Public License
37 * along with this program; if not, write to the Free Software
38 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
40 * Contact addresses for paper mail and Email:
42 * Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
44 * Thomas.Nau@rz.uni-ulm.de
47 #ifdef HAVE_CONFIG_H
48 #include "config.h"
49 #endif
51 #include <stdlib.h>
52 #include <stdarg.h>
53 #ifdef HAVE_STRING_H
54 #include <string.h>
55 #endif
56 #include <assert.h>
57 #include <memory.h>
58 #include <ctype.h>
59 #include <signal.h>
60 #include <sys/param.h>
61 #include <sys/stat.h>
62 #include <sys/types.h>
63 #include <math.h>
64 #ifdef HAVE_UNISTD_H
65 #include <unistd.h>
66 #endif
67 #ifdef HAVE_PWD_H
68 #include <pwd.h>
69 #endif
71 #include "global.h"
73 #include "create.h"
74 #include "data.h"
75 #include "draw.h"
76 #include "error.h"
77 #include "misc.h"
78 #include "move.h"
79 #include "polygon.h"
80 #include "rtree.h"
81 #include "thermal.h"
82 #include "undo.h"
84 #ifdef HAVE_LIBDMALLOC
85 #include <dmalloc.h>
86 #endif
88 static PCBType *pcb;
90 struct cent
92 Coord x, y;
93 Coord s, c;
94 char style;
95 POLYAREA *p;
98 static POLYAREA *
99 diag_line (Coord X, Coord Y, Coord l, Coord w, bool rt)
101 PLINE *c;
102 Vector v;
103 Coord x1, x2, y1, y2;
105 if (rt)
107 x1 = (l - w) * M_SQRT1_2;
108 x2 = (l + w) * M_SQRT1_2;
109 y1 = x1;
110 y2 = x2;
112 else
114 x2 = -(l - w) * M_SQRT1_2;
115 x1 = -(l + w) * M_SQRT1_2;
116 y1 = -x1;
117 y2 = -x2;
120 v[0] = X + x1;
121 v[1] = Y + y2;
122 if ((c = poly_NewContour (poly_CreateNode (v))) == NULL)
123 return NULL;
124 v[0] = X - x2;
125 v[1] = Y - y1;
126 poly_InclVertex (c->head.prev, poly_CreateNode (v));
127 v[0] = X - x1;
128 v[1] = Y - y2;
129 poly_InclVertex (c->head.prev, poly_CreateNode (v));
130 v[0] = X + x2;
131 v[1] = Y + y1;
132 poly_InclVertex (c->head.prev, poly_CreateNode (v));
133 return ContourToPoly (c);
136 static POLYAREA *
137 M_Poly_prepend (POLYAREA *a, POLYAREA *b)
139 if (a == NULL)
140 return b;
142 if (b == NULL)
143 return a;
145 a->f->b = b->b;
146 b->b->f = a->f;
147 a->f = b;
148 b->b = a;
150 return a;
153 static POLYAREA *
154 square_therm (PinType *pin, Cardinal style)
156 POLYAREA *p;
157 PLINE *c;
158 Vector v;
159 Coord d, in, out;
161 switch (style)
163 case 1:
164 d = pcb->ThermScale * pin->Clearance * M_SQRT1_2;
165 out = (pin->Thickness + pin->Clearance) / 2;
166 in = pin->Thickness / 2;
167 /* top (actually bottom since +y is down) */
168 v[0] = pin->X - in + d;
169 v[1] = pin->Y + in;
170 if ((c = poly_NewContour (poly_CreateNode (v))) == NULL)
171 return NULL;
172 v[0] = pin->X + in - d;
173 poly_InclVertex (c->head.prev, poly_CreateNode (v));
174 v[0] = pin->X + out - d;
175 v[1] = pin->Y + out;
176 poly_InclVertex (c->head.prev, poly_CreateNode (v));
177 v[0] = pin->X - out + d;
178 poly_InclVertex (c->head.prev, poly_CreateNode (v));
179 p = ContourToPoly (c);
180 /* right */
181 v[0] = pin->X + in;
182 v[1] = pin->Y + in - d;
183 if ((c = poly_NewContour (poly_CreateNode (v))) == NULL)
184 return NULL;
185 v[1] = pin->Y - in + d;
186 poly_InclVertex (c->head.prev, poly_CreateNode (v));
187 v[0] = pin->X + out;
188 v[1] = pin->Y - out + d;
189 poly_InclVertex (c->head.prev, poly_CreateNode (v));
190 v[1] = pin->Y + out - d;
191 poly_InclVertex (c->head.prev, poly_CreateNode (v));
192 p = M_Poly_prepend (p, ContourToPoly (c));
193 /* left */
194 v[0] = pin->X - in;
195 v[1] = pin->Y - in + d;
196 if ((c = poly_NewContour (poly_CreateNode (v))) == NULL)
197 return NULL;
198 v[1] = pin->Y + in - d;
199 poly_InclVertex (c->head.prev, poly_CreateNode (v));
200 v[0] = pin->X - out;
201 v[1] = pin->Y + out - d;
202 poly_InclVertex (c->head.prev, poly_CreateNode (v));
203 v[1] = pin->Y - out + d;
204 poly_InclVertex (c->head.prev, poly_CreateNode (v));
205 p = M_Poly_prepend (p, ContourToPoly (c));
206 /* bottom (actually top since +y is down) */
207 v[0] = pin->X + in - d;
208 v[1] = pin->Y - in;
209 if ((c = poly_NewContour (poly_CreateNode (v))) == NULL)
210 return NULL;
211 v[0] = pin->X - in + d;
212 poly_InclVertex (c->head.prev, poly_CreateNode (v));
213 v[0] = pin->X - out + d;
214 v[1] = pin->Y - out;
215 poly_InclVertex (c->head.prev, poly_CreateNode (v));
216 v[0] = pin->X + out - d;
217 poly_InclVertex (c->head.prev, poly_CreateNode (v));
218 p = M_Poly_prepend (p, ContourToPoly (c));
219 return p;
220 case 4:
222 LineType l;
223 l.Flags = NoFlags ();
224 d = pin->Thickness / 2 - pcb->ThermScale * pin->Clearance;
225 out = pin->Thickness / 2 + pin->Clearance / 4;
226 in = pin->Clearance / 2;
227 /* top */
228 l.Point1.X = pin->X - d;
229 l.Point2.Y = l.Point1.Y = pin->Y + out;
230 l.Point2.X = pin->X + d;
231 p = LinePoly (&l, in, NULL);
232 /* right */
233 l.Point1.X = l.Point2.X = pin->X + out;
234 l.Point1.Y = pin->Y - d;
235 l.Point2.Y = pin->Y + d;
236 p = M_Poly_prepend (p, LinePoly (&l, in, NULL));
237 /* bottom */
238 l.Point1.X = pin->X - d;
239 l.Point2.Y = l.Point1.Y = pin->Y - out;
240 l.Point2.X = pin->X + d;
241 p = M_Poly_prepend (p, LinePoly (&l, in, NULL));
242 /* left */
243 l.Point1.X = l.Point2.X = pin->X - out;
244 l.Point1.Y = pin->Y - d;
245 l.Point2.Y = pin->Y + d;
246 p = M_Poly_prepend (p, LinePoly (&l, in, NULL));
247 return p;
249 default: /* style 2 and 5 */
250 d = 0.5 * pcb->ThermScale * pin->Clearance;
251 if (style == 5)
252 d += d;
253 out = (pin->Thickness + pin->Clearance) / 2;
254 in = pin->Thickness / 2;
255 /* topright */
256 v[0] = pin->X + in;
257 v[1] = pin->Y + in;
258 if ((c = poly_NewContour (poly_CreateNode (v))) == NULL)
259 return NULL;
260 v[1] = pin->Y + d;
261 poly_InclVertex (c->head.prev, poly_CreateNode (v));
262 if (style == 2)
264 v[0] = pin->X + out;
265 poly_InclVertex (c->head.prev, poly_CreateNode (v));
267 else
269 frac_circle2 (c, v[0] + pin->Clearance / 4, v[1], v, 2);
270 v[0] = pin->X + out;
271 v[1] = pin->Y + d;
272 poly_InclVertex (c->head.prev, poly_CreateNode (v));
274 v[1] = pin->Y + in;
275 poly_InclVertex (c->head.prev, poly_CreateNode (v));
276 /* pivot 1/4 circle to next point */
277 frac_circle2 (c, pin->X + in, pin->Y + in, v, 4);
278 v[0] = pin->X + in;
279 v[1] = pin->Y + out;
280 poly_InclVertex (c->head.prev, poly_CreateNode (v));
282 v[0] = pin->X + d;
283 poly_InclVertex (c->head.prev, poly_CreateNode (v));
284 if (style == 2)
286 poly_InclVertex (c->head.prev, poly_CreateNode (v));
287 v[1] = pin->Y + in;
288 poly_InclVertex (c->head.prev, poly_CreateNode (v));
290 else
292 frac_circle2 (c, v[0], v[1] - pin->Clearance / 4, v, 2);
293 v[0] = pin->X + d;
294 v[1] = pin->Y + in;
295 poly_InclVertex (c->head.prev, poly_CreateNode (v));
297 p = ContourToPoly (c);
298 /* bottom right */
299 v[0] = pin->X + in;
300 v[1] = pin->Y - d;
301 if ((c = poly_NewContour (poly_CreateNode (v))) == NULL)
302 return NULL;
303 v[1] = pin->Y - in;
304 poly_InclVertex (c->head.prev, poly_CreateNode (v));
305 v[0] = pin->X + d;
306 poly_InclVertex (c->head.prev, poly_CreateNode (v));
307 if (style == 2)
309 v[1] = pin->Y - out;
310 poly_InclVertex (c->head.prev, poly_CreateNode (v));
312 else
314 frac_circle2 (c, v[0], v[1] - pin->Clearance / 4, v, 2);
315 v[0] = pin->X + d;
316 v[1] = pin->Y - out;
317 poly_InclVertex (c->head.prev, poly_CreateNode (v));
319 v[0] = pin->X + in;
320 poly_InclVertex (c->head.prev, poly_CreateNode (v));
321 /* pivot 1/4 circle to next point */
322 frac_circle2 (c, pin->X + in, pin->Y - in, v, 4);
323 v[0] = pin->X + out;
324 v[1] = pin->Y - in;
325 poly_InclVertex (c->head.prev, poly_CreateNode (v));
326 v[1] = pin->Y - d;
327 poly_InclVertex (c->head.prev, poly_CreateNode (v));
328 if (style == 5)
330 frac_circle2 (c, v[0] - pin->Clearance / 4, v[1], v, 2);
331 v[0] = pin->X + in;
332 v[1] = pin->Y - d;
333 poly_InclVertex (c->head.prev, poly_CreateNode (v));
335 p = M_Poly_prepend (p, ContourToPoly (c));
336 /* bottom left */
337 v[0] = pin->X - d;
338 v[1] = pin->Y - in;
339 if ((c = poly_NewContour (poly_CreateNode (v))) == NULL)
340 return NULL;
341 v[0] = pin->X - in;
342 poly_InclVertex (c->head.prev, poly_CreateNode (v));
343 v[1] = pin->Y - d;
344 poly_InclVertex (c->head.prev, poly_CreateNode (v));
345 if (style == 2)
347 v[0] = pin->X - out;
348 poly_InclVertex (c->head.prev, poly_CreateNode (v));
350 else
352 frac_circle2 (c, v[0] - pin->Clearance / 4, v[1], v, 2);
353 v[0] = pin->X - out;
354 v[1] = pin->Y - d;
355 poly_InclVertex (c->head.prev, poly_CreateNode (v));
357 v[1] = pin->Y - in;
358 poly_InclVertex (c->head.prev, poly_CreateNode (v));
359 /* pivot 1/4 circle to next point */
360 frac_circle2 (c, pin->X - in, pin->Y - in, v, 4);
361 v[0] = pin->X - in;
362 v[1] = pin->Y - out;
363 poly_InclVertex (c->head.prev, poly_CreateNode (v));
364 v[0] = pin->X - d;
365 poly_InclVertex (c->head.prev, poly_CreateNode (v));
366 if (style == 5)
368 frac_circle2 (c, v[0], v[1] + pin->Clearance / 4, v, 2);
369 v[0] = pin->X - d;
370 v[1] = pin->Y - in;
371 poly_InclVertex (c->head.prev, poly_CreateNode (v));
373 p = M_Poly_prepend (p, ContourToPoly (c));
374 /* top left */
375 v[0] = pin->X - d;
376 v[1] = pin->Y + out;
377 if ((c = poly_NewContour (poly_CreateNode (v))) == NULL)
378 return NULL;
379 v[0] = pin->X - in;
380 poly_InclVertex (c->head.prev, poly_CreateNode (v));
381 /* pivot 1/4 circle to next point (x-out, y+in) */
382 frac_circle2 (c, pin->X - in, pin->Y + in, v, 4);
383 v[0] = pin->X - out;
384 v[1] = pin->Y + in;
385 poly_InclVertex (c->head.prev, poly_CreateNode (v));
386 v[1] = pin->Y + d;
387 poly_InclVertex (c->head.prev, poly_CreateNode (v));
388 if (style == 2)
390 v[0] = pin->X - in;
391 poly_InclVertex (c->head.prev, poly_CreateNode (v));
393 else
395 frac_circle2 (c, v[0] + pin->Clearance / 4, v[1], v, 2);
396 v[0] = pin->X - in;
397 v[1] = pin->Y + d;
398 poly_InclVertex (c->head.prev, poly_CreateNode (v));
400 v[1] = pin->Y + in;
401 poly_InclVertex (c->head.prev, poly_CreateNode (v));
402 v[0] = pin->X - d;
403 poly_InclVertex (c->head.prev, poly_CreateNode (v));
404 if (style == 5)
405 frac_circle2 (c, v[0], v[1] + pin->Clearance / 4, v, 2);
406 p = M_Poly_prepend (p, ContourToPoly (c));
407 return p;
411 static POLYAREA *
412 oct_therm (PinType *pin, Cardinal style)
414 POLYAREA *p, *p2, *m;
415 Coord t = 0.5 * pcb->ThermScale * pin->Clearance;
416 Coord w = pin->Thickness + pin->Clearance;
418 p = OctagonPoly (pin->X, pin->Y, w);
419 p2 = OctagonPoly (pin->X, pin->Y, pin->Thickness);
420 /* make full clearance ring */
421 poly_Boolean_free (p, p2, &m, PBO_SUB);
422 switch (style)
424 default:
425 case 1:
426 p = diag_line (pin->X, pin->Y, w, t, true);
427 poly_Boolean_free (m, p, &p2, PBO_SUB);
428 p = diag_line (pin->X, pin->Y, w, t, false);
429 poly_Boolean_free (p2, p, &m, PBO_SUB);
430 return m;
431 case 2:
432 p = RectPoly (pin->X - t, pin->X + t, pin->Y - w, pin->Y + w);
433 poly_Boolean_free (m, p, &p2, PBO_SUB);
434 p = RectPoly (pin->X - w, pin->X + w, pin->Y - t, pin->Y + t);
435 poly_Boolean_free (p2, p, &m, PBO_SUB);
436 return m;
437 /* fix me add thermal style 4 */
438 case 5:
440 Coord t = pin->Thickness / 2;
441 POLYAREA *q;
442 /* cheat by using the square therm's rounded parts */
443 p = square_therm (pin, style);
444 q = RectPoly (pin->X - t, pin->X + t, pin->Y - t, pin->Y + t);
445 poly_Boolean_free (p, q, &p2, PBO_UNITE);
446 poly_Boolean_free (m, p2, &p, PBO_ISECT);
447 return p;
453 * \brief .
455 * \return ThermPoly returns a POLYAREA having all of the clearance that
456 * when subtracted from the plane create the desired thermal fingers.
457 * Usually this is 4 disjoint regions.
459 /* XXX: Could check for polygon with hole as output... indicating the geometry isolated the pin? */
460 POLYAREA *
461 ThermPoly (PCBType *p, PinType *pin, Cardinal laynum)
463 ArcType a;
464 POLYAREA *pa, *arc;
465 Cardinal style = GET_THERM (laynum, pin);
467 if (style == 3)
468 return NULL; /* solid connection no clearance */
469 pcb = p;
470 if (TEST_FLAG (SQUAREFLAG, pin))
471 return square_therm (pin, style);
472 if (TEST_FLAG (OCTAGONFLAG, pin))
473 return oct_therm (pin, style);
474 /* must be circular */
475 switch (style)
477 case 1:
478 case 2:
480 POLYAREA *m;
481 Coord t = (pin->Thickness + pin->Clearance) / 2;
482 Coord w = 0.5 * pcb->ThermScale * pin->Clearance;
483 pa = CirclePoly (pin->X, pin->Y, t, NULL);
484 arc = CirclePoly (pin->X, pin->Y, pin->Thickness / 2, NULL);
485 /* create a thin ring */
486 poly_Boolean_free (pa, arc, &m, PBO_SUB);
487 /* fix me needs error checking */
488 if (style == 2)
490 /* t is the theoretically required length, but we use twice that
491 * to avoid descritisation errors in our circle approximation.
493 pa = RectPoly (pin->X - t * 2, pin->X + t * 2, pin->Y - w, pin->Y + w);
494 poly_Boolean_free (m, pa, &arc, PBO_SUB);
495 pa = RectPoly (pin->X - w, pin->X + w, pin->Y - t * 2, pin->Y + t * 2);
497 else
499 /* t is the theoretically required length, but we use twice that
500 * to avoid descritisation errors in our circle approximation.
502 pa = diag_line (pin->X, pin->Y, t * 2, w, true);
503 poly_Boolean_free (m, pa, &arc, PBO_SUB);
504 pa = diag_line (pin->X, pin->Y, t * 2, w, false);
506 poly_Boolean_free (arc, pa, &m, PBO_SUB);
507 return m;
511 default:
512 a.X = pin->X;
513 a.Y = pin->Y;
514 a.Height = a.Width = pin->Thickness / 2 + pin->Clearance / 4;
515 a.Thickness = 1;
516 a.Clearance = pin->Clearance / 2;
517 a.Flags = NoFlags ();
518 a.Delta =
519 90 -
520 (a.Clearance * (1. + 2. * pcb->ThermScale) * 180) / (M_PI * a.Width);
521 a.StartAngle = 90 - a.Delta / 2 + (style == 4 ? 0 : 45);
522 pa = ArcPoly (&a, a.Clearance, NULL);
523 if (!pa)
524 return NULL;
525 a.StartAngle += 90;
526 arc = ArcPoly (&a, a.Clearance, NULL);
527 if (!arc)
528 return NULL;
529 pa->f = arc;
530 arc->b = pa;
531 a.StartAngle += 90;
532 arc = ArcPoly (&a, a.Clearance, NULL);
533 if (!arc)
534 return NULL;
535 pa->f->f = arc;
536 arc->b = pa->f;
537 a.StartAngle += 90;
538 arc = ArcPoly (&a, a.Clearance, NULL);
539 if (!arc)
540 return NULL;
541 pa->b = arc;
542 pa->f->f->f = arc;
543 arc->b = pa->f->f;
544 arc->f = pa;
545 pa->b = arc;
546 return pa;