Performance optimization - using squared distance metric until the last moment

This commit is contained in:
Chlumsky 2019-04-10 14:27:49 +02:00
parent 4f3a0d620f
commit 6c1e1d01ee
12 changed files with 142 additions and 132 deletions

View File

@ -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.

View File

@ -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);
} }
} }

View File

@ -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);

View File

@ -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);
} }

View File

@ -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.

View File

@ -5,22 +5,22 @@
namespace msdfgen { namespace msdfgen {
static void initDistance(double &distance) { static void initDistance(double &sqDistance) {
distance = SignedDistance::INFINITE.distance; sqDistance = SignedDistance::INFINITE.sqDistance;
} }
static void initDistance(MultiDistance &distance) { static void initDistance(MultiDistance &sqDistance) {
distance.r = SignedDistance::INFINITE.distance; sqDistance.r = SignedDistance::INFINITE.sqDistance;
distance.g = SignedDistance::INFINITE.distance; sqDistance.g = SignedDistance::INFINITE.sqDistance;
distance.b = SignedDistance::INFINITE.distance; sqDistance.b = SignedDistance::INFINITE.sqDistance;
} }
static double resolveDistance(double distance) { static double resolveDistance(double sqDistance) {
return distance; return sqDistance;
} }
static double resolveDistance(const MultiDistance &distance) { static double resolveDistance(const MultiDistance &sqDistance) {
return median(distance.r, distance.g, distance.b); 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 { typename SimpleContourCombiner<EdgeSelector>::DistanceType SimpleContourCombiner<EdgeSelector>::squaredDistance() const {
return shapeEdgeSelector.distance(); 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) { void OverlappingContourCombiner<EdgeSelector>::setContourEdgeSelection(int i, const EdgeSelector &edgeSelector) {
DistanceType edgeDistance = edgeSelector.distance(); 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 { typename OverlappingContourCombiner<EdgeSelector>::DistanceType OverlappingContourCombiner<EdgeSelector>::squaredDistance() const {
DistanceType shapeDistance = shapeEdgeSelector.distance(); DistanceType shapeSqDistance = shapeEdgeSelector.squaredDistance();
DistanceType innerDistance = innerEdgeSelector.distance(); DistanceType innerSqDistance = innerEdgeSelector.squaredDistance();
DistanceType outerDistance = outerEdgeSelector.distance(); DistanceType outerSqDistance = outerEdgeSelector.squaredDistance();
double innerScalarDistance = resolveDistance(innerDistance); double innerScalarSqDistance = resolveDistance(innerSqDistance);
double outerScalarDistance = resolveDistance(outerDistance); double outerScalarSqDistance = resolveDistance(outerSqDistance);
DistanceType distance; DistanceType sqDistance;
initDistance(distance); 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)) { if (innerScalarSqDistance >= 0 && fabs(innerScalarSqDistance) <= fabs(outerScalarSqDistance)) {
distance = innerDistance; 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(); DistanceType contourSqDistance = edgeSelectors[i].squaredDistance();
if (fabs(resolveDistance(contourDistance)) < fabs(outerScalarDistance) && resolveDistance(contourDistance) > resolveDistance(distance)) if (fabs(resolveDistance(contourSqDistance)) < fabs(outerScalarSqDistance) && resolveDistance(contourSqDistance) > resolveDistance(sqDistance))
distance = contourDistance; sqDistance = contourSqDistance;
} }
} else if (outerScalarDistance <= 0 && fabs(outerScalarDistance) < fabs(innerScalarDistance)) { } else if (outerScalarSqDistance <= 0 && fabs(outerScalarSqDistance) < fabs(innerScalarSqDistance)) {
distance = outerDistance; 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(); DistanceType contourSqDistance = edgeSelectors[i].squaredDistance();
if (fabs(resolveDistance(contourDistance)) < fabs(innerScalarDistance) && resolveDistance(contourDistance) < resolveDistance(distance)) if (fabs(resolveDistance(contourSqDistance)) < fabs(innerScalarSqDistance) && resolveDistance(contourSqDistance) < resolveDistance(sqDistance))
distance = contourDistance; 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(); DistanceType contourSqDistance = edgeSelectors[i].squaredDistance();
if (resolveDistance(contourDistance)*resolveDistance(distance) >= 0 && fabs(resolveDistance(contourDistance)) < fabs(resolveDistance(distance))) if (resolveDistance(contourSqDistance)*resolveDistance(sqDistance) >= 0 && fabs(resolveDistance(contourSqDistance)) < fabs(resolveDistance(sqDistance)))
distance = contourDistance; sqDistance = contourSqDistance;
} }
if (resolveDistance(distance) == resolveDistance(shapeDistance)) if (resolveDistance(sqDistance) == resolveDistance(shapeSqDistance))
distance = shapeDistance; sqDistance = shapeSqDistance;
return distance; return sqDistance;
} }
template class OverlappingContourCombiner<TrueDistanceSelector>; template class OverlappingContourCombiner<TrueDistanceSelector>;

View File

@ -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); void setContourEdgeSelection(int i, const EdgeSelector &edgeSelector);
DistanceType distance() const; 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); void setContourEdgeSelection(int i, const EdgeSelector &edgeSelector);
DistanceType distance() const; DistanceType squaredDistance() const;
private: private:
std::vector<int> windings; std::vector<int> windings;

View File

@ -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)) { if (pseudoDistance*pseudoDistance <= fabs(distance.sqDistance)) {
distance.distance = pseudoDistance; 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)) { if (pseudoDistance*pseudoDistance <= fabs(distance.sqDistance)) {
distance.distance = pseudoDistance; 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 distanceSign = nonZeroSign(crossProduct(aq, ab));
double endpointDistance = eq.length(); if (param > 0 && param < 1)
if (param > 0 && param < 1) { return SignedDistance(distanceSign*(param*ab-aq).squaredLength(), 0);
double orthoDistance = dotProduct(ab.getOrthonormal(false), aq); Vector2 qe = p[param > .5]-origin;
if (fabs(orthoDistance) < endpointDistance) return SignedDistance(distanceSign*qe.squaredLength(), fabs(dotProduct(ab.normalize(), qe.normalize())));
return SignedDistance(orthoDistance, 0);
}
return SignedDistance(nonZeroSign(crossProduct(aq, ab))*endpointDistance, fabs(dotProduct(ab.normalize(), eq.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 double sqDistance = nonZeroSign(crossProduct(p[2]-p[1], p[2]-origin))*(p[2]-origin).squaredLength(); // distance from B
if (fabs(distance) < fabs(minDistance)) { if (fabs(sqDistance) < fabs(minSqDistance)) {
minDistance = distance; 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; Vector2 qe = qa+2*t[i]*ab+t[i]*t[i]*br;
double distance = nonZeroSign(crossProduct(p[2]-p[0], endpoint-origin))*(endpoint-origin).length(); double sqDistance = nonZeroSign(crossProduct(p[2]-p[0], qe))*qe.squaredLength();
if (fabs(distance) <= fabs(minDistance)) { if (fabs(sqDistance) <= fabs(minSqDistance)) {
minDistance = distance; 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 double sqDistance = nonZeroSign(crossProduct(epDir, p[3]-origin))*(p[3]-origin).squaredLength(); // distance from B
if (fabs(distance) < fabs(minDistance)) { if (fabs(sqDistance) < fabs(minSqDistance)) {
minDistance = distance; 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; Vector2 qe = qa+3*t*ab+3*t*t*br+t*t*t*as;
double distance = nonZeroSign(crossProduct(direction(t), qpt))*qpt.length(); double sqDistance = nonZeroSign(crossProduct(direction(t), qe))*qe.squaredLength();
if (fabs(distance) < fabs(minDistance)) { if (fabs(sqDistance) < fabs(minSqDistance)) {
minDistance = distance; 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 {

View File

@ -17,8 +17,8 @@ void TrueDistanceSelector::merge(const TrueDistanceSelector &other) {
minDistance = other.minDistance; minDistance = other.minDistance;
} }
TrueDistanceSelector::DistanceType TrueDistanceSelector::distance() const { TrueDistanceSelector::DistanceType TrueDistanceSelector::squaredDistance() const {
return minDistance.distance; 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 PseudoDistanceSelectorBase::computeSquaredDistance(const Point2 &p) const {
double minDistance = minTrueDistance.distance < 0 ? minNegativePseudoDistance.distance : minPositivePseudoDistance.distance; 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)) if (fabs(distance.sqDistance) < fabs(sqDistance))
minDistance = distance.distance; 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 { PseudoDistanceSelector::DistanceType PseudoDistanceSelector::squaredDistance() const {
return computeDistance(p); 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 { MultiDistanceSelector::DistanceType MultiDistanceSelector::squaredDistance() const {
MultiDistance multiDistance; MultiDistance sqDistance;
multiDistance.r = r.computeDistance(p); sqDistance.r = r.computeSquaredDistance(p);
multiDistance.g = g.computeDistance(p); sqDistance.g = g.computeSquaredDistance(p);
multiDistance.b = b.computeDistance(p); sqDistance.b = b.computeSquaredDistance(p);
return multiDistance; return sqDistance;
} }
} }

View File

@ -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;

View File

@ -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) { inline static PixelType convert(double sqDistance, double range) {
return PixelType(distance/range+.5); 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.r = float(sign(sqDistance.r)*sqrt(fabs(sqDistance.r))/range+.5);
pixel.g = float(distance.g/range+.5); pixel.g = float(sign(sqDistance.g)*sqrt(fabs(sqDistance.g))/range+.5);
pixel.b = float(distance.b/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(); ContourCombiner::DistanceType sqDistance = contourCombiner.squaredDistance();
output(x, row) = DistancePixelConversion<ContourCombiner::DistanceType>::convert(distance, range); 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).r = float(r.minDistance.distance()/range+.5);
output(x, row).g = float(g.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).b = float(b.minDistance.distance()/range+.5);
} }
} }

View File

@ -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) {