mirror of https://github.com/Chlumsky/msdfgen.git
Performance optimization - using squared distance metric until the last moment
This commit is contained in:
parent
4f3a0d620f
commit
6c1e1d01ee
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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>;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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 ¶m) 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 ¶m) const {
|
SignedDistance QuadraticSegment::signedDistance(Point2 origin, double ¶m) const {
|
||||||
|
|
@ -119,32 +118,32 @@ SignedDistance QuadraticSegment::signedDistance(Point2 origin, double ¶m) 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 ¶m) const {
|
SignedDistance CubicSegment::signedDistance(Point2 origin, double ¶m) const {
|
||||||
|
|
@ -154,13 +153,13 @@ SignedDistance CubicSegment::signedDistance(Point2 origin, double ¶m) 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 ¶m) 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 ¶m) 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,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
2
main.cpp
2
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) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue