mirror of https://github.com/Chlumsky/msdfgen.git
263 lines
9.8 KiB
C++
263 lines
9.8 KiB
C++
|
|
#include "edge-selectors.h"
|
|
|
|
#include <cstddef>
|
|
#include "arithmetics.hpp"
|
|
|
|
namespace msdfgen {
|
|
|
|
#define DISTANCE_DELTA_FACTOR ::msdfgen::real(1.001)
|
|
|
|
TrueDistanceSelector::EdgeCache::EdgeCache() : absDistance(0) { }
|
|
|
|
void TrueDistanceSelector::reset(const Point2 &p) {
|
|
real delta = DISTANCE_DELTA_FACTOR*(p-this->p).length();
|
|
minDistance.distance += real(nonZeroSign(minDistance.distance))*delta;
|
|
this->p = p;
|
|
}
|
|
|
|
void TrueDistanceSelector::addEdge(EdgeCache &cache, const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge) {
|
|
real delta = DISTANCE_DELTA_FACTOR*(p-cache.point).length();
|
|
if (cache.absDistance-delta <= fabs(minDistance.distance)) {
|
|
real dummy;
|
|
SignedDistance distance = edge->signedDistance(p, dummy);
|
|
if (distance < minDistance)
|
|
minDistance = distance;
|
|
cache.point = p;
|
|
cache.absDistance = fabs(distance.distance);
|
|
}
|
|
}
|
|
|
|
void TrueDistanceSelector::merge(const TrueDistanceSelector &other) {
|
|
if (other.minDistance < minDistance)
|
|
minDistance = other.minDistance;
|
|
}
|
|
|
|
TrueDistanceSelector::DistanceType TrueDistanceSelector::distance() const {
|
|
return minDistance.distance;
|
|
}
|
|
|
|
PseudoDistanceSelectorBase::EdgeCache::EdgeCache() : absDistance(0), aDomainDistance(0), bDomainDistance(0), aPseudoDistance(0), bPseudoDistance(0) { }
|
|
|
|
bool PseudoDistanceSelectorBase::getPseudoDistance(real &distance, const Vector2 &ep, const Vector2 &edgeDir) {
|
|
real ts = dotProduct(ep, edgeDir);
|
|
if (ts > real(0)) {
|
|
real pseudoDistance = crossProduct(ep, edgeDir);
|
|
if (fabs(pseudoDistance) < fabs(distance)) {
|
|
distance = pseudoDistance;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
PseudoDistanceSelectorBase::PseudoDistanceSelectorBase() : minNegativePseudoDistance(-fabs(minTrueDistance.distance)), minPositivePseudoDistance(fabs(minTrueDistance.distance)), nearEdge(NULL), nearEdgeParam(0) { }
|
|
|
|
void PseudoDistanceSelectorBase::reset(real delta) {
|
|
minTrueDistance.distance += real(nonZeroSign(minTrueDistance.distance))*delta;
|
|
minNegativePseudoDistance = -fabs(minTrueDistance.distance);
|
|
minPositivePseudoDistance = fabs(minTrueDistance.distance);
|
|
nearEdge = NULL;
|
|
nearEdgeParam = 0;
|
|
}
|
|
|
|
bool PseudoDistanceSelectorBase::isEdgeRelevant(const EdgeCache &cache, const EdgeSegment *edge, const Point2 &p) const {
|
|
real delta = DISTANCE_DELTA_FACTOR*(p-cache.point).length();
|
|
return (
|
|
cache.absDistance-delta <= fabs(minTrueDistance.distance) ||
|
|
fabs(cache.aDomainDistance) < delta ||
|
|
fabs(cache.bDomainDistance) < delta ||
|
|
(cache.aDomainDistance > real(0) && (cache.aPseudoDistance < real(0) ?
|
|
cache.aPseudoDistance+delta >= minNegativePseudoDistance :
|
|
cache.aPseudoDistance-delta <= minPositivePseudoDistance
|
|
)) ||
|
|
(cache.bDomainDistance > real(0) && (cache.bPseudoDistance < real(0) ?
|
|
cache.bPseudoDistance+delta >= minNegativePseudoDistance :
|
|
cache.bPseudoDistance-delta <= minPositivePseudoDistance
|
|
))
|
|
);
|
|
}
|
|
|
|
void PseudoDistanceSelectorBase::addEdgeTrueDistance(const EdgeSegment *edge, const SignedDistance &distance, real param) {
|
|
if (distance < minTrueDistance) {
|
|
minTrueDistance = distance;
|
|
nearEdge = edge;
|
|
nearEdgeParam = param;
|
|
}
|
|
}
|
|
|
|
void PseudoDistanceSelectorBase::addEdgePseudoDistance(real distance) {
|
|
if (distance <= real(0) && distance > minNegativePseudoDistance)
|
|
minNegativePseudoDistance = distance;
|
|
if (distance >= real(0) && distance < minPositivePseudoDistance)
|
|
minPositivePseudoDistance = distance;
|
|
}
|
|
|
|
void PseudoDistanceSelectorBase::merge(const PseudoDistanceSelectorBase &other) {
|
|
if (other.minTrueDistance < minTrueDistance) {
|
|
minTrueDistance = other.minTrueDistance;
|
|
nearEdge = other.nearEdge;
|
|
nearEdgeParam = other.nearEdgeParam;
|
|
}
|
|
if (other.minNegativePseudoDistance > minNegativePseudoDistance)
|
|
minNegativePseudoDistance = other.minNegativePseudoDistance;
|
|
if (other.minPositivePseudoDistance < minPositivePseudoDistance)
|
|
minPositivePseudoDistance = other.minPositivePseudoDistance;
|
|
}
|
|
|
|
real PseudoDistanceSelectorBase::computeDistance(const Point2 &p) const {
|
|
real minDistance = minTrueDistance.distance < real(0) ? minNegativePseudoDistance : minPositivePseudoDistance;
|
|
if (nearEdge) {
|
|
SignedDistance distance = minTrueDistance;
|
|
nearEdge->distanceToPseudoDistance(distance, p, nearEdgeParam);
|
|
if (fabs(distance.distance) < fabs(minDistance))
|
|
minDistance = distance.distance;
|
|
}
|
|
return minDistance;
|
|
}
|
|
|
|
SignedDistance PseudoDistanceSelectorBase::trueDistance() const {
|
|
return minTrueDistance;
|
|
}
|
|
|
|
void PseudoDistanceSelector::reset(const Point2 &p) {
|
|
real delta = DISTANCE_DELTA_FACTOR*(p-this->p).length();
|
|
PseudoDistanceSelectorBase::reset(delta);
|
|
this->p = p;
|
|
}
|
|
|
|
void PseudoDistanceSelector::addEdge(EdgeCache &cache, const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge) {
|
|
if (isEdgeRelevant(cache, edge, p)) {
|
|
real param;
|
|
SignedDistance distance = edge->signedDistance(p, param);
|
|
addEdgeTrueDistance(edge, distance, param);
|
|
cache.point = p;
|
|
cache.absDistance = fabs(distance.distance);
|
|
|
|
Vector2 ap = p-edge->point(0);
|
|
Vector2 bp = p-edge->point(1);
|
|
Vector2 aDir = edge->direction(0).normalize(true);
|
|
Vector2 bDir = edge->direction(1).normalize(true);
|
|
Vector2 prevDir = prevEdge->direction(1).normalize(true);
|
|
Vector2 nextDir = nextEdge->direction(0).normalize(true);
|
|
real add = dotProduct(ap, (prevDir+aDir).normalize(true));
|
|
real bdd = -dotProduct(bp, (bDir+nextDir).normalize(true));
|
|
if (add > real(0)) {
|
|
real pd = distance.distance;
|
|
if (getPseudoDistance(pd, ap, -aDir))
|
|
addEdgePseudoDistance(pd = -pd);
|
|
cache.aPseudoDistance = pd;
|
|
}
|
|
if (bdd > real(0)) {
|
|
real pd = distance.distance;
|
|
if (getPseudoDistance(pd, bp, bDir))
|
|
addEdgePseudoDistance(pd);
|
|
cache.bPseudoDistance = pd;
|
|
}
|
|
cache.aDomainDistance = add;
|
|
cache.bDomainDistance = bdd;
|
|
}
|
|
}
|
|
|
|
PseudoDistanceSelector::DistanceType PseudoDistanceSelector::distance() const {
|
|
return computeDistance(p);
|
|
}
|
|
|
|
void MultiDistanceSelector::reset(const Point2 &p) {
|
|
real delta = DISTANCE_DELTA_FACTOR*(p-this->p).length();
|
|
r.reset(delta);
|
|
g.reset(delta);
|
|
b.reset(delta);
|
|
this->p = p;
|
|
}
|
|
|
|
void MultiDistanceSelector::addEdge(EdgeCache &cache, const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge) {
|
|
if (
|
|
(edge->color&RED && r.isEdgeRelevant(cache, edge, p)) ||
|
|
(edge->color&GREEN && g.isEdgeRelevant(cache, edge, p)) ||
|
|
(edge->color&BLUE && b.isEdgeRelevant(cache, edge, p))
|
|
) {
|
|
real param;
|
|
SignedDistance distance = edge->signedDistance(p, param);
|
|
if (edge->color&RED)
|
|
r.addEdgeTrueDistance(edge, distance, param);
|
|
if (edge->color&GREEN)
|
|
g.addEdgeTrueDistance(edge, distance, param);
|
|
if (edge->color&BLUE)
|
|
b.addEdgeTrueDistance(edge, distance, param);
|
|
cache.point = p;
|
|
cache.absDistance = fabs(distance.distance);
|
|
|
|
Vector2 ap = p-edge->point(0);
|
|
Vector2 bp = p-edge->point(1);
|
|
Vector2 aDir = edge->direction(0).normalize(true);
|
|
Vector2 bDir = edge->direction(1).normalize(true);
|
|
Vector2 prevDir = prevEdge->direction(1).normalize(true);
|
|
Vector2 nextDir = nextEdge->direction(0).normalize(true);
|
|
real add = dotProduct(ap, (prevDir+aDir).normalize(true));
|
|
real bdd = -dotProduct(bp, (bDir+nextDir).normalize(true));
|
|
if (add > real(0)) {
|
|
real pd = distance.distance;
|
|
if (PseudoDistanceSelectorBase::getPseudoDistance(pd, ap, -aDir)) {
|
|
pd = -pd;
|
|
if (edge->color&RED)
|
|
r.addEdgePseudoDistance(pd);
|
|
if (edge->color&GREEN)
|
|
g.addEdgePseudoDistance(pd);
|
|
if (edge->color&BLUE)
|
|
b.addEdgePseudoDistance(pd);
|
|
}
|
|
cache.aPseudoDistance = pd;
|
|
}
|
|
if (bdd > real(0)) {
|
|
real pd = distance.distance;
|
|
if (PseudoDistanceSelectorBase::getPseudoDistance(pd, bp, bDir)) {
|
|
if (edge->color&RED)
|
|
r.addEdgePseudoDistance(pd);
|
|
if (edge->color&GREEN)
|
|
g.addEdgePseudoDistance(pd);
|
|
if (edge->color&BLUE)
|
|
b.addEdgePseudoDistance(pd);
|
|
}
|
|
cache.bPseudoDistance = pd;
|
|
}
|
|
cache.aDomainDistance = add;
|
|
cache.bDomainDistance = bdd;
|
|
}
|
|
}
|
|
|
|
void MultiDistanceSelector::merge(const MultiDistanceSelector &other) {
|
|
r.merge(other.r);
|
|
g.merge(other.g);
|
|
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;
|
|
}
|
|
|
|
SignedDistance MultiDistanceSelector::trueDistance() const {
|
|
SignedDistance distance = r.trueDistance();
|
|
if (g.trueDistance() < distance)
|
|
distance = g.trueDistance();
|
|
if (b.trueDistance() < distance)
|
|
distance = b.trueDistance();
|
|
return distance;
|
|
}
|
|
|
|
MultiAndTrueDistanceSelector::DistanceType MultiAndTrueDistanceSelector::distance() const {
|
|
MultiDistance multiDistance = MultiDistanceSelector::distance();
|
|
MultiAndTrueDistance mtd;
|
|
mtd.r = multiDistance.r;
|
|
mtd.g = multiDistance.g;
|
|
mtd.b = multiDistance.b;
|
|
mtd.a = trueDistance().distance;
|
|
return mtd;
|
|
}
|
|
|
|
}
|