Gerber and SVG export working
This commit is contained in:
parent
f7b4cc602b
commit
95b198a5aa
24 changed files with 1282 additions and 480 deletions
105
src/svg_doc.cpp
105
src/svg_doc.cpp
|
|
@ -1,57 +1,59 @@
|
|||
/*
|
||||
* This program source code file is part of KICAD, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2021 Jan Sebastian Götte <kicad@jaseg.de>
|
||||
* Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This file is part of gerbolyze, a vector image preprocessing toolchain
|
||||
* Copyright (C) 2021 Jan Sebastian Götte <gerbolyze@jaseg.de>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#include "svg_import_defs.h"
|
||||
#include "svg_doc.h"
|
||||
#include "svg_color.h"
|
||||
#include "svg_geom.h"
|
||||
#include "svg_path.h"
|
||||
#include "vec_core.h"
|
||||
|
||||
using namespace svg_plugin;
|
||||
using namespace gerbolyze;
|
||||
using namespace std;
|
||||
using namespace ClipperLib;
|
||||
using namespace vectorizer;
|
||||
|
||||
svg_plugin::SVGDocument::~SVGDocument() {
|
||||
gerbolyze::SVGDocument::~SVGDocument() {
|
||||
if (cr)
|
||||
cairo_destroy (cr);
|
||||
if (surface)
|
||||
cairo_surface_destroy (surface);
|
||||
}
|
||||
|
||||
bool svg_plugin::SVGDocument::load(string filename, string debug_out_filename) {
|
||||
bool gerbolyze::SVGDocument::load(string filename, string debug_out_filename) {
|
||||
ifstream in_f;
|
||||
in_f.open(filename);
|
||||
|
||||
return in_f && load(in_f, debug_out_filename);
|
||||
}
|
||||
|
||||
bool gerbolyze::SVGDocument::load(istream &in, string debug_out_filename) {
|
||||
/* Load XML document */
|
||||
auto res = svg_doc.load_file(filename.c_str());
|
||||
auto res = svg_doc.load(in);
|
||||
if (!res) {
|
||||
cerr << "Cannot open input file \"" << filename << "\": " << res << endl;
|
||||
cerr << "Cannot parse input file" << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
root_elem = svg_doc.child("svg");
|
||||
if (!root_elem) {
|
||||
cerr << "Cannot load input file \"" << filename << endl;
|
||||
cerr << "Input file is missing root <svg> element" << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -90,7 +92,7 @@ bool svg_plugin::SVGDocument::load(string filename, string debug_out_filename) {
|
|||
return true;
|
||||
}
|
||||
|
||||
const Paths *svg_plugin::SVGDocument::lookup_clip_path(const pugi::xml_node &node) {
|
||||
const Paths *gerbolyze::SVGDocument::lookup_clip_path(const pugi::xml_node &node) {
|
||||
string id(usvg_id_url(node.attribute("clip-path").value()));
|
||||
if (id.empty() || !clip_path_map.contains(id)) {
|
||||
return nullptr;
|
||||
|
|
@ -98,7 +100,7 @@ const Paths *svg_plugin::SVGDocument::lookup_clip_path(const pugi::xml_node &nod
|
|||
return &clip_path_map[id];
|
||||
}
|
||||
|
||||
Pattern *svg_plugin::SVGDocument::lookup_pattern(const string id) {
|
||||
Pattern *gerbolyze::SVGDocument::lookup_pattern(const string id) {
|
||||
if (id.empty() || !pattern_map.contains(id)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
|
@ -106,16 +108,16 @@ Pattern *svg_plugin::SVGDocument::lookup_pattern(const string id) {
|
|||
};
|
||||
|
||||
/* Used to convert mm values from configuration such as the minimum feature size into document units. */
|
||||
double svg_plugin::SVGDocument::mm_to_doc_units(double mm) const {
|
||||
double gerbolyze::SVGDocument::mm_to_doc_units(double mm) const {
|
||||
return mm * (vb_w / page_w_mm);
|
||||
}
|
||||
|
||||
double svg_plugin::SVGDocument::doc_units_to_mm(double px) const {
|
||||
double gerbolyze::SVGDocument::doc_units_to_mm(double px) const {
|
||||
return px / (vb_w / page_w_mm);
|
||||
}
|
||||
|
||||
/* Recursively export all SVG elements in the given group. */
|
||||
void svg_plugin::SVGDocument::export_svg_group(const pugi::xml_node &group, Paths &parent_clip_path) {
|
||||
void gerbolyze::SVGDocument::export_svg_group(const pugi::xml_node &group, Paths &parent_clip_path) {
|
||||
/* Enter the group's coordinate system */
|
||||
cairo_save(cr);
|
||||
apply_cairo_transform_from_svg(cr, group.attribute("transform").value());
|
||||
|
|
@ -137,19 +139,12 @@ void svg_plugin::SVGDocument::export_svg_group(const pugi::xml_node &group, Path
|
|||
/* Clip against parent's clip path (both are now in document coordinates) */
|
||||
if (!parent_clip_path.empty()) {
|
||||
if (!clip_path.empty()) {
|
||||
cerr << "Combining clip paths" << endl;
|
||||
combine_clip_paths(parent_clip_path, clip_path, clip_path);
|
||||
} else {
|
||||
cerr << "using parent clip path" << endl;
|
||||
clip_path = parent_clip_path;
|
||||
}
|
||||
}
|
||||
|
||||
ClipperLib::Clipper c2;
|
||||
c2.AddPaths(clip_path, ptSubject, /* closed */ true);
|
||||
ClipperLib::IntRect bbox = c2.GetBounds();
|
||||
cerr << "clip path is now: bbox={" << bbox.left << ", " << bbox.top << "} - {" << bbox.right << ", " << bbox.bottom << "}" << endl;
|
||||
|
||||
/* Iterate over the group's children, exporting them one by one. */
|
||||
for (const auto &node : group.children()) {
|
||||
string name(node.name());
|
||||
|
|
@ -162,7 +157,7 @@ void svg_plugin::SVGDocument::export_svg_group(const pugi::xml_node &group, Path
|
|||
} else if (name == "image") {
|
||||
double min_feature_size_mm = 0.1; /* TODO make configurable */
|
||||
double min_feature_size_px = mm_to_doc_units(min_feature_size_mm);
|
||||
vectorize_image(cr, node, min_feature_size_px, clip_path, viewport_matrix);
|
||||
vectorize_image(cr, node, min_feature_size_px, clip_path, viewport_matrix, *polygon_sink);
|
||||
} else if (name == "defs") {
|
||||
/* ignore */
|
||||
} else {
|
||||
|
|
@ -174,7 +169,7 @@ void svg_plugin::SVGDocument::export_svg_group(const pugi::xml_node &group, Path
|
|||
}
|
||||
|
||||
/* Export an SVG path element to gerber. Apply patterns and clip on the fly. */
|
||||
void svg_plugin::SVGDocument::export_svg_path(const pugi::xml_node &node, Paths &clip_path) {
|
||||
void gerbolyze::SVGDocument::export_svg_path(const pugi::xml_node &node, Paths &clip_path) {
|
||||
enum gerber_color fill_color = gerber_fill_color(node);
|
||||
enum gerber_color stroke_color = gerber_stroke_color(node);
|
||||
|
||||
|
|
@ -253,8 +248,7 @@ void svg_plugin::SVGDocument::export_svg_path(const pugi::xml_node &node, Paths
|
|||
out.push_back(std::array<double, 2>{
|
||||
((double)p.X) / clipper_scale, ((double)p.Y) / clipper_scale
|
||||
});
|
||||
std::cerr << "calling sink" << std::endl;
|
||||
polygon_sink(out, fill_color == GRB_DARK);
|
||||
*polygon_sink << (fill_color == GRB_DARK ? GRB_POL_DARK : GRB_POL_CLEAR) << out;
|
||||
}
|
||||
cairo_restore(cr);
|
||||
}
|
||||
|
|
@ -341,28 +335,37 @@ void svg_plugin::SVGDocument::export_svg_path(const pugi::xml_node &node, Paths
|
|||
out.push_back(std::array<double, 2>{
|
||||
((double)p.X) / clipper_scale, ((double)p.Y) / clipper_scale
|
||||
});
|
||||
std::cerr << "calling sink" << std::endl;
|
||||
polygon_sink(out, stroke_color == GRB_DARK);
|
||||
*polygon_sink << (stroke_color == GRB_DARK ? GRB_POL_DARK : GRB_POL_CLEAR) << out;
|
||||
}
|
||||
cairo_restore(cr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void svg_plugin::SVGDocument::do_export(string debug_out_filename) {
|
||||
void gerbolyze::SVGDocument::render(PolygonSink &sink) {
|
||||
assert(_valid);
|
||||
/* Export the actual SVG document to both SVG for debuggin and to gerber. We do this as we go, i.e. we immediately
|
||||
* process each element to gerber as we encounter it instead of first rendering everything to a giant list of gerber
|
||||
* primitives and then serializing those later. Exporting them on the fly saves a ton of memory and is much faster.
|
||||
*/
|
||||
polygon_sink = &sink;
|
||||
sink.header({vb_x, vb_y}, {vb_w, vb_h});
|
||||
ClipperLib::Clipper c;
|
||||
c.AddPaths(vb_paths, ptSubject, /* closed */ true);
|
||||
ClipperLib::IntRect bbox = c.GetBounds();
|
||||
cerr << "document viewbox clip: bbox={" << bbox.left << ", " << bbox.top << "} - {" << bbox.right << ", " << bbox.bottom << "}" << endl;
|
||||
export_svg_group(root_elem, vb_paths);
|
||||
sink.footer();
|
||||
}
|
||||
|
||||
void svg_plugin::SVGDocument::setup_debug_output(string filename) {
|
||||
void gerbolyze::SVGDocument::render_to_list(vector<pair<Polygon, GerberPolarityToken>> &out) {
|
||||
LambdaPolygonSink sink([&out](const Polygon &poly, GerberPolarityToken pol) {
|
||||
out.emplace_back(pair<Polygon, GerberPolarityToken>{poly, pol});
|
||||
});
|
||||
render(sink);
|
||||
}
|
||||
|
||||
void gerbolyze::SVGDocument::setup_debug_output(string filename) {
|
||||
/* Setup cairo to draw into a SVG surface (for debugging). For actual rendering, something like a recording surface
|
||||
* would work fine, too. */
|
||||
/* Cairo expects the SVG surface size to be given in pt (72.0 pt = 1.0 in = 25.4 mm) */
|
||||
|
|
@ -387,31 +390,27 @@ void svg_plugin::SVGDocument::setup_debug_output(string filename) {
|
|||
cairo_set_source_rgba (cr, 1.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
|
||||
void svg_plugin::SVGDocument::setup_viewport_clip() {
|
||||
void gerbolyze::SVGDocument::setup_viewport_clip() {
|
||||
/* Set up view port clip path */
|
||||
Path vb_path;
|
||||
for (auto &elem : vector<pair<double, double>> {{vb_x, vb_y}, {vb_x+vb_w, vb_y}, {vb_x+vb_w, vb_y+vb_h}, {vb_x, vb_y+vb_h}}) {
|
||||
double x = elem.first, y = elem.second;
|
||||
vb_path.push_back({ (cInt)round(x * clipper_scale), (cInt)round(y * clipper_scale) });
|
||||
cerr << "adding to path: " << (cInt)round(x * clipper_scale) << ", " << (cInt)round(y * clipper_scale) << endl;
|
||||
}
|
||||
vb_paths.push_back(vb_path);
|
||||
|
||||
ClipperLib::Clipper c;
|
||||
c.AddPaths(vb_paths, ptSubject, /* closed */ true);
|
||||
ClipperLib::IntRect bbox = c.GetBounds();
|
||||
cerr << "did set up viewbox clip: bbox={" << bbox.left << ", " << bbox.top << "} - {" << bbox.right << ", " << bbox.bottom << "}" << endl;
|
||||
export_svg_group(root_elem, vb_paths);
|
||||
}
|
||||
|
||||
void svg_plugin::SVGDocument::load_patterns() {
|
||||
void gerbolyze::SVGDocument::load_patterns() {
|
||||
/* Set up document-wide pattern registry. Load patterns from <defs> node. */
|
||||
for (const auto &node : defs_node.children("pattern")) {
|
||||
pattern_map.emplace(std::piecewise_construct, std::forward_as_tuple(node.attribute("id").value()), std::forward_as_tuple(node, *this));
|
||||
}
|
||||
}
|
||||
|
||||
void svg_plugin::SVGDocument::load_clips() {
|
||||
void gerbolyze::SVGDocument::load_clips() {
|
||||
/* Set up document-wide clip path registry: Extract clip path definitions from <defs> element */
|
||||
for (const auto &node : defs_node.children("clipPath")) {
|
||||
cairo_save(cr);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue