From 29c8245d0a28d70851b0be8635a88cf2c70316f0 Mon Sep 17 00:00:00 2001 From: jaseg Date: Wed, 29 Sep 2021 18:44:09 +0200 Subject: Auto-discover usvg and svg-flatten properly --- gerbolyze/gerbolyze.py | 11 +++++-- svg-flatten/Makefile | 3 ++ svg-flatten/src/vec_core.cpp | 14 ++++----- svg-flatten/svg_flatten_wasi/__init__.py | 52 ++++++++++++++++++++++++++++---- 4 files changed, 65 insertions(+), 15 deletions(-) diff --git a/gerbolyze/gerbolyze.py b/gerbolyze/gerbolyze.py index d4b4ab5..53b10a4 100755 --- a/gerbolyze/gerbolyze.py +++ b/gerbolyze/gerbolyze.py @@ -624,17 +624,24 @@ def svg_to_gerber(infile, outfile, candidates = [os.environ['SVG_FLATTEN']] else: - # By default, try three options: + # By default, try four options: candidates = [ # somewhere in $PATH 'svg-flatten', + 'wasi-svg-flatten', # in user-local pip installation Path.home() / '.local' / 'bin' / 'svg-flatten', - # next to our current python interpreter (e.g. in virtualenv + Path.home() / '.local' / 'bin' / 'wasi-svg-flatten', + # next to our current python interpreter (e.g. in virtualenv) str(Path(sys.executable).parent / 'svg-flatten'), + str(Path(sys.executable).parent / 'wasi-svg-flatten'), # next to this python source file in the development repo str(Path(__file__).parent.parent / 'svg-flatten' / 'build' / 'svg-flatten') ] + # if SVG_FLATTEN envvar is set, try that first. + if 'SVG_FLATTEN' in os.environ: + candidates = [os.environ['SVG_FLATTEN'], *candidates] + args = [ '--format', ('gerber-outline' if outline_mode else 'gerber'), '--precision', '6', # intermediate file, use higher than necessary precision '--trace-space', str(trace_space) ] diff --git a/svg-flatten/Makefile b/svg-flatten/Makefile index 902489e..e8d4dad 100644 --- a/svg-flatten/Makefile +++ b/svg-flatten/Makefile @@ -76,6 +76,9 @@ BINARY := svg-flatten all: $(BUILDDIR)/$(BINARY) $(BUILDDIR)/nopencv-test +.PHONY: wasm +wasm: $(BUILDDIR)/$(BINARY).wasm + $(CACHEDIR)/$(WASI_SDK): mkdir -p $(dir $@) cd $(dir $@); curl -L ${WASI_SDK_URL} | tar xzf - diff --git a/svg-flatten/src/vec_core.cpp b/svg-flatten/src/vec_core.cpp index 1b36887..39285f9 100644 --- a/svg-flatten/src/vec_core.cpp +++ b/svg-flatten/src/vec_core.cpp @@ -175,7 +175,7 @@ void gerbolyze::VoronoiVectorizer::vectorize_image(RenderContext &ctx, const pug double off_y = 0; handle_aspect_ratio(node.attribute("preserveAspectRatio").value(), scale_x, scale_y, off_x, off_y, orig_cols, orig_rows); - cerr << "aspect " << scale_x << ", " << scale_y << " / " << off_x << ", " << off_y << endl; + //cerr << "aspect " << scale_x << ", " << scale_y << " / " << off_x << ", " << off_y << endl; /* Adjust minimum feature size given in mm and translate into px document units in our local coordinate system. */ min_feature_size_px = img_ctx.mat().doc2phys_dist(min_feature_size_px); @@ -255,10 +255,10 @@ void gerbolyze::VoronoiVectorizer::vectorize_image(RenderContext &ctx, const pug vector adjusted_fill_factors; adjusted_fill_factors.reserve(32); /* Vector to hold adjusted fill factors for each edge for gap filling */ /* now iterate over all voronoi cells again to generate each cell's scaled polygon halftone blob. */ - cerr << " generating cells " << diagram.numsites << endl; + //cerr << " generating cells " << diagram.numsites << endl; for (int i=0; inext; } - cerr << " blob: "; + //cerr << " blob: "; /* Now, generate the actual halftone blob polygon */ ClipperLib::Path cell_path; double last_fill_factor = adjusted_fill_factors.back(); @@ -312,7 +312,7 @@ void gerbolyze::VoronoiVectorizer::vectorize_image(RenderContext &ctx, const pug off_x + center.x + (e->pos[0].x - center.x) * fill_factor, off_y + center.y + (e->pos[0].y - center.y) * fill_factor }); - cerr << " - <" << p[0] << ", " << p[1] << ">"; + //cerr << " - <" << p[0] << ", " << p[1] << ">"; cell_path.push_back({ (ClipperLib::cInt)round(p[0] * clipper_scale), (ClipperLib::cInt)round(p[1] * clipper_scale) @@ -324,7 +324,7 @@ void gerbolyze::VoronoiVectorizer::vectorize_image(RenderContext &ctx, const pug off_x + center.x + (e->pos[1].x - center.x) * fill_factor, off_y + center.y + (e->pos[1].y - center.y) * fill_factor }); - cerr << " - [" << p[0] << ", " << p[1] << "]"; + //cerr << " - [" << p[0] << ", " << p[1] << "]"; cell_path.push_back({ (ClipperLib::cInt)round(p[0] * clipper_scale), (ClipperLib::cInt)round(p[1] * clipper_scale) @@ -334,7 +334,7 @@ void gerbolyze::VoronoiVectorizer::vectorize_image(RenderContext &ctx, const pug last_fill_factor = fill_factor; e = e->next; } - cerr << endl; + //cerr << endl; /* Now, clip the halftone blob generated above against the given clip path. We do this individually for each * blob since this way is *much* faster than throwing a million blobs at once at poor clipper. */ diff --git a/svg-flatten/svg_flatten_wasi/__init__.py b/svg-flatten/svg_flatten_wasi/__init__.py index fe21b5b..3b35b1f 100644 --- a/svg-flatten/svg_flatten_wasi/__init__.py +++ b/svg-flatten/svg_flatten_wasi/__init__.py @@ -1,13 +1,14 @@ import os import sys +import subprocess import tempfile import wasmtime import platform import click -import pathlib +from pathlib import Path import hashlib -import appdirs import lzma +import appdirs from importlib import resources as importlib_resources try: importlib_resources.files # py3.9+ stdlib @@ -36,7 +37,7 @@ def _run_wasm_app(wasm_filename, argv, cachedir="svg-flatten-wasi"): module_path_digest = hashlib.sha256(__file__.encode()).hexdigest() module_digest = hashlib.sha256(module_binary).hexdigest() - cache_path = pathlib.Path(os.getenv("SVG_FLATTEN_WASI_CACHE_DIR", appdirs.user_cache_dir(cachedir))) + cache_path = Path(os.getenv("SVG_FLATTEN_WASI_CACHE_DIR", appdirs.user_cache_dir(cachedir))) cache_path.mkdir(parents=True, exist_ok=True) cache_filename = (cache_path / f'{wasm_filename}-{module_path_digest[:8]}-{module_digest[:16]}') @@ -72,11 +73,50 @@ def _run_wasm_app(wasm_filename, argv, cachedir="svg-flatten-wasi"): return trap.code +def run_usvg(input_file, output_file, dpi=96): + args = ['--keep-named-groups', '--dpi', str(dpi), input_file, output_file] + + # By default, try a number of options: + candidates = [ + # somewhere in $PATH + 'usvg', + 'wasi-usvg', + # in user-local cargo installation + Path.home() / '.cargo' / 'bin' / 'usvg', + # wasi-usvg in user-local pip installation + Path.home() / '.local' / 'bin' / 'wasi-usvg', + # next to our current python interpreter (e.g. in virtualenv) + str(Path(sys.executable).parent / 'wasi-usvg') + ] + + # if USVG envvar is set, try that first. + if 'USVG' in os.environ: + exec_candidates = [os.environ['USVG'], *exec_candidates] + + for candidate in candidates: + try: + res = subprocess.run([candidate, *args], check=True) + print('used usvg:', candidate) + break + except FileNotFoundError: + continue + else: + raise SystemError('usvg executable not found') + + @click.command(context_settings={'ignore_unknown_options': True}) +@click.option('--no-usvg', is_flag=True) +@click.option('--usvg-dpi', type=int, default=96) @click.argument('other_args', nargs=-1, type=click.UNPROCESSED) @click.argument('input_file', type=click.Path(resolve_path=True, dir_okay=False)) @click.argument('output_file', type=click.Path(resolve_path=True, dir_okay=False, writable=True)) -def run_usvg(input_file, output_file, other_args): +def run_svg_flatten(input_file, output_file, other_args, usvg_dpi, no_usvg): + + with tempfile.NamedTemporaryFile() as f: + if not no_usvg: + run_usvg(input_file, f.name, dpi=usvg_dpi) + input_file = f.name + + cmdline = ['svg-flatten', '--force-svg', '--no-usvg', *other_args, input_file, output_file] + sys.exit(_run_wasm_app("svg-flatten.wasm", cmdline)) - cmdline = ['svg-flatten', *other_args, input_file, output_file] - sys.exit(_run_wasm_app("svg-flatten.wasm", cmdline)) -- cgit