Skip to content

Commit

Permalink
Convergent corner elimination routine added to shape normalization
Browse files Browse the repository at this point in the history
  • Loading branch information
Chlumsky committed Oct 11, 2020
1 parent cddc445 commit 80039d7
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 18 deletions.
14 changes: 9 additions & 5 deletions core/EdgeHolder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
29 changes: 28 additions & 1 deletion core/Shape.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@

#include "Shape.h"

#include "arithmetics.hpp"

namespace msdfgen {

Shape::Shape() : inverseYAxis(false) { }
Expand Down Expand Up @@ -36,16 +38,41 @@ 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]);
contour->edges.clear();
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 {
Expand Down
5 changes: 5 additions & 0 deletions core/Shape.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down
30 changes: 30 additions & 0 deletions core/edge-segments.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 &param) const {
Vector2 aq = origin-p[0];
Vector2 ab = p[1]-p[0];
Expand Down Expand Up @@ -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;
}
}

}
9 changes: 9 additions & 0 deletions core/edge-segments.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ class EdgeSegment {
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 &param) const = 0;
/// Converts a previously retrieved signed distance from origin to pseudo-distance.
Expand Down Expand Up @@ -53,6 +55,7 @@ class LinearSegment : public EdgeSegment {
LinearSegment * clone() const;
Point2 point(double param) const;
Vector2 direction(double param) const;
Vector2 directionChange(double param) const;
SignedDistance signedDistance(Point2 origin, double &param) const;
int scanlineIntersections(double x[3], int dy[3], double y) const;
void bound(double &l, double &b, double &r, double &t) const;
Expand All @@ -73,6 +76,7 @@ class QuadraticSegment : public EdgeSegment {
QuadraticSegment * clone() const;
Point2 point(double param) const;
Vector2 direction(double param) const;
Vector2 directionChange(double param) const;
SignedDistance signedDistance(Point2 origin, double &param) const;
int scanlineIntersections(double x[3], int dy[3], double y) const;
void bound(double &l, double &b, double &r, double &t) const;
Expand All @@ -81,6 +85,8 @@ class QuadraticSegment : public EdgeSegment {
void moveEndPoint(Point2 to);
void splitInThirds(EdgeSegment *&part1, EdgeSegment *&part2, EdgeSegment *&part3) const;

EdgeSegment * convertToCubic() const;

};

/// A cubic Bezier curve.
Expand All @@ -93,6 +99,7 @@ class CubicSegment : public EdgeSegment {
CubicSegment * clone() const;
Point2 point(double param) const;
Vector2 direction(double param) const;
Vector2 directionChange(double param) const;
SignedDistance signedDistance(Point2 origin, double &param) const;
int scanlineIntersections(double x[3], int dy[3], double y) const;
void bound(double &l, double &b, double &r, double &t) const;
Expand All @@ -101,6 +108,8 @@ class CubicSegment : public EdgeSegment {
void moveEndPoint(Point2 to);
void splitInThirds(EdgeSegment *&part1, EdgeSegment *&part2, EdgeSegment *&part3) const;

void deconverge(int param, double amount);

};

}
22 changes: 10 additions & 12 deletions core/edge-selectors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down

0 comments on commit 80039d7

Please sign in to comment.