From e4a0c1ba4a16c0e0f7c1013afd924ecb41b41f05 Mon Sep 17 00:00:00 2001 From: jaseg Date: Mon, 20 Jun 2022 18:30:34 +0200 Subject: Fix outline mode clip & flash handling --- gerbolyze/__init__.py | 5 ++- svg-flatten/include/gerbolyze.hpp | 16 ++----- svg-flatten/src/out_dilater.cpp | 5 +++ svg-flatten/src/out_flattener.cpp | 2 +- svg-flatten/src/out_gerber.cpp | 11 ----- svg-flatten/src/out_scaler.cpp | 6 --- svg-flatten/src/out_sexp.cpp | 2 +- svg-flatten/src/out_svg.cpp | 2 +- svg-flatten/src/svg_doc.cpp | 91 +++++++++++++++++++++++---------------- 9 files changed, 71 insertions(+), 69 deletions(-) diff --git a/gerbolyze/__init__.py b/gerbolyze/__init__.py index 0ba7b02..3c624bb 100755 --- a/gerbolyze/__init__.py +++ b/gerbolyze/__init__.py @@ -213,8 +213,10 @@ def empty_template(output_svg, size, force, copper_layers, no_default_layers, la @click.option('--vectorizer-map', help='passed through to svg-flatten') @click.option('--exclude-groups', help='passed through to svg-flatten') @click.option('--pattern-complete-tiles-only', is_flag=True, help='passed through to svg-flatten') +@click.option('--use-apertures-for-patterns', is_flag=True, help='passed through to svg-flatten') def convert(input_svg, output_gerbers, is_zip, dilate, curve_tolerance, no_subtract, subtract, trace_space, vectorizer, - vectorizer_map, exclude_groups, separate_drill, naming_scheme, pattern_complete_tiles_only): + vectorizer_map, exclude_groups, separate_drill, naming_scheme, + pattern_complete_tiles_only, use_apertures_for_patterns): ''' Convert SVG file directly to gerbers. Unlike `gerbolyze paste`, this does not add the SVG's contents to existing gerbers. It allows you to directly create @@ -244,6 +246,7 @@ def convert(input_svg, output_gerbers, is_zip, dilate, curve_tolerance, no_subtr trace_space=trace_space, vectorizer=vectorizer, vectorizer_map=vectorizer_map, exclude_groups=exclude_groups, curve_tolerance=curve_tolerance, only_groups=group_id, pattern_complete_tiles_only=pattern_complete_tiles_only, + use_apertures_for_patterns=(use_apertures_for_patterns and use not in ('outline', 'drill')), outline_mode=(use == 'outline' or use == 'drill')) grb.original_path = Path() diff --git a/svg-flatten/include/gerbolyze.hpp b/svg-flatten/include/gerbolyze.hpp index d62cbcc..b50f18c 100644 --- a/svg-flatten/include/gerbolyze.hpp +++ b/svg-flatten/include/gerbolyze.hpp @@ -50,12 +50,6 @@ namespace gerbolyze { double m_size = 0.0; }; - class DrillToken { - public: - DrillToken(d2p center) : m_center(center) {} - d2p m_center; - }; - class PatternToken { public: PatternToken(vector> &polys) : m_polys(polys) {} @@ -91,7 +85,6 @@ namespace gerbolyze { virtual PolygonSink &operator<<(const LayerNameToken &) { return *this; }; virtual PolygonSink &operator<<(GerberPolarityToken pol) = 0; virtual PolygonSink &operator<<(const ApertureToken &) { return *this; }; - virtual PolygonSink &operator<<(const DrillToken &) { return *this; }; virtual PolygonSink &operator<<(const FlashToken &) { return *this; }; virtual PolygonSink &operator<<(const PatternToken &) { cerr << "Error: pattern to aperture mapping is not supporte for this output." << endl; @@ -110,7 +103,7 @@ namespace gerbolyze { virtual Flattener &operator<<(const LayerNameToken &layer_name); virtual Flattener &operator<<(GerberPolarityToken pol); virtual Flattener &operator<<(const ApertureToken &tok); - virtual Flattener &operator<<(const DrillToken &tok); + virtual Flattener &operator<<(const FlashToken &tok); virtual void footer(); private: @@ -129,6 +122,7 @@ namespace gerbolyze { virtual Dilater &operator<<(const LayerNameToken &layer_name); virtual Dilater &operator<<(GerberPolarityToken pol); virtual Dilater &operator<<(const ApertureToken &ap); + virtual Dilater &operator<<(const FlashToken &tok); virtual void footer(); private: @@ -145,7 +139,6 @@ namespace gerbolyze { virtual PolygonScaler &operator<<(const LayerNameToken &layer_name); virtual PolygonScaler &operator<<(GerberPolarityToken pol); virtual PolygonScaler &operator<<(const ApertureToken &tok); - virtual PolygonScaler &operator<<(const DrillToken &tok); virtual PolygonScaler &operator<<(const FlashToken &tok); virtual PolygonScaler &operator<<(const PatternToken &tok); virtual void footer(); @@ -328,7 +321,6 @@ namespace gerbolyze { virtual ~SimpleGerberOutput() {} virtual SimpleGerberOutput &operator<<(const Polygon &poly); virtual SimpleGerberOutput &operator<<(GerberPolarityToken pol); - virtual SimpleGerberOutput &operator<<(const DrillToken &tok); virtual SimpleGerberOutput &operator<<(const ApertureToken &ap); virtual SimpleGerberOutput &operator<<(const FlashToken &tok); virtual SimpleGerberOutput &operator<<(const PatternToken &tok); @@ -356,7 +348,7 @@ namespace gerbolyze { virtual ~SimpleSVGOutput() {} virtual SimpleSVGOutput &operator<<(const Polygon &poly); virtual SimpleSVGOutput &operator<<(GerberPolarityToken pol); - virtual SimpleSVGOutput &operator<<(const DrillToken &tok); + virtual SimpleSVGOutput &operator<<(const FlashToken &tok); virtual void header_impl(d2p origin, d2p size); virtual void footer_impl(); @@ -374,7 +366,7 @@ namespace gerbolyze { virtual ~KicadSexpOutput() {} virtual KicadSexpOutput &operator<<(const Polygon &poly); virtual KicadSexpOutput &operator<<(const LayerNameToken &layer_name); - virtual KicadSexpOutput &operator<<(const DrillToken &tok); + virtual KicadSexpOutput &operator<<(const FlashToken &tok); virtual KicadSexpOutput &operator<<(GerberPolarityToken pol); virtual void header_impl(d2p origin, d2p size); virtual void footer_impl(); diff --git a/svg-flatten/src/out_dilater.cpp b/svg-flatten/src/out_dilater.cpp index a5b0619..4a259a9 100644 --- a/svg-flatten/src/out_dilater.cpp +++ b/svg-flatten/src/out_dilater.cpp @@ -88,3 +88,8 @@ Dilater &Dilater::operator<<(const ApertureToken &ap) { m_sink << ApertureToken(ap.m_size + 2*m_dilation); return *this; } + +Dilater &Dilater::operator<<(const FlashToken &tok) { + m_sink << tok; + return *this; +} diff --git a/svg-flatten/src/out_flattener.cpp b/svg-flatten/src/out_flattener.cpp index 600584b..398d055 100644 --- a/svg-flatten/src/out_flattener.cpp +++ b/svg-flatten/src/out_flattener.cpp @@ -152,7 +152,7 @@ Flattener &Flattener::operator<<(const LayerNameToken &layer_name) { return *this; } -Flattener &Flattener::operator<<(const DrillToken &tok) { +Flattener &Flattener::operator<<(const FlashToken &tok) { m_sink << tok; return *this; } diff --git a/svg-flatten/src/out_gerber.cpp b/svg-flatten/src/out_gerber.cpp index 0b0a6bf..543d641 100644 --- a/svg-flatten/src/out_gerber.cpp +++ b/svg-flatten/src/out_gerber.cpp @@ -126,17 +126,6 @@ SimpleGerberOutput& SimpleGerberOutput::operator<<(const Polygon &poly) { return *this; } -SimpleGerberOutput &SimpleGerberOutput::operator<<(const DrillToken &tok) { - double x = round((tok.m_center[0] * m_scale + m_offset[0]) * m_gerber_scale); - double y = round((m_height - tok.m_center[1] * m_scale + m_offset[1]) * m_gerber_scale); - - m_out << "X" << setw(m_digits_int + m_digits_frac) << setfill('0') << std::internal /* isn't C++ a marvel of engineering? */ << (long long int)x - << "Y" << setw(m_digits_int + m_digits_frac) << setfill('0') << std::internal << (long long int)y - << "D03*" << endl; - - return *this; -} - void SimpleGerberOutput::footer_impl() { m_out << "M02*" << endl; } diff --git a/svg-flatten/src/out_scaler.cpp b/svg-flatten/src/out_scaler.cpp index fb54145..c6e624d 100644 --- a/svg-flatten/src/out_scaler.cpp +++ b/svg-flatten/src/out_scaler.cpp @@ -64,12 +64,6 @@ PolygonScaler &PolygonScaler::operator<<(const Polygon &poly) { return *this; } -PolygonScaler &PolygonScaler::operator<<(const DrillToken &tok) { - d2p new_center { tok.m_center[0] * m_scale, tok.m_center[1] * m_scale }; - m_sink << DrillToken(new_center); - return *this; -} - PolygonScaler &PolygonScaler::operator<<(const FlashToken &tok) { d2p new_offset = { tok.m_offset[0] * m_scale, tok.m_offset[1] * m_scale}; m_sink << FlashToken(new_offset); diff --git a/svg-flatten/src/out_sexp.cpp b/svg-flatten/src/out_sexp.cpp index f872b90..6c9f5ae 100644 --- a/svg-flatten/src/out_sexp.cpp +++ b/svg-flatten/src/out_sexp.cpp @@ -135,7 +135,7 @@ KicadSexpOutput &KicadSexpOutput::operator<<(const Polygon &poly) { return *this; } -KicadSexpOutput &KicadSexpOutput::operator<<(const DrillToken &) { +KicadSexpOutput &KicadSexpOutput::operator<<(const FlashToken &) { return *this; } diff --git a/svg-flatten/src/out_svg.cpp b/svg-flatten/src/out_svg.cpp index 66f0c94..80a5bdd 100644 --- a/svg-flatten/src/out_svg.cpp +++ b/svg-flatten/src/out_svg.cpp @@ -77,7 +77,7 @@ SimpleSVGOutput &SimpleSVGOutput::operator<<(const Polygon &poly) { return *this; } -SimpleSVGOutput &SimpleSVGOutput::operator<<(const DrillToken &) { +SimpleSVGOutput &SimpleSVGOutput::operator<<(const FlashToken &) { return *this; } diff --git a/svg-flatten/src/svg_doc.cpp b/svg-flatten/src/svg_doc.cpp index 7716a2e..20e11b2 100644 --- a/svg-flatten/src/svg_doc.cpp +++ b/svg-flatten/src/svg_doc.cpp @@ -285,7 +285,7 @@ void gerbolyze::SVGDocument::export_svg_path(RenderContext &ctx, const pugi::xml double diameter = sqrt(4*fabs(area)/M_PI) / clipper_scale; diameter = ctx.mat().doc2phys_dist(diameter); /* FIXME is this correct w.r.t. PolygonScaler? */ diameter = round(diameter * 1000.0) / 1000.0; /* Round to micrometer precsion; FIXME: make configurable */ - ctx.sink() << ApertureToken(diameter) << DrillToken(centroid); + ctx.sink() << ApertureToken(diameter) << FlashToken(centroid); } } return; @@ -346,6 +346,10 @@ void gerbolyze::SVGDocument::export_svg_path(RenderContext &ctx, const pugi::xml } if (has_stroke) { + Clipper stroke_clip; + stroke_clip.StrictlySimple(true); + stroke_clip.AddPaths(ctx.clip(), ptClip, /* closed */ true); + ClipperOffset offx; offx.ArcTolerance = 0.01 * clipper_scale; /* 10µm; TODO: Make this configurable */ offx.MiterLimit = stroke_miterlimit; @@ -359,10 +363,8 @@ void gerbolyze::SVGDocument::export_svg_path(RenderContext &ctx, const pugi::xml if (dasharray.empty()) { if (ctx.settings().outline_mode && stroke_color != GRB_PATTERN_FILL) { - /* In outline mode, manually close polys */ - poly.push_back(poly[0]); - ctx.sink() << ApertureToken() << poly; - + cerr << "add closed path of size " << poly.size() << endl; + stroke_clip.AddPath(poly, ptSubject, /* closed */ true); } else { offx.AddPath(poly, join_type, etClosedLine); } @@ -374,7 +376,8 @@ void gerbolyze::SVGDocument::export_svg_path(RenderContext &ctx, const pugi::xml dash_path(poly_copy, out, dasharray, stroke_dashoffset); if (ctx.settings().outline_mode && stroke_color != GRB_PATTERN_FILL) { - ctx.sink() << ApertureToken(stroke_width) << out; + cerr << "add open path dashes of size " << out.size() << endl; + stroke_clip.AddPaths(out, ptSubject, /* closed */ false); } else { offx.AddPaths(out, join_type, end_type); } @@ -386,47 +389,63 @@ void gerbolyze::SVGDocument::export_svg_path(RenderContext &ctx, const pugi::xml dash_path(poly, out, dasharray, stroke_dashoffset); if (ctx.settings().outline_mode && stroke_color != GRB_PATTERN_FILL) { - ctx.sink() << ApertureToken(stroke_width) << out; + cerr << "add open paths of size " << out.size() << endl; + stroke_clip.AddPaths(out, ptSubject, /* closed */ false); } else { offx.AddPaths(out, join_type, end_type); } } - /* Execute clipper offset operation to generate stroke outlines */ - offx.Execute(ptree, 0.5 * stroke_width * clipper_scale); + if (ctx.settings().outline_mode && stroke_color != GRB_PATTERN_FILL) { + stroke_clip.Execute(ctIntersection, ptree, pftNonZero, pftNonZero); + Paths outline_paths; + ctx.sink() << ApertureToken(stroke_width); + + ClosedPathsFromPolyTree(ptree, outline_paths); + for (auto &path : outline_paths) { + /* we have to connect the last and first point here */ + if (path.empty()) + continue; + path.push_back(path[0]); + ctx.sink() << path; + } - /* Clip. Note that after the outline, all we have is closed paths as any open path's stroke outline is itself - * a closed path. */ - if (!ctx.clip().empty()) { - Clipper c; + OpenPathsFromPolyTree(ptree, outline_paths); + ctx.sink() << outline_paths; - Paths outline_paths; - PolyTreeToPaths(ptree, outline_paths); - c.AddPaths(outline_paths, ptSubject, /* closed */ true); - c.AddPaths(ctx.clip(), ptClip, /* closed */ true); - c.StrictlySimple(true); - /* fill rules are nonzero since both subject and clip have already been normalized by clipper. */ - c.Execute(ctIntersection, ptree, pftNonZero, pftNonZero); - } + } else { + /* Execute clipper offset operation to generate stroke outlines */ + offx.Execute(ptree, 0.5 * stroke_width * clipper_scale); + + /* Clip. Note that (outside of outline mode) after the clipper outline operation, all we have is closed paths as + * any open path's stroke outline is itself a closed path. */ + if (!ctx.clip().empty()) { + Paths outline_paths; + PolyTreeToPaths(ptree, outline_paths); + stroke_clip.AddPaths(outline_paths, ptSubject, /* closed */ true); + /* fill rules are nonzero since both subject and clip have already been normalized by clipper. */ + stroke_clip.Execute(ctIntersection, ptree, pftNonZero, pftNonZero); + } - /* Call out to pattern tiler for pattern strokes. The stroke's outline becomes the clip here. */ - if (stroke_color == GRB_PATTERN_FILL) { - string stroke_pattern_id = usvg_id_url(node.attribute("stroke").value()); - Pattern *pattern = lookup_pattern(stroke_pattern_id); - if (!pattern) { - cerr << "Warning: Fill pattern with id \"" << stroke_pattern_id << "\" not found." << endl; + /* Call out to pattern tiler for pattern strokes. The stroke's outline becomes the clip here. */ + if (stroke_color == GRB_PATTERN_FILL) { + string stroke_pattern_id = usvg_id_url(node.attribute("stroke").value()); + Pattern *pattern = lookup_pattern(stroke_pattern_id); + if (!pattern) { + cerr << "Warning: Fill pattern with id \"" << stroke_pattern_id << "\" not found." << endl; + + } else { + Paths clip; + PolyTreeToPaths(ptree, clip); + RenderContext local_ctx(ctx, xform2d(), clip, true); + pattern->tile(local_ctx); + } } else { - Paths clip; - PolyTreeToPaths(ptree, clip); - RenderContext local_ctx(ctx, xform2d(), clip, true); - pattern->tile(local_ctx); + Paths s_polys; + dehole_polytree(ptree, s_polys); + ctx.sink() << (stroke_color == GRB_DARK ? GRB_POL_DARK : GRB_POL_CLEAR) << ApertureToken() << s_polys; } - - } else if (!ctx.settings().outline_mode) { - Paths s_polys; - dehole_polytree(ptree, s_polys); - ctx.sink() << (stroke_color == GRB_DARK ? GRB_POL_DARK : GRB_POL_CLEAR) << ApertureToken() << s_polys; } } } -- cgit