Browse Source

Performance optimization - using squared distance metric until the last moment

Chlumsky 6 years ago
parent
commit
6c1e1d01ee

+ 1 - 1
CHANGELOG.md

@@ -1,5 +1,5 @@
 
 
-## Version 1.6 (2019-04-06)
+## Version 1.6 (2019-04-08)
 
 
 - Core algorithm rewritten to split up advanced edge selection logic into modular template arguments.
 - Core algorithm rewritten to split up advanced edge selection logic into modular template arguments.
 - Pseudo-distance evaluation reworked to eliminate discontinuities at the midpoint between edges.
 - Pseudo-distance evaluation reworked to eliminate discontinuities at the midpoint between edges.

+ 11 - 6
core/SignedDistance.cpp

@@ -2,29 +2,34 @@
 #include "SignedDistance.h"
 #include "SignedDistance.h"
 
 
 #include <cmath>
 #include <cmath>
+#include "arithmetics.hpp"
 
 
 namespace msdfgen {
 namespace msdfgen {
 
 
 const SignedDistance SignedDistance::INFINITE(-1e240, 1);
 const SignedDistance SignedDistance::INFINITE(-1e240, 1);
 
 
-SignedDistance::SignedDistance() : distance(-1e240), dot(1) { }
+SignedDistance::SignedDistance() : sqDistance(-1e240), dot(1) { }
 
 
-SignedDistance::SignedDistance(double dist, double d) : distance(dist), dot(d) { }
+SignedDistance::SignedDistance(double sqDistance, double d) : sqDistance(sqDistance), dot(d) { }
+
+double SignedDistance::distance() const {
+    return sign(sqDistance)*sqrt(fabs(sqDistance));
+}
 
 
 bool operator<(SignedDistance a, SignedDistance b) {
 bool operator<(SignedDistance a, SignedDistance b) {
-    return fabs(a.distance) < fabs(b.distance) || (fabs(a.distance) == fabs(b.distance) && a.dot < b.dot);
+    return fabs(a.sqDistance) < fabs(b.sqDistance) || (fabs(a.sqDistance) == fabs(b.sqDistance) && a.dot < b.dot);
 }
 }
 
 
 bool operator>(SignedDistance a, SignedDistance b) {
 bool operator>(SignedDistance a, SignedDistance b) {
-    return fabs(a.distance) > fabs(b.distance) || (fabs(a.distance) == fabs(b.distance) && a.dot > b.dot);
+    return fabs(a.sqDistance) > fabs(b.sqDistance) || (fabs(a.sqDistance) == fabs(b.sqDistance) && a.dot > b.dot);
 }
 }
 
 
 bool operator<=(SignedDistance a, SignedDistance b) {
 bool operator<=(SignedDistance a, SignedDistance b) {
-    return fabs(a.distance) < fabs(b.distance) || (fabs(a.distance) == fabs(b.distance) && a.dot <= b.dot);
+    return fabs(a.sqDistance) < fabs(b.sqDistance) || (fabs(a.sqDistance) == fabs(b.sqDistance) && a.dot <= b.dot);
 }
 }
 
 
 bool operator>=(SignedDistance a, SignedDistance b) {
 bool operator>=(SignedDistance a, SignedDistance b) {
-    return fabs(a.distance) > fabs(b.distance) || (fabs(a.distance) == fabs(b.distance) && a.dot >= b.dot);
+    return fabs(a.sqDistance) > fabs(b.sqDistance) || (fabs(a.sqDistance) == fabs(b.sqDistance) && a.dot >= b.dot);
 }
 }
 
 
 }
 }

+ 4 - 3
core/SignedDistance.h

@@ -3,17 +3,18 @@
 
 
 namespace msdfgen {
 namespace msdfgen {
 
 
-/// Represents a signed distance and alignment, which together can be compared to uniquely determine the closest edge segment.
+/// Represents a signed squared distance and alignment, which together can be compared to uniquely determine the closest edge segment.
 class SignedDistance {
 class SignedDistance {
 
 
 public:
 public:
     static const SignedDistance INFINITE;
     static const SignedDistance INFINITE;
 
 
-    double distance;
+    double sqDistance;
     double dot;
     double dot;
 
 
     SignedDistance();
     SignedDistance();
-    SignedDistance(double dist, double d);
+    SignedDistance(double sqDistance, double d);
+    double distance() const;
 
 
     friend bool operator<(SignedDistance a, SignedDistance b);
     friend bool operator<(SignedDistance a, SignedDistance b);
     friend bool operator>(SignedDistance a, SignedDistance b);
     friend bool operator>(SignedDistance a, SignedDistance b);

+ 4 - 0
core/Vector2.cpp

@@ -19,6 +19,10 @@ double Vector2::length() const {
     return sqrt(x*x+y*y);
     return sqrt(x*x+y*y);
 }
 }
 
 
+double Vector2::squaredLength() const {
+    return x*x+y*y;
+}
+
 double Vector2::direction() const {
 double Vector2::direction() const {
     return atan2(y, x);
     return atan2(y, x);
 }
 }

+ 2 - 0
core/Vector2.h

@@ -23,6 +23,8 @@ struct Vector2 {
     void set(double x, double y);
     void set(double x, double y);
     /// Returns the vector's length.
     /// Returns the vector's length.
     double length() const;
     double length() const;
+    /// Returns the vector's squared length. (less expensive operation than length)
+    double squaredLength() const;
     /// Returns the angle of the vector in radians (atan2).
     /// Returns the angle of the vector in radians (atan2).
     double direction() const;
     double direction() const;
     /// Returns the normalized vector - one that has the same direction but unit length.
     /// Returns the normalized vector - one that has the same direction but unit length.

+ 42 - 42
core/contour-combiners.cpp

@@ -5,22 +5,22 @@
 
 
 namespace msdfgen {
 namespace msdfgen {
 
 
-static void initDistance(double &distance) {
-    distance = SignedDistance::INFINITE.distance;
+static void initDistance(double &sqDistance) {
+    sqDistance = SignedDistance::INFINITE.sqDistance;
 }
 }
 
 
-static void initDistance(MultiDistance &distance) {
-    distance.r = SignedDistance::INFINITE.distance;
-    distance.g = SignedDistance::INFINITE.distance;
-    distance.b = SignedDistance::INFINITE.distance;
+static void initDistance(MultiDistance &sqDistance) {
+    sqDistance.r = SignedDistance::INFINITE.sqDistance;
+    sqDistance.g = SignedDistance::INFINITE.sqDistance;
+    sqDistance.b = SignedDistance::INFINITE.sqDistance;
 }
 }
 
 
-static double resolveDistance(double distance) {
-    return distance;
+static double resolveDistance(double sqDistance) {
+    return sqDistance;
 }
 }
 
 
-static double resolveDistance(const MultiDistance &distance) {
-    return median(distance.r, distance.g, distance.b);
+static double resolveDistance(const MultiDistance &sqDistance) {
+    return median(sqDistance.r, sqDistance.g, sqDistance.b);
 }
 }
 
 
 template <class EdgeSelector>
 template <class EdgeSelector>
@@ -32,13 +32,13 @@ void SimpleContourCombiner<EdgeSelector>::reset(const Point2 &p) {
 }
 }
 
 
 template <class EdgeSelector>
 template <class EdgeSelector>
-void SimpleContourCombiner<EdgeSelector>::setContourEdge(int i, const EdgeSelector &edgeSelector) {
+void SimpleContourCombiner<EdgeSelector>::setContourEdgeSelection(int i, const EdgeSelector &edgeSelector) {
     shapeEdgeSelector.merge(edgeSelector);
     shapeEdgeSelector.merge(edgeSelector);
 }
 }
 
 
 template <class EdgeSelector>
 template <class EdgeSelector>
-typename SimpleContourCombiner<EdgeSelector>::DistanceType SimpleContourCombiner<EdgeSelector>::distance() const {
-    return shapeEdgeSelector.distance();
+typename SimpleContourCombiner<EdgeSelector>::DistanceType SimpleContourCombiner<EdgeSelector>::squaredDistance() const {
+    return shapeEdgeSelector.squaredDistance();
 }
 }
 
 
 template class SimpleContourCombiner<TrueDistanceSelector>;
 template class SimpleContourCombiner<TrueDistanceSelector>;
@@ -61,58 +61,58 @@ void OverlappingContourCombiner<EdgeSelector>::reset(const Point2 &p) {
 }
 }
 
 
 template <class EdgeSelector>
 template <class EdgeSelector>
-void OverlappingContourCombiner<EdgeSelector>::setContourEdge(int i, const EdgeSelector &edgeSelector) {
-    DistanceType edgeDistance = edgeSelector.distance();
+void OverlappingContourCombiner<EdgeSelector>::setContourEdgeSelection(int i, const EdgeSelector &edgeSelector) {
+    DistanceType edgeSqDistance = edgeSelector.squaredDistance();
     edgeSelectors[i] = edgeSelector;
     edgeSelectors[i] = edgeSelector;
     shapeEdgeSelector.merge(edgeSelector);
     shapeEdgeSelector.merge(edgeSelector);
-    if (windings[i] > 0 && resolveDistance(edgeDistance) >= 0)
+    if (windings[i] > 0 && resolveDistance(edgeSqDistance) >= 0)
         innerEdgeSelector.merge(edgeSelector);
         innerEdgeSelector.merge(edgeSelector);
-    if (windings[i] < 0 && resolveDistance(edgeDistance) <= 0)
+    if (windings[i] < 0 && resolveDistance(edgeSqDistance) <= 0)
         outerEdgeSelector.merge(edgeSelector);
         outerEdgeSelector.merge(edgeSelector);
 }
 }
 
 
 template <class EdgeSelector>
 template <class EdgeSelector>
-typename OverlappingContourCombiner<EdgeSelector>::DistanceType OverlappingContourCombiner<EdgeSelector>::distance() const {
-    DistanceType shapeDistance = shapeEdgeSelector.distance();
-    DistanceType innerDistance = innerEdgeSelector.distance();
-    DistanceType outerDistance = outerEdgeSelector.distance();
-    double innerScalarDistance = resolveDistance(innerDistance);
-    double outerScalarDistance = resolveDistance(outerDistance);
-    DistanceType distance;
-    initDistance(distance);
+typename OverlappingContourCombiner<EdgeSelector>::DistanceType OverlappingContourCombiner<EdgeSelector>::squaredDistance() const {
+    DistanceType shapeSqDistance = shapeEdgeSelector.squaredDistance();
+    DistanceType innerSqDistance = innerEdgeSelector.squaredDistance();
+    DistanceType outerSqDistance = outerEdgeSelector.squaredDistance();
+    double innerScalarSqDistance = resolveDistance(innerSqDistance);
+    double outerScalarSqDistance = resolveDistance(outerSqDistance);
+    DistanceType sqDistance;
+    initDistance(sqDistance);
     int contourCount = (int) windings.size();
     int contourCount = (int) windings.size();
 
 
     int winding = 0;
     int winding = 0;
-    if (innerScalarDistance >= 0 && fabs(innerScalarDistance) <= fabs(outerScalarDistance)) {
-        distance = innerDistance;
+    if (innerScalarSqDistance >= 0 && fabs(innerScalarSqDistance) <= fabs(outerScalarSqDistance)) {
+        sqDistance = innerSqDistance;
         winding = 1;
         winding = 1;
         for (int i = 0; i < contourCount; ++i)
         for (int i = 0; i < contourCount; ++i)
             if (windings[i] > 0) {
             if (windings[i] > 0) {
-                DistanceType contourDistance = edgeSelectors[i].distance();
-                if (fabs(resolveDistance(contourDistance)) < fabs(outerScalarDistance) && resolveDistance(contourDistance) > resolveDistance(distance))
-                    distance = contourDistance;
+                DistanceType contourSqDistance = edgeSelectors[i].squaredDistance();
+                if (fabs(resolveDistance(contourSqDistance)) < fabs(outerScalarSqDistance) && resolveDistance(contourSqDistance) > resolveDistance(sqDistance))
+                    sqDistance = contourSqDistance;
             }
             }
-    } else if (outerScalarDistance <= 0 && fabs(outerScalarDistance) < fabs(innerScalarDistance)) {
-        distance = outerDistance;
+    } else if (outerScalarSqDistance <= 0 && fabs(outerScalarSqDistance) < fabs(innerScalarSqDistance)) {
+        sqDistance = outerSqDistance;
         winding = -1;
         winding = -1;
         for (int i = 0; i < contourCount; ++i)
         for (int i = 0; i < contourCount; ++i)
             if (windings[i] < 0) {
             if (windings[i] < 0) {
-                DistanceType contourDistance = edgeSelectors[i].distance();
-                if (fabs(resolveDistance(contourDistance)) < fabs(innerScalarDistance) && resolveDistance(contourDistance) < resolveDistance(distance))
-                    distance = contourDistance;
+                DistanceType contourSqDistance = edgeSelectors[i].squaredDistance();
+                if (fabs(resolveDistance(contourSqDistance)) < fabs(innerScalarSqDistance) && resolveDistance(contourSqDistance) < resolveDistance(sqDistance))
+                    sqDistance = contourSqDistance;
             }
             }
     } else
     } else
-        return shapeDistance;
+        return shapeSqDistance;
 
 
     for (int i = 0; i < contourCount; ++i)
     for (int i = 0; i < contourCount; ++i)
         if (windings[i] != winding) {
         if (windings[i] != winding) {
-            DistanceType contourDistance = edgeSelectors[i].distance();
-            if (resolveDistance(contourDistance)*resolveDistance(distance) >= 0 && fabs(resolveDistance(contourDistance)) < fabs(resolveDistance(distance)))
-                distance = contourDistance;
+            DistanceType contourSqDistance = edgeSelectors[i].squaredDistance();
+            if (resolveDistance(contourSqDistance)*resolveDistance(sqDistance) >= 0 && fabs(resolveDistance(contourSqDistance)) < fabs(resolveDistance(sqDistance)))
+                sqDistance = contourSqDistance;
         }
         }
-    if (resolveDistance(distance) == resolveDistance(shapeDistance))
-        distance = shapeDistance;
-    return distance;
+    if (resolveDistance(sqDistance) == resolveDistance(shapeSqDistance))
+        sqDistance = shapeSqDistance;
+    return sqDistance;
 }
 }
 
 
 template class OverlappingContourCombiner<TrueDistanceSelector>;
 template class OverlappingContourCombiner<TrueDistanceSelector>;

+ 4 - 4
core/contour-combiners.h

@@ -16,8 +16,8 @@ public:
 
 
     explicit SimpleContourCombiner(const Shape &shape);
     explicit SimpleContourCombiner(const Shape &shape);
     void reset(const Point2 &p);
     void reset(const Point2 &p);
-    void setContourEdge(int i, const EdgeSelector &edgeSelector);
-    DistanceType distance() const;
+    void setContourEdgeSelection(int i, const EdgeSelector &edgeSelector);
+    DistanceType squaredDistance() const;
 
 
 private:
 private:
     EdgeSelector shapeEdgeSelector;
     EdgeSelector shapeEdgeSelector;
@@ -34,8 +34,8 @@ public:
 
 
     explicit OverlappingContourCombiner(const Shape &shape);
     explicit OverlappingContourCombiner(const Shape &shape);
     void reset(const Point2 &p);
     void reset(const Point2 &p);
-    void setContourEdge(int i, const EdgeSelector &edgeSelector);
-    DistanceType distance() const;
+    void setContourEdgeSelection(int i, const EdgeSelector &edgeSelector);
+    DistanceType squaredDistance() const;
 
 
 private:
 private:
     std::vector<int> windings;
     std::vector<int> windings;

+ 34 - 35
core/edge-segments.cpp

@@ -12,9 +12,10 @@ void EdgeSegment::distanceToPseudoDistance(SignedDistance &distance, Point2 orig
         Vector2 aq = origin-point(0);
         Vector2 aq = origin-point(0);
         double ts = dotProduct(aq, dir);
         double ts = dotProduct(aq, dir);
         if (ts < 0) {
         if (ts < 0) {
+            // TODO can we get squared pseudo-distance without squaring it?
             double pseudoDistance = crossProduct(aq, dir);
             double pseudoDistance = crossProduct(aq, dir);
-            if (fabs(pseudoDistance) <= fabs(distance.distance)) {
-                distance.distance = pseudoDistance;
+            if (pseudoDistance*pseudoDistance <= fabs(distance.sqDistance)) {
+                distance.sqDistance = pseudoDistance*fabs(pseudoDistance);
                 distance.dot = 0;
                 distance.dot = 0;
             }
             }
         }
         }
@@ -23,9 +24,10 @@ void EdgeSegment::distanceToPseudoDistance(SignedDistance &distance, Point2 orig
         Vector2 bq = origin-point(1);
         Vector2 bq = origin-point(1);
         double ts = dotProduct(bq, dir);
         double ts = dotProduct(bq, dir);
         if (ts > 0) {
         if (ts > 0) {
+            // TODO can we get squared pseudo-distance without squaring it?
             double pseudoDistance = crossProduct(bq, dir);
             double pseudoDistance = crossProduct(bq, dir);
-            if (fabs(pseudoDistance) <= fabs(distance.distance)) {
-                distance.distance = pseudoDistance;
+            if (pseudoDistance*pseudoDistance <= fabs(distance.sqDistance)) {
+                distance.sqDistance = pseudoDistance*fabs(pseudoDistance);
                 distance.dot = 0;
                 distance.dot = 0;
             }
             }
         }
         }
@@ -98,14 +100,11 @@ SignedDistance LinearSegment::signedDistance(Point2 origin, double &param) const
     Vector2 aq = origin-p[0];
     Vector2 aq = origin-p[0];
     Vector2 ab = p[1]-p[0];
     Vector2 ab = p[1]-p[0];
     param = dotProduct(aq, ab)/dotProduct(ab, ab);
     param = dotProduct(aq, ab)/dotProduct(ab, ab);
-    Vector2 eq = p[param > .5]-origin;
-    double endpointDistance = eq.length();
-    if (param > 0 && param < 1) {
-        double orthoDistance = dotProduct(ab.getOrthonormal(false), aq);
-        if (fabs(orthoDistance) < endpointDistance)
-            return SignedDistance(orthoDistance, 0);
-    }
-    return SignedDistance(nonZeroSign(crossProduct(aq, ab))*endpointDistance, fabs(dotProduct(ab.normalize(), eq.normalize())));
+    double distanceSign = nonZeroSign(crossProduct(aq, ab));
+    if (param > 0 && param < 1)
+        return SignedDistance(distanceSign*(param*ab-aq).squaredLength(), 0);
+    Vector2 qe = p[param > .5]-origin;
+    return SignedDistance(distanceSign*qe.squaredLength(), fabs(dotProduct(ab.normalize(), qe.normalize())));
 }
 }
 
 
 SignedDistance QuadraticSegment::signedDistance(Point2 origin, double &param) const {
 SignedDistance QuadraticSegment::signedDistance(Point2 origin, double &param) const {
@@ -119,32 +118,32 @@ SignedDistance QuadraticSegment::signedDistance(Point2 origin, double &param) co
     double t[3];
     double t[3];
     int solutions = solveCubic(t, a, b, c, d);
     int solutions = solveCubic(t, a, b, c, d);
 
 
-    double minDistance = nonZeroSign(crossProduct(ab, qa))*qa.length(); // distance from A
+    double minSqDistance = nonZeroSign(crossProduct(ab, qa))*qa.squaredLength(); // distance from A
     param = -dotProduct(qa, ab)/dotProduct(ab, ab);
     param = -dotProduct(qa, ab)/dotProduct(ab, ab);
     {
     {
-        double distance = nonZeroSign(crossProduct(p[2]-p[1], p[2]-origin))*(p[2]-origin).length(); // distance from B
-        if (fabs(distance) < fabs(minDistance)) {
-            minDistance = distance;
+        double sqDistance = nonZeroSign(crossProduct(p[2]-p[1], p[2]-origin))*(p[2]-origin).squaredLength(); // distance from B
+        if (fabs(sqDistance) < fabs(minSqDistance)) {
+            minSqDistance = sqDistance;
             param = dotProduct(origin-p[1], p[2]-p[1])/dotProduct(p[2]-p[1], p[2]-p[1]);
             param = dotProduct(origin-p[1], p[2]-p[1])/dotProduct(p[2]-p[1], p[2]-p[1]);
         }
         }
     }
     }
     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 endpoint = p[0]+2*t[i]*ab+t[i]*t[i]*br;
-            double distance = nonZeroSign(crossProduct(p[2]-p[0], endpoint-origin))*(endpoint-origin).length();
-            if (fabs(distance) <= fabs(minDistance)) {
-                minDistance = distance;
+            Vector2 qe = qa+2*t[i]*ab+t[i]*t[i]*br;
+            double sqDistance = nonZeroSign(crossProduct(p[2]-p[0], qe))*qe.squaredLength();
+            if (fabs(sqDistance) <= fabs(minSqDistance)) {
+                minSqDistance = sqDistance;
                 param = t[i];
                 param = t[i];
             }
             }
         }
         }
     }
     }
 
 
     if (param >= 0 && param <= 1)
     if (param >= 0 && param <= 1)
-        return SignedDistance(minDistance, 0);
+        return SignedDistance(minSqDistance, 0);
     if (param < .5)
     if (param < .5)
-        return SignedDistance(minDistance, fabs(dotProduct(ab.normalize(), qa.normalize())));
+        return SignedDistance(minSqDistance, fabs(dotProduct(ab.normalize(), qa.normalize())));
     else
     else
-        return SignedDistance(minDistance, fabs(dotProduct((p[2]-p[1]).normalize(), (p[2]-origin).normalize())));
+        return SignedDistance(minSqDistance, fabs(dotProduct((p[2]-p[1]).normalize(), (p[2]-origin).normalize())));
 }
 }
 
 
 SignedDistance CubicSegment::signedDistance(Point2 origin, double &param) const {
 SignedDistance CubicSegment::signedDistance(Point2 origin, double &param) const {
@@ -154,13 +153,13 @@ SignedDistance CubicSegment::signedDistance(Point2 origin, double &param) const
     Vector2 as = (p[3]-p[2])-(p[2]-p[1])-br;
     Vector2 as = (p[3]-p[2])-(p[2]-p[1])-br;
 
 
     Vector2 epDir = direction(0);
     Vector2 epDir = direction(0);
-    double minDistance = nonZeroSign(crossProduct(epDir, qa))*qa.length(); // distance from A
+    double minSqDistance = nonZeroSign(crossProduct(epDir, qa))*qa.squaredLength(); // distance from A
     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
-        if (fabs(distance) < fabs(minDistance)) {
-            minDistance = distance;
+        double sqDistance = nonZeroSign(crossProduct(epDir, p[3]-origin))*(p[3]-origin).squaredLength(); // distance from B
+        if (fabs(sqDistance) < fabs(minSqDistance)) {
+            minSqDistance = sqDistance;
             param = dotProduct(origin+epDir-p[3], epDir)/dotProduct(epDir, epDir);
             param = dotProduct(origin+epDir-p[3], epDir)/dotProduct(epDir, epDir);
         }
         }
     }
     }
@@ -168,10 +167,10 @@ SignedDistance CubicSegment::signedDistance(Point2 origin, double &param) const
     for (int i = 0; i <= MSDFGEN_CUBIC_SEARCH_STARTS; ++i) {
     for (int i = 0; i <= MSDFGEN_CUBIC_SEARCH_STARTS; ++i) {
         double t = (double) i/MSDFGEN_CUBIC_SEARCH_STARTS;
         double t = (double) i/MSDFGEN_CUBIC_SEARCH_STARTS;
         for (int step = 0;; ++step) {
         for (int step = 0;; ++step) {
-            Vector2 qpt = point(t)-origin;
-            double distance = nonZeroSign(crossProduct(direction(t), qpt))*qpt.length();
-            if (fabs(distance) < fabs(minDistance)) {
-                minDistance = distance;
+            Vector2 qe = qa+3*t*ab+3*t*t*br+t*t*t*as;
+            double sqDistance = nonZeroSign(crossProduct(direction(t), qe))*qe.squaredLength();
+            if (fabs(sqDistance) < fabs(minSqDistance)) {
+                minSqDistance = sqDistance;
                 param = t;
                 param = t;
             }
             }
             if (step == MSDFGEN_CUBIC_SEARCH_STEPS)
             if (step == MSDFGEN_CUBIC_SEARCH_STEPS)
@@ -179,18 +178,18 @@ SignedDistance CubicSegment::signedDistance(Point2 origin, double &param) const
             // Improve t
             // Improve t
             Vector2 d1 = 3*as*t*t+6*br*t+3*ab;
             Vector2 d1 = 3*as*t*t+6*br*t+3*ab;
             Vector2 d2 = 6*as*t+6*br;
             Vector2 d2 = 6*as*t+6*br;
-            t -= dotProduct(qpt, d1)/(dotProduct(d1, d1)+dotProduct(qpt, d2));
+            t -= dotProduct(qe, d1)/(dotProduct(d1, d1)+dotProduct(qe, d2));
             if (t < 0 || t > 1)
             if (t < 0 || t > 1)
                 break;
                 break;
         }
         }
     }
     }
 
 
     if (param >= 0 && param <= 1)
     if (param >= 0 && param <= 1)
-        return SignedDistance(minDistance, 0);
+        return SignedDistance(minSqDistance, 0);
     if (param < .5)
     if (param < .5)
-        return SignedDistance(minDistance, fabs(dotProduct(direction(0).normalize(), qa.normalize())));
+        return SignedDistance(minSqDistance, fabs(dotProduct(ab.normalize(), qa.normalize())));
     else
     else
-        return SignedDistance(minDistance, fabs(dotProduct(direction(1).normalize(), (p[3]-origin).normalize())));
+        return SignedDistance(minSqDistance, fabs(dotProduct((p[3]-p[2]).normalize(), (p[3]-origin).normalize())));
 }
 }
 
 
 int LinearSegment::scanlineIntersections(double x[3], int dy[3], double y) const {
 int LinearSegment::scanlineIntersections(double x[3], int dy[3], double y) const {

+ 17 - 17
core/edge-selectors.cpp

@@ -17,8 +17,8 @@ void TrueDistanceSelector::merge(const TrueDistanceSelector &other) {
         minDistance = other.minDistance;
         minDistance = other.minDistance;
 }
 }
 
 
-TrueDistanceSelector::DistanceType TrueDistanceSelector::distance() const {
-    return minDistance.distance;
+TrueDistanceSelector::DistanceType TrueDistanceSelector::squaredDistance() const {
+    return minDistance.sqDistance;
 }
 }
 
 
 bool PseudoDistanceSelectorBase::pointFacingEdge(const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge, const Point2 &p, double param) {
 bool PseudoDistanceSelectorBase::pointFacingEdge(const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge, const Point2 &p, double param) {
@@ -48,7 +48,7 @@ void PseudoDistanceSelectorBase::addEdgeTrueDistance(const EdgeSegment *edge, co
 }
 }
 
 
 void PseudoDistanceSelectorBase::addEdgePseudoDistance(const SignedDistance &distance) {
 void PseudoDistanceSelectorBase::addEdgePseudoDistance(const SignedDistance &distance) {
-    SignedDistance &minPseudoDistance = distance.distance < 0 ? minNegativePseudoDistance : minPositivePseudoDistance;
+    SignedDistance &minPseudoDistance = distance.sqDistance < 0 ? minNegativePseudoDistance : minPositivePseudoDistance;
     if (distance < minPseudoDistance)
     if (distance < minPseudoDistance)
         minPseudoDistance = distance;
         minPseudoDistance = distance;
 }
 }
@@ -65,15 +65,15 @@ void PseudoDistanceSelectorBase::merge(const PseudoDistanceSelectorBase &other)
         minPositivePseudoDistance = other.minPositivePseudoDistance;
         minPositivePseudoDistance = other.minPositivePseudoDistance;
 }
 }
 
 
-double PseudoDistanceSelectorBase::computeDistance(const Point2 &p) const {
-    double minDistance = minTrueDistance.distance < 0 ? minNegativePseudoDistance.distance : minPositivePseudoDistance.distance;
+double PseudoDistanceSelectorBase::computeSquaredDistance(const Point2 &p) const {
+    double sqDistance = minTrueDistance.sqDistance < 0 ? minNegativePseudoDistance.sqDistance : minPositivePseudoDistance.sqDistance;
     if (nearEdge) {
     if (nearEdge) {
         SignedDistance distance = minTrueDistance;
         SignedDistance distance = minTrueDistance;
         nearEdge->distanceToPseudoDistance(distance, p, nearEdgeParam);
         nearEdge->distanceToPseudoDistance(distance, p, nearEdgeParam);
-        if (fabs(distance.distance) < fabs(minDistance))
-            minDistance = distance.distance;
+        if (fabs(distance.sqDistance) < fabs(sqDistance))
+            sqDistance = distance.sqDistance;
     }
     }
-    return minDistance;
+    return sqDistance;
 }
 }
 
 
 PseudoDistanceSelector::PseudoDistanceSelector(const Point2 &p) : p(p) { }
 PseudoDistanceSelector::PseudoDistanceSelector(const Point2 &p) : p(p) { }
@@ -88,8 +88,8 @@ void PseudoDistanceSelector::addEdge(const EdgeSegment *prevEdge, const EdgeSegm
     }
     }
 }
 }
 
 
-PseudoDistanceSelector::DistanceType PseudoDistanceSelector::distance() const {
-    return computeDistance(p);
+PseudoDistanceSelector::DistanceType PseudoDistanceSelector::squaredDistance() const {
+    return computeSquaredDistance(p);
 }
 }
 
 
 MultiDistanceSelector::MultiDistanceSelector(const Point2 &p) : p(p) { }
 MultiDistanceSelector::MultiDistanceSelector(const Point2 &p) : p(p) { }
@@ -103,7 +103,7 @@ void MultiDistanceSelector::addEdge(const EdgeSegment *prevEdge, const EdgeSegme
         g.addEdgeTrueDistance(edge, distance, param);
         g.addEdgeTrueDistance(edge, distance, param);
     if (edge->color&BLUE)
     if (edge->color&BLUE)
         b.addEdgeTrueDistance(edge, distance, param);
         b.addEdgeTrueDistance(edge, distance, param);
-    if (PseudoDistanceSelector::pointFacingEdge(prevEdge, edge, nextEdge, p, param)) {
+    if (PseudoDistanceSelectorBase::pointFacingEdge(prevEdge, edge, nextEdge, p, param)) {
         edge->distanceToPseudoDistance(distance, p, param);
         edge->distanceToPseudoDistance(distance, p, param);
         if (edge->color&RED)
         if (edge->color&RED)
             r.addEdgePseudoDistance(distance);
             r.addEdgePseudoDistance(distance);
@@ -120,12 +120,12 @@ void MultiDistanceSelector::merge(const MultiDistanceSelector &other) {
     b.merge(other.b);
     b.merge(other.b);
 }
 }
 
 
-MultiDistanceSelector::DistanceType MultiDistanceSelector::distance() const {
-    MultiDistance multiDistance;
-    multiDistance.r = r.computeDistance(p);
-    multiDistance.g = g.computeDistance(p);
-    multiDistance.b = b.computeDistance(p);
-    return multiDistance;
+MultiDistanceSelector::DistanceType MultiDistanceSelector::squaredDistance() const {
+    MultiDistance sqDistance;
+    sqDistance.r = r.computeSquaredDistance(p);
+    sqDistance.g = g.computeSquaredDistance(p);
+    sqDistance.b = b.computeSquaredDistance(p);
+    return sqDistance;
 }
 }
 
 
 }
 }

+ 4 - 4
core/edge-selectors.h

@@ -20,7 +20,7 @@ public:
     explicit TrueDistanceSelector(const Point2 &p = Point2());
     explicit TrueDistanceSelector(const Point2 &p = Point2());
     void addEdge(const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge);
     void addEdge(const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge);
     void merge(const TrueDistanceSelector &other);
     void merge(const TrueDistanceSelector &other);
-    DistanceType distance() const;
+    DistanceType squaredDistance() const;
 
 
 private:
 private:
     Point2 p;
     Point2 p;
@@ -37,7 +37,7 @@ public:
     void addEdgeTrueDistance(const EdgeSegment *edge, const SignedDistance &distance, double param);
     void addEdgeTrueDistance(const EdgeSegment *edge, const SignedDistance &distance, double param);
     void addEdgePseudoDistance(const SignedDistance &distance);
     void addEdgePseudoDistance(const SignedDistance &distance);
     void merge(const PseudoDistanceSelectorBase &other);
     void merge(const PseudoDistanceSelectorBase &other);
-    double computeDistance(const Point2 &p) const;
+    double computeSquaredDistance(const Point2 &p) const;
 
 
 private:
 private:
     SignedDistance minTrueDistance;
     SignedDistance minTrueDistance;
@@ -56,7 +56,7 @@ public:
 
 
     explicit PseudoDistanceSelector(const Point2 &p = Point2());
     explicit PseudoDistanceSelector(const Point2 &p = Point2());
     void addEdge(const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge);
     void addEdge(const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge);
-    DistanceType distance() const;
+    DistanceType squaredDistance() const;
 
 
 private:
 private:
     Point2 p;
     Point2 p;
@@ -72,7 +72,7 @@ public:
     explicit MultiDistanceSelector(const Point2 &p = Point2());
     explicit MultiDistanceSelector(const Point2 &p = Point2());
     void addEdge(const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge);
     void addEdge(const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge);
     void merge(const MultiDistanceSelector &other);
     void merge(const MultiDistanceSelector &other);
-    DistanceType distance() const;
+    DistanceType squaredDistance() const;
 
 
 private:
 private:
     Point2 p;
     Point2 p;

+ 18 - 19
core/msdfgen.cpp

@@ -1,39 +1,38 @@
 
 
 #include "../msdfgen.h"
 #include "../msdfgen.h"
 
 
-#include <vector>
 #include "edge-selectors.h"
 #include "edge-selectors.h"
 #include "contour-combiners.h"
 #include "contour-combiners.h"
 
 
 namespace msdfgen {
 namespace msdfgen {
 
 
 template <typename DistanceType>
 template <typename DistanceType>
-class DistancePixelConversion;
+class SquaredDistancePixelConversion;
 
 
 template <>
 template <>
-class DistancePixelConversion<double> {
+class SquaredDistancePixelConversion<double> {
 public:
 public:
     typedef float PixelType;
     typedef float PixelType;
-    inline static PixelType convert(double distance, double range) {
-        return PixelType(distance/range+.5);
+    inline static PixelType convert(double sqDistance, double range) {
+        return PixelType(sign(sqDistance)*sqrt(fabs(sqDistance))/range+.5);
     }
     }
 };
 };
 
 
 template <>
 template <>
-class DistancePixelConversion<MultiDistance> {
+class SquaredDistancePixelConversion<MultiDistance> {
 public:
 public:
     typedef FloatRGB PixelType;
     typedef FloatRGB PixelType;
-    inline static PixelType convert(const MultiDistance &distance, double range) {
+    inline static PixelType convert(const MultiDistance &sqDistance, double range) {
         PixelType pixel;
         PixelType pixel;
-        pixel.r = float(distance.r/range+.5);
-        pixel.g = float(distance.g/range+.5);
-        pixel.b = float(distance.b/range+.5);
+        pixel.r = float(sign(sqDistance.r)*sqrt(fabs(sqDistance.r))/range+.5);
+        pixel.g = float(sign(sqDistance.g)*sqrt(fabs(sqDistance.g))/range+.5);
+        pixel.b = float(sign(sqDistance.b)*sqrt(fabs(sqDistance.b))/range+.5);
         return pixel;
         return pixel;
     }
     }
 };
 };
 
 
 template <class ContourCombiner>
 template <class ContourCombiner>
-void generateDistanceField(Bitmap<typename DistancePixelConversion<typename ContourCombiner::DistanceType>::PixelType> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate) {
+void generateDistanceField(Bitmap<typename SquaredDistancePixelConversion<typename ContourCombiner::DistanceType>::PixelType> &output, const Shape &shape, double range, const Vector2 &scale, const Vector2 &translate) {
     int w = output.width(), h = output.height();
     int w = output.width(), h = output.height();
 
 
 #ifdef MSDFGEN_USE_OPENMP
 #ifdef MSDFGEN_USE_OPENMP
@@ -66,12 +65,12 @@ void generateDistanceField(Bitmap<typename DistancePixelConversion<typename Cont
                             curEdge = nextEdge;
                             curEdge = nextEdge;
                         }
                         }
 
 
-                        contourCombiner.setContourEdge(int(contour-shape.contours.begin()), edgeSelector);
+                        contourCombiner.setContourEdgeSelection(int(contour-shape.contours.begin()), edgeSelector);
                     }
                     }
                 }
                 }
 
 
-                ContourCombiner::DistanceType distance = contourCombiner.distance();
-                output(x, row) = DistancePixelConversion<ContourCombiner::DistanceType>::convert(distance, range);
+                ContourCombiner::DistanceType sqDistance = contourCombiner.squaredDistance();
+                output(x, row) = SquaredDistancePixelConversion<ContourCombiner::DistanceType>::convert(sqDistance, range);
             }
             }
         }
         }
     }
     }
@@ -179,7 +178,7 @@ void generateSDF_legacy(Bitmap<float> &output, const Shape &shape, double range,
                     if (distance < minDistance)
                     if (distance < minDistance)
                         minDistance = distance;
                         minDistance = distance;
                 }
                 }
-            output(x, row) = float(minDistance.distance/range+.5);
+            output(x, row) = float(minDistance.distance()/range+.5);
         }
         }
     }
     }
 }
 }
@@ -208,7 +207,7 @@ void generatePseudoSDF_legacy(Bitmap<float> &output, const Shape &shape, double
                 }
                 }
             if (nearEdge)
             if (nearEdge)
                 (*nearEdge)->distanceToPseudoDistance(minDistance, p, nearParam);
                 (*nearEdge)->distanceToPseudoDistance(minDistance, p, nearParam);
-            output(x, row) = float(minDistance.distance/range+.5);
+            output(x, row) = float(minDistance.distance()/range+.5);
         }
         }
     }
     }
 }
 }
@@ -258,9 +257,9 @@ void generateMSDF_legacy(Bitmap<FloatRGB> &output, const Shape &shape, double ra
                 (*g.nearEdge)->distanceToPseudoDistance(g.minDistance, p, g.nearParam);
                 (*g.nearEdge)->distanceToPseudoDistance(g.minDistance, p, g.nearParam);
             if (b.nearEdge)
             if (b.nearEdge)
                 (*b.nearEdge)->distanceToPseudoDistance(b.minDistance, p, b.nearParam);
                 (*b.nearEdge)->distanceToPseudoDistance(b.minDistance, p, b.nearParam);
-            output(x, row).r = float(r.minDistance.distance/range+.5);
-            output(x, row).g = float(g.minDistance.distance/range+.5);
-            output(x, row).b = float(b.minDistance.distance/range+.5);
+            output(x, row).r = float(r.minDistance.distance()/range+.5);
+            output(x, row).g = float(g.minDistance.distance()/range+.5);
+            output(x, row).b = float(b.minDistance.distance()/range+.5);
         }
         }
     }
     }
 
 

+ 1 - 1
main.cpp

@@ -816,7 +816,7 @@ int main(int argc, const char * const *argv) {
                 if (distance < minDistance)
                 if (distance < minDistance)
                     minDistance = distance;
                     minDistance = distance;
             }
             }
-        orientation = minDistance.distance <= 0 ? KEEP : REVERSE;
+        orientation = minDistance.sqDistance <= 0 ? KEEP : REVERSE;
     }
     }
     if (orientation == REVERSE) {
     if (orientation == REVERSE) {
         switch (mode) {
         switch (mode) {