mirror of https://github.com/Chlumsky/msdfgen.git
Added algorithm to fix contour windings, low level optimization
This commit is contained in:
parent
b22f79ed27
commit
fe910c8b5a
|
|
@ -80,4 +80,11 @@ int Contour::winding() const {
|
|||
return sign(total);
|
||||
}
|
||||
|
||||
void Contour::reverse() {
|
||||
for (int i = (int) edges.size()/2; i > 0; --i)
|
||||
EdgeHolder::swap(edges[i-1], edges[edges.size()-i]);
|
||||
for (std::vector<EdgeHolder>::iterator edge = edges.begin(); edge != edges.end(); ++edge)
|
||||
(*edge)->reverse();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ public:
|
|||
void boundMiters(double &l, double &b, double &r, double &t, double border, double miterLimit, int polarity) const;
|
||||
/// Computes the winding of the contour. Returns 1 if positive, -1 if negative.
|
||||
int winding() const;
|
||||
/// Reverses the sequence of edges on the contour.
|
||||
void reverse();
|
||||
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,12 @@
|
|||
|
||||
namespace msdfgen {
|
||||
|
||||
void EdgeHolder::swap(EdgeHolder &a, EdgeHolder &b) {
|
||||
EdgeSegment *tmp = a.edgeSegment;
|
||||
a.edgeSegment = b.edgeSegment;
|
||||
b.edgeSegment = tmp;
|
||||
}
|
||||
|
||||
EdgeHolder::EdgeHolder() : edgeSegment(NULL) { }
|
||||
|
||||
EdgeHolder::EdgeHolder(EdgeSegment *segment) : edgeSegment(segment) { }
|
||||
|
|
|
|||
|
|
@ -9,6 +9,9 @@ namespace msdfgen {
|
|||
class EdgeHolder {
|
||||
|
||||
public:
|
||||
/// Swaps the edges held by a and b.
|
||||
static void swap(EdgeHolder &a, EdgeHolder &b);
|
||||
|
||||
EdgeHolder();
|
||||
EdgeHolder(EdgeSegment *segment);
|
||||
EdgeHolder(Point2 p0, Point2 p1, EdgeColor edgeColor = WHITE);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
|
||||
#include "Shape.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include "arithmetics.hpp"
|
||||
|
||||
namespace msdfgen {
|
||||
|
|
@ -125,4 +126,58 @@ int Shape::edgeCount() const {
|
|||
return total;
|
||||
}
|
||||
|
||||
void Shape::orientContours() {
|
||||
struct Intersection {
|
||||
double x;
|
||||
int direction;
|
||||
int contourIndex;
|
||||
|
||||
static int compare(const void *a, const void *b) {
|
||||
return sign(reinterpret_cast<const Intersection *>(a)->x-reinterpret_cast<const Intersection *>(b)->x);
|
||||
}
|
||||
};
|
||||
|
||||
const double ratio = .5*(sqrt(5)-1); // an irrational number to minimize chance of intersecting a corner or other point of interest
|
||||
std::vector<int> orientations(contours.size());
|
||||
std::vector<Intersection> intersections;
|
||||
for (int i = 0; i < (int) contours.size(); ++i) {
|
||||
if (!orientations[i] && !contours[i].edges.empty()) {
|
||||
// Find an Y that crosses the contour
|
||||
double y0 = contours[i].edges.front()->point(0).y;
|
||||
double y1 = y0;
|
||||
for (std::vector<EdgeHolder>::const_iterator edge = contours[i].edges.begin(); edge != contours[i].edges.end() && y0 == y1; ++edge)
|
||||
y1 = (*edge)->point(1).y;
|
||||
for (std::vector<EdgeHolder>::const_iterator edge = contours[i].edges.begin(); edge != contours[i].edges.end() && y0 == y1; ++edge)
|
||||
y1 = (*edge)->point(ratio).y; // in case all endpoints are in a horizontal line
|
||||
double y = mix(y0, y1, ratio);
|
||||
// Scanline through whole shape at Y
|
||||
double x[3];
|
||||
int dy[3];
|
||||
for (int j = 0; j < (int) contours.size(); ++j) {
|
||||
for (std::vector<EdgeHolder>::const_iterator edge = contours[j].edges.begin(); edge != contours[j].edges.end(); ++edge) {
|
||||
int n = (*edge)->scanlineIntersections(x, dy, y);
|
||||
for (int k = 0; k < n; ++k) {
|
||||
Intersection intersection = { x[k], dy[k], j };
|
||||
intersections.push_back(intersection);
|
||||
}
|
||||
}
|
||||
}
|
||||
qsort(&intersections[0], intersections.size(), sizeof(Intersection), &Intersection::compare);
|
||||
// Disqualify multiple intersections
|
||||
for (int j = 1; j < (int) intersections.size(); ++j)
|
||||
if (intersections[j].x == intersections[j-1].x)
|
||||
intersections[j].direction = intersections[j-1].direction = 0;
|
||||
// Inspect scanline and deduce orientations of intersected contours
|
||||
for (int j = 0; j < (int) intersections.size(); ++j)
|
||||
if (intersections[j].direction)
|
||||
orientations[intersections[j].contourIndex] += 2*((j&1)^(intersections[j].direction > 0))-1;
|
||||
intersections.clear();
|
||||
}
|
||||
}
|
||||
// Reverse contours that have the opposite orientation
|
||||
for (int i = 0; i < (int) contours.size(); ++i)
|
||||
if (orientations[i] < 0)
|
||||
contours[i].reverse();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,6 +47,8 @@ public:
|
|||
void scanline(Scanline &line, double y) const;
|
||||
/// Returns the total number of edge segments
|
||||
int edgeCount() const;
|
||||
/// Assumes its contours are unoriented (even-odd fill rule). Attempts to orient them to conform to the non-zero winding rule.
|
||||
void orientContours();
|
||||
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -46,6 +46,10 @@ QuadraticSegment::QuadraticSegment(Point2 p0, Point2 p1, Point2 p2, EdgeColor ed
|
|||
}
|
||||
|
||||
CubicSegment::CubicSegment(Point2 p0, Point2 p1, Point2 p2, Point2 p3, EdgeColor edgeColor) : EdgeSegment(edgeColor) {
|
||||
if ((p1 == p0 || p1 == p3) && (p2 == p0 || p2 == p3)) {
|
||||
p1 = mix(p0, p3, 1/3.);
|
||||
p2 = mix(p0, p3, 2/3.);
|
||||
}
|
||||
p[0] = p0;
|
||||
p[1] = p1;
|
||||
p[2] = p2;
|
||||
|
|
@ -139,18 +143,18 @@ SignedDistance QuadraticSegment::signedDistance(Point2 origin, double ¶m) co
|
|||
param = -dotProduct(qa, epDir)/dotProduct(epDir, epDir);
|
||||
{
|
||||
epDir = direction(1);
|
||||
double distance = nonZeroSign(crossProduct(epDir, p[2]-origin))*(p[2]-origin).length(); // distance from B
|
||||
if (fabs(distance) < fabs(minDistance)) {
|
||||
minDistance = distance;
|
||||
double distance = (p[2]-origin).length(); // distance from B
|
||||
if (distance < fabs(minDistance)) {
|
||||
minDistance = nonZeroSign(crossProduct(epDir, p[2]-origin))*distance;
|
||||
param = dotProduct(origin-p[1], epDir)/dotProduct(epDir, epDir);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < solutions; ++i) {
|
||||
if (t[i] > 0 && t[i] < 1) {
|
||||
Point2 qe = p[0]+2*t[i]*ab+t[i]*t[i]*br-origin;
|
||||
double distance = nonZeroSign(crossProduct(direction(t[i]), qe))*qe.length();
|
||||
if (fabs(distance) <= fabs(minDistance)) {
|
||||
minDistance = distance;
|
||||
double distance = qe.length();
|
||||
if (distance <= fabs(minDistance)) {
|
||||
minDistance = nonZeroSign(crossProduct(direction(t[i]), qe))*distance;
|
||||
param = t[i];
|
||||
}
|
||||
}
|
||||
|
|
@ -175,9 +179,9 @@ SignedDistance CubicSegment::signedDistance(Point2 origin, double ¶m) const
|
|||
param = -dotProduct(qa, epDir)/dotProduct(epDir, epDir);
|
||||
{
|
||||
epDir = direction(1);
|
||||
double distance = nonZeroSign(crossProduct(epDir, p[3]-origin))*(p[3]-origin).length(); // distance from B
|
||||
if (fabs(distance) < fabs(minDistance)) {
|
||||
minDistance = distance;
|
||||
double distance = (p[3]-origin).length(); // distance from B
|
||||
if (distance < fabs(minDistance)) {
|
||||
minDistance = nonZeroSign(crossProduct(epDir, p[3]-origin))*distance;
|
||||
param = dotProduct(epDir-(p[3]-origin), epDir)/dotProduct(epDir, epDir);
|
||||
}
|
||||
}
|
||||
|
|
@ -193,9 +197,9 @@ SignedDistance CubicSegment::signedDistance(Point2 origin, double ¶m) const
|
|||
if (t <= 0 || t >= 1)
|
||||
break;
|
||||
qe = qa+3*t*ab+3*t*t*br+t*t*t*as;
|
||||
double distance = nonZeroSign(crossProduct(direction(t), qe))*qe.length();
|
||||
if (fabs(distance) < fabs(minDistance)) {
|
||||
minDistance = distance;
|
||||
double distance = qe.length();
|
||||
if (distance < fabs(minDistance)) {
|
||||
minDistance = nonZeroSign(crossProduct(direction(t), qe))*distance;
|
||||
param = t;
|
||||
}
|
||||
}
|
||||
|
|
@ -381,6 +385,27 @@ void CubicSegment::bound(double &l, double &b, double &r, double &t) const {
|
|||
pointBounds(point(params[i]), l, b, r, t);
|
||||
}
|
||||
|
||||
void LinearSegment::reverse() {
|
||||
Point2 tmp = p[0];
|
||||
p[0] = p[1];
|
||||
p[1] = tmp;
|
||||
}
|
||||
|
||||
void QuadraticSegment::reverse() {
|
||||
Point2 tmp = p[0];
|
||||
p[0] = p[2];
|
||||
p[2] = tmp;
|
||||
}
|
||||
|
||||
void CubicSegment::reverse() {
|
||||
Point2 tmp = p[0];
|
||||
p[0] = p[3];
|
||||
p[3] = tmp;
|
||||
tmp = p[1];
|
||||
p[1] = p[2];
|
||||
p[2] = tmp;
|
||||
}
|
||||
|
||||
void LinearSegment::moveStartPoint(Point2 to) {
|
||||
p[0] = to;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@ public:
|
|||
/// Adjusts the bounding box to fit the edge segment.
|
||||
virtual void bound(double &l, double &b, double &r, double &t) const = 0;
|
||||
|
||||
/// Reverses the edge (swaps its start point and end point).
|
||||
virtual void reverse() = 0;
|
||||
/// Moves the start point of the edge segment.
|
||||
virtual void moveStartPoint(Point2 to) = 0;
|
||||
/// Moves the end point of the edge segment.
|
||||
|
|
@ -60,6 +62,7 @@ public:
|
|||
int scanlineIntersections(double x[3], int dy[3], double y) const;
|
||||
void bound(double &l, double &b, double &r, double &t) const;
|
||||
|
||||
void reverse();
|
||||
void moveStartPoint(Point2 to);
|
||||
void moveEndPoint(Point2 to);
|
||||
void splitInThirds(EdgeSegment *&part1, EdgeSegment *&part2, EdgeSegment *&part3) const;
|
||||
|
|
@ -81,6 +84,7 @@ public:
|
|||
int scanlineIntersections(double x[3], int dy[3], double y) const;
|
||||
void bound(double &l, double &b, double &r, double &t) const;
|
||||
|
||||
void reverse();
|
||||
void moveStartPoint(Point2 to);
|
||||
void moveEndPoint(Point2 to);
|
||||
void splitInThirds(EdgeSegment *&part1, EdgeSegment *&part2, EdgeSegment *&part3) const;
|
||||
|
|
@ -104,6 +108,7 @@ public:
|
|||
int scanlineIntersections(double x[3], int dy[3], double y) const;
|
||||
void bound(double &l, double &b, double &r, double &t) const;
|
||||
|
||||
void reverse();
|
||||
void moveStartPoint(Point2 to);
|
||||
void moveEndPoint(Point2 to);
|
||||
void splitInThirds(EdgeSegment *&part1, EdgeSegment *&part2, EdgeSegment *&part3) const;
|
||||
|
|
|
|||
Loading…
Reference in New Issue