diff options
Diffstat (limited to 'src/gerber-output-dev.cc')
-rw-r--r-- | src/gerber-output-dev.cc | 285 |
1 files changed, 285 insertions, 0 deletions
diff --git a/src/gerber-output-dev.cc b/src/gerber-output-dev.cc new file mode 100644 index 0000000..f4b4aab --- /dev/null +++ b/src/gerber-output-dev.cc @@ -0,0 +1,285 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 2; tab-width: 2 -*- */ +/* + * gerber-output-dev.cc + * Copyright (C) 2018 David Barksdale <amatus@amat.us> + * + * pdf2gerber 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 3 of the License, or + * (at your option) any later version. + * + * pdf2gerber 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, see <http://www.gnu.org/licenses/>. + */ + +#include <cmath> +#include <iostream> +#include <GfxState.h> +#include "gerber-output-dev.h" + +static double midpoint(double a, double b, double c, double d) +{ + return (a + 3 * (b + c) + d) / 8; +} + +static void center(double x0, double y0, + double x1, double y1, + double x2, double y2, + double *x, double *y) +{ + double idet = 1 / ((x0 - x1) * (y1 - y2) - (x1 - x2) * (y0 - y1)); + double offset = x1 * x1 + y1 * y1; + double m01 = (x0 * x0 + y0 * y0 - offset) / 2; + double m12 = (offset - x2 * x2 - y2 * y2) / 2; + + *x = (m01 * (y1 - y2) - m12 * (y0 - y1)) * idet; + *y = (m12 * (x0 - x1) - m01 * (x1 - x2)) * idet; +} + +static bool clockwise(double x0, double y0, + double x1, double y1, + double x2, double y2) +{ + return (x1 - x0) * (y2 - y0) < (x2 - x0) * (y1 - y0); +} + +// Constructor. +GerberOutputDev::GerberOutputDev() +{ + // Set format to 6.6 + std::cout << "%FSLAX66Y66*%" << std::endl + // Set units to mm + << "%MOMM*%" << std::endl + // Set multi-quadrant, why would you ever not want this? + << "G75*" << std::endl; +} + +// Does this device use upside-down coordinates? +// (Upside-down means (0,0) is the top left corner of the page.) +GBool GerberOutputDev::upsideDown() +{ + return gFalse; +} + +// Does this device use drawChar() or drawString()? +GBool GerberOutputDev::useDrawChar() +{ + return gTrue; +} + +// Does this device use beginType3Char/endType3Char? Otherwise, +// text in Type 3 fonts will be drawn with drawChar/drawString. +GBool GerberOutputDev::interpretType3Chars() +{ + return gFalse; +} + +// Start a page. +void GerberOutputDev::startPage(int pageNum, GfxState *state, XRef *xref) +{ + std::cout << "G04 startPage " + << pageNum << "*" << std::endl; +} + +// End a page. +void GerberOutputDev::endPage() +{ + std::cout << "G04 endPage*" << std::endl; + std::cout << "M02*" << std::endl; +} + +// Dump page contents to display. +void GerberOutputDev::dump() +{ + //std::cerr << "dump" << std::endl; +} + +//----- path painting +void GerberOutputDev::stroke(GfxState *state) +{ + GfxPath *path = state->getPath(); + double width = state->getTransformedLineWidth(); + GfxGray gray; + + state->getStrokeGray(&gray); + std::cout << "G04 stroke width " << width + << " gray " << colToDbl(gray) << "*" << std::endl; + // McCAD seems to always generate vertical or horizontal strokes. + // Since stoking an aperature will add stuff to the ends of the stroke + // we're just going to make a region. + if (1 != path->getNumSubpaths()) { + std::cerr << "Stroke with more than 1 sub-path" << std::endl; + return; + } + GfxSubpath *subpath = path->getSubpath(0); + + if (0 != state->getLineCap()) { + std::cerr << "Stoke with non-but line cap" << std::endl; + return; + } + if (colToDbl(gray) < 0.5) { + // Set polarity to dark + std::cout << "%LPD*%" << std::endl; + } else { + // Set polarity to clear + std::cout << "%LPC*%" << std::endl; + } + std::cout << "G36*" << std::endl; + if (2 == subpath->getNumPoints()) { + double x0, y0, x1, y1; + + state->transform(subpath->getX(0), subpath->getY(0), &x0, &y0); + state->transform(subpath->getX(1), subpath->getY(1), &x1, &y1); + double dx = x1 - x0; + double dy = y1 - y0; + double l = sqrt(dx * dx + dy * dy); + // half-width perpendicular vector + double hwx = dy / l * width / 2; + double hwy = -dx / l * width / 2; + + std::cout << "X" << coord(x0 + hwx) << "Y" << coord(y0 + hwy) + << "D02*" << std::endl; + std::cout << "G01*" << std::endl; + std::cout << "X" << coord(x0 - hwx) << "Y" << coord(y0 - hwy) + << "D01*" << std::endl; + std::cout << "X" << coord(x1 - hwx) << "Y" << coord(y1 - hwy) + << "D01*" << std::endl; + std::cout << "X" << coord(x1 + hwx) << "Y" << coord(y1 + hwy) + << "D01*" << std::endl; + + } else if (13 == subpath->getNumPoints()) { + // I lied, sometimes there are circles + double x0, y0, x1, y1; + + state->transform(subpath->getX(0), subpath->getY(0), &x0, &y0); + state->transform(subpath->getX(6), subpath->getY(6), &x1, &y1); + double cx = (x0 + x1) / 2; + double cy = (y0 + y1) / 2; + double hw = width / 2; + + std::cout << "G04 x0 " << x0 << " y0 " << y0 + << " x1 " << x1 << " y1 " << y1 << "*" << std::endl; + // Make outer circle + std::cout << "X" << coord(x0 - hw) << "Y" << coord(y0) + << "D02*" << std::endl; + std::cout << "G02*" << std::endl; + std::cout << "X" << coord(x1 + hw) << "Y" << coord(y1) + << "I" << coord(cx - (x0 - hw)) << "J" << coord(cy - y0) + << "D01*" << std::endl; + std::cout << "X" << coord(x0 - hw) << "Y" << coord(y0) + << "I" << coord(cx - (x1 + hw)) << "J" << coord(cy - y1) + << "D01*" << std::endl; + // Make inner circle + std::cout << "G01*" << std::endl; + std::cout << "X" << coord(x0 + hw) << "Y" << coord(y0) + << "D01*" << std::endl; + std::cout << "G03*" << std::endl; + std::cout << "X" << coord(x1 - hw) << "Y" << coord(y1) + << "I" << coord(cx - (x0 + hw)) << "J" << coord(cy - y0) + << "D01*" << std::endl; + std::cout << "X" << coord(x0 + hw) << "Y" << coord(y0) + << "I" << coord(cx - (x1 - hw)) << "J" << coord(cy - y1) + << "D01*" << std::endl; + } else { + std::cerr << "Stroke with points I can't handle" << std::endl; + } + std::cout << "G37*" << std::endl; +} + +void GerberOutputDev::fill(GfxState *state) +{ + // McCAD seems to generate fills for any shape that's not a rectangle, + // rectangles are made with either vertical or horizontal strokes. + GfxPath *path = state->getPath(); + int nsubpaths = path->getNumSubpaths(); + GfxGray gray; + + state->getFillGray(&gray); + std::cout << "G04 fill " << nsubpaths << " subpath(s) gray " + << colToDbl(gray) << "*" << std::endl; + + if (colToDbl(gray) < 0.5) { + // Set polarity to dark + std::cout << "%LPD*%" << std::endl; + } else { + // Set polarity to clear + std::cout << "%LPC*%" << std::endl; + } + + // Use a filled region + std::cout << "G36*" << std::endl; + for (int i = 0; i < nsubpaths; ++i) { + GfxSubpath *subpath = path->getSubpath(i); + int npoints = subpath->getNumPoints(); + int j = 1; + double x, y; + + state->transform(subpath->getX(0), subpath->getY(0), &x, &y); + std::cout << "X" << coord(x) << "Y" << coord(y) << "D02*" << std::endl; + + while (j < npoints) { + if (subpath->getCurve(j)) { + // This is a bezier curve, approximate it with a single arc! + double x0, y0, x1, y1, x2, y2; + + state->transform(subpath->getX(j), subpath->getY(j), &x0, &y0); + state->transform(subpath->getX(j + 1), subpath->getY(j + 1), &x1, &y1); + state->transform(subpath->getX(j + 2), subpath->getY(j + 2), &x2, &y2); + j += 3; + + double mx = midpoint(x, x0, x1, x2); + double my = midpoint(y, y0, y1, y2); + double cx, cy; + + center(x, y, mx, my, x2, y2, &cx, &cy); + if (clockwise(x0, y0, mx, my, x2, y2)) { + // Set clockwise circular interpolation + std::cout << "G02*" << std::endl; + } else { + // Set counterclockwise circular interpolation + std::cout << "G03*" << std::endl; + } + // Arc to x,y with center offset i,j + std::cout << "X" << coord(x2) << "Y" << coord(y2) + << "I" << coord(cx - x) << "J" << coord(cy - y) + << "D01*" << std::endl; + x = x2; + y = y2; + } else { + state->transform(subpath->getX(j), subpath->getY(j), &x, &y); + // Set linear interpolation + std::cout << "G01*" << std::endl + // Line to x,y + << "X" << coord(x) << "Y" << coord(y) << "D01*" << std::endl; + ++j; + } + } + } + std::cout << "G37*" << std::endl; +} + +//----- text drawing +void GerberOutputDev::drawChar(GfxState *state, double x, double y, + double dx, double dy, + double originX, double originY, + CharCode code, int nBytes, Unicode *u, int uLen) +{ + std::cout << "G04 drawChar(" + << x << ", " + << y << ", " + << dx << ", " + << dy << ", " + << originX << ", " + << originY << ", ...)*" << std::endl; +} + +int GerberOutputDev::coord(double decimal) +{ + return round(decimal * 1000000); +} + |