2 * Copyright © 2012 Keith Packard <keithp@keithp.com>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 2 of the License.
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
22 public int mm2mils100(real mm) = floor (mm / 25.4 * 1000 * 100 + 0.5);
24 public real mils1002mm(real mils100) = mils100 * 25.4 / 100 / 1000;
26 /* OSH park adprocess requirement */
27 public real process_soldermask = mils1002mm(300);
28 public real process_trace = mils1002mm(600);
29 public real process_space = mils1002mm(600);
30 public real process_drill = mils1002mm(1300);
31 public real process_ring = mils1002mm(700);
33 /* default pad options */
34 public string pad_options = "square";
36 /* default pin options */
37 public string pin_options = "pin";
39 /* default ink thickness */
40 public real line_thickness = mils1002mm(1000);
49 bool[string] override_rules = {};
51 public void override(string rule) {
52 override_rules[rule] = true;
55 bool overridden(string rule) {
56 return hash_test(override_rules, rule) && override_rules[rule];
59 public void element_start(string name) {
61 if (!is_uninit(&out_name)) {
62 tmp_name = sprintf("_tmp-%d.fp", PID::getpid());
63 out = open(tmp_name, "w");
64 reread = open(tmp_name, "r");
69 fprintf(out, "# author: Keith Packard\n");
70 fprintf(out, "# email: keithp@keithp.com\n");
71 fprintf(out, "# dist-license: GPL 2\n");
72 fprintf(out, "# use-license: unlimited\n");
73 fprintf(out, "Element [\"\" \"%s\" \"\" \"\" 0 0 0 0 0 100 \"\"]\n",
78 public void element_args() {
82 ParseArgs::argdesc argd = {
85 .var = (ParseArgs::arg_var.arg_string) &out_name,
88 .expr_name = "filename",
89 .desc = "Output file name"
92 .unknown = &(int other_args)
95 ParseArgs::parseargs(&argd, &argv);
97 if (!is_uninit(&other_args))
98 argv = (string[dim(argv)-other_args+1]) {
99 [i] = (i == 0) ? argv[0] : argv[i + other_args-1]
105 public void element_end() {
108 if (!is_uninit(&out_name)) {
111 twixt(file result = open(out_name, "w"); close(result)) {
112 while (!end(reread)) {
113 putb(getb(reread), result);
116 } catch open_error (string message, error_type error, string name) {
117 fprintf(stderr, "%s: %s\n", name, message);
123 public typedef struct {
124 real center_x; /* center of pad */
127 real width; /* size of pad */
130 real spacing; /* space between pad and other traces */
131 real soldermask; /* space between pad and solder mask */
132 string name; /* pad name */
133 string number; /* pad number */
134 string options; /* pad options */
136 /* normally computed, but can be provided */
137 real x1; /* start of pad "line" */
139 real x2; /* end of pad "line" */
141 real thickness; /* thickness of pad "line" */
142 real clearance; /* twice the spacing between pad and other traces */
143 real mask; /* thickness of pad and solder mask */
146 pad_t fill_pad(pad_t pad) {
147 if (is_uninit(&pad.x1))
148 pad.x1 = pad.center_x - max(0, (pad.width - pad.height) / 2);
149 if (is_uninit(&pad.x2))
150 pad.x2 = pad.center_x + max(0, (pad.width - pad.height) / 2);
152 if (is_uninit(&pad.y1))
153 pad.y1 = pad.center_y - max(0, (pad.height - pad.width) / 2);
154 if (is_uninit(&pad.y2))
155 pad.y2 = pad.center_y + max(0, (pad.height - pad.width) / 2);
157 if (is_uninit(&pad.spacing))
158 pad.spacing = process_space;
159 if (is_uninit(&pad.soldermask))
160 pad.soldermask = process_soldermask;
161 if (is_uninit(&pad.number))
162 pad.number = pad.name;
163 if (is_uninit(&pad.options))
164 pad.options = pad_options;
166 if (is_uninit(&pad.thickness))
167 pad.thickness = min(pad.width, pad.height);
169 if (is_uninit(&pad.clearance))
170 pad.clearance = pad.spacing * 2;
172 if (is_uninit(&pad.mask))
173 pad.mask = pad.thickness + pad.soldermask * 2;
175 # fprintf(out, "pad %v\n", pad);
180 public exception violation(string rule, real min, real val);
182 public check(string rule, real min, real val, bool zero_ok) {
184 if (overridden(rule))
186 if (zero_ok && val <= 0)
189 Debug::trace(Thread::current());
190 File::fprintf(stderr, "rule %s violated (%fmm %fmils < %fmm %fmils)\n",
192 val, mm2mils100(val)/100.0,
193 min, mm2mils100(min)/100.0);
198 public void pad(pad_t pad)
202 check("pad trace", process_trace, pad.thickness, false);
203 check("pad space", process_space, pad.clearance / 2, true);
204 check("pad mask", process_soldermask, (pad.mask - pad.thickness) / 2, true);
206 fprintf(out, " Pad[");
207 fprintf(out, " %6d %6d %6d %6d",
212 fprintf(out, " %6d %6d %6d",
213 mm2mils100(pad.thickness),
214 mm2mils100(pad.clearance),
215 mm2mils100(pad.mask));
216 fprintf(out, " \"%s\" \"%s\" \"%s\"]\n",
217 pad.name, pad.number, pad.options);
220 public void pad_mm_space_mask_options(real center_x,
231 .center_x = center_x,
232 .center_y = center_y,
236 .soldermask = soldermask,
243 public void pad_mm_space_options(real center_x,
253 .center_x = center_x,
254 .center_y = center_y,
264 public void pad_mm_mask_options(real center_x,
274 .center_x = center_x,
275 .center_y = center_y,
278 .soldermask = soldermask,
285 public void pad_mm_space(real center_x,
294 .center_x = center_x,
295 .center_y = center_y,
304 public void pad_mm(real center_x,
312 .center_x = center_x,
313 .center_y = center_y,
321 public void pad_mm_options(real center_x,
330 .center_x = center_x,
331 .center_y = center_y,
340 /* Pad with partial solder coverage.
341 * Useful for pads under parts which aren't
342 * big enough to use the center function below
344 public void pad_mm_partial(real center_x,
352 .center_x = center_x,
353 .center_y = center_y,
357 .options = pad_options + ",nopaste"
360 real ratio = floor(sqrt(partial) * 100.0 + 0.5) / 100;
363 .center_x = center_x,
364 .center_y = center_y,
365 .width = width * ratio,
366 .height = height * ratio,
371 public void pad_mm_arbitrary( real x1,
385 .thickness = thickness,
392 public typedef struct {
393 real x; /* center of pin */
395 real drill; /* diameter of drill hole */
397 real ring; /* width of annular ring around hole */
398 real spacing; /* space between pin and other traces */
399 real soldermask; /* space between pin and solder mask */
401 string name; /* pin name */
402 string number; /* pin number */
403 string options; /* pin options */
405 /* normally computed, but can be provided */
406 real thickness; /* thickness of pin "line" */
407 real clearance; /* twice the spacing between pin and other traces */
408 real mask; /* thickness of pin and solder mask */
411 pin_t fill_pin(pin_t pin) {
413 /* Fill in process rules if unset */
414 if (is_uninit(&pin.spacing))
415 pin.spacing = process_space;
416 if (is_uninit(&pin.ring))
417 pin.ring = process_ring;
418 if (is_uninit(&pin.soldermask))
419 pin.soldermask = process_soldermask;
421 if (is_uninit(&pin.number))
422 pin.number = pin.name;
423 if (is_uninit(&pin.options))
424 pin.options = pin_options;
426 if (is_uninit(&pin.thickness))
427 pin.thickness = pin.drill + pin.ring * 2;
429 if (is_uninit(&pin.mask))
430 pin.mask = pin.thickness + pin.soldermask * 2;
432 if (is_uninit(&pin.clearance))
433 pin.clearance = pin.spacing * 2;
438 public void pin(pin_t pin)
442 check("pin drill", process_drill, pin.drill, false);
443 check("pin ring", process_ring, (pin.thickness - pin.drill) / 2, true);
444 check("pin space", process_space, pin.clearance / 2, true);
445 if (String::index(pin.options, "via") < 0)
446 check("pin mask", process_soldermask, (pin.mask - pin.thickness) / 2, true);
448 fprintf(out, " Pin[");
449 fprintf(out, " %6d %6d",
452 fprintf(out, " %6d %6d %6d",
453 mm2mils100(pin.thickness),
454 mm2mils100(pin.clearance),
455 mm2mils100(pin.mask));
457 mm2mils100(pin.drill));
458 fprintf(out, " \"%s\" \"%s\" \"%s\"]\n",
459 pin.name, pin.number, pin.options);
462 public void pin_mm_space_mask_options(real x, real y,
463 real drill, real copper, real spacing, real soldermask,
464 string name, string number, string options)
472 .soldermask = soldermask,
475 .options = options });
478 public void pin_mm_space_options(real x, real y, real drill, real copper, real spacing,
491 .options = options });
494 public void pin_mm_space_mask(real x, real y,
495 real drill, real copper, real spacing, real soldermask,
496 string name, string number)
504 .soldermask = soldermask,
509 public void pin_mm_space(real x, real y, real drill, real copper, real spacing,
523 public void pin_mm(real x, real y, real drill, real copper,
536 public void pin_mm_options(real x, real y, real drill, real copper,
548 .options = options });
551 public void pin_mm_mask_options(real x, real y, real drill, real copper,
562 .soldermask = soldermask,
565 .options = options });
568 public void via_mm(real x, real y,
569 real drill, real copper,
583 public void line (real x1, real y1, real x2, real y2)
585 fprintf(out, " ElementLine[");
586 fprintf(out, " %6d %6d %6d %6d",
591 fprintf(out, " %d]\n", mm2mils100(line_thickness));
594 public void rect (real x, real y, real w, real h)
602 public void arc (real center_x, real center_y,
603 real radius_x, real radius_y,
604 real start_angle, real delta_angle)
606 fprintf(out, " ElementArc[ %6d %6d %6d %6d %3d %3d %d]\n",
607 mm2mils100(center_x), mm2mils100(center_y),
608 mm2mils100(radius_x), mm2mils100(radius_y),
609 start_angle, delta_angle, mm2mils100(line_thickness));
612 public typedef struct {
613 real x, y; /* center */
614 real width, height; /* size */
618 int via_cols, via_rows;
626 public void center_pad(center_t center) {
628 if (is_uninit(¢er.via_drill))
629 center.via_drill = process_drill;
631 if (is_uninit(¢er.via_copper))
632 center.via_copper = process_ring;
634 if (is_uninit(¢er.via_space_x)) {
635 real side_x = center.via_drill / 2 + center.via_copper;
636 real space_x = center.width - 2 * side_x;
638 center.via_space_x = space_x / (center.via_cols - 1);
641 if (is_uninit(¢er.via_space_y)) {
642 real side_y = center.via_drill / 2 + center.via_copper;
643 real space_y = center.width - 2 * side_y;
645 center.via_space_y = space_y / (center.via_rows - 1);
650 .center_x = center.x,
651 .center_y = center.y,
652 .width = center.width,
653 .height = center.height,
655 .options = "square,nopaste",
656 .spacing = process_space,
660 for (int r = 0; r < center.via_rows; r++)
661 for (int c = 0; c < center.via_cols; c++) {
662 real x = (c - (center.via_cols - 1) / 2) * center.via_space_x;
663 real y = (r - (center.via_rows - 1) / 2) * center.via_space_y;
665 via_mm(x, y, process_drill, process_ring, center.name);
668 .center_x = center.x + x,
669 .center_y = center.y + y,
670 .width = center.via_space_x / 2,
671 .height = center.via_space_y / 2,
673 .options = "square,nopaste",
679 for (real r = 0; r < center.via_rows - 0.5; r += 0.5) {
680 for (real c = 0; c < center.via_cols - 0.5; c += 0.5) {
682 if (is_int(r) && is_int(c))
685 real x = (c - (center.via_cols - 1) / 2) * center.via_space_x;
686 real y = (r - (center.via_rows - 1) / 2) * center.via_space_y;
690 .center_x = center.x + x,
691 .center_y = center.y + y,
692 .width = center.via_space_x / 2,
693 .height = center.via_space_x / 2,
696 .options = "square,nopaste",
701 .center_x = center.x + x,
702 .center_y = center.y + y,
703 .width = center.via_space_x / 3.5,
704 .height = center.via_space_y / 3.5,