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.
- Pseudo-distance evaluation reworked to eliminate discontinuities at the midpoint between edges.

View File

@ -2,29 +2,34 @@
#include "SignedDistance.h"
#include <cmath>
#include "arithmetics.hpp"
namespace msdfgen {
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) {
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) {
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) {
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) {
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 {
/// 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 {
public:
static const SignedDistance INFINITE;
double distance;
double sqDistance;
double dot;
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);

View File

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

View File

@ -23,6 +23,8 @@ struct Vector2 {
void set(double x, double y);
/// Returns the vector's length.
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).
double direction() const;
/// Returns the normalized vector - one that has the same direction but unit length.

View File

@ -5,22 +5,22 @@
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>
@ -32,13 +32,13 @@ void SimpleContourCombiner<EdgeSelector>::reset(const Point2 &p) {
}
template <class EdgeSelector>
void SimpleContourCombiner<EdgeSelector>::setContourEdge(int i, const EdgeSelector &edgeSelector) {
void SimpleContourCombiner<EdgeSelector>::setContourEdgeSelection(int i, const EdgeSelector &edgeSelector) {
shapeEdgeSelector.merge(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>;
@ -61,58 +61,58 @@ void OverlappingContourCombiner<EdgeSelector>::reset(const Point2 &p) {
}
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;
shapeEdgeSelector.merge(edgeSelector);
if (windings[i] > 0 && resolveDistance(edgeDistance) >= 0)
if (windings[i] > 0 && resolveDistance(edgeSqDistance) >= 0)
innerEdgeSelector.merge(edgeSelector);
if (windings[i] < 0 && resolveDistance(edgeDistance) <= 0)
if (windings[i] < 0 && resolveDistance(edgeSqDistance) <= 0)
outerEdgeSelector.merge(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 winding = 0;
if (innerScalarDistance >= 0 && fabs(innerScalarDistance) <= fabs(outerScalarDistance)) {
distance = innerDistance;
if (innerScalarSqDistance >= 0 && fabs(innerScalarSqDistance) <= fabs(outerScalarSqDistance)) {
sqDistance = innerSqDistance;
winding = 1;
for (int i = 0; i < contourCount; ++i)
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;
for (int i = 0; i < contourCount; ++i)
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
return shapeDistance;
return shapeSqDistance;
for (int i = 0; i < contourCount; ++i)
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>;

View File

@ -16,8 +16,8 @@ public:
explicit SimpleContourCombiner(const Shape &shape);
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:
EdgeSelector shapeEdgeSelector;
@ -34,8 +34,8 @@ public:
explicit OverlappingContourCombiner(const Shape &shape);
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:
std::vector<int> windings;

View File

@ -12,9 +12,10 @@ void EdgeSegment::distanceToPseudoDistance(SignedDistance &distance, Point2 orig
Vector2 aq = origin-point(0);
double ts = dotProduct(aq, dir);
if (ts < 0) {
// TODO can we get squared pseudo-distance without squaring it?
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;
}
}
@ -23,9 +24,10 @@ void EdgeSegment::distanceToPseudoDistance(SignedDistance &distance, Point2 orig
Vector2 bq = origin-point(1);
double ts = dotProduct(bq, dir);
if (ts > 0) {
// TODO can we get squared pseudo-distance without squaring it?
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;
}
}
@ -98,14 +100,11 @@ SignedDistance LinearSegment::signedDistance(Point2 origin, double &param) const
Vector2 aq = origin-p[0];
Vector2 ab = p[1]-p[0];
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 {
@ -119,32 +118,32 @@ SignedDistance QuadraticSegment::signedDistance(Point2 origin, double &param) co
double t[3];
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);
{
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]);
}
}
for (int i = 0; i < solutions; ++i) {
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];
}
}
}
if (param >= 0 && param <= 1)
return SignedDistance(minDistance, 0);
return SignedDistance(minSqDistance, 0);
if (param < .5)
return SignedDistance(minDistance, fabs(dotProduct(ab.normalize(), qa.normalize())));
return SignedDistance(minSqDistance, fabs(dotProduct(ab.normalize(), qa.normalize())));
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 {
@ -154,13 +153,13 @@ SignedDistance CubicSegment::signedDistance(Point2 origin, double &param) const
Vector2 as = (p[3]-p[2])-(p[2]-p[1])-br;
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);
{
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);
}
}
@ -168,10 +167,10 @@ SignedDistance CubicSegment::signedDistance(Point2 origin, double &param) const
for (int i = 0; i <= MSDFGEN_CUBIC_SEARCH_STARTS; ++i) {
double t = (double) i/MSDFGEN_CUBIC_SEARCH_STARTS;
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;
}
if (step == MSDFGEN_CUBIC_SEARCH_STEPS)
@ -179,18 +178,18 @@ SignedDistance CubicSegment::signedDistance(Point2 origin, double &param) const
// Improve t
Vector2 d1 = 3*as*t*t+6*br*t+3*ab;
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)
break;
}
}
if (param >= 0 && param <= 1)
return SignedDistance(minDistance, 0);
return SignedDistance(minSqDistance, 0);
if (param < .5)
return SignedDistance(minDistance, fabs(dotProduct(direction(0).normalize(), qa.normalize())));
return SignedDistance(minSqDistance, fabs(dotProduct(ab.normalize(), qa.normalize())));
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 {

View File

@ -17,8 +17,8 @@ void TrueDistanceSelector::merge(const TrueDistanceSelector &other) {
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) {
@ -48,7 +48,7 @@ void PseudoDistanceSelectorBase::addEdgeTrueDistance(const EdgeSegment *edge, co
}
void PseudoDistanceSelectorBase::addEdgePseudoDistance(const SignedDistance &distance) {
SignedDistance &minPseudoDistance = distance.distance < 0 ? minNegativePseudoDistance : minPositivePseudoDistance;
SignedDistance &minPseudoDistance = distance.sqDistance < 0 ? minNegativePseudoDistance : minPositivePseudoDistance;
if (distance < minPseudoDistance)
minPseudoDistance = distance;
}
@ -65,15 +65,15 @@ void PseudoDistanceSelectorBase::merge(const PseudoDistanceSelectorBase &other)
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) {
SignedDistance distance = minTrueDistance;
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) { }
@ -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) { }
@ -103,7 +103,7 @@ void MultiDistanceSelector::addEdge(const EdgeSegment *prevEdge, const EdgeSegme
g.addEdgeTrueDistance(edge, distance, param);
if (edge->color&BLUE)
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);
if (edge->color&RED)
r.addEdgePseudoDistance(distance);
@ -120,12 +120,12 @@ void MultiDistanceSelector::merge(const MultiDistanceSelector &other) {
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;
}
}

View File

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

View File

@ -1,39 +1,38 @@
#include "../msdfgen.h"
#include <vector>
#include "edge-selectors.h"
#include "contour-combiners.h"
namespace msdfgen {
template <typename DistanceType>
class DistancePixelConversion;
class SquaredDistancePixelConversion;
template <>
class DistancePixelConversion<double> {
class SquaredDistancePixelConversion<double> {
public:
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 <>
class DistancePixelConversion<MultiDistance> {
class SquaredDistancePixelConversion<MultiDistance> {
public:
typedef FloatRGB PixelType;
inline static PixelType convert(const MultiDistance &distance, double range) {
inline static PixelType convert(const MultiDistance &sqDistance, double range) {
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;
}
};
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();
#ifdef MSDFGEN_USE_OPENMP
@ -66,12 +65,12 @@ void generateDistanceField(Bitmap<typename DistancePixelConversion<typename Cont
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)
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)
(*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);
if (b.nearEdge)
(*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);
}
}

View File

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