mirror of https://github.com/Chlumsky/msdfgen.git
Convergent corner elimination routine added to shape normalization
This commit is contained in:
parent
cddc445bcc
commit
80039d7ed6
|
|
@ -26,16 +26,20 @@ EdgeHolder::~EdgeHolder() {
|
|||
}
|
||||
|
||||
EdgeHolder & EdgeHolder::operator=(const EdgeHolder &orig) {
|
||||
delete edgeSegment;
|
||||
edgeSegment = orig.edgeSegment ? orig.edgeSegment->clone() : NULL;
|
||||
if (this != &orig) {
|
||||
delete edgeSegment;
|
||||
edgeSegment = orig.edgeSegment ? orig.edgeSegment->clone() : NULL;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
#ifdef MSDFGEN_USE_CPP11
|
||||
EdgeHolder & EdgeHolder::operator=(EdgeHolder &&orig) {
|
||||
delete edgeSegment;
|
||||
edgeSegment = orig.edgeSegment;
|
||||
orig.edgeSegment = NULL;
|
||||
if (this != &orig) {
|
||||
delete edgeSegment;
|
||||
edgeSegment = orig.edgeSegment;
|
||||
orig.edgeSegment = NULL;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
|
||||
#include "Shape.h"
|
||||
|
||||
#include "arithmetics.hpp"
|
||||
|
||||
namespace msdfgen {
|
||||
|
||||
Shape::Shape() : inverseYAxis(false) { }
|
||||
|
|
@ -36,8 +38,21 @@ bool Shape::validate() const {
|
|||
return true;
|
||||
}
|
||||
|
||||
static void deconvergeEdge(EdgeHolder &edgeHolder, int param) {
|
||||
{
|
||||
const QuadraticSegment *quadraticSegment = dynamic_cast<const QuadraticSegment *>(&*edgeHolder);
|
||||
if (quadraticSegment)
|
||||
edgeHolder = quadraticSegment->convertToCubic();
|
||||
}
|
||||
{
|
||||
CubicSegment *cubicSegment = dynamic_cast<CubicSegment *>(&*edgeHolder);
|
||||
if (cubicSegment)
|
||||
cubicSegment->deconverge(param, MSDFGEN_DECONVERGENCE_FACTOR);
|
||||
}
|
||||
}
|
||||
|
||||
void Shape::normalize() {
|
||||
for (std::vector<Contour>::iterator contour = contours.begin(); contour != contours.end(); ++contour)
|
||||
for (std::vector<Contour>::iterator contour = contours.begin(); contour != contours.end(); ++contour) {
|
||||
if (contour->edges.size() == 1) {
|
||||
EdgeSegment *parts[3] = { };
|
||||
contour->edges[0]->splitInThirds(parts[0], parts[1], parts[2]);
|
||||
|
|
@ -45,7 +60,19 @@ void Shape::normalize() {
|
|||
contour->edges.push_back(EdgeHolder(parts[0]));
|
||||
contour->edges.push_back(EdgeHolder(parts[1]));
|
||||
contour->edges.push_back(EdgeHolder(parts[2]));
|
||||
} else {
|
||||
EdgeHolder *prevEdge = &contour->edges.back();
|
||||
for (std::vector<EdgeHolder>::iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
|
||||
Vector2 prevDir = (*prevEdge)->direction(1).normalize();
|
||||
Vector2 curDir = (*edge)->direction(0).normalize();
|
||||
if (dotProduct(prevDir, curDir) < MSDFGEN_CORNER_DOT_EPSILON-1) {
|
||||
deconvergeEdge(*prevEdge, 1);
|
||||
deconvergeEdge(*edge, 0);
|
||||
}
|
||||
prevEdge = &*edge;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Shape::bound(double &l, double &b, double &r, double &t) const {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,11 @@
|
|||
|
||||
namespace msdfgen {
|
||||
|
||||
// Threshold of the dot product of adjacent edge directions to be considered convergent.
|
||||
#define MSDFGEN_CORNER_DOT_EPSILON .000001
|
||||
// The proportional amount by which a curve's control point will be adjusted to eliminate convergent corners.
|
||||
#define MSDFGEN_DECONVERGENCE_FACTOR .000001
|
||||
|
||||
/// Vector shape representation.
|
||||
class Shape {
|
||||
|
||||
|
|
|
|||
|
|
@ -97,6 +97,18 @@ Vector2 CubicSegment::direction(double param) const {
|
|||
return tangent;
|
||||
}
|
||||
|
||||
Vector2 LinearSegment::directionChange(double param) const {
|
||||
return Vector2();
|
||||
}
|
||||
|
||||
Vector2 QuadraticSegment::directionChange(double param) const {
|
||||
return (p[2]-p[1])-(p[1]-p[0]);
|
||||
}
|
||||
|
||||
Vector2 CubicSegment::directionChange(double param) const {
|
||||
return mix((p[2]-p[1])-(p[1]-p[0]), (p[3]-p[2])-(p[2]-p[1]), param);
|
||||
}
|
||||
|
||||
SignedDistance LinearSegment::signedDistance(Point2 origin, double ¶m) const {
|
||||
Vector2 aq = origin-p[0];
|
||||
Vector2 ab = p[1]-p[0];
|
||||
|
|
@ -426,4 +438,22 @@ void CubicSegment::splitInThirds(EdgeSegment *&part1, EdgeSegment *&part2, EdgeS
|
|||
part3 = new CubicSegment(point(2/3.), mix(mix(p[1], p[2], 2/3.), mix(p[2], p[3], 2/3.), 2/3.), p[2] == p[3] ? p[3] : mix(p[2], p[3], 2/3.), p[3], color);
|
||||
}
|
||||
|
||||
EdgeSegment * QuadraticSegment::convertToCubic() const {
|
||||
return new CubicSegment(p[0], mix(p[0], p[1], 2/3.), mix(p[1], p[2], 1/3.), p[2], color);
|
||||
}
|
||||
|
||||
void CubicSegment::deconverge(int param, double amount) {
|
||||
Vector2 dir = direction(param);
|
||||
Vector2 normal = dir.getOrthonormal();
|
||||
double h = dotProduct(directionChange(param)-dir, normal);
|
||||
switch (param) {
|
||||
case 0:
|
||||
p[1] += amount*(dir+sign(h)*sqrt(fabs(h))*normal);
|
||||
break;
|
||||
case 1:
|
||||
p[2] -= amount*(dir-sign(h)*sqrt(fabs(h))*normal);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ public:
|
|||
virtual Point2 point(double param) const = 0;
|
||||
/// Returns the direction the edge has at the point specified by the parameter.
|
||||
virtual Vector2 direction(double param) const = 0;
|
||||
/// Returns the change of direction (second derivative) at the point specified by the parameter.
|
||||
virtual Vector2 directionChange(double param) const = 0;
|
||||
/// Returns the minimum signed distance between origin and the edge.
|
||||
virtual SignedDistance signedDistance(Point2 origin, double ¶m) const = 0;
|
||||
/// Converts a previously retrieved signed distance from origin to pseudo-distance.
|
||||
|
|
@ -53,6 +55,7 @@ public:
|
|||
LinearSegment * clone() const;
|
||||
Point2 point(double param) const;
|
||||
Vector2 direction(double param) const;
|
||||
Vector2 directionChange(double param) const;
|
||||
SignedDistance signedDistance(Point2 origin, double ¶m) const;
|
||||
int scanlineIntersections(double x[3], int dy[3], double y) const;
|
||||
void bound(double &l, double &b, double &r, double &t) const;
|
||||
|
|
@ -73,6 +76,7 @@ public:
|
|||
QuadraticSegment * clone() const;
|
||||
Point2 point(double param) const;
|
||||
Vector2 direction(double param) const;
|
||||
Vector2 directionChange(double param) const;
|
||||
SignedDistance signedDistance(Point2 origin, double ¶m) const;
|
||||
int scanlineIntersections(double x[3], int dy[3], double y) const;
|
||||
void bound(double &l, double &b, double &r, double &t) const;
|
||||
|
|
@ -81,6 +85,8 @@ public:
|
|||
void moveEndPoint(Point2 to);
|
||||
void splitInThirds(EdgeSegment *&part1, EdgeSegment *&part2, EdgeSegment *&part3) const;
|
||||
|
||||
EdgeSegment * convertToCubic() const;
|
||||
|
||||
};
|
||||
|
||||
/// A cubic Bezier curve.
|
||||
|
|
@ -93,6 +99,7 @@ public:
|
|||
CubicSegment * clone() const;
|
||||
Point2 point(double param) const;
|
||||
Vector2 direction(double param) const;
|
||||
Vector2 directionChange(double param) const;
|
||||
SignedDistance signedDistance(Point2 origin, double ¶m) const;
|
||||
int scanlineIntersections(double x[3], int dy[3], double y) const;
|
||||
void bound(double &l, double &b, double &r, double &t) const;
|
||||
|
|
@ -101,6 +108,8 @@ public:
|
|||
void moveEndPoint(Point2 to);
|
||||
void splitInThirds(EdgeSegment *&part1, EdgeSegment *&part2, EdgeSegment *&part3) const;
|
||||
|
||||
void deconverge(int param, double amount);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,19 +38,17 @@ TrueDistanceSelector::DistanceType TrueDistanceSelector::distance() const {
|
|||
|
||||
PseudoDistanceSelectorBase::EdgeCache::EdgeCache() : absDistance(0), edgeDomainDistance(0), pseudoDistance(0) { }
|
||||
|
||||
static double cornerEdgeDomainDistance(const EdgeSegment *a, const EdgeSegment *b, const Point2 &p) {
|
||||
Vector2 aDir = a->direction(1).normalize(true);
|
||||
Vector2 bDir = b->direction(0).normalize(true);
|
||||
return dotProduct(p-b->point(0), (aDir+bDir).normalize(true));
|
||||
}
|
||||
|
||||
double PseudoDistanceSelectorBase::edgeDomainDistance(const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge, const Point2 &p, double param) {
|
||||
if (param < 0) {
|
||||
Vector2 prevEdgeDir = -prevEdge->direction(1).normalize(true);
|
||||
Vector2 edgeDir = edge->direction(0).normalize(true);
|
||||
Vector2 pointDir = p-edge->point(0);
|
||||
return dotProduct(pointDir, (prevEdgeDir-edgeDir).normalize(true));
|
||||
}
|
||||
if (param > 1) {
|
||||
Vector2 edgeDir = -edge->direction(1).normalize(true);
|
||||
Vector2 nextEdgeDir = nextEdge->direction(0).normalize(true);
|
||||
Vector2 pointDir = p-edge->point(1);
|
||||
return dotProduct(pointDir, (nextEdgeDir-edgeDir).normalize(true));
|
||||
}
|
||||
if (param < 0)
|
||||
return -cornerEdgeDomainDistance(prevEdge, edge, p);
|
||||
else if (param > 1)
|
||||
return cornerEdgeDomainDistance(edge, nextEdge, p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue