diff --git a/core/Shape.cpp b/core/Shape.cpp index cf99bd0..eb89e25 100644 --- a/core/Shape.cpp +++ b/core/Shape.cpp @@ -4,6 +4,8 @@ #include #include "arithmetics.hpp" +#define DECONVERGE_OVERSHOOT 1.11111111111111111 // moves control points slightly more than necessary to account for floating-point errors + namespace msdfgen { Shape::Shape() : inverseYAxis(false) { } @@ -39,13 +41,23 @@ bool Shape::validate() const { return true; } -static void deconvergeEdge(EdgeHolder &edgeHolder, int param) { +static void deconvergeEdge(EdgeHolder &edgeHolder, int param, Vector2 vector) { switch (edgeHolder->type()) { case (int) QuadraticSegment::EDGE_TYPE: edgeHolder = static_cast(&*edgeHolder)->convertToCubic(); // fallthrough case (int) CubicSegment::EDGE_TYPE: - static_cast(&*edgeHolder)->deconverge(param, MSDFGEN_DECONVERGENCE_FACTOR); + { + Point2 *p = static_cast(&*edgeHolder)->p; + switch (param) { + case 0: + p[1] += (p[1]-p[0]).length()*vector; + break; + case 1: + p[2] += (p[2]-p[3]).length()*vector; + break; + } + } } } @@ -59,13 +71,19 @@ void Shape::normalize() { contour->edges.push_back(EdgeHolder(parts[1])); contour->edges.push_back(EdgeHolder(parts[2])); } else { + // Push apart convergent edge segments EdgeHolder *prevEdge = &contour->edges.back(); for (std::vector::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); + double factor = DECONVERGE_OVERSHOOT*sqrt(1-(MSDFGEN_CORNER_DOT_EPSILON-1)*(MSDFGEN_CORNER_DOT_EPSILON-1))/(MSDFGEN_CORNER_DOT_EPSILON-1); + Vector2 axis = factor*(curDir-prevDir).normalize(); + // Determine curve ordering using third-order derivative (t = 0) of crossProduct((*prevEdge)->point(1-t)-p0, (*edge)->point(t)-p0) where p0 is the corner (*edge)->point(0) + if (crossProduct((*prevEdge)->directionChange(1), (*edge)->direction(0))+crossProduct((*edge)->directionChange(0), (*prevEdge)->direction(1)) < 0) + axis = -axis; + deconvergeEdge(*prevEdge, 1, axis.getOrthogonal(true)); + deconvergeEdge(*edge, 0, axis.getOrthogonal(false)); } prevEdge = &*edge; } diff --git a/core/Shape.h b/core/Shape.h index fd9222d..248b192 100644 --- a/core/Shape.h +++ b/core/Shape.h @@ -9,8 +9,6 @@ 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 { diff --git a/core/edge-segments.cpp b/core/edge-segments.cpp index 74a2788..7f6df77 100644 --- a/core/edge-segments.cpp +++ b/core/edge-segments.cpp @@ -524,18 +524,4 @@ 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; - } -} - } diff --git a/core/edge-segments.h b/core/edge-segments.h index 2bd8967..8152416 100644 --- a/core/edge-segments.h +++ b/core/edge-segments.h @@ -141,8 +141,6 @@ public: void moveEndPoint(Point2 to); void splitInThirds(EdgeSegment *&part0, EdgeSegment *&part1, EdgeSegment *&part2) const; - void deconverge(int param, double amount); - }; }