diff --git a/Makefile b/Makefile index d7f6f91..c92ba0a 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ # OBJS = fped.o expr.o coord.o obj.o delete.o inst.o util.o error.o \ - unparse.o dump.o meas.o \ + unparse.o dump.o kicad.o meas.o \ cpp.o lex.yy.o y.tab.o \ gui.o gui_util.o gui_style.o gui_inst.o gui_status.o gui_canvas.o \ gui_tool.o gui_over.o gui_meas.o gui_frame.o diff --git a/TODO b/TODO index b7ed09e..cc4bc23 100644 --- a/TODO +++ b/TODO @@ -2,7 +2,6 @@ Major missing features: - populate input area (still needed: mm/mil, rezoom) - add default unit (combine with grid unit selection ?) - consider adding auto/mm/mil selection for each dimension -- add KiCad output - add postscript output - add option to include/omit helper vecs and frames (done for display, still need postscript) diff --git a/coord.h b/coord.h index 44199b9..892da15 100644 --- a/coord.h +++ b/coord.h @@ -20,7 +20,7 @@ #define MICRON_UNITS 10 #define MIL_UNITS (25.4*MICRON_UNITS) #define MM_UNITS (1000.0*MICRON_UNITS) -#define KICAD_UNIT (10.0*MIL_UNITS) +#define KICAD_UNIT (MIL_UNITS/10.0) #define MIL_IN_MM 0.0254 @@ -54,7 +54,7 @@ static inline double units_to_mm(unit_type u) } -static inline double units_to_kicad(unit_type u) +static inline int units_to_kicad(unit_type u) { return (double) u/KICAD_UNIT; } diff --git a/gui.c b/gui.c index b214d83..b08b736 100644 --- a/gui.c +++ b/gui.c @@ -12,11 +12,16 @@ #include +#include +#include +#include #include +#include "util.h" #include "inst.h" #include "obj.h" #include "dump.h" +#include "kicad.h" #include "gui_util.h" #include "gui_style.h" #include "gui_status.h" @@ -46,24 +51,95 @@ static GtkWidget *stuff_image[2], *meas_image[2]; /* ----- menu bar ---------------------------------------------------------- */ -static void menu_save(GtkWidget *widget, gpointer user) +static char *set_extension(const char *name, const char *ext) +{ + char *s = stralloc(name); + char *slash, *dot; + char *res; + + slash = strrchr(s, '/'); + dot = strrchr(slash ? slash : s, '.'); + if (dot) + *dot = 0; + res = stralloc_printf("%s.%s", s, ext); + free(s); + return res; +} + + +static void save_with_backup(const char *name, int (*fn)(FILE *file)) { FILE *file; + char *s = stralloc(name); + char *slash, *dot, *tmp; + int n; + struct stat st; - if (!save_file) { + slash = strrchr(s, '/'); + dot = strrchr(slash ? slash : s, '.'); + if (dot) + *dot = 0; + n = 0; + while (1) { + tmp = stralloc_printf("%s~%d%s%s", + s, n, dot ? "." : "", dot ? dot+1 : ""); + if (stat(tmp, &st) < 0) { + if (errno == ENOENT) + break; + perror(tmp); + free(tmp); + return; + } + free(tmp); + n++; + } + if (rename(name, tmp) < 0) { + if (errno != ENOENT) { + perror(name); + free(tmp); + return; + } + } else { + fprintf(stderr, "renamed %s to %s\n", name, tmp); + } + free(tmp); + + file = fopen(name, "w"); + if (!file) { + perror(name); + return; + } + if (!fn(file)) + perror(name); + if (fclose(file) == EOF) + perror(name); + fprintf(stderr, "saved to %s\n", name); +} + + +static void menu_save(GtkWidget *widget, gpointer user) +{ + if (save_file) + save_with_backup(save_file, dump); + else { if (!dump(stdout)) perror("stdout"); - return; } - file = fopen(save_file, "w"); - if (!file) { - perror(save_file); - return; +} + + +static void menu_save_kicad(GtkWidget *widget, gpointer user) +{ + char *name; + + if (save_file) { + name = set_extension(save_file, "mod"); + save_with_backup(name, kicad); + free(name); + } else { + if (!kicad(stdout)) + perror("stdout"); } - if (!dump(file)) - perror(save_file); - if (fclose(file) == EOF) - perror(save_file); } @@ -86,6 +162,11 @@ static void make_menu_bar(GtkWidget *hbox) g_signal_connect(G_OBJECT(save), "activate", G_CALLBACK(menu_save), NULL); + save = gtk_menu_item_new_with_label("Save as KiCad"); + gtk_menu_shell_append(GTK_MENU_SHELL(file_menu), save); + g_signal_connect(G_OBJECT(save), "activate", + G_CALLBACK(menu_save_kicad), NULL); + quit = gtk_menu_item_new_with_label("Quit"); gtk_menu_shell_append(GTK_MENU_SHELL(file_menu), quit); g_signal_connect(G_OBJECT(quit), "activate", diff --git a/inst.c b/inst.c index 71afb84..9bfd5b8 100644 --- a/inst.c +++ b/inst.c @@ -29,39 +29,12 @@ #include "inst.h" -enum inst_prio { - ip_frame, /* frames have their own selection */ - ip_pad, /* pads also accept clicks inside */ - ip_circ, /* circles don't overlap easily */ - ip_arc, /* arc are like circles, just shorter */ - ip_rect, /* rectangles have plenty of sides */ - ip_meas, /* mesurements are like lines but set a bit apart */ - ip_line, /* lines are easly overlapped by other things */ - ip_vec, /* vectors only have the end point */ - ip_n, /* number of priorities */ -}; - - -#define FOR_INST_PRIOS_UP(prio) \ - for (prio = 0; prio != ip_n; prio++) - -#define FOR_INST_PRIOS_DOWN(prio) \ - for (prio = ip_n-1; prio != (enum inst_prio) -1; prio--) - -#define FOR_INSTS_UP(prio, inst) \ - FOR_INST_PRIOS_UP(prio) \ - for (inst = insts[prio]; inst; inst = inst->next) - -#define FOR_INSTS_DOWN(prio, inst) \ - FOR_INST_PRIOS_DOWN(prio) \ - for (inst = insts[prio]; inst; inst = inst->next) - - struct inst *selected_inst = NULL; +struct inst *insts[ip_n]; struct bbox active_frame_bbox; static struct inst *curr_frame = NULL; -static struct inst *insts[ip_n], **next_inst[ip_n]; +static struct inst **next_inst[ip_n]; static struct inst *prev_insts[ip_n]; static unsigned long active_set = 0; diff --git a/inst.h b/inst.h index 7471201..b0540cf 100644 --- a/inst.h +++ b/inst.h @@ -36,8 +36,23 @@ struct bbox { struct coord max; }; + +enum inst_prio { + ip_frame, /* frames have their own selection */ + ip_pad, /* pads also accept clicks inside */ + ip_circ, /* circles don't overlap easily */ + ip_arc, /* arc are like circles, just shorter */ + ip_rect, /* rectangles have plenty of sides */ + ip_meas, /* mesurements are like lines but set a bit apart */ + ip_line, /* lines are easly overlapped by other things */ + ip_vec, /* vectors only have the end point */ + ip_n, /* number of priorities */ +}; + + struct inst; + struct inst_ops { void (*debug)(struct inst *self); void (*save)(FILE *file, struct inst *self); @@ -95,9 +110,25 @@ struct inst { extern struct inst *selected_inst; +extern struct inst *insts[ip_n]; extern struct bbox active_frame_bbox; +#define FOR_INST_PRIOS_UP(prio) \ + for (prio = 0; prio != ip_n; prio++) + +#define FOR_INST_PRIOS_DOWN(prio) \ + for (prio = ip_n-1; prio != (enum inst_prio) -1; prio--) + +#define FOR_INSTS_UP(prio, inst) \ + FOR_INST_PRIOS_UP(prio) \ + for (inst = insts[prio]; inst; inst = inst->next) + +#define FOR_INSTS_DOWN(prio, inst) \ + FOR_INST_PRIOS_DOWN(prio) \ + for (inst = insts[prio]; inst; inst = inst->next) + + void inst_select_outside(void *item, void (*deselect)(void *item)); int inst_select(struct coord pos); void inst_deselect(void); diff --git a/kicad.c b/kicad.c new file mode 100644 index 0000000..5213908 --- /dev/null +++ b/kicad.c @@ -0,0 +1,260 @@ +/* + * kicad.c - Dump objects in the KiCad board/module format + * + * Written 2009 by Werner Almesberger + * Copyright 2009 by Werner Almesberger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + + +#include +#include + +#include "coord.h" +#include "inst.h" +#include "kicad.h" + + +enum kicad_layer { + layer_bottom, /* "copper" */ + layer_l15, + layer_l14, + layer_l13, + layer_l12, + layer_l11, + layer_l10, + layer_l9, + layer_l8, + layer_l7, + layer_l6, + layer_l5, + layer_l4, + layer_l3, + layer_l2, + layer_top, /* "component" */ + layer_glue_bottom, /* adhesive, copper side */ + layer_glue_top, /* adhesive, component side */ + layer_paste_bottom, /* solder paste */ + layer_paste_top, + layer_silk_bottom, /* silk screen */ + layer_silk_top, + layer_mask_bottom, /* solder mask */ + layer_mask_top, + layer_draw, /* general drawing */ + layer_comment, + layer_eco1, + layer_eco2, + layer_edge, /* edge */ +}; + + +static void kicad_pad(FILE *file, const struct inst *inst) +{ + struct coord min, max; + unit_type tmp; + + min.x = units_to_kicad(inst->base.x); + min.y = units_to_kicad(inst->base.y); + max.x = units_to_kicad(inst->u.pad.other.x); + max.y = units_to_kicad(inst->u.pad.other.y); + + if (min.x > max.x) { + tmp = min.x; + min.x = max.x; + max.x = tmp; + } + if (min.y > max.y) { + tmp = min.y; + min.y = max.y; + max.y = tmp; + } + + fprintf(file, "$PAD\n"); + + /* + * name, shape (rectangle), Xsize, Ysize, Xdelta, Ydelta, Orientation + */ + fprintf(file, "Sh \"%s\" R %d %d 0 0 0\n", + inst->u.pad.name, max.x-min.x, max.y-min.y); + + /* + * Attributes: pad type, N, layer mask + */ + fprintf(file, "At SMD N %8.8X\n", + (1 << layer_top) | + (1 << layer_paste_top) | + (1 << layer_mask_top)); + + /* + * Position: Xpos, Ypos + */ + fprintf(file, "Po %d %d\n", (min.x+max.x)/2, -(min.y+max.y)/2); + + fprintf(file, "$EndPAD\n"); +} + + +static void kicad_line(FILE *file, const struct inst *inst) +{ + /* + * Xstart, Ystart, Xend, Yend, Width, Layer + */ + fprintf(file, "DS %d %d %d %d %d %d\n", + units_to_kicad(inst->base.x), + -units_to_kicad(inst->base.y), + units_to_kicad(inst->u.rect.end.x), + -units_to_kicad(inst->u.rect.end.y), + units_to_kicad(inst->u.rect.width), + layer_silk_top); +} + + +static void kicad_rect(FILE *file, const struct inst *inst) +{ + unit_type xa, ya, xb, yb; + unit_type width; + + xa = units_to_kicad(inst->base.x); + ya = units_to_kicad(inst->base.y); + xb = units_to_kicad(inst->u.rect.end.x); + yb = units_to_kicad(inst->u.rect.end.y); + width = units_to_kicad(inst->u.rect.width); + + fprintf(file, "DS %d %d %d %d %d %d\n", + xa, -ya, xa, -yb, width, layer_silk_top); + fprintf(file, "DS %d %d %d %d %d %d\n", + xa, -yb, xb, -yb, width, layer_silk_top); + fprintf(file, "DS %d %d %d %d %d %d\n", + xb, -yb, xb, -ya, width, layer_silk_top); + fprintf(file, "DS %d %d %d %d %d %d\n", + xb, -ya, xa, -ya, width, layer_silk_top); +} + + +static void kicad_circ(FILE *file, const struct inst *inst) +{ + /* + * Xcenter, Ycenter, Xpoint, Ypoint, Width, Layer + */ + fprintf(file, "DC %d %d %d %d %d %d\n", + units_to_kicad(inst->base.x), + -units_to_kicad(inst->base.y), + units_to_kicad(inst->base.x), + units_to_kicad(inst->base.y+inst->u.arc.r), + units_to_kicad(inst->u.arc.width), + layer_silk_top); +} + + +static void kicad_arc(FILE *file, const struct inst *inst) +{ + fprintf(stderr, "NOT YET IMPLEMENTED\n"); +} + + +static void kicad_inst(FILE *file, enum inst_prio prio, const struct inst *inst) +{ + switch (prio) { + case ip_pad: + kicad_pad(file, inst); + break; + case ip_line: + kicad_line(file, inst); + break; + case ip_rect: + kicad_rect(file, inst); + break; + case ip_circ: + kicad_circ(file, inst); + break; + case ip_arc: + kicad_arc(file, inst); + break; + default: + break; + } +} + + +static void kicad_module(FILE *file, const char *name, time_t now) +{ + enum inst_prio prio; + const struct inst *inst; + + /* + * Module library name + */ + fprintf(file, "$MODULE %s\n", name); + + /* + * Xpos = 0, Ypos = 0, 15 layers, last modification, timestamp, + * moveable, not autoplaced. + */ + fprintf(file, "Po 0 0 0 15 %8.8lX 00000000 ~~\n", (long) now); + + /* + * Module library name again + */ + fprintf(file, "Li %s\n", name); + +#if 0 /* optional */ + /* + * Description + */ + fprintf(file, "Cd %s\n", name); +#endif + + /* + * + */ + fprintf(file, "Sc %8.8lX\n", (long) now); + + /* + * Attributes: SMD = listed in the automatic insertion list + */ + fprintf(file, "At SMD\n"); + + /* + * Rotation cost: 0 for 90 deg, 0 for 180 deg, 0 = disable rotation + */ + fprintf(file, "Op 0 0 0\n"); + + /* + * Text fields: Tn = field number, Xpos, Ypos, Xsize ("emspace"), + * Ysize ("emspace"), rotation, pen width, N (none), V = visible, + * comment layer. All dimensions are 1/10 mil. + */ + + fprintf(file, "T0 0 -150 200 200 0 40 N V %d \"%s\"\n", + layer_comment, name); + fprintf(file, "T1 0 150 200 200 0 40 N I %d \"Val*\"\n", + layer_comment); + + FOR_INSTS_UP(prio, inst) + kicad_inst(file, prio, inst); + + fprintf(file, "$EndMODULE %s\n", name); +} + + +int kicad(FILE *file) +{ + time_t now = time(NULL); + + fprintf(file, "PCBNEW-LibModule-V1 %s\n", ctime(&now)); + + fprintf(file, "$INDEX\n"); + fprintf(file, "%s\n", part_name); + fprintf(file, "$EndINDEX\n"); + + kicad_module(file, part_name, now); + + fprintf(file, "$EndLIBRARY\n"); + + fflush(file); + return !ferror(file); +} diff --git a/kicad.h b/kicad.h new file mode 100644 index 0000000..0f91b38 --- /dev/null +++ b/kicad.h @@ -0,0 +1,22 @@ +/* + * kicad.h - Dump objects in the KiCad board/module format + * + * Written 2009 by Werner Almesberger + * Copyright 2009 by Werner Almesberger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + + +#ifndef KICAD_H +#define KICAD_H + +#include + + +int kicad(FILE *file); + +#endif /* !KICAD_H */