kopia lustrzana https://github.com/jaseg/gerbolyze
Fix outline mode clip & flash handling
rodzic
9e9cc2bc01
commit
e4a0c1ba4a
|
@ -213,8 +213,10 @@ def empty_template(output_svg, size, force, copper_layers, no_default_layers, la
|
|||
@click.option('--vectorizer-map', help='passed through to svg-flatten')
|
||||
@click.option('--exclude-groups', help='passed through to svg-flatten')
|
||||
@click.option('--pattern-complete-tiles-only', is_flag=True, help='passed through to svg-flatten')
|
||||
@click.option('--use-apertures-for-patterns', is_flag=True, help='passed through to svg-flatten')
|
||||
def convert(input_svg, output_gerbers, is_zip, dilate, curve_tolerance, no_subtract, subtract, trace_space, vectorizer,
|
||||
vectorizer_map, exclude_groups, separate_drill, naming_scheme, pattern_complete_tiles_only):
|
||||
vectorizer_map, exclude_groups, separate_drill, naming_scheme,
|
||||
pattern_complete_tiles_only, use_apertures_for_patterns):
|
||||
''' Convert SVG file directly to gerbers.
|
||||
|
||||
Unlike `gerbolyze paste`, this does not add the SVG's contents to existing gerbers. It allows you to directly create
|
||||
|
@ -244,6 +246,7 @@ def convert(input_svg, output_gerbers, is_zip, dilate, curve_tolerance, no_subtr
|
|||
trace_space=trace_space, vectorizer=vectorizer, vectorizer_map=vectorizer_map,
|
||||
exclude_groups=exclude_groups, curve_tolerance=curve_tolerance, only_groups=group_id,
|
||||
pattern_complete_tiles_only=pattern_complete_tiles_only,
|
||||
use_apertures_for_patterns=(use_apertures_for_patterns and use not in ('outline', 'drill')),
|
||||
outline_mode=(use == 'outline' or use == 'drill'))
|
||||
grb.original_path = Path()
|
||||
|
||||
|
|
|
@ -50,12 +50,6 @@ namespace gerbolyze {
|
|||
double m_size = 0.0;
|
||||
};
|
||||
|
||||
class DrillToken {
|
||||
public:
|
||||
DrillToken(d2p center) : m_center(center) {}
|
||||
d2p m_center;
|
||||
};
|
||||
|
||||
class PatternToken {
|
||||
public:
|
||||
PatternToken(vector<pair<Polygon, GerberPolarityToken>> &polys) : m_polys(polys) {}
|
||||
|
@ -91,7 +85,6 @@ 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 PolygonSink &operator<<(const FlashToken &) { return *this; };
|
||||
virtual PolygonSink &operator<<(const PatternToken &) {
|
||||
cerr << "Error: pattern to aperture mapping is not supporte for this output." << endl;
|
||||
|
@ -110,7 +103,7 @@ namespace gerbolyze {
|
|||
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 Flattener &operator<<(const FlashToken &tok);
|
||||
virtual void footer();
|
||||
|
||||
private:
|
||||
|
@ -129,6 +122,7 @@ namespace gerbolyze {
|
|||
virtual Dilater &operator<<(const LayerNameToken &layer_name);
|
||||
virtual Dilater &operator<<(GerberPolarityToken pol);
|
||||
virtual Dilater &operator<<(const ApertureToken &ap);
|
||||
virtual Dilater &operator<<(const FlashToken &tok);
|
||||
virtual void footer();
|
||||
|
||||
private:
|
||||
|
@ -145,7 +139,6 @@ namespace gerbolyze {
|
|||
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 PolygonScaler &operator<<(const FlashToken &tok);
|
||||
virtual PolygonScaler &operator<<(const PatternToken &tok);
|
||||
virtual void footer();
|
||||
|
@ -328,7 +321,6 @@ 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 SimpleGerberOutput &operator<<(const FlashToken &tok);
|
||||
virtual SimpleGerberOutput &operator<<(const PatternToken &tok);
|
||||
|
@ -356,7 +348,7 @@ namespace gerbolyze {
|
|||
virtual ~SimpleSVGOutput() {}
|
||||
virtual SimpleSVGOutput &operator<<(const Polygon &poly);
|
||||
virtual SimpleSVGOutput &operator<<(GerberPolarityToken pol);
|
||||
virtual SimpleSVGOutput &operator<<(const DrillToken &tok);
|
||||
virtual SimpleSVGOutput &operator<<(const FlashToken &tok);
|
||||
virtual void header_impl(d2p origin, d2p size);
|
||||
virtual void footer_impl();
|
||||
|
||||
|
@ -374,7 +366,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<<(const FlashToken &tok);
|
||||
virtual KicadSexpOutput &operator<<(GerberPolarityToken pol);
|
||||
virtual void header_impl(d2p origin, d2p size);
|
||||
virtual void footer_impl();
|
||||
|
|
|
@ -88,3 +88,8 @@ Dilater &Dilater::operator<<(const ApertureToken &ap) {
|
|||
m_sink << ApertureToken(ap.m_size + 2*m_dilation);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Dilater &Dilater::operator<<(const FlashToken &tok) {
|
||||
m_sink << tok;
|
||||
return *this;
|
||||
}
|
||||
|
|
|
@ -152,7 +152,7 @@ Flattener &Flattener::operator<<(const LayerNameToken &layer_name) {
|
|||
return *this;
|
||||
}
|
||||
|
||||
Flattener &Flattener::operator<<(const DrillToken &tok) {
|
||||
Flattener &Flattener::operator<<(const FlashToken &tok) {
|
||||
m_sink << tok;
|
||||
return *this;
|
||||
}
|
||||
|
|
|
@ -126,17 +126,6 @@ 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;
|
||||
}
|
||||
|
|
|
@ -64,12 +64,6 @@ 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;
|
||||
}
|
||||
|
||||
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);
|
||||
|
|
|
@ -135,7 +135,7 @@ KicadSexpOutput &KicadSexpOutput::operator<<(const Polygon &poly) {
|
|||
return *this;
|
||||
}
|
||||
|
||||
KicadSexpOutput &KicadSexpOutput::operator<<(const DrillToken &) {
|
||||
KicadSexpOutput &KicadSexpOutput::operator<<(const FlashToken &) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
|
|
@ -77,7 +77,7 @@ SimpleSVGOutput &SimpleSVGOutput::operator<<(const Polygon &poly) {
|
|||
return *this;
|
||||
}
|
||||
|
||||
SimpleSVGOutput &SimpleSVGOutput::operator<<(const DrillToken &) {
|
||||
SimpleSVGOutput &SimpleSVGOutput::operator<<(const FlashToken &) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
|
|
@ -285,7 +285,7 @@ void gerbolyze::SVGDocument::export_svg_path(RenderContext &ctx, const pugi::xml
|
|||
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(centroid);
|
||||
ctx.sink() << ApertureToken(diameter) << FlashToken(centroid);
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
@ -346,6 +346,10 @@ void gerbolyze::SVGDocument::export_svg_path(RenderContext &ctx, const pugi::xml
|
|||
}
|
||||
|
||||
if (has_stroke) {
|
||||
Clipper stroke_clip;
|
||||
stroke_clip.StrictlySimple(true);
|
||||
stroke_clip.AddPaths(ctx.clip(), ptClip, /* closed */ true);
|
||||
|
||||
ClipperOffset offx;
|
||||
offx.ArcTolerance = 0.01 * clipper_scale; /* 10µm; TODO: Make this configurable */
|
||||
offx.MiterLimit = stroke_miterlimit;
|
||||
|
@ -359,10 +363,8 @@ void gerbolyze::SVGDocument::export_svg_path(RenderContext &ctx, const pugi::xml
|
|||
if (dasharray.empty()) {
|
||||
|
||||
if (ctx.settings().outline_mode && stroke_color != GRB_PATTERN_FILL) {
|
||||
/* In outline mode, manually close polys */
|
||||
poly.push_back(poly[0]);
|
||||
ctx.sink() << ApertureToken() << poly;
|
||||
|
||||
cerr << "add closed path of size " << poly.size() << endl;
|
||||
stroke_clip.AddPath(poly, ptSubject, /* closed */ true);
|
||||
} else {
|
||||
offx.AddPath(poly, join_type, etClosedLine);
|
||||
}
|
||||
|
@ -374,7 +376,8 @@ void gerbolyze::SVGDocument::export_svg_path(RenderContext &ctx, const pugi::xml
|
|||
dash_path(poly_copy, out, dasharray, stroke_dashoffset);
|
||||
|
||||
if (ctx.settings().outline_mode && stroke_color != GRB_PATTERN_FILL) {
|
||||
ctx.sink() << ApertureToken(stroke_width) << out;
|
||||
cerr << "add open path dashes of size " << out.size() << endl;
|
||||
stroke_clip.AddPaths(out, ptSubject, /* closed */ false);
|
||||
} else {
|
||||
offx.AddPaths(out, join_type, end_type);
|
||||
}
|
||||
|
@ -386,47 +389,63 @@ void gerbolyze::SVGDocument::export_svg_path(RenderContext &ctx, const pugi::xml
|
|||
dash_path(poly, out, dasharray, stroke_dashoffset);
|
||||
|
||||
if (ctx.settings().outline_mode && stroke_color != GRB_PATTERN_FILL) {
|
||||
ctx.sink() << ApertureToken(stroke_width) << out;
|
||||
cerr << "add open paths of size " << out.size() << endl;
|
||||
stroke_clip.AddPaths(out, ptSubject, /* closed */ false);
|
||||
} else {
|
||||
offx.AddPaths(out, join_type, end_type);
|
||||
}
|
||||
}
|
||||
|
||||
/* Execute clipper offset operation to generate stroke outlines */
|
||||
offx.Execute(ptree, 0.5 * stroke_width * clipper_scale);
|
||||
|
||||
/* Clip. Note that after the outline, all we have is closed paths as any open path's stroke outline is itself
|
||||
* a closed path. */
|
||||
if (!ctx.clip().empty()) {
|
||||
Clipper c;
|
||||
|
||||
if (ctx.settings().outline_mode && stroke_color != GRB_PATTERN_FILL) {
|
||||
stroke_clip.Execute(ctIntersection, ptree, pftNonZero, pftNonZero);
|
||||
Paths outline_paths;
|
||||
PolyTreeToPaths(ptree, outline_paths);
|
||||
c.AddPaths(outline_paths, ptSubject, /* closed */ true);
|
||||
c.AddPaths(ctx.clip(), ptClip, /* closed */ true);
|
||||
c.StrictlySimple(true);
|
||||
/* fill rules are nonzero since both subject and clip have already been normalized by clipper. */
|
||||
c.Execute(ctIntersection, ptree, pftNonZero, pftNonZero);
|
||||
}
|
||||
ctx.sink() << ApertureToken(stroke_width);
|
||||
|
||||
/* Call out to pattern tiler for pattern strokes. The stroke's outline becomes the clip here. */
|
||||
if (stroke_color == GRB_PATTERN_FILL) {
|
||||
string stroke_pattern_id = usvg_id_url(node.attribute("stroke").value());
|
||||
Pattern *pattern = lookup_pattern(stroke_pattern_id);
|
||||
if (!pattern) {
|
||||
cerr << "Warning: Fill pattern with id \"" << stroke_pattern_id << "\" not found." << endl;
|
||||
|
||||
} else {
|
||||
Paths clip;
|
||||
PolyTreeToPaths(ptree, clip);
|
||||
RenderContext local_ctx(ctx, xform2d(), clip, true);
|
||||
pattern->tile(local_ctx);
|
||||
ClosedPathsFromPolyTree(ptree, outline_paths);
|
||||
for (auto &path : outline_paths) {
|
||||
/* we have to connect the last and first point here */
|
||||
if (path.empty())
|
||||
continue;
|
||||
path.push_back(path[0]);
|
||||
ctx.sink() << path;
|
||||
}
|
||||
|
||||
} else if (!ctx.settings().outline_mode) {
|
||||
Paths s_polys;
|
||||
dehole_polytree(ptree, s_polys);
|
||||
ctx.sink() << (stroke_color == GRB_DARK ? GRB_POL_DARK : GRB_POL_CLEAR) << ApertureToken() << s_polys;
|
||||
OpenPathsFromPolyTree(ptree, outline_paths);
|
||||
ctx.sink() << outline_paths;
|
||||
|
||||
} else {
|
||||
/* Execute clipper offset operation to generate stroke outlines */
|
||||
offx.Execute(ptree, 0.5 * stroke_width * clipper_scale);
|
||||
|
||||
/* Clip. Note that (outside of outline mode) after the clipper outline operation, all we have is closed paths as
|
||||
* any open path's stroke outline is itself a closed path. */
|
||||
if (!ctx.clip().empty()) {
|
||||
Paths outline_paths;
|
||||
PolyTreeToPaths(ptree, outline_paths);
|
||||
stroke_clip.AddPaths(outline_paths, ptSubject, /* closed */ true);
|
||||
/* fill rules are nonzero since both subject and clip have already been normalized by clipper. */
|
||||
stroke_clip.Execute(ctIntersection, ptree, pftNonZero, pftNonZero);
|
||||
}
|
||||
|
||||
/* Call out to pattern tiler for pattern strokes. The stroke's outline becomes the clip here. */
|
||||
if (stroke_color == GRB_PATTERN_FILL) {
|
||||
string stroke_pattern_id = usvg_id_url(node.attribute("stroke").value());
|
||||
Pattern *pattern = lookup_pattern(stroke_pattern_id);
|
||||
if (!pattern) {
|
||||
cerr << "Warning: Fill pattern with id \"" << stroke_pattern_id << "\" not found." << endl;
|
||||
|
||||
} else {
|
||||
Paths clip;
|
||||
PolyTreeToPaths(ptree, clip);
|
||||
RenderContext local_ctx(ctx, xform2d(), clip, true);
|
||||
pattern->tile(local_ctx);
|
||||
}
|
||||
|
||||
} else {
|
||||
Paths s_polys;
|
||||
dehole_polytree(ptree, s_polys);
|
||||
ctx.sink() << (stroke_color == GRB_DARK ? GRB_POL_DARK : GRB_POL_CLEAR) << ApertureToken() << s_polys;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Ładowanie…
Reference in New Issue