171 lines
5.5 KiB
C++
171 lines
5.5 KiB
C++
/*
|
|
* 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 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/>.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <array>
|
|
#include <string>
|
|
#include <sstream>
|
|
#include <iomanip>
|
|
#include <cmath>
|
|
#include <algorithm>
|
|
|
|
#include <clipper.hpp>
|
|
|
|
#include "svg_import_defs.h"
|
|
|
|
using namespace std;
|
|
|
|
namespace gerbolyze {
|
|
|
|
typedef std::array<double, 2> d2p;
|
|
typedef std::vector<d2p> Polygon;
|
|
|
|
typedef std::array<int64_t, 2> i2p;
|
|
typedef std::vector<i2p> Polygon_i;
|
|
|
|
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;
|
|
//cerr << "xform loaded " << dbg_str() << endl;
|
|
}
|
|
|
|
xform2d &translate(double x, double y) {
|
|
xform2d xf(1, 0, 0, 1, x, y);
|
|
transform(xf);
|
|
return *this;
|
|
}
|
|
|
|
xform2d &scale(double x, double y) {
|
|
xform2d xf(x, 0, 0, y);
|
|
transform(xf);
|
|
return *this;
|
|
}
|
|
|
|
xform2d &transform(const xform2d &other) {
|
|
double n_xx = other.xx * xx + other.yx * xy;
|
|
double n_yx = other.xx * yx + other.yx * yy;
|
|
|
|
double n_xy = other.xy * xx + other.yy * xy;
|
|
double n_yy = other.xy * yx + other.yy * yy;
|
|
|
|
double n_x0 = other.x0 * xx + other.y0 * xy + x0;
|
|
double n_y0 = other.x0 * yx + other.y0 * yy + 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 dist_doc * sqrt(xx*xx + xy * xy);
|
|
}
|
|
|
|
double phys2doc_dist(double dist_doc) {
|
|
return dist_doc / sqrt(xx*xx + xy * xy);
|
|
}
|
|
|
|
d2p doc2phys(const d2p p) {
|
|
return d2p {
|
|
xx * p[0] + xy * p[1] + x0,
|
|
yx * p[0] + 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)
|
|
};
|
|
});
|
|
}
|
|
}
|
|
|
|
string dbg_str() {
|
|
ostringstream os;
|
|
os << "xform2d< " << setw(5);
|
|
os << xx << ", " << xy << ", " << x0 << " / ";
|
|
os << yy << ", " << yx << ", " << y0;
|
|
os << " >";
|
|
return os.str();
|
|
}
|
|
|
|
private:
|
|
double xx, yx,
|
|
xy, yy,
|
|
x0, y0;
|
|
};
|
|
}
|