diff options
Diffstat (limited to 'Build/source/texk/dvisvgm/dvisvgm-src/src/TriangularPatch.cpp')
-rw-r--r-- | Build/source/texk/dvisvgm/dvisvgm-src/src/TriangularPatch.cpp | 214 |
1 files changed, 214 insertions, 0 deletions
diff --git a/Build/source/texk/dvisvgm/dvisvgm-src/src/TriangularPatch.cpp b/Build/source/texk/dvisvgm/dvisvgm-src/src/TriangularPatch.cpp new file mode 100644 index 00000000000..75b3132345b --- /dev/null +++ b/Build/source/texk/dvisvgm/dvisvgm-src/src/TriangularPatch.cpp @@ -0,0 +1,214 @@ +/************************************************************************* +** TriangularPatch.cpp ** +** ** +** This file is part of dvisvgm -- the DVI to SVG converter ** +** Copyright (C) 2005-2015 Martin Gieseking <martin.gieseking@uos.de> ** +** ** +** 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 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, see <http://www.gnu.org/licenses/>. ** +*************************************************************************/ + +#include "TriangularPatch.h" + +using namespace std; + +TriangularPatch::TriangularPatch (const PointVec &points, const ColorVec &colors, Color::ColorSpace cspace, int edgeflag, TriangularPatch *patch) + : ShadingPatch(cspace) +{ + setPoints(points, edgeflag, patch); + setColors(colors, edgeflag, patch); +} + + +void TriangularPatch::setPoints (const PointVec &points, int edgeflag, ShadingPatch *patch) { + TriangularPatch *triangularPatch = dynamic_cast<TriangularPatch*>(patch); + if (edgeflag > 0 && !triangularPatch) + throw ShadingException("missing preceding data in definition of triangular patch"); + if ((edgeflag == 0 && points.size() != 3) || (edgeflag > 0 && points.size() != 1)) + throw ShadingException("invalid number of vertices in triangular patch definition"); + + _points[0] = points[0]; + switch (edgeflag) { + case 0: + _points[1] = points[1]; + _points[2] = points[2]; + break; + case 1: + _points[1] = triangularPatch->_points[1]; + _points[2] = triangularPatch->_points[2]; + break; + case 2: + _points[1] = triangularPatch->_points[2]; + _points[2] = triangularPatch->_points[0]; + } +} + + +void TriangularPatch::setPoints (const DPair &p1, const DPair &p2, const DPair &p3) { + _points[0] = p1; + _points[1] = p2; + _points[2] = p3; +} + + +void TriangularPatch::setColors (const ColorVec &colors, int edgeflag, ShadingPatch *patch) { + TriangularPatch *triangularPatch = dynamic_cast<TriangularPatch*>(patch); + if (edgeflag > 0 && !triangularPatch) + throw ShadingException("missing preceding data in definition of triangular patch"); + if ((edgeflag == 0 && colors.size() != 3) || (edgeflag > 0 && colors.size() != 1)) + throw ShadingException("invalid number of colors in triangular patch definition"); + + _colors[0] = colors[0]; + switch (edgeflag) { + case 0: + _colors[1] = colors[1]; + _colors[2] = colors[2]; + break; + case 1: + _colors[1] = triangularPatch->_colors[1]; + _colors[2] = triangularPatch->_colors[2]; + break; + case 2: + _colors[1] = triangularPatch->_colors[2]; + _colors[2] = triangularPatch->_colors[0]; + } +} + + +void TriangularPatch::setColors (const Color &c1, const Color &c2, const Color &c3) { + _colors[0] = c1; + _colors[1] = c2; + _colors[2] = c3; +} + + +/** Returns the Cartesian coordinates for the barycentric coordinates \f$(u, v, 1-u-v)\f$ + * of a point of the triangle, where \f$u, v \in [0,1]\f$ and \f$u+v \le 1\f$. + * The relation between the vertices of the triangle and their barycentric coordinates + * is as follows: \f$(1,0,0)=p_1, (0,1,0)=p_2, (0,0,1)=p_0\f$. */ +DPair TriangularPatch::valueAt (double u, double v) const { + return _points[0] + (_points[1]-_points[0])*u + (_points[2]-_points[0])*v; +} + + +/** Returns the color at a given point of the triangle. The point must be given + * in barycentric coordinates \f$(u, v, 1-u-v)\f$, where \f$u, v \in [0,1]\f$ + * and \f$u+v \le 1\f$. + * The relation between the vertices of the triangle and their barycentric coordinates + * is as follows: \f$(1,0,0)=p_1, (0,1,0)=p_2, (0,0,1)=p_0\f$. */ +Color TriangularPatch::colorAt (double u, double v) const { + ColorGetter getComponents; + ColorSetter setComponents; + colorQueryFuncs(getComponents, setComponents); + valarray<double> comp[3]; + for (int i=0; i < 3; i++) + (_colors[i].*getComponents)(comp[i]); + Color color; + (color.*setComponents)(comp[0]*(1-u-v) + comp[1]*u + comp[2]*v); + return color; +} + + +Color TriangularPatch::averageColor () const { + return averageColor(_colors[0], _colors[1], _colors[2]); +} + + +/** Compute the average of three given colors depending on the assigned color space. */ +Color TriangularPatch::averageColor (const Color &c1, const Color &c2, const Color &c3) const { + ColorGetter getComponents; + ColorSetter setComponents; + colorQueryFuncs(getComponents, setComponents); + valarray<double> va1, va2, va3; + (c1.*getComponents)(va1); + (c2.*getComponents)(va2); + (c3.*getComponents)(va3); + Color averageColor; + (averageColor.*setComponents)((va1+va2+va3)/3.0); + return averageColor; +} + + +/** Snaps value x to the interval [0,1]. Values lesser than or near 0 are mapped to 0, values + * greater than or near 1 are mapped to 1. */ +static inline double snap (double x) { + if (fabs(x) < 0.001) + return 0; + if (fabs(1-x) < 0.001) + return 1; + return x; +} + + +/** Approximate the patch by dividing it into a grid of triangular segments that are filled + * with the average color of the corresponding region. In order to prevent visual gaps between + * adjacent segments due to anti-aliasing, the flag 'overlap' can be set. It enlarges the + * segments so that they overlap with their right and bottom neighbors (which are drawn on + * top of the overlapping regions). + * @param[in] gridsize number of segments per row/column + * @param[in] overlap if true, enlarge each segment to overlap with its right and bottom neighbors + * @param[in] delta reduce level of detail if the segment size is smaller than the given value + * @param[in] callback object notified */ +void TriangularPatch::approximate (int gridsize, bool overlap, double delta, Callback &callback) const { + if (_colors[0] == _colors[1] && _colors[1] == _colors[2]) { + GraphicPath<double> path; + getBoundaryPath(path); + callback.patchSegment(path, _colors[0]); + } + else { + const double inc = 1.0/gridsize; + for (double u1=0; u1 < 1; u1=snap(u1+inc)) { + double u2 = snap(u1+inc); + double ou2 = (overlap && snap(u2+inc) <= 1 ? snap(u2+inc) : u2); + for (double v1=0; snap(u1+v1) < 1; v1=snap(v1+inc)) { + double v2 = snap(v1+inc); + double ov2 = (overlap && snap(v2+inc) <= 1 ? snap(v2+inc) : v2); + if (!overlap || (snap(u1+ov2) <= 1 && snap(ou2+v1) <= 1)) { + // create triangular segments pointing in the same orientation as the whole patch + GraphicPath<double> path; + path.moveto(valueAt(u1, v1)); + path.lineto(valueAt(ou2, v1)); + path.lineto(valueAt(u1, ov2)); + path.closepath(); + callback.patchSegment(path, averageColor(colorAt(u1, v1), colorAt(u2, v1), colorAt(u1, v2))); + if (snap(u2+v2) <= 1 && (!overlap || inc > delta)) { + // create triangular segments pointing in the opposite direction as the whole patch + path.clear(); + path.moveto(valueAt(u1, v2)); + path.lineto(valueAt(u2, v1)); + path.lineto(valueAt(u2, v2)); + path.closepath(); + callback.patchSegment(path, averageColor(colorAt(u1, v2), colorAt(u2, v1), colorAt(u2, v2))); + } + } + } + } + } +} + + +void TriangularPatch::getBoundaryPath(GraphicPath<double> &path) const { + path.clear(); + path.moveto(_points[0]); + path.lineto(_points[1]); + path.lineto(_points[2]); + path.closepath(); +} + + +void TriangularPatch::getBBox (BoundingBox &bbox) const { + bbox.invalidate(); + bbox.embed(_points[0]); + bbox.embed(_points[1]); + bbox.embed(_points[2]); +} |