Root/
Source at commit bc27b094af74a7c6f8b71ceaa63d1ba80d067cbb created 13 years 11 months ago. By werner, With a little help from m8cutils and abyss, we now have regression tests for the topological sort. "make test" or "make tests" invokes the regression tests, "make valgrind" runs them under valgrind's watchful eyes. | |
---|---|
1 | /* |
2 | * kicad.c - Dump objects in the KiCad board/module format |
3 | * |
4 | * Written 2009, 2010 by Werner Almesberger |
5 | * Copyright 2009, 2010 by Werner Almesberger |
6 | * |
7 | * This program is free software; you can redistribute it and/or modify |
8 | * it under the terms of the GNU General Public License as published by |
9 | * the Free Software Foundation; either version 2 of the License, or |
10 | * (at your option) any later version. |
11 | */ |
12 | |
13 | |
14 | #include <stdlib.h> |
15 | #include <stdio.h> |
16 | #include <time.h> |
17 | |
18 | #include "coord.h" |
19 | #include "inst.h" |
20 | #include "kicad.h" |
21 | |
22 | |
23 | static unit_type zeroize(unit_type n) |
24 | { |
25 | return n == -1 || n == 1 ? 0 : n; |
26 | } |
27 | |
28 | |
29 | static void kicad_centric(struct coord a, struct coord b, |
30 | struct coord *center, struct coord *size) |
31 | { |
32 | struct coord min, max; |
33 | unit_type tmp; |
34 | |
35 | min.x = units_to_kicad(a.x); |
36 | min.y = units_to_kicad(a.y); |
37 | max.x = units_to_kicad(b.x); |
38 | max.y = units_to_kicad(b.y); |
39 | |
40 | if (min.x > max.x) { |
41 | tmp = min.x; |
42 | min.x = max.x; |
43 | max.x = tmp; |
44 | } |
45 | if (min.y > max.y) { |
46 | tmp = min.y; |
47 | min.y = max.y; |
48 | max.y = tmp; |
49 | } |
50 | |
51 | size->x = max.x-min.x; |
52 | size->y = max.y-min.y; |
53 | center->x = (min.x+max.x)/2; |
54 | center->y = -(min.y+max.y)/2; |
55 | } |
56 | |
57 | |
58 | static void do_drill(FILE *file, const struct inst *pad, struct coord *ref) |
59 | { |
60 | const struct inst *hole = pad->u.pad.hole; |
61 | struct coord center, size; |
62 | |
63 | if (!hole) |
64 | return; |
65 | |
66 | kicad_centric(hole->base, hole->u.hole.other, ¢er, &size); |
67 | |
68 | /* Allow for rounding errors */ |
69 | |
70 | fprintf(file, "Dr %d %d %d", size.x, |
71 | -zeroize(center.x-ref->x), -zeroize(center.y-ref->y)); |
72 | if (size.x < size.y-1 || size.x > size.y+1) |
73 | fprintf(file, " O %d %d", size.x, size.y); |
74 | fprintf(file, "\n"); |
75 | *ref = center; |
76 | } |
77 | |
78 | |
79 | static void kicad_pad(FILE *file, const struct inst *inst) |
80 | { |
81 | struct coord center, size; |
82 | |
83 | kicad_centric(inst->base, inst->u.pad.other, ¢er, &size); |
84 | |
85 | fprintf(file, "$PAD\n"); |
86 | |
87 | /* |
88 | * name, shape (rectangle), Xsize, Ysize, Xdelta, Ydelta, Orientation |
89 | */ |
90 | fprintf(file, "Sh \"%s\" %c %d %d 0 0 0\n", |
91 | inst->u.pad.name, inst->obj->u.pad.rounded ? 'O' : 'R', |
92 | size.x, size.y); |
93 | |
94 | /* |
95 | * Drill hole |
96 | */ |
97 | do_drill(file, inst, ¢er); |
98 | |
99 | /* |
100 | * Attributes: pad type, N, layer mask |
101 | */ |
102 | fprintf(file, "At %s N %8.8X\n", |
103 | inst->u.pad.hole ? "STD" : "SMD", (unsigned) inst->u.pad.layers); |
104 | |
105 | /* |
106 | * Position: Xpos, Ypos |
107 | */ |
108 | fprintf(file, "Po %d %d\n", center.x, center.y); |
109 | |
110 | fprintf(file, "$EndPAD\n"); |
111 | } |
112 | |
113 | |
114 | static void kicad_hole(FILE *file, const struct inst *inst) |
115 | { |
116 | struct coord center, size; |
117 | |
118 | if (inst->u.hole.pad) |
119 | return; |
120 | kicad_centric(inst->base, inst->u.hole.other, ¢er, &size); |
121 | fprintf(file, "$PAD\n"); |
122 | if (size.x < size.y-1 || size.x > size.y+1) { |
123 | fprintf(file, "Sh \"HOLE\" O %d %d 0 0 0\n", size.x, size.y); |
124 | fprintf(file, "Dr %d 0 0 O %d %d\n", size.x, size.x, size.y); |
125 | } else { |
126 | fprintf(file, "Sh \"HOLE\" C %d %d 0 0 0\n", size.x, size.x); |
127 | fprintf(file, "Dr %d 0 0\n", size.x); |
128 | } |
129 | fprintf(file, "At HOLE N %8.8X\n", (unsigned) inst->u.hole.layers); |
130 | fprintf(file, "Po %d %d\n", center.x, center.y); |
131 | fprintf(file, "$EndPAD\n"); |
132 | } |
133 | |
134 | |
135 | static void kicad_line(FILE *file, const struct inst *inst) |
136 | { |
137 | /* |
138 | * Xstart, Ystart, Xend, Yend, Width, Layer |
139 | */ |
140 | fprintf(file, "DS %d %d %d %d %d %d\n", |
141 | units_to_kicad(inst->base.x), |
142 | -units_to_kicad(inst->base.y), |
143 | units_to_kicad(inst->u.rect.end.x), |
144 | -units_to_kicad(inst->u.rect.end.y), |
145 | units_to_kicad(inst->u.rect.width), |
146 | layer_silk_top); |
147 | } |
148 | |
149 | |
150 | static void kicad_rect(FILE *file, const struct inst *inst) |
151 | { |
152 | unit_type xa, ya, xb, yb; |
153 | unit_type width; |
154 | |
155 | xa = units_to_kicad(inst->base.x); |
156 | ya = units_to_kicad(inst->base.y); |
157 | xb = units_to_kicad(inst->u.rect.end.x); |
158 | yb = units_to_kicad(inst->u.rect.end.y); |
159 | width = units_to_kicad(inst->u.rect.width); |
160 | |
161 | fprintf(file, "DS %d %d %d %d %d %d\n", |
162 | xa, -ya, xa, -yb, width, layer_silk_top); |
163 | fprintf(file, "DS %d %d %d %d %d %d\n", |
164 | xa, -yb, xb, -yb, width, layer_silk_top); |
165 | fprintf(file, "DS %d %d %d %d %d %d\n", |
166 | xb, -yb, xb, -ya, width, layer_silk_top); |
167 | fprintf(file, "DS %d %d %d %d %d %d\n", |
168 | xb, -ya, xa, -ya, width, layer_silk_top); |
169 | } |
170 | |
171 | |
172 | static void kicad_circ(FILE *file, const struct inst *inst) |
173 | { |
174 | /* |
175 | * Xcenter, Ycenter, Xpoint, Ypoint, Width, Layer |
176 | */ |
177 | fprintf(file, "DC %d %d %d %d %d %d\n", |
178 | units_to_kicad(inst->base.x), |
179 | -units_to_kicad(inst->base.y), |
180 | units_to_kicad(inst->base.x), |
181 | -units_to_kicad(inst->base.y+inst->u.arc.r), |
182 | units_to_kicad(inst->u.arc.width), |
183 | layer_silk_top); |
184 | } |
185 | |
186 | |
187 | static void kicad_arc(FILE *file, const struct inst *inst) |
188 | { |
189 | struct coord p; |
190 | double a; |
191 | |
192 | /* |
193 | * The documentation says: |
194 | * Xstart, Ystart, Xend, Yend, Angle, Width, Layer |
195 | * |
196 | * But it's really: |
197 | * Xcenter, Ycenter, Xend, Yend, ... |
198 | */ |
199 | p = rotate_r(inst->base, inst->u.arc.r, inst->u.arc.a2); |
200 | a = inst->u.arc.a2-inst->u.arc.a1; |
201 | while (a <= 0) |
202 | a += 360; |
203 | while (a > 360) |
204 | a -= 360; |
205 | fprintf(file, "DA %d %d %d %d %d %d %d\n", |
206 | units_to_kicad(inst->base.x), |
207 | -units_to_kicad(inst->base.y), |
208 | units_to_kicad(p.x), |
209 | -units_to_kicad(p.y), |
210 | (int) (a*10.0), |
211 | units_to_kicad(inst->u.arc.width), |
212 | layer_silk_top); |
213 | } |
214 | |
215 | |
216 | static void kicad_inst(FILE *file, enum inst_prio prio, const struct inst *inst) |
217 | { |
218 | switch (prio) { |
219 | case ip_pad_copper: |
220 | case ip_pad_special: |
221 | kicad_pad(file, inst); |
222 | break; |
223 | case ip_hole: |
224 | kicad_hole(file, inst); |
225 | break; |
226 | case ip_line: |
227 | kicad_line(file, inst); |
228 | break; |
229 | case ip_rect: |
230 | kicad_rect(file, inst); |
231 | break; |
232 | case ip_circ: |
233 | kicad_circ(file, inst); |
234 | break; |
235 | case ip_arc: |
236 | kicad_arc(file, inst); |
237 | break; |
238 | default: |
239 | /* |
240 | * Don't try to export vectors, frame references, or |
241 | * measurements. |
242 | */ |
243 | break; |
244 | } |
245 | } |
246 | |
247 | |
248 | static void kicad_module(FILE *file, const struct pkg *pkg, time_t now) |
249 | { |
250 | enum inst_prio prio; |
251 | const struct inst *inst; |
252 | |
253 | /* |
254 | * Module library name |
255 | */ |
256 | fprintf(file, "$MODULE %s\n", pkg->name); |
257 | |
258 | /* |
259 | * Xpos = 0, Ypos = 0, 15 layers, last modification, timestamp, |
260 | * moveable, not autoplaced. |
261 | */ |
262 | fprintf(file, "Po 0 0 0 15 %8.8lX 00000000 ~~\n", (long) now); |
263 | |
264 | /* |
265 | * Module library name again |
266 | */ |
267 | fprintf(file, "Li %s\n", pkg->name); |
268 | |
269 | #if 0 /* optional */ |
270 | /* |
271 | * Description |
272 | */ |
273 | fprintf(file, "Cd %s\n", pkg->name); |
274 | #endif |
275 | |
276 | /* |
277 | * |
278 | */ |
279 | fprintf(file, "Sc %8.8lX\n", (long) now); |
280 | |
281 | /* |
282 | * Attributes: SMD = listed in the automatic insertion list |
283 | */ |
284 | fprintf(file, "At SMD\n"); |
285 | |
286 | /* |
287 | * Rotation cost: 0 for 90 deg, 0 for 180 deg, 0 = disable rotation |
288 | */ |
289 | fprintf(file, "Op 0 0 0\n"); |
290 | |
291 | /* |
292 | * Text fields: Tn = field number, Xpos, Ypos, Xsize ("emspace"), |
293 | * Ysize ("emspace"), rotation, pen width, N (none), V = visible, |
294 | * comment layer. All dimensions are 1/10 mil. |
295 | */ |
296 | |
297 | fprintf(file, "T0 0 -150 200 200 0 40 N V %d \"%s\"\n", |
298 | layer_comment, pkg->name); |
299 | fprintf(file, "T1 0 150 200 200 0 40 N I %d \"Val*\"\n", |
300 | layer_comment); |
301 | |
302 | FOR_INST_PRIOS_UP(prio) { |
303 | for (inst = pkgs->insts[prio]; inst; inst = inst->next) |
304 | kicad_inst(file, prio, inst); |
305 | for (inst = pkg->insts[prio]; inst; inst = inst->next) |
306 | kicad_inst(file, prio, inst); |
307 | } |
308 | |
309 | fprintf(file, "$EndMODULE %s\n", pkg->name); |
310 | } |
311 | |
312 | |
313 | int kicad(FILE *file) |
314 | { |
315 | const struct pkg *pkg; |
316 | time_t now = time(NULL); |
317 | |
318 | fprintf(file, "PCBNEW-LibModule-V1 %s", ctime(&now)); |
319 | |
320 | fprintf(file, "$INDEX\n"); |
321 | for (pkg = pkgs; pkg; pkg = pkg->next) |
322 | if (pkg->name) |
323 | fprintf(file, "%s\n", pkg->name); |
324 | fprintf(file, "$EndINDEX\n"); |
325 | |
326 | for (pkg = pkgs; pkg; pkg = pkg->next) |
327 | if (pkg->name) |
328 | kicad_module(file, pkg, now); |
329 | |
330 | fprintf(file, "$EndLIBRARY\n"); |
331 | |
332 | fflush(file); |
333 | return !ferror(file); |
334 | } |
335 |
Branches:
master