From a941a5f2466be33f7f51ca5186e2b87e486fb87d Mon Sep 17 00:00:00 2001 From: Werner Almesberger Date: Tue, 2 Aug 2016 11:00:08 -0300 Subject: [PATCH] rename sch2fig to eeshow --- eeshow/DEMO | 25 ++ eeshow/Makefile | 91 ++++++ eeshow/TODO | 24 ++ eeshow/cro.c | 461 ++++++++++++++++++++++++++++++ eeshow/cro.h | 31 ++ eeshow/diff.c | 279 ++++++++++++++++++ eeshow/diff.h | 22 ++ eeshow/dwg.c | 463 ++++++++++++++++++++++++++++++ eeshow/dwg.h | 46 +++ eeshow/fig.c | 293 +++++++++++++++++++ eeshow/fig.h | 22 ++ eeshow/file.c | 76 +++++ eeshow/file.h | 22 ++ eeshow/gfx.c | 120 ++++++++ eeshow/gfx.h | 72 +++++ eeshow/git-file.c | 371 ++++++++++++++++++++++++ eeshow/git-file.h | 22 ++ eeshow/lib-parse.c | 268 ++++++++++++++++++ eeshow/lib-render.c | 396 ++++++++++++++++++++++++++ eeshow/lib.h | 124 ++++++++ eeshow/main.c | 172 ++++++++++++ eeshow/main.h | 31 ++ eeshow/misc.c | 114 ++++++++ eeshow/misc.h | 51 ++++ eeshow/neo900-template.fig | 15 + eeshow/record.c | 342 ++++++++++++++++++++++ eeshow/record.h | 58 ++++ eeshow/sch-parse.c | 562 +++++++++++++++++++++++++++++++++++++ eeshow/sch-render.c | 174 ++++++++++++ eeshow/sch.h | 123 ++++++++ eeshow/sch2pdf | 92 ++++++ eeshow/style.c | 31 ++ eeshow/style.h | 91 ++++++ eeshow/test.lib | 62 ++++ eeshow/test.pro | 34 +++ eeshow/test.sch | 372 ++++++++++++++++++++++++ eeshow/test/README | 24 ++ eeshow/test/comp | 29 ++ eeshow/test/genpng | 37 +++ eeshow/text.c | 158 +++++++++++ eeshow/text.h | 58 ++++ eeshow/util.h | 52 ++++ 42 files changed, 5910 insertions(+) create mode 100644 eeshow/DEMO create mode 100644 eeshow/Makefile create mode 100644 eeshow/TODO create mode 100644 eeshow/cro.c create mode 100644 eeshow/cro.h create mode 100644 eeshow/diff.c create mode 100644 eeshow/diff.h create mode 100644 eeshow/dwg.c create mode 100644 eeshow/dwg.h create mode 100644 eeshow/fig.c create mode 100644 eeshow/fig.h create mode 100644 eeshow/file.c create mode 100644 eeshow/file.h create mode 100644 eeshow/gfx.c create mode 100644 eeshow/gfx.h create mode 100644 eeshow/git-file.c create mode 100644 eeshow/git-file.h create mode 100644 eeshow/lib-parse.c create mode 100644 eeshow/lib-render.c create mode 100644 eeshow/lib.h create mode 100644 eeshow/main.c create mode 100644 eeshow/main.h create mode 100644 eeshow/misc.c create mode 100644 eeshow/misc.h create mode 100644 eeshow/neo900-template.fig create mode 100644 eeshow/record.c create mode 100644 eeshow/record.h create mode 100644 eeshow/sch-parse.c create mode 100644 eeshow/sch-render.c create mode 100644 eeshow/sch.h create mode 100755 eeshow/sch2pdf create mode 100644 eeshow/style.c create mode 100644 eeshow/style.h create mode 100644 eeshow/test.lib create mode 100644 eeshow/test.pro create mode 100644 eeshow/test.sch create mode 100644 eeshow/test/README create mode 100755 eeshow/test/comp create mode 100755 eeshow/test/genpng create mode 100644 eeshow/text.c create mode 100644 eeshow/text.h create mode 100644 eeshow/util.h diff --git a/eeshow/DEMO b/eeshow/DEMO new file mode 100644 index 0000000..dc307db --- /dev/null +++ b/eeshow/DEMO @@ -0,0 +1,25 @@ +# Prerequisites (depends on distribution) + +apt-get install libcairo2-dev +apt-get install libgit2-dev +apt-get install qiv + +# Get all the things we need + +git clone http://neo900.org/git/ee.git newdir +cd newdir/hw +git clone git://projects.qi-hardware.com/kicad-libs.git +git clone git://projects.qi-hardware.com/eda-tools.git +make -C eda-tools/eeshow + +# Generate PNG for old, new, and difference + +LIBS="neo900.lib kicad-libs/components/powered.lib" +eda-tools/eeshow/eeshow $LIBS 6a9f71:neo900_SS_5.sch -- png -s 2 -o old.png +eda-tools/eeshow/eeshow $LIBS neo900_SS_5.sch -- png -s 2 -o new.png +eda-tools/eeshow/eeshow $LIBS 6a9f71:neo900_SS_5.sch -- \ + diff -s 2 -o diff.png $LIBS neo900_SS_5.sch + +# View the result + +qiv -t diff.png old.png new.png diff --git a/eeshow/Makefile b/eeshow/Makefile new file mode 100644 index 0000000..1fa2b28 --- /dev/null +++ b/eeshow/Makefile @@ -0,0 +1,91 @@ +# +# Makefile - build eeshow +# +# Written 2016 by Werner Almesberger +# Copyright 2016 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. +# + +NAME = eeshow +OBJS = main.o sch-parse.o sch-render.o lib-parse.o lib-render.o \ + file.o git-file.o \ + style.o fig.o record.o cro.o diff.o gfx.o dwg.o text.o misc.o + +CFLAGS = -g -Wall -Wextra -Wno-unused-parameter -Wshadow \ + -Wmissing-prototypes -Wmissing-declarations \ + `pkg-config --cflags cairo` \ + `pkg-config --cflags libgit2` +LDLIBS = -lm \ + `pkg-config --libs cairo` \ + `pkg-config --libs libgit2` + +include ../common/Makefile.c-common + +.PHONY: test neo900 sch test testref png pngref diff view newref + +all:: $(NAME) + +$(NAME): $(OBJS) + $(CC) -o $(NAME) $(OBJS) $(LDLIBS) + +#----- Test sheet ------------------------------------------------------------- + +sch: + eeschema test.sch + +test: $(NAME) + ./$(NAME) test.lib test.sch >out.fig + fig2dev -L png -m 2 out.fig _out.png + [ ! -r ref.png ] || \ + compare -metric AE ref.png _out.png _diff.png || \ + qiv -t -R -D _diff.png ref.png _out.png + +testref: $(NAME) + ./$(NAME) test.lib test.sch | fig2dev -L png -m 2 >ref.png + +png: $(NAME) + ./$(NAME) test.lib test.sch -- png -o _out.png -s 2 + [ ! -r pngref.png ] || \ + compare -metric AE pngref.png _out.png _diff.png || \ + qiv -t -R -D _diff.png pngref.png _out.png + +pngref: $(NAME) + ./$(NAME) test.lib test.sch -- png -o pngref.png -s 2 + +clean:: + rm -f out.fig _out.png _diff.png + +#----- Render Neo900 schematics ----------------------------------------------- + +NEO900_HW = ../../../n9/ee/hw +KICAD_LIBS = ../../kicad-libs/components + +SHEET ?= 12 + +neo900: $(NAME) + ./$(NAME) $(NEO900_HW)/neo900.lib \ + $(KICAD_LIBS)/powered.lib \ + $(NEO900_HW)/neo900_SS_$(SHEET).sch \ + >out.fig + +neo900.pdf: $(NAME) sch2pdf neo900-template.fig + ./sch2pdf -o $@ -t neo900-template.fig \ + $(NEO900_HW)/neo900.lib $(KICAD_LIBS)/powered.lib \ + $(NEO900_HW)/neo900.sch + +#----- Regression test based on Neo900 schematics ----------------------------- + +diff: $(NAME) + test/genpng test out + test/comp test || $(MAKE) view + +view: + qiv -t -R -D `echo test/_diff*.png | \ + sed 's/\([^ ]*\)_diff\([^ ]*\)/\1_diff\2 \1ref\2 \1out\2/g'` + +newref: + test/genpng test ref diff --git a/eeshow/TODO b/eeshow/TODO new file mode 100644 index 0000000..a2e2899 --- /dev/null +++ b/eeshow/TODO @@ -0,0 +1,24 @@ +- better text size guessing also for FIG +- unify alignment, direction +- support fonts attributes ? +- support line thickness ? +- ~ as overline (grep for ~ in out.fig) +- glabel: build for "right" style, then rotate poly (like hlabel) +- show open pins / wires +- check remaining alignment / direction / rotation cases in switch statements +- support mirroring (and detect-complain if unexpected) [should be done now] +- pin shapes (inverted, clock, etc.) +- optionally display pin type +- find libraries (e.g., from .pro) +- PDF TOC +- let user set PNG size or zoom level +- parse .kicad_wks +- on parse error, politely complain, don't terminate; + convert abort / assert(0) to proper error indications +- implement destructors +- check for memory leaks +- record.c (bb_rot): implement bounding boxes for text +- nesting gfx in diff is a huge kludge, caused by global vars in gfx.c +- move path name guessing into file.c +- return indication of whether diff found any differences +- in diff, pass only options understood by cairo_png diff --git a/eeshow/cro.c b/eeshow/cro.c new file mode 100644 index 0000000..8ede2b9 --- /dev/null +++ b/eeshow/cro.c @@ -0,0 +1,461 @@ +/* + * cro.c - Cairo graphics back-end + * + * Written 2016 by Werner Almesberger + * Copyright 2016 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 +#include +#include +#include +#include + +#include +#include + +#include "util.h" +#include "style.h" +#include "text.h" +#include "gfx.h" +#include "record.h" +#include "main.h" +#include "cro.h" + + +/* + * FIG works with 1/1200 in + * KiCad works with mil + * 1 point = 1/72 in + */ + +#define DEFAULT_SCALE (72.0 / 1200) + + +struct cro_ctx { + struct record record; /* must be first */ + + int xo, yo; + float scale; + + cairo_t *cr; + cairo_surface_t *s; + + struct record *sheets; /* for PDF */ + unsigned n_sheets; + + const char *output_name; +}; + + +static inline int cd(struct cro_ctx *cc, int x) +{ + return x * cc->scale; +} + + +static inline int cx(struct cro_ctx *cc, int x) +{ + return cc->xo + x * cc->scale; +} + + +static inline int xc(struct cro_ctx *cc, int x) +{ + return (x - cc->xo) / cc->scale; +} + + +static inline int cy(struct cro_ctx *cc, int y) +{ + return cc->yo + y * cc->scale; +} + + +static inline float pt(struct cro_ctx *cc, int x) +{ + return cd(cc, x) * 72 * 1.5 / 1200.0; +} + + +static void set_color(cairo_t *cr, int color) +{ + uint32_t c; + + if (color < 0) + return; + c = color_rgb[color]; + cairo_set_source_rgb(cr, (c >> 16) / 255.0, ((c >> 8) & 255) / 255.0, + (c & 255) / 255.0); +} + + +static void paint(cairo_t *cr, int color, int fill_color) +{ + if (fill_color != COLOR_NONE) { + set_color(cr, fill_color); + if (color == COLOR_NONE) + cairo_fill(cr); + else + cairo_fill_preserve(cr); + } + if (color != COLOR_NONE) { + set_color(cr, color); + cairo_stroke(cr); + } +} + + +/* ----- General items ----------------------------------------------------- */ + + +static void cr_line(void *ctx, int sx, int sy, int ex, int ey, + int color, unsigned layer) +{ + struct cro_ctx *cc = ctx; + static const double dashes[] = { 4, 2 }; + + cairo_new_path(cc->cr); + cairo_move_to(cc->cr, cx(cc, sx), cy(cc, sy)); + cairo_line_to(cc->cr, cx(cc, ex), cy(cc, ey)); + cairo_set_dash(cc->cr, dashes, ARRAY_ELEMENTS(dashes), 0); + paint(cc->cr, color, COLOR_NONE); + cairo_set_dash(cc->cr, NULL, 0, 0); +} + + +static void cr_poly(void *ctx, + int points, const int x[points], const int y[points], + int color, int fill_color, unsigned layer) +{ + struct cro_ctx *cc = ctx; + bool closed; + int i; + + if (points < 2) + return; + closed = x[0] == x[points - 1] && y[0] == y[points - 1]; + + cairo_new_path(cc->cr); + cairo_move_to(cc->cr, cx(cc, x[0]), cy(cc, y[0])); + + for (i = 1; i != points - closed; i++) + cairo_line_to(cc->cr, cx(cc, x[i]), cy(cc, y[i])); + if (closed) + cairo_close_path(cc->cr); + + paint(cc->cr, color, fill_color); +} + + +static void cr_circ(void *ctx, int x, int y, int r, + int color, int fill_color, unsigned layer) +{ + struct cro_ctx *cc = ctx; + + cairo_new_path(cc->cr); + cairo_arc(cc->cr, cx(cc, x), cy(cc, y), cd(cc, r), 0, 2 * M_PI); + paint(cc->cr, color, fill_color); +} + + +static void cr_arc(void *ctx, int x, int y, int r, int sa, int ea, + int color, int fill_color, unsigned layer) +{ + struct cro_ctx *cc = ctx; + + cairo_new_path(cc->cr); + cairo_arc(cc->cr, cx(cc, x), cy(cc, y), cd(cc, r), + -ea / 180.0 * M_PI, -sa / 180.0 * M_PI); + paint(cc->cr, color, fill_color); +} + + +#define TEXT_STRETCH 1.3 + + +static void cr_text(void *ctx, int x, int y, const char *s, unsigned size, + enum text_align align, int rot, unsigned color, unsigned layer) +{ + struct cro_ctx *cc = ctx; + cairo_text_extents_t ext; + cairo_matrix_t m; + + cairo_set_font_size(cc->cr, cd(cc, size) * TEXT_STRETCH); + cairo_text_extents(cc->cr, s, &ext); + + set_color(cc->cr, color); + + cairo_move_to(cc->cr, cx(cc, x), cy(cc, y)); + + cairo_get_matrix(cc->cr, &m); + cairo_rotate(cc->cr, -rot / 180.0 * M_PI); + + switch (align) { + case text_min: + break; + case text_mid: + cairo_rel_move_to(cc->cr, -ext.width / 2.0, 0); + break; + case text_max: + cairo_rel_move_to(cc->cr, -ext.width, 0); + break; + default: + abort(); + } + + cairo_show_text(cc->cr, s); + cairo_set_matrix(cc->cr, &m); +} + + +static unsigned cr_text_width(void *ctx, const char *s, unsigned size) +{ + struct cro_ctx *cc = ctx; + cairo_text_extents_t ext; + + cairo_set_font_size(cc->cr, cx(cc, size) * TEXT_STRETCH); + cairo_text_extents(cc->cr, s, &ext); + return xc(cc, ext.width) * 1.05; /* @@@ Cairo seems to underestimate */ +} + + +/* ----- Initializatio and termination ------------------------------------- */ + + +static const struct gfx_ops real_cro_ops = { + .name = "cairo", + .line = cr_line, + .poly = cr_poly, + .circ = cr_circ, + .arc = cr_arc, + .text = cr_text, + .text_width = cr_text_width, +}; + + +static struct cro_ctx *init_common(int argc, char *const *argv) +{ + struct cro_ctx *cc; + char c; + + cc = alloc_type(struct cro_ctx); + cc->xo = cc->yo = 0; + cc->scale = DEFAULT_SCALE; + + cc->sheets = NULL; + cc->n_sheets = 0; + + cc->output_name = NULL; + while ((c = getopt(argc, argv, "o:s:")) != EOF) + switch (c) { + case 'o': + cc->output_name = optarg; + break; + case 's': + cc->scale = atof(optarg) * DEFAULT_SCALE; + break; + default: + usage(*argv); + } + + record_init(&cc->record, &real_cro_ops, cc); + + return cc; +} + + +static void *cr_png_init(int argc, char *const *argv) +{ + struct cro_ctx *cc; + + cc = init_common(argc, argv); + + /* cr_text_width needs *something* to work with */ + + cc->s = cairo_image_surface_create(CAIRO_FORMAT_RGB24, 16, 16); + cc->cr = cairo_create(cc->s); + + return cc; +} + + +static void *cr_pdf_init(int argc, char *const *argv) +{ + struct cro_ctx *cc; + + cc = init_common(argc, argv); + + /* cr_text_width needs *something* to work with */ + + cc->s = cairo_pdf_surface_create(NULL, 16, 16); + cc->cr = cairo_create(cc->s); + + return cc; +} + + +static void end_common(struct cro_ctx *cc, int *w, int *h) +{ + int x, y; + + cairo_surface_destroy(cc->s); + cairo_destroy(cc->cr); + + record_bbox(&cc->record, &x, &y, w, h); + +// fprintf(stderr, "%dx%d%+d%+d\n", *w, *h, x, y); + cc->xo = -cd(cc, x); + cc->yo = -cd(cc, y); + *w = cd(cc, *w); + *h = cd(cc, *h); +// fprintf(stderr, "%dx%d%+d%+d\n", *w, *h, x, y); +} + + +static void cr_png_end(void *ctx) +{ + struct cro_ctx *cc = ctx; + int w, h; + + end_common(cc, &w, &h); + + cc->s = cairo_image_surface_create(CAIRO_FORMAT_RGB24, w, h); + cc->cr = cairo_create(cc->s); + + set_color(cc->cr, COLOR_WHITE); + cairo_paint(cc->cr); + + cairo_select_font_face(cc->cr, "Helvetica", CAIRO_FONT_SLANT_NORMAL, + CAIRO_FONT_WEIGHT_BOLD); + cairo_set_line_width(cc->cr, 2); + + record_replay(&cc->record); + record_destroy(&cc->record); + + if (cc->output_name) + cairo_surface_write_to_png(cc->s, cc->output_name); +} + + +static void cr_pdf_new_sheet(void *ctx) +{ + struct cro_ctx *cc = ctx; + + cc->n_sheets++; + cc->sheets = realloc(cc->sheets, sizeof(struct record) * cc->n_sheets); + if (!cc->sheets) { + perror("realloc"); + exit(1); + } + cc->sheets[cc->n_sheets - 1] = cc->record; + record_wipe(&cc->record); +} + + +static void cr_pdf_end(void *ctx) +{ + struct cro_ctx *cc = ctx; + int w, h; + unsigned i; + + end_common(cc, &w, &h); + + cc->s = cairo_pdf_surface_create(cc->output_name, w, h); + cc->cr = cairo_create(cc->s); + + cairo_select_font_face(cc->cr, "Helvetica", CAIRO_FONT_SLANT_NORMAL, + CAIRO_FONT_WEIGHT_BOLD); + cairo_set_line_width(cc->cr, 2); + + for (i = 0; i != cc->n_sheets; i++) { + set_color(cc->cr, COLOR_WHITE); + cairo_paint(cc->cr); + + record_replay(cc->sheets + i); + record_destroy(cc->sheets + i); + + cairo_show_page(cc->cr); + } + + record_replay(&cc->record); + record_destroy(&cc->record); + + cairo_show_page(cc->cr); + + cairo_surface_destroy(cc->s); + cairo_destroy(cc->cr); +} + + +uint32_t *cro_img_end(void *ctx, int *w, int *h, int *stride) +{ + struct cro_ctx *cc = ctx; + uint32_t *data; + + end_common(cc, w, h); + + *stride = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, *w); + data = alloc_size(*stride * *h); + + cc->s = cairo_image_surface_create_for_data((unsigned char *) data, + CAIRO_FORMAT_RGB24, *w, *h, *stride); + cc->cr = cairo_create(cc->s); + + set_color(cc->cr, COLOR_WHITE); + cairo_paint(cc->cr); + + cairo_select_font_face(cc->cr, "Helvetica", CAIRO_FONT_SLANT_NORMAL, + CAIRO_FONT_WEIGHT_BOLD); + cairo_set_line_width(cc->cr, 2); + + record_replay(&cc->record); + record_destroy(&cc->record); + + return data; +} + + +void cro_img_write(void *ctx, const char *name) +{ + struct cro_ctx *cc = ctx; + + cairo_surface_write_to_png(cc->s, name); +} + + +/* ----- Operations -------------------------------------------------------- */ + + +const struct gfx_ops cro_png_ops = { + .name = "png", + .line = record_line, + .poly = record_poly, + .circ = record_circ, + .arc = record_arc, + .text = record_text, + .text_width = cr_text_width, + .init = cr_png_init, + .end = cr_png_end, +}; + +const struct gfx_ops cro_pdf_ops = { + .name = "pdf", + .line = record_line, + .poly = record_poly, + .circ = record_circ, + .arc = record_arc, + .text = record_text, + .text_width = cr_text_width, + .init = cr_pdf_init, + .new_sheet = cr_pdf_new_sheet, + .end = cr_pdf_end, +}; diff --git a/eeshow/cro.h b/eeshow/cro.h new file mode 100644 index 0000000..3595da8 --- /dev/null +++ b/eeshow/cro.h @@ -0,0 +1,31 @@ +/* + * cro.h - Cairo graphics back-end + * + * Written 2016 by Werner Almesberger + * Copyright 2016 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 CRO_H +#define CRO_H + +#include + +#include "gfx.h" + + +extern const struct gfx_ops cro_png_ops; +extern const struct gfx_ops cro_pdf_ops; + +#define cro_img_ops cro_png_ops /* just don't call cro_img_ops.end */ + + +uint32_t *cro_img_end(void *ctx, int *w, int *h, int *stride); +void cro_img_write(void *ctx, const char *name); + +#endif /* !CRO_H */ diff --git a/eeshow/diff.c b/eeshow/diff.c new file mode 100644 index 0000000..8f7d4a0 --- /dev/null +++ b/eeshow/diff.c @@ -0,0 +1,279 @@ +/* + * diff.c - Schematics difference + * + * Written 2016 by Werner Almesberger + * Copyright 2016 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 +#include +#include + +#include "util.h" +#include "main.h" +#include "cro.h" +#include "sch.h" +#include "lib.h" +#include "diff.h" + + +#define DEFAULT_FRAME_RADIUS 30 + + +struct area { + int xa, ya, xb, yb; + struct area *next; +}; + +struct diff { + void *cr_ctx; + uint32_t *new_img; + int w, h, stride; + const char *output_name; + int frame_radius; + struct area *areas; +}; + + +/* ----- Wrappers ---------------------------------------------------------- */ + + +static void diff_line(void *ctx, int sx, int sy, int ex, int ey, + int color, unsigned layer) +{ + const struct diff *diff = ctx; + + cro_img_ops.line(diff->cr_ctx, sx, sy, ex, ey, color, layer); +} + + +static void diff_poly(void *ctx, + int points, const int x[points], const int y[points], + int color, int fill_color, unsigned layer) +{ + const struct diff *diff = ctx; + + cro_img_ops.poly(diff->cr_ctx, points, x, y, color, fill_color, layer); +} + + +static void diff_circ(void *ctx, int x, int y, int r, + int color, int fill_color, unsigned layer) +{ + const struct diff *diff = ctx; + + cro_img_ops.circ(diff->cr_ctx, x, y, r, color, fill_color, layer); +} + + +static void diff_arc(void *ctx, int x, int y, int r, int sa, int ea, + int color, int fill_color, unsigned layer) +{ + const struct diff *diff = ctx; + + cro_img_ops.arc(diff->cr_ctx, x, y, r, sa, ea, + color, fill_color, layer); +} + + +static void diff_text(void *ctx, int x, int y, const char *s, unsigned size, + enum text_align align, int rot, unsigned color, unsigned layer) +{ + const struct diff *diff = ctx; + + cro_img_ops.text(diff->cr_ctx, x, y, s, size, align, rot, + color, layer); +} + + +static unsigned diff_text_width(void *ctx, const char *s, unsigned size) +{ + const struct diff *diff = ctx; + + return cro_img_ops.text_width(diff->cr_ctx, s, size); +} + + +/* ----- Initialization and termination ------------------------------------ */ + + +static void *diff_init(int argc, char *const *argv) +{ + struct diff *diff; + char c; + int arg; + struct sch_ctx new_sch; + struct lib new_lib; + + diff = alloc_type(struct diff); + diff->areas = NULL; + + sch_init(&new_sch, 0); + lib_init(&new_lib); + + diff->output_name = NULL; + diff->frame_radius = DEFAULT_FRAME_RADIUS; + while ((c = getopt(argc, argv, "o:s:")) != EOF) + switch (c) { + case 'o': + diff->output_name = optarg; + break; + case 's': + /* for cro_png */ + break; + default: + usage(*argv); + } + + if (argc - optind < 1) + usage(*argv); + + for (arg = optind; arg != argc - 1; arg++) + lib_parse(&new_lib, argv[arg]); + sch_parse(&new_sch, argv[argc - 1], &new_lib); + + optind = 0; + gfx_init(&cro_img_ops, argc, argv); + diff->cr_ctx = gfx_ctx; + sch_render(new_sch.sheets); + diff->new_img = cro_img_end(gfx_ctx, + &diff->w, &diff->h, &diff->stride); + + optind = 0; + diff->cr_ctx = cro_img_ops.init(argc, argv); + + return diff; +} + + +/* steal from schhist/ppmdiff.c */ + +#define ONLY_OLD 0xff0000 +#define ONLY_NEW 0x00d000 +#define BOTH 0x707070 + +#define AREA_FILL 0xffffc8 + + +static void mark_area(struct diff *diff, int x, int y) +{ + struct area *area; + + for (area = diff->areas; area; area = area->next) + if (x >= area->xa && x <= area->xb && + y >= area->ya && y <= area->yb) { + if (area->xa > x - diff->frame_radius) + area->xa = x - diff->frame_radius; + if (area->xb < x + diff->frame_radius) + area->xb = x + diff->frame_radius; + if (area->ya > y - diff->frame_radius) + area->ya = y - diff->frame_radius; + if (area->yb < y + diff->frame_radius) + area->yb = y + diff->frame_radius; + return; + } + + area = alloc_type(struct area); + + area->xa = x - diff->frame_radius; + area->xb = x + diff->frame_radius; + area->ya = y - diff->frame_radius; + area->yb = y + diff->frame_radius; + + area->next = diff->areas; + diff->areas = area; +} + + +#define MASK 0xffffff + + +static void differences(struct diff *diff, uint32_t *a, const uint32_t *b) +{ + int x, y; + unsigned skip = diff->w * 4 - diff->stride; + + for (y = 0; y != diff->h; y++) { + for (x = 0; x != diff->w; x++) { + if (!((*a ^ *b) & MASK)) { + *a = ((*a >> 3) & 0x1f1f1f) | 0xe0e0e0; +// *a = ((*a >> 2) & 0x3f3f3f) | 0xc0c0c0; + } else { + mark_area(diff, x, y); +//fprintf(stderr, "0x%06x 0x%06x", *a, *b); + *a = (*a & MASK) == MASK ? ONLY_NEW : + (*b & MASK) == MASK ? ONLY_OLD : BOTH; +//fprintf(stderr, "-> 0x%06x\n", *a); + } + a++; + b++; + } + a += skip; + b += skip; + } +} + + +static void show_areas(struct diff *diff, uint32_t *a) +{ + const struct area *area; + uint32_t *p; + int x, y; + + for (area = diff->areas; area; area = area->next) + for (y = area->ya; y != area->yb; y++) { + if (y < 0 || y >= diff->h) + continue; + p = a + y * (diff->stride >> 2); + for (x = area->xa; x != area->xb; x++) { + if (x >= 0 && x < diff->w && + (p[x] & MASK) == MASK) + p[x] = AREA_FILL; + } + } +} + + +static void diff_end(void *ctx) +{ + struct diff *diff = ctx; + uint32_t *old_img; + int w, h, stride; + + old_img = cro_img_end(diff->cr_ctx, &w, &h, &stride); + if (diff->w != w || diff->h != h) { + fprintf(stderr, "%d x %d vs. %d x %d image\n", + w, h, diff->w, diff->h); + exit(1); + } + + differences(diff, old_img, diff->new_img); + show_areas(diff, old_img); + + if (diff->output_name) + cro_img_write(diff->cr_ctx, diff->output_name); +} + + +/* ----- Operations -------------------------------------------------------- */ + + +const struct gfx_ops diff_ops = { + .name = "diff", + .line = diff_line, + .poly = diff_poly, + .circ = diff_circ, + .arc = diff_arc, + .text = diff_text, + .text_width = diff_text_width, + .init = diff_init, + .end = diff_end, +}; diff --git a/eeshow/diff.h b/eeshow/diff.h new file mode 100644 index 0000000..22626b1 --- /dev/null +++ b/eeshow/diff.h @@ -0,0 +1,22 @@ +/* + * diff.h - Schematics difference + * + * Written 2016 by Werner Almesberger + * Copyright 2016 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 DIFF_H +#define DIFF_H + +#include "gfx.h" + + +extern const struct gfx_ops diff_ops; + +#endif /* !DIFF_H */ diff --git a/eeshow/dwg.c b/eeshow/dwg.c new file mode 100644 index 0000000..08fa64a --- /dev/null +++ b/eeshow/dwg.c @@ -0,0 +1,463 @@ +/* + * dwg.c - Complex drawing functions + * + * Written 2016 by Werner Almesberger + * Copyright 2016 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. + */ + + +#define _GNU_SOURCE /* for asprintf */ +#include +#include +#include +#include + +#include "util.h" +#include "misc.h" +#include "style.h" +#include "text.h" +#include "gfx.h" +#include "dwg.h" + + +/* ----- Labels ------------------------------------------------------------ */ + + +enum box_type { // ___ + box_simple, // [___] + box_left, // <___] + box_right, // [___> + box_both, // <___> +}; + + +static enum box_type flip_box(enum box_type box) +{ + switch (box) { + case box_simple: + return box_simple; + case box_left: + return box_right; + case box_right: + return box_left; + case box_both: + return box_both; + default: + abort(); + } +} + + +void dwg_label(int x, int y, const char *s, int dir, int dim, + enum dwg_shape shape) +{ + struct text txt = { + .s = s, + .size = dim, + .x = x, + .y = y, + .rot = 0, + .hor = 0, + .vert = text_min, + }; + int dx = 0, dy = 0; + + switch (dir) { + case 0: /* right */ + txt.rot = 0; + txt.hor = text_min; + dy = 1; + break; + case 1: /* up */ + txt.rot = 90; + txt.hor = text_min; + dx = -1; + break; + case 2: /* left */ + txt.rot = 0; + txt.hor = text_max; + dy = 1; + break; + case 3: /* down */ + txt.rot = 90; + txt.hor = text_max; + dx = -1; + break; + default: + assert(0); + } + + txt.y -= dy * LABEL_OFFSET; + txt.x += dx * LABEL_OFFSET; + text_fig(&txt, COLOR_LABEL, LAYER_LABEL); +} + + +void dwg_glabel(int x, int y, const char *s, int dir, int dim, + enum dwg_shape shape) +{ + struct text txt = { + .s = s, + .size = dim, + .x = x, + .y = y, + .rot = 0, + .hor = 0, + .vert = text_mid, + }; + int n = 6; + int vx[7]; + int vy[7]; + int half = (dim >> 1) + GLABEL_OFFSET; + enum box_type box; + int dx, shift_flat, shift_tip; + bool anchor_right = 1; + char *tag; + + switch (shape) { + case dwg_unspec: + box = box_simple; + break; + case dwg_in: + box = box_right; + break; + case dwg_out: + box = box_left; + break; + case dwg_bidir: + box = box_both; + break; + default: + assert(0); + } + + switch (dir) { + case 0: /* left */ + txt.rot = 0; + txt.hor = text_max; + dx = -1; + break; + case 1: /* up */ + txt.rot = 90; + txt.hor = text_min; + dx = 1; + box = flip_box(box); + anchor_right = !anchor_right; + break; + case 2: /* right */ + txt.rot = 0; + txt.hor = text_min; + dx = 1; + box = flip_box(box); + anchor_right = !anchor_right; + break; + case 3: /* down */ + txt.rot = 90; + txt.hor = text_max; + dx = -1; + break; + default: + assert(0); + } + + shift_flat = dx * GLABEL_OFFSET; + shift_tip = dx * (GLABEL_OFFSET + half); + + switch (box) { + case box_simple: + n = 5; + text_shift(&txt, txt.hor, text_mid, shift_flat, 0); + text_rel(&txt, text_min, text_min, + -GLABEL_OFFSET, GLABEL_OFFSET, vx + 1, vy + 1); + text_rel(&txt, text_max, text_min, + GLABEL_OFFSET, GLABEL_OFFSET, vx + 2, vy + 2); + text_rel(&txt, text_max, text_max, + GLABEL_OFFSET, -GLABEL_OFFSET, vx + 3, vy + 3); + text_rel(&txt, text_min, text_max, + -GLABEL_OFFSET, -GLABEL_OFFSET, vx + 4, vy + 4); + break; + case box_right: + text_shift(&txt, txt.hor, text_mid, + anchor_right ? shift_tip : shift_flat, 0); + text_rel(&txt, text_min, text_min, + -GLABEL_OFFSET, GLABEL_OFFSET, vx + 1, vy + 1); + text_rel(&txt, text_max, text_min, + GLABEL_OFFSET, GLABEL_OFFSET, vx + 2, vy + 2); + text_rel(&txt, text_max, text_mid, GLABEL_OFFSET + half, 0, + vx + 3, vy + 3); + text_rel(&txt, text_max, text_max, + GLABEL_OFFSET, -GLABEL_OFFSET, vx + 4, vy + 4); + text_rel(&txt, text_min, text_max, + -GLABEL_OFFSET, -GLABEL_OFFSET, vx + 5, vy + 5); + break; + case box_left: + text_shift(&txt, txt.hor, text_mid, + anchor_right ? shift_flat : shift_tip, 0); + text_rel(&txt, text_min, text_min, + -GLABEL_OFFSET, GLABEL_OFFSET, vx + 1, vy + 1); + text_rel(&txt, text_max, text_min, + GLABEL_OFFSET, GLABEL_OFFSET, vx + 2, vy + 2); + text_rel(&txt, text_max, text_max, + GLABEL_OFFSET, -GLABEL_OFFSET, vx + 3, vy + 3); + text_rel(&txt, text_min, text_max, + -GLABEL_OFFSET, -GLABEL_OFFSET, vx + 4, vy + 4); + text_rel(&txt, text_min, text_mid, -GLABEL_OFFSET- half, 0, + vx + 5, vy + 5); + break; + case box_both: + n = 7; + text_shift(&txt, txt.hor, text_mid, shift_tip, 0); + text_rel(&txt, text_min, text_min, + -GLABEL_OFFSET, GLABEL_OFFSET, vx + 1, vy + 1); + text_rel(&txt, text_max, text_min, + GLABEL_OFFSET, GLABEL_OFFSET, vx + 2, vy + 2); + text_rel(&txt, text_max, text_mid, GLABEL_OFFSET + half, 0, + vx + 3, vy + 3); + text_rel(&txt, text_max, text_max, + GLABEL_OFFSET, -GLABEL_OFFSET, vx + 4, vy + 4); + text_rel(&txt, text_min, text_max, + -GLABEL_OFFSET, -GLABEL_OFFSET, vx + 5, vy + 5); + text_rel(&txt, text_min, text_mid, -GLABEL_OFFSET- half, 0, + vx + 6, vy + 6); + break; + default: + assert(0); + } + + text_fig(&txt, COLOR_GLABEL, LAYER_GLABEL); + + vx[0] = vx[n - 1]; + vy[0] = vy[n - 1]; + gfx_poly(n, vx, vy, COLOR_GLABEL, COLOR_NONE, LAYER_GLABEL); + + if (asprintf(&tag, "G:%s", s)) {} + gfx_tag(tag, n, vx, vy); +} + + +static int make_box(enum box_type box, int h, int *vx, int *vy) +{ + int r = h / 2; + + switch (box) { + case box_simple: + vx[0] = 0; + vy[0] = -r; + vx[1] = 2 * r; + vy[1] = -r; + vx[2] = 2 * r; + vy[2] = r; + vx[3] = 0; + vy[3] = r; + return 4; + case box_right: + vx[0] = 0; + vy[0] = -r; + vx[1] = r; + vy[1] = -r; + vx[2] = 2 * r; + vy[2] = 0; + vx[3] = r; + vy[3] = r; + vx[4] = 0; + vy[4] = r; + return 5; + case box_left: + vx[0] = r; + vy[0] = -r; + vx[1] = 2 * r; + vy[1] = -r; + vx[2] = 2 * r; + vy[2] = r; + vx[3] = r; + vy[3] = r; + vx[4] = 0; + vy[4] = 0; + return 5; + case box_both: + vx[0] = 0; + vy[0] = 0; + vx[1] = r; + vy[1] = -r; + vx[2] = 2 * r; + vy[2] = 0; + vx[3] = r; + vy[3] = r; + return 4; + default: + assert(0); + } +} + + +void dwg_hlabel(int x, int y, const char *s, int dir, int dim, + enum dwg_shape shape) +{ + struct text txt = { + .s = s, + .size = dim, + .x = x, + .y = y, + .rot = 0, + .hor = 0, + .vert = text_mid, + }; + int vx[6], vy[6]; + int rot; + int n, i; + + switch (shape) { + case dwg_unspec: + n = make_box(box_simple, dim, vx, vy); + break; + case dwg_in: + n = make_box(box_left, dim, vx, vy); + break; + case dwg_out: + n = make_box(box_right, dim, vx, vy); + break; + case dwg_bidir: + n = make_box(box_both, dim, vx, vy); + break; + default: + assert(0); + } + + switch (dir) { + case 0: /* right */ + rot = 180; + txt.hor = text_max; + break; + case 1: /* up */ + rot = 90; + txt.hor = text_min; + break; + case 2: /* left */ + rot = 0; + txt.hor = text_min; + break; + case 3: /* down */ + rot = 270; + txt.hor = text_max; + break; + default: + assert(0); + } + + txt.x += rx((1 + HLABEL_OFFSET_F) * dim, 0, rot); + txt.y += ry((1 + HLABEL_OFFSET_F) * dim, 0, rot); + + for (i = 0; i != n; i++) { + int tmp; + + tmp = x + rx(vx[i], vy[i], rot); + vy[i] = y + ry(vx[i], vy[i], rot); + vx[i] = tmp; + } + + vx[n] = vx[0]; + vy[n] = vy[0]; + + txt.rot = rot % 180; + + text_fig(&txt, COLOR_HLABEL, LAYER_HLABEL); + gfx_poly(n + 1, vx, vy, COLOR_HLABEL, COLOR_NONE, LAYER_HLABEL); +} + + +/* ----- Text -------------------------------------------------------------- */ + + +void dwg_text(int x, int y, const char *s, int dir, int dim, + enum dwg_shape shape) +{ + struct text txt = { + .s = s, + .size = dim, + .x = x, + .y = y, + .rot = 0, + .hor = text_min, + .vert = text_min, + }; + + switch (dir) { + case 0: /* right */ + break; + case 1: /* up */ + text_rot(&txt, 90); + break; + case 2: /* left */ + txt.hor = text_max; + break; + case 3: /* down */ + text_rot(&txt, 90); + txt.hor = text_max; + break; + default: + assert(2 + 2 == 5); + } + + text_fig(&txt, COLOR_TEXT, LAYER_TEXT); +} + + +/* ----- Connections ------------------------------------------------------- */ + + +void dwg_junction(int x, int y) +{ + gfx_circ(x, y, JUNCTION_R, COLOR_NONE, COLOR_WIRE, LAYER_WIRES); +} + + +void dwg_noconn(int x, int y) +{ + int vx[2] = { x - NOCONN_LEN, x + NOCONN_LEN }; + int vy[2] = { y - NOCONN_LEN, y + NOCONN_LEN }; + + gfx_poly(2, vx, vy, COLOR_NOCONN, COLOR_NONE, LAYER_NOCONN); + swap(vy[0], vy[1]); + gfx_poly(2, vx, vy, COLOR_NOCONN, COLOR_NONE, LAYER_NOCONN); +} + + +/* ----- Lines ------------------------------------------------------------- */ + +/* + * We can't use gfx_poly because lines are dashed and we don't have that + * property at the gfx_poly API. + */ + +void dwg_line(int sx, int sy, int ex, int ey) +{ + gfx_line(sx, sy, ex, ey, COLOR_SHEET_DWG, LAYER_LINES); +} + + +/* ----- Wires and busses -------------------------------------------------- */ + + +void dwg_wire(int sx, int sy, int ex, int ey) +{ + int vx[] = { sx, ex }; + int vy[] = { sy, ey }; + + // WIDTH_WIRE + gfx_poly(2, vx, vy, COLOR_WIRE, COLOR_NONE, LAYER_WIRES); +} + + +void dwg_bus(int sx, int sy, int ex, int ey) +{ + int vx[] = { sx, ex }; + int vy[] = { sy, ey }; + + // WIDTH_BUS + gfx_poly(2, vx, vy, COLOR_BUS, COLOR_NONE, LAYER_BUSSES); +} diff --git a/eeshow/dwg.h b/eeshow/dwg.h new file mode 100644 index 0000000..697f524 --- /dev/null +++ b/eeshow/dwg.h @@ -0,0 +1,46 @@ +/* + * dwg.h - Complex drawing functions + * + * Written 2016 by Werner Almesberger + * Copyright 2016 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 DWG_H +#define DWG_H + +#include "fig.h" + + +enum dwg_shape { + dwg_unspec, // UnSpc + dwg_in, // Input + dwg_out, // Output + dwg_tri, // 3State + dwg_bidir, // Bidirectional +}; + + +void dwg_label(int x, int y, const char *s, int dir, int dim, + enum dwg_shape shape); +void dwg_hlabel(int x, int y, const char *s, int dir, int dim, + enum dwg_shape shape); +void dwg_glabel(int x, int y, const char *s, int dir, int dim, + enum dwg_shape shape); +void dwg_text(int x, int y, const char *s, int dir, int dim, + enum dwg_shape shape); + +void dwg_junction(int x, int y); +void dwg_noconn(int x, int y); + +void dwg_line(int sx, int sy, int ex, int ey); + +void dwg_wire(int sx, int sy, int ex, int ey); +void dwg_bus(int sx, int sy, int ex, int ey); + +#endif /* !DWG_H */ diff --git a/eeshow/fig.c b/eeshow/fig.c new file mode 100644 index 0000000..04ec9f4 --- /dev/null +++ b/eeshow/fig.c @@ -0,0 +1,293 @@ +/* + * fig.c - Generate FIG output for Eeschema items + * + * Written 2016 by Werner Almesberger + * Copyright 2016 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 +#include +#include +#include +#include + +#include "util.h" +#include "style.h" +#include "text.h" +#include "main.h" +#include "fig.h" + + +/* + * FIG works with 1/1200 in + * KiCad works with mil + * 1 point = 1/72 in + */ + + +static inline int cx(int x) +{ + return x * 1200 / 1000; +} + + +static inline int cy(int y) +{ + return y * 1200 / 1000; +} + + +static inline float pt(int x) +{ + return cx(x) * 72 * 1.5 / 1200.0; +} + + +/* ----- Schematics items -------------------------------------------------- */ + + +static void fig_line(void *ctx, int sx, int sy, int ex, int ey, + int color, unsigned layer) +{ + // TypeStyle FillCol AreaFil Cap FwdAr + // SubTy Color Pen StyleV Rad BwdAr + // Thick Depth Join Points + printf("2 1 2 %d %d 7 %d -1 -1 3.0 1 1 -1 0 0 2\n", + WIDTH_LINE, color, layer); + printf("\t%d %d %d %d\n", cx(sx), cy(sy), cx(ex), cy(ey)); +} + + +/* ----- General items ----------------------------------------------------- */ + + +static void fig_rect(void *ctx, int sx, int sy, int ex, int ey, + int color, int fill_color, unsigned layer) +{ + // Type Thick Depth StyleV Rad + // SubTy Color Pen Join FwdAr + // Style FillCol AreaFil Cap BwdAr + printf("2 2 0 %d %d %d %d -1 %d 0.0 1 1 -1 0 0 5\n", + color == -1 ? 0 : WIDTH_COMP_DWG, color, fill_color, layer, + fill_color == -1 ? -1 : 20); + printf("\t%d %d %d %d %d %d %d %d %d %d\n", + cx(sx), cy(sy), cx(ex), cy(sy), cx(ex), cy(ey), cx(sx), cy(ey), + cx(sx), cy(sy)); +} + + +static void fig_poly(void *ctx, + int points, const int x[points], const int y[points], + int color, int fill_color, unsigned layer) +{ + int i; + char ch = '\t'; + + // Type Thick Depth StyleV Rad + // SubTy Color Pen Join FwdAr + // Style FillCol AreaFil Cap BwdAr + printf("2 1 0 %d %d %d %d -1 %d 0.0 1 1 -1 0 0 %d\n", + color == -1 ? 0 : WIDTH_COMP_DWG, color, fill_color, layer, + fill_color == -1 ? -1 : 20, points); + for (i = 0; i != points; i++) { + printf("%c%d %d", ch, cx(x[i]), cy(y[i])); + ch = ' '; + } + printf("\n"); +} + + +static void fig_circ(void *ctx, int x, int y, int r, + int color, int fill_color, unsigned layer) +{ + // Type Thick Depth StyleV Cx Rx Sx Ex + // SubTy Color Pen Dir Cy Ry Sy Ey + // Style FillCol AreaFil Angle + printf("1 3 0 %d %d %d %d -1 %d 0.0 1 0.0 %d %d %d %d %d %d %d %d\n", + color == -1 ? 0 : WIDTH_COMP_DWG, color, fill_color, layer, + fill_color == -1 ? -1 : 20, + cx(x), cy(y), r, r, + cx(x), cy(y), cx(x) + r, cy(y)); +} + + +static int ax(int x, int y, int r, int angle) +{ + float a = angle / 180.0 * M_PI; + + return cx(x + r * cos(a)); +} + + +static int ay(int x, int y, int r, int angle) +{ + float a = angle / 180.0 * M_PI; + + return cy(y - r * sin(a)); +} + + +static void fig_arc(void *ctx, int x, int y, int r, int sa, int ea, + int color, int fill_color, unsigned layer) +{ + int ma = (sa + ea) / 2; + + // Type Thick Depth StyleV FwdAr + // SubTy Color Pen Cap BwdAr + // Style FillCol AreaFil Dir points + printf("5 1 0 %d %d %d %d -1 %d 0.0 1 1 0 0 %d %d %d %d %d %d %d %d\n", + color == -1 ? 0 : WIDTH_COMP_DWG, color, fill_color, layer, + fill_color == -1 ? -1 : 20, + cx(x), cy(y), + ax(x, y, r, sa), ay(x, y, r, sa), + ax(x, y, r, ma), ay(x, y, r, ma), + ax(x, y, r, ea), ay(x, y, r, ea)); +} + + +static void fig_tag(void *ctx, const char *s, + int points, const int x[points], const int y[points]) +{ + printf("# href=\"%s\" alt=\"\"\n", s); + fig_poly(ctx, points, x, y, COLOR_NONE, COLOR_NONE, 999); +} + + +static void fig_text(void *ctx, int x, int y, const char *s, unsigned size, + enum text_align align, int rot, unsigned color, unsigned layer) +{ + // Type Depth FontSiz Height + // Just Pen Angle Length + // Color Font Flags X Y + printf("4 %u %d %d -1 %d %f %f 4 0.0 0.0 %d %d %s\\001\n", + align, color, layer, FONT_HELVETICA_BOLD, + pt(size), rot / 180.0 * M_PI, cx(x), cy(y), s); +} + + +static unsigned fig_text_width(void *ctx, const char *s, unsigned size) +{ + /* + * Note that we stretch the text size, so the ratio is larger than + * expressed here. + */ + return strlen(s) * size * 1.0; +} + + +/* ----- FIG file header --------------------------------------------------- */ + + +static void fig_header(void) +{ + printf("#FIG 3.2\n"); + printf("Landscape\n"); + printf("Center\n"); + printf("Metric\n"); + printf("A4\n"); + printf("100.00\n"); + printf("Single\n"); + printf("-2\n"); + printf("1200 2\n"); + + /* User32, COLOR_DARK_YELLOW */ + printf("0 32 #848400\n"); +} + + +static bool apply_vars(char *buf, int n_vars, const char **vars) +{ + char *p; + const char **var, *eq; + int var_len, value_len; + + p = strchr(buf, '<'); + if (!p) + return 0; + for (var = vars; var != vars + n_vars; var++) { + eq = strchr(*var, '='); + assert(eq); + var_len = eq - *var; + if (strncmp(p + 1, *var, var_len)) + continue; + value_len = strlen(eq + 1); + memmove(p + value_len, p + var_len + 2, + strlen(p + var_len + 2) + 1); + memcpy(p, eq + 1, value_len); + return 1; + } + return 0; +} + + + +static void *fig_init(int argc, char *const *argv) +{ + const char *template = NULL; + const char **vars = NULL; + int n_vars = 0; + char c; + int arg; + FILE *file; + char buf[1000]; + + while ((c = getopt(argc, argv, "t:")) != EOF) + switch (c) { + case 't': + template = optarg; + break; + default: + usage(*argv); + } + + for (arg = optind; arg != argc; arg++) { + if (!strchr(argv[arg], '=')) + usage(*argv); + n_vars++; + vars = realloc(vars, sizeof(const char *) * n_vars); + vars[n_vars - 1] = argv[arg]; + } + + if (!template) { + fig_header(); + return NULL; + } + + file = fopen(template, "r"); + if (!file) { + perror(template); + exit(1); + } + while (fgets(buf, sizeof(buf), file)) { + while (apply_vars(buf, n_vars, vars)); + printf("%s", buf); + } + fclose(file); + + return NULL; +} + + +/* ----- Operations -------------------------------------------------------- */ + + +const struct gfx_ops fig_ops = { + .name = "fig", + .line = fig_line, + .rect = fig_rect, + .poly = fig_poly, + .circ = fig_circ, + .arc = fig_arc, + .text = fig_text, + .tag = fig_tag, + .text_width = fig_text_width, + .init = fig_init, +}; diff --git a/eeshow/fig.h b/eeshow/fig.h new file mode 100644 index 0000000..6e6a361 --- /dev/null +++ b/eeshow/fig.h @@ -0,0 +1,22 @@ +/* + * fig.h - Generate FIG output for Eeschema items + * + * Written 2016 by Werner Almesberger + * Copyright 2016 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 FIG_H +#define FIG_H + +#include "gfx.h" + + +extern const struct gfx_ops fig_ops; + +#endif /* !FIG_H */ diff --git a/eeshow/file.c b/eeshow/file.c new file mode 100644 index 0000000..1145173 --- /dev/null +++ b/eeshow/file.c @@ -0,0 +1,76 @@ +/* + * file.c - Open and read a file + * + * Written 2016 by Werner Almesberger + * Copyright 2016 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 +#include + +#include "util.h" +#include "main.h" +#include "git-file.h" +#include "file.h" + + +bool file_cat(void *user, const char *line) +{ + printf("%s\n", line); + return 1; +} + + +static void read_from_file(FILE *file, + bool (*parse)(void *user, const char *line), void *user) +{ + char buf[1000]; + char *nl; + + while (fgets(buf, sizeof(buf), file)) { + nl = strchr(buf, '\n'); + if (nl) + *nl = 0; + if (!parse(user, buf)) + break; + } +} + + +void file_read(const char *name, bool (*parse)(void *user, const char *line), + void *user) +{ + FILE *file; + char *colon, *tmp; + + file = fopen(name, "r"); + if (file) { + if (verbose) + fprintf(stderr, "reading %s\n", name); + read_from_file(file, parse, user); + fclose(file); + return; + } + + if (verbose) + perror(name); + + colon = strchr(name, ':'); + if (!colon) { + if (!verbose) + perror(name); + exit(1); + } + + tmp = stralloc(name); + tmp[colon - name] = 0; + git_read(tmp, colon + 1, parse, user); + free(tmp); +} diff --git a/eeshow/file.h b/eeshow/file.h new file mode 100644 index 0000000..a6cc170 --- /dev/null +++ b/eeshow/file.h @@ -0,0 +1,22 @@ +/* + * file.h - Open and read a file + * + * Written 2016 by Werner Almesberger + * Copyright 2016 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 FILE_H +#define FILE_H + +#include + +bool file_cat(void *user, const char *line); +void file_read(const char *name, bool (*parse)(void *user, const char *line), + void *user); + +#endif /* !FILE_H */ diff --git a/eeshow/gfx.c b/eeshow/gfx.c new file mode 100644 index 0000000..ba88134 --- /dev/null +++ b/eeshow/gfx.c @@ -0,0 +1,120 @@ +/* + * gfx.c - Generate graphical output for Eeschema items + * + * Written 2016 by Werner Almesberger + * Copyright 2016 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 "style.h" +#include "text.h" +#include "gfx.h" + + +void *gfx_ctx; + +static const struct gfx_ops *gfx_ops; + + +void gfx_line(int sx, int sy, int ex, int ey, int color, unsigned layer) +{ + if (gfx_ops->line) { + gfx_ops->line(gfx_ctx, sx, sy, ex, ey, color, layer); + return; + } + + int vx[] = { sx, ex }; + int vy[] = { sy, ey }; + + gfx_poly(2, vx, vy, color, COLOR_NONE, layer); +} + + +void gfx_rect(int sx, int sy, int ex, int ey, + int color, int fill_color, unsigned layer) +{ + if (gfx_ops->rect) { + gfx_ops->rect(gfx_ctx, sx, sy, ex, ey, + color, fill_color, layer); + return; + } + + int vx[] = { sx, ex, ex, sx, sx }; + int vy[] = { sy, sy, ey, ey, sy }; + + gfx_poly(5, vx, vy, color, fill_color, layer); +} + + +void gfx_poly(int points, const int x[points], const int y[points], + int color, int fill_color, unsigned layer) +{ + gfx_ops->poly(gfx_ctx, points, x, y, color, fill_color, layer); +} + + +void gfx_circ(int x, int y, int r, int color, int fill_color, unsigned layer) +{ + gfx_ops->circ(gfx_ctx, x, y, r, color, fill_color, layer); +} + + +void gfx_arc(int x, int y, int r, int sa, int ea, + int color, int fill_color, unsigned layer) +{ + gfx_ops->arc(gfx_ctx, x, y, r, sa, ea, color, fill_color, layer); +} + + +void gfx_text(int x, int y, const char *s, unsigned size, + enum text_align align, int rot, unsigned color, unsigned layer) +{ + gfx_ops->text(gfx_ctx, x, y, s, size, align, rot, color, layer); +} + + +void gfx_tag(const char *s, + unsigned points, const int x[points], int const y[points]) +{ + if (gfx_ops->tag) + gfx_ops->tag(gfx_ctx, s, points, x, y); +} + + +unsigned gfx_text_width(const char *s, unsigned size) +{ + return gfx_ops->text_width(gfx_ctx, s, size); +} + + +void gfx_init(const struct gfx_ops *ops, int argc, char *const *argv) +{ + gfx_ctx = ops->init(argc, argv); + gfx_ops = ops; +} + + +void gfx_new_sheet(void) +{ + if (gfx_ops->new_sheet) + gfx_ops->new_sheet(gfx_ctx); +} + + +bool gfx_multi_sheet(void) +{ + return !!gfx_ops->new_sheet; +} + +void gfx_end(void) +{ + if (gfx_ops->end) + gfx_ops->end(gfx_ctx); +} diff --git a/eeshow/gfx.h b/eeshow/gfx.h new file mode 100644 index 0000000..d707c24 --- /dev/null +++ b/eeshow/gfx.h @@ -0,0 +1,72 @@ +/* + * gfx.h - Generate graphical output for Eeschema items + * + * Written 2016 by Werner Almesberger + * Copyright 2016 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 GFX_H +#define GFX_H + +#include + +#include "text.h" + + +struct gfx_ops { + const char *name; + void (*line)(void *ctx, int sx, int sy, int ex, int ey, + int color, unsigned layer); + void (*rect)(void *ctx, int sx, int sy, int ex, int ey, + int color, int fill_color, unsigned layer); + void (*poly)(void *ctx, + int points, const int x[points], const int y[points], + int color, int fill_color, unsigned layer); + void (*circ)(void *ctx, int x, int y, int r, + int color, int fill_color, unsigned layer); + void (*arc)(void *ctx, int x, int y, int r, int sa, int ea, + int color, int fill_color, unsigned layer); + void (*text)(void *ctx, int x, int y, const char *s, unsigned size, + enum text_align align, int rot, unsigned color, unsigned layer); + void (*tag)(void *ctx, const char *s, + int points, const int x[points], const int y[points]); + unsigned (*text_width)(void *ctx, const char *s, unsigned size); + void *(*init)(int argc, char *const *argv); + void (*new_sheet)(void *ctx); + void (*end)(void *ctx); +}; + + +extern void *gfx_ctx; + + +/* wrappers */ + +void gfx_line(int sx, int sy, int ex, int ey, int color, unsigned layer); +void gfx_rect(int sx, int sy, int ex, int ey, + int color, int fill_color, unsigned layer); +void gfx_poly(int points, const int x[points], const int y[points], + int color, int fill_color, unsigned layer); +void gfx_circ(int x, int y, int r, int color, int fill_color, unsigned layer); +void gfx_arc(int x, int y, int r, int sa, int ea, + int color, int fill_color, unsigned layer); +void gfx_text(int x, int y, const char *s, unsigned size, + enum text_align align, int rot, unsigned color, unsigned layer); +void gfx_tag(const char *s, + unsigned points, const int x[points], int const y[points]); +unsigned gfx_text_width(const char *s, unsigned size); + +/* inititalization and termination */ + +void gfx_init(const struct gfx_ops *ops, int argc, char *const *argv); +void gfx_new_sheet(void); +bool gfx_multi_sheet(void); +void gfx_end(void); + +#endif /* !GFX_H */ diff --git a/eeshow/git-file.c b/eeshow/git-file.c new file mode 100644 index 0000000..098f136 --- /dev/null +++ b/eeshow/git-file.c @@ -0,0 +1,371 @@ +/* + * git-file.c - Open and read a file from git + * + * Written 2016 by Werner Almesberger + * Copyright 2016 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. + */ + +#define _GNU_SOURCE /* for get_current_dir_name */ +#include +#include +#include +#include +#include +#include + +#include + +#include "util.h" +#include "main.h" +#include "git-file.h" + + +static git_repository *select_repo(const char *path) +{ + git_repository *repo = NULL; + char *tmp = stralloc(path); + char *slash; + + /* + * If we can't find a repo, this may be due to the file or directory + * the path points to not existing in the currently checked-out tree. + * So we trim off elements until we find a repository. + */ + while (1) { + if (verbose > 2) + fprintf(stderr, "trying \"%s\"\n", tmp); + if (!git_repository_open_ext(&repo, *tmp ? tmp : "/", + GIT_REPOSITORY_OPEN_CROSS_FS, NULL)) + break; + slash = strrchr(tmp, '/'); + if (!slash) + break; + *slash = 0; + } + free(tmp); + return repo; +} + + +static git_tree *pick_revision(git_repository *repo, const char *revision) +{ + git_commit *commit; + git_object *obj; + git_tree *tree; + + if (git_revparse_single(&obj, repo, revision)) { + const git_error *e = giterr_last(); + + fprintf(stderr, "%s: %s\n", + git_repository_path(repo), e->message); + exit(1); + } + + if (git_object_type(obj) != GIT_OBJ_COMMIT) { + fprintf(stderr, "%s: not a commit\n", revision); + exit(1); + } + commit = (git_commit *) obj; + + if (git_commit_tree(&tree, commit)) { + const git_error *e = giterr_last(); + + fprintf(stderr, "%s: %s\n", revision, e->message); + exit(1); + } + + return tree; +} + + +static char *canonical_path_into_repo(const char *repo_dir, const char *path) +{ + struct stat repo_st, path_st; + char *tmp, *tmp2, *slash, *tail, *real; + char *to; + const char *end, *from; + + /* identify inode of repo root */ + + if (stat(repo_dir, &repo_st) < 0) { + perror(repo_dir); + exit(1); + } + if (!S_ISDIR(repo_st.st_mode)) { + fprintf(stderr, "%s: not a directory\n", repo_dir); + exit(1); + } + + /* convert relative paths to absolute */ + + if (*path == '/') { + tmp = stralloc(path); + } else { + char *cwd = get_current_dir_name(); + + tmp = alloc_size(strlen(cwd) + 1 + strlen(path) + 1); + sprintf(tmp, "%s/%s", cwd, path); + free(cwd); + } + + /* remove trailing / */ + + slash = strrchr(tmp, '/'); + if (slash && slash != tmp && !slash[1]) + *slash = 0; + + + /* + * If path does point to inexistent object, separate into the part that + * is valid on the current system and the tail containing dead things. + */ + end = tail = strchr(tmp, 0); + + while (1) { + if (verbose > 2) + fprintf(stderr, "probing \"%s\" tail \"%s\"\n", + tmp, tail); + if (stat(tmp, &path_st) == 0) + break; + if (!tmp[1]) { + fprintf(stderr, "%s: cannot resolve\n", path); + exit(1); + } + slash = strrchr(tmp, '/'); + if (tail != end) + tail[-1] = '/'; + tail = slash + 1; + *slash = 0; + } + + /* remove . and .. from tail */ + + if (verbose > 2) + fprintf(stderr, "input tail \"%s\"\n", tail); + from = to = tail; + while (1) { + if (!strncmp(from, "./", 2)) { + from += 2; + continue; + } + if (!strcmp(from, ".")) + break; + if (strncmp(from, "../", 3) && strcmp(from, "..")) { + while (*from) { + *to++ = *from++; + if (from[-1] == '/') + break; + } + if (!*from) + break; + } + if (to == tail) { + /* + * We have something like this: + * /home/repo/dead/../../foo + */ + fprintf(stderr, "%s: can't climb out of dead path\n", + path); + exit(1); + } + + /* + * We have something like + * "foo/" -> "" + * or + * "foo/bar/" -> "foo/" + * where "to" points to the end. + */ + to--; + while (to != tail && to[-1] != '/') + to--; + } + *to = 0; + if (verbose > 2) + fprintf(stderr, "output tail \"%s\"\n", tail); + + /* resolve all symlinks */ + + real = realpath(tmp, NULL); + if (verbose > 2) + fprintf(stderr, "realpath(\"%s\") = \"%s\"\n", tmp, real); + + /* append tail */ + + if (*tail) { + tmp2 = alloc_size(strlen(real) + 1 + strlen(tail) + 1); + sprintf(tmp2, "%s/%s", real, tail); + free(real); + } else { + tmp2 = real; + } + free(tmp); + tmp = tmp2; + + if (verbose > 1) + fprintf(stderr, "full object path \"%s\"\n", tmp); + + /* find which part of our path is inside the repo */ + + end = tail = strchr(tmp, 0); + while (1) { + if (verbose > 2) + fprintf(stderr, "trying \"%s\" tail \"%s\"\n", + tmp, tail); + + if (stat(tmp, &path_st) == 0 && + path_st.st_dev == repo_st.st_dev && + path_st.st_ino == repo_st.st_ino) + break; + + /* "this cannot happen" */ + if (tail == tmp) { + fprintf(stderr, + "divergent paths:\nrepo \"%s\"\nobject \"%s\"\n", + repo_dir, tmp); + exit(1); + } + + slash = strrchr(tmp, '/'); + if (tail != end) + tail[-1] = '/'; + tail = slash + 1; + *slash = 0; + } + + if (verbose > 1) + fprintf(stderr, "path in repo \"%s\"\n", tail); + + tmp2 = stralloc(tail); + free(tmp); + return tmp2; +} + + +static git_tree_entry *find_file(git_repository *repo, git_tree *tree, + const char *path) +{ + git_tree_entry *entry; + char *repo_path = stralloc(git_repository_path(repo)); + char *slash, *canon_path; + int len; + + /* remove trailing / from repo_path */ + slash = strrchr(repo_path, '/'); + if (slash && slash != repo_path && !slash[1]) + *slash = 0; + + len = strlen(repo_path); + if (len >= 5 && !strcmp(repo_path + len - 5, "/.git")) + repo_path[len == 5 ? 1 : len - 5] = 0; + + if (verbose > 1) + fprintf(stderr, "repo dir \"%s\"\n", repo_path); + + canon_path = canonical_path_into_repo(repo_path, path); + + if (git_tree_entry_bypath(&entry, tree, canon_path)) { + const git_error *e = giterr_last(); + + fprintf(stderr, "%s: %s\n", path, e->message); + exit(1); + } + free(canon_path); + + return entry; +} + + +static const void *get_data(git_repository *repo, git_tree_entry *entry, + unsigned *size) +{ + git_object *obj; + git_blob *blob; + + if (git_tree_entry_type(entry) != GIT_OBJ_BLOB) { + fprintf(stderr, "entry is not a blob\n"); + exit(1); + } + if (git_tree_entry_to_object(&obj, repo, entry)) { + const git_error *e = giterr_last(); + + fprintf(stderr, "%s\n", e->message); + exit(1); + } + + blob = (git_blob *) obj; + *size = git_blob_rawsize(blob); + return git_blob_rawcontent(blob); +} + + +static bool send_line(const char *s, unsigned len, + bool (*parse)(void *user, const char *line), void *user) +{ + char *tmp = alloc_size(len + 1); + bool res; + + memcpy(tmp, s, len); + tmp[len] = 0; + res = parse(user, tmp); + free(tmp); + return res; +} + + +static void send_data(const char *data, unsigned size, + bool (*parse)(void *user, const char *line), void *user) +{ + const char *end = data + size; + const char *p = data; + const char *nl; + + while (p != end) { + nl = memchr(p, '\n', end - p); + if (!nl) { + send_line(p, end - p, parse, user); + return; + } + if (!send_line(p, nl - p, parse, user)) + return; + p = nl + 1; + } +} + + +void git_read(const char *revision, const char *name, + bool (*parse)(void *user, const char *line), void *user) +{ + static bool initialized = 0; + git_repository *repo; + git_tree *tree; + git_tree_entry *entry; + const void *data; + unsigned size; + + if (!initialized) { + git_libgit2_init(); + initialized = 1; + } + + repo = select_repo(name); + if (!repo) { + fprintf(stderr, "%s:%s not found\n", revision, name); + exit(1); + } + if (verbose > 1) + fprintf(stderr, "using repository %s\n", + git_repository_path(repo)); + + tree = pick_revision(repo, revision); + entry = find_file(repo, tree, name); + if (verbose) + fprintf(stderr, "reading %s:%s\n", revision, name); + data = get_data(repo, entry, &size); + send_data(data, size, parse, user); +} diff --git a/eeshow/git-file.h b/eeshow/git-file.h new file mode 100644 index 0000000..4642528 --- /dev/null +++ b/eeshow/git-file.h @@ -0,0 +1,22 @@ +/* + * git-file.h - Open and read a file from git + * + * Written 2016 by Werner Almesberger + * Copyright 2016 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 GIT_FILE_H +#define GIT_FILE_H + +#include + + +void git_read(const char *revision, const char *name, + bool (*parse)(void *user, const char *line), void *user); + +#endif /* !GIT_FILE_H */ diff --git a/eeshow/lib-parse.c b/eeshow/lib-parse.c new file mode 100644 index 0000000..21d37ff --- /dev/null +++ b/eeshow/lib-parse.c @@ -0,0 +1,268 @@ +/* + * lib.c - Parse Eeschema .lib file + * + * Written 2016 by Werner Almesberger + * Copyright 2016 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 + +#include "util.h" +#include "text.h" +#include "file.h" +#include "lib.h" + + +/* ----- Text -------------------------------------------------------------- */ + + +static enum text_style decode_style(const char *s) +{ + if (!strcmp(s, "Normal")) + return text_normal; + assert(0); +} + + +/* ----- Polygons ---------------------------------------------------------- */ + + +static bool parse_poly(struct lib_poly *poly, const char *line, int points) +{ + int i, n; + + poly->points = points; + poly->x = alloc_size(sizeof(int) * points); + poly->y = alloc_size(sizeof(int) * points); + for (i = 0; i != points; i++) { + if (sscanf(line, "%d %d %n", + poly->x + i, poly->y + i, &n) != 2) + return 0; + line += n; + } + if (sscanf(line, "%c", &poly->fill) != 1) + return 0; + return 1; +} + + +/* ----- Definitions ------------------------------------------------------- */ + + +static bool parse_def(struct lib *lib, const char *line) +{ + char *s; + char draw_num, draw_name; + unsigned name_offset; + unsigned units; + + if (sscanf(line, "DEF %ms %*s %*d %u %c %c %u", + &s, &name_offset, &draw_num, &draw_name, &units) != 5) + return 0; + + lib->curr_comp = alloc_type(struct comp); + if (*s == '~') + s++; + lib->curr_comp->name = s; + lib->curr_comp->units = units; + + lib->curr_comp->visible = 0; + lib->curr_comp->show_pin_name = draw_name == 'Y'; + lib->curr_comp->show_pin_num = draw_num == 'Y'; + lib->curr_comp->name_offset = name_offset; + + lib->curr_comp->objs = NULL; + lib->next_obj = &lib->curr_comp->objs; + + lib->curr_comp->next = NULL; + *lib->next_comp = lib->curr_comp; + lib->next_comp = &lib->curr_comp->next; + + return 1; +} + + +/* ----- Arcs -------------------------------------------------------------- */ + + +static bool parse_arc(struct lib_obj *obj, const char *line) +{ + struct lib_arc *arc = &obj->u.arc; + int a1, a2; + + if (sscanf(line, "A %d %d %d %d %d %u %u %u %c", + &arc->x, &arc->y, &arc->r, &a1, &a2, &obj->unit, &obj->convert, + &arc->thick, &arc->fill) != 9) + return 0; + + /* + * KiCad arcs can be clockwise or counter-clockwise. They must always + * be smaller than 180 degrees. + */ + + while (a1 < 0) + a1 += 3600; + while (a2 < 0) + a2 += 3600; + a1 %= 3600; + a2 %= 3600; + if (a2 < a1) + a2 += 3600; + assert(a2 - a1 != 1800); + if (a2 - a1 > 1800) + swap(a1, a2); + + arc->start_a = (a1 % 3600) / 10; + arc->end_a = (a2 % 3600) / 10; + + return 1; +} + + +/* ----- Library parser ---------------------------------------------------- */ + + +static bool lib_parse_line(void *user, const char *line) +{ + struct lib *lib = user; + int n = 0; + unsigned points; + struct lib_obj *obj; + char *style; + unsigned zero1, zero2; + char vis; + + lib->lineno++; + + switch (lib->state) { + case lib_skip: + if (parse_def(lib, line)) { + lib->state = lib_def; + return 1; + } + return 1; + case lib_def: + if (sscanf(line, "DRAW%n", &n) == 0 && n) { + lib->state = lib_draw; + return 1; + } + if (sscanf(line, "F%d \"\" %*d %*d %*d %*c %c", &n, &vis) == 2 + || sscanf(line, "F%d \"%*[^\"]\" %*d %*d %*d %*c %c", + &n, &vis) == 2) { + if (vis == 'V') + lib->curr_comp->visible |= 1 << n; + return 1; + } + /* @@@ explicitly ignore FPLIST */ + return 1; + case lib_draw: + if (sscanf(line, "ENDDRAW%n", &n) == 0 && n) { + lib->state = lib_skip; + return 1; + } + + obj = alloc_type(struct lib_obj); + obj->next = NULL; + *lib->next_obj = obj; + lib->next_obj = &obj->next; + + if (sscanf(line, "P %u %u %u %u %n", + &points, &obj->unit, &obj->convert, &obj->u.poly.thick, + &n) == 4) { + obj->type = lib_obj_poly; + if (parse_poly(&obj->u.poly, line + n, points)) + return 1; + break; + } + if (sscanf(line, "S %d %d %d %d %u %u %d %c", + &obj->u.rect.sx, &obj->u.rect.sy, &obj->u.rect.ex, + &obj->u.rect.ey, &obj->unit, &obj->convert, + &obj->u.rect.thick, &obj->u.rect.fill) == 8) { + obj->type = lib_obj_rect; + return 1; + } + if (sscanf(line, "C %d %d %d %u %u %d %c", + &obj->u.circ.x, &obj->u.circ.y, &obj->u.circ.r, + &obj->unit, &obj->convert, &obj->u.circ.thick, + &obj->u.circ.fill) == 7) { + obj->type = lib_obj_circ; + return 1; + } + if (parse_arc(obj, line)) { + obj->type = lib_obj_arc; + return 1; + } + n = sscanf(line, + "T %d %d %d %d %u %u %u \"%m[^\"]\" %ms %u %c %c", + &obj->u.text.orient, &obj->u.text.x, &obj->u.text.y, + &obj->u.text.dim, &zero1, &obj->unit, &obj->convert, + &obj->u.text.s, &style, &zero2, + &obj->u.text.hor_align, &obj->u.text.vert_align); + if (n != 12) { + n = sscanf(line, + "T %d %d %d %d %u %u %u %ms %ms %u %c %c", + &obj->u.text.orient, &obj->u.text.x, &obj->u.text.y, + &obj->u.text.dim, &zero1, &obj->unit, &obj->convert, + &obj->u.text.s, &style, &zero2, + &obj->u.text.hor_align, &obj->u.text.vert_align); + while (n == 12) { + char *tilde; + + tilde = strchr(obj->u.text.s, '~'); + if (!tilde) + break; + *tilde = ' '; + } + } + /* + * zero2 seems to be the font style: 0 = normal, 1 = bold ? + */ + if (n == 12) { + if (zero1) { + fprintf(stderr, "%u: only understand 0 x x\n" + "\"%s\"\n", lib->lineno, line); + exit(1); + } + obj->u.text.style = decode_style(style); + obj->type = lib_obj_text; + return 1; + } + if (sscanf(line, "X %ms %ms %d %d %d %c %d %d %u %u %c", + &obj->u.pin.name, &obj->u.pin.number, + &obj->u.pin.x, &obj->u.pin.y, &obj->u.pin.length, + &obj->u.pin.orient, + &obj->u.pin.number_size, &obj->u.pin.name_size, + &obj->unit, &obj->convert, &obj->u.pin.etype) == 11) { + obj->type = lib_obj_pin; + return 1; + } + break; + default: + abort(); + } + fprintf(stderr, "%u: cannot parse\n\"%s\"\n", lib->lineno, line); + exit(1); +} + + +void lib_parse(struct lib *lib, const char *file) +{ + lib->state = lib_skip; + lib->lineno = 0; + file_read(file, lib_parse_line, lib); +} + + +void lib_init(struct lib *lib) +{ + lib->comps = NULL; + lib->next_comp = &lib->comps; +} diff --git a/eeshow/lib-render.c b/eeshow/lib-render.c new file mode 100644 index 0000000..fe20c9a --- /dev/null +++ b/eeshow/lib-render.c @@ -0,0 +1,396 @@ +/* + * lib.c - Render component from library + * + * Written 2016 by Werner Almesberger + * Copyright 2016 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 + +#include "util.h" +#include "misc.h" +#include "style.h" +#include "gfx.h" +#include "text.h" +#include "sch.h" +#include "lib.h" + + +/* ----- Drawing ----------------------------------------------------------- */ + + +static void draw_poly(const struct lib_poly *poly, const int m[6]) +{ + int n = poly->points; + int x[n]; + int y[n]; + int i; + + for (i = 0; i != n; i++) { + x[i] = mx(poly->x[i], poly->y[i], m); + y[i] = my(poly->x[i], poly->y[i], m); + } + + gfx_poly(n, x, y, COLOR_COMP_DWG, COLOR_NONE, LAYER_COMP_DWG); + + switch (poly->fill) { + case 'N': + break; + case 'F': + gfx_poly(n, x, y, COLOR_NONE, COLOR_COMP_DWG, + LAYER_COMP_DWG_BG); + break; + case 'f': + gfx_poly(n, x, y, COLOR_NONE, COLOR_COMP_DWG_BG, + LAYER_COMP_DWG_BG); + break; + default: + abort(); + } +} + + +static void draw_rect(const struct lib_rect *rect, const int m[6]) +{ + int sx = mx(rect->sx, rect->sy, m); + int sy = my(rect->sx, rect->sy, m); + int ex = mx(rect->ex, rect->ey, m); + int ey = my(rect->ex, rect->ey, m); + + gfx_rect(sx, sy, ex, ey, COLOR_COMP_DWG, COLOR_NONE, LAYER_COMP_DWG); + + switch (rect->fill) { + case 'N': + break; + case 'F': + gfx_rect(sx, sy, ex, ey, COLOR_NONE, COLOR_COMP_DWG, + LAYER_COMP_DWG_BG); + break; + case 'f': + gfx_rect(sx, sy, ex, ey, COLOR_NONE, COLOR_COMP_DWG_BG, + LAYER_COMP_DWG_BG); + break; + default: + abort(); + } +} + + +static void draw_circ(const struct lib_circ *circ, const int m[6]) +{ + int x = mx(circ->x, circ->y, m); + int y = my(circ->x, circ->y, m); + int r = circ->r; + + gfx_circ(x, y, r, COLOR_COMP_DWG, COLOR_NONE, LAYER_COMP_DWG); + + switch (circ->fill) { + case 'N': + break; + case 'F': + gfx_circ(x, y, r, COLOR_NONE, COLOR_COMP_DWG, + LAYER_COMP_DWG_BG); + break; + case 'f': + gfx_circ(x, y, r, COLOR_NONE, COLOR_COMP_DWG_BG, + LAYER_COMP_DWG_BG); + break; + default: + abort(); + } +} + + +static void draw_arc(const struct lib_arc *arc, const int m[6]) +{ + int a = matrix_to_angle(m); + int x = mx(arc->x, arc->y, m); + int y = my(arc->x, arc->y, m); + int sa = angle_add(arc->start_a, a); + int ea = angle_add(arc->end_a, a); + + if (matrix_is_mirrored(m)) { + sa = 180 - sa; + ea = 180 - ea; + while (ea < sa) + ea += 360; + while (ea - sa > 360) + ea -= 360; + if (ea - sa >= 180) { + swap(sa, ea); + sa += 360; + } + } + + gfx_arc(x, y, arc->r, sa, ea, + COLOR_COMP_DWG, COLOR_NONE, LAYER_COMP_DWG); + + assert(arc->fill == 'N'); +} + + +static void draw_pin_name(const struct comp *comp, const struct lib_pin *pin, + const int m[6], int dx, int dy, int rot, enum text_align hor) +{ + int ox, oy, sx, sy; + + if (comp->name_offset) { + ox = dx * (pin->length + comp->name_offset); + oy = dy * (pin->length + comp->name_offset); + sx = sy = 0; + } else { + ox = dx * pin->length / 2; + oy = dy * pin->length / 2; + sx = mxr(-dy * PIN_NUM_OFFSET, dx * PIN_NUM_OFFSET, m); + sy = myr(-dy * PIN_NUM_OFFSET, dx * PIN_NUM_OFFSET, m); + if (sx > 0) + sx = -sx; + if (sy > 0) + sy = -sy; + } + + struct text txt = { + .s = pin->name, + .x = mx(pin->x + ox, pin->y + oy, m) + sx, + .y = my(pin->x + ox, pin->y + oy, m) + sy, + .size = pin->name_size, + .rot = rot, + .hor = comp->name_offset ? hor : text_mid, + .vert = comp->name_offset ? text_mid : text_min, + }; + + text_rot(&txt, matrix_to_angle(m)); + if (matrix_is_mirrored(m)) { + if ((txt.rot % 180) == 0) + txt.hor = text_flip(txt.hor); + else + txt.vert = text_flip(txt.vert); + } + + switch (txt.rot) { + case 180: + case 270: + text_flip_x(&txt); + break; + default: + break; + } + + text_fig(&txt, COLOR_PIN_NAME, LAYER_PIN_NAME); +} + + +static void draw_pin_num(const struct comp *comp, const struct lib_pin *pin, + const int m[6], int dx, int dy, int rot, enum text_align hor) +{ + int ox, oy, sx, sy; + + ox = dx * pin->length / 2; + oy = dy * pin->length / 2; + + sx = mxr(-dy * PIN_NUM_OFFSET, dx * PIN_NUM_OFFSET, m); + sy = myr(-dy * PIN_NUM_OFFSET, dx * PIN_NUM_OFFSET, m); + if (sx > 0) + sx = -sx; + if (sy > 0) + sy = -sy; + + if (!comp->name_offset) { + sx = -sx; + sy = -sy; + } + + struct text txt = { + .s = pin->number, + .x = mx(pin->x + ox, pin->y + oy, m) + sx, + .y = my(pin->x + ox, pin->y + oy, m) + sy, + .size = pin->number_size, + .rot = rot, + .hor = text_mid, + .vert = comp->name_offset ? text_min : text_max, + }; + + text_rot(&txt, matrix_to_angle(m) % 180); + if (matrix_is_mirrored(m)) { + switch (txt.rot) { + case 0: + txt.hor = text_flip(txt.hor); + break; + case 90: + break; + case 180: + txt.hor = text_flip(txt.hor); + break; + case 270: + break; + } + } + + switch (txt.rot) { + case 180: + case 270: + text_flip_x(&txt); + break; + default: + break; + } + + text_fig(&txt, COLOR_PIN_NUMBER, LAYER_PIN_NUMBER); +} + + +static void draw_pin(const struct comp *comp, const struct lib_pin *pin, + const int m[6]) +{ + int x[2], y[2]; + int dx = 0, dy = 0; + int rot; + enum text_align hor; + + switch (pin->orient) { + case 'U': + dy = 1; + rot = 90; + hor = text_min; + break; + case 'D': + dy = -1; + rot = 90; + hor = text_max; + break; + case 'R': + dx = 1; + rot = 0; + hor = text_min; + break; + case 'L': + dx = -1; + rot = 0; + hor = text_max; + break; + default: + abort(); + } + x[0] = mx(pin->x, pin->y, m); + y[0] = my(pin->x, pin->y, m); + x[1] = mx(pin->x + dx * pin->length, pin->y + dy * pin->length, m); + y[1] = my(pin->x + dx * pin->length, pin->y + dy * pin->length, m); + gfx_poly(2, x, y, COLOR_COMP_DWG, COLOR_NONE, LAYER_COMP_DWG); + + if (comp->show_pin_name) + draw_pin_name(comp, pin, m, dx, dy, rot, hor); + + if (comp->show_pin_num) + draw_pin_num(comp, pin, m, dx, dy, rot, hor); +} + + +static void draw_text(const struct lib_text *text, const int m[6]) +{ + struct text txt = { + .s = text->s, + .size = text->dim, + .x = mx(text->x, text->y, m), + .y = my(text->x, text->y, m), + .rot = angle_add(text->orient / 10, matrix_to_angle(m)), + }; + + decode_alignment(&txt, text->hor_align, text->vert_align); + + switch (txt.rot) { + case 180: + case 270: + /* @@@ consolidate this with text_flip_x */ + txt.rot = angle_add(txt.rot, 180); + txt.hor = text_flip(txt.hor); + txt.vert = text_flip(txt.vert); +// text_flip_x(&txt); + break; + default: + break; + } + + if (matrix_is_mirrored(m)) + switch (txt.rot) { + case 0: + case 180: + txt.hor = text_flip(txt.hor); + break; + case 90: + case 270: + txt.vert = text_flip(txt.vert); + break; + default: + abort(); + } + + text_fig(&txt, COLOR_COMP_DWG, WIDTH_COMP_DWG); +} + + +static void draw(const struct comp *comp, const struct lib_obj *obj, + const int m[6]) +{ + switch (obj->type) { + case lib_obj_poly: + draw_poly(&obj->u.poly, m); + break; + case lib_obj_rect: + draw_rect(&obj->u.rect, m); + break; + case lib_obj_circ: + draw_circ(&obj->u.circ, m); + break; + case lib_obj_arc: + draw_arc(&obj->u.arc, m); + break; + case lib_obj_text: + draw_text(&obj->u.text, m); + break; + case lib_obj_pin: + draw_pin(comp, &obj->u.pin, m); + break; + default: + abort(); + } +} + + +const struct comp *lib_find(const struct lib *lib, const char *name) +{ + const struct comp *comp; + + for (comp = lib->comps; comp; comp = comp->next) + if (!strcmp(comp->name, name)) + return comp; + fprintf(stderr, "\"%s\" not found\n", name); + exit(1); +} + + +bool lib_field_visible(const struct comp *comp, int n) +{ + return (comp->visible >> n) & 1; +} + + +void lib_render(const struct comp *comp, unsigned unit, const int m[4]) +{ + const struct lib_obj *obj; + + if (!unit) + unit = 1; + for (obj = comp->objs; obj; obj = obj->next) { + if (obj->unit && obj->unit != unit) + continue; + draw(comp, obj, m); + } +} diff --git a/eeshow/lib.h b/eeshow/lib.h new file mode 100644 index 0000000..4f1e9f0 --- /dev/null +++ b/eeshow/lib.h @@ -0,0 +1,124 @@ +/* + * lib.h - Parse Eeschema .lib file + * + * Written 2016 by Werner Almesberger + * Copyright 2016 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 LIB_H +#define LIB_H + +#include + + +enum lib_state { + lib_skip, /* before a definition */ + lib_def, /* in definition */ + lib_draw, /* in drawings */ +}; + +struct lib_obj { + enum lib_obj_type { + lib_obj_poly, + lib_obj_rect, + lib_obj_circ, + lib_obj_arc, + lib_obj_text, + lib_obj_pin, + } type; + unsigned unit; + unsigned convert; + union { + struct lib_poly { + int thick; + char fill; + int points; + int *x; + int *y; + } poly; + struct lib_rect { + int thick; + char fill; + int sx, sy; + int ex, ey; + } rect; + struct lib_circ { + int x, y; + int r; + int thick; + char fill; + } circ; + struct lib_arc { + int x, y; + int r; + int start_a, end_a; + int thick; + char fill; + } arc; + struct lib_text { + int orient; + int x, y; + int dim; + char *s; + enum text_style style; + char hor_align; + char vert_align; + } text; + struct lib_pin { + char *name; + char *number; + int x, y; + int length; + char orient; + int number_size; + int name_size; + char etype; + // @@@ shape + } pin; + } u; + struct lib_obj *next; +}; + +struct comp { + const char *name; + unsigned units; + + unsigned visible; /* visible fields, bit mask */ + bool show_pin_name; + bool show_pin_num; + unsigned name_offset; + + struct lib_obj *objs; + struct comp *next; +}; + +struct lib { + enum lib_state state; + unsigned lineno; + + struct comp *comps; + + struct comp *curr_comp; /* current component */ + struct comp **next_comp; + struct lib_obj **next_obj; + +}; + + +extern struct comp *comps; + + +const struct comp *lib_find(const struct lib *lib, const char *name); +bool lib_field_visible(const struct comp *comp, int n); +void lib_render(const struct comp *comp, unsigned unit, const int m[6]); + +void lib_parse(struct lib *lib, const char *file); +void lib_init(struct lib *lib); + +#endif /* !LIB_H */ diff --git a/eeshow/main.c b/eeshow/main.c new file mode 100644 index 0000000..7e02d0e --- /dev/null +++ b/eeshow/main.c @@ -0,0 +1,172 @@ +/* + * main.c - Convert Eeschema schematics to FIG + * + * Written 2016 by Werner Almesberger + * Copyright 2016 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 +#include +#include + +#include "util.h" +#include "fig.h" +#include "cro.h" +#include "diff.h" +#include "gfx.h" +#include "file.h" +#include "lib.h" +#include "sch.h" +#include "main.h" + + +int verbose = 0; + + +static struct gfx_ops const *ops_list[] = { + &fig_ops, + &cro_png_ops, + &cro_pdf_ops, + &diff_ops, +}; + + +void usage(const char *name) +{ + fprintf(stderr, +"usage: %s [-r] [-v ...] [[rev:]file.lib ...] [rev:]file.sch\n" +" %*s-- driver_spec\n" +" %s [-v ...] -C [rev:]file\n" +"\n" +" rev git revision\n" +" -r recurse into sub-sheets\n" +" -v increase verbosity of diagnostic output\n" +" -C 'cat' the file to standard output\n" +"\n" +"FIG driver spec:\n" +" fig [-t template.fig] [var=value ...]\n" +"\n" +" var=value substitute \"\" with \"value\" in template\n" +" -t template.fig merge this file with generated output\n" +"\n" +"Cairo PNG driver spec:\n" +" png [-o output.png] [-s scale]\n" +"\n" +" -o output.png generate PNG output and write to specified file\n" +" -s scale scale by indicated factor (default: 1.0)\n" +"\n" +"Cairo PDF driver spec:\n" +" pdf [-o output.pdf] [-s scale]\n" +"\n" +" see PNG\n" +"\n" +"Diff driver spec:\n" +" diff [-o output.pdf] [-s scale] [file.lib ...] file.sch\n" +"\n" +" see PNG\n" + , name, (int) strlen(name) + 1, "", name); + exit(1); +} + + +int main(int argc, char *const *argv) +{ + struct lib lib; + struct sch_ctx sch_ctx; + bool recurse = 0; + const char *cat = NULL; + char c; + int arg, dashdash; + int gfx_argc; + char **gfx_argv; + const struct gfx_ops **ops = ops_list; + + for (dashdash = 1; dashdash != argc; dashdash++) + if (!strcmp(argv[dashdash], "--")) + break; + + while ((c = getopt(dashdash, argv, "rvC:")) != EOF) + switch (c) { + case 'r': + recurse = 1; + break; + case 'v': + verbose++; + break; + case 'C': + cat = optarg; + break; + default: + usage(*argv); + } + + if (cat) { + if (argc != optind) + usage(*argv); + file_read(cat, file_cat, NULL); + return 0; + } + + if (dashdash - optind < 1) + usage(*argv); + + lib_init(&lib); + for (arg = optind; arg != dashdash - 1; arg++) + lib_parse(&lib, argv[arg]); + + if (dashdash == argc) { + gfx_argc = 1; + gfx_argv = alloc_size(sizeof(const char *) * 2); + gfx_argv[0] = (char *) (*ops)->name; + gfx_argv[1] = NULL; + } else { + gfx_argc = argc - dashdash - 1; + if (!gfx_argc) + usage(*argv); + gfx_argv = alloc_size(sizeof(const char *) * (gfx_argc + 1)); + memcpy(gfx_argv, argv + dashdash + 1, + sizeof(const char *) * (gfx_argc + 1)); + + for (ops = ops_list; ops != ARRAY_END(ops_list); ops++) + if (!strcmp((*ops)->name, *gfx_argv)) + goto found; + fprintf(stderr, "graphics backend \"%s\" not found\n", + *gfx_argv); + exit(1); +found: + ; + } + + optind = 0; /* reset getopt */ + + sch_init(&sch_ctx, recurse); + sch_parse(&sch_ctx, argv[dashdash - 1], &lib); + gfx_init(*ops, gfx_argc, gfx_argv); + if (recurse) { + const struct sheet *sheet; + + if (!gfx_multi_sheet()) { + fprintf(stderr, + "graphics backend only supports single sheet\n"); + exit(1); + } + for (sheet = sch_ctx.sheets; sheet; sheet = sheet->next) { + sch_render(sheet); + if (sheet->next) + gfx_new_sheet(); + } + } else { + sch_render(sch_ctx.sheets); + } + gfx_end(); + + return 0; +} diff --git a/eeshow/main.h b/eeshow/main.h new file mode 100644 index 0000000..4a3bdc1 --- /dev/null +++ b/eeshow/main.h @@ -0,0 +1,31 @@ +/* + * main.h - Convert Eeschema schematics to FIG + * + * Written 2016 by Werner Almesberger + * Copyright 2016 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 MAIN_H +#define MAIN_H + +#include + + +/* + * 0: no progress indications + * 1: reasonable progress indications + * 2: verbose output + * > 2: go wild ! + */ + +extern int verbose; + + +void usage(const char *name); + +#endif /* !MAIN_H */ diff --git a/eeshow/misc.c b/eeshow/misc.c new file mode 100644 index 0000000..5e2401c --- /dev/null +++ b/eeshow/misc.c @@ -0,0 +1,114 @@ +/* + * misc.c - Helper functions for geometry and attributes + * + * Written 2016 by Werner Almesberger + * Copyright 2016 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 +#include + +#include "misc.h" + + +static bool eq(const int m[6], int xx, int xy, int yx, int yy) +{ + return m[1] == xx && m[2] == xy && m[4] == yx && m[5] == yy; +} + + +unsigned matrix_to_angle(const int m[6]) +{ + if (eq(m, 1, 0, 0, -1)) + return 0; + if (eq(m, 0, -1, -1, 0)) + return 90; + if (eq(m, -1, 0, 0, 1)) + return 180; + if (eq(m, 0, 1, 1, 0)) + return 270; + if (eq(m, -1, 0, 0, -1)) + return 0; + if (eq(m, 1, 0, 0, 1)) /* x-flipped */ + return 180; + if (eq(m, 0, 1, -1, 0)) /* x-flipped, 90 deg */ + return 90; + fprintf(stderr, "unrecognized matrix %d %d %d %d\n", + m[1], m[2], m[4], m[5]); + exit(1); +} + + +bool matrix_is_mirrored(const int m[6]) +{ + if (eq(m, 1, 0, 0, -1)) + return 0; + if (eq(m, 0, -1, -1, 0)) + return 0; + if (eq(m, -1, 0, 0, 1)) + return 0; + if (eq(m, 0, 1, 1, 0)) + return 0; + + if (eq(m, -1, 0, 0, -1)) + return 1; + if (eq(m, 1, 0, 0, 1)) + return 1; + if (eq(m, 0, 1, -1, 0)) + return 1; + assert(0); +} + + +int angle_add(int a, int b) +{ + a += b; + while (a < 0) + a += 360; + return a % 360; +} + + + +int rx(int x, int y, int rot) +{ + switch (rot) { + case 0: + return x; + case 90: + return y; + case 180: + return -x; + case 270: + return -y; + default: + assert(0); + + } +} + + +int ry(int x, int y, int rot) +{ + switch (rot) { + case 0: + return y; + case 90: + return -x; + case 180: + return -y; + case 270: + return x; + default: + assert(0); + + } +} diff --git a/eeshow/misc.h b/eeshow/misc.h new file mode 100644 index 0000000..40c15f0 --- /dev/null +++ b/eeshow/misc.h @@ -0,0 +1,51 @@ +/* + * misc.h - Helper functions for geometry and attributes + * + * Written 2016 by Werner Almesberger + * Copyright 2016 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 MISC_H +#define MISC_H + +#include + + +static inline int mxr(int x, int y, const int m[6]) +{ + return x * m[1] + y * m[2]; +} + + +static inline int myr(int x, int y, const int m[6]) +{ + return x * m[4] + y * m[5]; +} + + +static inline int mx(int x, int y, const int m[6]) +{ + return m[0] + mxr(x, y, m); +} + + +static inline int my(int x, int y, const int m[6]) +{ + return m[3] + myr(x, y, m); +} + + +unsigned matrix_to_angle(const int m[6]); +bool matrix_is_mirrored(const int m[6]); +int angle_add(int a, int b); + +int rx(int x, int y, int rot); +int ry(int x, int y, int rot); + +#endif /* !MISC_H */ diff --git a/eeshow/neo900-template.fig b/eeshow/neo900-template.fig new file mode 100644 index 0000000..4371404 --- /dev/null +++ b/eeshow/neo900-template.fig @@ -0,0 +1,15 @@ +#FIG 3.2 Produced by xfig version 3.2.5c +Landscape +Center +Metric +A4 +100.00 +Single +-2 +1200 2 +0 32 #c0c0c0 +2 2 0 1 7 7 250 -1 -1 0.000 0 0 -1 0 0 5 + 225 450 19350 450 19350 13500 225 13500 225 450 +4 2 32 199 -1 18 150 0.0000 4 1890 13980 19305 13455 \001 +4 2 0 50 -1 18 24 0.0000 4 300 1635 19125 13140 \001 +4 0 0 250 -1 22 12 0.0000 4 180 3090 315 13410 CC-BY-SA (c) 2014-2016 Neo900 & GDC\001 diff --git a/eeshow/record.c b/eeshow/record.c new file mode 100644 index 0000000..03cf90c --- /dev/null +++ b/eeshow/record.c @@ -0,0 +1,342 @@ +/* + * record.c - Record graphics operations by layers and replay + * + * Written 2016 by Werner Almesberger + * Copyright 2016 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 <stdlib.h> +#include <limits.h> +#include <math.h> + +#include "util.h" +#include "style.h" +#include "gfx.h" +#include "text.h" +#include "record.h" + + +struct record_obj { + enum ro_type { + ro_line, + ro_rect, + ro_poly, + ro_circ, + ro_arc, + ro_text, + } type; + + int x, y; + int color, fill_color; + union { + struct { + int ex, ey; + } line; + struct { + int ex, ey; + } rect; + struct { + unsigned n; + int *vx, *vy; + } poly; + struct { + int r; + } circ; + struct { + int r; + int sa, ea; + } arc; + struct { + char *s; + unsigned size; + enum text_align align; + int rot; + } text; + } u; + struct record_obj *next; +}; + + +static void bb(struct record *rec, int x, int y) +{ + if (rec->xmin > x) + rec->xmin = x; + if (rec->ymin > y) + rec->ymin = y; + if (rec->xmax < x) + rec->xmax = x; + if (rec->ymax < y) + rec->ymax = y; +} + + +static void bb_rot(struct record *rec, int x, int y, int rot) +{ + double a = -rot / 180.0 * M_PI; + + // @@@ figure this out later +return; + bb(rec, cos(a) * x + sin(a) * y, cos(a) * y - sin(a) * x); +} + + +static struct record_obj *new_obj(struct record *rec, enum ro_type type, + int color, int fill_color, unsigned layer) +{ + struct record_layer **curr_layer; + struct record_layer *new_layer; + struct record_obj *new_obj; + + for (curr_layer = &rec->layers; *curr_layer; + curr_layer= &(*curr_layer)->next) { + if ((*curr_layer)->layer == layer) + goto this_layer; + if ((*curr_layer)->layer < layer) + break; + } + + new_layer = alloc_type(struct record_layer); + new_layer->layer = layer; + new_layer->objs = NULL; + new_layer->next_obj = &new_layer->objs; + new_layer->next = *curr_layer; + *curr_layer = new_layer; + +this_layer: + new_obj = alloc_type(struct record_obj); + new_obj->type = type; + new_obj->color = color; + new_obj->fill_color = fill_color; + new_obj->next = NULL; + + *(*curr_layer)->next_obj = new_obj; + (*curr_layer)->next_obj = &new_obj->next; + + return new_obj; +} + + +void record_line(void *ctx, int sx, int sy, int ex, int ey, + int color, unsigned layer) +{ + struct record *rec = ctx; + struct record_obj *obj = + new_obj(rec, ro_line, color, COLOR_NONE, layer); + + bb(rec, sx, sy); + bb(rec, ex, ey); + + obj->x = sx; + obj->y = sy; + obj->u.line.ex = ex; + obj->u.line.ey = ey; +} + + +void record_rect(void *ctx, int sx, int sy, int ex, int ey, + int color, int fill_color, unsigned layer) +{ + struct record *rec = ctx; + struct record_obj *obj = + new_obj(rec, ro_rect, color, fill_color, layer); + + bb(rec, sx, sy); + bb(rec, ex, ey); + + obj->x = sx; + obj->y = sy; + obj->u.rect.ex = ex; + obj->u.rect.ey = ey; +} + + +void record_poly(void *ctx, + int points, const int x[points], const int y[points], + int color, int fill_color, unsigned layer) +{ + struct record *rec = ctx; + struct record_obj *obj = + new_obj(ctx, ro_poly, color, fill_color, layer); + int i; + unsigned size; + + for (i = 0; i != points; i++) + bb(rec, x[i], y[i]); + + obj->u.poly.n = points; + size = sizeof(int) * points; + obj->u.poly.vx = alloc_size(size); + obj->u.poly.vy = alloc_size(size); + memcpy(obj->u.poly.vx, x, size); + memcpy(obj->u.poly.vy, y, size); +} + + +void record_circ(void *ctx, int x, int y, int r, + int color, int fill_color, unsigned layer) +{ + struct record *rec = ctx; + struct record_obj *obj = + new_obj(ctx, ro_circ, color, fill_color, layer); + + bb(rec, x - r, y - r); + bb(rec, x + r, y + r); + + obj->x = x; + obj->y = y; + obj->u.circ.r = r; +} + + +void record_arc(void *ctx, int x, int y, int r, int sa, int ea, + int color, int fill_color, unsigned layer) +{ + struct record *rec = ctx; + struct record_obj *obj = new_obj(ctx, ro_arc, color, fill_color, layer); + + bb(rec, x - r, y - r); + bb(rec, x + r, y + r); + + obj->x = x; + obj->y = y; + obj->u.arc.r = r; + obj->u.arc.sa = sa; + obj->u.arc.ea = ea; +} + + +void record_text(void *ctx, int x, int y, const char *s, unsigned size, + enum text_align align, int rot, unsigned color, unsigned layer) +{ + struct record *rec = ctx; + struct record_obj *obj = + new_obj(ctx, ro_text, color, COLOR_NONE, layer); + unsigned width = rec->ops->text_width(rec->user, s, size); + + bb_rot(rec, x, y - size, rot); + bb_rot(rec, x + width, y, rot); + + obj->x = x; + obj->y = y; + obj->u.text.s = stralloc(s); + obj->u.text.size = size; + obj->u.text.align = align; + obj->u.text.rot = rot; +} + + +void record_init(struct record *rec, const struct gfx_ops *ops, void *user) +{ + rec->ops = ops; + rec->user = user; + rec->xmin = rec->ymin = INT_MAX; + rec->xmax = rec->ymax = INT_MIN; + rec->layers = NULL; +} + + +void record_wipe(struct record *rec) +{ + rec->layers = NULL; +} + + +void record_replay(const struct record *rec) +{ + const struct gfx_ops *ops = rec->ops; + void *ctx = rec->user; + const struct record_layer *layer; + const struct record_obj *obj; + + for (layer = rec->layers; layer; layer = layer->next) + for (obj = layer->objs; obj; obj = obj->next) + switch (obj->type) { + case ro_line: + ops->line(ctx, obj->x, obj->y, + obj->u.line.ex, obj->u.line.ey, + obj->color, layer->layer); + break; + case ro_rect: + ops->rect(ctx, obj->x, obj->y, + obj->u.rect.ex, obj->u.rect.ey, + obj->color, obj->fill_color, layer->layer); + break; + case ro_poly: + ops->poly(ctx, obj->u.poly.n, + obj->u.poly.vx, obj->u.poly.vy, + obj->color, obj->fill_color, layer->layer); + break; + case ro_circ: + ops->circ(ctx, obj->x, obj->y, + obj->u.circ.r, + obj->color, obj->fill_color, layer->layer); + break; + case ro_arc: + ops->arc(ctx, obj->x, obj->y, obj->u.arc.r, + obj->u.arc.sa, obj->u.arc.ea, + obj->color, obj->fill_color, layer->layer); + break; + case ro_text: + ops->text(ctx, obj->x, obj->y, obj->u.text.s, + obj->u.text.size, obj->u.text.align, + obj->u.text.rot, + obj->color, layer->layer); + break; + default: + abort(); + } +} + + +void record_bbox(const struct record *rec, int *x, int *y, int *w, int *h) +{ + if (x) + *x = rec->xmin; + if (y) + *y = rec->ymin; + if (w) + *w = rec->xmax - rec->xmin + 1; + if (h) + *h = rec->ymax - rec->ymin + 1; +} + + +static void record_obj_destroy(struct record_obj *obj) +{ + switch (obj->type) { + case ro_poly: + free(obj->u.poly.vx); + free(obj->u.poly.vy); + break; + case ro_text: + free(obj->u.text.s); + break; + default: + break; + } + free(obj); +} + + +void record_destroy(struct record *rec) +{ + struct record_layer *next_layer; + struct record_obj *next_obj; + + while (rec->layers) { + next_layer = rec->layers->next; + while (rec->layers->objs) { + next_obj = rec->layers->objs->next; + record_obj_destroy(rec->layers->objs); + rec->layers->objs = next_obj; + } + free(rec->layers); + rec->layers = next_layer; + } +} diff --git a/eeshow/record.h b/eeshow/record.h new file mode 100644 index 0000000..3ff26f8 --- /dev/null +++ b/eeshow/record.h @@ -0,0 +1,58 @@ +/* + * record.h - Record graphics operations by layers and replay + * + * Written 2016 by Werner Almesberger + * Copyright 2016 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 RECORD_H +#define RECORD_H + +#include "gfx.h" + + +struct record_obj; + +struct record_layer { + unsigned layer; + struct record_obj *objs; + struct record_obj **next_obj; + struct record_layer *next; +}; + +struct record { + const struct gfx_ops *ops; + void *user; + int xmin, xmax; + int ymin, ymax; + struct record_layer *layers; +}; + + +void record_line(void *ctx, int sx, int sy, int ex, int ey, + int color, unsigned layer); +void record_rect(void *ctx, int sx, int sy, int ex, int ey, + int color, int fill_color, unsigned layer); +void record_poly(void *ctx, + int points, const int x[points], const int y[points], + int color, int fill_color, unsigned layer); +void record_circ(void *ctx, int x, int y, int r, + int color, int fill_color, unsigned layer); +void record_arc(void *ctx, int x, int y, int r, int sa, int ea, + int color, int fill_color, unsigned layer); +void record_text(void *ctx, int x, int y, const char *s, unsigned size, + enum text_align align, int rot, unsigned color, unsigned layer); + +void record_init(struct record *rec, const struct gfx_ops *ops, void *user); +void record_wipe(struct record *rec); +void record_replay(const struct record *rec); +void record_bbox(const struct record *rec, int *x, int *y, int *w, int *h); +void record_destroy(struct record *rec); + +#endif /* !RECORD_H */ diff --git a/eeshow/sch-parse.c b/eeshow/sch-parse.c new file mode 100644 index 0000000..e47ac49 --- /dev/null +++ b/eeshow/sch-parse.c @@ -0,0 +1,562 @@ +/* + * sch-parse.c - Parse Eeschema .sch file + * + * Written 2016 by Werner Almesberger + * Copyright 2016 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 <stddef.h> +#include <stdbool.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <assert.h> + +#include "util.h" +#include "dwg.h" +#include "file.h" +#include "lib.h" +#include "sch.h" + + +/* ----- (Global) Labels --------------------------------------------------- */ + + +static enum dwg_shape do_decode_shape(const char *s) +{ + if (!strcmp(s, "UnSpc")) + return dwg_unspec; + if (!strcmp(s, "Input")) + return dwg_in; + if (!strcmp(s, "Output")) + return dwg_out; + if (!strcmp(s, "3State")) + return dwg_tri; + if (!strcmp(s, "BiDi")) + return dwg_bidir; + fprintf(stderr, "unknown shape: \"%s\"\n", s); + exit(1); +} + + +static enum dwg_shape decode_shape(const char *s) +{ + enum dwg_shape res; + + res = do_decode_shape(s); + free((void *) s); + return res; +} + + +/* ----- Component fields -------------------------------------------------- */ + + +void decode_alignment(struct text *txt, char hor, char vert) +{ + switch (hor) { + case 'L': + txt->hor = text_min; + break; + case 'C': + txt->hor = text_mid; + break; + case 'R': + txt->hor = text_max; + break; + default: + assert(0); + } + + switch (vert) { + case 'B': + txt->vert = text_min; + break; + case 'C': + txt->vert = text_mid; + break; + case 'T': + txt->vert = text_max; + break; + default: + assert(0); + } +} + + +static bool parse_field(struct sch_ctx *ctx, const char *line) +{ + struct sch_comp *comp = &ctx->obj.u.comp; + int n; + unsigned flags; + char hv, hor, vert, italic, bold; + struct comp_field *field; + struct text *txt; + + field = alloc_type(struct comp_field); + txt = &field->txt; + + if (sscanf(line, "F %d \"\" %c %d %d %u %u %c %c%c%c", + &n, &hv, &txt->x, &txt->y, &txt->size, &flags, &hor, &vert, + &italic, &bold) == 10) { + free(field); + return 1; + } + if (sscanf(line, "F %d \"%m[^\"]\" %c %d %d %u %u %c %c%c%c", + &n, &txt->s, &hv, &txt->x, &txt->y, &txt->size, &flags, + &hor, &vert, &italic, &bold) != 11) + return 0; + + if (flags || !lib_field_visible(comp->comp, n)) { + free(field); + return 1; + } + + if (n == 0 && comp->comp->units > 1) { + int len = strlen(txt->s); + char *s; + + s = realloc((void *) txt->s, len + 3); + if (!s) { + perror("realloc"); + exit(1); + } + if (comp->unit <= 26) + sprintf(s + len, "%c", 'A' + comp->unit - 1); + else + sprintf(s + len, "%c%c", + 'A' + (comp->unit - 1) / 26 - 1, + 'A' + (comp->unit - 1) % 26); + txt->s = s; + } + + field->next = comp->fields; + comp->fields = field; + + switch (hv) { + case 'H': + txt->rot = 0; + break; + case 'V': + txt->rot = 90; + break; + default: + assert(0); + } + + decode_alignment(txt, hor, vert); + + // @@@ decode font + + return 1; +} + + +/* ----- Sheet field ------------------------------------------------------- */ + + +static enum dwg_shape decode_form(char form) +{ + switch (form) { + case 'O': + return dwg_in; + case 'I': + return dwg_out; + case 'B': + /* fall through */ + case 'T': + return dwg_bidir; + case 'U': + return dwg_unspec; + default: + fprintf(stderr, "unknown form: \"%c\"\n", form); + exit(1); + } +} + + +static int decode_side(char side) +{ + switch (side) { + case 'L': + return 2; /* left */ + case 'B': + return 1; /* up */ + case 'R': + return 0; /* right */ + case 'T': + return 3; /* down */ + default: + fprintf(stderr, "unknown side: \"%c\"\n", side); + exit(1); + } +} + + +static bool parse_hsheet_field(struct sch_ctx *ctx, const char *line) +{ + struct sch_sheet *sheet = &ctx->obj.u.sheet; + char *s; + char form, side; + unsigned n, dim; + struct sheet_field *field; + + if (sscanf(line, "F%d \"%m[^\"]\" %u", &n, &s, &dim) == 3) { + switch (n) { + case 0: + sheet->sheet = s; + sheet->sheet_dim = dim; + return 1; + case 1: + sheet->file = s; + sheet->file_dim = dim; + return 1; + default: + assert(0); + } + } + + field = alloc_type(struct sheet_field); + if (sscanf(line, "F%d \"%m[^\"]\" %c %c %d %d %u", + &n, &field->s, &form, &side, &field->x, &field->y, &field->dim) + != 7) { + free(field); + return 0; + } + assert(n >= 2); + + if (side == 'B' || side == 'T') { + /* + * This is beautiful: since there is no indication for rotation + * on the hsheet, or the sheet or file fields, we need to look + * at whether the imported sheet pins go left or right (no + * rotation) or whether they go top or bottom (rotation). + * + * A sheet with no imported pins lacks these hints, and is + * therefore always assumed to be without rotation. + * + * Eeschema is careful to be consistent, and does not allow + * sheets with no imported pins to be rotated. Even better, it + * flips rotated sheets where the last imported pin is deleted + * back. + */ + sheet->rotated = 1; + } + field->shape = decode_form(form); + field->side = decode_side(side); + + field->next = sheet->fields; + sheet->fields = field; + + return 1; +} + + +/* ----- Schematics parser ------------------------------------------------- */ + + +static void submit_obj(struct sch_ctx *ctx, enum sch_obj_type type) +{ + struct sch_obj *obj; + + obj = alloc_type(struct sch_obj); + *obj = ctx->obj; + obj->type = type; + obj->next = NULL; + + *ctx->next_obj = obj; + ctx->next_obj = &obj->next; +} + + +static struct sheet *new_sheet(struct sch_ctx *ctx) +{ + struct sheet *sheet; + + sheet = alloc_type(struct sheet); + sheet->objs = NULL; + sheet->next = NULL; + + ctx->next_obj = &sheet->objs; + + *ctx->next_sheet = sheet; + ctx->next_sheet = &sheet->next; + + return sheet; +} + + +static bool parse_line(void *user, const char *line); + + +static void recurse_sheet(struct sch_ctx *ctx) +{ + struct sch_obj **saved_next_obj = ctx->next_obj; + const char *parent = ctx->file->name; + char *tmp = NULL; + struct sch_file dsc = { + .name = ctx->obj.u.sheet.file, + .lineno = 0, + .parent = ctx->file, + }; + + /* @@@ clean this up */ + + if (access(dsc.name, R_OK)) { + const char *slash; + + slash = strrchr(parent, '/'); + if (slash) { + unsigned len = slash + 1 - parent; + + tmp = alloc_size(len + strlen(dsc.name) + 1); + memcpy(tmp, parent, len); + strcpy(tmp + len, dsc.name); + dsc.name = tmp; + } + } + + new_sheet(ctx); + ctx->file = &dsc; + ctx->state = sch_descr; + file_read(dsc.name, parse_line, ctx); + ctx->file = dsc.parent; + ctx->next_obj = saved_next_obj; + free(tmp); +} + + +static bool parse_line(void *user, const char *line) +{ + struct sch_ctx *ctx = user; + struct sch_obj *obj = &ctx->obj; + int n = 0; + char *s; + + ctx->file->lineno++; + + switch (ctx->state) { + case sch_basic: + if (sscanf(line, "$Comp%n", &n) == 0 && n) { + ctx->state = sch_comp; + obj->u.comp.fields = NULL; + return 1; + } + if (sscanf(line, "$Sheet%n", &n) == 0 && n) { + ctx->state = sch_sheet; + obj->u.sheet.sheet = NULL; + obj->u.sheet.file = NULL; + obj->u.sheet.rotated = 0; + obj->u.sheet.fields = NULL; + return 1; + } + + /* Text */ + + struct sch_text *text = &obj->u.text; + + if (sscanf(line, "Text Notes %d %d %d %d", + &obj->x, &obj->y, &text->dir, &text->dim) == 4) { + ctx->state = sch_text; + obj->u.text.fn = dwg_text; + return 1; + } + if (sscanf(line, "Text GLabel %d %d %d %d %ms", + &obj->x, &obj->y, &text->dir, &text->dim, &s) == 5) { + ctx->state = sch_text; + obj->u.text.fn = dwg_glabel; + obj->u.text.shape = decode_shape(s); + return 1; + } + if (sscanf(line, "Text HLabel %d %d %d %d %ms", + &obj->x, &obj->y, &text->dir, &text->dim, &s) == 5) { + ctx->state = sch_text; + obj->u.text.fn = dwg_hlabel; + obj->u.text.shape = decode_shape(s); + return 1; + } + if (sscanf(line, "Text Label %d %d %d %d", + &obj->x, &obj->y, &text->dir, &text->dim) == 4) { + ctx->state = sch_text; + obj->u.text.fn = dwg_label; + return 1; + } + + /* Connection */ + + if (sscanf(line, "Connection ~ %d %d", &obj->x, &obj->y) == 2) { + submit_obj(ctx, sch_obj_junction); + return 1; + } + + /* NoConn */ + + if (sscanf(line, "NoConn ~ %d %d", &obj->x, &obj->y) == 2) { + submit_obj(ctx, sch_obj_noconn); + return 1; + } + + /* Wire */ + + if (sscanf(line, "Wire Wire Line%n", &n) == 0 && n) { + ctx->state = sch_wire; + obj->u.wire.fn = dwg_wire; + return 1; + } + if (sscanf(line, "Wire Bus Line%n", &n) == 0 && n) { + ctx->state = sch_wire; + obj->u.wire.fn = dwg_bus; + return 1; + } + if (sscanf(line, "Wire Notes Line%n", &n) == 0 && n) { + ctx->state = sch_wire; + obj->u.wire.fn = dwg_line; + return 1; + } + + /* Entry */ + + /* + * Documentation mentions the following additional variants: + * + * - Entry Wire Line equivalent: + * Wire Wire Bus + * Entry Wire Bus + * + * - Entry Bus Bus equivalent: + * Wire Bus Bus + */ + if (sscanf(line, "Entry Wire Line%n", &n) == 0 && n) { + ctx->state = sch_wire; + obj->u.wire.fn = dwg_wire; + return 1; + } + if (sscanf(line, "Entry Bus Bus%n", &n) == 0 && n) { + ctx->state = sch_wire; + obj->u.wire.fn = dwg_bus; + return 1; + } + + /* EndSCHEMATC */ + + if (sscanf(line, "$EndSCHEMATC%n", &n) == 0 && n) + return 0; + break; + case sch_descr: + if (sscanf(line, "$EndDescr%n", &n) || !n) + return 1; + ctx->state = sch_basic; + return 1; + case sch_comp: + if (sscanf(line, "$EndComp%n", &n) == 0 && n) { + ctx->state = sch_basic; + submit_obj(ctx, sch_obj_comp); + return 1; + } + if (sscanf(line, "L %ms", &s) == 1) { + obj->u.comp.comp = lib_find(ctx->lib, s); + free(s); + return 1; + } + if (sscanf(line, "U %u", &obj->u.comp.unit) == 1) + return 1; + if (sscanf(line, "P %d %d", &obj->x, &obj->y) == 2) + return 1; + if (parse_field(ctx, line)) + return 1; + if (sscanf(line, "AR %n", &n) == 0 && n) + return 1; /* @@@ what is "AR" ? */ + n = sscanf(line, " %d %d %d %d", + obj->u.comp.m + 1, obj->u.comp.m + 2, + obj->u.comp.m + 4, obj->u.comp.m + 5); + if (n == 3) + return 1; + if (n == 4) { + obj->u.comp.m[0] = obj->x; + obj->u.comp.m[3] = obj->y; + return 1; + } + break; + case sch_sheet: + if (sscanf(line, "$EndSheet%n", &n) == 0 && n) { + submit_obj(ctx, sch_obj_sheet); + if (ctx->recurse) + recurse_sheet(ctx); + ctx->state = sch_basic; + return 1; + } + if (sscanf(line, "S %d %d %u %u", + &obj->x, &obj->y, &obj->u.sheet.w, &obj->u.sheet.h) == 4) + return 1; + if (sscanf(line, "U %*x%n", &n) == 0 && n) + return 1; + if (parse_hsheet_field(ctx, line)) + return 1; + break; + case sch_text: + ctx->state = sch_basic; + { + const char *from; + char *to; + + s = alloc_size(strlen(line) + 1); + from = line; + to = s; + while (*from) { + if (from[0] != '\\' || from[1] != 'n') { + *to++ = *from++; + continue; + } + *to++ = '\n'; + from += 2; + } + *to = 0; + obj->u.text.s = s; + submit_obj(ctx, sch_obj_text); + } + return 1; + case sch_wire: + if (sscanf(line, "%d %d %d %d", &obj->x, &obj->y, + &obj->u.wire.ex, &obj->u.wire.ey) != 4) + break; + submit_obj(ctx, sch_obj_wire); + ctx->state = sch_basic; + return 1; + default: + abort(); + } + fprintf(stderr, "%s:%u: cannot parse\n\"%s\"\n", + ctx->file->name, ctx->file->lineno, line); + exit(1); +} + + +void sch_parse(struct sch_ctx *ctx, const char *file, const struct lib *lib) +{ + struct sch_file dsc = { + .name = file, + .lineno = 0, + .parent = NULL, + }; + + ctx->file = &dsc; + ctx->lib = lib; + file_read(file, parse_line, ctx); +} + + +void sch_init(struct sch_ctx *ctx, bool recurse) +{ + ctx->state = sch_descr; + ctx->recurse = recurse; + ctx->sheets = NULL; + ctx->next_sheet = &ctx->sheets; + new_sheet(ctx); +} diff --git a/eeshow/sch-render.c b/eeshow/sch-render.c new file mode 100644 index 0000000..b1b9311 --- /dev/null +++ b/eeshow/sch-render.c @@ -0,0 +1,174 @@ +/* + * sch-render.c - Render schematics + * + * Written 2016 by Werner Almesberger + * Copyright 2016 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. + */ + + +#define _GNU_SOURCE /* for asprintf */ +#include <stdio.h> +#include <assert.h> + +#include "misc.h" +#include "style.h" +#include "gfx.h" +#include "dwg.h" +#include "lib.h" +#include "sch.h" + + +/* ----- Rendering --------------------------------------------------------- */ + + +static void dump_field(const struct comp_field *field, const int m[6]) +{ + struct text txt = field->txt; + int dx, dy; + + dx = txt.x - m[0]; + dy = txt.y - m[3]; + txt.x = mx(dx, dy, m); + txt.y = my(dx, dy, m); + + text_rot(&txt, matrix_to_angle(m)); + + switch (txt.rot) { + case 180: + text_rot(&txt, 180); + txt.hor = text_flip(txt.hor); + txt.vert = text_flip(txt.vert); + break; + case 270: + text_rot(&txt, 180); + txt.vert = text_flip(txt.vert); + txt.hor = text_flip(txt.hor); + break; + default: + break; + } + + if (matrix_is_mirrored(m)) { + if ((txt.rot % 180) == 0) + txt.hor = text_flip(txt.hor); + else + txt.vert = text_flip(txt.vert); + } + + text_fig(&txt, COLOR_FIELD, LAYER_FIELD); +} + + +static void do_hsheet_text(const struct sch_obj *obj, + const struct sch_sheet *sheet) +{ + char *s; + + assert(sheet->sheet && sheet->file); + + struct text sheet_txt = { + .size = sheet->sheet_dim, + .x = obj->x, + .y = obj->y, + .rot = 0, + .hor = text_min, + .vert = text_min, + }; + if (asprintf(&s, "Sheet: %s", sheet->sheet)) {} + sheet_txt.s = s; /* work around "const" mismatch */ + + struct text file_txt = { + .size = sheet->file_dim, + .x = obj->x, + .y = obj->y, + .rot = 0, + .hor = text_min, + .vert = text_max, + }; + if (asprintf(&s, "File: %s", sheet->file)) {} + file_txt.s = s; /* work around "const" mismatch */ + + if (sheet->rotated) { + sheet_txt.rot = file_txt.rot = 90; + sheet_txt.x -= HSHEET_FIELD_OFFSET; + sheet_txt.y += sheet->h; + file_txt.x += sheet->w + HSHEET_FIELD_OFFSET; + file_txt.y += sheet->h; + } else { + sheet_txt.y -= HSHEET_FIELD_OFFSET; + file_txt.y += sheet->h + HSHEET_FIELD_OFFSET; + } + + text_fig(&sheet_txt, COLOR_HSHEET_SHEET, LAYER_HSHEET_FIELD); + text_fig(&file_txt, COLOR_HSHEET_FILE, LAYER_HSHEET_FIELD); + +// free((void *) ctx->sheet); +// free((void *) ctx->file); +} + + +static void render_sheet(const struct sch_obj *obj, + const struct sch_sheet *sheet) +{ + const struct sheet_field *field; + + gfx_rect(obj->x, obj->y, obj->x + sheet->w, obj->y + sheet->h, + COLOR_HSHEET_BOX, COLOR_NONE, LAYER_HSHEET_BOX); + do_hsheet_text(obj, sheet); + + for (field = sheet->fields; field; field = field->next) + dwg_hlabel(obj->x, obj->y, field->s, + field->side, field->dim, + field->shape); + // free(field->s) +} + + +void sch_render(const struct sheet *sheet) +{ + const struct sch_obj *obj; + + for (obj = sheet->objs; obj; obj = obj->next) + switch (obj->type) { + case sch_obj_wire: + { + const struct sch_wire *wire = &obj->u.wire; + + wire->fn(obj->x, obj->y, wire->ex, wire->ey); + } + break; + case sch_obj_junction: + dwg_junction(obj->x, obj->y); + break; + case sch_obj_noconn: + dwg_noconn(obj->x, obj->y); + break; + case sch_obj_text: + { + const struct sch_text *text = &obj->u.text; + + text->fn(obj->x, obj->y, text->s, text->dir, + text->dim, text->shape); + } + break; + case sch_obj_comp: + { + const struct sch_comp *comp = &obj->u.comp; + const struct comp_field *field; + + lib_render(comp->comp, comp->unit, comp->m); + for (field = comp->fields; field; + field = field->next) + dump_field(field, comp->m); + } + break; + case sch_obj_sheet: + render_sheet(obj, &obj->u.sheet); + break; + } +} diff --git a/eeshow/sch.h b/eeshow/sch.h new file mode 100644 index 0000000..b1e2d70 --- /dev/null +++ b/eeshow/sch.h @@ -0,0 +1,123 @@ +/* + * sch.h - Parse Eeschema .sch file + * + * Written 2016 by Werner Almesberger + * Copyright 2016 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 SCH_H +#define SCH_H + +#include <stdbool.h> + +#include "dwg.h" +#include "text.h" +#include "lib.h" + + +enum sch_state { + sch_basic, /* basic state */ + sch_descr, /* prelude and description */ + sch_comp, /* component */ + sch_sheet, /* sub-sheet */ + sch_text, /* text or label */ + sch_wire, /* wire */ +}; + +struct sch_obj { + enum sch_obj_type { + sch_obj_wire, + sch_obj_junction, + sch_obj_noconn, + sch_obj_text, + sch_obj_comp, + sch_obj_sheet, + } type; + + int x, y; + + union { + struct sch_wire { + void (*fn)(int sx, int sy, int ex, int ey); + int ex, ey; + } wire; + struct sch_text { + void (*fn)(int x, int y, const char *s, + int dir, int dim, enum dwg_shape shape); + const char *s; + int dir; /* orientation */ + int dim; /* dimension */ + enum dwg_shape shape; + } text; + struct sch_comp { + const struct comp *comp; /* current component */ + unsigned unit; /* unit of current component */ + struct comp_field { + struct text txt; + struct comp_field *next; + } *fields; + int m[6]; + } comp; + struct sch_sheet { + unsigned h, w; + const char *sheet; + unsigned sheet_dim; + const char *file; + unsigned file_dim; + bool rotated; + + struct sheet_field { + char *s; + int x, y; + unsigned dim; + enum dwg_shape shape; + unsigned side; + struct sheet_field *next; + } *fields; + } sheet; + } u; + + struct sch_obj *next; +}; + +struct sheet { + struct sch_obj *objs; + struct sheet *next; +}; + +struct sch_file { + const char *name; + int lineno; + struct sch_file *parent; +}; + +struct sch_ctx { + enum sch_state state; + + bool recurse; + + struct sch_obj obj; + struct sch_obj **next_obj; + + struct sheet *sheets; + struct sheet **next_sheet; + + const struct lib *lib; + + struct sch_file *file; +}; + + +void decode_alignment(struct text *txt, char hor, char vert); + +void sch_render(const struct sheet *sheet); +void sch_parse(struct sch_ctx *ctx, const char *file, const struct lib *lib); +void sch_init(struct sch_ctx *ctx, bool recurse); + +#endif /* !SCH_H */ diff --git a/eeshow/sch2pdf b/eeshow/sch2pdf new file mode 100755 index 0000000..8e3c275 --- /dev/null +++ b/eeshow/sch2pdf @@ -0,0 +1,92 @@ +#!/bin/bash +# +# sch2pdf - Generate PDF from schematics, using eeshow +# +# Written 2016 by Werner Almesberger +# Copyright 2016 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. +# + +# +# Known bugs: +# - expects first sheet to be index page +# - only renders sub-sheets +# - has all the limitations of eeshow (see TODO) +# + + +usage() +{ + cat <<EOF 1>&2 +usage: $0 [-n first_num] [-o output.pdf] [-q] [-t template.fig ] + file.lib ... file.sch +EOF + exit 1 +} + + +out=out.pdf +quiet=false +template= +num=1 +while [ "$1" ]; do + case "$1" in + -n) num=$2 + shift 2;; + -o) out=$2 + shift 2;; + -q) quiet=true + shift;; + -t) template="-t $2" + shift 2;; + -*) usage;; + *) break;; + esac +done + +[ "$1" ] || usage + +libs= +while [ "$2" ]; do + libs="$libs $1" + shift +done + +./eeshow $libs "$1" \ + -- fig $template "TITLE=`basename \"$1\" .sch`" NUMBER=$num | + fig2dev -L pdf >"$out" + +sheet=false +while read line; do + if ! $sheet; then + [ "${line#\$Sheet}" != "$line" ] && sheet=true + continue + else + if [ "${line#\$EndSheet}" != "$line" ]; then + sheet=false + continue + fi + fi + + if [ "${line#F0 \"}" != "$line" ]; then + name=${line#F0 \"} + name=${name%%\"*} + fi + [ "${line#F1 \"}" = "$line" ] && continue + file=${line#F1 \"} + file=${file%%\"*} + + num=`expr $num + 1` + + $quiet || echo "$file" 1>&2 + ./eeshow $libs `dirname "$1"`/$file \ + -- fig $template "TITLE=$name" NUMBER=$num | + fig2dev -L pdf >_tmp.pdf + pdfunite "$out" _tmp.pdf _tmp2.pdf + mv _tmp2.pdf "$out" +done <"$1" +exit diff --git a/eeshow/style.c b/eeshow/style.c new file mode 100644 index 0000000..7f86798 --- /dev/null +++ b/eeshow/style.c @@ -0,0 +1,31 @@ +/* + * style.c - Drawing style + * + * Written 2016 by Werner Almesberger + * Copyright 2016 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 <stdint.h> + +#include "style.h" + + +uint32_t color_rgb[] = { + [COLOR_BLACK] = 0x000000, + [COLOR_BLUE] = 0x0000ff, + [COLOR_YELLOW] = 0xffff00, + [COLOR_WHITE] = 0xffffff, + [COLOR_GREEN4] = 0x009000, + [COLOR_CYAN4] = 0x009090, + [COLOR_CYAN3] = 0x00b0b0, + [COLOR_RED4] = 0x900000, + [COLOR_RED3] = 0xb00000, + [COLOR_MAGENTA4] = 0x900090, + [COLOR_BROWN2] = 0xc06000, + [COLOR_DARK_YELLOW] = 0x848400, +}; diff --git a/eeshow/style.h b/eeshow/style.h new file mode 100644 index 0000000..95e5e39 --- /dev/null +++ b/eeshow/style.h @@ -0,0 +1,91 @@ +/* + * style.h - Drawing style + * + * Written 2016 by Werner Almesberger + * Copyright 2016 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 STYLE_H +#define STYLE_H + +#include <stdint.h> + + +/* FIG colors */ + +#define COLOR_NONE -1 +#define COLOR_BLACK 0 +#define COLOR_BLUE 1 +#define COLOR_YELLOW 6 +#define COLOR_WHITE 7 +#define COLOR_GREEN4 12 +#define COLOR_CYAN4 15 +#define COLOR_CYAN3 16 +#define COLOR_RED4 18 +#define COLOR_RED3 19 +#define COLOR_MAGENTA4 21 +#define COLOR_BROWN2 26 + +#define COLOR_DARK_YELLOW 32 /* user-defined */ + +#define COLOR_COMP_DWG COLOR_RED4 +#define COLOR_COMP_DWG_BG COLOR_YELLOW +#define COLOR_SHEET_DWG COLOR_BLUE +#define COLOR_TEXT COLOR_BLUE +#define COLOR_WIRE COLOR_GREEN4 +#define COLOR_BUS COLOR_BLUE +#define COLOR_NOCONN COLOR_BLUE +#define COLOR_GLABEL COLOR_RED4 +#define COLOR_HLABEL COLOR_DARK_YELLOW +#define COLOR_HSHEET_BOX COLOR_MAGENTA4 +#define COLOR_HSHEET_SHEET COLOR_FIELD +#define COLOR_HSHEET_FILE COLOR_HLABEL +#define COLOR_LABEL COLOR_BLACK +#define COLOR_FIELD COLOR_CYAN4 +#define COLOR_PIN_NAME COLOR_FIELD +#define COLOR_PIN_NUMBER COLOR_RED4 + +#define FONT_HELVETICA_BOLD 18 + +#define LAYER_GLABEL 20 +#define LAYER_HLABEL LAYER_GLABEL +#define LAYER_LABEL LAYER_GLABEL +#define LAYER_TEXT 30 +#define LAYER_NOCONN 40 +#define LAYER_WIRES 50 +#define LAYER_BUSSES LAYER_WIRES +#define LAYER_FIELD 60 +#define LAYER_PIN_NAME LAYER_FIELD +#define LAYER_PIN_NUMBER LAYER_FIELD +#define LAYER_HSHEET_FIELD LAYER_FIELD +#define LAYER_HSHEET_BOX 70 +#define LAYER_LINES 100 +#define LAYER_COMP_DWG 120 +#define LAYER_COMP_DWG_BG 200 + +#define WIDTH_WIRE 2 +#define WIDTH_BUS WIDTH_WIRE +#define WIDTH_LINE 2 +#define WIDTH_COMP_DWG 2 + +#define JUNCTION_R 30 + +#define NOCONN_LEN 25 + +#define LABEL_OFFSET 15 // eeschema has more like 10 +#define GLABEL_OFFSET 20 +#define HLABEL_OFFSET_F 0.4 // * text size +#define PIN_NUM_OFFSET 15 // eeschema has more like 10 +#define HSHEET_FIELD_OFFSET 10 + +#define NEWLINE_SKIP 1.4 // * text size + + +extern uint32_t color_rgb[]; + +#endif /* !STYLE_H */ diff --git a/eeshow/test.lib b/eeshow/test.lib new file mode 100644 index 0000000..7714415 --- /dev/null +++ b/eeshow/test.lib @@ -0,0 +1,62 @@ +EESchema-LIBRARY Version 2.3 +#encoding utf-8 +# +# TEST +# +DEF TEST U 0 40 Y Y 1 F N +F0 "U" -475 675 60 H V C CNN +F1 "TEST" -425 -675 60 H V C CNN +F2 "" 150 200 60 H I C CNN +F3 "" 150 200 60 H I C CNN +DRAW +T 900 1050 -550 100 0 0 0 BLT Normal 1 L T +T 0 900 450 100 0 0 0 NCC Normal 0 C C +T 900 925 -550 100 0 0 0 NCC Normal 0 C C +T 0 900 550 100 0 0 0 NLB Normal 0 L B +T 900 1275 -550 100 0 0 0 NRB Normal 0 R B +T 900 800 -550 100 0 0 0 NRC Normal 0 R C +T 0 900 350 100 0 0 0 NRT Normal 0 R T +P 2 0 0 0 750 -550 1325 -550 N +P 2 0 0 0 800 -425 800 -650 N +P 2 0 0 0 925 -425 925 -650 N +P 2 0 0 0 1050 -425 1050 -650 N +P 2 0 0 0 1275 -425 1275 -650 N +A 975 -50 150 1800 -900 0 1 0 N 825 -50 975 -200 +A 975 50 150 1800 900 0 1 0 N 825 50 975 200 +A 1075 -50 150 -900 0 0 1 0 N 1075 -200 1225 -50 +A 1075 50 150 900 0 0 1 0 N 1075 200 1225 50 +C 1025 0 150 0 1 0 N +S -550 600 550 -600 0 1 0 N +P 2 0 1 0 -100 -100 100 100 N +P 2 0 1 0 -100 100 100 -100 N +P 2 0 1 0 825 350 1150 350 N +P 2 0 1 0 825 450 1150 450 N +P 2 0 1 0 825 550 1150 550 N +P 2 0 1 0 900 600 900 275 N +P 3 0 1 0 250 550 450 550 450 400 N +P 10 0 1 0 775 0 775 200 825 250 1225 250 1275 200 1275 -200 1225 -250 825 -250 775 -200 775 0 N +X R_IN 1 -750 500 200 R 50 50 1 1 I +X R_OUT 2 -750 400 200 R 50 50 1 1 O +X R_BIDIR 3 -750 300 200 R 50 50 1 1 O +X R_TRI 4 -750 200 200 R 50 50 1 1 T +X R_PASS 5 -750 100 200 R 50 50 1 1 P +X R_UNSPEC 6 -750 0 200 R 50 50 1 1 U +X R_PIN 7 -750 -100 200 R 50 50 1 1 W +X R_POUT 8 -750 -200 200 R 50 50 1 1 w +X R_OC 9 -750 -300 200 R 50 50 1 1 w +X R_OE 10 -750 -400 200 R 50 50 1 1 w +X D_CLOW 20 0 800 200 D 50 50 1 1 I CL +X R_NC 11 -750 -500 200 R 50 50 1 1 N +X D_ILOW 21 -100 800 200 D 50 50 1 1 I L +X U_LINE 12 -50 -800 200 U 50 50 1 1 I +X U_INV 13 50 -800 200 U 50 50 1 1 I I +X U_CLK 14 150 -800 200 U 50 50 1 1 I C +X U_INVCLK 15 250 -800 200 U 50 50 1 1 I IC +X LEFT 16 750 -325 200 L 50 50 1 1 I +X D_NONLOGIC 17 300 800 200 D 50 50 1 1 I X +X D_FALLING 18 200 800 200 D 50 50 1 1 I F +X D_OLOW 19 100 800 200 D 50 50 1 1 I V +ENDDRAW +ENDDEF +# +#End Library diff --git a/eeshow/test.pro b/eeshow/test.pro new file mode 100644 index 0000000..4695c8c --- /dev/null +++ b/eeshow/test.pro @@ -0,0 +1,34 @@ +update=22/05/2015 07:44:53 +version=1 +last_client=kicad +[general] +version=1 +RootSch= +BoardNm= +[pcbnew] +version=1 +LastNetListRead= +UseCmpFile=1 +PadDrill=0.600000000000 +PadDrillOvalY=0.600000000000 +PadSizeH=1.500000000000 +PadSizeV=1.500000000000 +PcbTextSizeV=1.500000000000 +PcbTextSizeH=1.500000000000 +PcbTextThickness=0.300000000000 +ModuleTextSizeV=1.000000000000 +ModuleTextSizeH=1.000000000000 +ModuleTextSizeThickness=0.150000000000 +SolderMaskClearance=0.000000000000 +SolderMaskMinWidth=0.000000000000 +DrawSegmentWidth=0.200000000000 +BoardOutlineThickness=0.100000000000 +ModuleOutlineThickness=0.150000000000 +[cvpcb] +version=1 +NetIExt=net +[eeschema] +version=1 +LibDir= +[eeschema/libraries] +LibName1=test diff --git a/eeshow/test.sch b/eeshow/test.sch new file mode 100644 index 0000000..11c5613 --- /dev/null +++ b/eeshow/test.sch @@ -0,0 +1,372 @@ +EESchema Schematic File Version 2 +LIBS:test +LIBS:test-cache +EELAYER 25 0 +EELAYER END +$Descr A4 11693 8268 +encoding utf-8 +Sheet 1 3 +Title "" +Date "" +Rev "" +Comp "" +Comment1 "" +Comment2 "" +Comment3 "" +Comment4 "" +$EndDescr +Text Notes 1450 1300 0 100 ~ 0 +Text, Right, Normal, 100 mil +Wire Notes Line + 1400 1300 1250 1300 +Wire Notes Line + 1450 1350 1450 1500 +Text Notes 3750 1500 2 100 ~ 20 +Text, Left, Bold, 100 mil +Wire Notes Line + 3800 1500 3950 1500 +Wire Notes Line + 3750 1550 3750 1700 +Text Notes 1050 1550 1 100 Italic 0 +Up, Italic +Wire Notes Line + 1100 1550 1250 1550 +Wire Notes Line + 1050 1600 1050 1750 +Text Notes 4300 700 3 100 Italic 20 +Down, Bold italic +Wire Notes Line + 4350 700 4500 700 +Wire Notes Line + 4300 650 4300 500 +Wire Wire Line + 4800 1100 5300 1100 +Wire Wire Line + 5050 850 5050 1350 +Wire Wire Line + 5550 850 5550 1100 +Wire Wire Line + 5550 1100 5550 1350 +Wire Wire Line + 6050 1100 5550 1100 +Connection ~ 5550 1100 +Wire Bus Line + 6750 600 6750 650 +Wire Bus Line + 6750 650 6750 800 +Wire Bus Line + 6750 800 6750 900 +Wire Bus Line + 6750 900 6750 1000 +Wire Bus Line + 6750 1000 6750 1050 +Wire Bus Line + 6750 1050 6750 1100 +Wire Bus Line + 6750 1100 6750 1250 +Wire Bus Line + 6750 1250 6750 1350 +Wire Bus Line + 6750 1350 6750 1400 +Entry Bus Bus + 6650 1250 6750 1350 +Wire Bus Line + 6200 1250 6650 1250 +Wire Wire Line + 6650 950 6200 950 +Entry Wire Line + 6650 950 6750 1050 +NoConn ~ 5050 850 +NoConn ~ 5050 1350 +NoConn ~ 5300 1100 +NoConn ~ 4800 1100 +Wire Wire Line + 1300 2400 2800 2400 +Text Label 1300 2400 0 60 ~ 0 +LOCAL_RIGHT_NORMAL +Wire Wire Line + 1300 2600 2800 2600 +Text Label 2800 2600 2 60 ~ 12 +LOCAL_LEFT_BOLD +Wire Wire Line + 3400 1800 3400 2900 +Text Label 3400 2900 1 60 ~ 12 +LOCAL_UP_ITALIC +Wire Wire Line + 3700 1800 3700 2900 +Text Label 3700 1800 3 60 Italic 12 +LOCAL_DOWN_BI +Wire Wire Line + 4650 2400 5350 2400 +Wire Wire Line + 4650 2700 5350 2700 +Text GLabel 4650 2400 0 60 Input ~ 0 +G_R_IN +Text GLabel 5350 2700 2 60 Input ~ 0 +G_L_IN +Text GLabel 4650 2700 0 60 Output ~ 0 +G_R_OUT +Text GLabel 5350 2400 2 60 Output ~ 0 +G_L_OUT +Wire Wire Line + 6100 2200 6100 2900 +Wire Wire Line + 6400 2200 6400 2900 +Text GLabel 6100 2200 1 60 BiDi ~ 0 +G_UP_BI +Wire Wire Line + 4650 3000 4850 3000 +Wire Wire Line + 4850 3000 5350 3000 +Text GLabel 4650 3000 0 60 BiDi ~ 0 +G_R_BIDIR +Text GLabel 5350 3000 2 60 UnSpc ~ 0 +G_LEFT_PASS +Text GLabel 4650 3150 0 60 UnSpc ~ 0 +G_R_TRI +Wire Wire Line + 4650 3150 4850 3150 +Wire Wire Line + 4850 3150 4850 3000 +Connection ~ 4850 3000 +Text GLabel 6100 2900 3 60 Input ~ 0 +G_DOWN_IN +Text GLabel 6400 2200 1 60 Output ~ 0 +G_UP_OUT +Text GLabel 6400 2900 3 60 UnSpc ~ 0 +G_DOWN_PASS +Wire Wire Line + 7250 2700 7950 2700 +Wire Wire Line + 8750 2200 8750 2900 +Wire Wire Line + 9050 2200 9050 2900 +Wire Wire Line + 7250 3150 7450 3150 +Wire Wire Line + 7450 3150 7450 3000 +Connection ~ 7450 3000 +Text HLabel 7250 2400 0 60 Input ~ 0 +H_R_IN +Text HLabel 7250 2700 0 60 Output ~ 0 +H_R_OUT +Text HLabel 7250 3000 0 60 BiDi ~ 0 +H_R_BIDIR +Text HLabel 7250 3150 0 60 UnSpc ~ 0 +H_R_TRI +Text HLabel 7950 2400 2 60 Output ~ 0 +H_L_OUT +Text HLabel 7950 2700 2 60 Input ~ 0 +H_L_IN +Wire Wire Line + 7250 2400 7950 2400 +Text HLabel 7950 3000 2 60 UnSpc ~ 0 +H_LEFT_PASS +Text HLabel 8750 2200 1 60 BiDi ~ 0 +H_UP_BI +Text HLabel 8750 2900 3 60 Input ~ 0 +H_DOWN_IN +Text HLabel 9050 2200 1 60 Output ~ 0 +H_UP_OUT +Text HLabel 9050 2900 3 60 UnSpc ~ 0 +H_DOWN_PASS +Wire Wire Line + 7250 3000 7450 3000 +Wire Wire Line + 7450 3000 7950 3000 +Text Notes 1300 3400 0 60 ~ 0 +60 mil Text +Wire Notes Line + 1300 3150 1300 3550 +Wire Notes Line + 1850 3550 1850 3150 +Wire Notes Line + 1150 3300 2000 3300 +Wire Notes Line + 1150 3400 2000 3400 +Text GLabel 1400 3750 2 60 Output ~ 0 +GLOBAL +Text GLabel 1800 3950 0 60 Output ~ 0 +GLOBAL +Wire Notes Line + 1400 3550 1400 4150 +Wire Notes Line + 1800 3550 1800 4150 +Wire Notes Line + 1250 3700 1950 3700 +Wire Notes Line + 1250 3800 1950 3800 +Wire Notes Line + 1250 3900 1950 3900 +Wire Notes Line + 1250 4000 1950 4000 +Text HLabel 2950 3600 0 60 Input ~ 0 +HIERARCHICAL +Text HLabel 2250 3750 2 60 Input ~ 0 +HIERARCHICAL +Wire Notes Line + 2250 3400 2250 3950 +Wire Notes Line + 2100 3700 3100 3700 +Wire Notes Line + 2100 3650 3100 3650 +Wire Notes Line + 2100 3550 3100 3550 +Wire Notes Line + 2100 3800 3100 3800 +Wire Notes Line + 2950 3400 2950 3950 +$Comp +L TEST U? +U 1 1 57933A17 +P 1650 5200 +F 0 "U?" H 1175 5875 60 0000 C CNN +F 1 "TEST" H 1225 4525 60 0000 C CNN +F 2 "" H 1800 5400 60 0001 C CNN +F 3 "" H 1800 5400 60 0001 C CNN + 1 1650 5200 + 1 0 0 -1 +$EndComp +$Comp +L TEST U? +U 1 1 57933A5F +P 4650 4750 +F 0 "U?" H 4175 5425 60 0000 C CNN +F 1 "TEST" H 4225 4075 60 0000 C CNN +F 2 "" H 4800 4950 60 0001 C CNN +F 3 "" H 4800 4950 60 0001 C CNN + 1 4650 4750 + 0 1 1 0 +$EndComp +$Comp +L TEST U? +U 1 1 57933A97 +P 7450 4900 +F 0 "U?" H 6975 5575 60 0000 C CNN +F 1 "TEST" H 7025 4225 60 0000 C CNN +F 2 "" H 7600 5100 60 0001 C CNN +F 3 "" H 7600 5100 60 0001 C CNN + 1 7450 4900 + -1 0 0 1 +$EndComp +$Comp +L TEST U? +U 1 1 57933ACD +P 9750 5250 +F 0 "U?" H 9275 5925 60 0000 C CNN +F 1 "TEST" H 9325 4575 60 0000 C CNN +F 2 "" H 9900 5450 60 0001 C CNN +F 3 "" H 9900 5450 60 0001 C CNN + 1 9750 5250 + 0 -1 -1 0 +$EndComp +$Comp +L TEST U? +U 1 1 57933B03 +P 2450 6950 +F 0 "U?" H 1975 7625 60 0000 C CNN +F 1 "TEST" H 2025 6275 60 0000 C CNN +F 2 "" H 2600 7150 60 0001 C CNN +F 3 "" H 2600 7150 60 0001 C CNN + 1 2450 6950 + 1 0 0 1 +$EndComp +$Comp +L TEST U? +U 1 1 57933B47 +P 5950 6900 +F 0 "U?" H 5475 7575 60 0000 C CNN +F 1 "TEST" H 5525 6225 60 0000 C CNN +F 2 "" H 6100 7100 60 0001 C CNN +F 3 "" H 6100 7100 60 0001 C CNN + 1 5950 6900 + -1 0 0 -1 +$EndComp +Text Notes 900 7000 0 60 ~ 0 +X-flip +Text Notes 4250 6900 0 60 ~ 0 +Y-flip +$Comp +L TEST U? +U 1 1 57933BAB +P 10200 2400 +F 0 "U?" H 9725 3075 60 0000 C CNN +F 1 "TEST" H 9775 1725 60 0000 C CNN +F 2 "" H 10350 2600 60 0001 C CNN +F 3 "" H 10350 2600 60 0001 C CNN + 1 10200 2400 + 0 1 -1 0 +$EndComp +Text Notes 10000 850 0 60 ~ 0 +R+X +$Sheet +S 7250 790 800 560 +U 579BE133 +F0 "Sheet579BE132" 60 +F1 "file579BE132.sch" 60 +$EndSheet +$Sheet +S 7190 1790 770 400 +U 579BE13D +F0 "Sheet579BE13C" 60 +F1 "file579BE13C.sch" 60 +$EndSheet +Wire Bus Line + 6200 1200 6650 1200 +Entry Bus Bus + 6650 1200 6750 1100 +Wire Wire Line + 6650 900 6200 900 +Entry Wire Line + 6650 900 6750 800 +Wire Wire Line + 6850 750 7100 750 +Wire Wire Line + 6850 800 7100 800 +Entry Wire Line + 6750 900 6850 800 +Entry Wire Line + 6750 650 6850 750 +Entry Bus Bus + 6750 1250 6850 1150 +Entry Bus Bus + 6750 1000 6850 1100 +Wire Bus Line + 7100 1100 6850 1100 +Wire Bus Line + 7100 1150 6850 1150 +Wire Bus Line + 4750 1950 5700 1950 +Wire Wire Line + 4850 1850 4850 1500 +Wire Bus Line + 4950 1850 4950 1500 +Wire Wire Line + 5400 1850 5400 1500 +Wire Bus Line + 5500 1500 5500 1850 +Wire Wire Line + 4800 2250 4800 2050 +Wire Wire Line + 5300 2250 5300 2050 +Wire Bus Line + 5450 2250 5450 2050 +Wire Bus Line + 4900 2250 4900 2050 +Entry Wire Line + 4850 1850 4950 1950 +Entry Wire Line + 5200 1950 5300 2050 +Entry Wire Line + 4800 2050 4900 1950 +Entry Wire Line + 5300 1950 5400 1850 +Entry Bus Bus + 4950 1850 5050 1950 +Entry Bus Bus + 4900 2050 5000 1950 +Entry Bus Bus + 5400 1950 5500 1850 +Entry Bus Bus + 5350 1950 5450 2050 +$EndSCHEMATC diff --git a/eeshow/test/README b/eeshow/test/README new file mode 100644 index 0000000..fdc81f2 --- /dev/null +++ b/eeshow/test/README @@ -0,0 +1,24 @@ +Setup +----- + +git clone https://neo900.org/git/ee.git neo900-ee +cd neo900-ee +git reset --hard 57eebdcf573311c049bc57527bc03a517aff0fef + +cd .. + +git clone git://projects.qi-hardware.com/kicad-libs.git +cd kicad-libs +git reset --hard 143fa7fe10cabbfe1cb12d010c7426d482d7e6f4 + +cd .. + +./genpng ref + + +Run test +-------- + +./genpng +./comp +qiv -t _diff*.png diff --git a/eeshow/test/comp b/eeshow/test/comp new file mode 100755 index 0000000..b406607 --- /dev/null +++ b/eeshow/test/comp @@ -0,0 +1,29 @@ +#!/bin/sh + +usage() +{ + echo "usage: $0 [dir]" 1>&2 + exit 1 +} + + +[ "$2" ] && usage +[ "${1#-}" != "$1" ] && usage + +dir=${1:-.} + +diffs=0 +rm -f $dir/_diff*png +for n in $dir/out*.png; do + out=`basename "$n"` + ref=$dir/ref${out#out} + diff=$dir/_diff${out#out} + if ! compare -metric AE $ref $n - >/dev/null; then + diffs=`expr $diffs + 1` + compare -metric AE $ref $n $diff + fi +done +echo +[ $diffs = 0 ] && exit 0 +echo "$diffs difference(s)" 1>&2 +exit 1 diff --git a/eeshow/test/genpng b/eeshow/test/genpng new file mode 100755 index 0000000..0d73ec1 --- /dev/null +++ b/eeshow/test/genpng @@ -0,0 +1,37 @@ +#!/bin/sh + +usage() +{ + echo "usage: $0 [[dir] prefix]" 1>&2 + exit 1 +} + + +[ "$3" ] && usage +[ "${1#-}" != "$1" ] && usage + +dir=. +if [ "$2" ]; then + dir=$1 + shift +fi +prefix=${1:-out} + +sheet=1 +while [ $sheet -le 38 ]; do + echo -n . + sn=`printf '%02d' $sheet` + file=$dir/$prefix$sn.png + if [ $sheet = 1 ]; then + in=$dir/neo900-ee/hw/neo900.sch + else + in=$dir/neo900-ee/hw/neo900_SS_`expr $sheet - 1`.sch + fi + file=$dir/$prefix$sn.png + $dir/../eeshow $dir/neo900-ee/hw/neo900.lib \ + $dir/kicad-libs/components/powered.lib "$in" \ + -- fig -t $dir/frame.fig SHEET=$sn | + fig2dev -L png -m 2 >$file + sheet=`expr $sheet + 1` +done +echo diff --git a/eeshow/text.c b/eeshow/text.c new file mode 100644 index 0000000..68754c8 --- /dev/null +++ b/eeshow/text.c @@ -0,0 +1,158 @@ +/* + * text.c - FIG text object + * + * Written 2016 by Werner Almesberger + * Copyright 2016 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 <stddef.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "util.h" +#include "misc.h" +#include "style.h" +#include "gfx.h" +#include "text.h" + + +void text_init(struct text *txt) +{ + txt->s = NULL; + txt->size = 0; + txt->x = txt->y = 0; + txt->rot = 0; + txt->hor = text_mid; + txt->vert = text_mid; +} + + +void text_free(struct text *txt) +{ + free((void *) txt->s); + txt->s = NULL; +} + + +void text_set(struct text *txt, const char *s) +{ + free((void *) txt->s); + txt->s = stralloc(s); +} + + +void text_rot(struct text *txt, int deg) +{ + txt->rot = angle_add(txt->rot, deg); +} + + +enum text_align text_flip(enum text_align align) +{ + switch (align) { + case text_min: + return text_max; + case text_mid: + return text_mid; + case text_max: + return text_min; + default: + abort(); + } +} + + +void text_flip_x(struct text *txt) +{ + txt->rot = angle_add(txt->rot, 180); + txt->hor = text_flip(txt->hor); + // @@@ flip vert, too ? +} + + +static int align(int dim, enum text_align align) +{ + switch (align) { + case text_min: + return 0; + case text_mid: + return dim / 2; + case text_max: + return dim; + default: + abort(); + } +} + + +void text_fig(const struct text *txt, int color, unsigned layer) +{ + char *buf = stralloc(txt->s); + char *tmp = buf; + const char *s; + int x = txt->x; + int y = txt->y; + + x += rx(0, align(txt->size, txt->vert), txt->rot); + y += ry(0, align(txt->size, txt->vert), txt->rot); + while (1) { + s = strtok(tmp, "\n"); + if (!s) + break; + tmp = NULL; + gfx_text(x, y, s, txt->size, txt->hor, txt->rot, color, layer); + x += rx(0, NEWLINE_SKIP * txt->size, txt->rot); + y += ry(0, NEWLINE_SKIP * txt->size, txt->rot); + } + free(buf); +} + + +void text_rel(const struct text *txt, enum text_align xr, enum text_align yr, + int dx, int dy, int *res_x, int *res_y) +{ + int width = gfx_text_width(txt->s, txt->size); + + dx -= align(width, txt->hor); + dy += align(txt->size, txt->vert); + dx += align(width, xr); + dy -= align(txt->size, yr); + if (res_x) + *res_x = txt->x + rx(dx, dy, txt->rot); + if (res_y) + *res_y = txt->y + ry(dx, dy, txt->rot); +} + + +void text_shift(struct text *txt, enum text_align xr, enum text_align yr, + int dx, int dy) +{ + text_rel(txt, xr, yr, dx, dy, &txt->x, &txt->y); +} + + +int text_rel_x(const struct text *txt, enum text_align xr, enum text_align yr, + int dx, int dy) +{ + int x; + + text_rel(txt, xr, yr, dx, dy, &x, NULL); + return x; +} + + +int text_rel_y(const struct text *txt, enum text_align xr, enum text_align yr, + int dx, int dy) +{ + int y; + + text_rel(txt, xr, yr, dx, dy, NULL, &y); + return y; +} diff --git a/eeshow/text.h b/eeshow/text.h new file mode 100644 index 0000000..05f1ffb --- /dev/null +++ b/eeshow/text.h @@ -0,0 +1,58 @@ +/* + * text.h - FIG text object + * + * Written 2016 by Werner Almesberger + * Copyright 2016 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 TEXT_H +#define TEXT_H + + +/* use constants of FIG text sub_type */ + +enum text_align { + text_min = 0, // left or bottom + text_mid = 1, // center + text_max = 2, // right or top +}; + +enum text_style { + text_normal, +}; + +struct text { + const char *s; + int size; + int x, y; + int rot; + enum text_align hor; + enum text_align vert; +}; + +void text_init(struct text *txt); +void text_free(struct text *txt); + +void text_set(struct text *txt, const char *s); +void text_rot(struct text *txt, int deg); +void text_flip_x(struct text *txt); +enum text_align text_flip(enum text_align align); + +void text_fig(const struct text *txt, int color, unsigned layer); + +void text_rel(const struct text *txt, enum text_align xr, enum text_align yr, + int dx, int dy, int *res_x, int *res_y); +void text_shift(struct text *txt, enum text_align xr, enum text_align yr, + int dx, int dy); +int text_rel_x(const struct text *txt, enum text_align xr, enum text_align yr, + int dx, int dy); +int text_rel_y(const struct text *txt, enum text_align xr, enum text_align yr, + int dx, int dy); + +#endif /* !TEXT_H */ diff --git a/eeshow/util.h b/eeshow/util.h new file mode 100644 index 0000000..ea40ce5 --- /dev/null +++ b/eeshow/util.h @@ -0,0 +1,52 @@ +/* + * util.h - Common utility functions + * + * Written 2016 by Werner Almesberger + * Copyright 2016 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 UTIL_H +#define UTIL_H + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + + +#define alloc_size(s) \ + ({ void *alloc_size_tmp = malloc(s); \ + if (!alloc_size_tmp) { \ + perror("malloc"); \ + exit(1); \ + } \ + alloc_size_tmp; }) + +#define alloc_type(t) ((t *) alloc_size(sizeof(t))) + +#define stralloc(s) \ + ({ char *stralloc_tmp = strdup(s); \ + if (!stralloc_tmp) { \ + perror("strdup"); \ + exit(1); \ + } \ + stralloc_tmp; }) + + +#define ARRAY_ELEMENTS(a) (sizeof(a) / sizeof(a[0])) +#define ARRAY_END(a) ((a) + ARRAY_ELEMENTS(a)) + + +#define swap(a, b) \ + ({ typeof(a) _tmp = (a); a = (b); b = _tmp; }) + + +#define unsupported(s) \ + fprintf(stderr, __FILE__ ":%d: unsupported: " s "\n", __LINE__) + +#endif /* !UTIL_H */