kopia lustrzana https://github.com/jaseg/gerbolyze
svg-flatten: add drill handling to outline mode
rodzic
1622e9c943
commit
14e9d7fbc2
|
@ -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':
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit f09fe2f255be57e6b2427bdbec6772d4f98c1503
|
||||
Subproject commit 79e4adfa2c6e2bfbe63da05cc668eb9ad5596748
|
|
@ -1 +1 @@
|
|||
Subproject commit 53c1f73957450408ec5adc767c214b900d74ce22
|
||||
Subproject commit 80525f93cbabaa11bbcae137f24467f97d74345d
|
|
@ -1 +1 @@
|
|||
Subproject commit 4e21ab305794f5309a1454b4ae82ab9a0f5e0d25
|
||||
Subproject commit cd6805e94dd5d6346be1b75a54cdc27787319dd2
|
|
@ -1 +1 @@
|
|||
Subproject commit 08b3433180727ea2f78fe02e860a08471db1e03c
|
||||
Subproject commit 2639dfd053221d3e8c9e9ff013e58699d9c1af15
|
|
@ -1 +1 @@
|
|||
Subproject commit c9064e317699d2e495f36ba4f9ac037e88ee371a
|
||||
Subproject commit af1a5bc352164740c1cc1354942b1c6b72eacb8a
|
Ładowanie…
Reference in New Issue