Collect various rendering params into new RenderContext object
This commit is contained in:
parent
aad0ae0215
commit
aaade1b168
10 changed files with 225 additions and 169 deletions
|
|
@ -110,13 +110,13 @@ namespace gerbolyze {
|
|||
GerberPolarityToken m_current_polarity = GRB_POL_DARK;
|
||||
};
|
||||
|
||||
class Scaler : public PolygonSink {
|
||||
class PolygonScaler : public PolygonSink {
|
||||
public:
|
||||
Scaler(PolygonSink &sink, double scale=1.0) : m_sink(sink), m_scale(scale) {}
|
||||
PolygonScaler(PolygonSink &sink, double scale=1.0) : m_sink(sink), m_scale(scale) {}
|
||||
virtual void header(d2p origin, d2p size);
|
||||
virtual Scaler &operator<<(const Polygon &poly);
|
||||
virtual Scaler &operator<<(const LayerNameToken &layer_name);
|
||||
virtual Scaler &operator<<(GerberPolarityToken pol);
|
||||
virtual PolygonScaler &operator<<(const Polygon &poly);
|
||||
virtual PolygonScaler &operator<<(const LayerNameToken &layer_name);
|
||||
virtual PolygonScaler &operator<<(GerberPolarityToken pol);
|
||||
virtual void footer();
|
||||
|
||||
private:
|
||||
|
|
@ -143,7 +143,10 @@ namespace gerbolyze {
|
|||
|
||||
class ElementSelector {
|
||||
public:
|
||||
virtual bool match(const pugi::xml_node &node, bool included, bool is_root) const = 0;
|
||||
virtual bool match(const pugi::xml_node &node, bool included, bool is_root) const {
|
||||
(void) node, (void) included, (void) is_root;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class IDElementSelector : public ElementSelector {
|
||||
|
|
@ -158,7 +161,7 @@ namespace gerbolyze {
|
|||
class ImageVectorizer {
|
||||
public:
|
||||
virtual ~ImageVectorizer() {};
|
||||
virtual void vectorize_image(xform2d &mat, const pugi::xml_node &node, ClipperLib::Paths &clip_path, PolygonSink &sink, double min_feature_size_px) = 0;
|
||||
virtual void vectorize_image(RenderContext &ctx, const pugi::xml_node &node, double min_feature_size_px) = 0;
|
||||
};
|
||||
|
||||
ImageVectorizer *makeVectorizer(const std::string &name);
|
||||
|
|
@ -183,6 +186,42 @@ namespace gerbolyze {
|
|||
bool flip_color_interpretation = false;
|
||||
};
|
||||
|
||||
class RenderContext {
|
||||
public:
|
||||
RenderContext(const RenderSettings &settings,
|
||||
PolygonSink &sink,
|
||||
const ElementSelector &sel,
|
||||
ClipperLib::Paths &clip);
|
||||
RenderContext(RenderContext &parent,
|
||||
xform2d transform);
|
||||
RenderContext(RenderContext &parent,
|
||||
xform2d transform,
|
||||
ClipperLib::Paths &clip);
|
||||
|
||||
PolygonSink &sink() { return m_sink; }
|
||||
const ElementSelector &sel() { return m_sel; }
|
||||
const RenderSettings &settings() { return m_settings; }
|
||||
xform2d &mat() { return m_mat; }
|
||||
bool root() const { return m_root; }
|
||||
bool included() const { return m_included; }
|
||||
ClipperLib::Paths &clip() { return m_clip; }
|
||||
void transform(xform2d &transform) {
|
||||
m_mat.transform(transform);
|
||||
}
|
||||
bool match(const pugi::xml_node &node) {
|
||||
return m_sel.match(node, m_included, m_root);
|
||||
}
|
||||
|
||||
private:
|
||||
PolygonSink &m_sink;
|
||||
const RenderSettings &m_settings;
|
||||
xform2d m_mat;
|
||||
bool m_root;
|
||||
bool m_included; /* TODO: refactor name */
|
||||
const ElementSelector &m_sel;
|
||||
ClipperLib::Paths &m_clip;
|
||||
};
|
||||
|
||||
class SVGDocument {
|
||||
public:
|
||||
SVGDocument() : _valid(false) {}
|
||||
|
|
@ -200,8 +239,8 @@ namespace gerbolyze {
|
|||
double width() const { return page_w_mm; }
|
||||
double height() const { return page_h_mm; }
|
||||
|
||||
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);
|
||||
void render(const RenderSettings &rset, PolygonSink &sink, const ElementSelector &sel=ElementSelector());
|
||||
void render_to_list(const RenderSettings &rset, std::vector<std::pair<Polygon, GerberPolarityToken>> &out, const ElementSelector &sel=ElementSelector());
|
||||
|
||||
private:
|
||||
friend class Pattern;
|
||||
|
|
@ -209,8 +248,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(xform2d &mat, const RenderSettings &rset, const pugi::xml_node &group, ClipperLib::Paths &parent_clip_path, const ElementSelector *sel=nullptr, bool included=true, bool is_root=false);
|
||||
void export_svg_path(xform2d &mat, const RenderSettings &rset, const pugi::xml_node &node, ClipperLib::Paths &clip_path);
|
||||
void export_svg_group(RenderContext &ctx, const pugi::xml_node &group);
|
||||
void export_svg_path(RenderContext &ctx, const pugi::xml_node &node);
|
||||
void setup_viewport_clip();
|
||||
void load_clips(const RenderSettings &rset);
|
||||
void load_patterns();
|
||||
|
|
@ -226,8 +265,6 @@ namespace gerbolyze {
|
|||
std::map<std::string, ClipperLib::Paths> clip_path_map;
|
||||
ClipperLib::Paths vb_paths; /* viewport clip rect */
|
||||
|
||||
PolygonSink *polygon_sink = nullptr;
|
||||
|
||||
static constexpr double dbg_fill_alpha = 0.8;
|
||||
static constexpr double dbg_stroke_alpha = 1.0;
|
||||
static constexpr double assumed_usvg_dpi = 96.0;
|
||||
|
|
|
|||
|
|
@ -377,7 +377,7 @@ int main(int argc, char **argv) {
|
|||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
doc.render(rset, *top_sink, &sel);
|
||||
doc.render(rset, *top_sink, sel);
|
||||
|
||||
remove(frob.c_str());
|
||||
remove(barf.c_str());
|
||||
|
|
|
|||
|
|
@ -29,27 +29,27 @@
|
|||
using namespace gerbolyze;
|
||||
using namespace std;
|
||||
|
||||
void Scaler::header(d2p origin, d2p size) {
|
||||
void PolygonScaler::header(d2p origin, d2p size) {
|
||||
m_sink.header({origin[0] * m_scale, origin[1] * m_scale}, {size[0] * m_scale, size[1] * m_scale});
|
||||
}
|
||||
|
||||
void Scaler::footer() {
|
||||
void PolygonScaler::footer() {
|
||||
m_sink.footer();
|
||||
}
|
||||
|
||||
Scaler &Scaler::operator<<(const LayerNameToken &layer_name) {
|
||||
PolygonScaler &PolygonScaler::operator<<(const LayerNameToken &layer_name) {
|
||||
m_sink << layer_name;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Scaler &Scaler::operator<<(GerberPolarityToken pol) {
|
||||
PolygonScaler &PolygonScaler::operator<<(GerberPolarityToken pol) {
|
||||
m_sink << pol;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Scaler &Scaler::operator<<(const Polygon &poly) {
|
||||
PolygonScaler &PolygonScaler::operator<<(const Polygon &poly) {
|
||||
Polygon new_poly;
|
||||
for (auto &p : poly) {
|
||||
new_poly.push_back({ p[0] * m_scale, p[1] * m_scale });
|
||||
|
|
|
|||
|
|
@ -29,6 +29,40 @@ using namespace gerbolyze;
|
|||
using namespace std;
|
||||
|
||||
|
||||
/* Note: These values come from KiCAD's common/lset.cpp. KiCAD uses *multiple different names* for the same layer in
|
||||
* different places, and not all of them are stable. Sometimes, these names change without notice. If this list isn't
|
||||
* up-to-date, it's not my fault. Still, please file an issue. */
|
||||
const std::vector<std::string> gerbolyze::kicad_default_layers ({
|
||||
/* Copper */
|
||||
"F.Cu",
|
||||
"In1.Cu", "In2.Cu", "In3.Cu", "In4.Cu", "In5.Cu", "In6.Cu", "In7.Cu", "In8.Cu",
|
||||
"In9.Cu", "In10.Cu", "In11.Cu", "In12.Cu", "In13.Cu", "In14.Cu", "In15.Cu", "In16.Cu",
|
||||
"In17.Cu", "In18.Cu", "In19.Cu", "In20.Cu", "In21.Cu", "In22.Cu", "In23.Cu",
|
||||
"In24.Cu", "In25.Cu", "In26.Cu", "In27.Cu", "In28.Cu", "In29.Cu", "In30.Cu",
|
||||
"B.Cu",
|
||||
|
||||
/* Technical layers */
|
||||
"B.Adhes", "F.Adhes",
|
||||
"B.Paste", "F.Paste",
|
||||
"B.SilkS", "F.SilkS",
|
||||
"B.Mask", "F.Mask",
|
||||
|
||||
/* User layers */
|
||||
"Dwgs.User",
|
||||
"Cmts.User",
|
||||
"Eco1.User", "Eco2.User",
|
||||
"Edge.Cuts",
|
||||
"Margin",
|
||||
|
||||
/* Footprint layers */
|
||||
"F.CrtYd", "B.CrtYd",
|
||||
"F.Fab", "B.Fab",
|
||||
|
||||
/* Layers for user scripting etc. */
|
||||
"User.1", "User.2", "User.3", "User.4", "User.5", "User.6", "User.7", "User.8", "User.9",
|
||||
});
|
||||
|
||||
|
||||
KicadSexpOutput::KicadSexpOutput(ostream &out, string mod_name, string layer, bool only_polys, string ref_text, string val_text, d2p ref_pos, d2p val_pos)
|
||||
: StreamPolygonSink(out, only_polys),
|
||||
m_mod_name(mod_name),
|
||||
|
|
@ -105,4 +139,3 @@ void KicadSexpOutput::footer_impl() {
|
|||
m_out << ")" << endl;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -132,14 +132,7 @@ bool IDElementSelector::match(const pugi::xml_node &node, bool included, bool is
|
|||
}
|
||||
|
||||
/* Recursively export all SVG elements in the given group. */
|
||||
void gerbolyze::SVGDocument::export_svg_group(xform2d &mat, const RenderSettings &rset, const pugi::xml_node &group, Paths &parent_clip_path, const ElementSelector *sel, bool included, bool is_root) {
|
||||
|
||||
/* Load clip paths from defs given bezier flattening tolerance from rset */
|
||||
load_clips(rset);
|
||||
|
||||
/* Enter the group's coordinate system */
|
||||
xform2d local_xf(mat);
|
||||
local_xf.transform(xform2d(group.attribute("transform").value()));
|
||||
void gerbolyze::SVGDocument::export_svg_group(RenderContext &ctx, const pugi::xml_node &group) {
|
||||
|
||||
/* Fetch clip path from global registry and transform it into document coordinates. */
|
||||
Paths clip_path;
|
||||
|
|
@ -152,50 +145,56 @@ void gerbolyze::SVGDocument::export_svg_group(xform2d &mat, const RenderSettings
|
|||
|
||||
} else {
|
||||
clip_path = *lookup;
|
||||
local_xf.transform_paths(clip_path);
|
||||
ctx.mat().transform_paths(clip_path);
|
||||
}
|
||||
|
||||
/* Clip against parent's clip path (both are now in document coordinates) */
|
||||
if (!parent_clip_path.empty()) {
|
||||
if (!ctx.clip().empty()) {
|
||||
if (!clip_path.empty()) {
|
||||
combine_clip_paths(parent_clip_path, clip_path, clip_path);
|
||||
Clipper c;
|
||||
c.StrictlySimple(true);
|
||||
c.AddPaths(ctx.clip(), ptClip, /* closed */ true);
|
||||
c.AddPaths(clip_path, ptSubject, /* closed */ true);
|
||||
/* Nonzero fill since both input clip paths must already have been preprocessed by clipper. */
|
||||
c.Execute(ctIntersection, clip_path, pftNonZero);
|
||||
} else {
|
||||
clip_path = parent_clip_path;
|
||||
clip_path = ctx.clip();
|
||||
}
|
||||
}
|
||||
|
||||
/* Iterate over the group's children, exporting them one by one. */
|
||||
for (const auto &node : group.children()) {
|
||||
if (sel && !sel->match(node, included, is_root))
|
||||
if (!ctx.match(node))
|
||||
continue;
|
||||
|
||||
string name(node.name());
|
||||
RenderContext elem_ctx(ctx, xform2d(node.attribute("transform").value()), clip_path);
|
||||
if (name == "g") {
|
||||
if (is_root) { /* Treat top-level groups as "layers" like inkscape does. */
|
||||
if (ctx.root()) { /* Treat top-level groups as "layers" like inkscape does. */
|
||||
cerr << "Forwarding layer name to sink: \"" << node.attribute("id").value() << "\"" << endl;
|
||||
LayerNameToken tok { node.attribute("id").value() };
|
||||
*polygon_sink << tok;
|
||||
elem_ctx.sink() << tok;
|
||||
}
|
||||
|
||||
export_svg_group(local_xf, rset, node, clip_path, sel, true);
|
||||
export_svg_group(elem_ctx, node);
|
||||
|
||||
if (is_root) {
|
||||
if (ctx.root()) {
|
||||
LayerNameToken tok {""};
|
||||
*polygon_sink << tok;
|
||||
elem_ctx.sink() << tok;
|
||||
}
|
||||
|
||||
} else if (name == "path") {
|
||||
export_svg_path(local_xf, rset, node, clip_path);
|
||||
export_svg_path(elem_ctx, node);
|
||||
|
||||
} else if (name == "image") {
|
||||
ImageVectorizer *vec = rset.m_vec_sel.select(node);
|
||||
ImageVectorizer *vec = ctx.settings().m_vec_sel.select(node);
|
||||
if (!vec) {
|
||||
cerr << "Cannot resolve vectorizer for node \"" << node.attribute("id").value() << "\"" << endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
double min_feature_size_px = mm_to_doc_units(rset.m_minimum_feature_size_mm);
|
||||
vec->vectorize_image(local_xf, node, clip_path, *polygon_sink, min_feature_size_px);
|
||||
double min_feature_size_px = mm_to_doc_units(ctx.settings().m_minimum_feature_size_mm);
|
||||
vec->vectorize_image(elem_ctx, node, min_feature_size_px);
|
||||
delete vec;
|
||||
|
||||
} else if (name == "defs") {
|
||||
|
|
@ -207,9 +206,9 @@ void gerbolyze::SVGDocument::export_svg_group(xform2d &mat, const RenderSettings
|
|||
}
|
||||
|
||||
/* Export an SVG path element to gerber. Apply patterns and clip on the fly. */
|
||||
void gerbolyze::SVGDocument::export_svg_path(xform2d &mat, const RenderSettings &rset, const pugi::xml_node &node, Paths &clip_path) {
|
||||
enum gerber_color fill_color = gerber_fill_color(node, rset);
|
||||
enum gerber_color stroke_color = gerber_stroke_color(node, rset);
|
||||
void gerbolyze::SVGDocument::export_svg_path(RenderContext &ctx, const pugi::xml_node &node) {
|
||||
enum gerber_color fill_color = gerber_fill_color(node, ctx.settings());
|
||||
enum gerber_color stroke_color = gerber_stroke_color(node, ctx.settings());
|
||||
|
||||
double stroke_width = usvg_double_attr(node, "stroke-width", /* default */ 1.0);
|
||||
assert(stroke_width > 0.0);
|
||||
|
|
@ -225,15 +224,13 @@ void gerbolyze::SVGDocument::export_svg_path(xform2d &mat, const RenderSettings
|
|||
}
|
||||
|
||||
/* Load path from SVG path data and transform into document units. */
|
||||
xform2d local_xf(mat);
|
||||
local_xf.transform(xform2d(node.attribute("transform").value()));
|
||||
/* FIXME transform stroke width here? */
|
||||
stroke_width = local_xf.doc2phys_dist(stroke_width);
|
||||
stroke_width = ctx.mat().doc2phys_dist(stroke_width);
|
||||
|
||||
Paths stroke_open, stroke_closed;
|
||||
PolyTree ptree_fill;
|
||||
PolyTree ptree;
|
||||
load_svg_path(local_xf, node, stroke_open, stroke_closed, ptree_fill, rset.curve_tolerance_mm);
|
||||
load_svg_path(ctx.mat(), node, stroke_open, stroke_closed, ptree_fill, ctx.settings().curve_tolerance_mm);
|
||||
|
||||
Paths fill_paths;
|
||||
PolyTreeToPaths(ptree_fill, fill_paths);
|
||||
|
|
@ -243,17 +240,16 @@ void gerbolyze::SVGDocument::export_svg_path(xform2d &mat, const RenderSettings
|
|||
|
||||
/* Skip filling for transparent fills. In outline mode, skip filling if a stroke is also set to avoid double lines.
|
||||
*/
|
||||
if (fill_color && !(rset.outline_mode && has_stroke)) {
|
||||
if (has_fill && !(ctx.settings().outline_mode && has_stroke)) {
|
||||
/* Clip paths. Consider all paths closed for filling. */
|
||||
if (!clip_path.empty()) {
|
||||
if (!ctx.clip().empty()) {
|
||||
Clipper c;
|
||||
c.AddPaths(fill_paths, ptSubject, /* closed */ true);
|
||||
c.AddPaths(clip_path, ptClip, /* 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_fill, pftNonZero, pftNonZero);
|
||||
PolyTreeToPaths(ptree_fill, fill_paths);
|
||||
}
|
||||
|
||||
/* Call out to pattern tiler for pattern fills. The path becomes the clip here. */
|
||||
|
|
@ -264,11 +260,13 @@ void gerbolyze::SVGDocument::export_svg_path(xform2d &mat, const RenderSettings
|
|||
cerr << "Warning: Fill pattern with id \"" << fill_pattern_id << "\" not found." << endl;
|
||||
|
||||
} else {
|
||||
pattern->tile(local_xf, rset, fill_paths);
|
||||
PolyTreeToPaths(ptree_fill, fill_paths);
|
||||
RenderContext local_ctx(ctx, xform2d(), fill_paths);
|
||||
pattern->tile(local_ctx);
|
||||
}
|
||||
|
||||
} else { /* solid fill */
|
||||
if (rset.outline_mode) {
|
||||
if (ctx.settings().outline_mode) {
|
||||
fill_color = GRB_DARK;
|
||||
}
|
||||
|
||||
|
|
@ -286,10 +284,10 @@ void gerbolyze::SVGDocument::export_svg_path(xform2d &mat, const RenderSettings
|
|||
});
|
||||
|
||||
/* In outline mode, manually close polys */
|
||||
if (rset.outline_mode && !out.empty())
|
||||
if (ctx.settings().outline_mode && !out.empty())
|
||||
out.push_back(out[0]);
|
||||
|
||||
*polygon_sink << (fill_color == GRB_DARK ? GRB_POL_DARK : GRB_POL_CLEAR) << out;
|
||||
ctx.sink() << (fill_color == GRB_DARK ? GRB_POL_DARK : GRB_POL_CLEAR) << out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -307,10 +305,10 @@ void gerbolyze::SVGDocument::export_svg_path(xform2d &mat, const RenderSettings
|
|||
/* Special case: A closed path becomes a number of open paths when it is dashed. */
|
||||
if (dasharray.empty()) {
|
||||
|
||||
if (rset.outline_mode && stroke_color != GRB_PATTERN_FILL) {
|
||||
if (ctx.settings().outline_mode && stroke_color != GRB_PATTERN_FILL) {
|
||||
/* In outline mode, manually close polys */
|
||||
poly.push_back(poly[0]);
|
||||
*polygon_sink << ApertureToken() << poly;
|
||||
ctx.sink() << ApertureToken() << poly;
|
||||
|
||||
} else {
|
||||
offx.AddPath(poly, join_type, etClosedLine);
|
||||
|
|
@ -322,8 +320,8 @@ void gerbolyze::SVGDocument::export_svg_path(xform2d &mat, const RenderSettings
|
|||
Paths out;
|
||||
dash_path(poly_copy, out, dasharray, stroke_dashoffset);
|
||||
|
||||
if (rset.outline_mode && stroke_color != GRB_PATTERN_FILL) {
|
||||
*polygon_sink << ApertureToken(stroke_width) << out;
|
||||
if (ctx.settings().outline_mode && stroke_color != GRB_PATTERN_FILL) {
|
||||
ctx.sink() << ApertureToken(stroke_width) << out;
|
||||
} else {
|
||||
offx.AddPaths(out, join_type, end_type);
|
||||
}
|
||||
|
|
@ -334,8 +332,8 @@ void gerbolyze::SVGDocument::export_svg_path(xform2d &mat, const RenderSettings
|
|||
Paths out;
|
||||
dash_path(poly, out, dasharray, stroke_dashoffset);
|
||||
|
||||
if (rset.outline_mode && stroke_color != GRB_PATTERN_FILL) {
|
||||
*polygon_sink << ApertureToken(stroke_width) << out;
|
||||
if (ctx.settings().outline_mode && stroke_color != GRB_PATTERN_FILL) {
|
||||
ctx.sink() << ApertureToken(stroke_width) << out;
|
||||
} else {
|
||||
offx.AddPaths(out, join_type, end_type);
|
||||
}
|
||||
|
|
@ -346,13 +344,13 @@ void gerbolyze::SVGDocument::export_svg_path(xform2d &mat, const RenderSettings
|
|||
|
||||
/* 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 (!clip_path.empty()) {
|
||||
if (!ctx.clip().empty()) {
|
||||
Clipper c;
|
||||
|
||||
Paths outline_paths;
|
||||
PolyTreeToPaths(ptree, outline_paths);
|
||||
c.AddPaths(outline_paths, ptSubject, /* closed */ true);
|
||||
c.AddPaths(clip_path, ptClip, /* 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);
|
||||
|
|
@ -368,20 +366,21 @@ void gerbolyze::SVGDocument::export_svg_path(xform2d &mat, const RenderSettings
|
|||
} else {
|
||||
Paths clip;
|
||||
PolyTreeToPaths(ptree, clip);
|
||||
pattern->tile(local_xf, rset, clip);
|
||||
RenderContext local_ctx(ctx, xform2d(), clip);
|
||||
pattern->tile(local_ctx);
|
||||
}
|
||||
|
||||
} else if (!rset.outline_mode) {
|
||||
} else if (!ctx.settings().outline_mode) {
|
||||
Paths s_polys;
|
||||
dehole_polytree(ptree, s_polys);
|
||||
|
||||
*polygon_sink << ApertureToken() << (stroke_color == GRB_DARK ? GRB_POL_DARK : GRB_POL_CLEAR) << s_polys;
|
||||
ctx.sink() << ApertureToken() << (stroke_color == GRB_DARK ? GRB_POL_DARK : GRB_POL_CLEAR) << s_polys;
|
||||
}
|
||||
}
|
||||
*polygon_sink << ApertureToken();
|
||||
ctx.sink() << ApertureToken();
|
||||
}
|
||||
|
||||
void gerbolyze::SVGDocument::render(const RenderSettings &rset, PolygonSink &sink, const ElementSelector *sel) {
|
||||
void gerbolyze::SVGDocument::render(const RenderSettings &rset, PolygonSink &sink, const ElementSelector &sel) {
|
||||
assert(_valid);
|
||||
/* Export the actual SVG document. 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 primitives and then serializing
|
||||
|
|
@ -389,21 +388,18 @@ void gerbolyze::SVGDocument::render(const RenderSettings &rset, PolygonSink &sin
|
|||
*/
|
||||
|
||||
/* Scale document pixels to mm for sinks */
|
||||
Scaler scaler(sink, doc_units_to_mm(1.0));
|
||||
PolygonScaler scaler(sink, doc_units_to_mm(1.0));
|
||||
RenderContext ctx(rset, scaler, sel, vb_paths);
|
||||
|
||||
/* Load clip paths from defs with given bezier flattening tolerance and unit scale */
|
||||
load_clips(rset);
|
||||
|
||||
polygon_sink = &scaler;
|
||||
scaler.header({vb_x, vb_y}, {vb_w, vb_h});
|
||||
ClipperLib::Clipper c;
|
||||
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;
|
||||
xform2d xf;
|
||||
export_svg_group(xf, rset, root_elem, vb_paths, sel, false, true);
|
||||
export_svg_group(ctx, root_elem);
|
||||
scaler.footer();
|
||||
polygon_sink = nullptr;
|
||||
}
|
||||
|
||||
void gerbolyze::SVGDocument::render_to_list(const RenderSettings &rset, 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});
|
||||
});
|
||||
|
|
@ -413,14 +409,14 @@ void gerbolyze::SVGDocument::render_to_list(const RenderSettings &rset, vector<p
|
|||
void gerbolyze::SVGDocument::setup_viewport_clip() {
|
||||
/* Set up view port clip path */
|
||||
Path vb_path;
|
||||
for (auto &elem : vector<pair<double, double>> {{vb_x, vb_y}, {vb_x+vb_w, vb_y}, {vb_x+vb_w, vb_y+vb_h}, {vb_x, vb_y+vb_h}}) {
|
||||
double x = elem.first, y = elem.second;
|
||||
vb_path.push_back({ (cInt)round(x * clipper_scale), (cInt)round(y * clipper_scale) });
|
||||
for (d2p &p : vector<d2p> {
|
||||
{vb_x, vb_y},
|
||||
{vb_x+vb_w, vb_y},
|
||||
{vb_x+vb_w, vb_y+vb_h},
|
||||
{vb_x, vb_y+vb_h}}) {
|
||||
vb_path.push_back({ (cInt)round(p[0] * clipper_scale), (cInt)round(p[1] * clipper_scale) });
|
||||
}
|
||||
vb_paths.push_back(vb_path);
|
||||
|
||||
ClipperLib::Clipper c;
|
||||
c.AddPaths(vb_paths, ptSubject, /* closed */ true);
|
||||
}
|
||||
|
||||
void gerbolyze::SVGDocument::load_patterns() {
|
||||
|
|
@ -478,35 +474,35 @@ void gerbolyze::SVGDocument::load_clips(const RenderSettings &rset) {
|
|||
}
|
||||
}
|
||||
|
||||
/* Note: These values come from KiCAD's common/lset.cpp. KiCAD uses *multiple different names* for the same layer in
|
||||
* different places, and not all of them are stable. Sometimes, these names change without notice. If this list isn't
|
||||
* up-to-date, it's not my fault. Still, please file an issue. */
|
||||
const std::vector<std::string> gerbolyze::kicad_default_layers ({
|
||||
/* Copper */
|
||||
"F.Cu",
|
||||
"In1.Cu", "In2.Cu", "In3.Cu", "In4.Cu", "In5.Cu", "In6.Cu", "In7.Cu", "In8.Cu",
|
||||
"In9.Cu", "In10.Cu", "In11.Cu", "In12.Cu", "In13.Cu", "In14.Cu", "In15.Cu", "In16.Cu",
|
||||
"In17.Cu", "In18.Cu", "In19.Cu", "In20.Cu", "In21.Cu", "In22.Cu", "In23.Cu",
|
||||
"In24.Cu", "In25.Cu", "In26.Cu", "In27.Cu", "In28.Cu", "In29.Cu", "In30.Cu",
|
||||
"B.Cu",
|
||||
|
||||
/* Technical layers */
|
||||
"B.Adhes", "F.Adhes",
|
||||
"B.Paste", "F.Paste",
|
||||
"B.SilkS", "F.SilkS",
|
||||
"B.Mask", "F.Mask",
|
||||
gerbolyze::RenderContext::RenderContext(const RenderSettings &settings,
|
||||
PolygonSink &sink,
|
||||
const ElementSelector &sel,
|
||||
ClipperLib::Paths &clip) :
|
||||
m_sink(sink),
|
||||
m_settings(settings),
|
||||
m_mat(),
|
||||
m_root(true),
|
||||
m_included(false),
|
||||
m_sel(sel),
|
||||
m_clip(clip)
|
||||
{
|
||||
}
|
||||
|
||||
/* User layers */
|
||||
"Dwgs.User",
|
||||
"Cmts.User",
|
||||
"Eco1.User", "Eco2.User",
|
||||
"Edge.Cuts",
|
||||
"Margin",
|
||||
gerbolyze::RenderContext::RenderContext(RenderContext &parent, xform2d transform) :
|
||||
RenderContext(parent, transform, parent.clip())
|
||||
{
|
||||
}
|
||||
|
||||
/* Footprint layers */
|
||||
"F.CrtYd", "B.CrtYd",
|
||||
"F.Fab", "B.Fab",
|
||||
gerbolyze::RenderContext::RenderContext(RenderContext &parent, xform2d transform, ClipperLib::Paths &clip) :
|
||||
m_sink(parent.sink()),
|
||||
m_settings(parent.settings()),
|
||||
m_mat(parent.mat()),
|
||||
m_root(false),
|
||||
m_included(parent.included()),
|
||||
m_sel(parent.sel()),
|
||||
m_clip(clip)
|
||||
{
|
||||
m_mat.transform(transform);
|
||||
}
|
||||
|
||||
/* Layers for user scripting etc. */
|
||||
"User.1", "User.2", "User.3", "User.4", "User.5", "User.6", "User.7", "User.8", "User.9",
|
||||
});
|
||||
|
|
|
|||
|
|
@ -147,14 +147,3 @@ void gerbolyze::dehole_polytree(PolyTree &ptree, Paths &out) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/* 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;
|
||||
c.StrictlySimple(true);
|
||||
c.AddPaths(in_a, ptClip, /* closed */ true);
|
||||
c.AddPaths(in_b, ptSubject, /* closed */ true);
|
||||
/* Nonzero fill since both input clip paths must already have been preprocessed by clipper. */
|
||||
c.Execute(ctIntersection, out, pftNonZero);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -54,17 +54,17 @@ 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 (xform2d &mat, const gerbolyze::RenderSettings &rset, ClipperLib::Paths &clip) {
|
||||
void gerbolyze::Pattern::tile (gerbolyze::RenderContext &ctx) {
|
||||
assert(doc);
|
||||
|
||||
/* Transform x, y, w, h from pattern coordinate space into parent coordinates by applying the inverse
|
||||
* patternTransform. This is necessary so we iterate over the correct bounds when tiling below */
|
||||
d2p pos_xf = mat.doc2phys(d2p{x, y});
|
||||
d2p pos_xf = ctx.mat().doc2phys(d2p{x, y});
|
||||
double inst_x = pos_xf[0], inst_y = pos_xf[1];
|
||||
double inst_w = mat.doc2phys_dist(w);
|
||||
double inst_h = mat.doc2phys_dist(h);
|
||||
double inst_w = ctx.mat().doc2phys_dist(w);
|
||||
double inst_h = ctx.mat().doc2phys_dist(h);
|
||||
|
||||
ClipperLib::IntRect clip_bounds = get_paths_bounds(clip);
|
||||
ClipperLib::IntRect clip_bounds = get_paths_bounds(ctx.clip());
|
||||
double bx = clip_bounds.left / clipper_scale;
|
||||
double by = clip_bounds.top / clipper_scale;
|
||||
double bw = (clip_bounds.right - clip_bounds.left) / clipper_scale;
|
||||
|
|
@ -86,8 +86,7 @@ void gerbolyze::Pattern::tile (xform2d &mat, const gerbolyze::RenderSettings &rs
|
|||
}
|
||||
|
||||
/* Switch to pattern coordinates */
|
||||
xform2d local_xf(mat);
|
||||
local_xf.transform(patternTransform);
|
||||
RenderContext pat_ctx(ctx, patternTransform);
|
||||
|
||||
/* Iterate over all pattern tiles in pattern coordinates */
|
||||
for (double inst_off_x = fmod(inst_x, inst_w) - 2*inst_w;
|
||||
|
|
@ -98,7 +97,7 @@ void gerbolyze::Pattern::tile (xform2d &mat, const gerbolyze::RenderSettings &rs
|
|||
inst_off_y < bh + 2*inst_h;
|
||||
inst_off_y += inst_h) {
|
||||
|
||||
xform2d elem_xf(local_xf);
|
||||
xform2d elem_xf;
|
||||
/* Change into this individual tile's coordinate system */
|
||||
elem_xf.translate(inst_off_x, inst_off_y);
|
||||
if (has_vb) {
|
||||
|
|
@ -109,7 +108,8 @@ void gerbolyze::Pattern::tile (xform2d &mat, const gerbolyze::RenderSettings &rs
|
|||
}
|
||||
|
||||
/* Export the pattern tile's content like a group */
|
||||
doc->export_svg_group(elem_xf, rset, _node, clip);
|
||||
RenderContext elem_ctx(pat_ctx, elem_xf);
|
||||
doc->export_svg_group(elem_ctx, _node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,13 +30,14 @@ namespace gerbolyze {
|
|||
|
||||
class SVGDocument;
|
||||
class RenderSettings;
|
||||
class RenderContext;
|
||||
|
||||
class Pattern {
|
||||
public:
|
||||
Pattern() {}
|
||||
Pattern(const pugi::xml_node &node, SVGDocument &doc);
|
||||
|
||||
void tile (xform2d &mat, const gerbolyze::RenderSettings &rset, ClipperLib::Paths &clip);
|
||||
void tile (RenderContext &ctx);
|
||||
|
||||
private:
|
||||
double x, y, w, h;
|
||||
|
|
|
|||
|
|
@ -95,15 +95,19 @@ template<typename T> nopencv::Image<T> *img_from_node(const pugi::xml_node &node
|
|||
return img;
|
||||
}
|
||||
|
||||
void gerbolyze::draw_bg_rect(xform2d &mat, double width, double height, ClipperLib::Paths &clip_path, PolygonSink &sink) {
|
||||
void gerbolyze::draw_bg_rect(RenderContext &ctx, double width, double height) {
|
||||
/* For our output to look correct, we have to paint the image's bounding box in black as background for our halftone
|
||||
* blobs. We cannot simply draw a rect here, though. Instead we have to first intersect the bounding box with the
|
||||
* clip path we get from the caller.
|
||||
*
|
||||
* First, setup the bounding box rectangle in our local px coordinate space. */
|
||||
ClipperLib::Path rect_path;
|
||||
for (auto &elem : vector<pair<double, double>> {{0, 0}, {width, 0}, {width, height}, {0, height}}) {
|
||||
d2p xf(mat.doc2phys(d2p{elem.first, elem.second}));
|
||||
for (auto &elem : vector<pair<double, double>> {
|
||||
{0, 0},
|
||||
{width, 0},
|
||||
{width, height},
|
||||
{0, height}}) {
|
||||
d2p xf(ctx.mat().doc2phys(d2p{elem.first, elem.second}));
|
||||
rect_path.push_back({
|
||||
(ClipperLib::cInt)round(xf[0] * clipper_scale),
|
||||
(ClipperLib::cInt)round(xf[1] * clipper_scale)
|
||||
|
|
@ -113,8 +117,8 @@ void gerbolyze::draw_bg_rect(xform2d &mat, double width, double height, ClipperL
|
|||
/* Intersect the bounding box with the caller's clip path */
|
||||
ClipperLib::Clipper c;
|
||||
c.AddPath(rect_path, ClipperLib::ptSubject, /* closed */ true);
|
||||
if (!clip_path.empty()) {
|
||||
c.AddPaths(clip_path, ClipperLib::ptClip, /* closed */ true);
|
||||
if (!ctx.clip().empty()) {
|
||||
c.AddPaths(ctx.clip(), ClipperLib::ptClip, /* closed */ true);
|
||||
}
|
||||
|
||||
ClipperLib::Paths rect_out;
|
||||
|
|
@ -128,7 +132,7 @@ void gerbolyze::draw_bg_rect(xform2d &mat, double width, double height, ClipperL
|
|||
out.push_back(std::array<double, 2>{
|
||||
((double)p.X) / clipper_scale, ((double)p.Y) / clipper_scale
|
||||
});
|
||||
sink << GRB_POL_CLEAR << out;
|
||||
ctx.sink() << GRB_POL_CLEAR << out;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -152,7 +156,7 @@ void gerbolyze::draw_bg_rect(xform2d &mat, double width, double height, ClipperL
|
|||
* 4. It scales each of these voronoi cell polygons to match the input images brightness at the spot covered by this
|
||||
* cell.
|
||||
*/
|
||||
void gerbolyze::VoronoiVectorizer::vectorize_image(xform2d &mat, const pugi::xml_node &node, ClipperLib::Paths &clip_path, PolygonSink &sink, double min_feature_size_px) {
|
||||
void gerbolyze::VoronoiVectorizer::vectorize_image(RenderContext &ctx, const pugi::xml_node &node, double min_feature_size_px) {
|
||||
double x, y, width, height;
|
||||
parse_img_meta(node, x, y, width, height);
|
||||
nopencv::Image32f *img = img_from_node<float>(node);
|
||||
|
|
@ -160,10 +164,8 @@ void gerbolyze::VoronoiVectorizer::vectorize_image(xform2d &mat, const pugi::xml
|
|||
return;
|
||||
|
||||
/* Set up target transform using SVG transform and x/y attributes */
|
||||
xform2d local_xf(mat);
|
||||
local_xf.transform(xform2d(node.attribute("transform").value()));
|
||||
local_xf.translate(x, y);
|
||||
cerr << "voronoi vectorizer: local_xf = " << local_xf.dbg_str() << endl;
|
||||
RenderContext img_ctx(ctx, xform2d(1, 0, 0, 1, x, y));
|
||||
cerr << "voronoi vectorizer: local_xf = " << ctx.mat().dbg_str() << endl;
|
||||
|
||||
double orig_rows = img->rows();
|
||||
double orig_cols = img->cols();
|
||||
|
|
@ -176,10 +178,10 @@ void gerbolyze::VoronoiVectorizer::vectorize_image(xform2d &mat, const pugi::xml
|
|||
cerr << "aspect " << scale_x << ", " << scale_y << " / " << off_x << ", " << off_y << endl;
|
||||
|
||||
/* Adjust minimum feature size given in mm and translate into px document units in our local coordinate system. */
|
||||
min_feature_size_px = local_xf.doc2phys_dist(min_feature_size_px);
|
||||
min_feature_size_px = img_ctx.mat().doc2phys_dist(min_feature_size_px);
|
||||
cerr << " min_feature_size_px = " << min_feature_size_px << endl;
|
||||
|
||||
draw_bg_rect(local_xf, width, height, clip_path, sink);
|
||||
draw_bg_rect(img_ctx, width, height);
|
||||
|
||||
/* Set up a poisson-disc sampled point "grid" covering the image. Calculate poisson disc parameters from given
|
||||
* minimum feature size. */
|
||||
|
|
@ -306,7 +308,7 @@ void gerbolyze::VoronoiVectorizer::vectorize_image(xform2d &mat, const pugi::xml
|
|||
if (last_fill_factor != fill_factor) {
|
||||
/* Fill factor was adjusted since last edge, so generate one extra point so we have a nice radial
|
||||
* "step". */
|
||||
d2p p = local_xf.doc2phys(d2p{
|
||||
d2p p = img_ctx.mat().doc2phys(d2p{
|
||||
off_x + center.x + (e->pos[0].x - center.x) * fill_factor,
|
||||
off_y + center.y + (e->pos[0].y - center.y) * fill_factor
|
||||
});
|
||||
|
|
@ -318,7 +320,7 @@ void gerbolyze::VoronoiVectorizer::vectorize_image(xform2d &mat, const pugi::xml
|
|||
}
|
||||
|
||||
/* Emit endpoint of current edge */
|
||||
d2p p = local_xf.doc2phys(d2p{
|
||||
d2p p = img_ctx.mat().doc2phys(d2p{
|
||||
off_x + center.x + (e->pos[1].x - center.x) * fill_factor,
|
||||
off_y + center.y + (e->pos[1].y - center.y) * fill_factor
|
||||
});
|
||||
|
|
@ -339,8 +341,8 @@ void gerbolyze::VoronoiVectorizer::vectorize_image(xform2d &mat, const pugi::xml
|
|||
ClipperLib::Paths polys;
|
||||
ClipperLib::Clipper c;
|
||||
c.AddPath(cell_path, ClipperLib::ptSubject, /* closed */ true);
|
||||
if (!clip_path.empty()) {
|
||||
c.AddPaths(clip_path, ClipperLib::ptClip, /* closed */ true);
|
||||
if (!img_ctx.clip().empty()) {
|
||||
c.AddPaths(img_ctx.clip(), ClipperLib::ptClip, /* closed */ true);
|
||||
}
|
||||
c.StrictlySimple(true);
|
||||
c.Execute(ClipperLib::ctIntersection, polys, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
|
||||
|
|
@ -352,7 +354,7 @@ void gerbolyze::VoronoiVectorizer::vectorize_image(xform2d &mat, const pugi::xml
|
|||
out.push_back(std::array<double, 2>{
|
||||
((double)p.X) / clipper_scale, ((double)p.Y) / clipper_scale
|
||||
});
|
||||
sink << GRB_POL_DARK << out;
|
||||
img_ctx.sink() << GRB_POL_DARK << out;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -413,7 +415,7 @@ void gerbolyze::handle_aspect_ratio(string spec, double &scale_x, double &scale_
|
|||
}
|
||||
|
||||
|
||||
void gerbolyze::OpenCVContoursVectorizer::vectorize_image(xform2d &mat, const pugi::xml_node &node, ClipperLib::Paths &clip_path, PolygonSink &sink, double min_feature_size_px) {
|
||||
void gerbolyze::OpenCVContoursVectorizer::vectorize_image(RenderContext &ctx, const pugi::xml_node &node, double min_feature_size_px) {
|
||||
(void) min_feature_size_px; /* unused by this vectorizer */
|
||||
double x, y, width, height;
|
||||
parse_img_meta(node, x, y, width, height);
|
||||
|
|
@ -422,9 +424,7 @@ void gerbolyze::OpenCVContoursVectorizer::vectorize_image(xform2d &mat, const pu
|
|||
return;
|
||||
|
||||
/* Set up target transform using SVG transform and x/y attributes */
|
||||
xform2d local_xf(mat);
|
||||
local_xf.transform(xform2d(node.attribute("transform").value()));
|
||||
local_xf.translate(x, y);
|
||||
RenderContext img_ctx(ctx, xform2d(1, 0, 0, 1, x, y));
|
||||
|
||||
double scale_x = (double)width / (double)img->cols();
|
||||
double scale_y = (double)height / (double)img->rows();
|
||||
|
|
@ -433,24 +433,24 @@ void gerbolyze::OpenCVContoursVectorizer::vectorize_image(xform2d &mat, const pu
|
|||
handle_aspect_ratio(node.attribute("preserveAspectRatio").value(),
|
||||
scale_x, scale_y, off_x, off_y, img->cols(), img->rows());
|
||||
|
||||
draw_bg_rect(local_xf, width, height, clip_path, sink);
|
||||
draw_bg_rect(img_ctx, width, height);
|
||||
|
||||
img->binarize(128);
|
||||
nopencv::find_contours(*img,
|
||||
nopencv::simplify_contours_douglas_peucker(
|
||||
[&sink, &local_xf, &clip_path, off_x, off_y, scale_x, scale_y](Polygon_i& poly, nopencv::ContourPolarity pol) {
|
||||
[&img_ctx, off_x, off_y, scale_x, scale_y](Polygon_i& poly, nopencv::ContourPolarity pol) {
|
||||
|
||||
if (pol == nopencv::CP_HOLE) {
|
||||
std::reverse(poly.begin(), poly.end());
|
||||
sink << GRB_POL_CLEAR;
|
||||
img_ctx.sink() << GRB_POL_CLEAR;
|
||||
|
||||
} else {
|
||||
sink << GRB_POL_DARK;
|
||||
img_ctx.sink() << GRB_POL_DARK;
|
||||
}
|
||||
|
||||
ClipperLib::Path out;
|
||||
for (const auto &p : poly) {
|
||||
d2p q = local_xf.doc2phys(d2p{
|
||||
d2p q = img_ctx.mat().doc2phys(d2p{
|
||||
off_x + (double)p[0] * scale_x,
|
||||
off_y + (double)p[1] * scale_y
|
||||
});
|
||||
|
|
@ -462,8 +462,8 @@ void gerbolyze::OpenCVContoursVectorizer::vectorize_image(xform2d &mat, const pu
|
|||
|
||||
ClipperLib::Clipper c;
|
||||
c.AddPath(out, ClipperLib::ptSubject, /* closed */ true);
|
||||
if (!clip_path.empty()) {
|
||||
c.AddPaths(clip_path, ClipperLib::ptClip, /* closed */ true);
|
||||
if (!img_ctx.clip().empty()) {
|
||||
c.AddPaths(img_ctx.clip(), ClipperLib::ptClip, /* closed */ true);
|
||||
}
|
||||
c.StrictlySimple(true);
|
||||
ClipperLib::Paths polys;
|
||||
|
|
@ -476,7 +476,7 @@ void gerbolyze::OpenCVContoursVectorizer::vectorize_image(xform2d &mat, const pu
|
|||
out.push_back(std::array<double, 2>{
|
||||
((double)p.X) / clipper_scale, ((double)p.Y) / clipper_scale
|
||||
});
|
||||
sink << out;
|
||||
img_ctx.sink() << out;
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ namespace gerbolyze {
|
|||
public:
|
||||
VoronoiVectorizer(grid_type grid, bool relax=true) : m_relax(relax), m_grid_type(grid) {}
|
||||
|
||||
virtual void vectorize_image(xform2d &mat, const pugi::xml_node &node, ClipperLib::Paths &clip_path, PolygonSink &sink, double min_feature_size_px);
|
||||
virtual void vectorize_image(RenderContext &ctx, const pugi::xml_node &node, double min_feature_size_px);
|
||||
private:
|
||||
double m_relax;
|
||||
grid_type m_grid_type;
|
||||
|
|
@ -39,18 +39,18 @@ namespace gerbolyze {
|
|||
public:
|
||||
OpenCVContoursVectorizer() {}
|
||||
|
||||
virtual void vectorize_image(xform2d &mat, const pugi::xml_node &node, ClipperLib::Paths &clip_path, PolygonSink &sink, double min_feature_size_px);
|
||||
virtual void vectorize_image(RenderContext &ctx, const pugi::xml_node &node, double min_feature_size_px);
|
||||
};
|
||||
|
||||
class DevNullVectorizer : public ImageVectorizer {
|
||||
public:
|
||||
DevNullVectorizer() {}
|
||||
|
||||
virtual void vectorize_image(xform2d &, const pugi::xml_node &, ClipperLib::Paths &, PolygonSink &, double) {}
|
||||
virtual void vectorize_image(RenderContext &, const pugi::xml_node &, double) {}
|
||||
};
|
||||
|
||||
void parse_img_meta(const pugi::xml_node &node, double &x, double &y, double &width, double &height);
|
||||
void draw_bg_rect(xform2d &mat, double width, double height, ClipperLib::Paths &clip_path, PolygonSink &sink);
|
||||
void draw_bg_rect(RenderContext &ctx, double width, double height);
|
||||
void handle_aspect_ratio(std::string spec, double &scale_x, double &scale_y, double &off_x, double &off_y, double cols, double rows);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue