diff options
-rw-r--r-- | include/gerbolyze.hpp | 27 | ||||
-rw-r--r-- | src/main.cpp | 11 | ||||
-rw-r--r-- | src/svg_doc.cpp | 31 | ||||
-rw-r--r-- | src/svg_pattern.cpp | 4 | ||||
-rw-r--r-- | src/svg_pattern.h | 5 | ||||
-rw-r--r-- | src/vec_core.cpp | 9 | ||||
-rw-r--r-- | src/vec_core.h | 11 | ||||
-rw-r--r-- | src/vec_grid.cpp | 2 | ||||
-rw-r--r-- | src/vec_grid.h | 2 |
9 files changed, 68 insertions, 34 deletions
diff --git a/include/gerbolyze.hpp b/include/gerbolyze.hpp index b2634f0..3078a6f 100644 --- a/include/gerbolyze.hpp +++ b/include/gerbolyze.hpp @@ -29,6 +29,7 @@ namespace gerbolyze { constexpr char lib_version[] = "2.0"; typedef std::array<double, 2> d2p; + typedef std::function<std::vector<d2p> *(double, double, double)> sampling_fun; typedef std::vector<d2p> Polygon; enum GerberPolarityToken { @@ -79,12 +80,28 @@ namespace gerbolyze { class ElementSelector { public: - bool match(const pugi::xml_node &node, bool included) const; + virtual bool match(const pugi::xml_node &node, bool included) const = 0; + }; + + class IDElementSelector : public ElementSelector { + public: + virtual bool match(const pugi::xml_node &node, bool included) const; std::vector<std::string> include; std::vector<std::string> exclude; }; + class ImageVectorizer { + public: + virtual void vectorize_image(cairo_t *cr, const pugi::xml_node &node, ClipperLib::Paths &clip_path, cairo_matrix_t &viewport_matrix, PolygonSink &sink, double min_feature_size_px) = 0; + }; + + class RenderSettings { + public: + double m_minimum_feature_size_mm = 0.1; + ImageVectorizer *m_vec = nullptr; + }; + class SVGDocument { public: SVGDocument() : _valid(false) {} @@ -103,8 +120,8 @@ namespace gerbolyze { double width() const { return page_w_mm; } double height() const { return page_h_mm; } - void render(PolygonSink &sink, const ElementSelector *sel=nullptr); - void render_to_list(std::vector<std::pair<Polygon, GerberPolarityToken>> &out, const ElementSelector *sel=nullptr); + void render(const RenderSettings &rset, PolygonSink &sink, const ElementSelector *sel=nullptr); + void render_to_list(const RenderSettings &rset, std::vector<std::pair<Polygon, GerberPolarityToken>> &out, const ElementSelector *sel=nullptr); private: friend class Pattern; @@ -113,8 +130,8 @@ namespace gerbolyze { const ClipperLib::Paths *lookup_clip_path(const pugi::xml_node &node); Pattern *lookup_pattern(const std::string id); - void export_svg_group(const pugi::xml_node &group, ClipperLib::Paths &parent_clip_path, const ElementSelector *sel=nullptr, bool included=true); - void export_svg_path(const pugi::xml_node &node, ClipperLib::Paths &clip_path); + void export_svg_group(const RenderSettings &rset, const pugi::xml_node &group, ClipperLib::Paths &parent_clip_path, const ElementSelector *sel=nullptr, bool included=true); + void export_svg_path(const RenderSettings &rset, const pugi::xml_node &node, ClipperLib::Paths &clip_path); void setup_debug_output(std::string filename=""); void setup_viewport_clip(); void load_clips(); diff --git a/src/main.cpp b/src/main.cpp index 889fe6a..ba75204 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,6 +6,7 @@ #include <string> #include <argagg.hpp> #include <gerbolyze.hpp> +#include "vec_core.h" using argagg::parser_results; using argagg::parser; @@ -158,13 +159,19 @@ int main(int argc, char **argv) { out.pop_back(); }; - ElementSelector sel; + IDElementSelector sel; if (args["only_groups"]) id_match(args["only_groups"], sel.include); if (args["exclude_groups"]) id_match(args["exclude_groups"], sel.exclude); - doc.render(flattener ? *flattener : *sink, &sel); + VoronoiVectorizer vec(POISSON_DISC, true); + RenderSettings rset { + 0.1, + &vec + }; + + doc.render(rset, flattener ? *flattener : *sink, &sel); return EXIT_SUCCESS; } diff --git a/src/svg_doc.cpp b/src/svg_doc.cpp index 196cf60..5b36f35 100644 --- a/src/svg_doc.cpp +++ b/src/svg_doc.cpp @@ -19,6 +19,7 @@ #include <iostream> #include <fstream> +#include <gerbolyze.hpp> #include "svg_import_defs.h" #include "svg_color.h" #include "svg_geom.h" @@ -116,7 +117,7 @@ double gerbolyze::SVGDocument::doc_units_to_mm(double px) const { return px / (vb_w / page_w_mm); } -bool ElementSelector::match(const pugi::xml_node &node, bool included) const { +bool IDElementSelector::match(const pugi::xml_node &node, bool included) const { if (include.empty() && exclude.empty()) return true; @@ -131,7 +132,7 @@ bool ElementSelector::match(const pugi::xml_node &node, bool included) const { } /* Recursively export all SVG elements in the given group. */ -void gerbolyze::SVGDocument::export_svg_group(const pugi::xml_node &group, Paths &parent_clip_path, const ElementSelector *sel, bool included) { +void gerbolyze::SVGDocument::export_svg_group(const RenderSettings &rset, const pugi::xml_node &group, Paths &parent_clip_path, const ElementSelector *sel, bool included) { /* Enter the group's coordinate system */ cairo_save(cr); apply_cairo_transform_from_svg(cr, group.attribute("transform").value()); @@ -166,15 +167,17 @@ void gerbolyze::SVGDocument::export_svg_group(const pugi::xml_node &group, Paths string name(node.name()); if (name == "g") { - export_svg_group(node, clip_path, sel, true); + export_svg_group(rset, node, clip_path, sel, true); } else if (name == "path") { - export_svg_path(node, clip_path); + export_svg_path(rset, node, clip_path); } else if (name == "image") { - double min_feature_size_mm = 0.1; /* TODO make configurable */ - double min_feature_size_px = mm_to_doc_units(min_feature_size_mm); - vectorize_image(cr, node, min_feature_size_px, clip_path, viewport_matrix, *polygon_sink); + if (!rset.m_vec) + continue; + + double min_feature_size_px = mm_to_doc_units(rset.m_minimum_feature_size_mm); + rset.m_vec->vectorize_image(cr, node, clip_path, viewport_matrix, *polygon_sink, min_feature_size_px); } else if (name == "defs") { /* ignore */ } else { @@ -186,7 +189,7 @@ void gerbolyze::SVGDocument::export_svg_group(const pugi::xml_node &group, Paths } /* Export an SVG path element to gerber. Apply patterns and clip on the fly. */ -void gerbolyze::SVGDocument::export_svg_path(const pugi::xml_node &node, Paths &clip_path) { +void gerbolyze::SVGDocument::export_svg_path(const RenderSettings &rset, const pugi::xml_node &node, Paths &clip_path) { enum gerber_color fill_color = gerber_fill_color(node); enum gerber_color stroke_color = gerber_stroke_color(node); @@ -236,7 +239,7 @@ void gerbolyze::SVGDocument::export_svg_path(const pugi::xml_node &node, Paths & } else { Paths clip; PolyTreeToPaths(ptree, clip); - pattern->tile(clip); + pattern->tile(rset, clip); } } else { /* solid fill */ @@ -325,7 +328,7 @@ void gerbolyze::SVGDocument::export_svg_path(const pugi::xml_node &node, Paths & } else { Paths clip; PolyTreeToPaths(ptree, clip); - pattern->tile(clip); + pattern->tile(rset, clip); } } else { @@ -359,7 +362,7 @@ void gerbolyze::SVGDocument::export_svg_path(const pugi::xml_node &node, Paths & } } -void gerbolyze::SVGDocument::render(PolygonSink &sink, const ElementSelector *sel) { +void gerbolyze::SVGDocument::render(const RenderSettings &rset, PolygonSink &sink, const ElementSelector *sel) { assert(_valid); /* Export the actual SVG document to both SVG for debuggin and to gerber. We do this as we go, i.e. we immediately * process each element to gerber as we encounter it instead of first rendering everything to a giant list of gerber @@ -371,15 +374,15 @@ void gerbolyze::SVGDocument::render(PolygonSink &sink, const ElementSelector *se c.AddPaths(vb_paths, ptSubject, /* closed */ true); ClipperLib::IntRect bbox = c.GetBounds(); cerr << "document viewbox clip: bbox={" << bbox.left << ", " << bbox.top << "} - {" << bbox.right << ", " << bbox.bottom << "}" << endl; - export_svg_group(root_elem, vb_paths, sel, false); + export_svg_group(rset, root_elem, vb_paths, sel, false); sink.footer(); } -void gerbolyze::SVGDocument::render_to_list(vector<pair<Polygon, GerberPolarityToken>> &out, const ElementSelector *sel) { +void gerbolyze::SVGDocument::render_to_list(const RenderSettings &rset, vector<pair<Polygon, GerberPolarityToken>> &out, const ElementSelector *sel) { LambdaPolygonSink sink([&out](const Polygon &poly, GerberPolarityToken pol) { out.emplace_back(pair<Polygon, GerberPolarityToken>{poly, pol}); }); - render(sink, sel); + render(rset, sink, sel); } void gerbolyze::SVGDocument::setup_debug_output(string filename) { diff --git a/src/svg_pattern.cpp b/src/svg_pattern.cpp index b7eec2e..b62df28 100644 --- a/src/svg_pattern.cpp +++ b/src/svg_pattern.cpp @@ -47,7 +47,7 @@ gerbolyze::Pattern::Pattern(const pugi::xml_node &node, SVGDocument &doc) : _nod /* Tile pattern into gerber. Note that this function may be called several times in case the pattern is * referenced from multiple places, so we must not clobber any of the object's state. */ -void gerbolyze::Pattern::tile (ClipperLib::Paths &clip) { +void gerbolyze::Pattern::tile (const gerbolyze::RenderSettings &rset, ClipperLib::Paths &clip) { assert(doc); cairo_t *cr = doc->cairo(); assert(cr); @@ -104,7 +104,7 @@ void gerbolyze::Pattern::tile (ClipperLib::Paths &clip) { } /* Export the pattern tile's content like a group */ - doc->export_svg_group(_node, clip); + doc->export_svg_group(rset, _node, clip); cairo_restore(cr); } } diff --git a/src/svg_pattern.h b/src/svg_pattern.h index 3780d79..e6a6fe2 100644 --- a/src/svg_pattern.h +++ b/src/svg_pattern.h @@ -20,22 +20,21 @@ #include <string> #include <cairo.h> - #include <pugixml.hpp> #include <clipper.hpp> - #include "svg_import_util.h" namespace gerbolyze { class SVGDocument; +class RenderSettings; class Pattern { public: Pattern() {} Pattern(const pugi::xml_node &node, SVGDocument &doc); - void tile (ClipperLib::Paths &clip); + void tile (const gerbolyze::RenderSettings &rset, ClipperLib::Paths &clip); private: double x, y, w, h; diff --git a/src/vec_core.cpp b/src/vec_core.cpp index d329722..85b33b0 100644 --- a/src/vec_core.cpp +++ b/src/vec_core.cpp @@ -78,7 +78,7 @@ static void voronoi_relax_points(const jcv_diagram* diagram, jcv_point* points) * 4. It scales each of these voronoi cell polygons to match the input images brightness at the spot covered by this * cell. */ -void gerbolyze::vectorize_image(cairo_t *cr, const pugi::xml_node &node, double min_feature_size_px, ClipperLib::Paths &clip_path, cairo_matrix_t &viewport_matrix, PolygonSink &sink) { +void gerbolyze::VoronoiVectorizer::vectorize_image(cairo_t *cr, const pugi::xml_node &node, ClipperLib::Paths &clip_path, cairo_matrix_t &viewport_matrix, PolygonSink &sink, double min_feature_size_px) { /* Read XML node attributes */ auto x = usvg_double_attr(node, "x", 0.0); auto y = usvg_double_attr(node, "y", 0.0); @@ -167,8 +167,8 @@ void gerbolyze::vectorize_image(cairo_t *cr, const pugi::xml_node &node, double grayscale interpolation. Larger values -> better grayscale resolution, larger cells. */ double center_distance = min_feature_size_px * 2.0 * (1.0 / (1.0-grayscale_overhead)); - vector<d2p> *grid_centers = sample_poisson_disc(width, height, min_feature_size_px * 2.0 * 2.0); - /* TODO make these alternative grids available to callers */ + vector<d2p> *grid_centers = get_sampler(m_grid_type)(width, height, center_distance); + //vector<d2p> *grid_centers = sample_poisson_disc(width, height, min_feature_size_px * 2.0 * 2.0); //vector<d2p> *grid_centers = sample_hexgrid(width, height, center_distance); //vector<d2p> *grid_centers = sample_squaregrid(width, height, center_distance); @@ -197,7 +197,8 @@ void gerbolyze::vectorize_image(cairo_t *cr, const pugi::xml_node &node, double jcv_point *pts = reinterpret_cast<jcv_point *>(grid_centers->data()); /* hackety hack */ jcv_diagram_generate(grid_centers->size(), pts, &rect, 0, &diagram); /* Relax points, i.e. wiggle them around a little bit to equalize differences between cell sizes a little bit. */ - voronoi_relax_points(&diagram, pts); + if (m_relax) + voronoi_relax_points(&diagram, pts); memset(&diagram, 0, sizeof(jcv_diagram)); jcv_diagram_generate(grid_centers->size(), pts, &rect, 0, &diagram); diff --git a/src/vec_core.h b/src/vec_core.h index 643efc9..9df8598 100644 --- a/src/vec_core.h +++ b/src/vec_core.h @@ -25,6 +25,15 @@ #include "vec_grid.h" namespace gerbolyze { - void vectorize_image(cairo_t *cr, const pugi::xml_node &node, double min_feature_size_px, ClipperLib::Paths &clip_path, cairo_matrix_t &viewport_matrix, PolygonSink &sink); + + class VoronoiVectorizer : public ImageVectorizer { + public: + VoronoiVectorizer(grid_type grid, bool relax=true) : m_relax(relax), m_grid_type(grid) {} + + void vectorize_image(cairo_t *cr, const pugi::xml_node &node, ClipperLib::Paths &clip_path, cairo_matrix_t &viewport_matrix, PolygonSink &sink, double min_feature_size_px); + private: + double m_relax; + grid_type m_grid_type; + }; } diff --git a/src/vec_grid.cpp b/src/vec_grid.cpp index 2a61153..7c27543 100644 --- a/src/vec_grid.cpp +++ b/src/vec_grid.cpp @@ -39,7 +39,7 @@ sampling_fun gerbolyze::get_sampler(enum grid_type type) { vector<d2p> *gerbolyze::sample_poisson_disc(double w, double h, double center_distance) { d2p top_left {0, 0}; d2p bottom_right {w, h}; - return new auto(thinks::PoissonDiskSampling(center_distance, top_left, bottom_right)); + return new auto(thinks::PoissonDiskSampling(center_distance/2.5, top_left, bottom_right)); } vector<d2p> *gerbolyze::sample_hexgrid(double w, double h, double center_distance) { diff --git a/src/vec_grid.h b/src/vec_grid.h index b574484..f2ed55a 100644 --- a/src/vec_grid.h +++ b/src/vec_grid.h @@ -31,8 +31,6 @@ enum grid_type { SQUAREGRID }; -typedef std::function<std::vector<d2p> *(double, double, double)> sampling_fun; - sampling_fun get_sampler(enum grid_type type); std::vector<d2p> *sample_poisson_disc(double w, double h, double center_distance); |