Index: trunk/doc-rnd/Makefile =================================================================== --- trunk/doc-rnd/Makefile (revision 1268) +++ trunk/doc-rnd/Makefile (revision 1269) @@ -4,4 +4,4 @@ all: keys.html keys.html: $(MENU_RES) $(KEYLIST) - $(KEYLIST) $(MENU_RES) > keys.html \ No newline at end of file + $(KEYLIST) $(MENU_RES) > keys.html Index: trunk/doc-rnd/TODO =================================================================== --- trunk/doc-rnd/TODO (revision 1268) +++ trunk/doc-rnd/TODO (revision 1269) @@ -7,6 +7,8 @@ - test onpoint - PrintQuotedString(FILE * FP, char *S): remove dynamic string allocation - get rid of gcode/lists.h, ds.[ch] and vector.[ch] + - test whether plugin compiled exporters work + - test whether gd based plugins can be compiled to so (png, nelma) Index: trunk/doc-rnd/mods/after.png =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Index: trunk/doc-rnd/mods/index.html =================================================================== --- trunk/doc-rnd/mods/index.html (revision 1268) +++ trunk/doc-rnd/mods/index.html (revision 1269) @@ -58,11 +58,11 @@ module size [sloc] status configure
default description -autoplace604 +autoplace608 works buildin Automatically place elements. -autoroute4172 +autoroute4177 works buildin Automatically route selected or all rats. This is the original autorouter. @@ -71,50 +71,63 @@
(TODO: needs scconfig support) disabled Remote control PCB using DBUS. -djopt2312 +djopt2315 works buildin Various board optimization algorithms. -edif3575 +export_gcode2450 works buildin - Import code for netlists in the EDIF format. -gpmi2688 + Export to gcode +export_lpr96 works buildin + Export to lpr (using export_ps to generate postscript) +export_nelma683 +export_ps1608 + works + buildin + Export postscript or embedded postscript. +gpmi2992 + works + buildin
(if gpmi is installed) Scriptable plugin system with about 10 scripting languages supported and dynamic load/unload of scripts that can manipulate the GUI, the board, can implement exporters, etc. -import_sch252 +import_edif3578 works buildin + Import code for netlists in the EDIF format. +import_sch255 + works + buildin Imports element and netlist data from the schematics (or some other source). -mincut884 +mincut886 works buildin Use the minimal cut algorithm to indicate shorts: instead of highlighting two random pins/pads, try to highlight the least number of objects that connect the two networks. -oldactions50 +oldactions53 works disabled Random collection of old/obsolete actions. Bell(): audible feedback; DumpLibrary(): print footprint library on stdout -puller1876 +puller1878 works buildin Pull traces to minimize their length. -renumber212 +renumber218 works buildin Renumber elements (renaming them) and generate a text file for back annotation. -stroke123 +stroke124 partially works (doesn't work with lesstif; works with the gtk hid, but there's no zoom bindings) disabled
(requires libstroke installed) Gesture recognition with libstroke. -toporouter6159 +toporouter6161 fails
(infinite loop in gts) disabled Automatically route selected or all rats using a topological algorithm. This is the new autorouter from 2009. -vendordrill564 +vendordrill567 works buildin Vendor drill mapping. Index: trunk/doc-rnd/mods/mods.png =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Index: trunk/scconfig/hooks.c =================================================================== --- trunk/scconfig/hooks.c (revision 1268) +++ trunk/scconfig/hooks.c (revision 1269) @@ -21,8 +21,6 @@ "png", "libs/gui/gd/gdImagePng/presents", "gif", "libs/gui/gd/gdImageGif/presents", "jpg", "libs/gui/gd/gdImageJpeg/presents", - "gcode", "libs/gui/gd/presents", - "nelma", "libs/gui/gd/presents", NULL, NULL }; @@ -96,6 +94,14 @@ {"buildin-export_lpr", "/local/pcb/export_lpr/buildin", arg_true, "$static link the lpr printer into the executable"}, {"plugin-export_lpr", "/local/pcb/export_lpr/buildin", arg_false, "$the lpr printer is a dynamic loadable plugin"}, + {"disable-export_gcode", "/local/pcb/export_gcode/enable", arg_false, "$do not compile the gcode exporter"}, + {"buildin-export_gcode", "/local/pcb/export_gcode/buildin", arg_true, "$static link the gcode exporter into the executable"}, + {"plugin-export_gcode", "/local/pcb/export_gcode/buildin", arg_false, "$the gcode exporter is a dynamic loadable plugin"}, + + {"disable-export_nelma", "/local/pcb/export_nelma/enable", arg_false, "$do not compile the nelma exporter"}, + {"buildin-export_nelma", "/local/pcb/export_nelma/buildin", arg_true, "$static link the nelma exporter into the executable"}, + {"plugin-export_nelma", "/local/pcb/export_nelma/buildin", arg_false, "$the nelma exporter is a dynamic loadable plugin"}, + {NULL, NULL, NULL, NULL} }; @@ -212,6 +218,14 @@ put("/local/pcb/export_lpr/enable", strue); put("/local/pcb/export_lpr/buildin", strue); + db_mkdir("/local/pcb/export_gcode"); + put("/local/pcb/export_gcode/enable", strue); + put("/local/pcb/export_gcode/buildin", strue); + + db_mkdir("/local/pcb/export_nelma"); + put("/local/pcb/export_nelma/enable", strue); + put("/local/pcb/export_nelma/buildin", strue); + return 0; } @@ -356,6 +370,13 @@ } } + if (!istrue(get("libs/gui/gd/presents"))) { + if (istrue(get("/local/pcb/export_nelma/enable"))) { + report_repeat("WARNING: disabling the nelma exporter because libgd is not found or not configured...\n"); + hook_custom_arg("disable-export_nelma", NULL); + } + } + return 0; } @@ -494,6 +515,8 @@ printf("\n"); plugin_stat("export_ps:", "/local/pcb/export_ps"); plugin_stat("export_lpr:", "/local/pcb/export_lpr"); + plugin_stat("export_gcode:", "/local/pcb/export_gcode"); + plugin_stat("export_nelma:", "/local/pcb/export_nelma"); if (repeat != NULL) printf("\n%s\n", repeat); Index: trunk/src/Makefile.in =================================================================== --- trunk/src/Makefile.in (revision 1268) +++ trunk/src/Makefile.in (revision 1269) @@ -136,8 +136,6 @@ include {Makefile.in.mod/bom} include {Makefile.in.mod/png} include {Makefile.in.mod/gerber} -include {Makefile.in.mod/gcode} -include {Makefile.in.mod/nelma} include {../src_plugins/autoplace/Plug.tmpasm} include {../src_plugins/autoroute/Plug.tmpasm} @@ -154,6 +152,8 @@ include {../src_plugins/import_sch/Plug.tmpasm} include {../src_plugins/export_ps/Plug.tmpasm} include {../src_plugins/export_lpr/Plug.tmpasm} +include {../src_plugins/export_gcode/Plug.tmpasm} +include {../src_plugins/export_nelma/Plug.tmpasm} append /local/pcb/CFLAGS /target/libs/sul/glib/cflags Index: trunk/src/Makefile.in.mod/gcode =================================================================== --- trunk/src/Makefile.in.mod/gcode (revision 1268) +++ trunk/src/Makefile.in.mod/gcode (nonexistent) @@ -1,14 +0,0 @@ -if /target/libs/gui/gd/presents then - -append /local/pcb/HIDS {gcode} -append /local/pcb/CFLAGS {-I./hid/gcode} -append /local/pcb/OBJS [@ - hid/gcode/gcode.o - hid/gcode/decompose.o - hid/gcode/trace.o - hid/gcode/curve.o -@] - -append /local/pcb/ACTION_REG_SRC {hid/gcode/gcode.c hid/gcode/hid.conf} - -end Index: trunk/src/Makefile.in.mod/nelma =================================================================== --- trunk/src/Makefile.in.mod/nelma (revision 1268) +++ trunk/src/Makefile.in.mod/nelma (nonexistent) @@ -1,7 +0,0 @@ -if /target/libs/gui/gd/presents then -append /local/pcb/HIDS {nelma} -append /local/pcb/CFLAGS {-I./hid/nelma} -append /local/pcb/OBJS {hid/nelma/nelma.o} -append /local/pcb/ACTION_REG_SRC {hid/nelma/nelma.c hid/nelma/hid.conf} -end - Index: trunk/src/hid/gcode/trace.c =================================================================== --- trunk/src/hid/gcode/trace.c (revision 1268) +++ trunk/src/hid/gcode/trace.c (nonexistent) @@ -1,1292 +0,0 @@ -/* This file was slightly modified by Alberto Maccioni to be used with PCB G-CODE exporter*/ - -/* Copyright (C) 2001-2007 Peter Selinger. - This file is part of Potrace. It is free software and it is covered - by the GNU General Public License. See the file COPYING for details. */ - -/* $Id: trace.c 147 2007-04-09 00:44:09Z selinger $ */ -/* transform jaggy paths into smooth curves */ - -#include -#include -#include -#include - -#include "global.h" -#include "potracelib.h" -#include "curve.h" -#include "lists.h" -#include "auxiliary.h" -#include "trace.h" -/*#include "progress.h"*/ - -#define INFTY 10000000 /* it suffices that this is longer than any - path; it need not be really infinite */ -#define COS179 -0.999847695156 /* the cosine of 179 degrees */ - -/* ---------------------------------------------------------------------- */ -#define SAFE_MALLOC(var, n, typ) \ - if ((var = (typ *)malloc((n)*sizeof(typ))) == NULL) goto malloc_error - -/* ---------------------------------------------------------------------- */ -/* auxiliary functions */ - -/* return a direction that is 90 degrees counterclockwise from p2-p0, - but then restricted to one of the major wind directions (n, nw, w, etc) */ -static inline point_t dorth_infty(dpoint_t p0, dpoint_t p2) -{ - point_t r; - - r.y = sign(p2.x - p0.x); - r.x = -sign(p2.y - p0.y); - - return r; -} - -/* return (p1-p0)x(p2-p0), the area of the parallelogram */ -static inline double dpara(dpoint_t p0, dpoint_t p1, dpoint_t p2) -{ - double x1, y1, x2, y2; - - x1 = p1.x - p0.x; - y1 = p1.y - p0.y; - x2 = p2.x - p0.x; - y2 = p2.y - p0.y; - - return x1 * y2 - x2 * y1; -} - -/* ddenom/dpara have the property that the square of radius 1 centered - at p1 intersects the line p0p2 iff |dpara(p0,p1,p2)| <= ddenom(p0,p2) */ -static inline double ddenom(dpoint_t p0, dpoint_t p2) -{ - point_t r = dorth_infty(p0, p2); - - return r.y * (p2.x - p0.x) - r.x * (p2.y - p0.y); -} - -/* return 1 if a <= b < c < a, in a cyclic sense (mod n) */ -static inline int cyclic(int a, int b, int c) -{ - if (a <= c) { - return (a <= b && b < c); - } - else { - return (a <= b || b < c); - } -} - -/* determine the center and slope of the line i..j. Assume ilen; - sums_t *sums = pp->sums; - - double x, y, x2, xy, y2; - double k; - double a, b, c, lambda2, l; - int r = 0; /* rotations from i to j */ - - while (j >= n) { - j -= n; - r += 1; - } - while (i >= n) { - i -= n; - r -= 1; - } - while (j < 0) { - j += n; - r -= 1; - } - while (i < 0) { - i += n; - r += 1; - } - - x = sums[j + 1].x - sums[i].x + r * sums[n].x; - y = sums[j + 1].y - sums[i].y + r * sums[n].y; - x2 = sums[j + 1].x2 - sums[i].x2 + r * sums[n].x2; - xy = sums[j + 1].xy - sums[i].xy + r * sums[n].xy; - y2 = sums[j + 1].y2 - sums[i].y2 + r * sums[n].y2; - k = j + 1 - i + r * n; - - ctr->x = x / k; - ctr->y = y / k; - - a = (x2 - (double) x * x / k) / k; - b = (xy - (double) x * y / k) / k; - c = (y2 - (double) y * y / k) / k; - - lambda2 = (a + c + sqrt((a - c) * (a - c) + 4 * b * b)) / 2; /* larger e.value */ - - /* now find e.vector for lambda2 */ - a -= lambda2; - c -= lambda2; - - if (fabs(a) >= fabs(c)) { - l = sqrt(a * a + b * b); - if (l != 0) { - dir->x = -b / l; - dir->y = a / l; - } - } - else { - l = sqrt(c * c + b * b); - if (l != 0) { - dir->x = -c / l; - dir->y = b / l; - } - } - if (l == 0) { - dir->x = dir->y = 0; /* sometimes this can happen when k=4: - the two eigenvalues coincide */ - } -} - -/* the type of (affine) quadratic forms, represented as symmetric 3x3 - matrices. The value of the quadratic form at a vector (x,y) is v^t - Q v, where v = (x,y,1)^t. */ -typedef double quadform_t[3][3]; - -/* Apply quadratic form Q to vector w = (w.x,w.y) */ -static inline double quadform(quadform_t Q, dpoint_t w) -{ - double v[3]; - int i, j; - double sum; - - v[0] = w.x; - v[1] = w.y; - v[2] = 1; - sum = 0.0; - - for (i = 0; i < 3; i++) { - for (j = 0; j < 3; j++) { - sum += v[i] * Q[i][j] * v[j]; - } - } - return sum; -} - -/* calculate p1 x p2 */ -static inline int xprod(point_t p1, point_t p2) -{ - return p1.x * p2.y - p1.y * p2.x; -} - -/* calculate (p1-p0)x(p3-p2) */ -static inline double cprod(dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3) -{ - double x1, y1, x2, y2; - - x1 = p1.x - p0.x; - y1 = p1.y - p0.y; - x2 = p3.x - p2.x; - y2 = p3.y - p2.y; - - return x1 * y2 - x2 * y1; -} - -/* calculate (p1-p0)*(p2-p0) */ -static inline double iprod(dpoint_t p0, dpoint_t p1, dpoint_t p2) -{ - double x1, y1, x2, y2; - - x1 = p1.x - p0.x; - y1 = p1.y - p0.y; - x2 = p2.x - p0.x; - y2 = p2.y - p0.y; - - return x1 * x2 + y1 * y2; -} - -/* calculate (p1-p0)*(p3-p2) */ -static inline double iprod1(dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3) -{ - double x1, y1, x2, y2; - - x1 = p1.x - p0.x; - y1 = p1.y - p0.y; - x2 = p3.x - p2.x; - y2 = p3.y - p2.y; - - return x1 * x2 + y1 * y2; -} - -/* calculate distance between two points */ -static inline double ddist(dpoint_t p, dpoint_t q) -{ - return sqrt(sq(p.x - q.x) + sq(p.y - q.y)); -} - -/* calculate point of a bezier curve */ -static inline dpoint_t bezier(double t, dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3) -{ - double s = 1 - t; - dpoint_t res; - - /* Note: a good optimizing compiler (such as gcc-3) reduces the - following to 16 multiplications, using common subexpression - elimination. */ - - res.x = s * s * s * p0.x + 3 * (s * s * t) * p1.x + 3 * (t * t * s) * p2.x + t * t * t * p3.x; - res.y = s * s * s * p0.y + 3 * (s * s * t) * p1.y + 3 * (t * t * s) * p2.y + t * t * t * p3.y; - - return res; -} - -/* calculate the point t in [0..1] on the (convex) bezier curve - (p0,p1,p2,p3) which is tangent to q1-q0. Return -1.0 if there is no - solution in [0..1]. */ -static double tangent(dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3, dpoint_t q0, dpoint_t q1) -{ - double A, B, C; /* (1-t)^2 A + 2(1-t)t B + t^2 C = 0 */ - double a, b, c; /* a t^2 + b t + c = 0 */ - double d, s, r1, r2; - - A = cprod(p0, p1, q0, q1); - B = cprod(p1, p2, q0, q1); - C = cprod(p2, p3, q0, q1); - - a = A - 2 * B + C; - b = -2 * A + 2 * B; - c = A; - - d = b * b - 4 * a * c; - - if (a == 0 || d < 0) { - return -1.0; - } - - s = sqrt(d); - - r1 = (-b + s) / (2 * a); - r2 = (-b - s) / (2 * a); - - if (r1 >= 0 && r1 <= 1) { - return r1; - } - else if (r2 >= 0 && r2 <= 1) { - return r2; - } - else { - return -1.0; - } -} - -/* ---------------------------------------------------------------------- */ -/* Preparation: fill in the sum* fields of a path (used for later - rapid summing). Return 0 on success, 1 with errno set on - failure. */ -static int calc_sums(privpath_t * pp) -{ - int i, x, y; - int n = pp->len; - - SAFE_MALLOC(pp->sums, pp->len + 1, sums_t); - - /* origin */ - pp->x0 = pp->pt[0].x; - pp->y0 = pp->pt[0].y; - - /* preparatory computation for later fast summing */ - pp->sums[0].x2 = pp->sums[0].xy = pp->sums[0].y2 = pp->sums[0].x = pp->sums[0].y = 0; - for (i = 0; i < n; i++) { - x = pp->pt[i].x - pp->x0; - y = pp->pt[i].y - pp->y0; - pp->sums[i + 1].x = pp->sums[i].x + x; - pp->sums[i + 1].y = pp->sums[i].y + y; - pp->sums[i + 1].x2 = pp->sums[i].x2 + x * x; - pp->sums[i + 1].xy = pp->sums[i].xy + x * y; - pp->sums[i + 1].y2 = pp->sums[i].y2 + y * y; - } - return 0; - -malloc_error: - return 1; -} - -/* ---------------------------------------------------------------------- */ -/* Stage 1: determine the straight subpaths (Sec. 2.2.1). Fill in the - "lon" component of a path object (based on pt/len). For each i, - lon[i] is the furthest index such that a straight line can be drawn - from i to lon[i]. Return 1 on error with errno set, else 0. */ - -/* this algorithm depends on the fact that the existence of straight - subpaths is a triplewise property. I.e., there exists a straight - line through squares i0,...,in iff there exists a straight line - through i,j,k, for all i0<=i= 0 and xprod(constraint[1], - cur) <= 0. */ - -/* Remark for Potrace 1.1: the current implementation of calc_lon is - more complex than the implementation found in Potrace 1.0, but it - is considerably faster. The introduction of the "nc" data structure - means that we only have to test the constraints for "corner" - points. On a typical input file, this speeds up the calc_lon - function by a factor of 31.2, thereby decreasing its time share - within the overall Potrace algorithm from 72.6% to 7.82%, and - speeding up the overall algorithm by a factor of 3.36. On another - input file, calc_lon was sped up by a factor of 6.7, decreasing its - time share from 51.4% to 13.61%, and speeding up the overall - algorithm by a factor of 1.78. In any case, the savings are - substantial. */ - -/* returns 0 on success, 1 on error with errno set */ -static int calc_lon(privpath_t * pp) -{ - point_t *pt = pp->pt; - int n = pp->len; - int i, j, k, k1; - int ct[4], dir; - point_t constraint[2]; - point_t cur; - point_t off; - int *pivk = NULL; /* pivk[n] */ - int *nc = NULL; /* nc[n]: next corner */ - point_t dk; /* direction of k-k1 */ - int a, b, c, d; - - SAFE_MALLOC(pivk, n, int); - SAFE_MALLOC(nc, n, int); - - /* initialize the nc data structure. Point from each point to the - furthest future point to which it is connected by a vertical or - horizontal segment. We take advantage of the fact that there is - always a direction change at 0 (due to the path decomposition - algorithm). But even if this were not so, there is no harm, as - in practice, correctness does not depend on the word "furthest" - above. */ - k = 0; - for (i = n - 1; i >= 0; i--) { - if (pt[i].x != pt[k].x && pt[i].y != pt[k].y) { - k = i + 1; /* necessarily ilon, n, int); - - /* determine pivot points: for each i, let pivk[i] be the furthest k - such that all j with i= 0; i--) { - ct[0] = ct[1] = ct[2] = ct[3] = 0; - - /* keep track of "directions" that have occurred */ - dir = (3 + 3 * (pt[mod(i + 1, n)].x - pt[i].x) + (pt[mod(i + 1, n)].y - pt[i].y)) / 2; - ct[dir]++; - - constraint[0].x = 0; - constraint[0].y = 0; - constraint[1].x = 0; - constraint[1].y = 0; - - /* find the next k such that no straight line from i to k */ - k = nc[i]; - k1 = i; - while (1) { - - dir = (3 + 3 * sign(pt[k].x - pt[k1].x) + sign(pt[k].y - pt[k1].y)) / 2; - ct[dir]++; - - /* if all four "directions" have occurred, cut this path */ - if (ct[0] && ct[1] && ct[2] && ct[3]) { - pivk[i] = k1; - goto foundk; - } - - cur.x = pt[k].x - pt[i].x; - cur.y = pt[k].y - pt[i].y; - - /* see if current constraint is violated */ - if (xprod(constraint[0], cur) < 0 || xprod(constraint[1], cur) > 0) { - goto constraint_viol; - } - - /* else, update constraint */ - if (abs(cur.x) <= 1 && abs(cur.y) <= 1) { - /* no constraint */ - } - else { - off.x = cur.x + ((cur.y >= 0 && (cur.y > 0 || cur.x < 0)) ? 1 : -1); - off.y = cur.y + ((cur.x <= 0 && (cur.x < 0 || cur.y < 0)) ? 1 : -1); - if (xprod(constraint[0], off) >= 0) { - constraint[0] = off; - } - off.x = cur.x + ((cur.y <= 0 && (cur.y < 0 || cur.x < 0)) ? 1 : -1); - off.y = cur.y + ((cur.x >= 0 && (cur.x > 0 || cur.y < 0)) ? 1 : -1); - if (xprod(constraint[1], off) <= 0) { - constraint[1] = off; - } - } - k1 = k; - k = nc[k1]; - if (!cyclic(k, i, k1)) { - break; - } - } - constraint_viol: - /* k1 was the last "corner" satisfying the current constraint, and - k is the first one violating it. We now need to find the last - point along k1..k which satisfied the constraint. */ - dk.x = sign(pt[k].x - pt[k1].x); - dk.y = sign(pt[k].y - pt[k1].y); - cur.x = pt[k1].x - pt[i].x; - cur.y = pt[k1].y - pt[i].y; - /* find largest integer j such that xprod(constraint[0], cur+j*dk) - >= 0 and xprod(constraint[1], cur+j*dk) <= 0. Use bilinearity - of xprod. */ - a = xprod(constraint[0], cur); - b = xprod(constraint[0], dk); - c = xprod(constraint[1], cur); - d = xprod(constraint[1], dk); - /* find largest integer j such that a+j*b>=0 and c+j*d<=0. This - can be solved with integer arithmetic. */ - j = INFTY; - if (b < 0) { - j = floordiv(a, -b); - } - if (d > 0) { - j = min(j, floordiv(-c, d)); - } - pivk[i] = mod(k1 + j, n); - foundk: - ; - } /* for i */ - - /* clean up: for each i, let lon[i] be the largest k such that for - all i' with i<=i'lon[n - 1] = j; - for (i = n - 2; i >= 0; i--) { - if (cyclic(i + 1, pivk[i], j)) { - j = pivk[i]; - } - pp->lon[i] = j; - } - - for (i = n - 1; cyclic(mod(i + 1, n), j, pp->lon[i]); i--) { - pp->lon[i] = j; - } - - free(pivk); - free(nc); - return 0; - -malloc_error: - free(pivk); - free(nc); - return 1; -} - - -/* ---------------------------------------------------------------------- */ -/* Stage 2: calculate the optimal polygon (Sec. 2.2.2-2.2.4). */ - -/* Auxiliary function: calculate the penalty of an edge from i to j in - the given path. This needs the "lon" and "sum*" data. */ - -static double penalty3(privpath_t * pp, int i, int j) -{ - int n = pp->len; - point_t *pt = pp->pt; - sums_t *sums = pp->sums; - - /* assume 0<=i= n) { - j -= n; - r += 1; - } - - x = sums[j + 1].x - sums[i].x + r * sums[n].x; - y = sums[j + 1].y - sums[i].y + r * sums[n].y; - x2 = sums[j + 1].x2 - sums[i].x2 + r * sums[n].x2; - xy = sums[j + 1].xy - sums[i].xy + r * sums[n].xy; - y2 = sums[j + 1].y2 - sums[i].y2 + r * sums[n].y2; - k = j + 1 - i + r * n; - - px = (pt[i].x + pt[j].x) / 2.0 - pt[0].x; - py = (pt[i].y + pt[j].y) / 2.0 - pt[0].y; - ey = (pt[j].x - pt[i].x); - ex = -(pt[j].y - pt[i].y); - - a = ((x2 - 2 * x * px) / k + px * px); - b = ((xy - x * py - y * px) / k + px * py); - c = ((y2 - 2 * y * py) / k + py * py); - - s = ex * ex * a + 2 * ex * ey * b + ey * ey * c; - - return sqrt(s); -} - -/* find the optimal polygon. Fill in the m and po components. Return 1 - on failure with errno set, else 0. Non-cyclic version: assumes i=0 - is in the polygon. Fixme: ### implement cyclic version. */ -static int bestpolygon(privpath_t * pp) -{ - int i, j, m, k; - int n = pp->len; - double *pen = NULL; /* pen[n+1]: penalty vector */ - int *prev = NULL; /* prev[n+1]: best path pointer vector */ - int *clip0 = NULL; /* clip0[n]: longest segment pointer, non-cyclic */ - int *clip1 = NULL; /* clip1[n+1]: backwards segment pointer, non-cyclic */ - int *seg0 = NULL; /* seg0[m+1]: forward segment bounds, m<=n */ - int *seg1 = NULL; /* seg1[m+1]: backward segment bounds, m<=n */ - double thispen; - double best; - int c; - - SAFE_MALLOC(pen, n + 1, double); - SAFE_MALLOC(prev, n + 1, int); - SAFE_MALLOC(clip0, n, int); - SAFE_MALLOC(clip1, n + 1, int); - SAFE_MALLOC(seg0, n + 1, int); - SAFE_MALLOC(seg1, n + 1, int); - - /* calculate clipped paths */ - for (i = 0; i < n; i++) { - c = mod(pp->lon[mod(i - 1, n)] - 1, n); - if (c == i) { - c = mod(i + 1, n); - } - if (c < i) { - clip0[i] = n; - } - else { - clip0[i] = c; - } - } - - /* calculate backwards path clipping, non-cyclic. j <= clip0[i] iff - clip1[j] <= i, for i,j=0..n. */ - j = 1; - for (i = 0; i < n; i++) { - while (j <= clip0[i]) { - clip1[j] = i; - j++; - } - } - - /* calculate seg0[j] = longest path from 0 with j segments */ - i = 0; - for (j = 0; i < n; j++) { - seg0[j] = i; - i = clip0[i]; - } - seg0[j] = n; - m = j; - - /* calculate seg1[j] = longest path to n with m-j segments */ - i = n; - for (j = m; j > 0; j--) { - seg1[j] = i; - i = clip1[i]; - } - seg1[0] = 0; - - /* now find the shortest path with m segments, based on penalty3 */ - /* note: the outer 2 loops jointly have at most n interations, thus - the worst-case behavior here is quadratic. In practice, it is - close to linear since the inner loop tends to be short. */ - pen[0] = 0; - for (j = 1; j <= m; j++) { - for (i = seg1[j]; i <= seg0[j]; i++) { - best = -1; - for (k = seg0[j - 1]; k >= clip1[i]; k--) { - thispen = penalty3(pp, k, i) + pen[k]; - if (best < 0 || thispen < best) { - prev[i] = k; - best = thispen; - } - } - pen[i] = best; - } - } - - pp->m = m; - SAFE_MALLOC(pp->po, m, int); - - /* read off shortest path */ - for (i = n, j = m - 1; i > 0; j--) { - i = prev[i]; - pp->po[j] = i; - } - - free(pen); - free(prev); - free(clip0); - free(clip1); - free(seg0); - free(seg1); - return 0; - -malloc_error: - free(pen); - free(prev); - free(clip0); - free(clip1); - free(seg0); - free(seg1); - return 1; -} - -/* ---------------------------------------------------------------------- */ -/* Stage 3: vertex adjustment (Sec. 2.3.1). */ - -/* Adjust vertices of optimal polygon: calculate the intersection of - the two "optimal" line segments, then move it into the unit square - if it lies outside. Return 1 with errno set on error; 0 on - success. */ - -static int adjust_vertices(privpath_t * pp) -{ - int m = pp->m; - int *po = pp->po; - int n = pp->len; - point_t *pt = pp->pt; - int x0 = pp->x0; - int y0 = pp->y0; - - dpoint_t *ctr = NULL; /* ctr[m] */ - dpoint_t *dir = NULL; /* dir[m] */ - quadform_t *q = NULL; /* q[m] */ - double v[3]; - double d; - int i, j, k, l; - dpoint_t s; - int r; - - SAFE_MALLOC(ctr, m, dpoint_t); - SAFE_MALLOC(dir, m, dpoint_t); - SAFE_MALLOC(q, m, quadform_t); - - r = privcurve_init(&pp->curve, m); - if (r) { - goto malloc_error; - } - - /* calculate "optimal" point-slope representation for each line - segment */ - for (i = 0; i < m; i++) { - j = po[mod(i + 1, m)]; - j = mod(j - po[i], n) + po[i]; - pointslope(pp, po[i], j, &ctr[i], &dir[i]); - } - - /* represent each line segment as a singular quadratic form; the - distance of a point (x,y) from the line segment will be - (x,y,1)Q(x,y,1)^t, where Q=q[i]. */ - for (i = 0; i < m; i++) { - d = sq(dir[i].x) + sq(dir[i].y); - if (d == 0.0) { - for (j = 0; j < 3; j++) { - for (k = 0; k < 3; k++) { - q[i][j][k] = 0; - } - } - } - else { - v[0] = dir[i].y; - v[1] = -dir[i].x; - v[2] = -v[1] * ctr[i].y - v[0] * ctr[i].x; - for (l = 0; l < 3; l++) { - for (k = 0; k < 3; k++) { - q[i][l][k] = v[l] * v[k] / d; - } - } - } - } - - /* now calculate the "intersections" of consecutive segments. - Instead of using the actual intersection, we find the point - within a given unit square which minimizes the square distance to - the two lines. */ - for (i = 0; i < m; i++) { - quadform_t Q; - dpoint_t w; - double dx, dy; - double det; - double min, cand; /* minimum and candidate for minimum of quad. form */ - double xmin, ymin; /* coordinates of minimum */ - int z; - - /* let s be the vertex, in coordinates relative to x0/y0 */ - s.x = pt[po[i]].x - x0; - s.y = pt[po[i]].y - y0; - - /* intersect segments i-1 and i */ - - j = mod(i - 1, m); - - /* add quadratic forms */ - for (l = 0; l < 3; l++) { - for (k = 0; k < 3; k++) { - Q[l][k] = q[j][l][k] + q[i][l][k]; - } - } - - while (1) { - /* minimize the quadratic form Q on the unit square */ - /* find intersection */ - -#ifdef HAVE_GCC_LOOP_BUG - /* work around gcc bug #12243 */ - free(NULL); -#endif - - det = Q[0][0] * Q[1][1] - Q[0][1] * Q[1][0]; - if (det != 0.0) { - w.x = (-Q[0][2] * Q[1][1] + Q[1][2] * Q[0][1]) / det; - w.y = (Q[0][2] * Q[1][0] - Q[1][2] * Q[0][0]) / det; - break; - } - - /* matrix is singular - lines are parallel. Add another, - orthogonal axis, through the center of the unit square */ - if (Q[0][0] > Q[1][1]) { - v[0] = -Q[0][1]; - v[1] = Q[0][0]; - } - else if (Q[1][1]) { - v[0] = -Q[1][1]; - v[1] = Q[1][0]; - } - else { - v[0] = 1; - v[1] = 0; - } - d = sq(v[0]) + sq(v[1]); - v[2] = -v[1] * s.y - v[0] * s.x; - for (l = 0; l < 3; l++) { - for (k = 0; k < 3; k++) { - Q[l][k] += v[l] * v[k] / d; - } - } - } - dx = fabs(w.x - s.x); - dy = fabs(w.y - s.y); - if (dx <= .5 && dy <= .5) { - pp->curve.vertex[i].x = w.x + x0; - pp->curve.vertex[i].y = w.y + y0; - continue; - } - - /* the minimum was not in the unit square; now minimize quadratic - on boundary of square */ - min = quadform(Q, s); - xmin = s.x; - ymin = s.y; - - if (Q[0][0] == 0.0) { - goto fixx; - } - for (z = 0; z < 2; z++) { /* value of the y-coordinate */ - w.y = s.y - 0.5 + z; - w.x = -(Q[0][1] * w.y + Q[0][2]) / Q[0][0]; - dx = fabs(w.x - s.x); - cand = quadform(Q, w); - if (dx <= .5 && cand < min) { - min = cand; - xmin = w.x; - ymin = w.y; - } - } - fixx: - if (Q[1][1] == 0.0) { - goto corners; - } - for (z = 0; z < 2; z++) { /* value of the x-coordinate */ - w.x = s.x - 0.5 + z; - w.y = -(Q[1][0] * w.x + Q[1][2]) / Q[1][1]; - dy = fabs(w.y - s.y); - cand = quadform(Q, w); - if (dy <= .5 && cand < min) { - min = cand; - xmin = w.x; - ymin = w.y; - } - } - corners: - /* check four corners */ - for (l = 0; l < 2; l++) { - for (k = 0; k < 2; k++) { - w.x = s.x - 0.5 + l; - w.y = s.y - 0.5 + k; - cand = quadform(Q, w); - if (cand < min) { - min = cand; - xmin = w.x; - ymin = w.y; - } - } - } - - pp->curve.vertex[i].x = xmin + x0; - pp->curve.vertex[i].y = ymin + y0; - continue; - } - - free(ctr); - free(dir); - free(q); - return 0; - -malloc_error: - free(ctr); - free(dir); - free(q); - return 1; -} - -/* ---------------------------------------------------------------------- */ -/* Stage 4: smoothing and corner analysis (Sec. 2.3.3) */ - -/* Always succeeds and returns 0 */ -static int -ATTRIBUTE_UNUSED smooth(privcurve_t * curve, int sign, double alphamax) -{ - int m = curve->n; - - int i, j, k; - double dd, denom, alpha; - dpoint_t p2, p3, p4; - - if (sign == '-') { - /* reverse orientation of negative paths */ - for (i = 0, j = m - 1; i < j; i++, j--) { - dpoint_t tmp; - tmp = curve->vertex[i]; - curve->vertex[i] = curve->vertex[j]; - curve->vertex[j] = tmp; - } - } - - /* examine each vertex and find its best fit */ - for (i = 0; i < m; i++) { - j = mod(i + 1, m); - k = mod(i + 2, m); - p4 = interval(1 / 2.0, curve->vertex[k], curve->vertex[j]); - - denom = ddenom(curve->vertex[i], curve->vertex[k]); - if (denom != 0.0) { - dd = dpara(curve->vertex[i], curve->vertex[j], curve->vertex[k]) / denom; - dd = fabs(dd); - alpha = dd > 1 ? (1 - 1.0 / dd) : 0; - alpha = alpha / 0.75; - } - else { - alpha = 4 / 3.0; - } - curve->alpha0[j] = alpha; /* remember "original" value of alpha */ - - if (alpha > alphamax) { /* pointed corner */ - curve->tag[j] = POTRACE_CORNER; - curve->c[j][1] = curve->vertex[j]; - curve->c[j][2] = p4; - } - else { - if (alpha < 0.55) { - alpha = 0.55; - } - else if (alpha > 1) { - alpha = 1; - } - p2 = interval(.5 + .5 * alpha, curve->vertex[i], curve->vertex[j]); - p3 = interval(.5 + .5 * alpha, curve->vertex[k], curve->vertex[j]); - curve->tag[j] = POTRACE_CURVETO; - curve->c[j][0] = p2; - curve->c[j][1] = p3; - curve->c[j][2] = p4; - } - curve->alpha[j] = alpha; /* store the "cropped" value of alpha */ - curve->beta[j] = 0.5; - } - curve->alphacurve = 1; - - return 0; -} - -/* ---------------------------------------------------------------------- */ -/* Stage 5: Curve optimization (Sec. 2.4) */ - -/* a private type for the result of opti_penalty */ -struct opti_s { - double pen; /* penalty */ - dpoint_t c[2]; /* curve parameters */ - double t, s; /* curve parameters */ - double alpha; /* curve parameter */ -}; -typedef struct opti_s opti_t; - -/* calculate best fit from i+.5 to j+.5. Assume icurve.n; - int k, k1, k2, conv, i1; - double area, alpha, d, d1, d2; - dpoint_t p0, p1, p2, p3, pt; - double A, R, A1, A2, A3, A4; - double s, t; - - /* check convexity, corner-freeness, and maximum bend < 179 degrees */ - - if (i == j) { /* sanity - a full loop can never be an opticurve */ - return 1; - } - - k = i; - i1 = mod(i + 1, m); - k1 = mod(k + 1, m); - conv = convc[k1]; - if (conv == 0) { - return 1; - } - d = ddist(pp->curve.vertex[i], pp->curve.vertex[i1]); - for (k = k1; k != j; k = k1) { - k1 = mod(k + 1, m); - k2 = mod(k + 2, m); - if (convc[k1] != conv) { - return 1; - } - if (sign(cprod(pp->curve.vertex[i], pp->curve.vertex[i1], pp->curve.vertex[k1], pp->curve.vertex[k2])) != conv) { - return 1; - } - if (iprod1 - (pp->curve.vertex[i], pp->curve.vertex[i1], pp->curve.vertex[k1], - pp->curve.vertex[k2]) < d * ddist(pp->curve.vertex[k1], pp->curve.vertex[k2]) * COS179) { - return 1; - } - } - - /* the curve we're working in: */ - p0 = pp->curve.c[mod(i, m)][2]; - p1 = pp->curve.vertex[mod(i + 1, m)]; - p2 = pp->curve.vertex[mod(j, m)]; - p3 = pp->curve.c[mod(j, m)][2]; - - /* determine its area */ - area = areac[j] - areac[i]; - area -= dpara(pp->curve.vertex[0], pp->curve.c[i][2], pp->curve.c[j][2]) / 2; - if (i >= j) { - area += areac[m]; - } - - /* find intersection o of p0p1 and p2p3. Let t,s such that o = - interval(t,p0,p1) = interval(s,p3,p2). Let A be the area of the - triangle (p0,o,p3). */ - - A1 = dpara(p0, p1, p2); - A2 = dpara(p0, p1, p3); - A3 = dpara(p0, p2, p3); - /* A4 = dpara(p1, p2, p3); */ - A4 = A1 + A3 - A2; - - if (A2 == A1) { /* this should never happen */ - return 1; - } - - t = A3 / (A3 - A4); - s = A2 / (A2 - A1); - A = A2 * t / 2.0; - - if (A == 0.0) { /* this should never happen */ - return 1; - } - - R = area / A; /* relative area */ - alpha = 2 - sqrt(4 - R / 0.3); /* overall alpha for p0-o-p3 curve */ - - res->c[0] = interval(t * alpha, p0, p1); - res->c[1] = interval(s * alpha, p3, p2); - res->alpha = alpha; - res->t = t; - res->s = s; - - p1 = res->c[0]; - p2 = res->c[1]; /* the proposed curve is now (p0,p1,p2,p3) */ - - res->pen = 0; - - /* calculate penalty */ - /* check tangency with edges */ - for (k = mod(i + 1, m); k != j; k = k1) { - k1 = mod(k + 1, m); - t = tangent(p0, p1, p2, p3, pp->curve.vertex[k], pp->curve.vertex[k1]); - if (t < -.5) { - return 1; - } - pt = bezier(t, p0, p1, p2, p3); - d = ddist(pp->curve.vertex[k], pp->curve.vertex[k1]); - if (d == 0.0) { /* this should never happen */ - return 1; - } - d1 = dpara(pp->curve.vertex[k], pp->curve.vertex[k1], pt) / d; - if (fabs(d1) > opttolerance) { - return 1; - } - if (iprod(pp->curve.vertex[k], pp->curve.vertex[k1], pt) < 0 || iprod(pp->curve.vertex[k1], pp->curve.vertex[k], pt) < 0) { - return 1; - } - res->pen += sq(d1); - } - - /* check corners */ - for (k = i; k != j; k = k1) { - k1 = mod(k + 1, m); - t = tangent(p0, p1, p2, p3, pp->curve.c[k][2], pp->curve.c[k1][2]); - if (t < -.5) { - return 1; - } - pt = bezier(t, p0, p1, p2, p3); - d = ddist(pp->curve.c[k][2], pp->curve.c[k1][2]); - if (d == 0.0) { /* this should never happen */ - return 1; - } - d1 = dpara(pp->curve.c[k][2], pp->curve.c[k1][2], pt) / d; - d2 = dpara(pp->curve.c[k][2], pp->curve.c[k1][2], pp->curve.vertex[k1]) / d; - d2 *= 0.75 * pp->curve.alpha[k1]; - if (d2 < 0) { - d1 = -d1; - d2 = -d2; - } - if (d1 < d2 - opttolerance) { - return 1; - } - if (d1 < d2) { - res->pen += sq(d1 - d2); - } - } - - return 0; -} - -/* optimize the path p, replacing sequences of Bezier segments by a - single segment when possible. Return 0 on success, 1 with errno set - on failure. */ -static int -ATTRIBUTE_UNUSED opticurve(privpath_t * pp, double opttolerance) -{ - int m = pp->curve.n; - int *pt = NULL; /* pt[m+1] */ - double *pen = NULL; /* pen[m+1] */ - int *len = NULL; /* len[m+1] */ - opti_t *opt = NULL; /* opt[m+1] */ - int om; - int i, j, r; - opti_t o; - dpoint_t p0; - int i1; - double area; - double alpha; - double *s = NULL; - double *t = NULL; - - int *convc = NULL; /* conv[m]: pre-computed convexities */ - double *areac = NULL; /* cumarea[m+1]: cache for fast area computation */ - - SAFE_MALLOC(pt, m + 1, int); - SAFE_MALLOC(pen, m + 1, double); - SAFE_MALLOC(len, m + 1, int); - SAFE_MALLOC(opt, m + 1, opti_t); - SAFE_MALLOC(convc, m, int); - SAFE_MALLOC(areac, m + 1, double); - - /* pre-calculate convexity: +1 = right turn, -1 = left turn, 0 = corner */ - for (i = 0; i < m; i++) { - if (pp->curve.tag[i] == POTRACE_CURVETO) { - convc[i] = sign(dpara(pp->curve.vertex[mod(i - 1, m)], pp->curve.vertex[i], pp->curve.vertex[mod(i + 1, m)])); - } - else { - convc[i] = 0; - } - } - - /* pre-calculate areas */ - area = 0.0; - areac[0] = 0.0; - p0 = pp->curve.vertex[0]; - for (i = 0; i < m; i++) { - i1 = mod(i + 1, m); - if (pp->curve.tag[i1] == POTRACE_CURVETO) { - alpha = pp->curve.alpha[i1]; - area += 0.3 * alpha * (4 - alpha) * dpara(pp->curve.c[i][2], pp->curve.vertex[i1], pp->curve.c[i1][2]) / 2; - area += dpara(p0, pp->curve.c[i][2], pp->curve.c[i1][2]) / 2; - } - areac[i + 1] = area; - } - - pt[0] = -1; - pen[0] = 0; - len[0] = 0; - - /* Fixme: we always start from a fixed point -- should find the best - curve cyclically ### */ - - for (j = 1; j <= m; j++) { - /* calculate best path from 0 to j */ - pt[j] = j - 1; - pen[j] = pen[j - 1]; - len[j] = len[j - 1] + 1; - - for (i = j - 2; i >= 0; i--) { - r = opti_penalty(pp, i, mod(j, m), &o, opttolerance, convc, areac); - if (r) { - break; - } - if (len[j] > len[i] + 1 || (len[j] == len[i] + 1 && pen[j] > pen[i] + o.pen)) { - pt[j] = i; - pen[j] = pen[i] + o.pen; - len[j] = len[i] + 1; - opt[j] = o; - } - } - } - om = len[m]; - r = privcurve_init(&pp->ocurve, om); - if (r) { - goto malloc_error; - } - SAFE_MALLOC(s, om, double); - SAFE_MALLOC(t, om, double); - - j = m; - for (i = om - 1; i >= 0; i--) { - if (pt[j] == j - 1) { - pp->ocurve.tag[i] = pp->curve.tag[mod(j, m)]; - pp->ocurve.c[i][0] = pp->curve.c[mod(j, m)][0]; - pp->ocurve.c[i][1] = pp->curve.c[mod(j, m)][1]; - pp->ocurve.c[i][2] = pp->curve.c[mod(j, m)][2]; - pp->ocurve.vertex[i] = pp->curve.vertex[mod(j, m)]; - pp->ocurve.alpha[i] = pp->curve.alpha[mod(j, m)]; - pp->ocurve.alpha0[i] = pp->curve.alpha0[mod(j, m)]; - pp->ocurve.beta[i] = pp->curve.beta[mod(j, m)]; - s[i] = t[i] = 1.0; - } - else { - pp->ocurve.tag[i] = POTRACE_CURVETO; - pp->ocurve.c[i][0] = opt[j].c[0]; - pp->ocurve.c[i][1] = opt[j].c[1]; - pp->ocurve.c[i][2] = pp->curve.c[mod(j, m)][2]; - pp->ocurve.vertex[i] = interval(opt[j].s, pp->curve.c[mod(j, m)][2], pp->curve.vertex[mod(j, m)]); - pp->ocurve.alpha[i] = opt[j].alpha; - pp->ocurve.alpha0[i] = opt[j].alpha; - s[i] = opt[j].s; - t[i] = opt[j].t; - } - j = pt[j]; - } - - /* calculate beta parameters */ - for (i = 0; i < om; i++) { - i1 = mod(i + 1, om); - pp->ocurve.beta[i] = s[i] / (s[i] + t[i1]); - } - pp->ocurve.alphacurve = 1; - - free(pt); - free(pen); - free(len); - free(opt); - free(s); - free(t); - free(convc); - free(areac); - return 0; - -malloc_error: - free(pt); - free(pen); - free(len); - free(opt); - free(s); - free(t); - free(convc); - free(areac); - return 1; -} - -/* ---------------------------------------------------------------------- */ -double plotpolygon(privpath_t * pp, FILE * f, double scale) -{ - int i; - int m = pp->m; - int *po = pp->po; - point_t *pt = pp->pt; - /* double scale=1.0/dpi; */ - double dm = 0; - - if (!m) - return 0; - - po = pp->po; - pt = pp->pt; - - fprintf(f, "G0 X%f Y%f (start point)\n", pt[po[0]].x * scale, pt[po[0]].y * scale); - fprintf(f, "G1 Z#101\n"); - for (i = 1; i < m; i++) { - fprintf(f, "G1 X%f Y%f\n", pt[po[i]].x * scale, pt[po[i]].y * scale); - dm += - sqrt((pt[po[i]].x - pt[po[i - 1]].x) * scale * (pt[po[i]].x - - pt[po[i - 1]].x) * - scale + (pt[po[i]].y - pt[po[i - 1]].y) * scale * (pt[po[i]].y - pt[po[i - 1]].y) * scale); - } - fprintf(f, "G1 X%f Y%f\n", pt[po[0]].x * scale, pt[po[0]].y * scale); - fprintf(f, "G0 Z#100\n"); - dm += - sqrt((pt[po[m - 1]].x - pt[po[0]].x) * scale * (pt[po[m - 1]].x - - pt[po[0]].x) * scale + - (pt[po[m - 1]].y - pt[po[0]].y) * scale * (pt[po[m - 1]].y - pt[po[0]].y) * scale); - fprintf(f, "(polygon end, distance %.2f)\n", dm); - return dm; -} - -#define TRY(x) if (x) goto try_error - -/* return distance on success, -1 on error with errno set. */ -double process_path(path_t * plist, const potrace_param_t * param, const potrace_bitmap_t * bm, FILE * f, double scale) -{ - path_t *p; - double dm = 0; - int n = 0; - /* call downstream function with each path */ - list_forall(p, plist) { - TRY(calc_sums(p->priv)); - TRY(calc_lon(p->priv)); - TRY(bestpolygon(p->priv)); - TRY(adjust_vertices(p->priv)); - fprintf(f, "(polygon %d)\n", ++n); - dm += plotpolygon(p->priv, f, scale); -/* No need to extract curves - TRY(smooth(&p->priv->curve, p->sign, param->alphamax)); - if (param->opticurve) { - TRY(opticurve(p->priv, param->opttolerance)); - p->priv->fcurve = &p->priv->ocurve; - } else { - p->priv->fcurve = &p->priv->curve; - } - privcurve_to_curve(p->priv->fcurve, &p->curve);*/ - } -/* fprintf(f,"(end, total distance %.2fmm = %.2fin)\n",25.4*dm,dm); */ - return dm; - -try_error: - return -1; -} Index: trunk/src/hid/gcode/decompose.c =================================================================== --- trunk/src/hid/gcode/decompose.c (revision 1268) +++ trunk/src/hid/gcode/decompose.c (nonexistent) @@ -1,525 +0,0 @@ -/* Copyright (C) 2001-2007 Peter Selinger. - This file is part of Potrace. It is free software and it is covered - by the GNU General Public License. See the file COPYING for details. */ - -/* $Id: decompose.c 146 2007-04-09 00:43:46Z selinger $ */ - -#include -#include -#include -#include - -#include "potracelib.h" -#include "curve.h" -#include "lists.h" -#include "auxiliary.h" -#include "bitmap.h" -#include "decompose.h" -/*#include "progress.h"*/ - -/* ---------------------------------------------------------------------- */ -/* auxiliary bitmap manipulations */ - -/* set the excess padding to 0 */ -static void bm_clearexcess(potrace_bitmap_t * bm) -{ - potrace_word mask; - int y; - - if (bm->w % BM_WORDBITS != 0) { - mask = BM_ALLBITS << (BM_WORDBITS - (bm->w % BM_WORDBITS)); - for (y = 0; y < bm->h; y++) { - *bm_index(bm, bm->w, y) &= mask; - } - } -} - -struct bbox_s { - int x0, x1, y0, y1; /* bounding box */ -}; -typedef struct bbox_s bbox_t; - -/* clear the bm, assuming the bounding box is set correctly (faster - than clearing the whole bitmap) */ -static void clear_bm_with_bbox(potrace_bitmap_t * bm, bbox_t * bbox) -{ - int imin = (bbox->x0 / BM_WORDBITS); - int imax = ((bbox->x1 + BM_WORDBITS - 1) / BM_WORDBITS); - int i, y; - - for (y = bbox->y0; y < bbox->y1; y++) { - for (i = imin; i < imax; i++) { - bm_scanline(bm, y)[i] = 0; - } - } -} - -/* ---------------------------------------------------------------------- */ -/* auxiliary functions */ - -/* deterministically and efficiently hash (x,y) into a pseudo-random bit */ -static inline int detrand(int x, int y) -{ - unsigned int z; - static const unsigned char t[256] = { - /* non-linear sequence: constant term of inverse in GF(8), - mod x^8+x^4+x^3+x+1 */ - 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, - 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, - 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, - 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, - 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, - 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, - 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, - 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, - 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, - 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, - 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, - }; - - /* 0x04b3e375 and 0x05a8ef93 are chosen to contain every possible - 5-bit sequence */ - z = ((0x04b3e375 * x) ^ y) * 0x05a8ef93; - z = t[z & 0xff] ^ t[(z >> 8) & 0xff] ^ t[(z >> 16) & 0xff] ^ t[(z >> 24) & 0xff]; - return z & 1; -} - -/* return the "majority" value of bitmap bm at intersection (x,y). We - assume that the bitmap is balanced at "radius" 1. */ -static int majority(potrace_bitmap_t * bm, int x, int y) -{ - int i, a, ct; - - for (i = 2; i < 5; i++) { /* check at "radius" i */ - ct = 0; - for (a = -i + 1; a <= i - 1; a++) { - ct += BM_GET(bm, x + a, y + i - 1) ? 1 : -1; - ct += BM_GET(bm, x + i - 1, y + a - 1) ? 1 : -1; - ct += BM_GET(bm, x + a - 1, y - i) ? 1 : -1; - ct += BM_GET(bm, x - i, y + a) ? 1 : -1; - } - if (ct > 0) { - return 1; - } - else if (ct < 0) { - return 0; - } - } - return 0; -} - -/* ---------------------------------------------------------------------- */ -/* decompose image into paths */ - -/* efficiently invert bits [x,infty) and [xa,infty) in line y. Here xa - must be a multiple of BM_WORDBITS. */ -static void xor_to_ref(potrace_bitmap_t * bm, int x, int y, int xa) -{ - int xhi = x & -BM_WORDBITS; - int xlo = x & (BM_WORDBITS - 1); /* = x % BM_WORDBITS */ - int i; - - if (xhi < xa) { - for (i = xhi; i < xa; i += BM_WORDBITS) { - *bm_index(bm, i, y) ^= BM_ALLBITS; - } - } - else { - for (i = xa; i < xhi; i += BM_WORDBITS) { - *bm_index(bm, i, y) ^= BM_ALLBITS; - } - } - /* note: the following "if" is needed because x86 treats a<priv->len <= 0) { /* a path of length 0 is silly, but legal */ - return; - } - - y1 = p->priv->pt[p->priv->len - 1].y; - - xa = p->priv->pt[0].x & -BM_WORDBITS; - for (k = 0; k < p->priv->len; k++) { - x = p->priv->pt[k].x; - y = p->priv->pt[k].y; - - if (y != y1) { - /* efficiently invert the rectangle [x,xa] x [y,y1] */ - xor_to_ref(bm, x, min(y, y1), xa); - y1 = y; - } - } -} - -/* Find the bounding box of a given path. Path is assumed to be of - non-zero length. */ -static void setbbox_path(bbox_t * bbox, path_t * p) -{ - int x, y; - int k; - - bbox->y0 = INT_MAX; - bbox->y1 = 0; - bbox->x0 = INT_MAX; - bbox->x1 = 0; - - for (k = 0; k < p->priv->len; k++) { - x = p->priv->pt[k].x; - y = p->priv->pt[k].y; - - if (x < bbox->x0) { - bbox->x0 = x; - } - if (x > bbox->x1) { - bbox->x1 = x; - } - if (y < bbox->y0) { - bbox->y0 = y; - } - if (y > bbox->y1) { - bbox->y1 = y; - } - } -} - -/* compute a path in the given pixmap, separating black from white. - Start path at the point (x0,x1), which must be an upper left corner - of the path. Also compute the area enclosed by the path. Return a - new path_t object, or NULL on error (note that a legitimate path - cannot have length 0). Sign is required for correct interpretation - of turnpolicies. */ -static path_t *findpath(potrace_bitmap_t * bm, int x0, int y0, int sign, int turnpolicy) -{ - int x, y, dirx, diry, len, size, area; - int c, d, tmp; - point_t *pt, *pt1; - path_t *p = NULL; - - x = x0; - y = y0; - dirx = 0; - diry = -1; - - len = size = 0; - pt = NULL; - area = 0; - - while (1) { - /* add point to path */ - if (len >= size) { - size += 100; - size = (int) (1.3 * size); - pt1 = (point_t *) realloc(pt, size * sizeof(point_t)); - if (!pt1) { - goto error; - } - pt = pt1; - } - pt[len].x = x; - pt[len].y = y; - len++; - - /* move to next point */ - x += dirx; - y += diry; - area += x * diry; - - /* path complete? */ - if (x == x0 && y == y0) { - break; - } - - /* determine next direction */ - c = BM_GET(bm, x + (dirx + diry - 1) / 2, y + (diry - dirx - 1) / 2); - d = BM_GET(bm, x + (dirx - diry - 1) / 2, y + (diry + dirx - 1) / 2); - - if (c && !d) { /* ambiguous turn */ - if (turnpolicy == POTRACE_TURNPOLICY_RIGHT || (turnpolicy == POTRACE_TURNPOLICY_BLACK && sign == '+') - || (turnpolicy == POTRACE_TURNPOLICY_WHITE && sign == '-') - || (turnpolicy == POTRACE_TURNPOLICY_RANDOM && detrand(x, y)) - || (turnpolicy == POTRACE_TURNPOLICY_MAJORITY && majority(bm, x, y)) - || (turnpolicy == POTRACE_TURNPOLICY_MINORITY && !majority(bm, x, y))) { - tmp = dirx; /* right turn */ - dirx = diry; - diry = -tmp; - } - else { - tmp = dirx; /* left turn */ - dirx = -diry; - diry = tmp; - } - } - else if (c) { /* right turn */ - tmp = dirx; - dirx = diry; - diry = -tmp; - } - else if (!d) { /* left turn */ - tmp = dirx; - dirx = -diry; - diry = tmp; - } - } /* while this path */ - - /* allocate new path object */ - p = path_new(); - if (!p) { - goto error; - } - - p->priv->pt = pt; - p->priv->len = len; - p->area = area; - p->sign = sign; - - return p; - -error: - free(pt); - return NULL; -} - -/* Give a tree structure to the given path list, based on "insideness" - testing. I.e., path A is considered "below" path B if it is inside - path B. The input pathlist is assumed to be ordered so that "outer" - paths occur before "inner" paths. The tree structure is stored in - the "childlist" and "sibling" components of the path_t - structure. The linked list structure is also changed so that - negative path components are listed immediately after their - positive parent. Note: some backends may ignore the tree - structure, others may use it e.g. to group path components. We - assume that in the input, point 0 of each path is an "upper left" - corner of the path, as returned by bm_to_pathlist. This makes it - easy to find an "interior" point. The bm argument should be a - bitmap of the correct size (large enough to hold all the paths), - and will be used as scratch space. Return 0 on success or -1 on - error with errno set. */ - -static void pathlist_to_tree(path_t * plist, potrace_bitmap_t * bm) -{ - path_t *p, *p1; - path_t *heap, *heap1; - path_t *cur; - path_t *head; - path_t **hook, **hook_in, **hook_out; /* for fast appending to linked list */ - bbox_t bbox; - - bm_clear(bm, 0); - - /* save original "next" pointers */ - list_forall(p, plist) { - p->sibling = p->next; - p->childlist = NULL; - } - - heap = plist; - - /* the heap holds a list of lists of paths. Use "childlist" field - for outer list, "next" field for inner list. Each of the sublists - is to be turned into a tree. This code is messy, but it is - actually fast. Each path is rendered exactly once. We use the - heap to get a tail recursive algorithm: the heap holds a list of - pathlists which still need to be transformed. */ - - while (heap) { - /* unlink first sublist */ - cur = heap; - heap = heap->childlist; - cur->childlist = NULL; - - /* unlink first path */ - head = cur; - cur = cur->next; - head->next = NULL; - - /* render path */ - xor_path(bm, head); - setbbox_path(&bbox, head); - - /* now do insideness test for each element of cur; append it to - head->childlist if it's inside head, else append it to - head->next. */ - hook_in = &head->childlist; - hook_out = &head->next; - list_forall_unlink(p, cur) { - if (p->priv->pt[0].y <= bbox.y0) { - list_insert_beforehook(p, hook_out); - /* append the remainder of the list to hook_out */ - *hook_out = cur; - break; - } - if (BM_GET(bm, p->priv->pt[0].x, p->priv->pt[0].y - 1)) { - list_insert_beforehook(p, hook_in); - } - else { - list_insert_beforehook(p, hook_out); - } - } - - /* clear bm */ - clear_bm_with_bbox(bm, &bbox); - - /* now schedule head->childlist and head->next for further - processing */ - if (head->next) { - head->next->childlist = heap; - heap = head->next; - } - if (head->childlist) { - head->childlist->childlist = heap; - heap = head->childlist; - } - } - - /* copy sibling structure from "next" to "sibling" component */ - p = plist; - while (p) { - p1 = p->sibling; - p->sibling = p->next; - p = p1; - } - - /* reconstruct a new linked list ("next") structure from tree - ("childlist", "sibling") structure. This code is slightly messy, - because we use a heap to make it tail recursive: the heap - contains a list of childlists which still need to be - processed. */ - heap = plist; - if (heap) { - heap->next = NULL; /* heap is a linked list of childlists */ - } - plist = NULL; - hook = &plist; - while (heap) { - heap1 = heap->next; - for (p = heap; p; p = p->sibling) { - /* p is a positive path */ - /* append to linked list */ - list_insert_beforehook(p, hook); - - /* go through its children */ - for (p1 = p->childlist; p1; p1 = p1->sibling) { - /* append to linked list */ - list_insert_beforehook(p1, hook); - /* append its childlist to heap, if non-empty */ - if (p1->childlist) { - list_append(path_t, heap1, p1->childlist); - } - } - } - heap = heap1; - } - - return; -} - -/* find the next set pixel in a row <= y. Pixels are searched first - left-to-right, then top-down. In other words, (x,y)<(x',y') if y>y' - or y=y' and x= 0; y--) { - for (x = 0; x < bm->w; x += BM_WORDBITS) { - if (*bm_index(bm, x, y)) { - while (!BM_GET(bm, x, y)) { - x++; - } - /* found */ - *xp = x; - *yp = y; - return 0; - } - } - } - /* not found */ - return 1; -} - -/* Decompose the given bitmap into paths. Returns a linked list of - path_t objects with the fields len, pt, area, sign filled - in. Returns 0 on success with plistp set, or -1 on error with errno - set. */ - -int bm_to_pathlist(const potrace_bitmap_t * bm, path_t ** plistp, const potrace_param_t * param) -{ - int x; - int y; - path_t *p; - path_t *plist = NULL; /* linked list of path objects */ - path_t **hook = &plist; /* used to speed up appending to linked list */ - potrace_bitmap_t *bm1 = NULL; - int sign; - - bm1 = bm_dup(bm); - if (!bm1) { - goto error; - } - - /* be sure the byte padding on the right is set to 0, as the fast - pixel search below relies on it */ - bm_clearexcess(bm1); - - /* iterate through components */ - y = bm1->h - 1; - while (findnext(bm1, &x, &y) == 0) { - /* calculate the sign by looking at the original */ - sign = BM_GET(bm, x, y) ? '+' : '-'; - - /* calculate the path */ - p = findpath(bm1, x, y + 1, sign, param->turnpolicy); - if (p == NULL) { - goto error; - } - - /* update buffered image */ - xor_path(bm1, p); - - /* if it's a turd, eliminate it, else append it to the list */ - if (p->area <= param->turdsize) { - path_free(p); - } - else { - list_insert_beforehook(p, hook); - } - - if (bm1->h > 0) { /* to be sure */ - /*progress_update(1-y/(double)bm1->h, progress); */ - } - } - - pathlist_to_tree(plist, bm1); - bm_free(bm1); - *plistp = plist; - -/* progress_update(1.0, progress);*/ - - return 0; - -error: - bm_free(bm1); - list_forall_unlink(p, plist) { - path_free(p); - } - return -1; -} Index: trunk/src/hid/gcode/bitmap.h =================================================================== --- trunk/src/hid/gcode/bitmap.h (revision 1268) +++ trunk/src/hid/gcode/bitmap.h (nonexistent) @@ -1,104 +0,0 @@ -/* Copyright (C) 2001-2007 Peter Selinger. - This file is part of Potrace. It is free software and it is covered - by the GNU General Public License. See the file COPYING for details. */ - -#ifndef BITMAP_H -#define BITMAP_H - -#include "config.h" - -#include -#include - -/* The bitmap type is defined in potracelib.h */ -#include "potracelib.h" - -/* The present file defines some convenient macros and static inline - functions for accessing bitmaps. Since they only produce inline - code, they can be conveniently shared by the library and frontends, - if desired */ - -/* ---------------------------------------------------------------------- */ -/* some measurements */ - -#define BM_WORDSIZE ((int)sizeof(potrace_word)) -#define BM_WORDBITS (8*BM_WORDSIZE) -#define BM_HIBIT (((potrace_word)1)<<(BM_WORDBITS-1)) -#define BM_ALLBITS (~(potrace_word)0) - -/* macros for accessing pixel at index (x,y). U* macros omit the - bounds check. */ - -#define bm_scanline(bm, y) ((bm)->map + (y)*(bm)->dy) -#define bm_index(bm, x, y) (&bm_scanline(bm, y)[(x)/BM_WORDBITS]) -#define bm_mask(x) (BM_HIBIT >> ((x) & (BM_WORDBITS-1))) -#define bm_range(x, a) ((int)(x) >= 0 && (int)(x) < (a)) -#define bm_safe(bm, x, y) (bm_range(x, (bm)->w) && bm_range(y, (bm)->h)) -#define BM_UGET(bm, x, y) ((*bm_index(bm, x, y) & bm_mask(x)) != 0) -#define BM_USET(bm, x, y) (*bm_index(bm, x, y) |= bm_mask(x)) -#define BM_UCLR(bm, x, y) (*bm_index(bm, x, y) &= ~bm_mask(x)) -#define BM_UINV(bm, x, y) (*bm_index(bm, x, y) ^= bm_mask(x)) -#define BM_UPUT(bm, x, y, b) ((b) ? BM_USET(bm, x, y) : BM_UCLR(bm, x, y)) -#define BM_GET(bm, x, y) (bm_safe(bm, x, y) ? BM_UGET(bm, x, y) : 0) -#define BM_SET(bm, x, y) (bm_safe(bm, x, y) ? BM_USET(bm, x, y) : 0) -#define BM_CLR(bm, x, y) (bm_safe(bm, x, y) ? BM_UCLR(bm, x, y) : 0) -#define BM_INV(bm, x, y) (bm_safe(bm, x, y) ? BM_UINV(bm, x, y) : 0) -#define BM_PUT(bm, x, y, b) (bm_safe(bm, x, y) ? BM_UPUT(bm, x, y, b) : 0) - -/* free the given bitmap. Leaves errno untouched. */ -static inline void bm_free(potrace_bitmap_t * bm) -{ - if (bm) { - free(bm->map); - } - free(bm); -} - -/* return new un-initialized bitmap. NULL with errno on error */ -static inline potrace_bitmap_t *bm_new(int w, int h) -{ - potrace_bitmap_t *bm; - int dy = (w + BM_WORDBITS - 1) / BM_WORDBITS; - - bm = (potrace_bitmap_t *) malloc(sizeof(potrace_bitmap_t)); - if (!bm) { - return NULL; - } - bm->w = w; - bm->h = h; - bm->dy = dy; - bm->map = (potrace_word *) malloc(dy * h * BM_WORDSIZE); - if (!bm->map) { - free(bm); - return NULL; - } - return bm; -} - -/* clear the given bitmap. Set all bits to c. */ -static inline void bm_clear(potrace_bitmap_t * bm, int c) -{ - memset(bm->map, c ? -1 : 0, bm->dy * bm->h * BM_WORDSIZE); -} - -/* duplicate the given bitmap. Return NULL on error with errno set. */ -static inline potrace_bitmap_t *bm_dup(const potrace_bitmap_t * bm) -{ - potrace_bitmap_t *bm1 = bm_new(bm->w, bm->h); - if (!bm1) { - return NULL; - } - memcpy(bm1->map, bm->map, bm->dy * bm->h * BM_WORDSIZE); - return bm1; -} - -/* invert the given bitmap. */ -static inline void bm_invert(potrace_bitmap_t * bm) -{ - int i; - for (i = 0; i < bm->dy * bm->h; i++) { - bm->map[i] ^= BM_ALLBITS; - } -} - -#endif /* BITMAP_H */ Index: trunk/src/hid/gcode/gcode.c =================================================================== --- trunk/src/hid/gcode/gcode.c (revision 1268) +++ trunk/src/hid/gcode/gcode.c (nonexistent) @@ -1,898 +0,0 @@ -/* - * COPYRIGHT - * - * PCB, interactive printed circuit board design - * - * GCODE export HID - * Copyright (C) 2010 Alberto Maccioni - * this code is based on the NELMA export HID, the PNG export HID, - * and potrace, a tracing program by Peter Selinger - * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - */ - -/* - * This HID exports a PCB layout into: - * one layer mask file (PNG format) per copper layer, - * one G-CODE CNC drill file. - * one G-CODE CNC file per copper layer. - * The latter is used by a CNC milling machine to mill the pcb. - */ - -#include "config.h" - -#include -#include -#include -#include -#include - -#include - -#include "global.h" -#include "error.h" /* Message() */ -#include "data.h" -#include "misc.h" -#include "rats.h" - -#include "hid.h" -#include "../hidint.h" -#include -#include "hid/common/hidnogui.h" -#include "hid/common/draw_helpers.h" -#include "gcode.h" -#include "bitmap.h" -#include "curve.h" -#include "potracelib.h" -#include "trace.h" -#include "decompose.h" -#include "pcb-printf.h" - -#include "hid/common/hidinit.h" - -const char *gcode_cookie = "gcode HID"; - -#define CRASH fprintf(stderr, "HID error: pcb called unimplemented GCODE function %s.\n", __FUNCTION__); abort() -struct color_struct { - /* the descriptor used by the gd library */ - int c; - - /* so I can figure out what rgb value c refers to */ - unsigned int r, g, b; -}; - -struct hid_gc_struct { - HID *me_pointer; - EndCapStyle cap; - int width; - unsigned char r, g, b; - int erase; - int faded; - struct color_struct *color; - gdImagePtr brush; -}; - -static struct color_struct *black = NULL, *white = NULL; -static int linewidth = -1; -static gdImagePtr lastbrush = (gdImagePtr) ((void *) -1); -static int lastcolor = -1; - -/* gd image and file for PNG export */ -static gdImagePtr gcode_im = NULL; -static FILE *gcode_f = NULL, *gcode_f2 = NULL; - -static int is_mask; -static int is_drill; -static int is_solder; - -/* - * Which groups of layers to export into PNG layer masks. 1 means export, 0 - * means do not export. - */ -static int gcode_export_group[MAX_LAYER]; - -/* Group that is currently exported. */ -static int gcode_cur_group; - -/* Filename prefix that will be used when saving files. */ -static const char *gcode_basename = NULL; - -/* Horizontal DPI (grid points per inch) */ -static int gcode_dpi = -1; - -static double gcode_cutdepth = 0; /* milling depth (inch) */ -static double gcode_drilldepth = 0; /* drilling depth (inch) */ -static double gcode_safeZ = 100; /* safe Z (inch) */ -static double gcode_toolradius = 0; /* tool radius(inch) */ -static int save_drill = 0; -static int n_drill = 0; -static int nmax_drill = 0; -struct drill_struct { - double x; - double y; -}; - -static struct drill_struct *drill = 0; - -static const char *units[] = { - "mm", - "mil", - "um", - "inch", - NULL -}; - -HID_Attribute gcode_attribute_list[] = { - /* other HIDs expect this to be first. */ - {"basename", "File name prefix", - HID_String, 0, 0, {0, 0, 0}, 0, 0}, -#define HA_basename 0 - - {"dpi", "Resolution of intermediate image (pixels/inch)", - HID_Integer, 0, 2000, {600, 0, 0}, 0, 0}, -#define HA_dpi 1 - - {"mill-depth", "Milling depth", - HID_Real, -1000, 1000, {0, 0, -0.05}, 0, 0}, -#define HA_cutdepth 2 - - {"safe-Z", "Safe Z for traverse move", - HID_Real, -1000, 10000, {0, 0, 2}, 0, 0}, -#define HA_safeZ 3 - - {"tool-radius", "Milling tool radius compensation", - HID_Real, 0, 10000, {0, 0, 0.1}, 0, 0}, -#define HA_toolradius 4 - - {"drill-depth", "Drilling depth", - HID_Real, -10000, 10000, {0, 0, -2}, 0, 0}, -#define HA_drilldepth 5 - - {"measurement-unit", "Measurement unit", - HID_Unit, 0, 0, {-1, 0, 0}, units, 0}, -#define HA_unit 6 - -}; - -#define NUM_OPTIONS (sizeof(gcode_attribute_list)/sizeof(gcode_attribute_list[0])) - -REGISTER_ATTRIBUTES(gcode_attribute_list, gcode_cookie) - static HID_Attr_Val gcode_values[NUM_OPTIONS]; - -/* *** Utility funcions **************************************************** */ - -/* convert from default PCB units to gcode units */ - static int pcb_to_gcode(int pcb) -{ - return round(COORD_TO_INCH(pcb) * gcode_dpi); -} - -static char *gcode_get_png_name(const char *basename, const char *suffix) -{ - return pcb_strdup_printf("%s.%s.png", basename, suffix); -} - -/* Sorts drills in order of distance from the origin */ -struct drill_struct *sort_drill(struct drill_struct *drill, int n_drill) -{ - int i, j, imin; - double dmin, d; - struct drill_struct p = { 0, 0 }; - struct drill_struct *temp = (struct drill_struct *) malloc(n_drill * sizeof(struct drill_struct)); - for (j = 0; j < n_drill; j++) { - dmin = 1e20; - imin = 0; - for (i = 0; i < n_drill - j; i++) { - d = (drill[i].x - p.x) * (drill[i].x - p.x) + (drill[i].y - p.y) * (drill[i].y - p.y); - if (d < dmin) { - imin = i; - dmin = d; - } - } - /* printf("j=%d imin=%d dmin=%f p=(%f,%f)\n",j,imin,dmin,p.x,p.y); */ - temp[j] = drill[imin]; - drill[imin] = drill[n_drill - j - 1]; - p = temp[j]; - } - free(drill); - return temp; -} - -/* *** Main export callback ************************************************ */ - -static void gcode_parse_arguments(int *argc, char ***argv) -{ - hid_register_attributes(gcode_attribute_list, sizeof(gcode_attribute_list) / sizeof(gcode_attribute_list[0]), gcode_cookie); - hid_parse_command_line(argc, argv); -} - -static HID_Attribute *gcode_get_export_options(int *n) -{ - static char *last_made_filename = 0; - static int last_unit_value = -1; - - if (gcode_attribute_list[HA_unit].default_val.int_value == last_unit_value) { - if (Settings.grid_unit) - gcode_attribute_list[HA_unit].default_val.int_value = Settings.grid_unit->index; - else - gcode_attribute_list[HA_unit].default_val.int_value = get_unit_struct("mil")->index; - last_unit_value = gcode_attribute_list[HA_unit].default_val.int_value; - } - - if (PCB) { - derive_default_filename(PCB->Filename, &gcode_attribute_list[HA_basename], ".gcode", &last_made_filename); - } - if (n) { - *n = NUM_OPTIONS; - } - return gcode_attribute_list; -} - -/* Populates gcode_export_group array */ -void gcode_choose_groups() -{ - int n, m; - LayerType *layer; - - /* Set entire array to 0 (don't export any layer groups by default */ - memset(gcode_export_group, 0, sizeof(gcode_export_group)); - - for (n = 0; n < max_copper_layer; n++) { - layer = &PCB->Data->Layer[n]; - - if (!LAYER_IS_EMPTY(layer)) { - /* layer isn't empty */ - - /* - * is this check necessary? It seems that special - * layers have negative indexes? - */ - - if (SL_TYPE(n) == 0) { - /* layer is a copper layer */ - m = GetLayerGroupNumberByNumber(n); - - /* the export layer */ - gcode_export_group[m] = 1; - } - } - } -} - -static void gcode_alloc_colors() -{ - /* - * Allocate white and black -- the first color allocated becomes the - * background color - */ - - white = (struct color_struct *) malloc(sizeof(*white)); - white->r = white->g = white->b = 255; - white->c = gdImageColorAllocate(gcode_im, white->r, white->g, white->b); - - black = (struct color_struct *) malloc(sizeof(*black)); - black->r = black->g = black->b = 0; - black->c = gdImageColorAllocate(gcode_im, black->r, black->g, black->b); -} - -static void gcode_start_png(const char *basename, const char *suffix) -{ - int h, w; - char *buf; - - buf = gcode_get_png_name(basename, suffix); - - h = pcb_to_gcode(PCB->MaxHeight); - w = pcb_to_gcode(PCB->MaxWidth); - - /* Nelma only works with true color images */ - gcode_im = gdImageCreate(w, h); - gcode_f = fopen(buf, "wb"); - - gcode_alloc_colors(); - - free(buf); -} - -static void gcode_finish_png() -{ -#ifdef HAVE_GDIMAGEPNG - gdImagePng(gcode_im, gcode_f); -#else - Message("GCODE: PNG not supported by gd. Can't write layer mask.\n"); -#endif - gdImageDestroy(gcode_im); - fclose(gcode_f); - - free(white); - free(black); - - gcode_im = NULL; - gcode_f = NULL; -} - -void gcode_start_png_export() -{ - BoxType region; - - region.X1 = 0; - region.Y1 = 0; - region.X2 = PCB->MaxWidth; - region.Y2 = PCB->MaxHeight; - - linewidth = -1; - lastbrush = (gdImagePtr) ((void *) -1); - lastcolor = -1; - - hid_expose_callback(&gcode_hid, ®ion, 0); -} - -static void gcode_do_export(HID_Attr_Val * options) -{ - int save_ons[MAX_LAYER + 2]; - int i, idx; - time_t t; - const Unit *unit; - double scale = 0, d = 0; - int r, c, v, p, metric; - char *filename; - path_t *plist = NULL; - potrace_bitmap_t *bm = NULL; - potrace_param_t param_default = { - 2, /* turnsize */ - POTRACE_TURNPOLICY_MINORITY, /* turnpolicy */ - 1.0, /* alphamax */ - 1, /* opticurve */ - 0.2, /* opttolerance */ - { - NULL, /* callback function */ - NULL, /* callback data */ - 0.0, 1.0, /* progress range */ - 0.0, /* granularity */ - }, - }; - - if (!options) { - gcode_get_export_options(0); - for (i = 0; i < NUM_OPTIONS; i++) { - gcode_values[i] = gcode_attribute_list[i].default_val; - } - options = gcode_values; - } - gcode_basename = options[HA_basename].str_value; - if (!gcode_basename) { - gcode_basename = "pcb-out"; - } - gcode_dpi = options[HA_dpi].int_value; - if (gcode_dpi < 0) { - fprintf(stderr, "ERROR: dpi may not be < 0\n"); - return; - } - unit = &(get_unit_list()[options[HA_unit].int_value]); - metric = (unit->family == METRIC); - scale = metric ? 1.0 / coord_to_unit(unit, MM_TO_COORD(1.0)) - : 1.0 / coord_to_unit(unit, INCH_TO_COORD(1.0)); - - gcode_cutdepth = options[HA_cutdepth].real_value * scale; - gcode_drilldepth = options[HA_drilldepth].real_value * scale; - gcode_safeZ = options[HA_safeZ].real_value * scale; - gcode_toolradius = metric ? MM_TO_COORD(options[HA_toolradius].real_value * scale) - : INCH_TO_COORD(options[HA_toolradius].real_value * scale); - gcode_choose_groups(); - - for (i = 0; i < MAX_LAYER; i++) { - if (gcode_export_group[i]) { - - gcode_cur_group = i; - - /* magic */ - idx = (i >= 0 && i < max_group) ? PCB->LayerGroups.Entries[i][0] : i; - printf("idx=%d %s\n", idx, layer_type_to_file_name(idx, FNS_fixed)); - is_solder = (GetLayerGroupNumberByNumber(idx) == GetLayerGroupNumberByNumber(solder_silk_layer)) ? 1 : 0; - save_drill = is_solder; /* save drills for one layer only */ - gcode_start_png(gcode_basename, layer_type_to_file_name(idx, FNS_fixed)); - hid_save_and_show_layer_ons(save_ons); - gcode_start_png_export(); - hid_restore_layer_ons(save_ons); - -/* ***************** gcode conversion *************************** */ -/* potrace uses a different kind of bitmap; for simplicity gcode_im is copied to this format */ - bm = bm_new(gdImageSX(gcode_im), gdImageSY(gcode_im)); - filename = (char *) malloc(MAXPATHLEN); - plist = NULL; - if (is_solder) { /* only for back layer */ - gdImagePtr temp_im = gdImageCreate(gdImageSX(gcode_im), gdImageSY(gcode_im)); - gdImageCopy(temp_im, gcode_im, 0, 0, 0, 0, gdImageSX(gcode_im), gdImageSY(gcode_im)); - for (r = 0; r < gdImageSX(gcode_im); r++) { - for (c = 0; c < gdImageSY(gcode_im); c++) { - gdImageSetPixel(gcode_im, r, c, gdImageGetPixel(temp_im, gdImageSX(gcode_im) - 1 - r, c)); - } - } - gdImageDestroy(temp_im); - } - sprintf(filename, "%s.%s.cnc", gcode_basename, layer_type_to_file_name(idx, FNS_fixed)); - for (r = 0; r < gdImageSX(gcode_im); r++) { - for (c = 0; c < gdImageSY(gcode_im); c++) { - v = gdImageGetPixel(gcode_im, r, gdImageSY(gcode_im) - 1 - c); - p = (gcode_im->red[v] || gcode_im->green[v] - || gcode_im->blue[v]) ? 0 : 0xFFFFFF; - BM_PUT(bm, r, c, p); - } - } - gcode_f2 = fopen(filename, "wb"); - if (!gcode_f2) { - perror(filename); - return; - } - fprintf(gcode_f2, "(Created by G-code exporter)\n"); - t = time(NULL); - sprintf(filename, "%s", ctime(&t)); - filename[strlen(filename) - 1] = 0; - fprintf(gcode_f2, "( %s )\n", filename); - fprintf(gcode_f2, "(%d dpi)\n", gcode_dpi); - fprintf(gcode_f2, "(Unit: %s)\n", metric ? "mm" : "inch"); - if (metric) - pcb_fprintf(gcode_f2, "(Board size: %.2mmx%.2mm mm)", PCB->MaxWidth, PCB->MaxHeight); - else - pcb_fprintf(gcode_f2, "(Board size: %.2mix%.2mi inches)", PCB->MaxWidth, PCB->MaxHeight); - fprintf(gcode_f2, "#100=%f (safe Z)\n", gcode_safeZ); - fprintf(gcode_f2, "#101=%f (cutting depth)\n", gcode_cutdepth); - fprintf(gcode_f2, "(---------------------------------)\n"); - fprintf(gcode_f2, "G17 G%d G90 G64 P0.003 M3 S3000 M7 F%d\n", metric ? 21 : 20, metric ? 25 : 1); - fprintf(gcode_f2, "G0 Z#100\n"); - /* extract contour points from image */ - r = bm_to_pathlist(bm, &plist, ¶m_default); - if (r) { - fprintf(stderr, "ERROR: pathlist function failed\n"); - return; - } - /* generate best polygon and write vertices in g-code format */ - d = process_path(plist, ¶m_default, bm, gcode_f2, metric ? 25.4 / gcode_dpi : 1.0 / gcode_dpi); - if (d < 0) { - fprintf(stderr, "ERROR: path process function failed\n"); - return; - } - if (metric) - fprintf(gcode_f2, "(end, total distance %.2fmm = %.2fin)\n", d, d * 1 / 25.4); - else - fprintf(gcode_f2, "(end, total distance %.2fmm = %.2fin)\n", 25.4 * d, d); - fprintf(gcode_f2, "M5 M9 M2\n"); - pathlist_free(plist); - bm_free(bm); - fclose(gcode_f2); - if (save_drill) { - d = 0; - drill = sort_drill(drill, n_drill); - sprintf(filename, "%s.drill.cnc", gcode_basename); - gcode_f2 = fopen(filename, "wb"); - if (!gcode_f2) { - perror(filename); - return; - } - fprintf(gcode_f2, "(Created by G-code exporter)\n"); - fprintf(gcode_f2, "(drill file: %d drills)\n", n_drill); - sprintf(filename, "%s", ctime(&t)); - filename[strlen(filename) - 1] = 0; - fprintf(gcode_f2, "( %s )\n", filename); - fprintf(gcode_f2, "(Unit: %s)\n", metric ? "mm" : "inch"); - if (metric) - pcb_fprintf(gcode_f2, "(Board size: %.2mmx%.2mm mm)", PCB->MaxWidth, PCB->MaxHeight); - else - pcb_fprintf(gcode_f2, "(Board size: %.2mix%.2mi inches)", PCB->MaxWidth, PCB->MaxHeight); - fprintf(gcode_f2, "#100=%f (safe Z)\n", gcode_safeZ); - fprintf(gcode_f2, "#101=%f (drill depth)\n", gcode_drilldepth); - fprintf(gcode_f2, "(---------------------------------)\n"); - fprintf(gcode_f2, "G17 G%d G90 G64 P0.003 M3 S3000 M7 F%d\n", metric ? 21 : 20, metric ? 25 : 1); -/* fprintf(gcode_f2,"G0 Z#100\n"); */ - for (r = 0; r < n_drill; r++) { -/* if(metric) fprintf(gcode_f2,"G0 X%f Y%f\n",drill[r].x*25.4,drill[r].y*25.4); */ -/* else fprintf(gcode_f2,"G0 X%f Y%f\n",drill[r].x,drill[r].y); */ - if (metric) - fprintf(gcode_f2, "G81 X%f Y%f Z#101 R#100\n", drill[r].x * 25.4, drill[r].y * 25.4); - else - fprintf(gcode_f2, "G81 X%f Y%f Z#101 R#100\n", drill[r].x, drill[r].y); -/* fprintf(gcode_f2,"G1 Z#101\n"); */ -/* fprintf(gcode_f2,"G0 Z#100\n"); */ - if (r > 0) - d += - sqrt((drill[r].x - drill[r - 1].x) * (drill[r].x - - drill[r - 1].x) + - (drill[r].y - drill[r - 1].y) * (drill[r].y - drill[r - 1].y)); - } - fprintf(gcode_f2, "M5 M9 M2\n"); - fprintf(gcode_f2, "(end, total distance %.2fmm = %.2fin)\n", 25.4 * d, d); - fclose(gcode_f2); - free(drill); - drill = NULL; - n_drill = nmax_drill = 0; - } - free(filename); - -/* ******************* end gcode conversion **************************** */ - gcode_finish_png(); - } - } -} - -/* *** PNG export (slightly modified code from PNG export HID) ************* */ - -static int gcode_set_layer(const char *name, int group, int empty) -{ - int idx = (group >= 0 && group < max_group) ? PCB->LayerGroups.Entries[group][0] : group; - - if (name == 0) { - name = PCB->Data->Layer[idx].Name; - } - if (strcmp(name, "invisible") == 0) { - return 0; - } - is_drill = (SL_TYPE(idx) == SL_PDRILL || SL_TYPE(idx) == SL_UDRILL); - is_mask = (SL_TYPE(idx) == SL_MASK); - - if (is_mask) { - /* Don't print masks */ - return 0; - } - if (is_drill) { - /* - * Print 'holes', so that we can fill gaps in the copper - * layer - */ - return 1; - } - if (group == gcode_cur_group) { - return 1; - } - return 0; -} - -static hidGC gcode_make_gc(void) -{ - hidGC rv = (hidGC) malloc(sizeof(struct hid_gc_struct)); - rv->me_pointer = &gcode_hid; - rv->cap = Trace_Cap; - rv->width = 1; - rv->color = (struct color_struct *) malloc(sizeof(*rv->color)); - rv->color->r = rv->color->g = rv->color->b = 0; - rv->color->c = 0; - return rv; -} - -static void gcode_destroy_gc(hidGC gc) -{ - free(gc); -} - -static void gcode_use_mask(int use_it) -{ - /* does nothing */ -} - -static void gcode_set_color(hidGC gc, const char *name) -{ - if (gcode_im == NULL) { - return; - } - if (name == NULL) { - name = "#ff0000"; - } - if (!strcmp(name, "drill")) { - gc->color = black; - gc->erase = 0; - return; - } - if (!strcmp(name, "erase")) { - /* FIXME -- should be background, not white */ - gc->color = white; - gc->erase = 1; - return; - } - gc->color = black; - gc->erase = 0; - return; -} - -static void gcode_set_line_cap(hidGC gc, EndCapStyle style) -{ - gc->cap = style; -} - -static void gcode_set_line_width(hidGC gc, Coord width) -{ - gc->width = width; -} - -static void gcode_set_draw_xor(hidGC gc, int xor_) -{ - ; -} - -static void gcode_set_draw_faded(hidGC gc, int faded) -{ - gc->faded = faded; -} - -static void use_gc(hidGC gc) -{ - int need_brush = 0; - - if (gc->me_pointer != &gcode_hid) { - fprintf(stderr, "Fatal: GC from another HID passed to gcode HID\n"); - abort(); - } - if (linewidth != gc->width) { - /* Make sure the scaling doesn't erase lines completely */ - /* - if (SCALE (gc->width) == 0 && gc->width > 0) - gdImageSetThickness (im, 1); - else - */ - gdImageSetThickness(gcode_im, pcb_to_gcode(gc->width + 2 * gcode_toolradius)); - linewidth = gc->width; - need_brush = 1; - } - if (lastbrush != gc->brush || need_brush) { - static void *bcache = 0; - hidval bval; - char name[256]; - char type; - int r; - - switch (gc->cap) { - case Round_Cap: - case Trace_Cap: - type = 'C'; - r = pcb_to_gcode(gc->width / 2 + gcode_toolradius); - break; - default: - case Square_Cap: - r = pcb_to_gcode(gc->width + gcode_toolradius * 2); - type = 'S'; - break; - } - sprintf(name, "#%.2x%.2x%.2x_%c_%d", gc->color->r, gc->color->g, gc->color->b, type, r); - - if (hid_cache_color(0, name, &bval, &bcache)) { - gc->brush = (gdImagePtr) bval.ptr; - } - else { - int bg, fg; - if (type == 'C') - gc->brush = gdImageCreate(2 * r + 1, 2 * r + 1); - else - gc->brush = gdImageCreate(r + 1, r + 1); - bg = gdImageColorAllocate(gc->brush, 255, 255, 255); - fg = gdImageColorAllocate(gc->brush, gc->color->r, gc->color->g, gc->color->b); - gdImageColorTransparent(gc->brush, bg); - - /* - * if we shrunk to a radius/box width of zero, then just use - * a single pixel to draw with. - */ - if (r == 0) - gdImageFilledRectangle(gc->brush, 0, 0, 0, 0, fg); - else { - if (type == 'C') - gdImageFilledEllipse(gc->brush, r, r, 2 * r, 2 * r, fg); - else - gdImageFilledRectangle(gc->brush, 0, 0, r, r, fg); - } - bval.ptr = gc->brush; - hid_cache_color(1, name, &bval, &bcache); - } - - gdImageSetBrush(gcode_im, gc->brush); - lastbrush = gc->brush; - - } -#define CBLEND(gc) (((gc->r)<<24)|((gc->g)<<16)|((gc->b)<<8)|(gc->faded)) - if (lastcolor != CBLEND(gc)) { - if (is_drill || is_mask) { -#ifdef FIXME - fprintf(f, "%d gray\n", gc->erase ? 0 : 1); -#endif - lastcolor = 0; - } - else { - double r, g, b; - r = gc->r; - g = gc->g; - b = gc->b; - if (gc->faded) { - r = 0.8 * 255 + 0.2 * r; - g = 0.8 * 255 + 0.2 * g; - b = 0.8 * 255 + 0.2 * b; - } -#ifdef FIXME - if (gc->r == gc->g && gc->g == gc->b) - fprintf(f, "%g gray\n", r / 255.0); - else - fprintf(f, "%g %g %g rgb\n", r / 255.0, g / 255.0, b / 255.0); -#endif - lastcolor = CBLEND(gc); - } - } -} - -static void gcode_draw_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2) -{ - use_gc(gc); - gdImageRectangle(gcode_im, - pcb_to_gcode(x1 - gcode_toolradius), - pcb_to_gcode(y1 - gcode_toolradius), - pcb_to_gcode(x2 + gcode_toolradius), pcb_to_gcode(y2 + gcode_toolradius), gc->color->c); -/* printf("Rect %d %d %d %d\n",x1,y1,x2,y2); */ -} - -static void gcode_fill_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2) -{ - use_gc(gc); - gdImageSetThickness(gcode_im, 0); - linewidth = 0; - gdImageFilledRectangle(gcode_im, - pcb_to_gcode(x1 - gcode_toolradius), - pcb_to_gcode(y1 - gcode_toolradius), - pcb_to_gcode(x2 + gcode_toolradius), pcb_to_gcode(y2 + gcode_toolradius), gc->color->c); -/* printf("FillRect %d %d %d %d\n",x1,y1,x2,y2); */ -} - -static void gcode_draw_line(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2) -{ - if (x1 == x2 && y1 == y2) { - Coord w = gc->width / 2; - gcode_fill_rect(gc, x1 - w, y1 - w, x1 + w, y1 + w); - return; - } - use_gc(gc); - - gdImageSetThickness(gcode_im, 0); - linewidth = 0; - gdImageLine(gcode_im, pcb_to_gcode(x1), pcb_to_gcode(y1), pcb_to_gcode(x2), pcb_to_gcode(y2), gdBrushed); -} - -static void gcode_draw_arc(hidGC gc, Coord cx, Coord cy, Coord width, Coord height, Angle start_angle, Angle delta_angle) -{ - Angle sa, ea; - - /* - * in gdImageArc, 0 degrees is to the right and +90 degrees is down - * in pcb, 0 degrees is to the left and +90 degrees is down - */ - start_angle = 180 - start_angle; - delta_angle = -delta_angle; - if (delta_angle > 0) { - sa = start_angle; - ea = start_angle + delta_angle; - } - else { - sa = start_angle + delta_angle; - ea = start_angle; - } - - /* - * make sure we start between 0 and 360 otherwise gd does strange - * things - */ - sa = NormalizeAngle(sa); - ea = NormalizeAngle(ea); - -#if 0 - printf("draw_arc %d,%d %dx%d %d..%d %d..%d\n", cx, cy, width, height, start_angle, delta_angle, sa, ea); - printf("gdImageArc (%p, %d, %d, %d, %d, %d, %d, %d)\n", - im, SCALE_X(cx), SCALE_Y(cy), SCALE(width), SCALE(height), sa, ea, gc->color->c); -#endif - use_gc(gc); - gdImageSetThickness(gcode_im, 0); - linewidth = 0; - gdImageArc(gcode_im, pcb_to_gcode(cx), pcb_to_gcode(cy), - pcb_to_gcode(2 * width + gcode_toolradius * 2), - pcb_to_gcode(2 * height + gcode_toolradius * 2), sa, ea, gdBrushed); -} - -static void gcode_fill_circle(hidGC gc, Coord cx, Coord cy, Coord radius) -{ - use_gc(gc); - - gdImageSetThickness(gcode_im, 0); - linewidth = 0; - gdImageFilledEllipse(gcode_im, pcb_to_gcode(cx), pcb_to_gcode(cy), - pcb_to_gcode(2 * radius + gcode_toolradius * 2), - pcb_to_gcode(2 * radius + gcode_toolradius * 2), gc->color->c); - if (save_drill && is_drill) { - if (n_drill == nmax_drill) { - drill = (struct drill_struct *) realloc(drill, (nmax_drill + 100) * sizeof(struct drill_struct)); - nmax_drill += 100; - } - drill[n_drill].x = COORD_TO_INCH(PCB->MaxWidth - cx); /* convert to inch, flip: will drill from bottom side */ - drill[n_drill].y = COORD_TO_INCH(PCB->MaxHeight - cy); /* PCB reverses y axis */ - n_drill++; -/* printf("Circle %d %d\n",cx,cy); */ - } -} - -static void gcode_fill_polygon(hidGC gc, int n_coords, Coord * x, Coord * y) -{ - int i; - gdPoint *points; - - points = (gdPoint *) malloc(n_coords * sizeof(gdPoint)); - if (points == NULL) { - fprintf(stderr, "ERROR: gcode_fill_polygon(): malloc failed\n"); - exit(1); - } - use_gc(gc); - for (i = 0; i < n_coords; i++) { - points[i].x = pcb_to_gcode(x[i]); - points[i].y = pcb_to_gcode(y[i]); - } - gdImageSetThickness(gcode_im, 0); - linewidth = 0; - gdImageFilledPolygon(gcode_im, points, n_coords, gc->color->c); - free(points); -/* printf("FillPoly\n"); */ -} - -static void gcode_calibrate(double xval, double yval) -{ - CRASH; -} - -static void gcode_set_crosshair(int x, int y, int a) -{ -} - -/* *** Miscellaneous ******************************************************* */ - -#include "dolists.h" - -HID gcode_hid; - -void hid_gcode_init() -{ - memset(&gcode_hid, 0, sizeof(HID)); - - common_nogui_init(&gcode_hid); - common_draw_helpers_init(&gcode_hid); - - gcode_hid.struct_size = sizeof(HID); - gcode_hid.name = "gcode"; - gcode_hid.description = "G-CODE export"; - gcode_hid.exporter = 1; - gcode_hid.poly_before = 1; - - gcode_hid.get_export_options = gcode_get_export_options; - gcode_hid.do_export = gcode_do_export; - gcode_hid.parse_arguments = gcode_parse_arguments; - gcode_hid.set_layer = gcode_set_layer; - gcode_hid.make_gc = gcode_make_gc; - gcode_hid.destroy_gc = gcode_destroy_gc; - gcode_hid.use_mask = gcode_use_mask; - gcode_hid.set_color = gcode_set_color; - gcode_hid.set_line_cap = gcode_set_line_cap; - gcode_hid.set_line_width = gcode_set_line_width; - gcode_hid.set_draw_xor = gcode_set_draw_xor; - gcode_hid.set_draw_faded = gcode_set_draw_faded; - gcode_hid.draw_line = gcode_draw_line; - gcode_hid.draw_arc = gcode_draw_arc; - gcode_hid.draw_rect = gcode_draw_rect; - gcode_hid.fill_circle = gcode_fill_circle; - gcode_hid.fill_polygon = gcode_fill_polygon; - gcode_hid.fill_rect = gcode_fill_rect; - gcode_hid.calibrate = gcode_calibrate; - gcode_hid.set_crosshair = gcode_set_crosshair; - - hid_register_hid(&gcode_hid); -} Index: trunk/src/hid/gcode/decompose.h =================================================================== --- trunk/src/hid/gcode/decompose.h (revision 1268) +++ trunk/src/hid/gcode/decompose.h (nonexistent) @@ -1,15 +0,0 @@ -/* Copyright (C) 2001-2007 Peter Selinger. - This file is part of Potrace. It is free software and it is covered - by the GNU General Public License. See the file COPYING for details. */ - -/* $Id: decompose.h 147 2007-04-09 00:44:09Z selinger $ */ - -#ifndef DECOMPOSE_H -#define DECOMPOSE_H - -#include "potracelib.h" -/*#include "progress.h"*/ - -int bm_to_pathlist(const potrace_bitmap_t * bm, path_t ** plistp, const potrace_param_t * param); - -#endif /* DECOMPOSE_H */ Index: trunk/src/hid/gcode/lists.h =================================================================== --- trunk/src/hid/gcode/lists.h (revision 1268) +++ trunk/src/hid/gcode/lists.h (nonexistent) @@ -1,285 +0,0 @@ -/* Copyright (C) 2001-2007 Peter Selinger. - This file is part of Potrace. It is free software and it is covered - by the GNU General Public License. See the file COPYING for details. */ - -/* $Id: lists.h 147 2007-04-09 00:44:09Z selinger $ */ - -#ifndef PCB_HID_GCODE_LISTS_H -#define PCB_HID_GCODE_LISTS_H - -/* here we define some general list macros. Because they are macros, - they should work on any datatype with a "->next" component. Some of - them use a "hook". If elt and list are of type t* then hook is of - type t**. A hook stands for an insertion point in the list, i.e., - either before the first element, or between two elements, or after - the last element. If an operation "sets the hook" for an element, - then the hook is set to just before the element. One can insert - something at a hook. One can also unlink at a hook: this means, - unlink the element just after the hook. By "to unlink", we mean the - element is removed from the list, but not deleted. Thus, it and its - components still need to be freed. */ - -/* Note: these macros are somewhat experimental. Only the ones that - are actually *used* have been tested. So be careful to test any - that you use. Looking at the output of the preprocessor, "gcc -E" - (possibly piped though "indent"), might help too. Also: these - macros define some internal (local) variables that start with - "_". */ - -/* we enclose macro definitions whose body consists of more than one - statement in MACRO_BEGIN and MACRO_END, rather than '{' and '}'. The - reason is that we want to be able to use the macro in a context - such as "if (...) macro(...); else ...". If we didn't use this obscure - trick, we'd have to omit the ";" in such cases. */ - -#define MACRO_BEGIN do { -#define MACRO_END } while (0) - -/* ---------------------------------------------------------------------- */ -/* macros for singly-linked lists */ - -/* traverse list. At the end, elt is set to NULL. */ -#define list_forall(elt, list) for (elt=list; elt!=NULL; elt=elt->next) - -/* set elt to the first element of list satisfying boolean condition - c, or NULL if not found */ -#define list_find(elt, list, c) \ - MACRO_BEGIN list_forall(elt, list) if (c) break; MACRO_END - -/* like forall, except also set hook for elt. */ -#define list_forall2(elt, list, hook) \ - for (elt=list, hook=&list; elt!=NULL; hook=&elt->next, elt=elt->next) - -/* same as list_find, except also set hook for elt. */ -#define list_find2(elt, list, c, hook) \ - MACRO_BEGIN list_forall2(elt, list, hook) if (c) break; MACRO_END - -/* same, except only use hook. */ -#define _list_forall_hook(list, hook) \ - for (hook=&list; *hook!=NULL; hook=&(*hook)->next) - -/* same, except only use hook. Note: c may only refer to *hook, not elt. */ -#define _list_find_hook(list, c, hook) \ - MACRO_BEGIN _list_forall_hook(list, hook) if (c) break; MACRO_END - -/* insert element after hook */ -#define list_insert_athook(elt, hook) \ - MACRO_BEGIN elt->next = *hook; *hook = elt; MACRO_END - -/* insert element before hook */ -#define list_insert_beforehook(elt, hook) \ - MACRO_BEGIN elt->next = *hook; *hook = elt; hook=&elt->next; MACRO_END - -/* unlink element after hook, let elt be unlinked element, or NULL. - hook remains. */ -#define list_unlink_athook(list, elt, hook) \ - MACRO_BEGIN \ - elt = hook ? *hook : NULL; if (elt) { *hook = elt->next; elt->next = NULL; }\ - MACRO_END - -/* unlink the specific element, if it is in the list. Otherwise, set - elt to NULL */ -#define list_unlink(listtype, list, elt) \ - MACRO_BEGIN \ - listtype **_hook; \ - _list_find_hook(list, *_hook==elt, _hook); \ - list_unlink_athook(list, elt, _hook); \ - MACRO_END - -/* prepend elt to list */ -#define list_prepend(list, elt) \ - MACRO_BEGIN elt->next = list; list = elt; MACRO_END - -/* append elt to list. */ -#define list_append(listtype, list, elt) \ - MACRO_BEGIN \ - listtype **_hook; \ - _list_forall_hook(list, _hook) {} \ - list_insert_athook(elt, _hook); \ - MACRO_END - -/* unlink the first element that satisfies the condition. */ -#define list_unlink_cond(listtype, list, elt, c) \ - MACRO_BEGIN \ - listtype **_hook; \ - list_find2(elt, list, c, _hook); \ - list_unlink_athook(list, elt, _hook); \ - MACRO_END - -/* let elt be the nth element of the list, starting to count from 0. - Return NULL if out of bounds. */ -#define list_nth(elt, list, n) \ - MACRO_BEGIN \ - int _x; /* only evaluate n once */ \ - for (_x=(n), elt=list; _x && elt; _x--, elt=elt->next) {} \ - MACRO_END - -/* let elt be the nth element of the list, starting to count from 0. - Return NULL if out of bounds. */ -#define list_nth_hook(elt, list, n, hook) \ - MACRO_BEGIN \ - int _x; /* only evaluate n once */ \ - for (_x=(n), elt=list, hook=&list; _x && elt; _x--, hook=&elt->next, elt=elt->next) {} \ - MACRO_END - -/* set n to the length of the list */ -#define list_length(listtype, list, n) \ - MACRO_BEGIN \ - listtype *_elt; \ - n=0; \ - list_forall(_elt, list) \ - n++; \ - MACRO_END - -/* set n to the index of the first element satisfying cond, or -1 if - none found. Also set elt to the element, or NULL if none found. */ -#define list_index(list, n, elt, c) \ - MACRO_BEGIN \ - n=0; \ - list_forall(elt, list) { \ - if (c) break; \ - n++; \ - } \ - if (!elt) \ - n=-1; \ - MACRO_END - -/* set n to the number of elements in the list that satisfy condition c */ -#define list_count(list, n, elt, c) \ - MACRO_BEGIN \ - n=0; \ - list_forall(elt, list) { \ - if (c) n++; \ - } \ - MACRO_END - -/* let elt be each element of the list, unlinked. At the end, set list=NULL. */ -#define list_forall_unlink(elt, list) \ - for (elt=list; elt ? (list=elt->next, elt->next=NULL), 1 : 0; elt=list) - -/* reverse a list (efficient) */ -#define list_reverse(listtype, list) \ - MACRO_BEGIN \ - listtype *_list1=NULL, *elt; \ - list_forall_unlink(elt, list) \ - list_prepend(_list1, elt); \ - list = _list1; \ - MACRO_END - -/* insert the element ELT just before the first element TMP of the - list for which COND holds. Here COND must be a condition of ELT and - TMP. Typical usage is to insert an element into an ordered list: - for instance, list_insert_ordered(listtype, list, elt, tmp, - elt->size <= tmp->size). Note: if we give a "less than or equal" - condition, the new element will be inserted just before a sequence - of equal elements. If we give a "less than" condition, the new - element will be inserted just after a list of equal elements. - Note: it is much more efficient to construct a list with - list_prepend and then order it with list_merge_sort, than to - construct it with list_insert_ordered. */ -#define list_insert_ordered(listtype, list, elt, tmp, cond) \ - MACRO_BEGIN \ - listtype **_hook; \ - _list_find_hook(list, (tmp=*_hook, (cond)), _hook); \ - list_insert_athook(elt, _hook); \ - MACRO_END - -/* sort the given list, according to the comparison condition. - Typical usage is list_sort(listtype, list, a, b, a->size < - b->size). Note: if we give "less than or equal" condition, each - segment of equal elements will be reversed in order. If we give a - "less than" condition, each segment of equal elements will retain - the original order. The latter is slower but sometimes - prettier. Average running time: n*n/2. */ -#define list_sort(listtype, list, a, b, cond) \ - MACRO_BEGIN \ - listtype *_newlist=NULL; \ - list_forall_unlink(a, list) \ - list_insert_ordered(listtype, _newlist, a, b, cond); \ - list = _newlist; \ - MACRO_END - -/* a much faster sort algorithm (merge sort, n log n worst case). It - is required that the list type has an additional, unused next1 - component. Note there is no curious reversal of order of equal - elements as for list_sort. */ - -#define list_mergesort(listtype, list, a, b, cond) \ - MACRO_BEGIN \ - listtype *_elt, **_hook1; \ - \ - for (_elt=list; _elt; _elt=_elt->next1) { \ - _elt->next1 = _elt->next; \ - _elt->next = NULL; \ - } \ - do { \ - _hook1 = &(list); \ - while ((a = *_hook1) != NULL && (b = a->next1) != NULL ) { \ - _elt = b->next1; \ - _list_merge_cond(listtype, a, b, cond, *_hook1); \ - _hook1 = &((*_hook1)->next1); \ - *_hook1 = _elt; \ - } \ - } while (_hook1 != &(list)); \ - MACRO_END - -/* merge two sorted lists. Store result at &result */ -#define _list_merge_cond(listtype, a, b, cond, result) \ - MACRO_BEGIN \ - listtype **_hook; \ - _hook = &(result); \ - while (1) { \ - if (a==NULL) { \ - *_hook = b; \ - break; \ - } else if (b==NULL) { \ - *_hook = a; \ - break; \ - } else if (cond) { \ - *_hook = a; \ - _hook = &(a->next); \ - a = a->next; \ - } else { \ - *_hook = b; \ - _hook = &(b->next); \ - b = b->next; \ - } \ - } \ - MACRO_END - -/* ---------------------------------------------------------------------- */ -/* macros for doubly-linked lists */ - -#define dlist_append(head, end, elt) \ - MACRO_BEGIN \ - elt->prev = end; \ - elt->next = NULL; \ - if (end) { \ - end->next = elt; \ - } else { \ - head = elt; \ - } \ - end = elt; \ - MACRO_END - -/* let elt be each element of the list, unlinked. At the end, set list=NULL. */ -#define dlist_forall_unlink(elt, head, end) \ - for (elt=head; elt ? (head=elt->next, elt->next=NULL, elt->prev=NULL), 1 : (end=NULL, 0); elt=head) - -/* unlink the first element of the list */ -#define dlist_unlink_first(head, end, elt) \ - MACRO_BEGIN \ - elt = head; \ - if (head) { \ - head = head->next; \ - if (head) { \ - head->prev = NULL; \ - } else { \ - end = NULL; \ - } \ - elt->prev = NULL; \ - elt->next = NULL; \ - } \ - MACRO_END - -#endif /* PCB_HID_GCODE_LISTS_H */ Index: trunk/src/hid/gcode/trace.h =================================================================== --- trunk/src/hid/gcode/trace.h (revision 1268) +++ trunk/src/hid/gcode/trace.h (nonexistent) @@ -1,14 +0,0 @@ -/* Copyright (C) 2001-2007 Peter Selinger. - This file is part of Potrace. It is free software and it is covered - by the GNU General Public License. See the file COPYING for details. */ - -/* $Id: trace.h 147 2007-04-09 00:44:09Z selinger $ */ - -#ifndef TRACE_H -#define TRACE_H - -#include "potracelib.h" - -double process_path(path_t * plist, const potrace_param_t * param, const potrace_bitmap_t * bm, FILE * f, double scale); - -#endif /* TRACE_H */ Index: trunk/src/hid/gcode/curve.c =================================================================== --- trunk/src/hid/gcode/curve.c (revision 1268) +++ trunk/src/hid/gcode/curve.c (nonexistent) @@ -1,114 +0,0 @@ -/* Copyright (C) 2001-2007 Peter Selinger. - This file is part of Potrace. It is free software and it is covered - by the GNU General Public License. See the file COPYING for details. */ - -/* $Id: curve.c 147 2007-04-09 00:44:09Z selinger $ */ -/* private part of the path and curve data structures */ - -#include -#include -#include - -#include "potracelib.h" -#include "lists.h" -#include "curve.h" - -#define SAFE_MALLOC(var, n, typ) \ - if ((var = (typ *)malloc((n)*sizeof(typ))) == NULL) goto malloc_error - -/* ---------------------------------------------------------------------- */ -/* allocate and free path objects */ - -path_t *path_new(void) -{ - path_t *p = NULL; - privpath_t *priv = NULL; - - SAFE_MALLOC(p, 1, path_t); - memset(p, 0, sizeof(path_t)); - SAFE_MALLOC(priv, 1, privpath_t); - memset(priv, 0, sizeof(privpath_t)); - p->priv = priv; - return p; - -malloc_error: - free(p); - free(priv); - return NULL; -} - -/* free the members of the given curve structure. Leave errno unchanged. */ -static void privcurve_free_members(privcurve_t * curve) -{ - free(curve->tag); - free(curve->c); - free(curve->vertex); - free(curve->alpha); - free(curve->alpha0); - free(curve->beta); -} - -/* free a path. Leave errno untouched. */ -void path_free(path_t * p) -{ - if (p) { - if (p->priv) { - free(p->priv->pt); - free(p->priv->lon); - free(p->priv->sums); - free(p->priv->po); - privcurve_free_members(&p->priv->curve); - privcurve_free_members(&p->priv->ocurve); - } - free(p->priv); - /* do not free p->fcurve ! */ - } - free(p); -} - -/* free a pathlist, leaving errno untouched. */ -void pathlist_free(path_t * plist) -{ - path_t *p; - - list_forall_unlink(p, plist) { - path_free(p); - } -} - -/* ---------------------------------------------------------------------- */ -/* initialize and finalize curve structures */ - -typedef dpoint_t dpoint3_t[3]; - -/* initialize the members of the given curve structure to size m. - Return 0 on success, 1 on error with errno set. */ -int privcurve_init(privcurve_t * curve, int n) -{ - memset(curve, 0, sizeof(privcurve_t)); - curve->n = n; - SAFE_MALLOC(curve->tag, n, int); - SAFE_MALLOC(curve->c, n, dpoint3_t); - SAFE_MALLOC(curve->vertex, n, dpoint_t); - SAFE_MALLOC(curve->alpha, n, double); - SAFE_MALLOC(curve->alpha0, n, double); - SAFE_MALLOC(curve->beta, n, double); - return 0; - -malloc_error: - free(curve->tag); - free(curve->c); - free(curve->vertex); - free(curve->alpha); - free(curve->alpha0); - free(curve->beta); - return 1; -} - -/* copy private to public curve structure */ -void privcurve_to_curve(privcurve_t * pc, potrace_curve_t * c) -{ - c->n = pc->n; - c->tag = pc->tag; - c->c = pc->c; -} Index: trunk/src/hid/gcode/gcode.h =================================================================== --- trunk/src/hid/gcode/gcode.h (revision 1268) +++ trunk/src/hid/gcode/gcode.h (nonexistent) @@ -1,3 +0,0 @@ -/* $Id: nelma.h,v 1.2 2007/04/20 11:31:15 danmc Exp $ */ -extern const char *gcode_cookie; -extern HID gcode_hid; Index: trunk/src/hid/gcode/hid.conf =================================================================== --- trunk/src/hid/gcode/hid.conf (revision 1268) +++ trunk/src/hid/gcode/hid.conf (nonexistent) @@ -1 +0,0 @@ -type=export Index: trunk/src/hid/gcode/curve.h =================================================================== --- trunk/src/hid/gcode/curve.h (revision 1268) +++ trunk/src/hid/gcode/curve.h (nonexistent) @@ -1,76 +0,0 @@ -/* Copyright (C) 2001-2007 Peter Selinger. - This file is part of Potrace. It is free software and it is covered - by the GNU General Public License. See the file COPYING for details. */ - -#ifndef CURVE_H -#define CURVE_H - -#include "auxiliary.h" - -/* vertex is c[1] for tag=POTRACE_CORNER, and the intersection of - .c[-1][2]..c[0] and c[1]..c[2] for tag=POTRACE_CURVETO. alpha is only - defined for tag=POTRACE_CURVETO and is the alpha parameter of the curve: - .c[-1][2]..c[0] = alpha*(.c[-1][2]..vertex), and - c[2]..c[1] = alpha*(c[2]..vertex). - Beta is so that (.beta[i])[.vertex[i],.vertex[i+1]] = .c[i][2]. -*/ - -struct privcurve_s { - int n; /* number of segments */ - int *tag; /* tag[n]: POTRACE_CORNER or POTRACE_CURVETO */ - dpoint_t(*c)[3]; /* c[n][i]: control points. - c[n][0] is unused for tag[n]=POTRACE_CORNER */ - /* the remainder of this structure is special to privcurve, and is - used in EPS debug output and special EPS "short coding". These - fields are valid only if "alphacurve" is set. */ - int alphacurve; /* have the following fields been initialized? */ - dpoint_t *vertex; /* for POTRACE_CORNER, this equals c[1] */ - double *alpha; /* only for POTRACE_CURVETO */ - double *alpha0; /* "uncropped" alpha parameter - for debug output only */ - double *beta; -}; -typedef struct privcurve_s privcurve_t; - -struct sums_s { - double x; - double y; - double x2; - double xy; - double y2; -}; -typedef struct sums_s sums_t; - -/* the path structure is filled in with information about a given path - as it is accumulated and passed through the different stages of the - Potrace algorithm. Backends only need to read the fcurve and fm - fields of this data structure, but debugging backends may read - other fields. */ -struct potrace_privpath_s { - int len; - point_t *pt; /* pt[len]: path as extracted from bitmap */ - int *lon; /* lon[len]: (i,lon[i]) = longest straight line from i */ - - int x0, y0; /* origin for sums */ - sums_t *sums; /* sums[len+1]: cache for fast summing */ - - int m; /* length of optimal polygon */ - int *po; /* po[m]: optimal polygon */ - - privcurve_t curve; /* curve[m]: array of curve elements */ - privcurve_t ocurve; /* ocurve[om]: array of curve elements */ - privcurve_t *fcurve; /* final curve: this points to either curve or - ocurve. Do not free this separately. */ -}; -typedef struct potrace_privpath_s potrace_privpath_t; - -/* shorter names */ -typedef potrace_privpath_t privpath_t; -typedef potrace_path_t path_t; - -path_t *path_new(void); -void path_free(path_t * p); -void pathlist_free(path_t * plist); -int privcurve_init(privcurve_t * curve, int n); -void privcurve_to_curve(privcurve_t * pc, potrace_curve_t * c); - -#endif /* CURVE_H */ Index: trunk/src/hid/gcode/potracelib.h =================================================================== --- trunk/src/hid/gcode/potracelib.h (revision 1268) +++ trunk/src/hid/gcode/potracelib.h (nonexistent) @@ -1,130 +0,0 @@ -/* Copyright (C) 2001-2007 Peter Selinger. - This file is part of Potrace. It is free software and it is covered - by the GNU General Public License. See the file COPYING for details. */ - -#ifndef POTRACELIB_H -#define POTRACELIB_H - -/* this file defines the API for the core Potrace library. For a more - detailed description of the API, see doc/potracelib.txt */ - -/* ---------------------------------------------------------------------- */ -/* tracing parameters */ - -/* turn policies */ -#define POTRACE_TURNPOLICY_BLACK 0 -#define POTRACE_TURNPOLICY_WHITE 1 -#define POTRACE_TURNPOLICY_LEFT 2 -#define POTRACE_TURNPOLICY_RIGHT 3 -#define POTRACE_TURNPOLICY_MINORITY 4 -#define POTRACE_TURNPOLICY_MAJORITY 5 -#define POTRACE_TURNPOLICY_RANDOM 6 - -/* structure to hold progress bar callback data */ -struct potrace_progress_s { - void (*callback) (double progress, void *privdata); /* callback fn */ - void *data; /* callback function's private data */ - double min, max; /* desired range of progress, e.g. 0.0 to 1.0 */ - double epsilon; /* granularity: can skip smaller increments */ -}; -typedef struct potrace_progress_s potrace_progress_t; - -/* structure to hold tracing parameters */ -struct potrace_param_s { - int turdsize; /* area of largest path to be ignored */ - int turnpolicy; /* resolves ambiguous turns in path decomposition */ - double alphamax; /* corner threshold */ - int opticurve; /* use curve optimization? */ - double opttolerance; /* curve optimization tolerance */ - potrace_progress_t progress; /* progress callback function */ -}; -typedef struct potrace_param_s potrace_param_t; - -/* ---------------------------------------------------------------------- */ -/* bitmaps */ - -/* native word size */ -typedef unsigned long potrace_word; - -/* Internal bitmap format. The n-th scanline starts at scanline(n) = - (map + n*dy). Raster data is stored as a sequence of potrace_words - (NOT bytes). The leftmost bit of scanline n is the most significant - bit of scanline(n)[0]. */ -struct potrace_bitmap_s { - int w, h; /* width and height, in pixels */ - int dy; /* words per scanline (not bytes) */ - potrace_word *map; /* raw data, dy*h words */ -}; -typedef struct potrace_bitmap_s potrace_bitmap_t; - -/* ---------------------------------------------------------------------- */ -/* curves */ - -/* point */ -struct potrace_dpoint_s { - double x, y; -}; -typedef struct potrace_dpoint_s potrace_dpoint_t; - -/* segment tags */ -#define POTRACE_CURVETO 1 -#define POTRACE_CORNER 2 - -/* closed curve segment */ -struct potrace_curve_s { - int n; /* number of segments */ - int *tag; /* tag[n]: POTRACE_CURVETO or POTRACE_CORNER */ - potrace_dpoint_t(*c)[3]; /* c[n][3]: control points. - c[n][0] is unused for tag[n]=POTRACE_CORNER */ -}; -typedef struct potrace_curve_s potrace_curve_t; - -/* Linked list of signed curve segments. Also carries a tree structure. */ -struct potrace_path_s { - int area; /* area of the bitmap path */ - int sign; /* '+' or '-', depending on orientation */ - potrace_curve_t curve; /* this path's vector data */ - - struct potrace_path_s *next; /* linked list structure */ - - struct potrace_path_s *childlist; /* tree structure */ - struct potrace_path_s *sibling; /* tree structure */ - - struct potrace_privpath_s *priv; /* private state */ -}; -typedef struct potrace_path_s potrace_path_t; - -/* ---------------------------------------------------------------------- */ -/* Potrace state */ - -#define POTRACE_STATUS_OK 0 -#define POTRACE_STATUS_INCOMPLETE 1 - -struct potrace_state_s { - int status; - potrace_path_t *plist; /* vector data */ - - struct potrace_privstate_s *priv; /* private state */ -}; -typedef struct potrace_state_s potrace_state_t; - -/* ---------------------------------------------------------------------- */ -/* API functions */ - -/* get default parameters */ -potrace_param_t *potrace_param_default(void); - -/* free parameter set */ -void potrace_param_free(potrace_param_t * p); - -/* trace a bitmap*/ -potrace_state_t *potrace_trace(const potrace_param_t * param, const potrace_bitmap_t * bm); - -/* free a Potrace state */ -void potrace_state_free(potrace_state_t * st); - -/* return a static plain text version string identifying this version - of potracelib */ -char *potrace_version(void); - -#endif /* POTRACELIB_H */ Index: trunk/src/hid/gcode/auxiliary.h =================================================================== --- trunk/src/hid/gcode/auxiliary.h (revision 1268) +++ trunk/src/hid/gcode/auxiliary.h (nonexistent) @@ -1,80 +0,0 @@ -/* Copyright (C) 2001-2007 Peter Selinger. - This file is part of Potrace. It is free software and it is covered - by the GNU General Public License. See the file COPYING for details. */ - -/* This header file collects some general-purpose macros (and static - inline functions) that are used in various places. */ - -#ifndef AUXILIARY_H -#define AUXILIARY_H - -#include "config.h" - -/* ---------------------------------------------------------------------- */ -/* point arithmetic */ - -#include "potracelib.h" - -struct point_s { - long x; - long y; -}; -typedef struct point_s point_t; - -typedef potrace_dpoint_t dpoint_t; - -/* convert point_t to dpoint_t */ -static inline dpoint_t dpoint(point_t p) -{ - dpoint_t res; - res.x = p.x; - res.y = p.y; - return res; -} - -/* range over the straight line segment [a,b] when lambda ranges over [0,1] */ -static inline dpoint_t interval(double lambda, dpoint_t a, dpoint_t b) -{ - dpoint_t res; - - res.x = a.x + lambda * (b.x - a.x); - res.y = a.y + lambda * (b.y - a.y); - return res; -} - -/* ---------------------------------------------------------------------- */ -/* some useful macros. Note: the "mod" macro works correctly for - negative a. Also note that the test for a>=n, while redundant, - speeds up the mod function by 70% in the average case (significant - since the program spends about 16% of its time here - or 40% - without the test). The "floordiv" macro returns the largest integer - <= a/n, and again this works correctly for negative a, as long as - a,n are integers and n>0. */ - -/* integer arithmetic */ - -static inline int mod(int a, int n) -{ - return a >= n ? a % n : a >= 0 ? a : n - 1 - (-1 - a) % n; -} - -static inline int floordiv(int a, int n) -{ - return a >= 0 ? a / n : -1 - (-1 - a) / n; -} - -/* Note: the following work for integers and other numeric types. */ -#undef sign -#undef abs -#undef min -#undef max -#undef sq -#undef cu -#define sign(x) ((x)>0 ? 1 : (x)<0 ? -1 : 0) -#define abs(a) ((a)>0 ? (a) : -(a)) -#define min(a,b) ((a)<(b) ? (a) : (b)) -#define max(a,b) ((a)>(b) ? (a) : (b)) -#define sq(a) ((a)*(a)) -#define cu(a) ((a)*(a)*(a)) - -#endif /* AUXILIARY_H */ Index: trunk/src/hid/nelma/hid.conf =================================================================== --- trunk/src/hid/nelma/hid.conf (revision 1268) +++ trunk/src/hid/nelma/hid.conf (nonexistent) @@ -1 +0,0 @@ -type=export Index: trunk/src/hid/nelma/nelma.c =================================================================== --- trunk/src/hid/nelma/nelma.c (revision 1268) +++ trunk/src/hid/nelma/nelma.c (nonexistent) @@ -1,1033 +0,0 @@ -/* - * COPYRIGHT - * - * PCB, interactive printed circuit board design - * - * NELMA (Numerical capacitance calculator) export HID - * Copyright (C) 2006 Tomaz Solc (tomaz.solc@tablix.org) - * - * PNG export code is based on the PNG export HID - * Copyright (C) 2006 Dan McMahill - * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - */ - -/* - * This HID exports a PCB layout into: o One layer mask file (PNG format) per - * copper layer. o Nelma configuration file that contains netlist and pin - * information. - */ - -/* - * FIXME: - * - * If you have a section of a net that does not contain any pins then that - * section will be missing from the Nelma's copper geometry. - * - * For example: - * - * this section will be ignored by Nelma | | - * - * || ||=======|| || component layer || - * || || || ||=============|| ||============|| - * solder layer - * - * pin1 via via pin2 - * - * Single layer layouts are always exported correctly. - * - */ - -#include "config.h" - -#include -#include -#include -#include -#include - -#include - -#include "global.h" -#include "error.h" /* Message() */ -#include "data.h" -#include "misc.h" -#include "rats.h" - -#include "hid.h" -#include "../hidint.h" -#include "hid/common/hidnogui.h" -#include "hid/common/draw_helpers.h" - -#include - -#include "hid/common/hidinit.h" - - -RCSID("$Id$"); - -const char *nelma_cookie = "nelma HID"; - -#define CRASH fprintf(stderr, "HID error: pcb called unimplemented PNG function %s.\n", __FUNCTION__); abort() - -/* Needed for PNG export */ - -struct color_struct { - /* the descriptor used by the gd library */ - int c; - - /* so I can figure out what rgb value c refers to */ - unsigned int r, g, b; -}; - -struct hid_gc_struct { - HID *me_pointer; - EndCapStyle cap; - Coord width; - unsigned char r, g, b; - int erase; - int faded; - struct color_struct *color; - gdImagePtr brush; -}; - -static HID nelma_hid; - -static struct color_struct *black = NULL, *white = NULL; -static Coord linewidth = -1; -static gdImagePtr lastbrush = (gdImagePtr) ((void *) -1); -static int lastcolor = -1; - -/* gd image and file for PNG export */ -static gdImagePtr nelma_im = NULL; -static FILE *nelma_f = NULL; - -static int is_mask; -static int is_drill; - -/* - * Which groups of layers to export into PNG layer masks. 1 means export, 0 - * means do not export. - */ -static int nelma_export_group[MAX_LAYER]; - -/* Group that is currently exported. */ -static int nelma_cur_group; - -/* Filename prefix that will be used when saving files. */ -static const char *nelma_basename = NULL; - -/* Horizontal DPI (grid points per inch) */ -static int nelma_dpi = -1; - -/* Height of the copper layers in micrometers. */ - -/* - * The height of the copper layer is currently taken as the vertical grid - * step, since this is the smallest vertical feature in the layout. - */ -static int nelma_copperh = -1; -/* Height of the substrate layers in micrometers. */ -static int nelma_substrateh = -1; -/* Relative permittivity of the substrate. */ -static double nelma_substratee = -1; - -/* Permittivity of empty space (As/Vm) */ -static const double nelma_air_epsilon = 8.85e-12; - -HID_Attribute nelma_attribute_list[] = { - /* other HIDs expect this to be first. */ - -/* %start-doc options "nelma Options" -@ftable @code -@item -- basename -File name prefix. -@end ftable -%end-doc -*/ - {"basename", "File name prefix", - HID_String, 0, 0, {0, 0, 0}, 0, 0}, -#define HA_basename 0 - -/* %start-doc options "nelma Options" -@ftable @code -@item --dpi -Horizontal scale factor (grid points/inch). -@end ftable -%end-doc -*/ - {"dpi", "Horizontal scale factor (grid points/inch)", - HID_Integer, 0, 1000, {100, 0, 0}, 0, 0}, -#define HA_dpi 1 - -/* %start-doc options "nelma Options" -@ftable @code -@item --copper-height -Copper layer height (um). -@end ftable -%end-doc -*/ - {"copper-height", "Copper layer height (um)", - HID_Integer, 0, 200, {100, 0, 0}, 0, 0}, -#define HA_copperh 2 - -/* %start-doc options "nelma Options" -@ftable @code -@item --substrate-height -Substrate layer height (um). -@end ftable -%end-doc -*/ - {"substrate-height", "Substrate layer height (um)", - HID_Integer, 0, 10000, {2000, 0, 0}, 0, 0}, -#define HA_substrateh 3 - -/* %start-doc options "nelma Options" -@ftable @code -@item --substrate-epsilon -Substrate relative epsilon. -@end ftable -%end-doc -*/ - {"substrate-epsilon", "Substrate relative epsilon", - HID_Real, 0, 100, {0, 0, 4.0}, 0, 0}, -#define HA_substratee 4 -}; - -#define NUM_OPTIONS (sizeof(nelma_attribute_list)/sizeof(nelma_attribute_list[0])) - -REGISTER_ATTRIBUTES(nelma_attribute_list, nelma_cookie) - static HID_Attr_Val nelma_values[NUM_OPTIONS]; - -/* *** Utility funcions **************************************************** */ - -/* convert from default PCB units to nelma units */ - static int pcb_to_nelma(Coord pcb) -{ - return COORD_TO_INCH(pcb) * nelma_dpi; -} - -static char *nelma_get_png_name(const char *basename, const char *suffix) -{ - char *buf; - int len; - - len = strlen(basename) + strlen(suffix) + 6; - buf = (char *) malloc(sizeof(*buf) * len); - - sprintf(buf, "%s.%s.png", basename, suffix); - - return buf; -} - -/* Retrieves coordinates (in default PCB units) of a pin or pad. */ -/* Copied from netlist.c */ -static int pin_name_to_xy(LibraryEntryType * pin, Coord * x, Coord * y) -{ - ConnectionType conn; - if (!SeekPad(pin, &conn, false)) - return 1; - switch (conn.type) { - case PIN_TYPE: - *x = ((PinType *) (conn.ptr2))->X; - *y = ((PinType *) (conn.ptr2))->Y; - return 0; - case PAD_TYPE: - *x = ((PadType *) (conn.ptr2))->Point1.X; - *y = ((PadType *) (conn.ptr2))->Point1.Y; - return 0; - } - return 1; -} - -/* *** Exporting netlist data and geometry to the nelma config file ******** */ - -static void nelma_write_space(FILE * out) -{ - double xh, zh; - - int z; - int i, idx; - const char *ext; - - xh = 2.54e-2 / ((double) nelma_dpi); - zh = nelma_copperh * 1e-6; - - fprintf(out, "\n/* **** Space **** */\n\n"); - - fprintf(out, "space pcb {\n"); - fprintf(out, "\tstep = { %e, %e, %e }\n", xh, xh, zh); - fprintf(out, "\tlayers = {\n"); - - fprintf(out, "\t\t\"air-top\",\n"); - fprintf(out, "\t\t\"air-bottom\""); - - z = 10; - for (i = 0; i < MAX_LAYER; i++) - if (nelma_export_group[i]) { - idx = (i >= 0 && i < max_group) ? PCB->LayerGroups.Entries[i][0] : i; - ext = layer_type_to_file_name(idx, FNS_fixed); - - if (z != 10) { - fprintf(out, ",\n"); - fprintf(out, "\t\t\"substrate-%d\"", z); - z++; - } - fprintf(out, ",\n"); - fprintf(out, "\t\t\"%s\"", ext); - z++; - } - fprintf(out, "\n\t}\n"); - fprintf(out, "}\n"); -} - - -static void nelma_write_material(FILE * out, char *name, char *type, double e) -{ - fprintf(out, "material %s {\n", name); - fprintf(out, "\ttype = \"%s\"\n", type); - fprintf(out, "\tpermittivity = %e\n", e); - fprintf(out, "\tconductivity = 0.0\n"); - fprintf(out, "\tpermeability = 0.0\n"); - fprintf(out, "}\n"); -} - -static void nelma_write_materials(FILE * out) -{ - fprintf(out, "\n/* **** Materials **** */\n\n"); - - nelma_write_material(out, "copper", "metal", nelma_air_epsilon); - nelma_write_material(out, "air", "dielectric", nelma_air_epsilon); - nelma_write_material(out, "composite", "dielectric", nelma_air_epsilon * nelma_substratee); -} - -static void nelma_write_nets(FILE * out) -{ - LibraryType netlist; - LibraryMenuTypePtr net; - LibraryEntryTypePtr pin; - - int n, m, i, idx; - - const char *ext; - - netlist = PCB->NetlistLib[NETLIST_EDITED]; - - fprintf(out, "\n/* **** Nets **** */\n\n"); - - for (n = 0; n < netlist.MenuN; n++) { - net = &netlist.Menu[n]; - - /* Weird, but correct */ - fprintf(out, "net %s {\n", &net->Name[2]); - - fprintf(out, "\tobjects = {\n"); - - for (m = 0; m < net->EntryN; m++) { - pin = &net->Entry[m]; - - /* pin_name_to_xy(pin, &x, &y); */ - - for (i = 0; i < MAX_LAYER; i++) - if (nelma_export_group[i]) { - idx = (i >= 0 && i < max_group) ? PCB->LayerGroups.Entries[i][0] : i; - ext = layer_type_to_file_name(idx, FNS_fixed); - - if (m != 0 || i != 0) - fprintf(out, ",\n"); - fprintf(out, "\t\t\"%s-%s\"", pin->ListEntry, ext); - } - } - - fprintf(out, "\n"); - fprintf(out, "\t}\n"); - fprintf(out, "}\n"); - } -} - -static void nelma_write_layer(FILE * out, int z, int h, const char *name, int full, char *mat) -{ - LibraryType netlist; - LibraryMenuTypePtr net; - LibraryEntryTypePtr pin; - - int n, m; - - fprintf(out, "layer %s {\n", name); - fprintf(out, "\theight = %d\n", h); - fprintf(out, "\tz-order = %d\n", z); - fprintf(out, "\tmaterial = \"%s\"\n", mat); - - if (full) { - fprintf(out, "\tobjects = {\n"); - netlist = PCB->NetlistLib[NETLIST_EDITED]; - - for (n = 0; n < netlist.MenuN; n++) { - net = &netlist.Menu[n]; - - for (m = 0; m < net->EntryN; m++) { - pin = &net->Entry[m]; - - if (m != 0 || n != 0) - fprintf(out, ",\n"); - fprintf(out, "\t\t\"%s-%s\"", pin->ListEntry, name); - } - - } - fprintf(out, "\n\t}\n"); - } - fprintf(out, "}\n"); -} - -static void nelma_write_layers(FILE * out) -{ - int i, idx; - int z; - - const char *ext; - char buf[100]; - - int subh; - - subh = nelma_substrateh / nelma_copperh; - - fprintf(out, "\n/* **** Layers **** */\n\n"); - - /* Air layers on top and bottom of the stack */ - /* Their height is double substrate height. */ - nelma_write_layer(out, 1, 2 * subh, "air-top", 0, "air"); - nelma_write_layer(out, 1000, 2 * subh, "air-bottom", 0, "air"); - - z = 10; - for (i = 0; i < MAX_LAYER; i++) - if (nelma_export_group[i]) { - idx = (i >= 0 && i < max_group) ? PCB->LayerGroups.Entries[i][0] : i; - ext = layer_type_to_file_name(idx, FNS_fixed); - - if (z != 10) { - sprintf(buf, "substrate-%d", z); - nelma_write_layer(out, z, subh, buf, 0, "composite"); - z++; - } - /* - * FIXME: for layers that are not on top or bottom, - * the material should be "composite" - */ - nelma_write_layer(out, z, 1, ext, 1, "air"); - - z++; - } -} - -static void nelma_write_object(FILE * out, LibraryEntryTypePtr pin) -{ - int i, idx; - Coord px = 0, py = 0; - int x, y; - - char *f; - const char *ext; - - pin_name_to_xy(pin, &px, &py); - - x = pcb_to_nelma(px); - y = pcb_to_nelma(py); - - for (i = 0; i < MAX_LAYER; i++) - if (nelma_export_group[i]) { - idx = (i >= 0 && i < max_group) ? PCB->LayerGroups.Entries[i][0] : i; - ext = layer_type_to_file_name(idx, FNS_fixed); - - fprintf(out, "object %s-%s {\n", pin->ListEntry, ext); - fprintf(out, "\tposition = { 0, 0 }\n"); - fprintf(out, "\tmaterial = \"copper\"\n"); - fprintf(out, "\ttype = \"image\"\n"); - fprintf(out, "\trole = \"net\"\n"); - - f = nelma_get_png_name(nelma_basename, ext); - - fprintf(out, "\tfile = \"%s\"\n", f); - - free(f); - - fprintf(out, "\tfile-pos = { %d, %d }\n", x, y); - fprintf(out, "}\n"); - } -} - -static void nelma_write_objects(FILE * out) -{ - LibraryType netlist; - LibraryMenuTypePtr net; - LibraryEntryTypePtr pin; - - int n, m; - - netlist = PCB->NetlistLib[NETLIST_EDITED]; - - fprintf(out, "\n/* **** Objects **** */\n\n"); - - for (n = 0; n < netlist.MenuN; n++) { - net = &netlist.Menu[n]; - - for (m = 0; m < net->EntryN; m++) { - pin = &net->Entry[m]; - - nelma_write_object(out, pin); - } - } -} - -/* *** Main export callback ************************************************ */ - -static void nelma_parse_arguments(int *argc, char ***argv) -{ - hid_register_attributes(nelma_attribute_list, sizeof(nelma_attribute_list) / sizeof(nelma_attribute_list[0]), nelma_cookie); - hid_parse_command_line(argc, argv); -} - -static HID_Attribute *nelma_get_export_options(int *n) -{ - static char *last_made_filename = 0; - - if (PCB) { - derive_default_filename(PCB->Filename, &nelma_attribute_list[HA_basename], ".nelma", &last_made_filename); - } - if (n) { - *n = NUM_OPTIONS; - } - return nelma_attribute_list; -} - -/* Populates nelma_export_group array */ -void nelma_choose_groups() -{ - int n, m; - LayerType *layer; - - /* Set entire array to 0 (don't export any layer groups by default */ - memset(nelma_export_group, 0, sizeof(nelma_export_group)); - - for (n = 0; n < max_copper_layer; n++) { - layer = &PCB->Data->Layer[n]; - - if (!LAYER_IS_EMPTY(layer)) { - /* layer isn't empty */ - - /* - * is this check necessary? It seems that special - * layers have negative indexes? - */ - - if (SL_TYPE(n) == 0) { - /* layer is a copper layer */ - m = GetLayerGroupNumberByNumber(n); - - /* the export layer */ - nelma_export_group[m] = 1; - } - } - } -} - -static void nelma_alloc_colors() -{ - /* - * Allocate white and black -- the first color allocated becomes the - * background color - */ - - white = (struct color_struct *) malloc(sizeof(*white)); - white->r = white->g = white->b = 255; - white->c = gdImageColorAllocate(nelma_im, white->r, white->g, white->b); - - black = (struct color_struct *) malloc(sizeof(*black)); - black->r = black->g = black->b = 0; - black->c = gdImageColorAllocate(nelma_im, black->r, black->g, black->b); -} - -static void nelma_start_png(const char *basename, const char *suffix) -{ - int h, w; - char *buf; - - buf = nelma_get_png_name(basename, suffix); - - h = pcb_to_nelma(PCB->MaxHeight); - w = pcb_to_nelma(PCB->MaxWidth); - - /* nelma_im = gdImageCreate (w, h); */ - - /* Nelma only works with true color images */ - nelma_im = gdImageCreate(w, h); - nelma_f = fopen(buf, "wb"); - - nelma_alloc_colors(); - - free(buf); -} - -static void nelma_finish_png() -{ -#ifdef HAVE_GDIMAGEPNG - gdImagePng(nelma_im, nelma_f); -#else - Message("NELMA: PNG not supported by gd. Can't write layer mask.\n"); -#endif - gdImageDestroy(nelma_im); - fclose(nelma_f); - - free(white); - free(black); - - nelma_im = NULL; - nelma_f = NULL; -} - -void nelma_start_png_export() -{ - BoxType region; - - region.X1 = 0; - region.Y1 = 0; - region.X2 = PCB->MaxWidth; - region.Y2 = PCB->MaxHeight; - - linewidth = -1; - lastbrush = (gdImagePtr) ((void *) -1); - lastcolor = -1; - - hid_expose_callback(&nelma_hid, ®ion, 0); -} - -static void nelma_do_export(HID_Attr_Val * options) -{ - int save_ons[MAX_LAYER + 2]; - int i, idx; - FILE *nelma_config; - char *buf; - int len; - - time_t t; - - if (!options) { - nelma_get_export_options(0); - for (i = 0; i < NUM_OPTIONS; i++) { - nelma_values[i] = nelma_attribute_list[i].default_val; - } - options = nelma_values; - } - nelma_basename = options[HA_basename].str_value; - if (!nelma_basename) { - nelma_basename = "pcb-out"; - } - nelma_dpi = options[HA_dpi].int_value; - if (nelma_dpi < 0) { - fprintf(stderr, "ERROR: dpi may not be < 0\n"); - return; - } - nelma_copperh = options[HA_copperh].int_value; - nelma_substrateh = options[HA_substrateh].int_value; - nelma_substratee = options[HA_substratee].real_value; - - nelma_choose_groups(); - - for (i = 0; i < MAX_LAYER; i++) { - if (nelma_export_group[i]) { - - nelma_cur_group = i; - - /* magic */ - idx = (i >= 0 && i < max_group) ? PCB->LayerGroups.Entries[i][0] : i; - - nelma_start_png(nelma_basename, layer_type_to_file_name(idx, FNS_fixed)); - - hid_save_and_show_layer_ons(save_ons); - nelma_start_png_export(); - hid_restore_layer_ons(save_ons); - - nelma_finish_png(); - } - } - - len = strlen(nelma_basename) + 4; - buf = (char *) malloc(sizeof(*buf) * len); - - sprintf(buf, "%s.em", nelma_basename); - nelma_config = fopen(buf, "w"); - - free(buf); - - fprintf(nelma_config, "/* Made with PCB Nelma export HID */"); - t = time(NULL); - fprintf(nelma_config, "/* %s */", ctime(&t)); - - nelma_write_nets(nelma_config); - nelma_write_objects(nelma_config); - nelma_write_layers(nelma_config); - nelma_write_materials(nelma_config); - nelma_write_space(nelma_config); - - fclose(nelma_config); -} - -/* *** PNG export (slightly modified code from PNG export HID) ************* */ - -static int nelma_set_layer(const char *name, int group, int empty) -{ - int idx = (group >= 0 && group < max_group) ? PCB->LayerGroups.Entries[group][0] : group; - - if (name == 0) { - name = PCB->Data->Layer[idx].Name; - } - if (strcmp(name, "invisible") == 0) { - return 0; - } - is_drill = (SL_TYPE(idx) == SL_PDRILL || SL_TYPE(idx) == SL_UDRILL); - is_mask = (SL_TYPE(idx) == SL_MASK); - - if (is_mask) { - /* Don't print masks */ - return 0; - } - if (is_drill) { - /* - * Print 'holes', so that we can fill gaps in the copper - * layer - */ - return 1; - } - if (group == nelma_cur_group) { - return 1; - } - return 0; -} - -static hidGC nelma_make_gc(void) -{ - hidGC rv = (hidGC) malloc(sizeof(struct hid_gc_struct)); - rv->me_pointer = &nelma_hid; - rv->cap = Trace_Cap; - rv->width = 1; - rv->color = (struct color_struct *) malloc(sizeof(*rv->color)); - rv->color->r = rv->color->g = rv->color->b = 0; - rv->color->c = 0; - return rv; -} - -static void nelma_destroy_gc(hidGC gc) -{ - free(gc); -} - -static void nelma_use_mask(int use_it) -{ - /* does nothing */ -} - -static void nelma_set_color(hidGC gc, const char *name) -{ - if (nelma_im == NULL) { - return; - } - if (name == NULL) { - name = "#ff0000"; - } - if (!strcmp(name, "drill")) { - gc->color = black; - gc->erase = 0; - return; - } - if (!strcmp(name, "erase")) { - /* FIXME -- should be background, not white */ - gc->color = white; - gc->erase = 1; - return; - } - gc->color = black; - gc->erase = 0; - return; -} - -static void nelma_set_line_cap(hidGC gc, EndCapStyle style) -{ - gc->cap = style; -} - -static void nelma_set_line_width(hidGC gc, Coord width) -{ - gc->width = width; -} - -static void nelma_set_draw_xor(hidGC gc, int xor_) -{ - ; -} - -static void nelma_set_draw_faded(hidGC gc, int faded) -{ - gc->faded = faded; -} - -static void use_gc(hidGC gc) -{ - int need_brush = 0; - - if (gc->me_pointer != &nelma_hid) { - fprintf(stderr, "Fatal: GC from another HID passed to nelma HID\n"); - abort(); - } - if (linewidth != gc->width) { - /* Make sure the scaling doesn't erase lines completely */ - /* - if (SCALE (gc->width) == 0 && gc->width > 0) - gdImageSetThickness (im, 1); - else - */ - gdImageSetThickness(nelma_im, pcb_to_nelma(gc->width)); - linewidth = gc->width; - need_brush = 1; - } - if (lastbrush != gc->brush || need_brush) { - static void *bcache = 0; - hidval bval; - char name[256]; - char type; - int r; - - switch (gc->cap) { - case Round_Cap: - case Trace_Cap: - type = 'C'; - r = pcb_to_nelma(gc->width / 2); - break; - default: - case Square_Cap: - r = pcb_to_nelma(gc->width); - type = 'S'; - break; - } - sprintf(name, "#%.2x%.2x%.2x_%c_%d", gc->color->r, gc->color->g, gc->color->b, type, r); - - if (hid_cache_color(0, name, &bval, &bcache)) { - gc->brush = (gdImagePtr) bval.ptr; - } - else { - int bg, fg; - if (type == 'C') - gc->brush = gdImageCreate(2 * r + 1, 2 * r + 1); - else - gc->brush = gdImageCreate(r + 1, r + 1); - bg = gdImageColorAllocate(gc->brush, 255, 255, 255); - fg = gdImageColorAllocate(gc->brush, gc->color->r, gc->color->g, gc->color->b); - gdImageColorTransparent(gc->brush, bg); - - /* - * if we shrunk to a radius/box width of zero, then just use - * a single pixel to draw with. - */ - if (r == 0) - gdImageFilledRectangle(gc->brush, 0, 0, 0, 0, fg); - else { - if (type == 'C') - gdImageFilledEllipse(gc->brush, r, r, 2 * r, 2 * r, fg); - else - gdImageFilledRectangle(gc->brush, 0, 0, r, r, fg); - } - bval.ptr = gc->brush; - hid_cache_color(1, name, &bval, &bcache); - } - - gdImageSetBrush(nelma_im, gc->brush); - lastbrush = gc->brush; - - } -#define CBLEND(gc) (((gc->r)<<24)|((gc->g)<<16)|((gc->b)<<8)|(gc->faded)) - if (lastcolor != CBLEND(gc)) { - if (is_drill || is_mask) { -#ifdef FIXME - fprintf(f, "%d gray\n", gc->erase ? 0 : 1); -#endif - lastcolor = 0; - } - else { - double r, g, b; - r = gc->r; - g = gc->g; - b = gc->b; - if (gc->faded) { - r = 0.8 * 255 + 0.2 * r; - g = 0.8 * 255 + 0.2 * g; - b = 0.8 * 255 + 0.2 * b; - } -#ifdef FIXME - if (gc->r == gc->g && gc->g == gc->b) - fprintf(f, "%g gray\n", r / 255.0); - else - fprintf(f, "%g %g %g rgb\n", r / 255.0, g / 255.0, b / 255.0); -#endif - lastcolor = CBLEND(gc); - } - } -} - -static void nelma_draw_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2) -{ - use_gc(gc); - gdImageRectangle(nelma_im, pcb_to_nelma(x1), pcb_to_nelma(y1), pcb_to_nelma(x2), pcb_to_nelma(y2), gc->color->c); -} - -static void nelma_fill_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2) -{ - use_gc(gc); - gdImageSetThickness(nelma_im, 0); - linewidth = 0; - gdImageFilledRectangle(nelma_im, pcb_to_nelma(x1), pcb_to_nelma(y1), pcb_to_nelma(x2), pcb_to_nelma(y2), gc->color->c); -} - -static void nelma_draw_line(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2) -{ - if (x1 == x2 && y1 == y2) { - Coord w = gc->width / 2; - nelma_fill_rect(gc, x1 - w, y1 - w, x1 + w, y1 + w); - return; - } - use_gc(gc); - - gdImageSetThickness(nelma_im, 0); - linewidth = 0; - gdImageLine(nelma_im, pcb_to_nelma(x1), pcb_to_nelma(y1), pcb_to_nelma(x2), pcb_to_nelma(y2), gdBrushed); -} - -static void nelma_draw_arc(hidGC gc, Coord cx, Coord cy, Coord width, Coord height, Angle start_angle, Angle delta_angle) -{ - Angle sa, ea; - - /* - * in gdImageArc, 0 degrees is to the right and +90 degrees is down - * in pcb, 0 degrees is to the left and +90 degrees is down - */ - start_angle = 180 - start_angle; - delta_angle = -delta_angle; - if (delta_angle > 0) { - sa = start_angle; - ea = start_angle + delta_angle; - } - else { - sa = start_angle + delta_angle; - ea = start_angle; - } - - /* - * make sure we start between 0 and 360 otherwise gd does strange - * things - */ - sa = NormalizeAngle(sa); - ea = NormalizeAngle(ea); - -#if 0 - printf("draw_arc %d,%d %dx%d %d..%d %d..%d\n", cx, cy, width, height, start_angle, delta_angle, sa, ea); - printf("gdImageArc (%p, %d, %d, %d, %d, %d, %d, %d)\n", - im, SCALE_X(cx), SCALE_Y(cy), SCALE(width), SCALE(height), sa, ea, gc->color->c); -#endif - use_gc(gc); - gdImageSetThickness(nelma_im, 0); - linewidth = 0; - gdImageArc(nelma_im, pcb_to_nelma(cx), pcb_to_nelma(cy), - pcb_to_nelma(2 * width), pcb_to_nelma(2 * height), sa, ea, gdBrushed); -} - -static void nelma_fill_circle(hidGC gc, Coord cx, Coord cy, Coord radius) -{ - use_gc(gc); - - gdImageSetThickness(nelma_im, 0); - linewidth = 0; - gdImageFilledEllipse(nelma_im, pcb_to_nelma(cx), pcb_to_nelma(cy), - pcb_to_nelma(2 * radius), pcb_to_nelma(2 * radius), gc->color->c); - -} - -static void nelma_fill_polygon(hidGC gc, int n_coords, Coord * x, Coord * y) -{ - int i; - gdPoint *points; - - points = (gdPoint *) malloc(n_coords * sizeof(gdPoint)); - if (points == NULL) { - fprintf(stderr, "ERROR: nelma_fill_polygon(): malloc failed\n"); - exit(1); - } - use_gc(gc); - for (i = 0; i < n_coords; i++) { - points[i].x = pcb_to_nelma(x[i]); - points[i].y = pcb_to_nelma(y[i]); - } - gdImageSetThickness(nelma_im, 0); - linewidth = 0; - gdImageFilledPolygon(nelma_im, points, n_coords, gc->color->c); - free(points); -} - -static void nelma_calibrate(double xval, double yval) -{ - CRASH; -} - -static void nelma_set_crosshair(int x, int y, int a) -{ -} - -/* *** Miscellaneous ******************************************************* */ - -#include "dolists.h" - -void hid_nelma_init() -{ - memset(&nelma_hid, 0, sizeof(HID)); - - common_nogui_init(&nelma_hid); - common_draw_helpers_init(&nelma_hid); - - nelma_hid.struct_size = sizeof(HID); - nelma_hid.name = "nelma"; - nelma_hid.description = "Numerical analysis package export"; - nelma_hid.exporter = 1; - nelma_hid.poly_before = 1; - - nelma_hid.get_export_options = nelma_get_export_options; - nelma_hid.do_export = nelma_do_export; - nelma_hid.parse_arguments = nelma_parse_arguments; - nelma_hid.set_layer = nelma_set_layer; - nelma_hid.make_gc = nelma_make_gc; - nelma_hid.destroy_gc = nelma_destroy_gc; - nelma_hid.use_mask = nelma_use_mask; - nelma_hid.set_color = nelma_set_color; - nelma_hid.set_line_cap = nelma_set_line_cap; - nelma_hid.set_line_width = nelma_set_line_width; - nelma_hid.set_draw_xor = nelma_set_draw_xor; - nelma_hid.set_draw_faded = nelma_set_draw_faded; - nelma_hid.draw_line = nelma_draw_line; - nelma_hid.draw_arc = nelma_draw_arc; - nelma_hid.draw_rect = nelma_draw_rect; - nelma_hid.fill_circle = nelma_fill_circle; - nelma_hid.fill_polygon = nelma_fill_polygon; - nelma_hid.fill_rect = nelma_fill_rect; - nelma_hid.calibrate = nelma_calibrate; - nelma_hid.set_crosshair = nelma_set_crosshair; - - hid_register_hid(&nelma_hid); -} Index: trunk/src/hid/common/actions.c =================================================================== --- trunk/src/hid/common/actions.c (revision 1268) +++ trunk/src/hid/common/actions.c (revision 1269) @@ -72,7 +72,7 @@ } } -void hid_register_action(const HID_Action * a, void *cookie) +void hid_register_action(const HID_Action * a, const char *cookie) { hid_register_actions(a, 1, cookie); } Index: trunk/src_plugins/export_gcode/Makefile =================================================================== --- trunk/src_plugins/export_gcode/Makefile (nonexistent) +++ trunk/src_plugins/export_gcode/Makefile (revision 1269) @@ -0,0 +1,5 @@ +all: + cd ../../src && make mod_export_gcode + +clean: + rm *.o *.so 2>/dev/null ; true Index: trunk/src_plugins/export_gcode/Plug.tmpasm =================================================================== --- trunk/src_plugins/export_gcode/Plug.tmpasm (nonexistent) +++ trunk/src_plugins/export_gcode/Plug.tmpasm (revision 1269) @@ -0,0 +1,15 @@ +append /local/pcb/export_gcode/enable {} +append /local/pcb/export_gcode/buildin {} + +put /local/pcb/mod {export_gcode} +put /local/pcb/mod/OBJS [@ $(PLUGDIR)/export_gcode/gcode.o $(PLUGDIR)/export_gcode/decompose.o $(PLUGDIR)/export_gcode/trace.o $(PLUGDIR)/export_gcode/curve.o @] + +if /local/pcb/export_gcode/enable then + if /local/pcb/export_gcode/buildin then + include {Makefile.in.mod/Buildin} + else + include {Makefile.in.mod/Plugin} + end +else + include {Makefile.in.mod/Disable} +end Index: trunk/src_plugins/export_gcode/README =================================================================== --- trunk/src_plugins/export_gcode/README (nonexistent) +++ trunk/src_plugins/export_gcode/README (revision 1269) @@ -0,0 +1,4 @@ +Export to gcode + +#state: works +#default: buildin Index: trunk/src_plugins/export_gcode/auxiliary.h =================================================================== --- trunk/src_plugins/export_gcode/auxiliary.h (nonexistent) +++ trunk/src_plugins/export_gcode/auxiliary.h (revision 1269) @@ -0,0 +1,80 @@ +/* Copyright (C) 2001-2007 Peter Selinger. + This file is part of Potrace. It is free software and it is covered + by the GNU General Public License. See the file COPYING for details. */ + +/* This header file collects some general-purpose macros (and static + inline functions) that are used in various places. */ + +#ifndef AUXILIARY_H +#define AUXILIARY_H + +#include "config.h" + +/* ---------------------------------------------------------------------- */ +/* point arithmetic */ + +#include "potracelib.h" + +struct point_s { + long x; + long y; +}; +typedef struct point_s point_t; + +typedef potrace_dpoint_t dpoint_t; + +/* convert point_t to dpoint_t */ +static inline dpoint_t dpoint(point_t p) +{ + dpoint_t res; + res.x = p.x; + res.y = p.y; + return res; +} + +/* range over the straight line segment [a,b] when lambda ranges over [0,1] */ +static inline dpoint_t interval(double lambda, dpoint_t a, dpoint_t b) +{ + dpoint_t res; + + res.x = a.x + lambda * (b.x - a.x); + res.y = a.y + lambda * (b.y - a.y); + return res; +} + +/* ---------------------------------------------------------------------- */ +/* some useful macros. Note: the "mod" macro works correctly for + negative a. Also note that the test for a>=n, while redundant, + speeds up the mod function by 70% in the average case (significant + since the program spends about 16% of its time here - or 40% + without the test). The "floordiv" macro returns the largest integer + <= a/n, and again this works correctly for negative a, as long as + a,n are integers and n>0. */ + +/* integer arithmetic */ + +static inline int mod(int a, int n) +{ + return a >= n ? a % n : a >= 0 ? a : n - 1 - (-1 - a) % n; +} + +static inline int floordiv(int a, int n) +{ + return a >= 0 ? a / n : -1 - (-1 - a) / n; +} + +/* Note: the following work for integers and other numeric types. */ +#undef sign +#undef abs +#undef min +#undef max +#undef sq +#undef cu +#define sign(x) ((x)>0 ? 1 : (x)<0 ? -1 : 0) +#define abs(a) ((a)>0 ? (a) : -(a)) +#define min(a,b) ((a)<(b) ? (a) : (b)) +#define max(a,b) ((a)>(b) ? (a) : (b)) +#define sq(a) ((a)*(a)) +#define cu(a) ((a)*(a)*(a)) + +#endif /* AUXILIARY_H */ Index: trunk/src_plugins/export_gcode/bitmap.h =================================================================== --- trunk/src_plugins/export_gcode/bitmap.h (nonexistent) +++ trunk/src_plugins/export_gcode/bitmap.h (revision 1269) @@ -0,0 +1,104 @@ +/* Copyright (C) 2001-2007 Peter Selinger. + This file is part of Potrace. It is free software and it is covered + by the GNU General Public License. See the file COPYING for details. */ + +#ifndef BITMAP_H +#define BITMAP_H + +#include "config.h" + +#include +#include + +/* The bitmap type is defined in potracelib.h */ +#include "potracelib.h" + +/* The present file defines some convenient macros and static inline + functions for accessing bitmaps. Since they only produce inline + code, they can be conveniently shared by the library and frontends, + if desired */ + +/* ---------------------------------------------------------------------- */ +/* some measurements */ + +#define BM_WORDSIZE ((int)sizeof(potrace_word)) +#define BM_WORDBITS (8*BM_WORDSIZE) +#define BM_HIBIT (((potrace_word)1)<<(BM_WORDBITS-1)) +#define BM_ALLBITS (~(potrace_word)0) + +/* macros for accessing pixel at index (x,y). U* macros omit the + bounds check. */ + +#define bm_scanline(bm, y) ((bm)->map + (y)*(bm)->dy) +#define bm_index(bm, x, y) (&bm_scanline(bm, y)[(x)/BM_WORDBITS]) +#define bm_mask(x) (BM_HIBIT >> ((x) & (BM_WORDBITS-1))) +#define bm_range(x, a) ((int)(x) >= 0 && (int)(x) < (a)) +#define bm_safe(bm, x, y) (bm_range(x, (bm)->w) && bm_range(y, (bm)->h)) +#define BM_UGET(bm, x, y) ((*bm_index(bm, x, y) & bm_mask(x)) != 0) +#define BM_USET(bm, x, y) (*bm_index(bm, x, y) |= bm_mask(x)) +#define BM_UCLR(bm, x, y) (*bm_index(bm, x, y) &= ~bm_mask(x)) +#define BM_UINV(bm, x, y) (*bm_index(bm, x, y) ^= bm_mask(x)) +#define BM_UPUT(bm, x, y, b) ((b) ? BM_USET(bm, x, y) : BM_UCLR(bm, x, y)) +#define BM_GET(bm, x, y) (bm_safe(bm, x, y) ? BM_UGET(bm, x, y) : 0) +#define BM_SET(bm, x, y) (bm_safe(bm, x, y) ? BM_USET(bm, x, y) : 0) +#define BM_CLR(bm, x, y) (bm_safe(bm, x, y) ? BM_UCLR(bm, x, y) : 0) +#define BM_INV(bm, x, y) (bm_safe(bm, x, y) ? BM_UINV(bm, x, y) : 0) +#define BM_PUT(bm, x, y, b) (bm_safe(bm, x, y) ? BM_UPUT(bm, x, y, b) : 0) + +/* free the given bitmap. Leaves errno untouched. */ +static inline void bm_free(potrace_bitmap_t * bm) +{ + if (bm) { + free(bm->map); + } + free(bm); +} + +/* return new un-initialized bitmap. NULL with errno on error */ +static inline potrace_bitmap_t *bm_new(int w, int h) +{ + potrace_bitmap_t *bm; + int dy = (w + BM_WORDBITS - 1) / BM_WORDBITS; + + bm = (potrace_bitmap_t *) malloc(sizeof(potrace_bitmap_t)); + if (!bm) { + return NULL; + } + bm->w = w; + bm->h = h; + bm->dy = dy; + bm->map = (potrace_word *) malloc(dy * h * BM_WORDSIZE); + if (!bm->map) { + free(bm); + return NULL; + } + return bm; +} + +/* clear the given bitmap. Set all bits to c. */ +static inline void bm_clear(potrace_bitmap_t * bm, int c) +{ + memset(bm->map, c ? -1 : 0, bm->dy * bm->h * BM_WORDSIZE); +} + +/* duplicate the given bitmap. Return NULL on error with errno set. */ +static inline potrace_bitmap_t *bm_dup(const potrace_bitmap_t * bm) +{ + potrace_bitmap_t *bm1 = bm_new(bm->w, bm->h); + if (!bm1) { + return NULL; + } + memcpy(bm1->map, bm->map, bm->dy * bm->h * BM_WORDSIZE); + return bm1; +} + +/* invert the given bitmap. */ +static inline void bm_invert(potrace_bitmap_t * bm) +{ + int i; + for (i = 0; i < bm->dy * bm->h; i++) { + bm->map[i] ^= BM_ALLBITS; + } +} + +#endif /* BITMAP_H */ Index: trunk/src_plugins/export_gcode/curve.c =================================================================== --- trunk/src_plugins/export_gcode/curve.c (nonexistent) +++ trunk/src_plugins/export_gcode/curve.c (revision 1269) @@ -0,0 +1,114 @@ +/* Copyright (C) 2001-2007 Peter Selinger. + This file is part of Potrace. It is free software and it is covered + by the GNU General Public License. See the file COPYING for details. */ + +/* $Id: curve.c 147 2007-04-09 00:44:09Z selinger $ */ +/* private part of the path and curve data structures */ + +#include +#include +#include + +#include "potracelib.h" +#include "lists.h" +#include "curve.h" + +#define SAFE_MALLOC(var, n, typ) \ + if ((var = (typ *)malloc((n)*sizeof(typ))) == NULL) goto malloc_error + +/* ---------------------------------------------------------------------- */ +/* allocate and free path objects */ + +path_t *path_new(void) +{ + path_t *p = NULL; + privpath_t *priv = NULL; + + SAFE_MALLOC(p, 1, path_t); + memset(p, 0, sizeof(path_t)); + SAFE_MALLOC(priv, 1, privpath_t); + memset(priv, 0, sizeof(privpath_t)); + p->priv = priv; + return p; + +malloc_error: + free(p); + free(priv); + return NULL; +} + +/* free the members of the given curve structure. Leave errno unchanged. */ +static void privcurve_free_members(privcurve_t * curve) +{ + free(curve->tag); + free(curve->c); + free(curve->vertex); + free(curve->alpha); + free(curve->alpha0); + free(curve->beta); +} + +/* free a path. Leave errno untouched. */ +void path_free(path_t * p) +{ + if (p) { + if (p->priv) { + free(p->priv->pt); + free(p->priv->lon); + free(p->priv->sums); + free(p->priv->po); + privcurve_free_members(&p->priv->curve); + privcurve_free_members(&p->priv->ocurve); + } + free(p->priv); + /* do not free p->fcurve ! */ + } + free(p); +} + +/* free a pathlist, leaving errno untouched. */ +void pathlist_free(path_t * plist) +{ + path_t *p; + + list_forall_unlink(p, plist) { + path_free(p); + } +} + +/* ---------------------------------------------------------------------- */ +/* initialize and finalize curve structures */ + +typedef dpoint_t dpoint3_t[3]; + +/* initialize the members of the given curve structure to size m. + Return 0 on success, 1 on error with errno set. */ +int privcurve_init(privcurve_t * curve, int n) +{ + memset(curve, 0, sizeof(privcurve_t)); + curve->n = n; + SAFE_MALLOC(curve->tag, n, int); + SAFE_MALLOC(curve->c, n, dpoint3_t); + SAFE_MALLOC(curve->vertex, n, dpoint_t); + SAFE_MALLOC(curve->alpha, n, double); + SAFE_MALLOC(curve->alpha0, n, double); + SAFE_MALLOC(curve->beta, n, double); + return 0; + +malloc_error: + free(curve->tag); + free(curve->c); + free(curve->vertex); + free(curve->alpha); + free(curve->alpha0); + free(curve->beta); + return 1; +} + +/* copy private to public curve structure */ +void privcurve_to_curve(privcurve_t * pc, potrace_curve_t * c) +{ + c->n = pc->n; + c->tag = pc->tag; + c->c = pc->c; +} Index: trunk/src_plugins/export_gcode/curve.h =================================================================== --- trunk/src_plugins/export_gcode/curve.h (nonexistent) +++ trunk/src_plugins/export_gcode/curve.h (revision 1269) @@ -0,0 +1,76 @@ +/* Copyright (C) 2001-2007 Peter Selinger. + This file is part of Potrace. It is free software and it is covered + by the GNU General Public License. See the file COPYING for details. */ + +#ifndef CURVE_H +#define CURVE_H + +#include "auxiliary.h" + +/* vertex is c[1] for tag=POTRACE_CORNER, and the intersection of + .c[-1][2]..c[0] and c[1]..c[2] for tag=POTRACE_CURVETO. alpha is only + defined for tag=POTRACE_CURVETO and is the alpha parameter of the curve: + .c[-1][2]..c[0] = alpha*(.c[-1][2]..vertex), and + c[2]..c[1] = alpha*(c[2]..vertex). + Beta is so that (.beta[i])[.vertex[i],.vertex[i+1]] = .c[i][2]. +*/ + +struct privcurve_s { + int n; /* number of segments */ + int *tag; /* tag[n]: POTRACE_CORNER or POTRACE_CURVETO */ + dpoint_t(*c)[3]; /* c[n][i]: control points. + c[n][0] is unused for tag[n]=POTRACE_CORNER */ + /* the remainder of this structure is special to privcurve, and is + used in EPS debug output and special EPS "short coding". These + fields are valid only if "alphacurve" is set. */ + int alphacurve; /* have the following fields been initialized? */ + dpoint_t *vertex; /* for POTRACE_CORNER, this equals c[1] */ + double *alpha; /* only for POTRACE_CURVETO */ + double *alpha0; /* "uncropped" alpha parameter - for debug output only */ + double *beta; +}; +typedef struct privcurve_s privcurve_t; + +struct sums_s { + double x; + double y; + double x2; + double xy; + double y2; +}; +typedef struct sums_s sums_t; + +/* the path structure is filled in with information about a given path + as it is accumulated and passed through the different stages of the + Potrace algorithm. Backends only need to read the fcurve and fm + fields of this data structure, but debugging backends may read + other fields. */ +struct potrace_privpath_s { + int len; + point_t *pt; /* pt[len]: path as extracted from bitmap */ + int *lon; /* lon[len]: (i,lon[i]) = longest straight line from i */ + + int x0, y0; /* origin for sums */ + sums_t *sums; /* sums[len+1]: cache for fast summing */ + + int m; /* length of optimal polygon */ + int *po; /* po[m]: optimal polygon */ + + privcurve_t curve; /* curve[m]: array of curve elements */ + privcurve_t ocurve; /* ocurve[om]: array of curve elements */ + privcurve_t *fcurve; /* final curve: this points to either curve or + ocurve. Do not free this separately. */ +}; +typedef struct potrace_privpath_s potrace_privpath_t; + +/* shorter names */ +typedef potrace_privpath_t privpath_t; +typedef potrace_path_t path_t; + +path_t *path_new(void); +void path_free(path_t * p); +void pathlist_free(path_t * plist); +int privcurve_init(privcurve_t * curve, int n); +void privcurve_to_curve(privcurve_t * pc, potrace_curve_t * c); + +#endif /* CURVE_H */ Index: trunk/src_plugins/export_gcode/decompose.c =================================================================== --- trunk/src_plugins/export_gcode/decompose.c (nonexistent) +++ trunk/src_plugins/export_gcode/decompose.c (revision 1269) @@ -0,0 +1,525 @@ +/* Copyright (C) 2001-2007 Peter Selinger. + This file is part of Potrace. It is free software and it is covered + by the GNU General Public License. See the file COPYING for details. */ + +/* $Id: decompose.c 146 2007-04-09 00:43:46Z selinger $ */ + +#include +#include +#include +#include + +#include "potracelib.h" +#include "curve.h" +#include "lists.h" +#include "auxiliary.h" +#include "bitmap.h" +#include "decompose.h" +/*#include "progress.h"*/ + +/* ---------------------------------------------------------------------- */ +/* auxiliary bitmap manipulations */ + +/* set the excess padding to 0 */ +static void bm_clearexcess(potrace_bitmap_t * bm) +{ + potrace_word mask; + int y; + + if (bm->w % BM_WORDBITS != 0) { + mask = BM_ALLBITS << (BM_WORDBITS - (bm->w % BM_WORDBITS)); + for (y = 0; y < bm->h; y++) { + *bm_index(bm, bm->w, y) &= mask; + } + } +} + +struct bbox_s { + int x0, x1, y0, y1; /* bounding box */ +}; +typedef struct bbox_s bbox_t; + +/* clear the bm, assuming the bounding box is set correctly (faster + than clearing the whole bitmap) */ +static void clear_bm_with_bbox(potrace_bitmap_t * bm, bbox_t * bbox) +{ + int imin = (bbox->x0 / BM_WORDBITS); + int imax = ((bbox->x1 + BM_WORDBITS - 1) / BM_WORDBITS); + int i, y; + + for (y = bbox->y0; y < bbox->y1; y++) { + for (i = imin; i < imax; i++) { + bm_scanline(bm, y)[i] = 0; + } + } +} + +/* ---------------------------------------------------------------------- */ +/* auxiliary functions */ + +/* deterministically and efficiently hash (x,y) into a pseudo-random bit */ +static inline int detrand(int x, int y) +{ + unsigned int z; + static const unsigned char t[256] = { + /* non-linear sequence: constant term of inverse in GF(8), + mod x^8+x^4+x^3+x+1 */ + 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, + 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, + 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, + 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, + 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, + 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, + 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, + }; + + /* 0x04b3e375 and 0x05a8ef93 are chosen to contain every possible + 5-bit sequence */ + z = ((0x04b3e375 * x) ^ y) * 0x05a8ef93; + z = t[z & 0xff] ^ t[(z >> 8) & 0xff] ^ t[(z >> 16) & 0xff] ^ t[(z >> 24) & 0xff]; + return z & 1; +} + +/* return the "majority" value of bitmap bm at intersection (x,y). We + assume that the bitmap is balanced at "radius" 1. */ +static int majority(potrace_bitmap_t * bm, int x, int y) +{ + int i, a, ct; + + for (i = 2; i < 5; i++) { /* check at "radius" i */ + ct = 0; + for (a = -i + 1; a <= i - 1; a++) { + ct += BM_GET(bm, x + a, y + i - 1) ? 1 : -1; + ct += BM_GET(bm, x + i - 1, y + a - 1) ? 1 : -1; + ct += BM_GET(bm, x + a - 1, y - i) ? 1 : -1; + ct += BM_GET(bm, x - i, y + a) ? 1 : -1; + } + if (ct > 0) { + return 1; + } + else if (ct < 0) { + return 0; + } + } + return 0; +} + +/* ---------------------------------------------------------------------- */ +/* decompose image into paths */ + +/* efficiently invert bits [x,infty) and [xa,infty) in line y. Here xa + must be a multiple of BM_WORDBITS. */ +static void xor_to_ref(potrace_bitmap_t * bm, int x, int y, int xa) +{ + int xhi = x & -BM_WORDBITS; + int xlo = x & (BM_WORDBITS - 1); /* = x % BM_WORDBITS */ + int i; + + if (xhi < xa) { + for (i = xhi; i < xa; i += BM_WORDBITS) { + *bm_index(bm, i, y) ^= BM_ALLBITS; + } + } + else { + for (i = xa; i < xhi; i += BM_WORDBITS) { + *bm_index(bm, i, y) ^= BM_ALLBITS; + } + } + /* note: the following "if" is needed because x86 treats a<priv->len <= 0) { /* a path of length 0 is silly, but legal */ + return; + } + + y1 = p->priv->pt[p->priv->len - 1].y; + + xa = p->priv->pt[0].x & -BM_WORDBITS; + for (k = 0; k < p->priv->len; k++) { + x = p->priv->pt[k].x; + y = p->priv->pt[k].y; + + if (y != y1) { + /* efficiently invert the rectangle [x,xa] x [y,y1] */ + xor_to_ref(bm, x, min(y, y1), xa); + y1 = y; + } + } +} + +/* Find the bounding box of a given path. Path is assumed to be of + non-zero length. */ +static void setbbox_path(bbox_t * bbox, path_t * p) +{ + int x, y; + int k; + + bbox->y0 = INT_MAX; + bbox->y1 = 0; + bbox->x0 = INT_MAX; + bbox->x1 = 0; + + for (k = 0; k < p->priv->len; k++) { + x = p->priv->pt[k].x; + y = p->priv->pt[k].y; + + if (x < bbox->x0) { + bbox->x0 = x; + } + if (x > bbox->x1) { + bbox->x1 = x; + } + if (y < bbox->y0) { + bbox->y0 = y; + } + if (y > bbox->y1) { + bbox->y1 = y; + } + } +} + +/* compute a path in the given pixmap, separating black from white. + Start path at the point (x0,x1), which must be an upper left corner + of the path. Also compute the area enclosed by the path. Return a + new path_t object, or NULL on error (note that a legitimate path + cannot have length 0). Sign is required for correct interpretation + of turnpolicies. */ +static path_t *findpath(potrace_bitmap_t * bm, int x0, int y0, int sign, int turnpolicy) +{ + int x, y, dirx, diry, len, size, area; + int c, d, tmp; + point_t *pt, *pt1; + path_t *p = NULL; + + x = x0; + y = y0; + dirx = 0; + diry = -1; + + len = size = 0; + pt = NULL; + area = 0; + + while (1) { + /* add point to path */ + if (len >= size) { + size += 100; + size = (int) (1.3 * size); + pt1 = (point_t *) realloc(pt, size * sizeof(point_t)); + if (!pt1) { + goto error; + } + pt = pt1; + } + pt[len].x = x; + pt[len].y = y; + len++; + + /* move to next point */ + x += dirx; + y += diry; + area += x * diry; + + /* path complete? */ + if (x == x0 && y == y0) { + break; + } + + /* determine next direction */ + c = BM_GET(bm, x + (dirx + diry - 1) / 2, y + (diry - dirx - 1) / 2); + d = BM_GET(bm, x + (dirx - diry - 1) / 2, y + (diry + dirx - 1) / 2); + + if (c && !d) { /* ambiguous turn */ + if (turnpolicy == POTRACE_TURNPOLICY_RIGHT || (turnpolicy == POTRACE_TURNPOLICY_BLACK && sign == '+') + || (turnpolicy == POTRACE_TURNPOLICY_WHITE && sign == '-') + || (turnpolicy == POTRACE_TURNPOLICY_RANDOM && detrand(x, y)) + || (turnpolicy == POTRACE_TURNPOLICY_MAJORITY && majority(bm, x, y)) + || (turnpolicy == POTRACE_TURNPOLICY_MINORITY && !majority(bm, x, y))) { + tmp = dirx; /* right turn */ + dirx = diry; + diry = -tmp; + } + else { + tmp = dirx; /* left turn */ + dirx = -diry; + diry = tmp; + } + } + else if (c) { /* right turn */ + tmp = dirx; + dirx = diry; + diry = -tmp; + } + else if (!d) { /* left turn */ + tmp = dirx; + dirx = -diry; + diry = tmp; + } + } /* while this path */ + + /* allocate new path object */ + p = path_new(); + if (!p) { + goto error; + } + + p->priv->pt = pt; + p->priv->len = len; + p->area = area; + p->sign = sign; + + return p; + +error: + free(pt); + return NULL; +} + +/* Give a tree structure to the given path list, based on "insideness" + testing. I.e., path A is considered "below" path B if it is inside + path B. The input pathlist is assumed to be ordered so that "outer" + paths occur before "inner" paths. The tree structure is stored in + the "childlist" and "sibling" components of the path_t + structure. The linked list structure is also changed so that + negative path components are listed immediately after their + positive parent. Note: some backends may ignore the tree + structure, others may use it e.g. to group path components. We + assume that in the input, point 0 of each path is an "upper left" + corner of the path, as returned by bm_to_pathlist. This makes it + easy to find an "interior" point. The bm argument should be a + bitmap of the correct size (large enough to hold all the paths), + and will be used as scratch space. Return 0 on success or -1 on + error with errno set. */ + +static void pathlist_to_tree(path_t * plist, potrace_bitmap_t * bm) +{ + path_t *p, *p1; + path_t *heap, *heap1; + path_t *cur; + path_t *head; + path_t **hook, **hook_in, **hook_out; /* for fast appending to linked list */ + bbox_t bbox; + + bm_clear(bm, 0); + + /* save original "next" pointers */ + list_forall(p, plist) { + p->sibling = p->next; + p->childlist = NULL; + } + + heap = plist; + + /* the heap holds a list of lists of paths. Use "childlist" field + for outer list, "next" field for inner list. Each of the sublists + is to be turned into a tree. This code is messy, but it is + actually fast. Each path is rendered exactly once. We use the + heap to get a tail recursive algorithm: the heap holds a list of + pathlists which still need to be transformed. */ + + while (heap) { + /* unlink first sublist */ + cur = heap; + heap = heap->childlist; + cur->childlist = NULL; + + /* unlink first path */ + head = cur; + cur = cur->next; + head->next = NULL; + + /* render path */ + xor_path(bm, head); + setbbox_path(&bbox, head); + + /* now do insideness test for each element of cur; append it to + head->childlist if it's inside head, else append it to + head->next. */ + hook_in = &head->childlist; + hook_out = &head->next; + list_forall_unlink(p, cur) { + if (p->priv->pt[0].y <= bbox.y0) { + list_insert_beforehook(p, hook_out); + /* append the remainder of the list to hook_out */ + *hook_out = cur; + break; + } + if (BM_GET(bm, p->priv->pt[0].x, p->priv->pt[0].y - 1)) { + list_insert_beforehook(p, hook_in); + } + else { + list_insert_beforehook(p, hook_out); + } + } + + /* clear bm */ + clear_bm_with_bbox(bm, &bbox); + + /* now schedule head->childlist and head->next for further + processing */ + if (head->next) { + head->next->childlist = heap; + heap = head->next; + } + if (head->childlist) { + head->childlist->childlist = heap; + heap = head->childlist; + } + } + + /* copy sibling structure from "next" to "sibling" component */ + p = plist; + while (p) { + p1 = p->sibling; + p->sibling = p->next; + p = p1; + } + + /* reconstruct a new linked list ("next") structure from tree + ("childlist", "sibling") structure. This code is slightly messy, + because we use a heap to make it tail recursive: the heap + contains a list of childlists which still need to be + processed. */ + heap = plist; + if (heap) { + heap->next = NULL; /* heap is a linked list of childlists */ + } + plist = NULL; + hook = &plist; + while (heap) { + heap1 = heap->next; + for (p = heap; p; p = p->sibling) { + /* p is a positive path */ + /* append to linked list */ + list_insert_beforehook(p, hook); + + /* go through its children */ + for (p1 = p->childlist; p1; p1 = p1->sibling) { + /* append to linked list */ + list_insert_beforehook(p1, hook); + /* append its childlist to heap, if non-empty */ + if (p1->childlist) { + list_append(path_t, heap1, p1->childlist); + } + } + } + heap = heap1; + } + + return; +} + +/* find the next set pixel in a row <= y. Pixels are searched first + left-to-right, then top-down. In other words, (x,y)<(x',y') if y>y' + or y=y' and x= 0; y--) { + for (x = 0; x < bm->w; x += BM_WORDBITS) { + if (*bm_index(bm, x, y)) { + while (!BM_GET(bm, x, y)) { + x++; + } + /* found */ + *xp = x; + *yp = y; + return 0; + } + } + } + /* not found */ + return 1; +} + +/* Decompose the given bitmap into paths. Returns a linked list of + path_t objects with the fields len, pt, area, sign filled + in. Returns 0 on success with plistp set, or -1 on error with errno + set. */ + +int bm_to_pathlist(const potrace_bitmap_t * bm, path_t ** plistp, const potrace_param_t * param) +{ + int x; + int y; + path_t *p; + path_t *plist = NULL; /* linked list of path objects */ + path_t **hook = &plist; /* used to speed up appending to linked list */ + potrace_bitmap_t *bm1 = NULL; + int sign; + + bm1 = bm_dup(bm); + if (!bm1) { + goto error; + } + + /* be sure the byte padding on the right is set to 0, as the fast + pixel search below relies on it */ + bm_clearexcess(bm1); + + /* iterate through components */ + y = bm1->h - 1; + while (findnext(bm1, &x, &y) == 0) { + /* calculate the sign by looking at the original */ + sign = BM_GET(bm, x, y) ? '+' : '-'; + + /* calculate the path */ + p = findpath(bm1, x, y + 1, sign, param->turnpolicy); + if (p == NULL) { + goto error; + } + + /* update buffered image */ + xor_path(bm1, p); + + /* if it's a turd, eliminate it, else append it to the list */ + if (p->area <= param->turdsize) { + path_free(p); + } + else { + list_insert_beforehook(p, hook); + } + + if (bm1->h > 0) { /* to be sure */ + /*progress_update(1-y/(double)bm1->h, progress); */ + } + } + + pathlist_to_tree(plist, bm1); + bm_free(bm1); + *plistp = plist; + +/* progress_update(1.0, progress);*/ + + return 0; + +error: + bm_free(bm1); + list_forall_unlink(p, plist) { + path_free(p); + } + return -1; +} Index: trunk/src_plugins/export_gcode/decompose.h =================================================================== --- trunk/src_plugins/export_gcode/decompose.h (nonexistent) +++ trunk/src_plugins/export_gcode/decompose.h (revision 1269) @@ -0,0 +1,15 @@ +/* Copyright (C) 2001-2007 Peter Selinger. + This file is part of Potrace. It is free software and it is covered + by the GNU General Public License. See the file COPYING for details. */ + +/* $Id: decompose.h 147 2007-04-09 00:44:09Z selinger $ */ + +#ifndef DECOMPOSE_H +#define DECOMPOSE_H + +#include "potracelib.h" +/*#include "progress.h"*/ + +int bm_to_pathlist(const potrace_bitmap_t * bm, path_t ** plistp, const potrace_param_t * param); + +#endif /* DECOMPOSE_H */ Index: trunk/src_plugins/export_gcode/gcode.c =================================================================== --- trunk/src_plugins/export_gcode/gcode.c (nonexistent) +++ trunk/src_plugins/export_gcode/gcode.c (revision 1269) @@ -0,0 +1,901 @@ +/* + * COPYRIGHT + * + * PCB, interactive printed circuit board design + * + * GCODE export HID + * Copyright (C) 2010 Alberto Maccioni + * this code is based on the NELMA export HID, the PNG export HID, + * and potrace, a tracing program by Peter Selinger + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* + * This HID exports a PCB layout into: + * one layer mask file (PNG format) per copper layer, + * one G-CODE CNC drill file. + * one G-CODE CNC file per copper layer. + * The latter is used by a CNC milling machine to mill the pcb. + */ + +#include "config.h" +#include "plugins.h" + +#include +#include +#include +#include +#include + +#include + +#include "global.h" +#include "error.h" /* Message() */ +#include "data.h" +#include "misc.h" +#include "rats.h" + +#include "hid.h" +#include "../hidint.h" +#include +#include "hid/common/hidnogui.h" +#include "hid/common/draw_helpers.h" +#include "gcode.h" +#include "bitmap.h" +#include "curve.h" +#include "potracelib.h" +#include "trace.h" +#include "decompose.h" +#include "pcb-printf.h" + +#include "hid/common/hidinit.h" + +const char *gcode_cookie = "gcode HID"; + +#define CRASH fprintf(stderr, "HID error: pcb called unimplemented GCODE function %s.\n", __FUNCTION__); abort() +struct color_struct { + /* the descriptor used by the gd library */ + int c; + + /* so I can figure out what rgb value c refers to */ + unsigned int r, g, b; +}; + +struct hid_gc_struct { + HID *me_pointer; + EndCapStyle cap; + int width; + unsigned char r, g, b; + int erase; + int faded; + struct color_struct *color; + gdImagePtr brush; +}; + +static struct color_struct *black = NULL, *white = NULL; +static int linewidth = -1; +static gdImagePtr lastbrush = (gdImagePtr) ((void *) -1); +static int lastcolor = -1; + +/* gd image and file for PNG export */ +static gdImagePtr gcode_im = NULL; +static FILE *gcode_f = NULL, *gcode_f2 = NULL; + +static int is_mask; +static int is_drill; +static int is_solder; + +/* + * Which groups of layers to export into PNG layer masks. 1 means export, 0 + * means do not export. + */ +static int gcode_export_group[MAX_LAYER]; + +/* Group that is currently exported. */ +static int gcode_cur_group; + +/* Filename prefix that will be used when saving files. */ +static const char *gcode_basename = NULL; + +/* Horizontal DPI (grid points per inch) */ +static int gcode_dpi = -1; + +static double gcode_cutdepth = 0; /* milling depth (inch) */ +static double gcode_drilldepth = 0; /* drilling depth (inch) */ +static double gcode_safeZ = 100; /* safe Z (inch) */ +static double gcode_toolradius = 0; /* tool radius(inch) */ +static int save_drill = 0; +static int n_drill = 0; +static int nmax_drill = 0; +struct drill_struct { + double x; + double y; +}; + +static struct drill_struct *drill = 0; + +static const char *units[] = { + "mm", + "mil", + "um", + "inch", + NULL +}; + +HID_Attribute gcode_attribute_list[] = { + /* other HIDs expect this to be first. */ + {"basename", "File name prefix", + HID_String, 0, 0, {0, 0, 0}, 0, 0}, +#define HA_basename 0 + + {"dpi", "Resolution of intermediate image (pixels/inch)", + HID_Integer, 0, 2000, {600, 0, 0}, 0, 0}, +#define HA_dpi 1 + + {"mill-depth", "Milling depth", + HID_Real, -1000, 1000, {0, 0, -0.05}, 0, 0}, +#define HA_cutdepth 2 + + {"safe-Z", "Safe Z for traverse move", + HID_Real, -1000, 10000, {0, 0, 2}, 0, 0}, +#define HA_safeZ 3 + + {"tool-radius", "Milling tool radius compensation", + HID_Real, 0, 10000, {0, 0, 0.1}, 0, 0}, +#define HA_toolradius 4 + + {"drill-depth", "Drilling depth", + HID_Real, -10000, 10000, {0, 0, -2}, 0, 0}, +#define HA_drilldepth 5 + + {"measurement-unit", "Measurement unit", + HID_Unit, 0, 0, {-1, 0, 0}, units, 0}, +#define HA_unit 6 + +}; + +#define NUM_OPTIONS (sizeof(gcode_attribute_list)/sizeof(gcode_attribute_list[0])) + +REGISTER_ATTRIBUTES(gcode_attribute_list, gcode_cookie) + static HID_Attr_Val gcode_values[NUM_OPTIONS]; + +/* *** Utility funcions **************************************************** */ + +/* convert from default PCB units to gcode units */ + static int pcb_to_gcode(int pcb) +{ + return round(COORD_TO_INCH(pcb) * gcode_dpi); +} + +static char *gcode_get_png_name(const char *basename, const char *suffix) +{ + return pcb_strdup_printf("%s.%s.png", basename, suffix); +} + +/* Sorts drills in order of distance from the origin */ +struct drill_struct *sort_drill(struct drill_struct *drill, int n_drill) +{ + int i, j, imin; + double dmin, d; + struct drill_struct p = { 0, 0 }; + struct drill_struct *temp = (struct drill_struct *) malloc(n_drill * sizeof(struct drill_struct)); + for (j = 0; j < n_drill; j++) { + dmin = 1e20; + imin = 0; + for (i = 0; i < n_drill - j; i++) { + d = (drill[i].x - p.x) * (drill[i].x - p.x) + (drill[i].y - p.y) * (drill[i].y - p.y); + if (d < dmin) { + imin = i; + dmin = d; + } + } + /* printf("j=%d imin=%d dmin=%f p=(%f,%f)\n",j,imin,dmin,p.x,p.y); */ + temp[j] = drill[imin]; + drill[imin] = drill[n_drill - j - 1]; + p = temp[j]; + } + free(drill); + return temp; +} + +/* *** Main export callback ************************************************ */ + +static void gcode_parse_arguments(int *argc, char ***argv) +{ + hid_register_attributes(gcode_attribute_list, sizeof(gcode_attribute_list) / sizeof(gcode_attribute_list[0]), gcode_cookie); + hid_parse_command_line(argc, argv); +} + +static HID_Attribute *gcode_get_export_options(int *n) +{ + static char *last_made_filename = 0; + static int last_unit_value = -1; + + if (gcode_attribute_list[HA_unit].default_val.int_value == last_unit_value) { + if (Settings.grid_unit) + gcode_attribute_list[HA_unit].default_val.int_value = Settings.grid_unit->index; + else + gcode_attribute_list[HA_unit].default_val.int_value = get_unit_struct("mil")->index; + last_unit_value = gcode_attribute_list[HA_unit].default_val.int_value; + } + + if (PCB) { + derive_default_filename(PCB->Filename, &gcode_attribute_list[HA_basename], ".gcode", &last_made_filename); + } + if (n) { + *n = NUM_OPTIONS; + } + return gcode_attribute_list; +} + +/* Populates gcode_export_group array */ +void gcode_choose_groups() +{ + int n, m; + LayerType *layer; + + /* Set entire array to 0 (don't export any layer groups by default */ + memset(gcode_export_group, 0, sizeof(gcode_export_group)); + + for (n = 0; n < max_copper_layer; n++) { + layer = &PCB->Data->Layer[n]; + + if (!LAYER_IS_EMPTY(layer)) { + /* layer isn't empty */ + + /* + * is this check necessary? It seems that special + * layers have negative indexes? + */ + + if (SL_TYPE(n) == 0) { + /* layer is a copper layer */ + m = GetLayerGroupNumberByNumber(n); + + /* the export layer */ + gcode_export_group[m] = 1; + } + } + } +} + +static void gcode_alloc_colors() +{ + /* + * Allocate white and black -- the first color allocated becomes the + * background color + */ + + white = (struct color_struct *) malloc(sizeof(*white)); + white->r = white->g = white->b = 255; + white->c = gdImageColorAllocate(gcode_im, white->r, white->g, white->b); + + black = (struct color_struct *) malloc(sizeof(*black)); + black->r = black->g = black->b = 0; + black->c = gdImageColorAllocate(gcode_im, black->r, black->g, black->b); +} + +static void gcode_start_png(const char *basename, const char *suffix) +{ + int h, w; + char *buf; + + buf = gcode_get_png_name(basename, suffix); + + h = pcb_to_gcode(PCB->MaxHeight); + w = pcb_to_gcode(PCB->MaxWidth); + + /* Nelma only works with true color images */ + gcode_im = gdImageCreate(w, h); + gcode_f = fopen(buf, "wb"); + + gcode_alloc_colors(); + + free(buf); +} + +static void gcode_finish_png() +{ +#ifdef HAVE_GDIMAGEPNG + gdImagePng(gcode_im, gcode_f); +#else + Message("GCODE: PNG not supported by gd. Can't write layer mask.\n"); +#endif + gdImageDestroy(gcode_im); + fclose(gcode_f); + + free(white); + free(black); + + gcode_im = NULL; + gcode_f = NULL; +} + +void gcode_start_png_export() +{ + BoxType region; + + region.X1 = 0; + region.Y1 = 0; + region.X2 = PCB->MaxWidth; + region.Y2 = PCB->MaxHeight; + + linewidth = -1; + lastbrush = (gdImagePtr) ((void *) -1); + lastcolor = -1; + + hid_expose_callback(&gcode_hid, ®ion, 0); +} + +static void gcode_do_export(HID_Attr_Val * options) +{ + int save_ons[MAX_LAYER + 2]; + int i, idx; + time_t t; + const Unit *unit; + double scale = 0, d = 0; + int r, c, v, p, metric; + char *filename; + path_t *plist = NULL; + potrace_bitmap_t *bm = NULL; + potrace_param_t param_default = { + 2, /* turnsize */ + POTRACE_TURNPOLICY_MINORITY, /* turnpolicy */ + 1.0, /* alphamax */ + 1, /* opticurve */ + 0.2, /* opttolerance */ + { + NULL, /* callback function */ + NULL, /* callback data */ + 0.0, 1.0, /* progress range */ + 0.0, /* granularity */ + }, + }; + + if (!options) { + gcode_get_export_options(0); + for (i = 0; i < NUM_OPTIONS; i++) { + gcode_values[i] = gcode_attribute_list[i].default_val; + } + options = gcode_values; + } + gcode_basename = options[HA_basename].str_value; + if (!gcode_basename) { + gcode_basename = "pcb-out"; + } + gcode_dpi = options[HA_dpi].int_value; + if (gcode_dpi < 0) { + fprintf(stderr, "ERROR: dpi may not be < 0\n"); + return; + } + unit = &(get_unit_list()[options[HA_unit].int_value]); + metric = (unit->family == METRIC); + scale = metric ? 1.0 / coord_to_unit(unit, MM_TO_COORD(1.0)) + : 1.0 / coord_to_unit(unit, INCH_TO_COORD(1.0)); + + gcode_cutdepth = options[HA_cutdepth].real_value * scale; + gcode_drilldepth = options[HA_drilldepth].real_value * scale; + gcode_safeZ = options[HA_safeZ].real_value * scale; + gcode_toolradius = metric ? MM_TO_COORD(options[HA_toolradius].real_value * scale) + : INCH_TO_COORD(options[HA_toolradius].real_value * scale); + gcode_choose_groups(); + + for (i = 0; i < MAX_LAYER; i++) { + if (gcode_export_group[i]) { + + gcode_cur_group = i; + + /* magic */ + idx = (i >= 0 && i < max_group) ? PCB->LayerGroups.Entries[i][0] : i; + printf("idx=%d %s\n", idx, layer_type_to_file_name(idx, FNS_fixed)); + is_solder = (GetLayerGroupNumberByNumber(idx) == GetLayerGroupNumberByNumber(solder_silk_layer)) ? 1 : 0; + save_drill = is_solder; /* save drills for one layer only */ + gcode_start_png(gcode_basename, layer_type_to_file_name(idx, FNS_fixed)); + hid_save_and_show_layer_ons(save_ons); + gcode_start_png_export(); + hid_restore_layer_ons(save_ons); + +/* ***************** gcode conversion *************************** */ +/* potrace uses a different kind of bitmap; for simplicity gcode_im is copied to this format */ + bm = bm_new(gdImageSX(gcode_im), gdImageSY(gcode_im)); + filename = (char *) malloc(MAXPATHLEN); + plist = NULL; + if (is_solder) { /* only for back layer */ + gdImagePtr temp_im = gdImageCreate(gdImageSX(gcode_im), gdImageSY(gcode_im)); + gdImageCopy(temp_im, gcode_im, 0, 0, 0, 0, gdImageSX(gcode_im), gdImageSY(gcode_im)); + for (r = 0; r < gdImageSX(gcode_im); r++) { + for (c = 0; c < gdImageSY(gcode_im); c++) { + gdImageSetPixel(gcode_im, r, c, gdImageGetPixel(temp_im, gdImageSX(gcode_im) - 1 - r, c)); + } + } + gdImageDestroy(temp_im); + } + sprintf(filename, "%s.%s.cnc", gcode_basename, layer_type_to_file_name(idx, FNS_fixed)); + for (r = 0; r < gdImageSX(gcode_im); r++) { + for (c = 0; c < gdImageSY(gcode_im); c++) { + v = gdImageGetPixel(gcode_im, r, gdImageSY(gcode_im) - 1 - c); + p = (gcode_im->red[v] || gcode_im->green[v] + || gcode_im->blue[v]) ? 0 : 0xFFFFFF; + BM_PUT(bm, r, c, p); + } + } + gcode_f2 = fopen(filename, "wb"); + if (!gcode_f2) { + perror(filename); + return; + } + fprintf(gcode_f2, "(Created by G-code exporter)\n"); + t = time(NULL); + sprintf(filename, "%s", ctime(&t)); + filename[strlen(filename) - 1] = 0; + fprintf(gcode_f2, "( %s )\n", filename); + fprintf(gcode_f2, "(%d dpi)\n", gcode_dpi); + fprintf(gcode_f2, "(Unit: %s)\n", metric ? "mm" : "inch"); + if (metric) + pcb_fprintf(gcode_f2, "(Board size: %.2mmx%.2mm mm)", PCB->MaxWidth, PCB->MaxHeight); + else + pcb_fprintf(gcode_f2, "(Board size: %.2mix%.2mi inches)", PCB->MaxWidth, PCB->MaxHeight); + fprintf(gcode_f2, "#100=%f (safe Z)\n", gcode_safeZ); + fprintf(gcode_f2, "#101=%f (cutting depth)\n", gcode_cutdepth); + fprintf(gcode_f2, "(---------------------------------)\n"); + fprintf(gcode_f2, "G17 G%d G90 G64 P0.003 M3 S3000 M7 F%d\n", metric ? 21 : 20, metric ? 25 : 1); + fprintf(gcode_f2, "G0 Z#100\n"); + /* extract contour points from image */ + r = bm_to_pathlist(bm, &plist, ¶m_default); + if (r) { + fprintf(stderr, "ERROR: pathlist function failed\n"); + return; + } + /* generate best polygon and write vertices in g-code format */ + d = process_path(plist, ¶m_default, bm, gcode_f2, metric ? 25.4 / gcode_dpi : 1.0 / gcode_dpi); + if (d < 0) { + fprintf(stderr, "ERROR: path process function failed\n"); + return; + } + if (metric) + fprintf(gcode_f2, "(end, total distance %.2fmm = %.2fin)\n", d, d * 1 / 25.4); + else + fprintf(gcode_f2, "(end, total distance %.2fmm = %.2fin)\n", 25.4 * d, d); + fprintf(gcode_f2, "M5 M9 M2\n"); + pathlist_free(plist); + bm_free(bm); + fclose(gcode_f2); + if (save_drill) { + d = 0; + drill = sort_drill(drill, n_drill); + sprintf(filename, "%s.drill.cnc", gcode_basename); + gcode_f2 = fopen(filename, "wb"); + if (!gcode_f2) { + perror(filename); + return; + } + fprintf(gcode_f2, "(Created by G-code exporter)\n"); + fprintf(gcode_f2, "(drill file: %d drills)\n", n_drill); + sprintf(filename, "%s", ctime(&t)); + filename[strlen(filename) - 1] = 0; + fprintf(gcode_f2, "( %s )\n", filename); + fprintf(gcode_f2, "(Unit: %s)\n", metric ? "mm" : "inch"); + if (metric) + pcb_fprintf(gcode_f2, "(Board size: %.2mmx%.2mm mm)", PCB->MaxWidth, PCB->MaxHeight); + else + pcb_fprintf(gcode_f2, "(Board size: %.2mix%.2mi inches)", PCB->MaxWidth, PCB->MaxHeight); + fprintf(gcode_f2, "#100=%f (safe Z)\n", gcode_safeZ); + fprintf(gcode_f2, "#101=%f (drill depth)\n", gcode_drilldepth); + fprintf(gcode_f2, "(---------------------------------)\n"); + fprintf(gcode_f2, "G17 G%d G90 G64 P0.003 M3 S3000 M7 F%d\n", metric ? 21 : 20, metric ? 25 : 1); +/* fprintf(gcode_f2,"G0 Z#100\n"); */ + for (r = 0; r < n_drill; r++) { +/* if(metric) fprintf(gcode_f2,"G0 X%f Y%f\n",drill[r].x*25.4,drill[r].y*25.4); */ +/* else fprintf(gcode_f2,"G0 X%f Y%f\n",drill[r].x,drill[r].y); */ + if (metric) + fprintf(gcode_f2, "G81 X%f Y%f Z#101 R#100\n", drill[r].x * 25.4, drill[r].y * 25.4); + else + fprintf(gcode_f2, "G81 X%f Y%f Z#101 R#100\n", drill[r].x, drill[r].y); +/* fprintf(gcode_f2,"G1 Z#101\n"); */ +/* fprintf(gcode_f2,"G0 Z#100\n"); */ + if (r > 0) + d += + sqrt((drill[r].x - drill[r - 1].x) * (drill[r].x - + drill[r - 1].x) + + (drill[r].y - drill[r - 1].y) * (drill[r].y - drill[r - 1].y)); + } + fprintf(gcode_f2, "M5 M9 M2\n"); + fprintf(gcode_f2, "(end, total distance %.2fmm = %.2fin)\n", 25.4 * d, d); + fclose(gcode_f2); + free(drill); + drill = NULL; + n_drill = nmax_drill = 0; + } + free(filename); + +/* ******************* end gcode conversion **************************** */ + gcode_finish_png(); + } + } +} + +/* *** PNG export (slightly modified code from PNG export HID) ************* */ + +static int gcode_set_layer(const char *name, int group, int empty) +{ + int idx = (group >= 0 && group < max_group) ? PCB->LayerGroups.Entries[group][0] : group; + + if (name == 0) { + name = PCB->Data->Layer[idx].Name; + } + if (strcmp(name, "invisible") == 0) { + return 0; + } + is_drill = (SL_TYPE(idx) == SL_PDRILL || SL_TYPE(idx) == SL_UDRILL); + is_mask = (SL_TYPE(idx) == SL_MASK); + + if (is_mask) { + /* Don't print masks */ + return 0; + } + if (is_drill) { + /* + * Print 'holes', so that we can fill gaps in the copper + * layer + */ + return 1; + } + if (group == gcode_cur_group) { + return 1; + } + return 0; +} + +static hidGC gcode_make_gc(void) +{ + hidGC rv = (hidGC) malloc(sizeof(struct hid_gc_struct)); + rv->me_pointer = &gcode_hid; + rv->cap = Trace_Cap; + rv->width = 1; + rv->color = (struct color_struct *) malloc(sizeof(*rv->color)); + rv->color->r = rv->color->g = rv->color->b = 0; + rv->color->c = 0; + return rv; +} + +static void gcode_destroy_gc(hidGC gc) +{ + free(gc); +} + +static void gcode_use_mask(int use_it) +{ + /* does nothing */ +} + +static void gcode_set_color(hidGC gc, const char *name) +{ + if (gcode_im == NULL) { + return; + } + if (name == NULL) { + name = "#ff0000"; + } + if (!strcmp(name, "drill")) { + gc->color = black; + gc->erase = 0; + return; + } + if (!strcmp(name, "erase")) { + /* FIXME -- should be background, not white */ + gc->color = white; + gc->erase = 1; + return; + } + gc->color = black; + gc->erase = 0; + return; +} + +static void gcode_set_line_cap(hidGC gc, EndCapStyle style) +{ + gc->cap = style; +} + +static void gcode_set_line_width(hidGC gc, Coord width) +{ + gc->width = width; +} + +static void gcode_set_draw_xor(hidGC gc, int xor_) +{ + ; +} + +static void gcode_set_draw_faded(hidGC gc, int faded) +{ + gc->faded = faded; +} + +static void use_gc(hidGC gc) +{ + int need_brush = 0; + + if (gc->me_pointer != &gcode_hid) { + fprintf(stderr, "Fatal: GC from another HID passed to gcode HID\n"); + abort(); + } + if (linewidth != gc->width) { + /* Make sure the scaling doesn't erase lines completely */ + /* + if (SCALE (gc->width) == 0 && gc->width > 0) + gdImageSetThickness (im, 1); + else + */ + gdImageSetThickness(gcode_im, pcb_to_gcode(gc->width + 2 * gcode_toolradius)); + linewidth = gc->width; + need_brush = 1; + } + if (lastbrush != gc->brush || need_brush) { + static void *bcache = 0; + hidval bval; + char name[256]; + char type; + int r; + + switch (gc->cap) { + case Round_Cap: + case Trace_Cap: + type = 'C'; + r = pcb_to_gcode(gc->width / 2 + gcode_toolradius); + break; + default: + case Square_Cap: + r = pcb_to_gcode(gc->width + gcode_toolradius * 2); + type = 'S'; + break; + } + sprintf(name, "#%.2x%.2x%.2x_%c_%d", gc->color->r, gc->color->g, gc->color->b, type, r); + + if (hid_cache_color(0, name, &bval, &bcache)) { + gc->brush = (gdImagePtr) bval.ptr; + } + else { + int bg, fg; + if (type == 'C') + gc->brush = gdImageCreate(2 * r + 1, 2 * r + 1); + else + gc->brush = gdImageCreate(r + 1, r + 1); + bg = gdImageColorAllocate(gc->brush, 255, 255, 255); + fg = gdImageColorAllocate(gc->brush, gc->color->r, gc->color->g, gc->color->b); + gdImageColorTransparent(gc->brush, bg); + + /* + * if we shrunk to a radius/box width of zero, then just use + * a single pixel to draw with. + */ + if (r == 0) + gdImageFilledRectangle(gc->brush, 0, 0, 0, 0, fg); + else { + if (type == 'C') + gdImageFilledEllipse(gc->brush, r, r, 2 * r, 2 * r, fg); + else + gdImageFilledRectangle(gc->brush, 0, 0, r, r, fg); + } + bval.ptr = gc->brush; + hid_cache_color(1, name, &bval, &bcache); + } + + gdImageSetBrush(gcode_im, gc->brush); + lastbrush = gc->brush; + + } +#define CBLEND(gc) (((gc->r)<<24)|((gc->g)<<16)|((gc->b)<<8)|(gc->faded)) + if (lastcolor != CBLEND(gc)) { + if (is_drill || is_mask) { +#ifdef FIXME + fprintf(f, "%d gray\n", gc->erase ? 0 : 1); +#endif + lastcolor = 0; + } + else { + double r, g, b; + r = gc->r; + g = gc->g; + b = gc->b; + if (gc->faded) { + r = 0.8 * 255 + 0.2 * r; + g = 0.8 * 255 + 0.2 * g; + b = 0.8 * 255 + 0.2 * b; + } +#ifdef FIXME + if (gc->r == gc->g && gc->g == gc->b) + fprintf(f, "%g gray\n", r / 255.0); + else + fprintf(f, "%g %g %g rgb\n", r / 255.0, g / 255.0, b / 255.0); +#endif + lastcolor = CBLEND(gc); + } + } +} + +static void gcode_draw_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2) +{ + use_gc(gc); + gdImageRectangle(gcode_im, + pcb_to_gcode(x1 - gcode_toolradius), + pcb_to_gcode(y1 - gcode_toolradius), + pcb_to_gcode(x2 + gcode_toolradius), pcb_to_gcode(y2 + gcode_toolradius), gc->color->c); +/* printf("Rect %d %d %d %d\n",x1,y1,x2,y2); */ +} + +static void gcode_fill_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2) +{ + use_gc(gc); + gdImageSetThickness(gcode_im, 0); + linewidth = 0; + gdImageFilledRectangle(gcode_im, + pcb_to_gcode(x1 - gcode_toolradius), + pcb_to_gcode(y1 - gcode_toolradius), + pcb_to_gcode(x2 + gcode_toolradius), pcb_to_gcode(y2 + gcode_toolradius), gc->color->c); +/* printf("FillRect %d %d %d %d\n",x1,y1,x2,y2); */ +} + +static void gcode_draw_line(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2) +{ + if (x1 == x2 && y1 == y2) { + Coord w = gc->width / 2; + gcode_fill_rect(gc, x1 - w, y1 - w, x1 + w, y1 + w); + return; + } + use_gc(gc); + + gdImageSetThickness(gcode_im, 0); + linewidth = 0; + gdImageLine(gcode_im, pcb_to_gcode(x1), pcb_to_gcode(y1), pcb_to_gcode(x2), pcb_to_gcode(y2), gdBrushed); +} + +static void gcode_draw_arc(hidGC gc, Coord cx, Coord cy, Coord width, Coord height, Angle start_angle, Angle delta_angle) +{ + Angle sa, ea; + + /* + * in gdImageArc, 0 degrees is to the right and +90 degrees is down + * in pcb, 0 degrees is to the left and +90 degrees is down + */ + start_angle = 180 - start_angle; + delta_angle = -delta_angle; + if (delta_angle > 0) { + sa = start_angle; + ea = start_angle + delta_angle; + } + else { + sa = start_angle + delta_angle; + ea = start_angle; + } + + /* + * make sure we start between 0 and 360 otherwise gd does strange + * things + */ + sa = NormalizeAngle(sa); + ea = NormalizeAngle(ea); + +#if 0 + printf("draw_arc %d,%d %dx%d %d..%d %d..%d\n", cx, cy, width, height, start_angle, delta_angle, sa, ea); + printf("gdImageArc (%p, %d, %d, %d, %d, %d, %d, %d)\n", + im, SCALE_X(cx), SCALE_Y(cy), SCALE(width), SCALE(height), sa, ea, gc->color->c); +#endif + use_gc(gc); + gdImageSetThickness(gcode_im, 0); + linewidth = 0; + gdImageArc(gcode_im, pcb_to_gcode(cx), pcb_to_gcode(cy), + pcb_to_gcode(2 * width + gcode_toolradius * 2), + pcb_to_gcode(2 * height + gcode_toolradius * 2), sa, ea, gdBrushed); +} + +static void gcode_fill_circle(hidGC gc, Coord cx, Coord cy, Coord radius) +{ + use_gc(gc); + + gdImageSetThickness(gcode_im, 0); + linewidth = 0; + gdImageFilledEllipse(gcode_im, pcb_to_gcode(cx), pcb_to_gcode(cy), + pcb_to_gcode(2 * radius + gcode_toolradius * 2), + pcb_to_gcode(2 * radius + gcode_toolradius * 2), gc->color->c); + if (save_drill && is_drill) { + if (n_drill == nmax_drill) { + drill = (struct drill_struct *) realloc(drill, (nmax_drill + 100) * sizeof(struct drill_struct)); + nmax_drill += 100; + } + drill[n_drill].x = COORD_TO_INCH(PCB->MaxWidth - cx); /* convert to inch, flip: will drill from bottom side */ + drill[n_drill].y = COORD_TO_INCH(PCB->MaxHeight - cy); /* PCB reverses y axis */ + n_drill++; +/* printf("Circle %d %d\n",cx,cy); */ + } +} + +static void gcode_fill_polygon(hidGC gc, int n_coords, Coord * x, Coord * y) +{ + int i; + gdPoint *points; + + points = (gdPoint *) malloc(n_coords * sizeof(gdPoint)); + if (points == NULL) { + fprintf(stderr, "ERROR: gcode_fill_polygon(): malloc failed\n"); + exit(1); + } + use_gc(gc); + for (i = 0; i < n_coords; i++) { + points[i].x = pcb_to_gcode(x[i]); + points[i].y = pcb_to_gcode(y[i]); + } + gdImageSetThickness(gcode_im, 0); + linewidth = 0; + gdImageFilledPolygon(gcode_im, points, n_coords, gc->color->c); + free(points); +/* printf("FillPoly\n"); */ +} + +static void gcode_calibrate(double xval, double yval) +{ + CRASH; +} + +static void gcode_set_crosshair(int x, int y, int a) +{ +} + +/* *** Miscellaneous ******************************************************* */ + +#include "dolists.h" + +HID gcode_hid; + +pcb_uninit_t hid_export_gcode_init() +{ + memset(&gcode_hid, 0, sizeof(HID)); + + common_nogui_init(&gcode_hid); + common_draw_helpers_init(&gcode_hid); + + gcode_hid.struct_size = sizeof(HID); + gcode_hid.name = "gcode"; + gcode_hid.description = "G-CODE export"; + gcode_hid.exporter = 1; + gcode_hid.poly_before = 1; + + gcode_hid.get_export_options = gcode_get_export_options; + gcode_hid.do_export = gcode_do_export; + gcode_hid.parse_arguments = gcode_parse_arguments; + gcode_hid.set_layer = gcode_set_layer; + gcode_hid.make_gc = gcode_make_gc; + gcode_hid.destroy_gc = gcode_destroy_gc; + gcode_hid.use_mask = gcode_use_mask; + gcode_hid.set_color = gcode_set_color; + gcode_hid.set_line_cap = gcode_set_line_cap; + gcode_hid.set_line_width = gcode_set_line_width; + gcode_hid.set_draw_xor = gcode_set_draw_xor; + gcode_hid.set_draw_faded = gcode_set_draw_faded; + gcode_hid.draw_line = gcode_draw_line; + gcode_hid.draw_arc = gcode_draw_arc; + gcode_hid.draw_rect = gcode_draw_rect; + gcode_hid.fill_circle = gcode_fill_circle; + gcode_hid.fill_polygon = gcode_fill_polygon; + gcode_hid.fill_rect = gcode_fill_rect; + gcode_hid.calibrate = gcode_calibrate; + gcode_hid.set_crosshair = gcode_set_crosshair; + + hid_register_hid(&gcode_hid); + + return NULL; +} Index: trunk/src_plugins/export_gcode/gcode.h =================================================================== --- trunk/src_plugins/export_gcode/gcode.h (nonexistent) +++ trunk/src_plugins/export_gcode/gcode.h (revision 1269) @@ -0,0 +1,3 @@ +/* $Id: nelma.h,v 1.2 2007/04/20 11:31:15 danmc Exp $ */ +extern const char *gcode_cookie; +extern HID gcode_hid; Index: trunk/src_plugins/export_gcode/lists.h =================================================================== --- trunk/src_plugins/export_gcode/lists.h (nonexistent) +++ trunk/src_plugins/export_gcode/lists.h (revision 1269) @@ -0,0 +1,285 @@ +/* Copyright (C) 2001-2007 Peter Selinger. + This file is part of Potrace. It is free software and it is covered + by the GNU General Public License. See the file COPYING for details. */ + +/* $Id: lists.h 147 2007-04-09 00:44:09Z selinger $ */ + +#ifndef PCB_HID_GCODE_LISTS_H +#define PCB_HID_GCODE_LISTS_H + +/* here we define some general list macros. Because they are macros, + they should work on any datatype with a "->next" component. Some of + them use a "hook". If elt and list are of type t* then hook is of + type t**. A hook stands for an insertion point in the list, i.e., + either before the first element, or between two elements, or after + the last element. If an operation "sets the hook" for an element, + then the hook is set to just before the element. One can insert + something at a hook. One can also unlink at a hook: this means, + unlink the element just after the hook. By "to unlink", we mean the + element is removed from the list, but not deleted. Thus, it and its + components still need to be freed. */ + +/* Note: these macros are somewhat experimental. Only the ones that + are actually *used* have been tested. So be careful to test any + that you use. Looking at the output of the preprocessor, "gcc -E" + (possibly piped though "indent"), might help too. Also: these + macros define some internal (local) variables that start with + "_". */ + +/* we enclose macro definitions whose body consists of more than one + statement in MACRO_BEGIN and MACRO_END, rather than '{' and '}'. The + reason is that we want to be able to use the macro in a context + such as "if (...) macro(...); else ...". If we didn't use this obscure + trick, we'd have to omit the ";" in such cases. */ + +#define MACRO_BEGIN do { +#define MACRO_END } while (0) + +/* ---------------------------------------------------------------------- */ +/* macros for singly-linked lists */ + +/* traverse list. At the end, elt is set to NULL. */ +#define list_forall(elt, list) for (elt=list; elt!=NULL; elt=elt->next) + +/* set elt to the first element of list satisfying boolean condition + c, or NULL if not found */ +#define list_find(elt, list, c) \ + MACRO_BEGIN list_forall(elt, list) if (c) break; MACRO_END + +/* like forall, except also set hook for elt. */ +#define list_forall2(elt, list, hook) \ + for (elt=list, hook=&list; elt!=NULL; hook=&elt->next, elt=elt->next) + +/* same as list_find, except also set hook for elt. */ +#define list_find2(elt, list, c, hook) \ + MACRO_BEGIN list_forall2(elt, list, hook) if (c) break; MACRO_END + +/* same, except only use hook. */ +#define _list_forall_hook(list, hook) \ + for (hook=&list; *hook!=NULL; hook=&(*hook)->next) + +/* same, except only use hook. Note: c may only refer to *hook, not elt. */ +#define _list_find_hook(list, c, hook) \ + MACRO_BEGIN _list_forall_hook(list, hook) if (c) break; MACRO_END + +/* insert element after hook */ +#define list_insert_athook(elt, hook) \ + MACRO_BEGIN elt->next = *hook; *hook = elt; MACRO_END + +/* insert element before hook */ +#define list_insert_beforehook(elt, hook) \ + MACRO_BEGIN elt->next = *hook; *hook = elt; hook=&elt->next; MACRO_END + +/* unlink element after hook, let elt be unlinked element, or NULL. + hook remains. */ +#define list_unlink_athook(list, elt, hook) \ + MACRO_BEGIN \ + elt = hook ? *hook : NULL; if (elt) { *hook = elt->next; elt->next = NULL; }\ + MACRO_END + +/* unlink the specific element, if it is in the list. Otherwise, set + elt to NULL */ +#define list_unlink(listtype, list, elt) \ + MACRO_BEGIN \ + listtype **_hook; \ + _list_find_hook(list, *_hook==elt, _hook); \ + list_unlink_athook(list, elt, _hook); \ + MACRO_END + +/* prepend elt to list */ +#define list_prepend(list, elt) \ + MACRO_BEGIN elt->next = list; list = elt; MACRO_END + +/* append elt to list. */ +#define list_append(listtype, list, elt) \ + MACRO_BEGIN \ + listtype **_hook; \ + _list_forall_hook(list, _hook) {} \ + list_insert_athook(elt, _hook); \ + MACRO_END + +/* unlink the first element that satisfies the condition. */ +#define list_unlink_cond(listtype, list, elt, c) \ + MACRO_BEGIN \ + listtype **_hook; \ + list_find2(elt, list, c, _hook); \ + list_unlink_athook(list, elt, _hook); \ + MACRO_END + +/* let elt be the nth element of the list, starting to count from 0. + Return NULL if out of bounds. */ +#define list_nth(elt, list, n) \ + MACRO_BEGIN \ + int _x; /* only evaluate n once */ \ + for (_x=(n), elt=list; _x && elt; _x--, elt=elt->next) {} \ + MACRO_END + +/* let elt be the nth element of the list, starting to count from 0. + Return NULL if out of bounds. */ +#define list_nth_hook(elt, list, n, hook) \ + MACRO_BEGIN \ + int _x; /* only evaluate n once */ \ + for (_x=(n), elt=list, hook=&list; _x && elt; _x--, hook=&elt->next, elt=elt->next) {} \ + MACRO_END + +/* set n to the length of the list */ +#define list_length(listtype, list, n) \ + MACRO_BEGIN \ + listtype *_elt; \ + n=0; \ + list_forall(_elt, list) \ + n++; \ + MACRO_END + +/* set n to the index of the first element satisfying cond, or -1 if + none found. Also set elt to the element, or NULL if none found. */ +#define list_index(list, n, elt, c) \ + MACRO_BEGIN \ + n=0; \ + list_forall(elt, list) { \ + if (c) break; \ + n++; \ + } \ + if (!elt) \ + n=-1; \ + MACRO_END + +/* set n to the number of elements in the list that satisfy condition c */ +#define list_count(list, n, elt, c) \ + MACRO_BEGIN \ + n=0; \ + list_forall(elt, list) { \ + if (c) n++; \ + } \ + MACRO_END + +/* let elt be each element of the list, unlinked. At the end, set list=NULL. */ +#define list_forall_unlink(elt, list) \ + for (elt=list; elt ? (list=elt->next, elt->next=NULL), 1 : 0; elt=list) + +/* reverse a list (efficient) */ +#define list_reverse(listtype, list) \ + MACRO_BEGIN \ + listtype *_list1=NULL, *elt; \ + list_forall_unlink(elt, list) \ + list_prepend(_list1, elt); \ + list = _list1; \ + MACRO_END + +/* insert the element ELT just before the first element TMP of the + list for which COND holds. Here COND must be a condition of ELT and + TMP. Typical usage is to insert an element into an ordered list: + for instance, list_insert_ordered(listtype, list, elt, tmp, + elt->size <= tmp->size). Note: if we give a "less than or equal" + condition, the new element will be inserted just before a sequence + of equal elements. If we give a "less than" condition, the new + element will be inserted just after a list of equal elements. + Note: it is much more efficient to construct a list with + list_prepend and then order it with list_merge_sort, than to + construct it with list_insert_ordered. */ +#define list_insert_ordered(listtype, list, elt, tmp, cond) \ + MACRO_BEGIN \ + listtype **_hook; \ + _list_find_hook(list, (tmp=*_hook, (cond)), _hook); \ + list_insert_athook(elt, _hook); \ + MACRO_END + +/* sort the given list, according to the comparison condition. + Typical usage is list_sort(listtype, list, a, b, a->size < + b->size). Note: if we give "less than or equal" condition, each + segment of equal elements will be reversed in order. If we give a + "less than" condition, each segment of equal elements will retain + the original order. The latter is slower but sometimes + prettier. Average running time: n*n/2. */ +#define list_sort(listtype, list, a, b, cond) \ + MACRO_BEGIN \ + listtype *_newlist=NULL; \ + list_forall_unlink(a, list) \ + list_insert_ordered(listtype, _newlist, a, b, cond); \ + list = _newlist; \ + MACRO_END + +/* a much faster sort algorithm (merge sort, n log n worst case). It + is required that the list type has an additional, unused next1 + component. Note there is no curious reversal of order of equal + elements as for list_sort. */ + +#define list_mergesort(listtype, list, a, b, cond) \ + MACRO_BEGIN \ + listtype *_elt, **_hook1; \ + \ + for (_elt=list; _elt; _elt=_elt->next1) { \ + _elt->next1 = _elt->next; \ + _elt->next = NULL; \ + } \ + do { \ + _hook1 = &(list); \ + while ((a = *_hook1) != NULL && (b = a->next1) != NULL ) { \ + _elt = b->next1; \ + _list_merge_cond(listtype, a, b, cond, *_hook1); \ + _hook1 = &((*_hook1)->next1); \ + *_hook1 = _elt; \ + } \ + } while (_hook1 != &(list)); \ + MACRO_END + +/* merge two sorted lists. Store result at &result */ +#define _list_merge_cond(listtype, a, b, cond, result) \ + MACRO_BEGIN \ + listtype **_hook; \ + _hook = &(result); \ + while (1) { \ + if (a==NULL) { \ + *_hook = b; \ + break; \ + } else if (b==NULL) { \ + *_hook = a; \ + break; \ + } else if (cond) { \ + *_hook = a; \ + _hook = &(a->next); \ + a = a->next; \ + } else { \ + *_hook = b; \ + _hook = &(b->next); \ + b = b->next; \ + } \ + } \ + MACRO_END + +/* ---------------------------------------------------------------------- */ +/* macros for doubly-linked lists */ + +#define dlist_append(head, end, elt) \ + MACRO_BEGIN \ + elt->prev = end; \ + elt->next = NULL; \ + if (end) { \ + end->next = elt; \ + } else { \ + head = elt; \ + } \ + end = elt; \ + MACRO_END + +/* let elt be each element of the list, unlinked. At the end, set list=NULL. */ +#define dlist_forall_unlink(elt, head, end) \ + for (elt=head; elt ? (head=elt->next, elt->next=NULL, elt->prev=NULL), 1 : (end=NULL, 0); elt=head) + +/* unlink the first element of the list */ +#define dlist_unlink_first(head, end, elt) \ + MACRO_BEGIN \ + elt = head; \ + if (head) { \ + head = head->next; \ + if (head) { \ + head->prev = NULL; \ + } else { \ + end = NULL; \ + } \ + elt->prev = NULL; \ + elt->next = NULL; \ + } \ + MACRO_END + +#endif /* PCB_HID_GCODE_LISTS_H */ Index: trunk/src_plugins/export_gcode/potracelib.h =================================================================== --- trunk/src_plugins/export_gcode/potracelib.h (nonexistent) +++ trunk/src_plugins/export_gcode/potracelib.h (revision 1269) @@ -0,0 +1,130 @@ +/* Copyright (C) 2001-2007 Peter Selinger. + This file is part of Potrace. It is free software and it is covered + by the GNU General Public License. See the file COPYING for details. */ + +#ifndef POTRACELIB_H +#define POTRACELIB_H + +/* this file defines the API for the core Potrace library. For a more + detailed description of the API, see doc/potracelib.txt */ + +/* ---------------------------------------------------------------------- */ +/* tracing parameters */ + +/* turn policies */ +#define POTRACE_TURNPOLICY_BLACK 0 +#define POTRACE_TURNPOLICY_WHITE 1 +#define POTRACE_TURNPOLICY_LEFT 2 +#define POTRACE_TURNPOLICY_RIGHT 3 +#define POTRACE_TURNPOLICY_MINORITY 4 +#define POTRACE_TURNPOLICY_MAJORITY 5 +#define POTRACE_TURNPOLICY_RANDOM 6 + +/* structure to hold progress bar callback data */ +struct potrace_progress_s { + void (*callback) (double progress, void *privdata); /* callback fn */ + void *data; /* callback function's private data */ + double min, max; /* desired range of progress, e.g. 0.0 to 1.0 */ + double epsilon; /* granularity: can skip smaller increments */ +}; +typedef struct potrace_progress_s potrace_progress_t; + +/* structure to hold tracing parameters */ +struct potrace_param_s { + int turdsize; /* area of largest path to be ignored */ + int turnpolicy; /* resolves ambiguous turns in path decomposition */ + double alphamax; /* corner threshold */ + int opticurve; /* use curve optimization? */ + double opttolerance; /* curve optimization tolerance */ + potrace_progress_t progress; /* progress callback function */ +}; +typedef struct potrace_param_s potrace_param_t; + +/* ---------------------------------------------------------------------- */ +/* bitmaps */ + +/* native word size */ +typedef unsigned long potrace_word; + +/* Internal bitmap format. The n-th scanline starts at scanline(n) = + (map + n*dy). Raster data is stored as a sequence of potrace_words + (NOT bytes). The leftmost bit of scanline n is the most significant + bit of scanline(n)[0]. */ +struct potrace_bitmap_s { + int w, h; /* width and height, in pixels */ + int dy; /* words per scanline (not bytes) */ + potrace_word *map; /* raw data, dy*h words */ +}; +typedef struct potrace_bitmap_s potrace_bitmap_t; + +/* ---------------------------------------------------------------------- */ +/* curves */ + +/* point */ +struct potrace_dpoint_s { + double x, y; +}; +typedef struct potrace_dpoint_s potrace_dpoint_t; + +/* segment tags */ +#define POTRACE_CURVETO 1 +#define POTRACE_CORNER 2 + +/* closed curve segment */ +struct potrace_curve_s { + int n; /* number of segments */ + int *tag; /* tag[n]: POTRACE_CURVETO or POTRACE_CORNER */ + potrace_dpoint_t(*c)[3]; /* c[n][3]: control points. + c[n][0] is unused for tag[n]=POTRACE_CORNER */ +}; +typedef struct potrace_curve_s potrace_curve_t; + +/* Linked list of signed curve segments. Also carries a tree structure. */ +struct potrace_path_s { + int area; /* area of the bitmap path */ + int sign; /* '+' or '-', depending on orientation */ + potrace_curve_t curve; /* this path's vector data */ + + struct potrace_path_s *next; /* linked list structure */ + + struct potrace_path_s *childlist; /* tree structure */ + struct potrace_path_s *sibling; /* tree structure */ + + struct potrace_privpath_s *priv; /* private state */ +}; +typedef struct potrace_path_s potrace_path_t; + +/* ---------------------------------------------------------------------- */ +/* Potrace state */ + +#define POTRACE_STATUS_OK 0 +#define POTRACE_STATUS_INCOMPLETE 1 + +struct potrace_state_s { + int status; + potrace_path_t *plist; /* vector data */ + + struct potrace_privstate_s *priv; /* private state */ +}; +typedef struct potrace_state_s potrace_state_t; + +/* ---------------------------------------------------------------------- */ +/* API functions */ + +/* get default parameters */ +potrace_param_t *potrace_param_default(void); + +/* free parameter set */ +void potrace_param_free(potrace_param_t * p); + +/* trace a bitmap*/ +potrace_state_t *potrace_trace(const potrace_param_t * param, const potrace_bitmap_t * bm); + +/* free a Potrace state */ +void potrace_state_free(potrace_state_t * st); + +/* return a static plain text version string identifying this version + of potracelib */ +char *potrace_version(void); + +#endif /* POTRACELIB_H */ Index: trunk/src_plugins/export_gcode/trace.c =================================================================== --- trunk/src_plugins/export_gcode/trace.c (nonexistent) +++ trunk/src_plugins/export_gcode/trace.c (revision 1269) @@ -0,0 +1,1292 @@ +/* This file was slightly modified by Alberto Maccioni to be used with PCB G-CODE exporter*/ + +/* Copyright (C) 2001-2007 Peter Selinger. + This file is part of Potrace. It is free software and it is covered + by the GNU General Public License. See the file COPYING for details. */ + +/* $Id: trace.c 147 2007-04-09 00:44:09Z selinger $ */ +/* transform jaggy paths into smooth curves */ + +#include +#include +#include +#include + +#include "global.h" +#include "potracelib.h" +#include "curve.h" +#include "lists.h" +#include "auxiliary.h" +#include "trace.h" +/*#include "progress.h"*/ + +#define INFTY 10000000 /* it suffices that this is longer than any + path; it need not be really infinite */ +#define COS179 -0.999847695156 /* the cosine of 179 degrees */ + +/* ---------------------------------------------------------------------- */ +#define SAFE_MALLOC(var, n, typ) \ + if ((var = (typ *)malloc((n)*sizeof(typ))) == NULL) goto malloc_error + +/* ---------------------------------------------------------------------- */ +/* auxiliary functions */ + +/* return a direction that is 90 degrees counterclockwise from p2-p0, + but then restricted to one of the major wind directions (n, nw, w, etc) */ +static inline point_t dorth_infty(dpoint_t p0, dpoint_t p2) +{ + point_t r; + + r.y = sign(p2.x - p0.x); + r.x = -sign(p2.y - p0.y); + + return r; +} + +/* return (p1-p0)x(p2-p0), the area of the parallelogram */ +static inline double dpara(dpoint_t p0, dpoint_t p1, dpoint_t p2) +{ + double x1, y1, x2, y2; + + x1 = p1.x - p0.x; + y1 = p1.y - p0.y; + x2 = p2.x - p0.x; + y2 = p2.y - p0.y; + + return x1 * y2 - x2 * y1; +} + +/* ddenom/dpara have the property that the square of radius 1 centered + at p1 intersects the line p0p2 iff |dpara(p0,p1,p2)| <= ddenom(p0,p2) */ +static inline double ddenom(dpoint_t p0, dpoint_t p2) +{ + point_t r = dorth_infty(p0, p2); + + return r.y * (p2.x - p0.x) - r.x * (p2.y - p0.y); +} + +/* return 1 if a <= b < c < a, in a cyclic sense (mod n) */ +static inline int cyclic(int a, int b, int c) +{ + if (a <= c) { + return (a <= b && b < c); + } + else { + return (a <= b || b < c); + } +} + +/* determine the center and slope of the line i..j. Assume ilen; + sums_t *sums = pp->sums; + + double x, y, x2, xy, y2; + double k; + double a, b, c, lambda2, l; + int r = 0; /* rotations from i to j */ + + while (j >= n) { + j -= n; + r += 1; + } + while (i >= n) { + i -= n; + r -= 1; + } + while (j < 0) { + j += n; + r -= 1; + } + while (i < 0) { + i += n; + r += 1; + } + + x = sums[j + 1].x - sums[i].x + r * sums[n].x; + y = sums[j + 1].y - sums[i].y + r * sums[n].y; + x2 = sums[j + 1].x2 - sums[i].x2 + r * sums[n].x2; + xy = sums[j + 1].xy - sums[i].xy + r * sums[n].xy; + y2 = sums[j + 1].y2 - sums[i].y2 + r * sums[n].y2; + k = j + 1 - i + r * n; + + ctr->x = x / k; + ctr->y = y / k; + + a = (x2 - (double) x * x / k) / k; + b = (xy - (double) x * y / k) / k; + c = (y2 - (double) y * y / k) / k; + + lambda2 = (a + c + sqrt((a - c) * (a - c) + 4 * b * b)) / 2; /* larger e.value */ + + /* now find e.vector for lambda2 */ + a -= lambda2; + c -= lambda2; + + if (fabs(a) >= fabs(c)) { + l = sqrt(a * a + b * b); + if (l != 0) { + dir->x = -b / l; + dir->y = a / l; + } + } + else { + l = sqrt(c * c + b * b); + if (l != 0) { + dir->x = -c / l; + dir->y = b / l; + } + } + if (l == 0) { + dir->x = dir->y = 0; /* sometimes this can happen when k=4: + the two eigenvalues coincide */ + } +} + +/* the type of (affine) quadratic forms, represented as symmetric 3x3 + matrices. The value of the quadratic form at a vector (x,y) is v^t + Q v, where v = (x,y,1)^t. */ +typedef double quadform_t[3][3]; + +/* Apply quadratic form Q to vector w = (w.x,w.y) */ +static inline double quadform(quadform_t Q, dpoint_t w) +{ + double v[3]; + int i, j; + double sum; + + v[0] = w.x; + v[1] = w.y; + v[2] = 1; + sum = 0.0; + + for (i = 0; i < 3; i++) { + for (j = 0; j < 3; j++) { + sum += v[i] * Q[i][j] * v[j]; + } + } + return sum; +} + +/* calculate p1 x p2 */ +static inline int xprod(point_t p1, point_t p2) +{ + return p1.x * p2.y - p1.y * p2.x; +} + +/* calculate (p1-p0)x(p3-p2) */ +static inline double cprod(dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3) +{ + double x1, y1, x2, y2; + + x1 = p1.x - p0.x; + y1 = p1.y - p0.y; + x2 = p3.x - p2.x; + y2 = p3.y - p2.y; + + return x1 * y2 - x2 * y1; +} + +/* calculate (p1-p0)*(p2-p0) */ +static inline double iprod(dpoint_t p0, dpoint_t p1, dpoint_t p2) +{ + double x1, y1, x2, y2; + + x1 = p1.x - p0.x; + y1 = p1.y - p0.y; + x2 = p2.x - p0.x; + y2 = p2.y - p0.y; + + return x1 * x2 + y1 * y2; +} + +/* calculate (p1-p0)*(p3-p2) */ +static inline double iprod1(dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3) +{ + double x1, y1, x2, y2; + + x1 = p1.x - p0.x; + y1 = p1.y - p0.y; + x2 = p3.x - p2.x; + y2 = p3.y - p2.y; + + return x1 * x2 + y1 * y2; +} + +/* calculate distance between two points */ +static inline double ddist(dpoint_t p, dpoint_t q) +{ + return sqrt(sq(p.x - q.x) + sq(p.y - q.y)); +} + +/* calculate point of a bezier curve */ +static inline dpoint_t bezier(double t, dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3) +{ + double s = 1 - t; + dpoint_t res; + + /* Note: a good optimizing compiler (such as gcc-3) reduces the + following to 16 multiplications, using common subexpression + elimination. */ + + res.x = s * s * s * p0.x + 3 * (s * s * t) * p1.x + 3 * (t * t * s) * p2.x + t * t * t * p3.x; + res.y = s * s * s * p0.y + 3 * (s * s * t) * p1.y + 3 * (t * t * s) * p2.y + t * t * t * p3.y; + + return res; +} + +/* calculate the point t in [0..1] on the (convex) bezier curve + (p0,p1,p2,p3) which is tangent to q1-q0. Return -1.0 if there is no + solution in [0..1]. */ +static double tangent(dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3, dpoint_t q0, dpoint_t q1) +{ + double A, B, C; /* (1-t)^2 A + 2(1-t)t B + t^2 C = 0 */ + double a, b, c; /* a t^2 + b t + c = 0 */ + double d, s, r1, r2; + + A = cprod(p0, p1, q0, q1); + B = cprod(p1, p2, q0, q1); + C = cprod(p2, p3, q0, q1); + + a = A - 2 * B + C; + b = -2 * A + 2 * B; + c = A; + + d = b * b - 4 * a * c; + + if (a == 0 || d < 0) { + return -1.0; + } + + s = sqrt(d); + + r1 = (-b + s) / (2 * a); + r2 = (-b - s) / (2 * a); + + if (r1 >= 0 && r1 <= 1) { + return r1; + } + else if (r2 >= 0 && r2 <= 1) { + return r2; + } + else { + return -1.0; + } +} + +/* ---------------------------------------------------------------------- */ +/* Preparation: fill in the sum* fields of a path (used for later + rapid summing). Return 0 on success, 1 with errno set on + failure. */ +static int calc_sums(privpath_t * pp) +{ + int i, x, y; + int n = pp->len; + + SAFE_MALLOC(pp->sums, pp->len + 1, sums_t); + + /* origin */ + pp->x0 = pp->pt[0].x; + pp->y0 = pp->pt[0].y; + + /* preparatory computation for later fast summing */ + pp->sums[0].x2 = pp->sums[0].xy = pp->sums[0].y2 = pp->sums[0].x = pp->sums[0].y = 0; + for (i = 0; i < n; i++) { + x = pp->pt[i].x - pp->x0; + y = pp->pt[i].y - pp->y0; + pp->sums[i + 1].x = pp->sums[i].x + x; + pp->sums[i + 1].y = pp->sums[i].y + y; + pp->sums[i + 1].x2 = pp->sums[i].x2 + x * x; + pp->sums[i + 1].xy = pp->sums[i].xy + x * y; + pp->sums[i + 1].y2 = pp->sums[i].y2 + y * y; + } + return 0; + +malloc_error: + return 1; +} + +/* ---------------------------------------------------------------------- */ +/* Stage 1: determine the straight subpaths (Sec. 2.2.1). Fill in the + "lon" component of a path object (based on pt/len). For each i, + lon[i] is the furthest index such that a straight line can be drawn + from i to lon[i]. Return 1 on error with errno set, else 0. */ + +/* this algorithm depends on the fact that the existence of straight + subpaths is a triplewise property. I.e., there exists a straight + line through squares i0,...,in iff there exists a straight line + through i,j,k, for all i0<=i= 0 and xprod(constraint[1], + cur) <= 0. */ + +/* Remark for Potrace 1.1: the current implementation of calc_lon is + more complex than the implementation found in Potrace 1.0, but it + is considerably faster. The introduction of the "nc" data structure + means that we only have to test the constraints for "corner" + points. On a typical input file, this speeds up the calc_lon + function by a factor of 31.2, thereby decreasing its time share + within the overall Potrace algorithm from 72.6% to 7.82%, and + speeding up the overall algorithm by a factor of 3.36. On another + input file, calc_lon was sped up by a factor of 6.7, decreasing its + time share from 51.4% to 13.61%, and speeding up the overall + algorithm by a factor of 1.78. In any case, the savings are + substantial. */ + +/* returns 0 on success, 1 on error with errno set */ +static int calc_lon(privpath_t * pp) +{ + point_t *pt = pp->pt; + int n = pp->len; + int i, j, k, k1; + int ct[4], dir; + point_t constraint[2]; + point_t cur; + point_t off; + int *pivk = NULL; /* pivk[n] */ + int *nc = NULL; /* nc[n]: next corner */ + point_t dk; /* direction of k-k1 */ + int a, b, c, d; + + SAFE_MALLOC(pivk, n, int); + SAFE_MALLOC(nc, n, int); + + /* initialize the nc data structure. Point from each point to the + furthest future point to which it is connected by a vertical or + horizontal segment. We take advantage of the fact that there is + always a direction change at 0 (due to the path decomposition + algorithm). But even if this were not so, there is no harm, as + in practice, correctness does not depend on the word "furthest" + above. */ + k = 0; + for (i = n - 1; i >= 0; i--) { + if (pt[i].x != pt[k].x && pt[i].y != pt[k].y) { + k = i + 1; /* necessarily ilon, n, int); + + /* determine pivot points: for each i, let pivk[i] be the furthest k + such that all j with i= 0; i--) { + ct[0] = ct[1] = ct[2] = ct[3] = 0; + + /* keep track of "directions" that have occurred */ + dir = (3 + 3 * (pt[mod(i + 1, n)].x - pt[i].x) + (pt[mod(i + 1, n)].y - pt[i].y)) / 2; + ct[dir]++; + + constraint[0].x = 0; + constraint[0].y = 0; + constraint[1].x = 0; + constraint[1].y = 0; + + /* find the next k such that no straight line from i to k */ + k = nc[i]; + k1 = i; + while (1) { + + dir = (3 + 3 * sign(pt[k].x - pt[k1].x) + sign(pt[k].y - pt[k1].y)) / 2; + ct[dir]++; + + /* if all four "directions" have occurred, cut this path */ + if (ct[0] && ct[1] && ct[2] && ct[3]) { + pivk[i] = k1; + goto foundk; + } + + cur.x = pt[k].x - pt[i].x; + cur.y = pt[k].y - pt[i].y; + + /* see if current constraint is violated */ + if (xprod(constraint[0], cur) < 0 || xprod(constraint[1], cur) > 0) { + goto constraint_viol; + } + + /* else, update constraint */ + if (abs(cur.x) <= 1 && abs(cur.y) <= 1) { + /* no constraint */ + } + else { + off.x = cur.x + ((cur.y >= 0 && (cur.y > 0 || cur.x < 0)) ? 1 : -1); + off.y = cur.y + ((cur.x <= 0 && (cur.x < 0 || cur.y < 0)) ? 1 : -1); + if (xprod(constraint[0], off) >= 0) { + constraint[0] = off; + } + off.x = cur.x + ((cur.y <= 0 && (cur.y < 0 || cur.x < 0)) ? 1 : -1); + off.y = cur.y + ((cur.x >= 0 && (cur.x > 0 || cur.y < 0)) ? 1 : -1); + if (xprod(constraint[1], off) <= 0) { + constraint[1] = off; + } + } + k1 = k; + k = nc[k1]; + if (!cyclic(k, i, k1)) { + break; + } + } + constraint_viol: + /* k1 was the last "corner" satisfying the current constraint, and + k is the first one violating it. We now need to find the last + point along k1..k which satisfied the constraint. */ + dk.x = sign(pt[k].x - pt[k1].x); + dk.y = sign(pt[k].y - pt[k1].y); + cur.x = pt[k1].x - pt[i].x; + cur.y = pt[k1].y - pt[i].y; + /* find largest integer j such that xprod(constraint[0], cur+j*dk) + >= 0 and xprod(constraint[1], cur+j*dk) <= 0. Use bilinearity + of xprod. */ + a = xprod(constraint[0], cur); + b = xprod(constraint[0], dk); + c = xprod(constraint[1], cur); + d = xprod(constraint[1], dk); + /* find largest integer j such that a+j*b>=0 and c+j*d<=0. This + can be solved with integer arithmetic. */ + j = INFTY; + if (b < 0) { + j = floordiv(a, -b); + } + if (d > 0) { + j = min(j, floordiv(-c, d)); + } + pivk[i] = mod(k1 + j, n); + foundk: + ; + } /* for i */ + + /* clean up: for each i, let lon[i] be the largest k such that for + all i' with i<=i'lon[n - 1] = j; + for (i = n - 2; i >= 0; i--) { + if (cyclic(i + 1, pivk[i], j)) { + j = pivk[i]; + } + pp->lon[i] = j; + } + + for (i = n - 1; cyclic(mod(i + 1, n), j, pp->lon[i]); i--) { + pp->lon[i] = j; + } + + free(pivk); + free(nc); + return 0; + +malloc_error: + free(pivk); + free(nc); + return 1; +} + + +/* ---------------------------------------------------------------------- */ +/* Stage 2: calculate the optimal polygon (Sec. 2.2.2-2.2.4). */ + +/* Auxiliary function: calculate the penalty of an edge from i to j in + the given path. This needs the "lon" and "sum*" data. */ + +static double penalty3(privpath_t * pp, int i, int j) +{ + int n = pp->len; + point_t *pt = pp->pt; + sums_t *sums = pp->sums; + + /* assume 0<=i= n) { + j -= n; + r += 1; + } + + x = sums[j + 1].x - sums[i].x + r * sums[n].x; + y = sums[j + 1].y - sums[i].y + r * sums[n].y; + x2 = sums[j + 1].x2 - sums[i].x2 + r * sums[n].x2; + xy = sums[j + 1].xy - sums[i].xy + r * sums[n].xy; + y2 = sums[j + 1].y2 - sums[i].y2 + r * sums[n].y2; + k = j + 1 - i + r * n; + + px = (pt[i].x + pt[j].x) / 2.0 - pt[0].x; + py = (pt[i].y + pt[j].y) / 2.0 - pt[0].y; + ey = (pt[j].x - pt[i].x); + ex = -(pt[j].y - pt[i].y); + + a = ((x2 - 2 * x * px) / k + px * px); + b = ((xy - x * py - y * px) / k + px * py); + c = ((y2 - 2 * y * py) / k + py * py); + + s = ex * ex * a + 2 * ex * ey * b + ey * ey * c; + + return sqrt(s); +} + +/* find the optimal polygon. Fill in the m and po components. Return 1 + on failure with errno set, else 0. Non-cyclic version: assumes i=0 + is in the polygon. Fixme: ### implement cyclic version. */ +static int bestpolygon(privpath_t * pp) +{ + int i, j, m, k; + int n = pp->len; + double *pen = NULL; /* pen[n+1]: penalty vector */ + int *prev = NULL; /* prev[n+1]: best path pointer vector */ + int *clip0 = NULL; /* clip0[n]: longest segment pointer, non-cyclic */ + int *clip1 = NULL; /* clip1[n+1]: backwards segment pointer, non-cyclic */ + int *seg0 = NULL; /* seg0[m+1]: forward segment bounds, m<=n */ + int *seg1 = NULL; /* seg1[m+1]: backward segment bounds, m<=n */ + double thispen; + double best; + int c; + + SAFE_MALLOC(pen, n + 1, double); + SAFE_MALLOC(prev, n + 1, int); + SAFE_MALLOC(clip0, n, int); + SAFE_MALLOC(clip1, n + 1, int); + SAFE_MALLOC(seg0, n + 1, int); + SAFE_MALLOC(seg1, n + 1, int); + + /* calculate clipped paths */ + for (i = 0; i < n; i++) { + c = mod(pp->lon[mod(i - 1, n)] - 1, n); + if (c == i) { + c = mod(i + 1, n); + } + if (c < i) { + clip0[i] = n; + } + else { + clip0[i] = c; + } + } + + /* calculate backwards path clipping, non-cyclic. j <= clip0[i] iff + clip1[j] <= i, for i,j=0..n. */ + j = 1; + for (i = 0; i < n; i++) { + while (j <= clip0[i]) { + clip1[j] = i; + j++; + } + } + + /* calculate seg0[j] = longest path from 0 with j segments */ + i = 0; + for (j = 0; i < n; j++) { + seg0[j] = i; + i = clip0[i]; + } + seg0[j] = n; + m = j; + + /* calculate seg1[j] = longest path to n with m-j segments */ + i = n; + for (j = m; j > 0; j--) { + seg1[j] = i; + i = clip1[i]; + } + seg1[0] = 0; + + /* now find the shortest path with m segments, based on penalty3 */ + /* note: the outer 2 loops jointly have at most n interations, thus + the worst-case behavior here is quadratic. In practice, it is + close to linear since the inner loop tends to be short. */ + pen[0] = 0; + for (j = 1; j <= m; j++) { + for (i = seg1[j]; i <= seg0[j]; i++) { + best = -1; + for (k = seg0[j - 1]; k >= clip1[i]; k--) { + thispen = penalty3(pp, k, i) + pen[k]; + if (best < 0 || thispen < best) { + prev[i] = k; + best = thispen; + } + } + pen[i] = best; + } + } + + pp->m = m; + SAFE_MALLOC(pp->po, m, int); + + /* read off shortest path */ + for (i = n, j = m - 1; i > 0; j--) { + i = prev[i]; + pp->po[j] = i; + } + + free(pen); + free(prev); + free(clip0); + free(clip1); + free(seg0); + free(seg1); + return 0; + +malloc_error: + free(pen); + free(prev); + free(clip0); + free(clip1); + free(seg0); + free(seg1); + return 1; +} + +/* ---------------------------------------------------------------------- */ +/* Stage 3: vertex adjustment (Sec. 2.3.1). */ + +/* Adjust vertices of optimal polygon: calculate the intersection of + the two "optimal" line segments, then move it into the unit square + if it lies outside. Return 1 with errno set on error; 0 on + success. */ + +static int adjust_vertices(privpath_t * pp) +{ + int m = pp->m; + int *po = pp->po; + int n = pp->len; + point_t *pt = pp->pt; + int x0 = pp->x0; + int y0 = pp->y0; + + dpoint_t *ctr = NULL; /* ctr[m] */ + dpoint_t *dir = NULL; /* dir[m] */ + quadform_t *q = NULL; /* q[m] */ + double v[3]; + double d; + int i, j, k, l; + dpoint_t s; + int r; + + SAFE_MALLOC(ctr, m, dpoint_t); + SAFE_MALLOC(dir, m, dpoint_t); + SAFE_MALLOC(q, m, quadform_t); + + r = privcurve_init(&pp->curve, m); + if (r) { + goto malloc_error; + } + + /* calculate "optimal" point-slope representation for each line + segment */ + for (i = 0; i < m; i++) { + j = po[mod(i + 1, m)]; + j = mod(j - po[i], n) + po[i]; + pointslope(pp, po[i], j, &ctr[i], &dir[i]); + } + + /* represent each line segment as a singular quadratic form; the + distance of a point (x,y) from the line segment will be + (x,y,1)Q(x,y,1)^t, where Q=q[i]. */ + for (i = 0; i < m; i++) { + d = sq(dir[i].x) + sq(dir[i].y); + if (d == 0.0) { + for (j = 0; j < 3; j++) { + for (k = 0; k < 3; k++) { + q[i][j][k] = 0; + } + } + } + else { + v[0] = dir[i].y; + v[1] = -dir[i].x; + v[2] = -v[1] * ctr[i].y - v[0] * ctr[i].x; + for (l = 0; l < 3; l++) { + for (k = 0; k < 3; k++) { + q[i][l][k] = v[l] * v[k] / d; + } + } + } + } + + /* now calculate the "intersections" of consecutive segments. + Instead of using the actual intersection, we find the point + within a given unit square which minimizes the square distance to + the two lines. */ + for (i = 0; i < m; i++) { + quadform_t Q; + dpoint_t w; + double dx, dy; + double det; + double min, cand; /* minimum and candidate for minimum of quad. form */ + double xmin, ymin; /* coordinates of minimum */ + int z; + + /* let s be the vertex, in coordinates relative to x0/y0 */ + s.x = pt[po[i]].x - x0; + s.y = pt[po[i]].y - y0; + + /* intersect segments i-1 and i */ + + j = mod(i - 1, m); + + /* add quadratic forms */ + for (l = 0; l < 3; l++) { + for (k = 0; k < 3; k++) { + Q[l][k] = q[j][l][k] + q[i][l][k]; + } + } + + while (1) { + /* minimize the quadratic form Q on the unit square */ + /* find intersection */ + +#ifdef HAVE_GCC_LOOP_BUG + /* work around gcc bug #12243 */ + free(NULL); +#endif + + det = Q[0][0] * Q[1][1] - Q[0][1] * Q[1][0]; + if (det != 0.0) { + w.x = (-Q[0][2] * Q[1][1] + Q[1][2] * Q[0][1]) / det; + w.y = (Q[0][2] * Q[1][0] - Q[1][2] * Q[0][0]) / det; + break; + } + + /* matrix is singular - lines are parallel. Add another, + orthogonal axis, through the center of the unit square */ + if (Q[0][0] > Q[1][1]) { + v[0] = -Q[0][1]; + v[1] = Q[0][0]; + } + else if (Q[1][1]) { + v[0] = -Q[1][1]; + v[1] = Q[1][0]; + } + else { + v[0] = 1; + v[1] = 0; + } + d = sq(v[0]) + sq(v[1]); + v[2] = -v[1] * s.y - v[0] * s.x; + for (l = 0; l < 3; l++) { + for (k = 0; k < 3; k++) { + Q[l][k] += v[l] * v[k] / d; + } + } + } + dx = fabs(w.x - s.x); + dy = fabs(w.y - s.y); + if (dx <= .5 && dy <= .5) { + pp->curve.vertex[i].x = w.x + x0; + pp->curve.vertex[i].y = w.y + y0; + continue; + } + + /* the minimum was not in the unit square; now minimize quadratic + on boundary of square */ + min = quadform(Q, s); + xmin = s.x; + ymin = s.y; + + if (Q[0][0] == 0.0) { + goto fixx; + } + for (z = 0; z < 2; z++) { /* value of the y-coordinate */ + w.y = s.y - 0.5 + z; + w.x = -(Q[0][1] * w.y + Q[0][2]) / Q[0][0]; + dx = fabs(w.x - s.x); + cand = quadform(Q, w); + if (dx <= .5 && cand < min) { + min = cand; + xmin = w.x; + ymin = w.y; + } + } + fixx: + if (Q[1][1] == 0.0) { + goto corners; + } + for (z = 0; z < 2; z++) { /* value of the x-coordinate */ + w.x = s.x - 0.5 + z; + w.y = -(Q[1][0] * w.x + Q[1][2]) / Q[1][1]; + dy = fabs(w.y - s.y); + cand = quadform(Q, w); + if (dy <= .5 && cand < min) { + min = cand; + xmin = w.x; + ymin = w.y; + } + } + corners: + /* check four corners */ + for (l = 0; l < 2; l++) { + for (k = 0; k < 2; k++) { + w.x = s.x - 0.5 + l; + w.y = s.y - 0.5 + k; + cand = quadform(Q, w); + if (cand < min) { + min = cand; + xmin = w.x; + ymin = w.y; + } + } + } + + pp->curve.vertex[i].x = xmin + x0; + pp->curve.vertex[i].y = ymin + y0; + continue; + } + + free(ctr); + free(dir); + free(q); + return 0; + +malloc_error: + free(ctr); + free(dir); + free(q); + return 1; +} + +/* ---------------------------------------------------------------------- */ +/* Stage 4: smoothing and corner analysis (Sec. 2.3.3) */ + +/* Always succeeds and returns 0 */ +static int +ATTRIBUTE_UNUSED smooth(privcurve_t * curve, int sign, double alphamax) +{ + int m = curve->n; + + int i, j, k; + double dd, denom, alpha; + dpoint_t p2, p3, p4; + + if (sign == '-') { + /* reverse orientation of negative paths */ + for (i = 0, j = m - 1; i < j; i++, j--) { + dpoint_t tmp; + tmp = curve->vertex[i]; + curve->vertex[i] = curve->vertex[j]; + curve->vertex[j] = tmp; + } + } + + /* examine each vertex and find its best fit */ + for (i = 0; i < m; i++) { + j = mod(i + 1, m); + k = mod(i + 2, m); + p4 = interval(1 / 2.0, curve->vertex[k], curve->vertex[j]); + + denom = ddenom(curve->vertex[i], curve->vertex[k]); + if (denom != 0.0) { + dd = dpara(curve->vertex[i], curve->vertex[j], curve->vertex[k]) / denom; + dd = fabs(dd); + alpha = dd > 1 ? (1 - 1.0 / dd) : 0; + alpha = alpha / 0.75; + } + else { + alpha = 4 / 3.0; + } + curve->alpha0[j] = alpha; /* remember "original" value of alpha */ + + if (alpha > alphamax) { /* pointed corner */ + curve->tag[j] = POTRACE_CORNER; + curve->c[j][1] = curve->vertex[j]; + curve->c[j][2] = p4; + } + else { + if (alpha < 0.55) { + alpha = 0.55; + } + else if (alpha > 1) { + alpha = 1; + } + p2 = interval(.5 + .5 * alpha, curve->vertex[i], curve->vertex[j]); + p3 = interval(.5 + .5 * alpha, curve->vertex[k], curve->vertex[j]); + curve->tag[j] = POTRACE_CURVETO; + curve->c[j][0] = p2; + curve->c[j][1] = p3; + curve->c[j][2] = p4; + } + curve->alpha[j] = alpha; /* store the "cropped" value of alpha */ + curve->beta[j] = 0.5; + } + curve->alphacurve = 1; + + return 0; +} + +/* ---------------------------------------------------------------------- */ +/* Stage 5: Curve optimization (Sec. 2.4) */ + +/* a private type for the result of opti_penalty */ +struct opti_s { + double pen; /* penalty */ + dpoint_t c[2]; /* curve parameters */ + double t, s; /* curve parameters */ + double alpha; /* curve parameter */ +}; +typedef struct opti_s opti_t; + +/* calculate best fit from i+.5 to j+.5. Assume icurve.n; + int k, k1, k2, conv, i1; + double area, alpha, d, d1, d2; + dpoint_t p0, p1, p2, p3, pt; + double A, R, A1, A2, A3, A4; + double s, t; + + /* check convexity, corner-freeness, and maximum bend < 179 degrees */ + + if (i == j) { /* sanity - a full loop can never be an opticurve */ + return 1; + } + + k = i; + i1 = mod(i + 1, m); + k1 = mod(k + 1, m); + conv = convc[k1]; + if (conv == 0) { + return 1; + } + d = ddist(pp->curve.vertex[i], pp->curve.vertex[i1]); + for (k = k1; k != j; k = k1) { + k1 = mod(k + 1, m); + k2 = mod(k + 2, m); + if (convc[k1] != conv) { + return 1; + } + if (sign(cprod(pp->curve.vertex[i], pp->curve.vertex[i1], pp->curve.vertex[k1], pp->curve.vertex[k2])) != conv) { + return 1; + } + if (iprod1 + (pp->curve.vertex[i], pp->curve.vertex[i1], pp->curve.vertex[k1], + pp->curve.vertex[k2]) < d * ddist(pp->curve.vertex[k1], pp->curve.vertex[k2]) * COS179) { + return 1; + } + } + + /* the curve we're working in: */ + p0 = pp->curve.c[mod(i, m)][2]; + p1 = pp->curve.vertex[mod(i + 1, m)]; + p2 = pp->curve.vertex[mod(j, m)]; + p3 = pp->curve.c[mod(j, m)][2]; + + /* determine its area */ + area = areac[j] - areac[i]; + area -= dpara(pp->curve.vertex[0], pp->curve.c[i][2], pp->curve.c[j][2]) / 2; + if (i >= j) { + area += areac[m]; + } + + /* find intersection o of p0p1 and p2p3. Let t,s such that o = + interval(t,p0,p1) = interval(s,p3,p2). Let A be the area of the + triangle (p0,o,p3). */ + + A1 = dpara(p0, p1, p2); + A2 = dpara(p0, p1, p3); + A3 = dpara(p0, p2, p3); + /* A4 = dpara(p1, p2, p3); */ + A4 = A1 + A3 - A2; + + if (A2 == A1) { /* this should never happen */ + return 1; + } + + t = A3 / (A3 - A4); + s = A2 / (A2 - A1); + A = A2 * t / 2.0; + + if (A == 0.0) { /* this should never happen */ + return 1; + } + + R = area / A; /* relative area */ + alpha = 2 - sqrt(4 - R / 0.3); /* overall alpha for p0-o-p3 curve */ + + res->c[0] = interval(t * alpha, p0, p1); + res->c[1] = interval(s * alpha, p3, p2); + res->alpha = alpha; + res->t = t; + res->s = s; + + p1 = res->c[0]; + p2 = res->c[1]; /* the proposed curve is now (p0,p1,p2,p3) */ + + res->pen = 0; + + /* calculate penalty */ + /* check tangency with edges */ + for (k = mod(i + 1, m); k != j; k = k1) { + k1 = mod(k + 1, m); + t = tangent(p0, p1, p2, p3, pp->curve.vertex[k], pp->curve.vertex[k1]); + if (t < -.5) { + return 1; + } + pt = bezier(t, p0, p1, p2, p3); + d = ddist(pp->curve.vertex[k], pp->curve.vertex[k1]); + if (d == 0.0) { /* this should never happen */ + return 1; + } + d1 = dpara(pp->curve.vertex[k], pp->curve.vertex[k1], pt) / d; + if (fabs(d1) > opttolerance) { + return 1; + } + if (iprod(pp->curve.vertex[k], pp->curve.vertex[k1], pt) < 0 || iprod(pp->curve.vertex[k1], pp->curve.vertex[k], pt) < 0) { + return 1; + } + res->pen += sq(d1); + } + + /* check corners */ + for (k = i; k != j; k = k1) { + k1 = mod(k + 1, m); + t = tangent(p0, p1, p2, p3, pp->curve.c[k][2], pp->curve.c[k1][2]); + if (t < -.5) { + return 1; + } + pt = bezier(t, p0, p1, p2, p3); + d = ddist(pp->curve.c[k][2], pp->curve.c[k1][2]); + if (d == 0.0) { /* this should never happen */ + return 1; + } + d1 = dpara(pp->curve.c[k][2], pp->curve.c[k1][2], pt) / d; + d2 = dpara(pp->curve.c[k][2], pp->curve.c[k1][2], pp->curve.vertex[k1]) / d; + d2 *= 0.75 * pp->curve.alpha[k1]; + if (d2 < 0) { + d1 = -d1; + d2 = -d2; + } + if (d1 < d2 - opttolerance) { + return 1; + } + if (d1 < d2) { + res->pen += sq(d1 - d2); + } + } + + return 0; +} + +/* optimize the path p, replacing sequences of Bezier segments by a + single segment when possible. Return 0 on success, 1 with errno set + on failure. */ +static int +ATTRIBUTE_UNUSED opticurve(privpath_t * pp, double opttolerance) +{ + int m = pp->curve.n; + int *pt = NULL; /* pt[m+1] */ + double *pen = NULL; /* pen[m+1] */ + int *len = NULL; /* len[m+1] */ + opti_t *opt = NULL; /* opt[m+1] */ + int om; + int i, j, r; + opti_t o; + dpoint_t p0; + int i1; + double area; + double alpha; + double *s = NULL; + double *t = NULL; + + int *convc = NULL; /* conv[m]: pre-computed convexities */ + double *areac = NULL; /* cumarea[m+1]: cache for fast area computation */ + + SAFE_MALLOC(pt, m + 1, int); + SAFE_MALLOC(pen, m + 1, double); + SAFE_MALLOC(len, m + 1, int); + SAFE_MALLOC(opt, m + 1, opti_t); + SAFE_MALLOC(convc, m, int); + SAFE_MALLOC(areac, m + 1, double); + + /* pre-calculate convexity: +1 = right turn, -1 = left turn, 0 = corner */ + for (i = 0; i < m; i++) { + if (pp->curve.tag[i] == POTRACE_CURVETO) { + convc[i] = sign(dpara(pp->curve.vertex[mod(i - 1, m)], pp->curve.vertex[i], pp->curve.vertex[mod(i + 1, m)])); + } + else { + convc[i] = 0; + } + } + + /* pre-calculate areas */ + area = 0.0; + areac[0] = 0.0; + p0 = pp->curve.vertex[0]; + for (i = 0; i < m; i++) { + i1 = mod(i + 1, m); + if (pp->curve.tag[i1] == POTRACE_CURVETO) { + alpha = pp->curve.alpha[i1]; + area += 0.3 * alpha * (4 - alpha) * dpara(pp->curve.c[i][2], pp->curve.vertex[i1], pp->curve.c[i1][2]) / 2; + area += dpara(p0, pp->curve.c[i][2], pp->curve.c[i1][2]) / 2; + } + areac[i + 1] = area; + } + + pt[0] = -1; + pen[0] = 0; + len[0] = 0; + + /* Fixme: we always start from a fixed point -- should find the best + curve cyclically ### */ + + for (j = 1; j <= m; j++) { + /* calculate best path from 0 to j */ + pt[j] = j - 1; + pen[j] = pen[j - 1]; + len[j] = len[j - 1] + 1; + + for (i = j - 2; i >= 0; i--) { + r = opti_penalty(pp, i, mod(j, m), &o, opttolerance, convc, areac); + if (r) { + break; + } + if (len[j] > len[i] + 1 || (len[j] == len[i] + 1 && pen[j] > pen[i] + o.pen)) { + pt[j] = i; + pen[j] = pen[i] + o.pen; + len[j] = len[i] + 1; + opt[j] = o; + } + } + } + om = len[m]; + r = privcurve_init(&pp->ocurve, om); + if (r) { + goto malloc_error; + } + SAFE_MALLOC(s, om, double); + SAFE_MALLOC(t, om, double); + + j = m; + for (i = om - 1; i >= 0; i--) { + if (pt[j] == j - 1) { + pp->ocurve.tag[i] = pp->curve.tag[mod(j, m)]; + pp->ocurve.c[i][0] = pp->curve.c[mod(j, m)][0]; + pp->ocurve.c[i][1] = pp->curve.c[mod(j, m)][1]; + pp->ocurve.c[i][2] = pp->curve.c[mod(j, m)][2]; + pp->ocurve.vertex[i] = pp->curve.vertex[mod(j, m)]; + pp->ocurve.alpha[i] = pp->curve.alpha[mod(j, m)]; + pp->ocurve.alpha0[i] = pp->curve.alpha0[mod(j, m)]; + pp->ocurve.beta[i] = pp->curve.beta[mod(j, m)]; + s[i] = t[i] = 1.0; + } + else { + pp->ocurve.tag[i] = POTRACE_CURVETO; + pp->ocurve.c[i][0] = opt[j].c[0]; + pp->ocurve.c[i][1] = opt[j].c[1]; + pp->ocurve.c[i][2] = pp->curve.c[mod(j, m)][2]; + pp->ocurve.vertex[i] = interval(opt[j].s, pp->curve.c[mod(j, m)][2], pp->curve.vertex[mod(j, m)]); + pp->ocurve.alpha[i] = opt[j].alpha; + pp->ocurve.alpha0[i] = opt[j].alpha; + s[i] = opt[j].s; + t[i] = opt[j].t; + } + j = pt[j]; + } + + /* calculate beta parameters */ + for (i = 0; i < om; i++) { + i1 = mod(i + 1, om); + pp->ocurve.beta[i] = s[i] / (s[i] + t[i1]); + } + pp->ocurve.alphacurve = 1; + + free(pt); + free(pen); + free(len); + free(opt); + free(s); + free(t); + free(convc); + free(areac); + return 0; + +malloc_error: + free(pt); + free(pen); + free(len); + free(opt); + free(s); + free(t); + free(convc); + free(areac); + return 1; +} + +/* ---------------------------------------------------------------------- */ +double plotpolygon(privpath_t * pp, FILE * f, double scale) +{ + int i; + int m = pp->m; + int *po = pp->po; + point_t *pt = pp->pt; + /* double scale=1.0/dpi; */ + double dm = 0; + + if (!m) + return 0; + + po = pp->po; + pt = pp->pt; + + fprintf(f, "G0 X%f Y%f (start point)\n", pt[po[0]].x * scale, pt[po[0]].y * scale); + fprintf(f, "G1 Z#101\n"); + for (i = 1; i < m; i++) { + fprintf(f, "G1 X%f Y%f\n", pt[po[i]].x * scale, pt[po[i]].y * scale); + dm += + sqrt((pt[po[i]].x - pt[po[i - 1]].x) * scale * (pt[po[i]].x - + pt[po[i - 1]].x) * + scale + (pt[po[i]].y - pt[po[i - 1]].y) * scale * (pt[po[i]].y - pt[po[i - 1]].y) * scale); + } + fprintf(f, "G1 X%f Y%f\n", pt[po[0]].x * scale, pt[po[0]].y * scale); + fprintf(f, "G0 Z#100\n"); + dm += + sqrt((pt[po[m - 1]].x - pt[po[0]].x) * scale * (pt[po[m - 1]].x - + pt[po[0]].x) * scale + + (pt[po[m - 1]].y - pt[po[0]].y) * scale * (pt[po[m - 1]].y - pt[po[0]].y) * scale); + fprintf(f, "(polygon end, distance %.2f)\n", dm); + return dm; +} + +#define TRY(x) if (x) goto try_error + +/* return distance on success, -1 on error with errno set. */ +double process_path(path_t * plist, const potrace_param_t * param, const potrace_bitmap_t * bm, FILE * f, double scale) +{ + path_t *p; + double dm = 0; + int n = 0; + /* call downstream function with each path */ + list_forall(p, plist) { + TRY(calc_sums(p->priv)); + TRY(calc_lon(p->priv)); + TRY(bestpolygon(p->priv)); + TRY(adjust_vertices(p->priv)); + fprintf(f, "(polygon %d)\n", ++n); + dm += plotpolygon(p->priv, f, scale); +/* No need to extract curves + TRY(smooth(&p->priv->curve, p->sign, param->alphamax)); + if (param->opticurve) { + TRY(opticurve(p->priv, param->opttolerance)); + p->priv->fcurve = &p->priv->ocurve; + } else { + p->priv->fcurve = &p->priv->curve; + } + privcurve_to_curve(p->priv->fcurve, &p->curve);*/ + } +/* fprintf(f,"(end, total distance %.2fmm = %.2fin)\n",25.4*dm,dm); */ + return dm; + +try_error: + return -1; +} Index: trunk/src_plugins/export_gcode/trace.h =================================================================== --- trunk/src_plugins/export_gcode/trace.h (nonexistent) +++ trunk/src_plugins/export_gcode/trace.h (revision 1269) @@ -0,0 +1,14 @@ +/* Copyright (C) 2001-2007 Peter Selinger. + This file is part of Potrace. It is free software and it is covered + by the GNU General Public License. See the file COPYING for details. */ + +/* $Id: trace.h 147 2007-04-09 00:44:09Z selinger $ */ + +#ifndef TRACE_H +#define TRACE_H + +#include "potracelib.h" + +double process_path(path_t * plist, const potrace_param_t * param, const potrace_bitmap_t * bm, FILE * f, double scale); + +#endif /* TRACE_H */ Index: trunk/src_plugins/export_lpr/Makefile =================================================================== --- trunk/src_plugins/export_lpr/Makefile (revision 1268) +++ trunk/src_plugins/export_lpr/Makefile (revision 1269) @@ -1,5 +1,5 @@ all: - cd ../../src && make mod_export_ps + cd ../../src && make mod_export_lpr clean: rm *.o *.so 2>/dev/null ; true Index: trunk/src_plugins/export_nelma/Makefile =================================================================== --- trunk/src_plugins/export_nelma/Makefile (nonexistent) +++ trunk/src_plugins/export_nelma/Makefile (revision 1269) @@ -0,0 +1,5 @@ +all: + cd ../../src && make mod_export_nelma + +clean: + rm *.o *.so 2>/dev/null ; true Index: trunk/src_plugins/export_nelma/Plug.tmpasm =================================================================== --- trunk/src_plugins/export_nelma/Plug.tmpasm (nonexistent) +++ trunk/src_plugins/export_nelma/Plug.tmpasm (revision 1269) @@ -0,0 +1,15 @@ +append /local/pcb/export_nelma/enable {} +append /local/pcb/export_nelma/buildin {} + +put /local/pcb/mod {export_nelma} +put /local/pcb/mod/OBJS [@ $(PLUGDIR)/export_nelma/nelma.o @] + +if /local/pcb/export_nelma/enable then + if /local/pcb/export_nelma/buildin then + include {Makefile.in.mod/Buildin} + else + include {Makefile.in.mod/Plugin} + end +else + include {Makefile.in.mod/Disable} +end Index: trunk/src_plugins/export_nelma/README =================================================================== --- trunk/src_plugins/export_nelma/README (nonexistent) +++ trunk/src_plugins/export_nelma/README (revision 1269) @@ -0,0 +1,4 @@ +Export to nelma (Numerical capacitance calculator) + +#state: works +#default: buildin Index: trunk/src_plugins/export_nelma/nelma.c =================================================================== --- trunk/src_plugins/export_nelma/nelma.c (nonexistent) +++ trunk/src_plugins/export_nelma/nelma.c (revision 1269) @@ -0,0 +1,1035 @@ +/* + * COPYRIGHT + * + * PCB, interactive printed circuit board design + * + * NELMA (Numerical capacitance calculator) export HID + * Copyright (C) 2006 Tomaz Solc (tomaz.solc@tablix.org) + * + * PNG export code is based on the PNG export HID + * Copyright (C) 2006 Dan McMahill + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* + * This HID exports a PCB layout into: o One layer mask file (PNG format) per + * copper layer. o Nelma configuration file that contains netlist and pin + * information. + */ + +/* + * FIXME: + * + * If you have a section of a net that does not contain any pins then that + * section will be missing from the Nelma's copper geometry. + * + * For example: + * + * this section will be ignored by Nelma | | + * + * || ||=======|| || component layer || + * || || || ||=============|| ||============|| + * solder layer + * + * pin1 via via pin2 + * + * Single layer layouts are always exported correctly. + * + */ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include + +#include "global.h" +#include "error.h" /* Message() */ +#include "data.h" +#include "misc.h" +#include "rats.h" +#include "plugins.h" + +#include "hid.h" +#include "../hidint.h" +#include "hid/common/hidnogui.h" +#include "hid/common/draw_helpers.h" + +#include + +#include "hid/common/hidinit.h" + + +RCSID("$Id$"); + +const char *nelma_cookie = "nelma HID"; + +#define CRASH fprintf(stderr, "HID error: pcb called unimplemented PNG function %s.\n", __FUNCTION__); abort() + +/* Needed for PNG export */ + +struct color_struct { + /* the descriptor used by the gd library */ + int c; + + /* so I can figure out what rgb value c refers to */ + unsigned int r, g, b; +}; + +struct hid_gc_struct { + HID *me_pointer; + EndCapStyle cap; + Coord width; + unsigned char r, g, b; + int erase; + int faded; + struct color_struct *color; + gdImagePtr brush; +}; + +static HID nelma_hid; + +static struct color_struct *black = NULL, *white = NULL; +static Coord linewidth = -1; +static gdImagePtr lastbrush = (gdImagePtr) ((void *) -1); +static int lastcolor = -1; + +/* gd image and file for PNG export */ +static gdImagePtr nelma_im = NULL; +static FILE *nelma_f = NULL; + +static int is_mask; +static int is_drill; + +/* + * Which groups of layers to export into PNG layer masks. 1 means export, 0 + * means do not export. + */ +static int nelma_export_group[MAX_LAYER]; + +/* Group that is currently exported. */ +static int nelma_cur_group; + +/* Filename prefix that will be used when saving files. */ +static const char *nelma_basename = NULL; + +/* Horizontal DPI (grid points per inch) */ +static int nelma_dpi = -1; + +/* Height of the copper layers in micrometers. */ + +/* + * The height of the copper layer is currently taken as the vertical grid + * step, since this is the smallest vertical feature in the layout. + */ +static int nelma_copperh = -1; +/* Height of the substrate layers in micrometers. */ +static int nelma_substrateh = -1; +/* Relative permittivity of the substrate. */ +static double nelma_substratee = -1; + +/* Permittivity of empty space (As/Vm) */ +static const double nelma_air_epsilon = 8.85e-12; + +HID_Attribute nelma_attribute_list[] = { + /* other HIDs expect this to be first. */ + +/* %start-doc options "nelma Options" +@ftable @code +@item -- basename +File name prefix. +@end ftable +%end-doc +*/ + {"basename", "File name prefix", + HID_String, 0, 0, {0, 0, 0}, 0, 0}, +#define HA_basename 0 + +/* %start-doc options "nelma Options" +@ftable @code +@item --dpi +Horizontal scale factor (grid points/inch). +@end ftable +%end-doc +*/ + {"dpi", "Horizontal scale factor (grid points/inch)", + HID_Integer, 0, 1000, {100, 0, 0}, 0, 0}, +#define HA_dpi 1 + +/* %start-doc options "nelma Options" +@ftable @code +@item --copper-height +Copper layer height (um). +@end ftable +%end-doc +*/ + {"copper-height", "Copper layer height (um)", + HID_Integer, 0, 200, {100, 0, 0}, 0, 0}, +#define HA_copperh 2 + +/* %start-doc options "nelma Options" +@ftable @code +@item --substrate-height +Substrate layer height (um). +@end ftable +%end-doc +*/ + {"substrate-height", "Substrate layer height (um)", + HID_Integer, 0, 10000, {2000, 0, 0}, 0, 0}, +#define HA_substrateh 3 + +/* %start-doc options "nelma Options" +@ftable @code +@item --substrate-epsilon +Substrate relative epsilon. +@end ftable +%end-doc +*/ + {"substrate-epsilon", "Substrate relative epsilon", + HID_Real, 0, 100, {0, 0, 4.0}, 0, 0}, +#define HA_substratee 4 +}; + +#define NUM_OPTIONS (sizeof(nelma_attribute_list)/sizeof(nelma_attribute_list[0])) + +REGISTER_ATTRIBUTES(nelma_attribute_list, nelma_cookie) + static HID_Attr_Val nelma_values[NUM_OPTIONS]; + +/* *** Utility funcions **************************************************** */ + +/* convert from default PCB units to nelma units */ + static int pcb_to_nelma(Coord pcb) +{ + return COORD_TO_INCH(pcb) * nelma_dpi; +} + +static char *nelma_get_png_name(const char *basename, const char *suffix) +{ + char *buf; + int len; + + len = strlen(basename) + strlen(suffix) + 6; + buf = (char *) malloc(sizeof(*buf) * len); + + sprintf(buf, "%s.%s.png", basename, suffix); + + return buf; +} + +/* Retrieves coordinates (in default PCB units) of a pin or pad. */ +/* Copied from netlist.c */ +static int pin_name_to_xy(LibraryEntryType * pin, Coord * x, Coord * y) +{ + ConnectionType conn; + if (!SeekPad(pin, &conn, false)) + return 1; + switch (conn.type) { + case PIN_TYPE: + *x = ((PinType *) (conn.ptr2))->X; + *y = ((PinType *) (conn.ptr2))->Y; + return 0; + case PAD_TYPE: + *x = ((PadType *) (conn.ptr2))->Point1.X; + *y = ((PadType *) (conn.ptr2))->Point1.Y; + return 0; + } + return 1; +} + +/* *** Exporting netlist data and geometry to the nelma config file ******** */ + +static void nelma_write_space(FILE * out) +{ + double xh, zh; + + int z; + int i, idx; + const char *ext; + + xh = 2.54e-2 / ((double) nelma_dpi); + zh = nelma_copperh * 1e-6; + + fprintf(out, "\n/* **** Space **** */\n\n"); + + fprintf(out, "space pcb {\n"); + fprintf(out, "\tstep = { %e, %e, %e }\n", xh, xh, zh); + fprintf(out, "\tlayers = {\n"); + + fprintf(out, "\t\t\"air-top\",\n"); + fprintf(out, "\t\t\"air-bottom\""); + + z = 10; + for (i = 0; i < MAX_LAYER; i++) + if (nelma_export_group[i]) { + idx = (i >= 0 && i < max_group) ? PCB->LayerGroups.Entries[i][0] : i; + ext = layer_type_to_file_name(idx, FNS_fixed); + + if (z != 10) { + fprintf(out, ",\n"); + fprintf(out, "\t\t\"substrate-%d\"", z); + z++; + } + fprintf(out, ",\n"); + fprintf(out, "\t\t\"%s\"", ext); + z++; + } + fprintf(out, "\n\t}\n"); + fprintf(out, "}\n"); +} + + +static void nelma_write_material(FILE * out, char *name, char *type, double e) +{ + fprintf(out, "material %s {\n", name); + fprintf(out, "\ttype = \"%s\"\n", type); + fprintf(out, "\tpermittivity = %e\n", e); + fprintf(out, "\tconductivity = 0.0\n"); + fprintf(out, "\tpermeability = 0.0\n"); + fprintf(out, "}\n"); +} + +static void nelma_write_materials(FILE * out) +{ + fprintf(out, "\n/* **** Materials **** */\n\n"); + + nelma_write_material(out, "copper", "metal", nelma_air_epsilon); + nelma_write_material(out, "air", "dielectric", nelma_air_epsilon); + nelma_write_material(out, "composite", "dielectric", nelma_air_epsilon * nelma_substratee); +} + +static void nelma_write_nets(FILE * out) +{ + LibraryType netlist; + LibraryMenuTypePtr net; + LibraryEntryTypePtr pin; + + int n, m, i, idx; + + const char *ext; + + netlist = PCB->NetlistLib[NETLIST_EDITED]; + + fprintf(out, "\n/* **** Nets **** */\n\n"); + + for (n = 0; n < netlist.MenuN; n++) { + net = &netlist.Menu[n]; + + /* Weird, but correct */ + fprintf(out, "net %s {\n", &net->Name[2]); + + fprintf(out, "\tobjects = {\n"); + + for (m = 0; m < net->EntryN; m++) { + pin = &net->Entry[m]; + + /* pin_name_to_xy(pin, &x, &y); */ + + for (i = 0; i < MAX_LAYER; i++) + if (nelma_export_group[i]) { + idx = (i >= 0 && i < max_group) ? PCB->LayerGroups.Entries[i][0] : i; + ext = layer_type_to_file_name(idx, FNS_fixed); + + if (m != 0 || i != 0) + fprintf(out, ",\n"); + fprintf(out, "\t\t\"%s-%s\"", pin->ListEntry, ext); + } + } + + fprintf(out, "\n"); + fprintf(out, "\t}\n"); + fprintf(out, "}\n"); + } +} + +static void nelma_write_layer(FILE * out, int z, int h, const char *name, int full, char *mat) +{ + LibraryType netlist; + LibraryMenuTypePtr net; + LibraryEntryTypePtr pin; + + int n, m; + + fprintf(out, "layer %s {\n", name); + fprintf(out, "\theight = %d\n", h); + fprintf(out, "\tz-order = %d\n", z); + fprintf(out, "\tmaterial = \"%s\"\n", mat); + + if (full) { + fprintf(out, "\tobjects = {\n"); + netlist = PCB->NetlistLib[NETLIST_EDITED]; + + for (n = 0; n < netlist.MenuN; n++) { + net = &netlist.Menu[n]; + + for (m = 0; m < net->EntryN; m++) { + pin = &net->Entry[m]; + + if (m != 0 || n != 0) + fprintf(out, ",\n"); + fprintf(out, "\t\t\"%s-%s\"", pin->ListEntry, name); + } + + } + fprintf(out, "\n\t}\n"); + } + fprintf(out, "}\n"); +} + +static void nelma_write_layers(FILE * out) +{ + int i, idx; + int z; + + const char *ext; + char buf[100]; + + int subh; + + subh = nelma_substrateh / nelma_copperh; + + fprintf(out, "\n/* **** Layers **** */\n\n"); + + /* Air layers on top and bottom of the stack */ + /* Their height is double substrate height. */ + nelma_write_layer(out, 1, 2 * subh, "air-top", 0, "air"); + nelma_write_layer(out, 1000, 2 * subh, "air-bottom", 0, "air"); + + z = 10; + for (i = 0; i < MAX_LAYER; i++) + if (nelma_export_group[i]) { + idx = (i >= 0 && i < max_group) ? PCB->LayerGroups.Entries[i][0] : i; + ext = layer_type_to_file_name(idx, FNS_fixed); + + if (z != 10) { + sprintf(buf, "substrate-%d", z); + nelma_write_layer(out, z, subh, buf, 0, "composite"); + z++; + } + /* + * FIXME: for layers that are not on top or bottom, + * the material should be "composite" + */ + nelma_write_layer(out, z, 1, ext, 1, "air"); + + z++; + } +} + +static void nelma_write_object(FILE * out, LibraryEntryTypePtr pin) +{ + int i, idx; + Coord px = 0, py = 0; + int x, y; + + char *f; + const char *ext; + + pin_name_to_xy(pin, &px, &py); + + x = pcb_to_nelma(px); + y = pcb_to_nelma(py); + + for (i = 0; i < MAX_LAYER; i++) + if (nelma_export_group[i]) { + idx = (i >= 0 && i < max_group) ? PCB->LayerGroups.Entries[i][0] : i; + ext = layer_type_to_file_name(idx, FNS_fixed); + + fprintf(out, "object %s-%s {\n", pin->ListEntry, ext); + fprintf(out, "\tposition = { 0, 0 }\n"); + fprintf(out, "\tmaterial = \"copper\"\n"); + fprintf(out, "\ttype = \"image\"\n"); + fprintf(out, "\trole = \"net\"\n"); + + f = nelma_get_png_name(nelma_basename, ext); + + fprintf(out, "\tfile = \"%s\"\n", f); + + free(f); + + fprintf(out, "\tfile-pos = { %d, %d }\n", x, y); + fprintf(out, "}\n"); + } +} + +static void nelma_write_objects(FILE * out) +{ + LibraryType netlist; + LibraryMenuTypePtr net; + LibraryEntryTypePtr pin; + + int n, m; + + netlist = PCB->NetlistLib[NETLIST_EDITED]; + + fprintf(out, "\n/* **** Objects **** */\n\n"); + + for (n = 0; n < netlist.MenuN; n++) { + net = &netlist.Menu[n]; + + for (m = 0; m < net->EntryN; m++) { + pin = &net->Entry[m]; + + nelma_write_object(out, pin); + } + } +} + +/* *** Main export callback ************************************************ */ + +static void nelma_parse_arguments(int *argc, char ***argv) +{ + hid_register_attributes(nelma_attribute_list, sizeof(nelma_attribute_list) / sizeof(nelma_attribute_list[0]), nelma_cookie); + hid_parse_command_line(argc, argv); +} + +static HID_Attribute *nelma_get_export_options(int *n) +{ + static char *last_made_filename = 0; + + if (PCB) { + derive_default_filename(PCB->Filename, &nelma_attribute_list[HA_basename], ".nelma", &last_made_filename); + } + if (n) { + *n = NUM_OPTIONS; + } + return nelma_attribute_list; +} + +/* Populates nelma_export_group array */ +void nelma_choose_groups() +{ + int n, m; + LayerType *layer; + + /* Set entire array to 0 (don't export any layer groups by default */ + memset(nelma_export_group, 0, sizeof(nelma_export_group)); + + for (n = 0; n < max_copper_layer; n++) { + layer = &PCB->Data->Layer[n]; + + if (!LAYER_IS_EMPTY(layer)) { + /* layer isn't empty */ + + /* + * is this check necessary? It seems that special + * layers have negative indexes? + */ + + if (SL_TYPE(n) == 0) { + /* layer is a copper layer */ + m = GetLayerGroupNumberByNumber(n); + + /* the export layer */ + nelma_export_group[m] = 1; + } + } + } +} + +static void nelma_alloc_colors() +{ + /* + * Allocate white and black -- the first color allocated becomes the + * background color + */ + + white = (struct color_struct *) malloc(sizeof(*white)); + white->r = white->g = white->b = 255; + white->c = gdImageColorAllocate(nelma_im, white->r, white->g, white->b); + + black = (struct color_struct *) malloc(sizeof(*black)); + black->r = black->g = black->b = 0; + black->c = gdImageColorAllocate(nelma_im, black->r, black->g, black->b); +} + +static void nelma_start_png(const char *basename, const char *suffix) +{ + int h, w; + char *buf; + + buf = nelma_get_png_name(basename, suffix); + + h = pcb_to_nelma(PCB->MaxHeight); + w = pcb_to_nelma(PCB->MaxWidth); + + /* nelma_im = gdImageCreate (w, h); */ + + /* Nelma only works with true color images */ + nelma_im = gdImageCreate(w, h); + nelma_f = fopen(buf, "wb"); + + nelma_alloc_colors(); + + free(buf); +} + +static void nelma_finish_png() +{ +#ifdef HAVE_GDIMAGEPNG + gdImagePng(nelma_im, nelma_f); +#else + Message("NELMA: PNG not supported by gd. Can't write layer mask.\n"); +#endif + gdImageDestroy(nelma_im); + fclose(nelma_f); + + free(white); + free(black); + + nelma_im = NULL; + nelma_f = NULL; +} + +void nelma_start_png_export() +{ + BoxType region; + + region.X1 = 0; + region.Y1 = 0; + region.X2 = PCB->MaxWidth; + region.Y2 = PCB->MaxHeight; + + linewidth = -1; + lastbrush = (gdImagePtr) ((void *) -1); + lastcolor = -1; + + hid_expose_callback(&nelma_hid, ®ion, 0); +} + +static void nelma_do_export(HID_Attr_Val * options) +{ + int save_ons[MAX_LAYER + 2]; + int i, idx; + FILE *nelma_config; + char *buf; + int len; + + time_t t; + + if (!options) { + nelma_get_export_options(0); + for (i = 0; i < NUM_OPTIONS; i++) { + nelma_values[i] = nelma_attribute_list[i].default_val; + } + options = nelma_values; + } + nelma_basename = options[HA_basename].str_value; + if (!nelma_basename) { + nelma_basename = "pcb-out"; + } + nelma_dpi = options[HA_dpi].int_value; + if (nelma_dpi < 0) { + fprintf(stderr, "ERROR: dpi may not be < 0\n"); + return; + } + nelma_copperh = options[HA_copperh].int_value; + nelma_substrateh = options[HA_substrateh].int_value; + nelma_substratee = options[HA_substratee].real_value; + + nelma_choose_groups(); + + for (i = 0; i < MAX_LAYER; i++) { + if (nelma_export_group[i]) { + + nelma_cur_group = i; + + /* magic */ + idx = (i >= 0 && i < max_group) ? PCB->LayerGroups.Entries[i][0] : i; + + nelma_start_png(nelma_basename, layer_type_to_file_name(idx, FNS_fixed)); + + hid_save_and_show_layer_ons(save_ons); + nelma_start_png_export(); + hid_restore_layer_ons(save_ons); + + nelma_finish_png(); + } + } + + len = strlen(nelma_basename) + 4; + buf = (char *) malloc(sizeof(*buf) * len); + + sprintf(buf, "%s.em", nelma_basename); + nelma_config = fopen(buf, "w"); + + free(buf); + + fprintf(nelma_config, "/* Made with PCB Nelma export HID */"); + t = time(NULL); + fprintf(nelma_config, "/* %s */", ctime(&t)); + + nelma_write_nets(nelma_config); + nelma_write_objects(nelma_config); + nelma_write_layers(nelma_config); + nelma_write_materials(nelma_config); + nelma_write_space(nelma_config); + + fclose(nelma_config); +} + +/* *** PNG export (slightly modified code from PNG export HID) ************* */ + +static int nelma_set_layer(const char *name, int group, int empty) +{ + int idx = (group >= 0 && group < max_group) ? PCB->LayerGroups.Entries[group][0] : group; + + if (name == 0) { + name = PCB->Data->Layer[idx].Name; + } + if (strcmp(name, "invisible") == 0) { + return 0; + } + is_drill = (SL_TYPE(idx) == SL_PDRILL || SL_TYPE(idx) == SL_UDRILL); + is_mask = (SL_TYPE(idx) == SL_MASK); + + if (is_mask) { + /* Don't print masks */ + return 0; + } + if (is_drill) { + /* + * Print 'holes', so that we can fill gaps in the copper + * layer + */ + return 1; + } + if (group == nelma_cur_group) { + return 1; + } + return 0; +} + +static hidGC nelma_make_gc(void) +{ + hidGC rv = (hidGC) malloc(sizeof(struct hid_gc_struct)); + rv->me_pointer = &nelma_hid; + rv->cap = Trace_Cap; + rv->width = 1; + rv->color = (struct color_struct *) malloc(sizeof(*rv->color)); + rv->color->r = rv->color->g = rv->color->b = 0; + rv->color->c = 0; + return rv; +} + +static void nelma_destroy_gc(hidGC gc) +{ + free(gc); +} + +static void nelma_use_mask(int use_it) +{ + /* does nothing */ +} + +static void nelma_set_color(hidGC gc, const char *name) +{ + if (nelma_im == NULL) { + return; + } + if (name == NULL) { + name = "#ff0000"; + } + if (!strcmp(name, "drill")) { + gc->color = black; + gc->erase = 0; + return; + } + if (!strcmp(name, "erase")) { + /* FIXME -- should be background, not white */ + gc->color = white; + gc->erase = 1; + return; + } + gc->color = black; + gc->erase = 0; + return; +} + +static void nelma_set_line_cap(hidGC gc, EndCapStyle style) +{ + gc->cap = style; +} + +static void nelma_set_line_width(hidGC gc, Coord width) +{ + gc->width = width; +} + +static void nelma_set_draw_xor(hidGC gc, int xor_) +{ + ; +} + +static void nelma_set_draw_faded(hidGC gc, int faded) +{ + gc->faded = faded; +} + +static void use_gc(hidGC gc) +{ + int need_brush = 0; + + if (gc->me_pointer != &nelma_hid) { + fprintf(stderr, "Fatal: GC from another HID passed to nelma HID\n"); + abort(); + } + if (linewidth != gc->width) { + /* Make sure the scaling doesn't erase lines completely */ + /* + if (SCALE (gc->width) == 0 && gc->width > 0) + gdImageSetThickness (im, 1); + else + */ + gdImageSetThickness(nelma_im, pcb_to_nelma(gc->width)); + linewidth = gc->width; + need_brush = 1; + } + if (lastbrush != gc->brush || need_brush) { + static void *bcache = 0; + hidval bval; + char name[256]; + char type; + int r; + + switch (gc->cap) { + case Round_Cap: + case Trace_Cap: + type = 'C'; + r = pcb_to_nelma(gc->width / 2); + break; + default: + case Square_Cap: + r = pcb_to_nelma(gc->width); + type = 'S'; + break; + } + sprintf(name, "#%.2x%.2x%.2x_%c_%d", gc->color->r, gc->color->g, gc->color->b, type, r); + + if (hid_cache_color(0, name, &bval, &bcache)) { + gc->brush = (gdImagePtr) bval.ptr; + } + else { + int bg, fg; + if (type == 'C') + gc->brush = gdImageCreate(2 * r + 1, 2 * r + 1); + else + gc->brush = gdImageCreate(r + 1, r + 1); + bg = gdImageColorAllocate(gc->brush, 255, 255, 255); + fg = gdImageColorAllocate(gc->brush, gc->color->r, gc->color->g, gc->color->b); + gdImageColorTransparent(gc->brush, bg); + + /* + * if we shrunk to a radius/box width of zero, then just use + * a single pixel to draw with. + */ + if (r == 0) + gdImageFilledRectangle(gc->brush, 0, 0, 0, 0, fg); + else { + if (type == 'C') + gdImageFilledEllipse(gc->brush, r, r, 2 * r, 2 * r, fg); + else + gdImageFilledRectangle(gc->brush, 0, 0, r, r, fg); + } + bval.ptr = gc->brush; + hid_cache_color(1, name, &bval, &bcache); + } + + gdImageSetBrush(nelma_im, gc->brush); + lastbrush = gc->brush; + + } +#define CBLEND(gc) (((gc->r)<<24)|((gc->g)<<16)|((gc->b)<<8)|(gc->faded)) + if (lastcolor != CBLEND(gc)) { + if (is_drill || is_mask) { +#ifdef FIXME + fprintf(f, "%d gray\n", gc->erase ? 0 : 1); +#endif + lastcolor = 0; + } + else { + double r, g, b; + r = gc->r; + g = gc->g; + b = gc->b; + if (gc->faded) { + r = 0.8 * 255 + 0.2 * r; + g = 0.8 * 255 + 0.2 * g; + b = 0.8 * 255 + 0.2 * b; + } +#ifdef FIXME + if (gc->r == gc->g && gc->g == gc->b) + fprintf(f, "%g gray\n", r / 255.0); + else + fprintf(f, "%g %g %g rgb\n", r / 255.0, g / 255.0, b / 255.0); +#endif + lastcolor = CBLEND(gc); + } + } +} + +static void nelma_draw_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2) +{ + use_gc(gc); + gdImageRectangle(nelma_im, pcb_to_nelma(x1), pcb_to_nelma(y1), pcb_to_nelma(x2), pcb_to_nelma(y2), gc->color->c); +} + +static void nelma_fill_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2) +{ + use_gc(gc); + gdImageSetThickness(nelma_im, 0); + linewidth = 0; + gdImageFilledRectangle(nelma_im, pcb_to_nelma(x1), pcb_to_nelma(y1), pcb_to_nelma(x2), pcb_to_nelma(y2), gc->color->c); +} + +static void nelma_draw_line(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2) +{ + if (x1 == x2 && y1 == y2) { + Coord w = gc->width / 2; + nelma_fill_rect(gc, x1 - w, y1 - w, x1 + w, y1 + w); + return; + } + use_gc(gc); + + gdImageSetThickness(nelma_im, 0); + linewidth = 0; + gdImageLine(nelma_im, pcb_to_nelma(x1), pcb_to_nelma(y1), pcb_to_nelma(x2), pcb_to_nelma(y2), gdBrushed); +} + +static void nelma_draw_arc(hidGC gc, Coord cx, Coord cy, Coord width, Coord height, Angle start_angle, Angle delta_angle) +{ + Angle sa, ea; + + /* + * in gdImageArc, 0 degrees is to the right and +90 degrees is down + * in pcb, 0 degrees is to the left and +90 degrees is down + */ + start_angle = 180 - start_angle; + delta_angle = -delta_angle; + if (delta_angle > 0) { + sa = start_angle; + ea = start_angle + delta_angle; + } + else { + sa = start_angle + delta_angle; + ea = start_angle; + } + + /* + * make sure we start between 0 and 360 otherwise gd does strange + * things + */ + sa = NormalizeAngle(sa); + ea = NormalizeAngle(ea); + +#if 0 + printf("draw_arc %d,%d %dx%d %d..%d %d..%d\n", cx, cy, width, height, start_angle, delta_angle, sa, ea); + printf("gdImageArc (%p, %d, %d, %d, %d, %d, %d, %d)\n", + im, SCALE_X(cx), SCALE_Y(cy), SCALE(width), SCALE(height), sa, ea, gc->color->c); +#endif + use_gc(gc); + gdImageSetThickness(nelma_im, 0); + linewidth = 0; + gdImageArc(nelma_im, pcb_to_nelma(cx), pcb_to_nelma(cy), + pcb_to_nelma(2 * width), pcb_to_nelma(2 * height), sa, ea, gdBrushed); +} + +static void nelma_fill_circle(hidGC gc, Coord cx, Coord cy, Coord radius) +{ + use_gc(gc); + + gdImageSetThickness(nelma_im, 0); + linewidth = 0; + gdImageFilledEllipse(nelma_im, pcb_to_nelma(cx), pcb_to_nelma(cy), + pcb_to_nelma(2 * radius), pcb_to_nelma(2 * radius), gc->color->c); + +} + +static void nelma_fill_polygon(hidGC gc, int n_coords, Coord * x, Coord * y) +{ + int i; + gdPoint *points; + + points = (gdPoint *) malloc(n_coords * sizeof(gdPoint)); + if (points == NULL) { + fprintf(stderr, "ERROR: nelma_fill_polygon(): malloc failed\n"); + exit(1); + } + use_gc(gc); + for (i = 0; i < n_coords; i++) { + points[i].x = pcb_to_nelma(x[i]); + points[i].y = pcb_to_nelma(y[i]); + } + gdImageSetThickness(nelma_im, 0); + linewidth = 0; + gdImageFilledPolygon(nelma_im, points, n_coords, gc->color->c); + free(points); +} + +static void nelma_calibrate(double xval, double yval) +{ + CRASH; +} + +static void nelma_set_crosshair(int x, int y, int a) +{ +} + +/* *** Miscellaneous ******************************************************* */ + +#include "dolists.h" + +pcb_uninit_t hid_export_nelma_init() +{ + memset(&nelma_hid, 0, sizeof(HID)); + + common_nogui_init(&nelma_hid); + common_draw_helpers_init(&nelma_hid); + + nelma_hid.struct_size = sizeof(HID); + nelma_hid.name = "nelma"; + nelma_hid.description = "Numerical analysis package export"; + nelma_hid.exporter = 1; + nelma_hid.poly_before = 1; + + nelma_hid.get_export_options = nelma_get_export_options; + nelma_hid.do_export = nelma_do_export; + nelma_hid.parse_arguments = nelma_parse_arguments; + nelma_hid.set_layer = nelma_set_layer; + nelma_hid.make_gc = nelma_make_gc; + nelma_hid.destroy_gc = nelma_destroy_gc; + nelma_hid.use_mask = nelma_use_mask; + nelma_hid.set_color = nelma_set_color; + nelma_hid.set_line_cap = nelma_set_line_cap; + nelma_hid.set_line_width = nelma_set_line_width; + nelma_hid.set_draw_xor = nelma_set_draw_xor; + nelma_hid.set_draw_faded = nelma_set_draw_faded; + nelma_hid.draw_line = nelma_draw_line; + nelma_hid.draw_arc = nelma_draw_arc; + nelma_hid.draw_rect = nelma_draw_rect; + nelma_hid.fill_circle = nelma_fill_circle; + nelma_hid.fill_polygon = nelma_fill_polygon; + nelma_hid.fill_rect = nelma_fill_rect; + nelma_hid.calibrate = nelma_calibrate; + nelma_hid.set_crosshair = nelma_set_crosshair; + + hid_register_hid(&nelma_hid); + return NULL; +} Index: trunk/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/actions/actions.c =================================================================== --- trunk/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/actions/actions.c (revision 1268) +++ trunk/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_pkg/actions/actions.c (revision 1269) @@ -81,7 +81,7 @@ ctx->module = gpmi_get_current_module(); ctx->next = NULL; - hid_register_action(&ctx->action, &gpmi_cookie); + hid_register_action(&ctx->action, gpmi_cookie); gpmi_mod_cleanup_insert(ctx->module, cleanup_action, "p", ctx); Index: trunk/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_plugin.c =================================================================== --- trunk/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_plugin.c (revision 1268) +++ trunk/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_plugin.c (revision 1269) @@ -13,7 +13,7 @@ extern char *homedir; /* detected by pcn-rnd in InitPaths() */ -char *gpmi_cookie = "GPMI plugin cookie"; +const char *gpmi_cookie = "GPMI plugin cookie"; /* This function is used to print a detailed GPMI error message */ void gpmi_hid_print_error(gpmi_err_stack_t *entry, char *string) @@ -118,7 +118,7 @@ act.description = "Manage gpmi scripts"; act.syntax = "TODO"; act.trigger_cb = action_gpmi_scripts; - hid_register_action(&act, &gpmi_cookie); + hid_register_action(&act, gpmi_cookie); act.name = "rehash"; act.need_coord_msg = NULL; @@ -125,7 +125,7 @@ act.description = "Reload all gpmi scripts"; act.syntax = "TODO"; act.trigger_cb = action_gpmi_rehash; - hid_register_action(&act, &gpmi_cookie); + hid_register_action(&act, gpmi_cookie); } #ifndef PLUGIN_INIT_NAME @@ -181,7 +181,7 @@ *gpmi_asm_scriptname = gpmi_hid_asm_scriptname; register_actions(); - event_bind(EVENT_GUI_INIT, ev_gui_init, NULL, &gpmi_cookie); + event_bind(EVENT_GUI_INIT, ev_gui_init, NULL, gpmi_cookie); hid_gpmi_load_dir(libdirh, 0); hid_gpmi_load_dir(libdirg, 0); @@ -210,8 +210,8 @@ static void plugin_gpmi_uninit(void) { - event_unbind_allcookie(&gpmi_cookie); - hid_remove_actions_by_cookie(&gpmi_cookie); + event_unbind_allcookie(gpmi_cookie); + hid_remove_actions_by_cookie(gpmi_cookie); hid_gpmi_script_info_uninit(); gpmi_pkg_unload(pkg_scripts); gpmi_uninit(); Index: trunk/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_plugin.h =================================================================== --- trunk/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_plugin.h (revision 1268) +++ trunk/src_plugins/gpmi/pcb-gpmi/gpmi_plugin/gpmi_plugin.h (revision 1269) @@ -1,4 +1,4 @@ -extern char *gpmi_cookie; +extern const char *gpmi_cookie; extern int gpmi_hid_gui_inited; /* whether the gui is already initialzied */ void gpmi_hid_print_error(gpmi_err_stack_t *entry, char *string);