kopia lustrzana https://github.com/jaseg/gerbolyze
Multi-layer module export working
rodzic
eac89409b8
commit
a6540b73da
|
@ -37,11 +37,17 @@ namespace gerbolyze {
|
|||
GRB_POL_DARK
|
||||
};
|
||||
|
||||
class LayerNameToken {
|
||||
public:
|
||||
std::string m_name;
|
||||
};
|
||||
|
||||
class PolygonSink {
|
||||
public:
|
||||
virtual ~PolygonSink() {}
|
||||
virtual void header(d2p origin, d2p size) {(void) origin; (void) size;}
|
||||
virtual PolygonSink &operator<<(const Polygon &poly) = 0;
|
||||
virtual PolygonSink &operator<<(const LayerNameToken &) { return *this; };
|
||||
virtual PolygonSink &operator<<(GerberPolarityToken pol) = 0;
|
||||
virtual void footer() {}
|
||||
};
|
||||
|
@ -53,11 +59,13 @@ namespace gerbolyze {
|
|||
virtual ~Flattener();
|
||||
virtual void header(d2p origin, d2p size);
|
||||
virtual Flattener &operator<<(const Polygon &poly);
|
||||
virtual Flattener &operator<<(const LayerNameToken &layer_name);
|
||||
virtual Flattener &operator<<(GerberPolarityToken pol);
|
||||
virtual void footer();
|
||||
|
||||
private:
|
||||
void render_out_clear_polys();
|
||||
void flush_polys_to_sink();
|
||||
PolygonSink &m_sink;
|
||||
GerberPolarityToken m_current_polarity = GRB_POL_DARK;
|
||||
Flattener_D *d;
|
||||
|
@ -78,17 +86,20 @@ namespace gerbolyze {
|
|||
std::ostream &m_out;
|
||||
};
|
||||
|
||||
extern const std::vector<std::string> kicad_default_layers;
|
||||
|
||||
class ElementSelector {
|
||||
public:
|
||||
virtual bool match(const pugi::xml_node &node, bool included) const = 0;
|
||||
virtual bool match(const pugi::xml_node &node, bool included, bool is_root) const = 0;
|
||||
};
|
||||
|
||||
class IDElementSelector : public ElementSelector {
|
||||
public:
|
||||
virtual bool match(const pugi::xml_node &node, bool included) const;
|
||||
virtual bool match(const pugi::xml_node &node, bool included, bool is_root) const;
|
||||
|
||||
std::vector<std::string> include;
|
||||
std::vector<std::string> exclude;
|
||||
const std::vector<std::string> *layers = nullptr;
|
||||
};
|
||||
|
||||
class ImageVectorizer {
|
||||
|
@ -144,7 +155,7 @@ 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 RenderSettings &rset, const pugi::xml_node &group, ClipperLib::Paths &parent_clip_path, const ElementSelector *sel=nullptr, bool included=true);
|
||||
void export_svg_group(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(const RenderSettings &rset, const pugi::xml_node &node, ClipperLib::Paths &clip_path);
|
||||
void setup_debug_output(std::string filename="");
|
||||
void setup_viewport_clip();
|
||||
|
@ -225,27 +236,21 @@ namespace gerbolyze {
|
|||
KicadSexpOutput(std::ostream &out, std::string mod_name, std::string layer, bool only_polys=false, std::string m_ref_text="", std::string m_val_text="G*****", d2p ref_pos={0,10}, d2p val_pos={0,-10});
|
||||
virtual ~KicadSexpOutput() {}
|
||||
virtual KicadSexpOutput &operator<<(const Polygon &poly);
|
||||
virtual KicadSexpOutput &operator<<(const LayerNameToken &layer_name);
|
||||
virtual KicadSexpOutput &operator<<(GerberPolarityToken pol);
|
||||
virtual void header_impl(d2p origin, d2p size);
|
||||
virtual void footer_impl();
|
||||
|
||||
void set_export_layers(const std::vector<std::string> &layers) { m_export_layers = &layers; }
|
||||
|
||||
private:
|
||||
const std::vector<std::string> *m_export_layers = &kicad_default_layers;
|
||||
std::string m_mod_name;
|
||||
std::string m_layer;
|
||||
bool m_auto_layer;
|
||||
std::string m_ref_text;
|
||||
std::string m_val_text;
|
||||
d2p m_ref_pos;
|
||||
d2p m_val_pos;
|
||||
};
|
||||
|
||||
|
||||
/* TODO
|
||||
class SExpOutput : public StreamPolygonSink {
|
||||
public:
|
||||
virtual SExpOutput &operator<<(const Polygon &poly);
|
||||
virtual SExpOutput &operator<<(GerberPolarityToken pol);
|
||||
virtual void header_impl(d2p origin, d2p size);
|
||||
virtual void footer_impl();
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
|
15
src/main.cpp
15
src/main.cpp
|
@ -71,7 +71,7 @@ int main(int argc, char **argv) {
|
|||
"Module name for KiCAD S-Exp output",
|
||||
1},
|
||||
{"sexp_layer", {"--sexp-layer"},
|
||||
"Layer for KiCAD S-Exp output",
|
||||
"Layer for KiCAD S-Exp output. Defaults to auto-detect layers from SVG layer/top-level group names",
|
||||
1},
|
||||
{"preserve_aspect_ratio", {"-a", "--preserve-aspect-ratio"},
|
||||
"Bitmap mode only: Preserve aspect ratio of image. Allowed values are meet, slice. Can also parse full SVG preserveAspectRatio syntax.",
|
||||
|
@ -160,6 +160,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); }); /* c++ yeah */
|
||||
|
||||
string sexp_layer = args["sexp_layer"] ? args["sexp_layer"].as<string>() : "auto";
|
||||
|
||||
bool force_flatten = false;
|
||||
PolygonSink *sink = nullptr;
|
||||
PolygonSink *flattener = nullptr;
|
||||
|
@ -172,14 +174,14 @@ int main(int argc, char **argv) {
|
|||
sink = new SimpleGerberOutput(*out_f, only_polys, 4, precision);
|
||||
|
||||
} else if (fmt == "s-exp" || fmt == "sexp" || fmt == "kicad") {
|
||||
if (!args["sexp_mod_name"] || !args["sexp_layer"]) {
|
||||
cerr << "--sexp-mod-name and --sexp-layer must be given for sexp export" << endl;
|
||||
if (!args["sexp_mod_name"]) {
|
||||
cerr << "--sexp-mod-name must be given for sexp export" << endl;
|
||||
argagg::fmt_ostream fmt(cerr);
|
||||
fmt << usage.str() << argparser;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
sink = new KicadSexpOutput(*out_f, args["sexp_mod_name"], args["sexp_layer"], only_polys);
|
||||
sink = new KicadSexpOutput(*out_f, args["sexp_mod_name"], sexp_layer, only_polys);
|
||||
force_flatten = true;
|
||||
|
||||
} else {
|
||||
|
@ -206,6 +208,9 @@ int main(int argc, char **argv) {
|
|||
id_match(args["only_groups"], sel.include);
|
||||
if (args["exclude_groups"])
|
||||
id_match(args["exclude_groups"], sel.exclude);
|
||||
if (sexp_layer == "auto") {
|
||||
sel.layers = &gerbolyze::kicad_default_layers;
|
||||
}
|
||||
|
||||
string vectorizer = args["vectorizer"] ? args["vectorizer"] : "poisson-disc";
|
||||
/* Check argument */
|
||||
|
@ -320,7 +325,7 @@ int main(int argc, char **argv) {
|
|||
|
||||
} else {
|
||||
cerr << "calling usvg on " << barf << " and " << frob << endl;
|
||||
const char *command_line[] = {"usvg", barf.c_str(), frob.c_str(), NULL};
|
||||
const char *command_line[] = {"usvg", "--keep-named-groups", barf.c_str(), frob.c_str(), NULL};
|
||||
struct subprocess_s subprocess;
|
||||
int rc = subprocess_create(command_line, subprocess_option_inherit_environment, &subprocess);
|
||||
if (rc) {
|
||||
|
|
|
@ -144,15 +144,19 @@ Flattener &Flattener::operator<<(GerberPolarityToken pol) {
|
|||
return *this;
|
||||
}
|
||||
|
||||
Flattener &Flattener::operator<<(const Polygon &poly) {
|
||||
static int i=0, j=0;
|
||||
Flattener &Flattener::operator<<(const LayerNameToken &layer_name) {
|
||||
flush_polys_to_sink();
|
||||
m_sink << layer_name;
|
||||
cerr << "Flattener forwarding layer name to sink: \"" << layer_name.m_name << "\"" << endl;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Flattener &Flattener::operator<<(const Polygon &poly) {
|
||||
if (m_current_polarity == GRB_POL_DARK) {
|
||||
d->add_dark_polygon(poly);
|
||||
cerr << "dark primitive " << i++ << endl;
|
||||
|
||||
} else { /* clear */
|
||||
cerr << "clear primitive " << j++ << endl;
|
||||
d->add_clear_polygon(poly);
|
||||
render_out_clear_polys();
|
||||
}
|
||||
|
@ -160,10 +164,8 @@ Flattener &Flattener::operator<<(const Polygon &poly) {
|
|||
return *this;
|
||||
}
|
||||
|
||||
void Flattener::footer() {
|
||||
if (m_current_polarity == GRB_POL_CLEAR) {
|
||||
render_out_clear_polys();
|
||||
}
|
||||
void Flattener::flush_polys_to_sink() {
|
||||
*this << GRB_POL_DARK; /* force render */
|
||||
m_sink << GRB_POL_DARK;
|
||||
|
||||
for (auto &poly : d->dark_polys) {
|
||||
|
@ -174,6 +176,12 @@ void Flattener::footer() {
|
|||
m_sink << poly_out;
|
||||
}
|
||||
|
||||
d->clear_polys.clear();
|
||||
d->dark_polys.clear();
|
||||
}
|
||||
|
||||
void Flattener::footer() {
|
||||
flush_polys_to_sink();
|
||||
m_sink.footer();
|
||||
}
|
||||
|
||||
|
|
|
@ -32,7 +32,8 @@ using namespace std;
|
|||
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),
|
||||
m_layer(layer),
|
||||
m_layer(layer == "auto" ? "unknown" : layer),
|
||||
m_auto_layer(layer == "auto"),
|
||||
m_val_text(val_text),
|
||||
m_ref_pos(ref_pos),
|
||||
m_val_pos(val_pos)
|
||||
|
@ -63,7 +64,28 @@ KicadSexpOutput &KicadSexpOutput::operator<<(GerberPolarityToken pol) {
|
|||
return *this;
|
||||
}
|
||||
|
||||
KicadSexpOutput &KicadSexpOutput::operator<<(const LayerNameToken &layer_name) {
|
||||
if (!m_auto_layer)
|
||||
return *this;
|
||||
|
||||
cerr << "Setting S-Exp export layer to \"" << layer_name.m_name << "\"" << endl;
|
||||
if (!layer_name.m_name.empty()) {
|
||||
m_layer = layer_name.m_name;
|
||||
} else {
|
||||
m_layer = "unknown";
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
KicadSexpOutput &KicadSexpOutput::operator<<(const Polygon &poly) {
|
||||
if (m_auto_layer) {
|
||||
if (std::find(m_export_layers->begin(), m_export_layers->end(), m_layer) == m_export_layers->end()) {
|
||||
cerr << "Rejecting S-Exp export layer \"" << m_layer << "\"" << endl;
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
|
||||
if (poly.size() < 3) {
|
||||
cerr << "Warning: " << poly.size() << "-element polygon passed to KicadSexpOutput" << endl;
|
||||
return *this;
|
||||
|
|
|
@ -117,12 +117,21 @@ double gerbolyze::SVGDocument::doc_units_to_mm(double px) const {
|
|||
return px / (vb_w / page_w_mm);
|
||||
}
|
||||
|
||||
bool IDElementSelector::match(const pugi::xml_node &node, bool included) const {
|
||||
bool IDElementSelector::match(const pugi::xml_node &node, bool included, bool is_root) const {
|
||||
string id = node.attribute("id").value();
|
||||
if (is_root && layers) {
|
||||
bool layer_match = std::find(layers->begin(), layers->end(), id) != layers->end();
|
||||
if (!layer_match) {
|
||||
cerr << "Rejecting layer \"" << id << "\"" << endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (include.empty() && exclude.empty())
|
||||
return true;
|
||||
|
||||
bool include_match = std::find(include.begin(), include.end(), node.attribute("id").value()) != include.end();
|
||||
bool exclude_match = std::find(exclude.begin(), exclude.end(), node.attribute("id").value()) != exclude.end();
|
||||
bool include_match = std::find(include.begin(), include.end(), id) != include.end();
|
||||
bool exclude_match = std::find(exclude.begin(), exclude.end(), id) != exclude.end();
|
||||
|
||||
if (exclude_match || (!included && !include_match)) {
|
||||
return false;
|
||||
|
@ -132,7 +141,7 @@ bool IDElementSelector::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 RenderSettings &rset, 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, bool is_root) {
|
||||
/* Enter the group's coordinate system */
|
||||
cairo_save(cr);
|
||||
apply_cairo_transform_from_svg(cr, group.attribute("transform").value());
|
||||
|
@ -162,12 +171,23 @@ void gerbolyze::SVGDocument::export_svg_group(const RenderSettings &rset, const
|
|||
|
||||
/* Iterate over the group's children, exporting them one by one. */
|
||||
for (const auto &node : group.children()) {
|
||||
if (sel && !sel->match(node, included))
|
||||
if (sel && !sel->match(node, included, is_root))
|
||||
continue;
|
||||
|
||||
string name(node.name());
|
||||
if (name == "g") {
|
||||
if (is_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;
|
||||
}
|
||||
|
||||
export_svg_group(rset, node, clip_path, sel, true);
|
||||
|
||||
if (is_root) {
|
||||
LayerNameToken tok {""};
|
||||
*polygon_sink << tok;
|
||||
}
|
||||
|
||||
} else if (name == "path") {
|
||||
export_svg_path(rset, node, clip_path);
|
||||
|
@ -379,7 +399,7 @@ void gerbolyze::SVGDocument::render(const RenderSettings &rset, PolygonSink &sin
|
|||
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(rset, root_elem, vb_paths, sel, false);
|
||||
export_svg_group(rset, root_elem, vb_paths, sel, false, true);
|
||||
sink.footer();
|
||||
}
|
||||
|
||||
|
@ -484,3 +504,35 @@ void gerbolyze::SVGDocument::load_clips() {
|
|||
}
|
||||
}
|
||||
|
||||
/* 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",
|
||||
});
|
||||
|
|
Ładowanie…
Reference in New Issue