From 1180ebdc1f18044a74f22f17b4d500ce7d6543fa Mon Sep 17 00:00:00 2001 From: jaseg Date: Sun, 25 Apr 2021 00:09:57 +0200 Subject: Remove cairo dependency We initially used Cairo for its bezier flattening algorithm. That algorithm turned out to be a bit too imprecise at the scales we're working at here (#17), so I ended up porting over some code from Antigrain Graphics. The only other thing we used Cairo for was debug output and coordinate transforms, so I just wrote the relevant vector math in a small header file, deleted all debug output code and thus eliminated the cairo dependency. This is a step towards Windows builds. --- svg-flatten/include/flatten.hpp | 6 +- svg-flatten/include/geom2d.hpp | 157 ++++++++++++++++++++++++++++++++++++++ svg-flatten/include/gerbolyze.hpp | 22 ++---- 3 files changed, 168 insertions(+), 17 deletions(-) create mode 100644 svg-flatten/include/geom2d.hpp (limited to 'svg-flatten/include') diff --git a/svg-flatten/include/flatten.hpp b/svg-flatten/include/flatten.hpp index b620890..92cbf38 100644 --- a/svg-flatten/include/flatten.hpp +++ b/svg-flatten/include/flatten.hpp @@ -5,9 +5,9 @@ namespace gerbolyze { class curve4_div { public: curve4_div(double distance_tolerance=0.1, double angle_tolerance=0.0, double cusp_limit=0.0) - : m_distance_tolerance_square(0.25*distance_tolerance*distance_tolerance), - m_angle_tolerance(angle_tolerance), - m_cusp_limit(cusp_limit) + : m_cusp_limit(cusp_limit), + m_distance_tolerance_square(0.25*distance_tolerance*distance_tolerance), + m_angle_tolerance(angle_tolerance) { } diff --git a/svg-flatten/include/geom2d.hpp b/svg-flatten/include/geom2d.hpp new file mode 100644 index 0000000..ac56628 --- /dev/null +++ b/svg-flatten/include/geom2d.hpp @@ -0,0 +1,157 @@ +/* + * This file is part of gerbolyze, a vector image preprocessing toolchain + * Copyright (C) 2021 Jan Sebastian Götte + * + * 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 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 . + */ + +#pragma once + +#include +#include +#include +#include +#include + +#include + +#include "svg_import_defs.h" + +using namespace std; + +namespace gerbolyze { + + typedef std::array d2p; + typedef std::vector Polygon; + + class xform2d { + public: + xform2d(double xx, double yx, double xy, double yy, double x0=0.0, double y0=0.0) : + xx(xx), yx(yx), xy(xy), yy(yy), x0(x0), y0(y0) {} + + xform2d() : xform2d(1.0, 0.0, 0.0, 1.0) {} + + xform2d(const string &svg_transform) : xform2d() { + if (svg_transform.empty()) + return; + + string start("matrix("); + if (svg_transform.substr(0, start.length()) != start) + return; + if (svg_transform.back() != ')') + return; + + const string &foo = svg_transform.substr(start.length(), svg_transform.length()); + const string &bar = foo.substr(0, foo.length() - 1); + + istringstream xform(bar); + + double a, c, e, + b, d, f; + xform >> a >> b >> c >> d >> e >> f; + if (xform.fail()) + return; + + xx=a, yx=b, xy=c, yy=d, x0=e, y0=f; + } + + xform2d &translate(double x, double y) { + x0 += x; + y0 += y; + return *this; + } + + xform2d &scale(double x, double y) { + xx *= x; yx *= y; xy *= x; + yy *= y; x0 *= x; y0 *= y; + return *this; + } + + xform2d &transform(const xform2d &other) { + double n_xx = xx * other.xx + yx * other.xy; + double n_yx = xx * other.yx + yx * other.yy; + + double n_xy = xy * other.xx + yy * other.xy; + double n_yy = xy * other.yx + yy * other.yy; + + double n_x0 = x0 * other.xx + y0 * other.xy + other.x0; + double n_y0 = x0 * other.yx + y0 * other.yy + other.y0; + + xx = n_xx; + yx = n_yx; + xy = n_xy; + yy = n_yy; + x0 = n_x0; + y0 = n_y0; + + return *this; + }; + + double doc2phys_dist(double dist_doc) { + return xx * dist_doc; + } + + double phys2doc_dist(double dist_doc) { + return xx * dist_doc; + } + + d2p doc2phys(const d2p p) { + return d2p { + xx * p[0] + xy * p[1] + x0, + xy * p[1] + yy * p[1] + y0 + }; + } + + xform2d &invert(bool *success_out=nullptr) { + /* From Cairo source */ + + /* inv (A) = 1/det (A) * adj (A) */ + double det = xx*yy - yx*xy; + + if (det == 0 || !isfinite(det)) { + if (success_out) + *success_out = false; + *this = xform2d(); /* unity matrix */ + return *this; + } + + *this = xform2d(yy/det, -yx/det, + -xy/det, xx/det, + (xy*y0 - yy*x0)/det, (yx*x0 - xx*y0)/det); + + if (success_out) + *success_out = true; + return *this; + } + + /* Transform given clipper paths */ + void transform_paths(ClipperLib::Paths &paths) { + for (auto &p : paths) { + std::transform(p.begin(), p.end(), p.begin(), + [this](ClipperLib::IntPoint p) -> ClipperLib::IntPoint { + d2p out(this->doc2phys(d2p{p.X / clipper_scale, p.Y / clipper_scale})); + return { + (ClipperLib::cInt)round(out[0] * clipper_scale), + (ClipperLib::cInt)round(out[1] * clipper_scale) + }; + }); + } + } + + private: + double xx, yx, + xy, yy, + x0, y0; + }; +} diff --git a/svg-flatten/include/gerbolyze.hpp b/svg-flatten/include/gerbolyze.hpp index ed93bbc..d1d5f85 100644 --- a/svg-flatten/include/gerbolyze.hpp +++ b/svg-flatten/include/gerbolyze.hpp @@ -22,16 +22,17 @@ #include #include #include + #include + #include "svg_pattern.h" +#include "geom2d.hpp" namespace gerbolyze { constexpr char lib_version[] = "2.0"; - typedef std::array d2p; typedef std::function *(double, double, double)> sampling_fun; - typedef std::vector Polygon; enum GerberPolarityToken { GRB_POL_CLEAR, @@ -121,7 +122,7 @@ namespace gerbolyze { class ImageVectorizer { public: virtual ~ImageVectorizer() {}; - virtual void vectorize_image(cairo_t *cr, const pugi::xml_node &node, ClipperLib::Paths &clip_path, cairo_matrix_t &viewport_matrix, PolygonSink &sink, double min_feature_size_px) = 0; + virtual void vectorize_image(xform2d &mat, const pugi::xml_node &node, ClipperLib::Paths &clip_path, PolygonSink &sink, double min_feature_size_px) = 0; }; ImageVectorizer *makeVectorizer(const std::string &name); @@ -147,11 +148,10 @@ namespace gerbolyze { class SVGDocument { public: SVGDocument() : _valid(false) {} - ~SVGDocument(); /* true -> load successful */ - bool load(std::istream &in, std::string debug_out_filename="/tmp/kicad_svg_debug.svg"); - bool load(std::string filename, std::string debug_out_filename="/tmp/kicad_svg_debug.svg"); + bool load(std::istream &in); + bool load(std::string filename); /* true -> load successful */ bool valid() const { return _valid; } operator bool() const { return valid(); } @@ -168,13 +168,11 @@ namespace gerbolyze { private: friend class Pattern; - cairo_t *cairo() { return cr; } const ClipperLib::Paths *lookup_clip_path(const pugi::xml_node &node); Pattern *lookup_pattern(const std::string id); - void export_svg_group(const RenderSettings &rset, const pugi::xml_node &group, ClipperLib::Paths &parent_clip_path, const ElementSelector *sel=nullptr, bool included=true, bool is_root=false); - void export_svg_path(const RenderSettings &rset, const pugi::xml_node &node, ClipperLib::Paths &clip_path); - void setup_debug_output(std::string filename=""); + void export_svg_group(xform2d &mat, const RenderSettings &rset, const pugi::xml_node &group, ClipperLib::Paths &parent_clip_path, const ElementSelector *sel=nullptr, bool included=true, bool is_root=false); + void export_svg_path(xform2d &mat, const RenderSettings &rset, const pugi::xml_node &node, ClipperLib::Paths &clip_path); void setup_viewport_clip(); void load_clips(const RenderSettings &rset); void load_patterns(); @@ -188,12 +186,8 @@ namespace gerbolyze { double page_w_mm, page_h_mm; std::map pattern_map; std::map clip_path_map; - cairo_matrix_t viewport_matrix; ClipperLib::Paths vb_paths; /* viewport clip rect */ - cairo_t *cr = nullptr; - cairo_surface_t *surface = nullptr; - PolygonSink *polygon_sink = nullptr; static constexpr double dbg_fill_alpha = 0.8; -- cgit