diff options
-rwxr-xr-x | gerbolyze/__init__.py | 2 | ||||
-rw-r--r-- | svg-flatten/include/gerbolyze.hpp | 16 | ||||
-rw-r--r-- | svg-flatten/src/main.cpp | 5 | ||||
-rw-r--r-- | svg-flatten/src/nopencv.cpp | 27 | ||||
-rw-r--r-- | svg-flatten/src/nopencv.hpp | 2 | ||||
-rw-r--r-- | svg-flatten/src/out_dilater.cpp | 4 | ||||
-rw-r--r-- | svg-flatten/src/out_flattener.cpp | 10 | ||||
-rw-r--r-- | svg-flatten/src/out_gerber.cpp | 12 | ||||
-rw-r--r-- | svg-flatten/src/out_scaler.cpp | 10 | ||||
-rw-r--r-- | svg-flatten/src/out_sexp.cpp | 4 | ||||
-rw-r--r-- | svg-flatten/src/out_svg.cpp | 4 | ||||
-rw-r--r-- | svg-flatten/src/svg_doc.cpp | 31 | ||||
m--------- | upstream/argagg | 0 | ||||
m--------- | upstream/cpp-base64 | 0 | ||||
m--------- | upstream/filesystem | 0 | ||||
m--------- | upstream/pugixml | 0 | ||||
m--------- | upstream/stb | 0 |
17 files changed, 123 insertions, 4 deletions
diff --git a/gerbolyze/__init__.py b/gerbolyze/__init__.py index 4203521..2c293d8 100755 --- a/gerbolyze/__init__.py +++ b/gerbolyze/__init__.py @@ -242,7 +242,7 @@ def convert(input_svg, output_gerbers, is_zip, dilate, curve_tolerance, no_subtr grb = svg_to_gerber(input_svg, trace_space=trace_space, vectorizer=vectorizer, vectorizer_map=vectorizer_map, exclude_groups=exclude_groups, curve_tolerance=curve_tolerance, only_groups=group_id, - outline_mode=(use == 'outline')) + outline_mode=(use == 'outline' or use == 'drill')) if use == 'drill': if side == 'plated': diff --git a/svg-flatten/include/gerbolyze.hpp b/svg-flatten/include/gerbolyze.hpp index 9311a98..cd837ba 100644 --- a/svg-flatten/include/gerbolyze.hpp +++ b/svg-flatten/include/gerbolyze.hpp @@ -50,6 +50,12 @@ namespace gerbolyze { double m_size = 0.0; }; + class DrillToken { + public: + DrillToken(d2p center) : m_center(center) {} + d2p m_center; + }; + class PolygonSink { public: virtual ~PolygonSink() {} @@ -73,6 +79,7 @@ namespace gerbolyze { virtual PolygonSink &operator<<(const LayerNameToken &) { return *this; }; virtual PolygonSink &operator<<(GerberPolarityToken pol) = 0; virtual PolygonSink &operator<<(const ApertureToken &) { return *this; }; + virtual PolygonSink &operator<<(const DrillToken &) { return *this; }; virtual void footer() {} }; @@ -85,6 +92,8 @@ namespace gerbolyze { virtual Flattener &operator<<(const Polygon &poly); virtual Flattener &operator<<(const LayerNameToken &layer_name); virtual Flattener &operator<<(GerberPolarityToken pol); + virtual Flattener &operator<<(const ApertureToken &tok); + virtual Flattener &operator<<(const DrillToken &tok); virtual void footer(); private: @@ -102,6 +111,7 @@ namespace gerbolyze { virtual Dilater &operator<<(const Polygon &poly); virtual Dilater &operator<<(const LayerNameToken &layer_name); virtual Dilater &operator<<(GerberPolarityToken pol); + virtual Dilater &operator<<(const ApertureToken &ap); virtual void footer(); private: @@ -117,6 +127,8 @@ namespace gerbolyze { virtual PolygonScaler &operator<<(const Polygon &poly); virtual PolygonScaler &operator<<(const LayerNameToken &layer_name); virtual PolygonScaler &operator<<(GerberPolarityToken pol); + virtual PolygonScaler &operator<<(const ApertureToken &tok); + virtual PolygonScaler &operator<<(const DrillToken &tok); virtual void footer(); private: @@ -181,6 +193,7 @@ namespace gerbolyze { public: double m_minimum_feature_size_mm = 0.1; double curve_tolerance_mm; + double drill_test_polsby_popper_tolerance = 0.01; VectorizerSelectorizer &m_vec_sel; bool outline_mode = false; bool flip_color_interpretation = false; @@ -289,6 +302,7 @@ namespace gerbolyze { virtual ~SimpleGerberOutput() {} virtual SimpleGerberOutput &operator<<(const Polygon &poly); virtual SimpleGerberOutput &operator<<(GerberPolarityToken pol); + virtual SimpleGerberOutput &operator<<(const DrillToken &tok); virtual SimpleGerberOutput &operator<<(const ApertureToken &ap); virtual void header_impl(d2p origin, d2p size); virtual void footer_impl(); @@ -313,6 +327,7 @@ namespace gerbolyze { virtual ~SimpleSVGOutput() {} virtual SimpleSVGOutput &operator<<(const Polygon &poly); virtual SimpleSVGOutput &operator<<(GerberPolarityToken pol); + virtual SimpleSVGOutput &operator<<(const DrillToken &tok); virtual void header_impl(d2p origin, d2p size); virtual void footer_impl(); @@ -330,6 +345,7 @@ namespace gerbolyze { virtual ~KicadSexpOutput() {} virtual KicadSexpOutput &operator<<(const Polygon &poly); virtual KicadSexpOutput &operator<<(const LayerNameToken &layer_name); + virtual KicadSexpOutput &operator<<(const DrillToken &tok); virtual KicadSexpOutput &operator<<(GerberPolarityToken pol); virtual void header_impl(d2p origin, d2p size); virtual void footer_impl(); diff --git a/svg-flatten/src/main.cpp b/svg-flatten/src/main.cpp index 817ba67..188aa57 100644 --- a/svg-flatten/src/main.cpp +++ b/svg-flatten/src/main.cpp @@ -79,6 +79,9 @@ int main(int argc, char **argv) { {"curve_tolerance", {"-c", "--curve-tolerance"}, "Tolerance for curve flattening in mm. Default: 0.1mm.", 1}, + {"drill_test_polsby_popper_tolerance", {"--drill-test-tolerance"}, + "Tolerance for identifying circles as drills in outline mode", + 1}, {"no_header", {"--no-header"}, "Do not export output format header/footer, only export the primitives themselves", 0}, @@ -291,6 +294,7 @@ 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 */ string ending = ""; auto idx = in_f_name.rfind("."); @@ -423,6 +427,7 @@ int main(int argc, char **argv) { RenderSettings rset { min_feature_size, curve_tolerance, + drill_test_polsby_popper_tolerance, vec_sel, outline_mode, flip_svg_colors, diff --git a/svg-flatten/src/nopencv.cpp b/svg-flatten/src/nopencv.cpp index 121e9e1..e157ab5 100644 --- a/svg-flatten/src/nopencv.cpp +++ b/svg-flatten/src/nopencv.cpp @@ -483,6 +483,33 @@ double gerbolyze::nopencv::polygon_area(Polygon_i &poly) { return acc / 2; } +double gerbolyze::nopencv::polygon_perimeter(Polygon_i &poly) { + double acc = 0; + size_t prev = poly.size() - 1; + for (size_t cur=0; cur<poly.size(); cur++) { + double dx = poly[cur][0] - poly[prev][0]; + double dy = poly[cur][1] - poly[prev][1]; + acc += sqrt(dx*dx + dy*dy); + prev = cur; + } + return acc; +} + +d2p gerbolyze::nopencv::polygon_centroid(Polygon_i &poly) { + double acc_x = 0, acc_y = 0; + + double area = polygon_area(poly); + size_t prev = poly.size() - 1; + for (size_t cur=0; cur<poly.size(); cur++) { + double a = poly[prev][1]*poly[cur][0] - poly[cur][1]*poly[prev][0]; + acc_x += (poly[prev][0] + poly[cur][0]) * a; + acc_y += (poly[prev][1] + poly[cur][1]) * a; + prev = cur; + } + + return { acc_x / (6*area), acc_y / (6*area) }; +} + template<typename T> gerbolyze::nopencv::Image<T>::Image(int size_x, int size_y, const T *data) { assert(size_x > 0 && size_x < 100000); diff --git a/svg-flatten/src/nopencv.hpp b/svg-flatten/src/nopencv.hpp index 5dd399d..bbc190b 100644 --- a/svg-flatten/src/nopencv.hpp +++ b/svg-flatten/src/nopencv.hpp @@ -119,6 +119,8 @@ namespace gerbolyze { ContourCallback simplify_contours_douglas_peucker(ContourCallback cb); double polygon_area(Polygon_i &poly); + double polygon_perimeter(Polygon_i &poly); + d2p polygon_centroid(Polygon_i &poly); } } diff --git a/svg-flatten/src/out_dilater.cpp b/svg-flatten/src/out_dilater.cpp index e9aefa8..a5b0619 100644 --- a/svg-flatten/src/out_dilater.cpp +++ b/svg-flatten/src/out_dilater.cpp @@ -84,3 +84,7 @@ Dilater &Dilater::operator<<(const Polygon &poly) { return *this; } +Dilater &Dilater::operator<<(const ApertureToken &ap) { + m_sink << ApertureToken(ap.m_size + 2*m_dilation); + return *this; +} diff --git a/svg-flatten/src/out_flattener.cpp b/svg-flatten/src/out_flattener.cpp index 8868ca2..0eae854 100644 --- a/svg-flatten/src/out_flattener.cpp +++ b/svg-flatten/src/out_flattener.cpp @@ -152,6 +152,16 @@ Flattener &Flattener::operator<<(const LayerNameToken &layer_name) { return *this; } +Flattener &Flattener::operator<<(const DrillToken &tok) { + m_sink << tok; + return *this; +} + +Flattener &Flattener::operator<<(const ApertureToken &tok) { + m_sink << tok; + return *this; +} + Flattener &Flattener::operator<<(const Polygon &poly) { if (m_current_polarity == GRB_POL_DARK) { d->add_dark_polygon(poly); diff --git a/svg-flatten/src/out_gerber.cpp b/svg-flatten/src/out_gerber.cpp index b1c5875..c43d07d 100644 --- a/svg-flatten/src/out_gerber.cpp +++ b/svg-flatten/src/out_gerber.cpp @@ -65,6 +65,7 @@ SimpleGerberOutput& SimpleGerberOutput::operator<<(const ApertureToken &ap) { if (ap.m_size == m_current_aperture) { return *this; } + m_current_aperture = ap.m_size; m_aperture_num += 1; @@ -123,6 +124,17 @@ SimpleGerberOutput& SimpleGerberOutput::operator<<(const Polygon &poly) { return *this; } +SimpleGerberOutput &SimpleGerberOutput::operator<<(const DrillToken &tok) { + double x = round((tok.m_center[0] * m_scale + m_offset[0]) * m_gerber_scale); + double y = round((m_height - tok.m_center[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; +} + void SimpleGerberOutput::footer_impl() { m_out << "M02*" << endl; } diff --git a/svg-flatten/src/out_scaler.cpp b/svg-flatten/src/out_scaler.cpp index 9b7de4e..0320d69 100644 --- a/svg-flatten/src/out_scaler.cpp +++ b/svg-flatten/src/out_scaler.cpp @@ -29,6 +29,7 @@ using namespace gerbolyze; using namespace std; +/* FIXME thoroughly test ApertureToken scale handling */ 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}); } @@ -45,7 +46,11 @@ PolygonScaler &PolygonScaler::operator<<(const LayerNameToken &layer_name) { PolygonScaler &PolygonScaler::operator<<(GerberPolarityToken pol) { m_sink << pol; + return *this; +} +PolygonScaler &PolygonScaler::operator<<(const ApertureToken &tok) { + m_sink << ApertureToken(tok.m_size * m_scale); return *this; } @@ -59,3 +64,8 @@ PolygonScaler &PolygonScaler::operator<<(const Polygon &poly) { return *this; } +PolygonScaler &PolygonScaler::operator<<(const DrillToken &tok) { + d2p new_center { tok.m_center[0] * m_scale, tok.m_center[1] * m_scale }; + m_sink << DrillToken(new_center); + return *this; +} diff --git a/svg-flatten/src/out_sexp.cpp b/svg-flatten/src/out_sexp.cpp index 1a7f1d0..6760920 100644 --- a/svg-flatten/src/out_sexp.cpp +++ b/svg-flatten/src/out_sexp.cpp @@ -135,6 +135,10 @@ KicadSexpOutput &KicadSexpOutput::operator<<(const Polygon &poly) { return *this; } +KicadSexpOutput &KicadSexpOutput::operator<<(const DrillToken &tok) { + return *this; +} + void KicadSexpOutput::footer_impl() { m_out << ")" << endl; } diff --git a/svg-flatten/src/out_svg.cpp b/svg-flatten/src/out_svg.cpp index d94edc0..4ecd979 100644 --- a/svg-flatten/src/out_svg.cpp +++ b/svg-flatten/src/out_svg.cpp @@ -77,6 +77,10 @@ SimpleSVGOutput &SimpleSVGOutput::operator<<(const Polygon &poly) { return *this; } +SimpleSVGOutput &SimpleSVGOutput::operator<<(const DrillToken &tok) { + return *this; +} + void SimpleSVGOutput::footer_impl() { //cerr << "svg: footer" << endl; m_out << "</svg>" << endl; diff --git a/svg-flatten/src/svg_doc.cpp b/svg-flatten/src/svg_doc.cpp index fb8d311..7ff0a11 100644 --- a/svg-flatten/src/svg_doc.cpp +++ b/svg-flatten/src/svg_doc.cpp @@ -18,6 +18,7 @@ #include <iostream> #include <fstream> +#include <cmath> #include <gerbolyze.hpp> #include "svg_import_defs.h" @@ -25,6 +26,7 @@ #include "svg_geom.h" #include "svg_path.h" #include "vec_core.h" +#include "nopencv.hpp" using namespace gerbolyze; using namespace std; @@ -243,7 +245,6 @@ void gerbolyze::SVGDocument::export_svg_path(RenderContext &ctx, const pugi::xml } /* Load path from SVG path data and transform into document units. */ - /* FIXME transform stroke width here? */ stroke_width = ctx.mat().doc2phys_dist(stroke_width); Paths stroke_open, stroke_closed; @@ -260,6 +261,31 @@ void gerbolyze::SVGDocument::export_svg_path(RenderContext &ctx, const pugi::xml /* Skip filling for transparent fills. In outline mode, skip filling if a stroke is also set to avoid double lines. */ if (has_fill && !(ctx.settings().outline_mode && has_stroke)) { + /* In outline mode, identify drills before applying clip */ + if (ctx.settings().outline_mode && fill_color != GRB_PATTERN_FILL) { + /* Polsby-Popper test */ + for (auto &p : fill_paths) { + Polygon_i geom_poly(p.size()); + for (size_t i=0; i<p.size(); i++) { + geom_poly[i] = { p[i].X, p[i].Y }; + } + + double area = nopencv::polygon_area(geom_poly); + double polsby_popper = 4*M_PI * area / pow(nopencv::polygon_perimeter(geom_poly), 2); + polsby_popper = fabs(fabs(polsby_popper) - 1.0); + if (polsby_popper < ctx.settings().drill_test_polsby_popper_tolerance) { + d2p centroid = nopencv::polygon_centroid(geom_poly); + centroid[0] /= clipper_scale; + centroid[1] /= clipper_scale; + double diameter = sqrt(4*fabs(area)/M_PI) / clipper_scale; + diameter = ctx.mat().doc2phys_dist(diameter); /* FIXME is this correct w.r.t. PolygonScaler? */ + diameter = round(diameter * 1000.0) / 1000.0; /* Round to micrometer precsion; FIXME: make configurable */ + ctx.sink() << ApertureToken(diameter) << DrillToken(ctx.mat().doc2phys(centroid)); + } + } + return; + } + /* Clip paths. Consider all paths closed for filling. */ if (!ctx.clip().empty()) { Clipper c; @@ -306,7 +332,7 @@ void gerbolyze::SVGDocument::export_svg_path(RenderContext &ctx, const pugi::xml if (ctx.settings().outline_mode && !out.empty()) out.push_back(out[0]); - ctx.sink() << (fill_color == GRB_DARK ? GRB_POL_DARK : GRB_POL_CLEAR) << out; + ctx.sink() << ApertureToken() << (fill_color == GRB_DARK ? GRB_POL_DARK : GRB_POL_CLEAR) << out; } } } @@ -396,7 +422,6 @@ void gerbolyze::SVGDocument::export_svg_path(RenderContext &ctx, const pugi::xml ctx.sink() << ApertureToken() << (stroke_color == GRB_DARK ? GRB_POL_DARK : GRB_POL_CLEAR) << s_polys; } } - ctx.sink() << ApertureToken(); } void gerbolyze::SVGDocument::render(const RenderSettings &rset, PolygonSink &sink, const ElementSelector &sel) { diff --git a/upstream/argagg b/upstream/argagg -Subproject f09fe2f255be57e6b2427bdbec6772d4f98c150 +Subproject 79e4adfa2c6e2bfbe63da05cc668eb9ad559674 diff --git a/upstream/cpp-base64 b/upstream/cpp-base64 -Subproject 53c1f73957450408ec5adc767c214b900d74ce2 +Subproject 80525f93cbabaa11bbcae137f24467f97d74345 diff --git a/upstream/filesystem b/upstream/filesystem -Subproject 4e21ab305794f5309a1454b4ae82ab9a0f5e0d2 +Subproject cd6805e94dd5d6346be1b75a54cdc27787319dd diff --git a/upstream/pugixml b/upstream/pugixml -Subproject 08b3433180727ea2f78fe02e860a08471db1e03 +Subproject 2639dfd053221d3e8c9e9ff013e58699d9c1af1 diff --git a/upstream/stb b/upstream/stb -Subproject c9064e317699d2e495f36ba4f9ac037e88ee371 +Subproject af1a5bc352164740c1cc1354942b1c6b72eacb8 |