svg-flatten: add export of patterns via aperture macros

This commit is contained in:
jaseg 2022-06-20 16:19:53 +02:00
parent 0e1c8507bb
commit 9e9cc2bc01
9 changed files with 136 additions and 7 deletions

View file

@ -56,6 +56,18 @@ namespace gerbolyze {
d2p m_center;
};
class PatternToken {
public:
PatternToken(vector<pair<Polygon, GerberPolarityToken>> &polys) : m_polys(polys) {}
vector<pair<Polygon, GerberPolarityToken>> &m_polys;
};
class FlashToken {
public:
FlashToken(d2p offset) : m_offset(offset) {}
d2p m_offset;
};
class PolygonSink {
public:
virtual ~PolygonSink() {}
@ -80,6 +92,11 @@ namespace gerbolyze {
virtual PolygonSink &operator<<(GerberPolarityToken pol) = 0;
virtual PolygonSink &operator<<(const ApertureToken &) { return *this; };
virtual PolygonSink &operator<<(const DrillToken &) { return *this; };
virtual PolygonSink &operator<<(const FlashToken &) { return *this; };
virtual PolygonSink &operator<<(const PatternToken &) {
cerr << "Error: pattern to aperture mapping is not supporte for this output." << endl;
return *this;
};
virtual void footer() {}
};
@ -129,6 +146,8 @@ namespace gerbolyze {
virtual PolygonScaler &operator<<(GerberPolarityToken pol);
virtual PolygonScaler &operator<<(const ApertureToken &tok);
virtual PolygonScaler &operator<<(const DrillToken &tok);
virtual PolygonScaler &operator<<(const FlashToken &tok);
virtual PolygonScaler &operator<<(const PatternToken &tok);
virtual void footer();
private:
@ -194,10 +213,13 @@ namespace gerbolyze {
double m_minimum_feature_size_mm = 0.1;
double curve_tolerance_mm;
double drill_test_polsby_popper_tolerance = 0.01;
double aperture_circle_test_tolerance = 0.01;
double aperture_rect_test_tolerance = 0.01;
VectorizerSelectorizer &m_vec_sel;
bool outline_mode = false;
bool flip_color_interpretation = false;
bool pattern_complete_tiles_only = false;
bool use_apertures_for_patterns = false;
};
class RenderContext {
@ -212,6 +234,9 @@ namespace gerbolyze {
xform2d transform,
ClipperLib::Paths &clip,
bool included);
RenderContext(RenderContext &parent,
PolygonSink &sink,
ClipperLib::Paths &clip);
PolygonSink &sink() { return m_sink; }
const ElementSelector &sel() { return m_sel; }
@ -305,6 +330,8 @@ namespace gerbolyze {
virtual SimpleGerberOutput &operator<<(GerberPolarityToken pol);
virtual SimpleGerberOutput &operator<<(const DrillToken &tok);
virtual SimpleGerberOutput &operator<<(const ApertureToken &ap);
virtual SimpleGerberOutput &operator<<(const FlashToken &tok);
virtual SimpleGerberOutput &operator<<(const PatternToken &tok);
virtual void header_impl(d2p origin, d2p size);
virtual void footer_impl();
@ -319,6 +346,7 @@ namespace gerbolyze {
bool m_flip_pol;
bool m_outline_mode;
double m_current_aperture;
bool m_macro_aperture;
unsigned int m_aperture_num;
};

View file

@ -76,6 +76,9 @@ int main(int argc, char **argv) {
{"pattern_complete_tiles_only", {"--pattern-complete-tiles-only"},
"Break SVG spec by only rendering complete pattern tiles, i.e. pattern tiles that entirely fit the target area, instead of performing clipping.",
0},
{"use_apertures_for_patterns", {"--use-apertures-for-patterns"},
"Try to use apertures to represent svg patterns where possible.",
0},
{"min_feature_size", {"-d", "--trace-space"},
"Minimum feature size of elements in vectorized graphics (trace/space) in mm. Default: 0.1mm.",
1},
@ -85,6 +88,12 @@ int main(int argc, char **argv) {
{"drill_test_polsby_popper_tolerance", {"--drill-test-tolerance"},
"Tolerance for identifying circles as drills in outline mode",
1},
{"aperture_circle_test_tolerance", {"--circle-test-tolerance"},
"Tolerance for identifying circles as apertures in patterns (--use-apertures-for-patterns)",
1},
{"aperture_rect_test_tolerance", {"--rect-test-tolerance"},
"Tolerance for identifying rectangles as apertures in patterns (--use-apertures-for-patterns)",
1},
{"no_header", {"--no-header"},
"Do not export output format header/footer, only export the primitives themselves",
0},
@ -297,7 +306,9 @@ int main(int argc, char **argv) {
double min_feature_size = args["min_feature_size"].as<double>(0.1); /* mm */
double curve_tolerance = args["curve_tolerance"].as<double>(0.1); /* mm */
double drill_test_polsby_popper_tolerance = args["drill_test_polsby_popper_tolerance"].as<double>(0.1); /* mm */
double drill_test_polsby_popper_tolerance = args["drill_test_polsby_popper_tolerance"].as<double>(0.1);
double aperture_rect_test_tolerance = args["aperture_rect_test_tolerance"].as<double>(0.1);
double aperture_circle_test_tolerance = args["aperture_circle_test_tolerance"].as<double>(0.1);
string ending = "";
auto idx = in_f_name.rfind(".");
@ -427,15 +438,19 @@ int main(int argc, char **argv) {
VectorizerSelectorizer vec_sel(vectorizer, args["vectorizer_map"] ? args["vectorizer_map"].as<string>() : "");
bool flip_svg_colors = args["flip_svg_color_interpretation"];
bool pattern_complete_tiles_only = args["pattern_complete_tiles_only"];
bool use_apertures_for_patterns = args["use_apertures_for_patterns"];
RenderSettings rset {
min_feature_size,
curve_tolerance,
drill_test_polsby_popper_tolerance,
aperture_circle_test_tolerance,
aperture_rect_test_tolerance,
vec_sel,
outline_mode,
flip_svg_colors,
pattern_complete_tiles_only,
use_apertures_for_patterns,
};
SVGDocument doc;

View file

@ -36,6 +36,7 @@ SimpleGerberOutput::SimpleGerberOutput(ostream &out, bool only_polys, int digits
m_flip_pol(flip_polarity),
m_outline_mode(outline_mode),
m_current_aperture(0.0),
m_macro_aperture(false),
m_aperture_num(10) /* See gerber standard */
{
assert(1 <= digits_int && digits_int <= 9);
@ -62,10 +63,11 @@ void SimpleGerberOutput::header_impl(d2p origin, d2p size) {
}
SimpleGerberOutput& SimpleGerberOutput::operator<<(const ApertureToken &ap) {
if (ap.m_size == m_current_aperture) {
if (!m_macro_aperture && ap.m_size == m_current_aperture) {
return *this;
}
m_macro_aperture = false;
m_current_aperture = ap.m_size;
m_aperture_num += 1;
@ -139,3 +141,37 @@ void SimpleGerberOutput::footer_impl() {
m_out << "M02*" << endl;
}
SimpleGerberOutput &SimpleGerberOutput::operator<<(const FlashToken &tok) {
double x = round((tok.m_offset[0] * m_scale + m_offset[0]) * m_gerber_scale);
double y = round((m_height - tok.m_offset[1] * m_scale + m_offset[1]) * m_gerber_scale);
m_out << "X" << setw(m_digits_int + m_digits_frac) << setfill('0') << std::internal /* isn't C++ a marvel of engineering? */ << (long long int)x
<< "Y" << setw(m_digits_int + m_digits_frac) << setfill('0') << std::internal << (long long int)y
<< "D03*" << endl;
return *this;
}
SimpleGerberOutput &SimpleGerberOutput::operator<<(const PatternToken &tok) {
m_macro_aperture = true;
m_aperture_num += 1;
m_out << "%AMmacro" << m_aperture_num << "*" << endl;
for (auto &pair : tok.m_polys) {
int exposure = (pair.second == GRB_POL_DARK) ? 1 : 0;
m_out << 4 << "," << exposure << "," << pair.first.size();
for (auto &pt : pair.first) {
m_out << "," << pt[0] << "," << pt[1];
}
m_out << "," << pair.first.back()[0] << "," << pair.first.back()[1] << "*" << endl;
}
m_out << "%" << endl;
m_out << "%ADD" << m_aperture_num << "macro" << m_aperture_num << "*%" << endl;
m_out << "D" << m_aperture_num << "*" << endl;
return *this;
}

View file

@ -69,3 +69,26 @@ PolygonScaler &PolygonScaler::operator<<(const DrillToken &tok) {
m_sink << DrillToken(new_center);
return *this;
}
PolygonScaler &PolygonScaler::operator<<(const FlashToken &tok) {
d2p new_offset = { tok.m_offset[0] * m_scale, tok.m_offset[1] * m_scale};
m_sink << FlashToken(new_offset);
return *this;
}
PolygonScaler &PolygonScaler::operator<<(const PatternToken &tok) {
vector<pair<Polygon, GerberPolarityToken>> new_polys;
for (size_t i=0; i<tok.m_polys.size(); i++) {
Polygon poly(tok.m_polys[i].first.size());
for (size_t j=0; j<poly.size(); j++) {
d2p new_point = tok.m_polys[i].first[j];
new_point[0] *= m_scale;
new_point[1] *= m_scale;
poly[j] = new_point;
}
new_polys.emplace_back(pair<Polygon, GerberPolarityToken>{poly, tok.m_polys[i].second});
}
m_sink << PatternToken(new_polys);
return *this;
}

View file

@ -135,7 +135,7 @@ KicadSexpOutput &KicadSexpOutput::operator<<(const Polygon &poly) {
return *this;
}
KicadSexpOutput &KicadSexpOutput::operator<<(const DrillToken &tok) {
KicadSexpOutput &KicadSexpOutput::operator<<(const DrillToken &) {
return *this;
}

View file

@ -77,7 +77,7 @@ SimpleSVGOutput &SimpleSVGOutput::operator<<(const Polygon &poly) {
return *this;
}
SimpleSVGOutput &SimpleSVGOutput::operator<<(const DrillToken &tok) {
SimpleSVGOutput &SimpleSVGOutput::operator<<(const DrillToken &) {
return *this;
}

View file

@ -557,3 +557,14 @@ gerbolyze::RenderContext::RenderContext(RenderContext &parent, xform2d transform
m_mat.transform(transform);
}
gerbolyze::RenderContext::RenderContext(RenderContext &parent, PolygonSink &sink, ClipperLib::Paths &clip) :
m_sink(sink),
m_settings(parent.settings()),
m_mat(parent.mat()),
m_root(false),
m_included(false),
m_sel(parent.sel()),
m_clip(clip)
{
}

View file

@ -25,7 +25,7 @@
using namespace std;
gerbolyze::Pattern::Pattern(const pugi::xml_node &node, SVGDocument &doc) : _node(node), doc(&doc) {
gerbolyze::Pattern::Pattern(const pugi::xml_node &node, SVGDocument &doc) : m_node(node), doc(&doc) {
/* Read pattern attributes from SVG node */
x = usvg_double_attr(node, "x");
y = usvg_double_attr(node, "y");
@ -87,6 +87,17 @@ void gerbolyze::Pattern::tile (gerbolyze::RenderContext &ctx) {
/* Switch to pattern coordinates */
RenderContext pat_ctx(ctx, patternTransform);
if (ctx.settings().use_apertures_for_patterns) {
vector<pair<Polygon, GerberPolarityToken>> out;
LambdaPolygonSink list_sink([&out](const Polygon &poly, GerberPolarityToken pol) {
out.emplace_back(pair<Polygon, GerberPolarityToken>{poly, pol});
});
ClipperLib::Paths empty_clip;
RenderContext macro_ctx(pat_ctx, list_sink, empty_clip);
doc->export_svg_group(macro_ctx, m_node);
pat_ctx.sink() << PatternToken(out);
}
/* Iterate over all pattern tiles in pattern coordinates */
for (double inst_off_x = fmod(inst_x, inst_w) - 2*inst_w;
inst_off_x < bx + bw + 2*inst_w;
@ -131,7 +142,12 @@ void gerbolyze::Pattern::tile (gerbolyze::RenderContext &ctx) {
}
}
doc->export_svg_group(elem_ctx, _node);
if (ctx.settings().use_apertures_for_patterns) {
/* use inst_h offset to compensate for gerber <-> svg "y" coordinate spaces */
elem_ctx.sink() << FlashToken(elem_ctx.mat().doc2phys({0, inst_h}));
} else {
doc->export_svg_group(elem_ctx, m_node);
}
}
}
}

View file

@ -47,7 +47,7 @@ private:
xform2d patternTransform_inv;
enum RelativeUnits patternUnits;
enum RelativeUnits patternContentUnits;
const pugi::xml_node _node;
const pugi::xml_node m_node;
SVGDocument *doc = nullptr;
};