aboutsummaryrefslogtreecommitdiff
path: root/src/gerber-output-dev.cc
diff options
context:
space:
mode:
authorDavid Barksdale <amatus@amat.us>2019-01-01 17:57:24 -0600
committerDavid Barksdale <amatus@amat.us>2019-01-01 17:57:24 -0600
commite05d90be8cf4b5e2144253ee839832565045abe6 (patch)
tree30fc211115776a7d6fa86a6ed000a2a1e0053a28 /src/gerber-output-dev.cc
Initial commit before things get crazyHEADmaster
This seems to be interpreting everything in some pdf files I have generated from McCAD (via printing to postscript) except for text. The scale also needs to be considered.
Diffstat (limited to 'src/gerber-output-dev.cc')
-rw-r--r--src/gerber-output-dev.cc285
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);
+}
+