aboutsummaryrefslogtreecommitdiff
path: root/svg-flatten/src
diff options
context:
space:
mode:
authorjaseg <git@jaseg.de>2023-03-29 15:45:14 +0200
committerjaseg <git@jaseg.de>2023-03-29 15:45:14 +0200
commit602e51ca104381990e0bf9b90e0afc4bf8b86801 (patch)
treef7115af19f57fa98a126887441062ee669d79490 /svg-flatten/src
parentb4753e66e29af0b6087f591838069deb9689b78f (diff)
downloadgerbolyze-602e51ca104381990e0bf9b90e0afc4bf8b86801.tar.gz
gerbolyze-602e51ca104381990e0bf9b90e0afc4bf8b86801.tar.bz2
gerbolyze-602e51ca104381990e0bf9b90e0afc4bf8b86801.zip
svg_doc: Fix gerber mapping of strokes with skewed or non-uniform transforms
Diffstat (limited to 'svg-flatten/src')
-rw-r--r--svg-flatten/src/main.cpp7
-rw-r--r--svg-flatten/src/svg_doc.cpp39
2 files changed, 33 insertions, 13 deletions
diff --git a/svg-flatten/src/main.cpp b/svg-flatten/src/main.cpp
index 6ba32a9..122b75b 100644
--- a/svg-flatten/src/main.cpp
+++ b/svg-flatten/src/main.cpp
@@ -88,6 +88,9 @@ int main(int argc, char **argv) {
{"stroke_width_cutoff", {"--min-stroke-width"},
"Don't render strokes thinner than the given width in mm. Default: 0.01mm.",
1},
+ {"no_stroke_interpolation", {"--no-stroke-interpolation"},
+ "Always outline SVG strokes as regions instead of rendering them using Geber interpolation commands where possible.",
+ 0},
{"drill_test_polsby_popper_tolerance", {"--drill-test-tolerance"},
"Tolerance for identifying circles as drills in outline mode",
1},
@@ -316,7 +319,7 @@ int main(int argc, char **argv) {
delete vec;
double min_feature_size = args["min_feature_size"].as<double>(0.1); /* mm */
- double geometric_tolerance = args["geometric_tolerance"].as<double>(0.1); /* mm */
+ double geometric_tolerance = args["geometric_tolerance"].as<double>(0.01); /* mm */
double stroke_width_cutoff = args["stroke_width_cutoff"].as<double>(0.01); /* mm */
double drill_test_polsby_popper_tolerance = args["drill_test_polsby_popper_tolerance"].as<double>(0.1);
double aperture_rect_test_tolerance = args["aperture_rect_test_tolerance"].as<double>(0.1);
@@ -451,6 +454,7 @@ int main(int argc, char **argv) {
bool flip_svg_colors = args["flip_svg_color_interpretation"];
bool pattern_complete_tiles_only = args["pattern_complete_tiles_only"];
bool use_apertures_for_patterns = args["use_apertures_for_patterns"];
+ bool do_gerber_interpolation = !args["no_stroke_interpolation"];
RenderSettings rset {
min_feature_size,
@@ -464,6 +468,7 @@ int main(int argc, char **argv) {
flip_svg_colors,
pattern_complete_tiles_only,
use_apertures_for_patterns,
+ do_gerber_interpolation,
};
SVGDocument doc;
diff --git a/svg-flatten/src/svg_doc.cpp b/svg-flatten/src/svg_doc.cpp
index df6e67a..6cb6138 100644
--- a/svg-flatten/src/svg_doc.cpp
+++ b/svg-flatten/src/svg_doc.cpp
@@ -253,7 +253,7 @@ void gerbolyze::SVGDocument::export_svg_path(RenderContext &ctx, const pugi::xml
Paths stroke_open, stroke_closed;
PolyTree ptree_fill;
PolyTree ptree;
- double geometric_tolerance_px = ctx.mat().doc2phys_min(ctx.settings().geometric_tolerance_mm);
+ double geometric_tolerance_px = ctx.mat().phys2doc_min(ctx.settings().geometric_tolerance_mm);
load_svg_path(node, stroke_open, stroke_closed, ptree_fill, geometric_tolerance_px);
Paths fill_paths;
@@ -267,6 +267,7 @@ void gerbolyze::SVGDocument::export_svg_path(RenderContext &ctx, const pugi::xml
bool has_stroke = stroke_color && ctx.mat().doc2phys_min(stroke_width) > ctx.settings().stroke_width_cutoff;
cerr << "processing svg path" << endl;
+ cerr << " * " << (has_stroke ? "has stroke" : "no stroke") << " / " << (has_fill ? "has fill" : "no fill") << endl;
cerr << " * " << fill_paths.size() << " fill paths" << endl;
cerr << " * " << stroke_closed.size() << " closed strokes" << endl;
cerr << " * " << stroke_open.size() << " open strokes" << endl;
@@ -298,7 +299,8 @@ void gerbolyze::SVGDocument::export_svg_path(RenderContext &ctx, const pugi::xml
centroid[0] /= clipper_scale;
centroid[1] /= clipper_scale;
double diameter = sqrt(4*fabs(area)/M_PI) / clipper_scale;
- diameter = round(diameter * 1000.0) / 1000.0; /* Round to micrometer precsion; FIXME: make configurable */
+ double tolerance = ctx.settings().geometric_tolerance_mm / 2;
+ diameter = round(diameter/tolerance) * tolerance;
ctx.sink() << ApertureToken(diameter) << FlashToken(centroid);
}
}
@@ -315,10 +317,10 @@ void gerbolyze::SVGDocument::export_svg_path(RenderContext &ctx, const pugi::xml
c.AddPaths(ctx.clip(), ptClip, /* closed */ true);
c.StrictlySimple(true);
- cerr << "clipping " << fill_paths.size() << " paths, got polytree with " << ptree_fill.ChildCount() << " top-level children" << endl;
+ //cerr << "clipping " << fill_paths.size() << " paths, got polytree with " << ptree_fill.ChildCount() << " top-level children" << endl;
/* fill rules are nonzero since both subject and clip have already been normalized by clipper. */
c.Execute(ctIntersection, ptree_fill, pftNonZero, pftNonZero);
- cerr << " > " << ptree_fill.ChildCount() << " clipped fill ptree top-level children" << endl;
+ //cerr << " > " << ptree_fill.ChildCount() << " clipped fill ptree top-level children" << endl;
}
/* Call out to pattern tiler for pattern fills. The path becomes the clip here. */
@@ -399,8 +401,9 @@ void gerbolyze::SVGDocument::export_svg_path(RenderContext &ctx, const pugi::xml
if (stroke_color != GRB_PATTERN_FILL
&& ctx.sink().can_do_apertures()
+ && ctx.settings().do_gerber_interpolation
/* check if we have an uniform transform */
- && ctx.mat().doc2phys_skew(stroke_width) < ctx.settings().geometric_tolerance_mm) {
+ && ctx.mat().doc2phys_skew_ok(stroke_width, 0.05, ctx.settings().geometric_tolerance_mm)) {
// cerr << "Analyzing direct conversion of stroke" << endl;
// cerr << " stroke_closed.size() = " << stroke_closed.size() << endl;
// cerr << " stroke_open.size() = " << stroke_open.size() << endl;
@@ -411,7 +414,7 @@ void gerbolyze::SVGDocument::export_svg_path(RenderContext &ctx, const pugi::xml
offx.MiterLimit = 10;
offx.AddPaths(ctx.clip(), jtRound, etClosedPolygon);
PolyTree clip_ptree;
- offx.Execute(clip_ptree, -0.5 * stroke_width * clipper_scale);
+ offx.Execute(clip_ptree, -0.5 * ctx.mat().doc2phys_dist(stroke_width) * clipper_scale);
Paths dilated_clip;
ClosedPathsFromPolyTree(clip_ptree, dilated_clip);
@@ -426,7 +429,7 @@ void gerbolyze::SVGDocument::export_svg_path(RenderContext &ctx, const pugi::xml
stroke_clip.AddPaths(stroke_closed_phys, ptSubject, /* closed */ true);
stroke_clip.AddPaths(stroke_open_phys, ptSubject, /* closed */ false);
stroke_clip.Execute(ctDifference, ptree, pftNonZero, pftNonZero);
- cerr << " > " << ptree.ChildCount() << " clipped stroke ptree top-level children" << endl;
+ // cerr << " > " << ptree.ChildCount() << " clipped stroke ptree top-level children" << endl;
/* Did any part of the path clip the clip path (which defaults to the document border)? */
bool nothing_clipped = ptree.Total() == 0;
@@ -445,14 +448,18 @@ void gerbolyze::SVGDocument::export_svg_path(RenderContext &ctx, const pugi::xml
bool ends_can_be_mapped = (end_type == ClipperLib::etOpenRound) || (stroke_open.size() == 0);
/* Can gerber losslessly express this path? */
bool gerber_lossless = nothing_clipped && ends_can_be_mapped && joins_can_be_mapped;
+ //cerr << " ends_can_be_mapped=" << ends_can_be_mapped << ", nothing_clipped=" << nothing_clipped << ", joins_can_be_mapped=" << joins_can_be_mapped << endl;
// cerr << " nothing_clipped = " << nothing_clipped << endl;
// cerr << " ends_can_be_mapped = " << ends_can_be_mapped << endl;
// cerr << " joins_can_be_mapped = " << joins_can_be_mapped << endl;
/* Accept loss of precision in outline mode. */
if (ctx.settings().outline_mode || gerber_lossless) {
- // cerr << " -> converting directly" << endl;
- ctx.sink() << ApertureToken(stroke_width);
+ //cerr << " -> converting directly" << endl;
+ ctx.mat().doc2phys_clipper(stroke_closed);
+ ctx.mat().doc2phys_clipper(stroke_open);
+
+ ctx.sink() << ApertureToken(ctx.mat().doc2phys_dist(stroke_width));
for (auto &path : stroke_closed) {
if (path.empty()) {
continue;
@@ -464,7 +471,7 @@ void gerbolyze::SVGDocument::export_svg_path(RenderContext &ctx, const pugi::xml
ctx.sink() << stroke_open;
return;
}
- // cerr << " -> NOT converting directly" << endl;
+ //cerr << " -> NOT converting directly" << endl;
/* else fall through to normal processing */
}
@@ -472,7 +479,12 @@ void gerbolyze::SVGDocument::export_svg_path(RenderContext &ctx, const pugi::xml
offx.ArcTolerance = ctx.mat().phys2doc_min(ctx.settings().geometric_tolerance_mm) * clipper_scale;
offx.MiterLimit = stroke_miterlimit;
- //cerr << "offsetting " << stroke_closed.size() << " closed and " << stroke_open.size() << " open paths" << endl;
+ //cerr << " offsetting " << stroke_closed.size() << " closed and " << stroke_open.size() << " open paths" << endl;
+ //cerr << " geometric tolerance = " << ctx.settings().geometric_tolerance_mm << " mm" << endl;
+ //cerr << " arc tolerance = " << offx.ArcTolerance/clipper_scale << " px" << endl;
+ //cerr << " stroke_width=" << stroke_width << "px" << endl;
+ //cerr << " offset = " << (0.5 * stroke_width * clipper_scale) << endl;
+
/* For stroking we have to separately handle open and closed paths since coincident start and end points may
* render differently than joined start and end points. */
offx.AddPaths(stroke_closed, join_type, etClosedLine);
@@ -483,6 +495,7 @@ void gerbolyze::SVGDocument::export_svg_path(RenderContext &ctx, const pugi::xml
/* 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()) {
+ //cerr << " Clipping polytree" << endl;
Paths outline_paths;
PolyTreeToPaths(ptree, outline_paths);
@@ -518,6 +531,7 @@ void gerbolyze::SVGDocument::export_svg_path(RenderContext &ctx, const pugi::xml
dehole_polytree(ptree, s_polys);
ctx.mat().doc2phys_clipper(s_polys);
/* color has alredy been pushed above. */
+ //cerr << " sinking " << s_polys.size() << " paths" << endl;
ctx.sink() << ApertureToken() << s_polys;
}
}
@@ -590,10 +604,11 @@ void gerbolyze::SVGDocument::load_clips(const RenderSettings &rset) {
xform2d child_xf(local_xf);
child_xf.transform(xform2d(child.attribute("transform").value()));
- load_svg_path(child_xf, child, _stroke_open, _stroke_closed, ptree_fill, rset.geometric_tolerance_mm);
+ load_svg_path(child, _stroke_open, _stroke_closed, ptree_fill, rset.geometric_tolerance_mm);
Paths paths;
PolyTreeToPaths(ptree_fill, paths);
+ child_xf.doc2phys_clipper(paths);
c.AddPaths(paths, ptSubject, /* closed */ false);
}