From a68e395cb622b9cb91e408b0846cc10126f8ddad Mon Sep 17 00:00:00 2001 From: jaseg Date: Sun, 31 Jan 2021 22:11:34 +0100 Subject: Advanced svg/gerber composition working --- TODO | 2 ++ gerbolyze/gerbolyze.py | 40 +++++++++++++++++++++++++++------------ svg-flatten/include/gerbolyze.hpp | 3 ++- svg-flatten/src/main.cpp | 18 ++++++++++++++++-- svg-flatten/src/out_dilater.cpp | 4 ---- svg-flatten/src/out_gerber.cpp | 21 ++++++++++---------- 6 files changed, 59 insertions(+), 29 deletions(-) create mode 100644 TODO diff --git a/TODO b/TODO new file mode 100644 index 0000000..55033ed --- /dev/null +++ b/TODO @@ -0,0 +1,2 @@ +[ ] Do not just return "error 255" if usvg is not installed +[ ] Straighten up svg-flatten input unit handling diff --git a/gerbolyze/gerbolyze.py b/gerbolyze/gerbolyze.py index 5dbbefe..1d69977 100755 --- a/gerbolyze/gerbolyze.py +++ b/gerbolyze/gerbolyze.py @@ -95,7 +95,9 @@ def paste_vectors(input_gerbers, output_gerbers, top, bottom, outfile = Path('debug') / f'dilated-{layer}-{amount}.gbr' # FIXME # outfile = tmpdir / 'dilated-{layer}-{amount}.gbr' dilate_gerber(layers, layer, amount, bbox, tmpdir, outfile, units) - return gerberex.read(str(outfile)) + gbr = gerberex.read(str(outfile)) + # gbr.offset(bounds[0][0], bounds[1][0]) + return gbr for layer, input_files in layers.items(): if layer == 'drill': @@ -510,19 +512,28 @@ def create_template_from_svg(bounds, svg_data, extra_layers=DEFAULT_EXTRA_LAYERS #================== def dilate_gerber(layers, layer_name, dilation, bbox, tmpdir, outfile, units): - # render gerber to SVG - scale = 1/25.4 if units == 'metric' else 1.0 # pcb-tools gerber scale - scale *= CAIRO_SVG_HARDCODED_DPI + if layer_name not in layers: + raise ValueError(f'Cannot dilate layer {layer_name}: layer not found in input dir') bounds = get_bounds(bbox, layers) - ctx = GerberCairoContext(scale=scale) - for _path, to_render in layers.get(layer_name, ()): - ctx.render_layer(to_render, bounds=bounds, - settings=gerber.render.RenderSettings(color=(0,0,0)), # FIXME should be 1, 1, 1 - bgsettings=gerber.render.RenderSettings(color=(0,0,0), alpha=0)) + (x_min_mm, x_max_mm), (y_min_mm, y_max_mm) = bounds + + origin_x = x_min_mm / MM_PER_INCH + origin_y = y_min_mm / MM_PER_INCH + + width = (x_max_mm - x_min_mm) / MM_PER_INCH + height = (y_max_mm - y_min_mm) / MM_PER_INCH tmpfile = tmpdir / 'dilate-tmp.svg' - ctx.dump(str(tmpfile)) + path, _gbr = layers[layer_name][0] + # NOTE: gerbv has an undocumented maximum length of 20 chars for the arguments to --origin and --window_inch + cmd = ['gerbv', '-x', 'svg', + '--border=0', + f'--origin={origin_x:.6f}x{origin_y:.6f}', f'--window_inch={width:.6f}x{height:.6f}', + '--foreground=#ffffff', + '-o', str(tmpfile), str(path)] + print('cmd:', " ".join(cmd)) + subprocess.run(cmd, check=True) # FIXME DEBUG import uuid @@ -531,9 +542,10 @@ def dilate_gerber(layers, layer_name, dilation, bbox, tmpdir, outfile, units): print('tmp debug dilation svg:', fn) # dilate & render back to gerber - svg_to_gerber(tmpfile, outfile, dilate=dilation) + # TODO: the scale parameter is a hack. ideally we would fix svg-flatten to handle input units correctly. + svg_to_gerber(tmpfile, outfile, dilate=-dilation, dpi=72, scale=25.4/72.0) -def svg_to_gerber(infile, outfile, layer=None, trace_space:'mm'=0.1, vectorizer=None, vectorizer_map=None, exclude_groups=None, dilate=None): +def svg_to_gerber(infile, outfile, layer=None, trace_space:'mm'=0.1, vectorizer=None, vectorizer_map=None, exclude_groups=None, dilate=None, dpi=None, scale=None): if 'SVG_FLATTEN' in os.environ: candidates = [os.environ['SVG_FLATTEN']] @@ -560,6 +572,10 @@ def svg_to_gerber(infile, outfile, layer=None, trace_space:'mm'=0.1, vectorizer= args += ['--exclude-groups', exclude_groups] if dilate: args += ['--dilate', str(dilate)] + if dpi: + args += ['--usvg-dpi', str(dpi)] + if scale: + args += ['--scale', str(scale)] args += [str(infile), str(outfile)] print('full args:', " ".join(args)) diff --git a/svg-flatten/include/gerbolyze.hpp b/svg-flatten/include/gerbolyze.hpp index 1fe13ae..96a4103 100644 --- a/svg-flatten/include/gerbolyze.hpp +++ b/svg-flatten/include/gerbolyze.hpp @@ -213,7 +213,7 @@ namespace gerbolyze { class SimpleGerberOutput : public StreamPolygonSink { public: - SimpleGerberOutput(std::ostream &out, bool only_polys=false, int digits_int=4, int digits_frac=6, d2p offset={0,0}); + SimpleGerberOutput(std::ostream &out, bool only_polys=false, int digits_int=4, int digits_frac=6, double scale=1.0, d2p offset={0,0}); virtual ~SimpleGerberOutput() {} virtual SimpleGerberOutput &operator<<(const Polygon &poly); virtual SimpleGerberOutput &operator<<(GerberPolarityToken pol); @@ -227,6 +227,7 @@ namespace gerbolyze { double m_height; long long int m_gerber_scale; d2p m_offset; + double m_scale; }; class SimpleSVGOutput : public StreamPolygonSink { diff --git a/svg-flatten/src/main.cpp b/svg-flatten/src/main.cpp index 135a4c1..d05672a 100644 --- a/svg-flatten/src/main.cpp +++ b/svg-flatten/src/main.cpp @@ -82,6 +82,12 @@ int main(int argc, char **argv) { {"skip_usvg", {"--no-usvg"}, "Do not preprocess input using usvg (do not use unless you know *exactly* what you're doing)", 0}, + {"usvg_dpi", {"--usvg-dpi"}, + "Passed through to usvg's --dpi, in case the input file has different ideas of DPI than usvg has.", + 1}, + {"scale", {"--scale"}, + "Scale input svg lengths by this factor.", + 1}, {"exclude_groups", {"-e", "--exclude-groups"}, "Comma-separated list of group IDs to exclude from export. Takes precedence over --only-groups.", 1}, @@ -176,7 +182,9 @@ int main(int argc, char **argv) { sink = new SimpleSVGOutput(*out_f, only_polys, precision, dark_color, clear_color); } else if (fmt == "gbr" || fmt == "grb" || fmt == "gerber") { - sink = new SimpleGerberOutput(*out_f, only_polys, 4, precision); + double scale = args["scale"].as(1.0); + cerr << "loading @scale=" << scale << endl; + sink = new SimpleGerberOutput(*out_f, only_polys, 4, precision, scale); } else if (fmt == "s-exp" || fmt == "sexp" || fmt == "kicad") { if (!args["sexp_mod_name"]) { @@ -334,7 +342,13 @@ int main(int argc, char **argv) { } else { cerr << "calling usvg on " << barf << " and " << frob << endl; - const char *command_line[] = {"usvg", "--keep-named-groups", barf.c_str(), frob.c_str(), NULL}; + int dpi = 96; + if (args["usvg_dpi"]) { + dpi = args["usvg_dpi"].as(); + } + string dpi_str = to_string(dpi); + + const char *command_line[] = {"usvg", "--keep-named-groups", "--dpi", dpi_str.c_str(), barf.c_str(), frob.c_str(), NULL}; struct subprocess_s subprocess; int rc = subprocess_create(command_line, subprocess_option_inherit_environment, &subprocess); if (rc) { diff --git a/svg-flatten/src/out_dilater.cpp b/svg-flatten/src/out_dilater.cpp index c0e5969..1ac1040 100644 --- a/svg-flatten/src/out_dilater.cpp +++ b/svg-flatten/src/out_dilater.cpp @@ -52,10 +52,6 @@ Dilater &Dilater::operator<<(GerberPolarityToken pol) { } Dilater &Dilater::operator<<(const Polygon &poly) { - static int i = 0; - cerr << "dilating poly " << i++ << endl; - - cerr << "got poly of " << poly.size() << " nodes" << endl; ClipperLib::Path poly_c; for (auto &p : poly) { poly_c.push_back({(ClipperLib::cInt)round(p[0] * clipper_scale), (ClipperLib::cInt)round(p[1] * clipper_scale)}); diff --git a/svg-flatten/src/out_gerber.cpp b/svg-flatten/src/out_gerber.cpp index c320184..b32e6e4 100644 --- a/svg-flatten/src/out_gerber.cpp +++ b/svg-flatten/src/out_gerber.cpp @@ -27,11 +27,12 @@ using namespace gerbolyze; using namespace std; -SimpleGerberOutput::SimpleGerberOutput(ostream &out, bool only_polys, int digits_int, int digits_frac, d2p offset) +SimpleGerberOutput::SimpleGerberOutput(ostream &out, bool only_polys, int digits_int, int digits_frac, double scale, d2p offset) : StreamPolygonSink(out, only_polys), m_digits_int(digits_int), m_digits_frac(digits_frac), - m_offset(offset) + m_offset(offset), + m_scale(scale) { assert(1 <= digits_int && digits_int <= 9); assert(0 <= digits_frac && digits_frac <= 9); @@ -39,10 +40,10 @@ SimpleGerberOutput::SimpleGerberOutput(ostream &out, bool only_polys, int digits } void SimpleGerberOutput::header_impl(d2p origin, d2p size) { - m_offset[0] += origin[0]; - m_offset[1] += origin[1]; - m_width = size[0] - origin[0]; - m_height = size[1] - origin[1]; + m_offset[0] += origin[0] * m_scale; + m_offset[1] += origin[1] * m_scale; + m_width = (size[0] - origin[0]) * m_scale; + m_height = (size[1] - origin[1]) * m_scale; if (pow(10, m_digits_int-1) < max(m_width, m_height)) { cerr << "Warning: Input has bounding box too large for " << m_digits_int << "." << m_digits_frac << " gerber resolution!" << endl; @@ -74,16 +75,16 @@ SimpleGerberOutput& SimpleGerberOutput::operator<<(const Polygon &poly) { } /* NOTE: Clipper and gerber both have different fixed-point scales. We get points in double mm. */ - double x = round((poly[0][0] + m_offset[0]) * m_gerber_scale); - double y = round((m_height - poly[0][1] + m_offset[1]) * m_gerber_scale); + double x = round((poly[0][0] * m_scale + m_offset[0]) * m_gerber_scale); + double y = round((m_height - poly[0][1] * m_scale + m_offset[1]) * m_gerber_scale); m_out << "G36*" << endl; m_out << "X" << setw(m_digits_int + m_digits_frac) << setfill('0') << (long long int)x << "Y" << setw(m_digits_int + m_digits_frac) << setfill('0') << (long long int)y << "D02*" << endl; m_out << "G01*" << endl; for (size_t i=1; i