Advanced svg/gerber composition working
This commit is contained in:
parent
49a7c6df41
commit
a68e395cb6
6 changed files with 59 additions and 29 deletions
2
TODO
Normal file
2
TODO
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
[ ] Do not just return "error 255" if usvg is not installed
|
||||
[ ] Straighten up svg-flatten input unit handling
|
||||
|
|
@ -95,7 +95,9 @@ def paste_vectors(input_gerbers, output_gerbers, top, bottom,
|
|||
outfile = Path('debug') / f'dilated-{layer}-{amount}.gbr' # FIXME
|
||||
# outfile = tmpdir / 'dilated-{layer}-{amount}.gbr'
|
||||
dilate_gerber(layers, layer, amount, bbox, tmpdir, outfile, units)
|
||||
return gerberex.read(str(outfile))
|
||||
gbr = gerberex.read(str(outfile))
|
||||
# gbr.offset(bounds[0][0], bounds[1][0])
|
||||
return gbr
|
||||
|
||||
for layer, input_files in layers.items():
|
||||
if layer == 'drill':
|
||||
|
|
@ -510,19 +512,28 @@ def create_template_from_svg(bounds, svg_data, extra_layers=DEFAULT_EXTRA_LAYERS
|
|||
#==================
|
||||
|
||||
def dilate_gerber(layers, layer_name, dilation, bbox, tmpdir, outfile, units):
|
||||
# render gerber to SVG
|
||||
scale = 1/25.4 if units == 'metric' else 1.0 # pcb-tools gerber scale
|
||||
scale *= CAIRO_SVG_HARDCODED_DPI
|
||||
if layer_name not in layers:
|
||||
raise ValueError(f'Cannot dilate layer {layer_name}: layer not found in input dir')
|
||||
|
||||
bounds = get_bounds(bbox, layers)
|
||||
ctx = GerberCairoContext(scale=scale)
|
||||
for _path, to_render in layers.get(layer_name, ()):
|
||||
ctx.render_layer(to_render, bounds=bounds,
|
||||
settings=gerber.render.RenderSettings(color=(0,0,0)), # FIXME should be 1, 1, 1
|
||||
bgsettings=gerber.render.RenderSettings(color=(0,0,0), alpha=0))
|
||||
(x_min_mm, x_max_mm), (y_min_mm, y_max_mm) = bounds
|
||||
|
||||
origin_x = x_min_mm / MM_PER_INCH
|
||||
origin_y = y_min_mm / MM_PER_INCH
|
||||
|
||||
width = (x_max_mm - x_min_mm) / MM_PER_INCH
|
||||
height = (y_max_mm - y_min_mm) / MM_PER_INCH
|
||||
|
||||
tmpfile = tmpdir / 'dilate-tmp.svg'
|
||||
ctx.dump(str(tmpfile))
|
||||
path, _gbr = layers[layer_name][0]
|
||||
# NOTE: gerbv has an undocumented maximum length of 20 chars for the arguments to --origin and --window_inch
|
||||
cmd = ['gerbv', '-x', 'svg',
|
||||
'--border=0',
|
||||
f'--origin={origin_x:.6f}x{origin_y:.6f}', f'--window_inch={width:.6f}x{height:.6f}',
|
||||
'--foreground=#ffffff',
|
||||
'-o', str(tmpfile), str(path)]
|
||||
print('cmd:', " ".join(cmd))
|
||||
subprocess.run(cmd, check=True)
|
||||
|
||||
# FIXME DEBUG
|
||||
import uuid
|
||||
|
|
@ -531,9 +542,10 @@ def dilate_gerber(layers, layer_name, dilation, bbox, tmpdir, outfile, units):
|
|||
print('tmp debug dilation svg:', fn)
|
||||
|
||||
# dilate & render back to gerber
|
||||
svg_to_gerber(tmpfile, outfile, dilate=dilation)
|
||||
# TODO: the scale parameter is a hack. ideally we would fix svg-flatten to handle input units correctly.
|
||||
svg_to_gerber(tmpfile, outfile, dilate=-dilation, dpi=72, scale=25.4/72.0)
|
||||
|
||||
def svg_to_gerber(infile, outfile, layer=None, trace_space:'mm'=0.1, vectorizer=None, vectorizer_map=None, exclude_groups=None, dilate=None):
|
||||
def svg_to_gerber(infile, outfile, layer=None, trace_space:'mm'=0.1, vectorizer=None, vectorizer_map=None, exclude_groups=None, dilate=None, dpi=None, scale=None):
|
||||
if 'SVG_FLATTEN' in os.environ:
|
||||
candidates = [os.environ['SVG_FLATTEN']]
|
||||
|
||||
|
|
@ -560,6 +572,10 @@ def svg_to_gerber(infile, outfile, layer=None, trace_space:'mm'=0.1, vectorizer=
|
|||
args += ['--exclude-groups', exclude_groups]
|
||||
if dilate:
|
||||
args += ['--dilate', str(dilate)]
|
||||
if dpi:
|
||||
args += ['--usvg-dpi', str(dpi)]
|
||||
if scale:
|
||||
args += ['--scale', str(scale)]
|
||||
|
||||
args += [str(infile), str(outfile)]
|
||||
print('full args:', " ".join(args))
|
||||
|
|
|
|||
|
|
@ -213,7 +213,7 @@ namespace gerbolyze {
|
|||
|
||||
class SimpleGerberOutput : public StreamPolygonSink {
|
||||
public:
|
||||
SimpleGerberOutput(std::ostream &out, bool only_polys=false, int digits_int=4, int digits_frac=6, d2p offset={0,0});
|
||||
SimpleGerberOutput(std::ostream &out, bool only_polys=false, int digits_int=4, int digits_frac=6, double scale=1.0, d2p offset={0,0});
|
||||
virtual ~SimpleGerberOutput() {}
|
||||
virtual SimpleGerberOutput &operator<<(const Polygon &poly);
|
||||
virtual SimpleGerberOutput &operator<<(GerberPolarityToken pol);
|
||||
|
|
@ -227,6 +227,7 @@ namespace gerbolyze {
|
|||
double m_height;
|
||||
long long int m_gerber_scale;
|
||||
d2p m_offset;
|
||||
double m_scale;
|
||||
};
|
||||
|
||||
class SimpleSVGOutput : public StreamPolygonSink {
|
||||
|
|
|
|||
|
|
@ -82,6 +82,12 @@ int main(int argc, char **argv) {
|
|||
{"skip_usvg", {"--no-usvg"},
|
||||
"Do not preprocess input using usvg (do not use unless you know *exactly* what you're doing)",
|
||||
0},
|
||||
{"usvg_dpi", {"--usvg-dpi"},
|
||||
"Passed through to usvg's --dpi, in case the input file has different ideas of DPI than usvg has.",
|
||||
1},
|
||||
{"scale", {"--scale"},
|
||||
"Scale input svg lengths by this factor.",
|
||||
1},
|
||||
{"exclude_groups", {"-e", "--exclude-groups"},
|
||||
"Comma-separated list of group IDs to exclude from export. Takes precedence over --only-groups.",
|
||||
1},
|
||||
|
|
@ -176,7 +182,9 @@ int main(int argc, char **argv) {
|
|||
sink = new SimpleSVGOutput(*out_f, only_polys, precision, dark_color, clear_color);
|
||||
|
||||
} else if (fmt == "gbr" || fmt == "grb" || fmt == "gerber") {
|
||||
sink = new SimpleGerberOutput(*out_f, only_polys, 4, precision);
|
||||
double scale = args["scale"].as<double>(1.0);
|
||||
cerr << "loading @scale=" << scale << endl;
|
||||
sink = new SimpleGerberOutput(*out_f, only_polys, 4, precision, scale);
|
||||
|
||||
} else if (fmt == "s-exp" || fmt == "sexp" || fmt == "kicad") {
|
||||
if (!args["sexp_mod_name"]) {
|
||||
|
|
@ -334,7 +342,13 @@ int main(int argc, char **argv) {
|
|||
|
||||
} else {
|
||||
cerr << "calling usvg on " << barf << " and " << frob << endl;
|
||||
const char *command_line[] = {"usvg", "--keep-named-groups", barf.c_str(), frob.c_str(), NULL};
|
||||
int dpi = 96;
|
||||
if (args["usvg_dpi"]) {
|
||||
dpi = args["usvg_dpi"].as<int>();
|
||||
}
|
||||
string dpi_str = to_string(dpi);
|
||||
|
||||
const char *command_line[] = {"usvg", "--keep-named-groups", "--dpi", dpi_str.c_str(), barf.c_str(), frob.c_str(), NULL};
|
||||
struct subprocess_s subprocess;
|
||||
int rc = subprocess_create(command_line, subprocess_option_inherit_environment, &subprocess);
|
||||
if (rc) {
|
||||
|
|
|
|||
|
|
@ -52,10 +52,6 @@ Dilater &Dilater::operator<<(GerberPolarityToken pol) {
|
|||
}
|
||||
|
||||
Dilater &Dilater::operator<<(const Polygon &poly) {
|
||||
static int i = 0;
|
||||
cerr << "dilating poly " << i++ << endl;
|
||||
|
||||
cerr << "got poly of " << poly.size() << " nodes" << endl;
|
||||
ClipperLib::Path poly_c;
|
||||
for (auto &p : poly) {
|
||||
poly_c.push_back({(ClipperLib::cInt)round(p[0] * clipper_scale), (ClipperLib::cInt)round(p[1] * clipper_scale)});
|
||||
|
|
|
|||
|
|
@ -27,11 +27,12 @@
|
|||
using namespace gerbolyze;
|
||||
using namespace std;
|
||||
|
||||
SimpleGerberOutput::SimpleGerberOutput(ostream &out, bool only_polys, int digits_int, int digits_frac, d2p offset)
|
||||
SimpleGerberOutput::SimpleGerberOutput(ostream &out, bool only_polys, int digits_int, int digits_frac, double scale, d2p offset)
|
||||
: StreamPolygonSink(out, only_polys),
|
||||
m_digits_int(digits_int),
|
||||
m_digits_frac(digits_frac),
|
||||
m_offset(offset)
|
||||
m_offset(offset),
|
||||
m_scale(scale)
|
||||
{
|
||||
assert(1 <= digits_int && digits_int <= 9);
|
||||
assert(0 <= digits_frac && digits_frac <= 9);
|
||||
|
|
@ -39,10 +40,10 @@ SimpleGerberOutput::SimpleGerberOutput(ostream &out, bool only_polys, int digits
|
|||
}
|
||||
|
||||
void SimpleGerberOutput::header_impl(d2p origin, d2p size) {
|
||||
m_offset[0] += origin[0];
|
||||
m_offset[1] += origin[1];
|
||||
m_width = size[0] - origin[0];
|
||||
m_height = size[1] - origin[1];
|
||||
m_offset[0] += origin[0] * m_scale;
|
||||
m_offset[1] += origin[1] * m_scale;
|
||||
m_width = (size[0] - origin[0]) * m_scale;
|
||||
m_height = (size[1] - origin[1]) * m_scale;
|
||||
|
||||
if (pow(10, m_digits_int-1) < max(m_width, m_height)) {
|
||||
cerr << "Warning: Input has bounding box too large for " << m_digits_int << "." << m_digits_frac << " gerber resolution!" << endl;
|
||||
|
|
@ -74,16 +75,16 @@ SimpleGerberOutput& SimpleGerberOutput::operator<<(const Polygon &poly) {
|
|||
}
|
||||
|
||||
/* NOTE: Clipper and gerber both have different fixed-point scales. We get points in double mm. */
|
||||
double x = round((poly[0][0] + m_offset[0]) * m_gerber_scale);
|
||||
double y = round((m_height - poly[0][1] + m_offset[1]) * m_gerber_scale);
|
||||
double x = round((poly[0][0] * m_scale + m_offset[0]) * m_gerber_scale);
|
||||
double y = round((m_height - poly[0][1] * m_scale + m_offset[1]) * m_gerber_scale);
|
||||
m_out << "G36*" << endl;
|
||||
m_out << "X" << setw(m_digits_int + m_digits_frac) << setfill('0') << (long long int)x
|
||||
<< "Y" << setw(m_digits_int + m_digits_frac) << setfill('0') << (long long int)y
|
||||
<< "D02*" << endl;
|
||||
m_out << "G01*" << endl;
|
||||
for (size_t i=1; i<poly.size(); i++) {
|
||||
double x = round((poly[i][0] + m_offset[0]) * m_gerber_scale);
|
||||
double y = round((m_height - poly[i][1] + m_offset[1]) * m_gerber_scale);
|
||||
double x = round((poly[i][0] * m_scale + m_offset[0]) * m_gerber_scale);
|
||||
double y = round((m_height - poly[i][1] * m_scale + m_offset[1]) * m_gerber_scale);
|
||||
m_out << "X" << setw(m_digits_int + m_digits_frac) << setfill('0') << (long long int)x
|
||||
<< "Y" << setw(m_digits_int + m_digits_frac) << setfill('0') << (long long int)y
|
||||
<< "D01*" << endl;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue