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);
|
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;
|
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.
|
/// Computes the winding of the contour. Returns 1 if positive, -1 if negative.
|
||||||
int winding() const;
|
int winding() const;
|
||||||
|
/// Reverses the sequence of edges on the contour.
|
||||||
|
void reverse();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,12 @@
|
||||||
|
|
||||||
namespace msdfgen {
|
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(NULL) { }
|
||||||
|
|
||||||
EdgeHolder::EdgeHolder(EdgeSegment *segment) : edgeSegment(segment) { }
|
EdgeHolder::EdgeHolder(EdgeSegment *segment) : edgeSegment(segment) { }
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,9 @@ namespace msdfgen {
|
||||||
class EdgeHolder {
|
class EdgeHolder {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
/// Swaps the edges held by a and b.
|
||||||
|
static void swap(EdgeHolder &a, EdgeHolder &b);
|
||||||
|
|
||||||
EdgeHolder();
|
EdgeHolder();
|
||||||
EdgeHolder(EdgeSegment *segment);
|
EdgeHolder(EdgeSegment *segment);
|
||||||
EdgeHolder(Point2 p0, Point2 p1, EdgeColor edgeColor = WHITE);
|
EdgeHolder(Point2 p0, Point2 p1, EdgeColor edgeColor = WHITE);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
|
|
||||||
#include "Shape.h"
|
#include "Shape.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include "arithmetics.hpp"
|
#include "arithmetics.hpp"
|
||||||
|
|
||||||
namespace msdfgen {
|
namespace msdfgen {
|
||||||
|
|
@ -125,4 +126,58 @@ int Shape::edgeCount() const {
|
||||||
return total;
|
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;
|
void scanline(Scanline &line, double y) const;
|
||||||
/// Returns the total number of edge segments
|
/// Returns the total number of edge segments
|
||||||
int edgeCount() const;
|
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) {
|
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[0] = p0;
|
||||||
p[1] = p1;
|
p[1] = p1;
|
||||||
p[2] = p2;
|
p[2] = p2;
|
||||||
|
|
@ -139,18 +143,18 @@ SignedDistance QuadraticSegment::signedDistance(Point2 origin, double ¶m) co
|
||||||
param = -dotProduct(qa, epDir)/dotProduct(epDir, epDir);
|
param = -dotProduct(qa, epDir)/dotProduct(epDir, epDir);
|
||||||
{
|
{
|
||||||
epDir = direction(1);
|
epDir = direction(1);
|
||||||
double distance = nonZeroSign(crossProduct(epDir, p[2]-origin))*(p[2]-origin).length(); // distance from B
|
double distance = (p[2]-origin).length(); // distance from B
|
||||||
if (fabs(distance) < fabs(minDistance)) {
|
if (distance < fabs(minDistance)) {
|
||||||
minDistance = distance;
|
minDistance = nonZeroSign(crossProduct(epDir, p[2]-origin))*distance;
|
||||||
param = dotProduct(origin-p[1], epDir)/dotProduct(epDir, epDir);
|
param = dotProduct(origin-p[1], epDir)/dotProduct(epDir, epDir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (int i = 0; i < solutions; ++i) {
|
for (int i = 0; i < solutions; ++i) {
|
||||||
if (t[i] > 0 && t[i] < 1) {
|
if (t[i] > 0 && t[i] < 1) {
|
||||||
Point2 qe = p[0]+2*t[i]*ab+t[i]*t[i]*br-origin;
|
Point2 qe = p[0]+2*t[i]*ab+t[i]*t[i]*br-origin;
|
||||||
double distance = nonZeroSign(crossProduct(direction(t[i]), qe))*qe.length();
|
double distance = qe.length();
|
||||||
if (fabs(distance) <= fabs(minDistance)) {
|
if (distance <= fabs(minDistance)) {
|
||||||
minDistance = distance;
|
minDistance = nonZeroSign(crossProduct(direction(t[i]), qe))*distance;
|
||||||
param = t[i];
|
param = t[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -175,9 +179,9 @@ SignedDistance CubicSegment::signedDistance(Point2 origin, double ¶m) const
|
||||||
param = -dotProduct(qa, epDir)/dotProduct(epDir, epDir);
|
param = -dotProduct(qa, epDir)/dotProduct(epDir, epDir);
|
||||||
{
|
{
|
||||||
epDir = direction(1);
|
epDir = direction(1);
|
||||||
double distance = nonZeroSign(crossProduct(epDir, p[3]-origin))*(p[3]-origin).length(); // distance from B
|
double distance = (p[3]-origin).length(); // distance from B
|
||||||
if (fabs(distance) < fabs(minDistance)) {
|
if (distance < fabs(minDistance)) {
|
||||||
minDistance = distance;
|
minDistance = nonZeroSign(crossProduct(epDir, p[3]-origin))*distance;
|
||||||
param = dotProduct(epDir-(p[3]-origin), epDir)/dotProduct(epDir, epDir);
|
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)
|
if (t <= 0 || t >= 1)
|
||||||
break;
|
break;
|
||||||
qe = qa+3*t*ab+3*t*t*br+t*t*t*as;
|
qe = qa+3*t*ab+3*t*t*br+t*t*t*as;
|
||||||
double distance = nonZeroSign(crossProduct(direction(t), qe))*qe.length();
|
double distance = qe.length();
|
||||||
if (fabs(distance) < fabs(minDistance)) {
|
if (distance < fabs(minDistance)) {
|
||||||
minDistance = distance;
|
minDistance = nonZeroSign(crossProduct(direction(t), qe))*distance;
|
||||||
param = t;
|
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);
|
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) {
|
void LinearSegment::moveStartPoint(Point2 to) {
|
||||||
p[0] = to;
|
p[0] = to;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,8 @@ public:
|
||||||
/// Adjusts the bounding box to fit the edge segment.
|
/// Adjusts the bounding box to fit the edge segment.
|
||||||
virtual void bound(double &l, double &b, double &r, double &t) const = 0;
|
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.
|
/// Moves the start point of the edge segment.
|
||||||
virtual void moveStartPoint(Point2 to) = 0;
|
virtual void moveStartPoint(Point2 to) = 0;
|
||||||
/// Moves the end point of the edge segment.
|
/// Moves the end point of the edge segment.
|
||||||
|
|
@ -60,6 +62,7 @@ public:
|
||||||
int scanlineIntersections(double x[3], int dy[3], double y) const;
|
int scanlineIntersections(double x[3], int dy[3], double y) const;
|
||||||
void bound(double &l, double &b, double &r, double &t) const;
|
void bound(double &l, double &b, double &r, double &t) const;
|
||||||
|
|
||||||
|
void reverse();
|
||||||
void moveStartPoint(Point2 to);
|
void moveStartPoint(Point2 to);
|
||||||
void moveEndPoint(Point2 to);
|
void moveEndPoint(Point2 to);
|
||||||
void splitInThirds(EdgeSegment *&part1, EdgeSegment *&part2, EdgeSegment *&part3) const;
|
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;
|
int scanlineIntersections(double x[3], int dy[3], double y) const;
|
||||||
void bound(double &l, double &b, double &r, double &t) const;
|
void bound(double &l, double &b, double &r, double &t) const;
|
||||||
|
|
||||||
|
void reverse();
|
||||||
void moveStartPoint(Point2 to);
|
void moveStartPoint(Point2 to);
|
||||||
void moveEndPoint(Point2 to);
|
void moveEndPoint(Point2 to);
|
||||||
void splitInThirds(EdgeSegment *&part1, EdgeSegment *&part2, EdgeSegment *&part3) const;
|
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;
|
int scanlineIntersections(double x[3], int dy[3], double y) const;
|
||||||
void bound(double &l, double &b, double &r, double &t) const;
|
void bound(double &l, double &b, double &r, double &t) const;
|
||||||
|
|
||||||
|
void reverse();
|
||||||
void moveStartPoint(Point2 to);
|
void moveStartPoint(Point2 to);
|
||||||
void moveEndPoint(Point2 to);
|
void moveEndPoint(Point2 to);
|
||||||
void splitInThirds(EdgeSegment *&part1, EdgeSegment *&part2, EdgeSegment *&part3) const;
|
void splitInThirds(EdgeSegment *&part1, EdgeSegment *&part2, EdgeSegment *&part3) const;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue