aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/gerbolyze.hpp33
-rw-r--r--src/main.cpp15
-rw-r--r--src/out_flattener.cpp24
-rw-r--r--src/out_sexp.cpp24
-rw-r--r--src/svg_doc.cpp64
5 files changed, 126 insertions, 34 deletions
diff --git a/include/gerbolyze.hpp b/include/gerbolyze.hpp
index af25571..a48c3bf 100644
--- a/include/gerbolyze.hpp
+++ b/include/gerbolyze.hpp
@@ -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();
- }
- */
}
diff --git a/src/main.cpp b/src/main.cpp
index 52110b6..45e2cfb 100644
--- a/src/main.cpp
+++ b/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) {
diff --git a/src/out_flattener.cpp b/src/out_flattener.cpp
index 5816f88..8868ca2 100644
--- a/src/out_flattener.cpp
+++ b/src/out_flattener.cpp
@@ -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();
}
diff --git a/src/out_sexp.cpp b/src/out_sexp.cpp
index 90adef2..9a04416 100644
--- a/src/out_sexp.cpp
+++ b/src/out_sexp.cpp
@@ -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;
diff --git a/src/svg_doc.cpp b/src/svg_doc.cpp
index 39c5724..21af576 100644
--- a/src/svg_doc.cpp
+++ b/src/svg_doc.cpp
@@ -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",
+ });