From fc495607dccbbf9820b67079508a6786010475b7 Mon Sep 17 00:00:00 2001 From: jaseg Date: Mon, 1 Feb 2021 09:26:55 +0100 Subject: Update README --- README.md | 61 - README.rst | 191 +- gerbolyze/gerbolyze.py | 3 +- pics/pcbway_sample_01_small.jpg | Bin 0 -> 453117 bytes pics/pcbway_sample_02_small.jpg | Bin 0 -> 521032 bytes pics/pcbway_sample_03_small.jpg | Bin 0 -> 746718 bytes pics/process-overview.png | Bin 0 -> 516640 bytes pics/process-overview.svg | 7230 +++++++++++++++++++++++++++++++++++ pics/sample1.jpg | Bin 0 -> 299841 bytes pics/sample2.jpg | Bin 0 -> 251440 bytes pics/sample3.jpg | Bin 0 -> 171160 bytes pics/test_svg_readme.svg | 515 +++ pics/test_svg_readme_composited.png | Bin 0 -> 1211058 bytes pics/vec_contours_composited.png | Bin 0 -> 40471 bytes pics/vec_hexgrid_composited.png | Bin 0 -> 212917 bytes pics/vec_poisson_composited.png | Bin 0 -> 308380 bytes pics/vec_square_composited.png | Bin 0 -> 176841 bytes sample1.jpg | Bin 299841 -> 0 bytes sample2.jpg | Bin 251440 -> 0 bytes sample3.jpg | Bin 171160 -> 0 bytes svg-flatten/src/main.cpp | 14 +- 21 files changed, 7919 insertions(+), 95 deletions(-) delete mode 100644 README.md create mode 100644 pics/pcbway_sample_01_small.jpg create mode 100644 pics/pcbway_sample_02_small.jpg create mode 100644 pics/pcbway_sample_03_small.jpg create mode 100644 pics/process-overview.png create mode 100644 pics/process-overview.svg create mode 100644 pics/sample1.jpg create mode 100644 pics/sample2.jpg create mode 100644 pics/sample3.jpg create mode 100644 pics/test_svg_readme.svg create mode 100644 pics/test_svg_readme_composited.png create mode 100644 pics/vec_contours_composited.png create mode 100644 pics/vec_hexgrid_composited.png create mode 100644 pics/vec_poisson_composited.png create mode 100644 pics/vec_square_composited.png delete mode 100644 sample1.jpg delete mode 100644 sample2.jpg delete mode 100644 sample3.jpg diff --git a/README.md b/README.md deleted file mode 100644 index 9cac2bf..0000000 --- a/README.md +++ /dev/null @@ -1,61 +0,0 @@ -build/svg-render 2.0 - -Usage: build/svg-render [options]... [input_file] [output_file] - -Specify "-" for stdin/stdout. - - -h, --help - Print help and exit - -v, --version - Print version and exit - -o, --format - Output format. Supported: gerber, svg, s-exp (KiCAD S-Expression) - -p, --precision - Number of decimal places use for exported coordinates (gerber: 1-9, - SVG: 0-*) - --clear-color - SVG color to use for "clear" areas (default: white) - --dark-color - SVG color to use for "dark" areas (default: black) - -d, --trace-space - Minimum feature size of elements in vectorized graphics - (trace/space) in mm. Default: 0.1mm. - --no-header - Do not export output format header/footer, only export the - primitives themselves - --flatten - Flatten output so it only consists of non-overlapping white - polygons. This perform composition at the vector level. Potentially slow. - --no-flatten - Disable automatic flattening for KiCAD S-Exp export - --dilate - Dilate output gerber primitives by this amount in mm. Used for - masking out other layers. - -g, --only-groups - Comma-separated list of group IDs to export. - -b, --vectorizer - Vectorizer to use for bitmap images. One of poisson-disc (default), - hex-grid, square-grid, binary-contours, dev-null. - --vectorizer-map - Map from image element id to vectorizer. Overrides --vectorizer. - Format: id1=vectorizer,id2=vectorizer,... - --force-svg - Force SVG input irrespective of file name - --force-png - Force bitmap graphics input irrespective of file name - -s, --size - Bitmap mode only: Physical size of output image in mm. Format: 12.34x56.78 - --sexp-mod-name - Module name for KiCAD S-Exp output - --sexp-layer - Layer for KiCAD S-Exp output. Defaults to auto-detect layers from - SVG layer/top-level group names - -a, --preserve-aspect-ratio - Bitmap mode only: Preserve aspect ratio of image. Allowed values - are meet, slice. Can also parse full SVG preserveAspectRatio syntax. - --no-usvg - Do not preprocess input using usvg (do not use unless you know - *exactly* what you're doing) - -e, --exclude-groups - Comma-separated list of group IDs to exclude from export. Takes - precedence over --only-groups. diff --git a/README.rst b/README.rst index 15d2654..6a67973 100644 --- a/README.rst +++ b/README.rst @@ -1,46 +1,168 @@ -Gerbolyze high-resolution image-to-PCB converter -================================================ +Gerbolyze high-fidelity SVG/PNG/JPG to PCB converter +==================================================== -.. image:: https://raw.githubusercontent.com/jaseg/gerbolyze/master/sample1.jpg +Gerbolyze renders SVG vector and PNG/JPG raster images into existing gerber PCB manufacturing files. +Vector data from SVG files is rendered losslessly *without* an intermediate rasterization/revectorization step. +Still, gerbolyze supports (almost) the full SVG 1.1 spec including complex, self-intersecting paths with holes, +patterns, dashes and transformations + +Raster images can either be vectorized through contour tracing (like gerbolyze v1.0 did) or they can be embedded using +high-resolution grayscale emulation while (mostly) guaranteeing trace/space design rules. + +.. image:: pics/pcbway_sample_02_small.jpg Tooling for PCB art is quite limited in both open source and closed source ecosystems. Something as simple as putting a pretty picture on a PCB can be an extremely tedious task. Depending on the PCB tool used, various arcane incantations may be necessary and even modestly complex images will slow down most PCB tools to a crawl. -Gerbolyze solves this problem in a toolchain-agnostic way by directly vectorizing bitmap files onto existing gerber -layers. Gerbolyze has been tested against both the leading open-source KiCAD toolchain and the industry-standard Altium -Designer. Gerbolyze is written with performance in mind and will happily vectorize tens of thousands of primitives, -generating tens of megabytes of gerber code without crapping itself. With gerbolyze you can finally be confident that -your PCB fab's toolchain will fall over before yours does if you overdo it with the high-poly anime silkscreen. +Gerbolyze solves this problem in a toolchain-agnostic way by directly vectorizing SVG vector and PNG or JPG bitmap files +onto existing gerber layers. Gerbolyze processes any spec-compliant SVG and "gerbolyzes" SVG vector data into a Gerber +spec-compliant form. Gerbolyze has been tested against both the leading open-source KiCAD toolchain and the +industry-standard Altium Designer. Gerbolyze is written with performance in mind and will happily vectorize tens of +thousands of primitives, generating tens of megabytes of gerber code without crapping itself. With gerbolyze you can +finally be confident that your PCB fab's toolchain will fall over before yours does if you overdo it with the high-poly +anime silkscreen. + +.. image:: pics/process-overview.png .. contents:: -Produce high-quality artistic PCBs in three easy steps! -------------------------------------------------------- +Tl;dr: Produce high-quality artistic PCBs in three easy steps! +-------------------------------------------------------------- Gerbolyze works in three steps. -1. Generate a scale-accurate preview of the finished PCB from your CAD tool's gerber output: +1. Generate a scale-accurate template of the finished PCB from your CAD tool's gerber output: .. code:: - $ gerbolyze render top my_gerber_dir preview.png + $ gerbolyze template --top template_top.svg [--bottom template_bottom.svg] my_gerber_dir -2. Load the resulting preview image into the GIMP or another image editing program. Use it as a guide to position scale - your artwork. Create a black-and-white image from your scaled artwork using GIMP's newsprint filter. Make sure most - details are larger than about 10px to ensure manufacturing goes smooth. +2. Load the resulting template image Inkscape_ or another SVG editing program. Put your artwork on the appropriate SVG + layer. Dark colors become filled gerber primitives, bright colors become unfilled primitives. You can directly put + raster images (PNG/JPG) into this SVG as well, just position and scale them like everything else. SVG clips work for + images, too. Masks are not supported. -3. Vectorize the resulting grayscale image drectly into the PCB's gerber files: +3. Vectorize the edited SVG template image drectly into the PCB's gerber files: .. code:: - $ gerbolyze vectorize top input_gerber_dir output_gerber_dir black_and_white_artwork.png + $ gerbolyze paste --top template_top_edited.svg [--bottom ...] my_gerber_dir output_gerber_dir + +Features +-------- + +Input on the left, output on the right. + +.. image:: pics/test_svg_readme_composited.png + +* Almost full SVG 1.1 static spec coverage (!) + + * Paths with beziers, self-intersections and holes + * Strokes, even with dashes and markers + * Pattern fills and strokes + * Transformations and nested groups + * Proper text rendering with support for complex text layout (e.g. Arabic) + * elements via either built-in vectorizer or built-in halftone processor + * (some) CSS + +* Writes Gerber, SVG or KiCAD S-Expression (``.kicad_mod``) formats +* Can export from top/bottom SVGs to a whole gerber layer stack at once with filename autodetection +* Can export SVGs to ``.kicad_mod`` files like svg2mod (but with full SVG support) +* Beziers flattening with configurable tolerance using actual math! +* Polygon intersection removal +* Polygon hole removal (!) +* Optionally vector-compositing of output: convert black/white/transparent image to black/transparent image +* Renders SVG templates from input gerbers for accurate and easy scaling and positioning of artwork +* layer masking with offset (e.g. all silk within 1mm of soldermask) +* Can read gerbers from zip files + +Gerbolyze is the end-to-end "paste this svg into these gerbers" command that handles all layers on both board sides at +once. The heavy-duty computer geometry logic of gerbolyze is handled by the svg-flatten utility (``svg-flatten`` +directory). svg-flatten reads an SVG file and renders it into a variety of output formats. svg-flatten can be used like +a variant of the popular svg2mod that supports all of SVG and handles arbitrary input ```` elements. + +Algorithm Overview +------------------ + +This is the algorithm gerbolyze uses to process a stack of gerbers. + +* Map input files to semantic layers by their filenames +* For each layer: + + * load input gerber + * Pass mask layers through ``gerbv`` for conversion to SVG + * Pass mask layers SVG through ``svg-flatten --dilate`` + * Pass input SVG through ``svg-flatten --only-groups [layer]`` + * Overlay input gerber, mask and input svg + * Write result to output gerber + +This is the algorithm svg-flatten uses to process an SVG. + +* pass input SVG through usvg_ +* iterate depth-first through resulting SVG. + + * for groups: apply transforms and clip and recurse + * for images: Vectorize using selected vectorizer + * for paths: + + * flatten path using Cairo + * remove self-intersections using Clipper + * if stroke is set: process dash, then offset using Clipper + * apply pattern fills + * clip to clip-path + * remove holes using Clipper + +* for KiCAD S-Expression export: vector-composite results using CavalierContours: subtract each clear output primitive + from all previous dark output primitives + +Gerbolyze image vectorization +----------------------------- -Image preprocessing guide -------------------------- +Gerbolyze has two built-in strategies to translate pixel images into vector images. One is its built-in halftone +processor that tries to approximate grayscale. The other is its built-in binary vectorizer that traces contours in +black-and-white images. Below are examples for the four options. -Nice black-and-white images can be generated from any grayscale image using the GIMP's newsprint filter. The -straight-forward pre-processing steps necessary for use by ``gerbolyze vectorize`` are as follows. +The vectorizers can be used in isolation through ``svg-flatten`` with either an SVG input that contains an image or a +PNG/JPG input. + +The vectorizer can be controlled globally using the ``--vectorizer`` flag in both ``gerbolyze`` and ``svg-flatten``. It +can also be set on a per-image basis in both using ``--vectorizer-map [image svg id]=[option]["," ...]``. + +.. for f in vec_*.png; convert -background white -gravity center $f -resize 500x500 -extent 500x500 (basename -s .png $f)-square.png; end +.. for vec in hexgrid square poisson contours; convert vec_"$vec"_whole-square.png vec_"$vec"_detail-square.png -background transparent -splice 25x0+0+0 +append -chop 25x0+0+0 vec_"$vec"_composited.png; end + +``--vectorizer poisson-disc`` (the default) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. image:: pics/vec_poisson_composited.png + +``--vectorizer hex-grid`` +~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. image:: pics/vec_hexgrid_composited.png + +``--vectorizer square-grid`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. image:: pics/vec_square_composited.png + +``--vectorizer binary-contours`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. image:: pics/vec_contours_composited.png + +The binary contours vectorizer requires a black-and-white binary input image. As you can see, like every bitmap tracer +it will produce some artifacts. For artistic input this is usually not too bad as long as the input data is +high-resolution. Antialiased edges in the input image are not only OK, they may even help with an accurate +vectorization. + +GIMP halftone preprocessing guide +--------------------------------- + +Gerbolyze has its own built-in halftone processor, but you can also use the high-quality "newsprint" filter built into +GIMP_ instead if you like. This section will guide you through this. The PNG you get out of this can then be fed into +gerbolyze using ``--vectorizer binary-contours``. 1 Import a render of the board generated using ``gerbolyze render`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -152,6 +274,28 @@ Gallery .. image:: https://raw.githubusercontent.com/jaseg/gerbolyze/master/sample3.jpg +Limitations +----------- + +SVG raster features +~~~~~~~~~~~~~~~~~~~ + +Currently, SVG masks and filters are not supported. Though SVG is marketed as a "vector graphics format", these two +features are really raster primitives that all SVG viewers perform at the pixel level after rasterization. Since +supporting these would likely not end up looking like what you want, it is not a planned feature. If you need masks or +filters, simply export the relevant parts of the SVG as a PNG then include that in your template. + +Gerber pass-through +~~~~~~~~~~~~~~~~~~~ + +Since gerbolyze has to composite your input gerbers with its own output, it has to fully parse and re-serialize them. +gerbolyze uses pcb-tools_ and pcb-tools-extension_ for all its gerber parsing needs. Both seem well-written, but likely +not free of bugs. This means that in rare cases information may get lost during this round trip. Thus, *always* check +the output files for errors before submitting them to production. + +Gerbolyze is provided without any warranty, but still please open an issue or `send me an email +`__ if you find any errors or inconsistencies. + Licensing --------- @@ -164,3 +308,8 @@ many people as possible and I wouldn't want the license to be a hurdle to anyone board house just integrating a fork into their webpage without providing their changes back upstream, and I want to avoid that so the default license is still AGPL. +.. _usvg: https://github.com/RazrFalcon/resvg +.. _Inkscape: https://inkscape.org/ +.. _pcb-tools: https://github.com/curtacircuitos/pcb-tools +.. _pcb-tools-extension: https://github.com/opiopan/pcb-tools-extension +.. _GIMP: https://gimp.org/ diff --git a/gerbolyze/gerbolyze.py b/gerbolyze/gerbolyze.py index 423adc6..c3107eb 100755 --- a/gerbolyze/gerbolyze.py +++ b/gerbolyze/gerbolyze.py @@ -96,6 +96,7 @@ def paste(input_gerbers, output_gerbers, top, bottom, outfile = tmpdir / 'dilated-{layer}-{amount}.gbr' dilate_gerber(layers, layer, amount, bbox, tmpdir, outfile, units) gbr = gerberex.read(str(outfile)) + gbr.offset(bounds[0][0], bounds[1][0]) return gbr for layer, input_files in layers.items(): @@ -121,8 +122,8 @@ def paste(input_gerbers, output_gerbers, top, bottom, print('compositing') comp = gerberex.GerberComposition() foo = gerberex.rs274x.GerberFile.from_gerber_file(in_grb.cam_source) - foo.offset(-bounds[0][0], -bounds[1][0]) comp.merge(foo) + overlay_grb.offset(bounds[0][0], bounds[1][0]) comp.merge(overlay_grb) dilations = subtract_map.get(layer, []) for d_layer, amount in dilations: diff --git a/pics/pcbway_sample_01_small.jpg b/pics/pcbway_sample_01_small.jpg new file mode 100644 index 0000000..f8d6397 Binary files /dev/null and b/pics/pcbway_sample_01_small.jpg differ diff --git a/pics/pcbway_sample_02_small.jpg b/pics/pcbway_sample_02_small.jpg new file mode 100644 index 0000000..ba8e984 Binary files /dev/null and b/pics/pcbway_sample_02_small.jpg differ diff --git a/pics/pcbway_sample_03_small.jpg b/pics/pcbway_sample_03_small.jpg new file mode 100644 index 0000000..1336739 Binary files /dev/null and b/pics/pcbway_sample_03_small.jpg differ diff --git a/pics/process-overview.png b/pics/process-overview.png new file mode 100644 index 0000000..01d3811 Binary files /dev/null and b/pics/process-overview.png differ diff --git a/pics/process-overview.svg b/pics/process-overview.svg new file mode 100644 index 0000000..b21aa70 --- /dev/null +++ b/pics/process-overview.svg @@ -0,0 +1,7230 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + KiCAD/Altium + SVG + Gerber! + + + + + + + + + + + + diff --git a/pics/sample1.jpg b/pics/sample1.jpg new file mode 100644 index 0000000..948da6f Binary files /dev/null and b/pics/sample1.jpg differ diff --git a/pics/sample2.jpg b/pics/sample2.jpg new file mode 100644 index 0000000..ef47bd4 Binary files /dev/null and b/pics/sample2.jpg differ diff --git a/pics/sample3.jpg b/pics/sample3.jpg new file mode 100644 index 0000000..780c080 Binary files /dev/null and b/pics/sample3.jpg differ diff --git a/pics/test_svg_readme.svg b/pics/test_svg_readme.svg new file mode 100644 index 0000000..1a0178e --- /dev/null +++ b/pics/test_svg_readme.svg @@ -0,0 +1,515 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + सर्वे मानवाः स्वतन्त्राः समुत्पन्नाः वर्तन्ते अपि च, गौरवदृशा + + + + + لكن لا بد أن أوضح لك أن كل هذه الأفكار المغلوطة حول استنكار + + This is a gerber export test + + diff --git a/pics/test_svg_readme_composited.png b/pics/test_svg_readme_composited.png new file mode 100644 index 0000000..f686e19 Binary files /dev/null and b/pics/test_svg_readme_composited.png differ diff --git a/pics/vec_contours_composited.png b/pics/vec_contours_composited.png new file mode 100644 index 0000000..ed0f3c9 Binary files /dev/null and b/pics/vec_contours_composited.png differ diff --git a/pics/vec_hexgrid_composited.png b/pics/vec_hexgrid_composited.png new file mode 100644 index 0000000..4923fc8 Binary files /dev/null and b/pics/vec_hexgrid_composited.png differ diff --git a/pics/vec_poisson_composited.png b/pics/vec_poisson_composited.png new file mode 100644 index 0000000..73d313e Binary files /dev/null and b/pics/vec_poisson_composited.png differ diff --git a/pics/vec_square_composited.png b/pics/vec_square_composited.png new file mode 100644 index 0000000..c02c2e8 Binary files /dev/null and b/pics/vec_square_composited.png differ diff --git a/sample1.jpg b/sample1.jpg deleted file mode 100644 index 948da6f..0000000 Binary files a/sample1.jpg and /dev/null differ diff --git a/sample2.jpg b/sample2.jpg deleted file mode 100644 index ef47bd4..0000000 Binary files a/sample2.jpg and /dev/null differ diff --git a/sample3.jpg b/sample3.jpg deleted file mode 100644 index 780c080..0000000 Binary files a/sample3.jpg and /dev/null differ diff --git a/svg-flatten/src/main.cpp b/svg-flatten/src/main.cpp index d05672a..3cf9a36 100644 --- a/svg-flatten/src/main.cpp +++ b/svg-flatten/src/main.cpp @@ -188,9 +188,7 @@ int main(int argc, char **argv) { } else if (fmt == "s-exp" || fmt == "sexp" || fmt == "kicad") { if (!args["sexp_mod_name"]) { - cerr << "--sexp-mod-name must be given for sexp export" << endl; - argagg::fmt_ostream fmt(cerr); - fmt << usage.str() << argparser; + cerr << "Error: --sexp-mod-name must be given for sexp export" << endl; return EXIT_FAILURE; } @@ -199,9 +197,7 @@ int main(int argc, char **argv) { is_sexp = true; } else { - cerr << "Unknown output format \"" << fmt << "\"" << endl; - argagg::fmt_ostream fmt(cerr); - fmt << usage.str() << argparser; + cerr << "Error: Unknown output format \"" << fmt << "\"" << endl; return EXIT_FAILURE; } @@ -265,8 +261,6 @@ int main(int argc, char **argv) { cerr << "writing bitmap into svg" << endl; if (!args["size"]) { cerr << "Error: --size must be given when using bitmap input." << endl; - argagg::fmt_ostream fmt(cerr); - fmt << usage.str() << argparser; return EXIT_FAILURE; } @@ -274,8 +268,6 @@ int main(int argc, char **argv) { auto pos = sz.find_first_of("x*,"); if (pos == string::npos) { cerr << "Error: --size must be of form 12.34x56.78" << endl; - argagg::fmt_ostream fmt(cerr); - fmt << usage.str() << argparser; return EXIT_FAILURE; } @@ -287,8 +279,6 @@ int main(int argc, char **argv) { if (width < 1 || height < 1) { cerr << "Error: --size must be of form 12.34x56.78 and values must be positive floating-point numbers in mm" << endl; - argagg::fmt_ostream fmt(cerr); - fmt << usage.str() << argparser; return EXIT_FAILURE; } -- cgit