From 3cee5d4f018be6d4f494fb2ab1018fbf53b2f15f Mon Sep 17 00:00:00 2001 From: jaseg Date: Tue, 26 Jan 2021 23:21:31 +0100 Subject: The cavalier flattener works!!1! --- src/main.cpp | 10 +++-- src/out_flattener.cpp | 115 ++++++++++++++++++++++++++++++++++++++------------ src/svg_geom.cpp | 31 ++++++++------ src/svg_geom.h | 2 +- 4 files changed, 114 insertions(+), 44 deletions(-) (limited to 'src') diff --git a/src/main.cpp b/src/main.cpp index 8656662..773b1a4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -35,7 +35,7 @@ int main(int argc, char **argv) { {"no_header", {"--no-header"}, "Do not export output format header/footer, only export the primitives themselves", 0}, - {"flatten", {"-f", "--flatten"}, + {"flatten", {"--flatten"}, "Flatten output so it only consists of non-overlapping white polygons. This perform composition at the vector level. Potentially slow.", 0}, }}; @@ -122,7 +122,8 @@ int main(int argc, char **argv) { string fmt = args["ofmt"] ? args["ofmt"] : "gerber"; transform(fmt.begin(), fmt.end(), fmt.begin(), [](unsigned char c){ return std::tolower(c); }); - PolygonSink *sink; + PolygonSink *sink = nullptr; + PolygonSink *flattener = nullptr; if (fmt == "svg") { string dark_color = args["svg_dark_color"] ? args["svg_dark_color"] : "#000000"; string clear_color = args["svg_clear_color"] ? args["svg_clear_color"] : "#ffffff"; @@ -138,10 +139,11 @@ int main(int argc, char **argv) { return EXIT_FAILURE; } - if (args["version"]) { + if (args["flatten"]) { + flattener = new Flattener(*sink); } - doc.render(*sink); + doc.render(flattener ? *flattener : *sink); return EXIT_SUCCESS; } diff --git a/src/out_flattener.cpp b/src/out_flattener.cpp index edb93b7..9bc4cbe 100644 --- a/src/out_flattener.cpp +++ b/src/out_flattener.cpp @@ -21,24 +21,40 @@ #include #include #include -#include #include #include #include +#include "polylinecombine.hpp" using namespace gerbolyze; using namespace std; +static void polygon_to_cavc (const Polygon &in, cavc::Polyline &out) { + for (auto &p : in) { + out.addVertex(p[0], p[1], 0); + } + out.isClosed() = true; /* sic! */ +} + +static void cavc_to_polygon (const cavc::Polyline &in, Polygon &out) { + for (auto &p : in.vertexes()) { + out.emplace_back(d2p{p.x(), p.y()}); + } +} + namespace gerbolyze { class Flattener_D { public: - ClipperLib::Clipper c; + vector> dark_polys; + + void add_dark_polygon(const Polygon &in) { + polygon_to_cavc(in, dark_polys.emplace_back()); + } }; } Flattener::Flattener(PolygonSink &sink) : m_sink(sink) { d = new Flattener_D(); - d->c.StrictlySimple(true); } Flattener::~Flattener() { @@ -49,6 +65,8 @@ void Flattener::header(d2p origin, d2p size) { m_sink.header(origin, size); } + + Flattener &Flattener::operator<<(GerberPolarityToken pol) { if (m_current_polarity != pol) { m_current_polarity = pol; @@ -58,42 +76,85 @@ Flattener &Flattener::operator<<(GerberPolarityToken pol) { } Flattener &Flattener::operator<<(const Polygon &poly) { - ClipperLib::Path le_path; - for (auto &p : poly) { - le_path.push_back({(ClipperLib::cInt)round(p[0] * clipper_scale), (ClipperLib::cInt)round(p[1] * clipper_scale)}); - } - - ClipperLib::Paths out; + static int i=0, j=0; if (m_current_polarity == GRB_POL_DARK) { - d->c.AddPath(le_path, ClipperLib::ptSubject, true); - d->c.Execute(ClipperLib::ctUnion, out, ClipperLib::pftNonZero); - + d->add_dark_polygon(poly); + cerr << "dark primitive " << i++ << endl; } else { /* clear */ - d->c.AddPath(le_path, ClipperLib::ptClip, true); - d->c.Execute(ClipperLib::ctDifference, out, ClipperLib::pftNonZero); - } + cerr << "clear primitive " << j++ << endl; + + cavc::Polyline sub; + polygon_to_cavc (poly, sub); + + vector> new_dark_polys; + new_dark_polys.reserve(d->dark_polys.size()); + + for (cavc::Polyline cavc_in : d->dark_polys) { + auto res = cavc::combinePolylines(cavc_in, sub, cavc::PlineCombineMode::Exclude); + + if (res.subtracted.size() == 0) { + for (auto &rem : res.remaining) { + new_dark_polys.push_back(std::move(rem)); + } + + } else { + assert (res.remaining.size() == 1); + assert (res.subtracted.size() == 1); + + auto &rem = res.remaining[0]; + auto &sub = res.subtracted[0]; + auto bbox = getExtents(rem); + + cavc::Polyline quad; + quad.addVertex(bbox.xMin, bbox.yMin, 0); + if (sub.vertexes()[0].x() < sub.vertexes()[1].x()) { + quad.addVertex(sub.vertexes()[0]); + quad.addVertex(sub.vertexes()[1]); + } else { + quad.addVertex(sub.vertexes()[1]); + quad.addVertex(sub.vertexes()[0]); + } + quad.addVertex(bbox.xMax, bbox.yMin, 0); + quad.isClosed() = true; /* sic! */ + + auto res2 = cavc::combinePolylines(rem, quad, cavc::PlineCombineMode::Exclude); + assert (res2.subtracted.size() == 0); + + for (auto &rem : res2.remaining) { + auto res3 = cavc::combinePolylines(rem, sub, cavc::PlineCombineMode::Exclude); + assert (res3.subtracted.size() == 0); + for (auto &p : res3.remaining) { + new_dark_polys.push_back(std::move(p)); + } + } + + auto res4 = cavc::combinePolylines(rem, quad, cavc::PlineCombineMode::Intersect); + assert (res4.subtracted.size() == 0); + + for (auto &rem : res4.remaining) { + auto res5 = cavc::combinePolylines(rem, sub, cavc::PlineCombineMode::Exclude); + assert (res5.subtracted.size() == 0); + for (auto &p : res5.remaining) { + new_dark_polys.push_back(std::move(p)); + } + } + } + } - d->c.Clear(); - d->c.AddPaths(out, ClipperLib::ptSubject, true); + d->dark_polys = std::move(new_dark_polys); + } return *this; } void Flattener::footer() { - ClipperLib::PolyTree t_out; - d->c.Execute(ClipperLib::ctDifference, t_out, ClipperLib::pftNonZero); - d->c.Clear(); - m_sink << GRB_POL_DARK; - ClipperLib::Paths out; - cerr << "deholing" << endl; - dehole_polytree(t_out, out); - for (auto &poly : out) { + for (auto &poly : d->dark_polys) { Polygon poly_out; - for (auto &p : poly) { - poly_out.push_back({p.X / clipper_scale, p.Y / clipper_scale}); + for (auto &p : poly.vertexes()) { + poly_out.emplace_back(d2p{p.x(), p.y()}); } m_sink << poly_out; } diff --git a/src/svg_geom.cpp b/src/svg_geom.cpp index e152f3a..385e848 100644 --- a/src/svg_geom.cpp +++ b/src/svg_geom.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include "svg_import_defs.h" @@ -90,11 +91,7 @@ enum ClipperLib::JoinType gerbolyze::clipper_join_type(const pugi::xml_node &nod return ClipperLib::jtMiter; } -/* Take a Clipper polytree, i.e. a description of a set of polygons, their holes and their inner polygons, and remove - * all holes from it. We remove holes by splitting each polygon that has a hole into two or more pieces so that the hole - * is no more. These pieces perfectly fit each other so there is no visual or functional difference. - */ -void gerbolyze::dehole_polytree(PolyNode &ptree, Paths &out) { +static void dehole_polytree_worker(PolyNode &ptree, Paths &out, queue &todo) { for (int i=0; iIsHole()); if (child->ChildCount() > 0) { - dehole_polytree(*child, out); + dehole_polytree_worker(*child, out, todo); } } @@ -129,19 +126,29 @@ void gerbolyze::dehole_polytree(PolyNode &ptree, Paths &out) { Path tri = { { bbox.left, bbox.top }, nod->Childs[0]->Contour[0], nod->Childs[0]->Contour[1], { bbox.right, bbox.top } }; c.AddPath(tri, ptClip, true); - PolyTree solution; c.StrictlySimple(true); /* Execute twice, once for intersection fragment and once for difference fragment. Note that this will yield * at least two, but possibly more polygons. */ - c.Execute(ctDifference, solution, pftNonZero); - dehole_polytree(solution, out); - - c.Execute(ctIntersection, solution, pftNonZero); - dehole_polytree(solution, out); + c.Execute(ctDifference, todo.emplace(), pftNonZero); + c.Execute(ctIntersection, todo.emplace(), pftNonZero); } } } +/* Take a Clipper polytree, i.e. a description of a set of polygons, their holes and their inner polygons, and remove + * all holes from it. We remove holes by splitting each polygon that has a hole into two or more pieces so that the hole + * is no more. These pieces perfectly fit each other so there is no visual or functional difference. + */ +void gerbolyze::dehole_polytree(PolyTree &ptree, Paths &out) { + queue todo; + dehole_polytree_worker(ptree, out, todo); + while (!todo.empty()) { + dehole_polytree_worker(todo.front(), out, todo); + todo.pop(); + } +} + + /* Intersect two clip paths. Both must share a coordinate system. */ void gerbolyze::combine_clip_paths(Paths &in_a, Paths &in_b, Paths &out) { Clipper c; diff --git a/src/svg_geom.h b/src/svg_geom.h index 1763d52..5f00479 100644 --- a/src/svg_geom.h +++ b/src/svg_geom.h @@ -28,7 +28,7 @@ ClipperLib::IntRect get_paths_bounds(const ClipperLib::Paths &paths); enum ClipperLib::PolyFillType clipper_fill_rule(const pugi::xml_node &node); enum ClipperLib::EndType clipper_end_type(const pugi::xml_node &node); enum ClipperLib::JoinType clipper_join_type(const pugi::xml_node &node); -void dehole_polytree(ClipperLib::PolyNode &ptree, ClipperLib::Paths &out); +void dehole_polytree(ClipperLib::PolyTree &ptree, ClipperLib::Paths &out); void combine_clip_paths(ClipperLib::Paths &in_a, ClipperLib::Paths &in_b, ClipperLib::Paths &out); void transform_paths(cairo_t *cr, ClipperLib::Paths &paths, cairo_matrix_t *mat=nullptr); -- cgit