1
2#include "edge-selectors.h"
3
4#include "arithmetics.hpp"
5
6namespace msdfgen {
7
8#define DISTANCE_DELTA_FACTOR 1.001
9
10TrueDistanceSelector::EdgeCache::EdgeCache() : absDistance(0) { }
11
12void TrueDistanceSelector::reset(const Point2 &p) {
13 double delta = DISTANCE_DELTA_FACTOR*(p-this->p).length();
14 minDistance.distance += nonZeroSign(minDistance.distance)*delta;
15 this->p = p;
16}
17
18void TrueDistanceSelector::addEdge(EdgeCache &cache, const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge) {
19 double delta = DISTANCE_DELTA_FACTOR*(p-cache.point).length();
20 if (cache.absDistance-delta <= fabs(minDistance.distance)) {
21 double dummy;
22 SignedDistance distance = edge->signedDistance(p, dummy);
23 if (distance < minDistance)
24 minDistance = distance;
25 cache.point = p;
26 cache.absDistance = fabs(distance.distance);
27 }
28}
29
30void TrueDistanceSelector::merge(const TrueDistanceSelector &other) {
31 if (other.minDistance < minDistance)
32 minDistance = other.minDistance;
33}
34
35TrueDistanceSelector::DistanceType TrueDistanceSelector::distance() const {
36 return minDistance.distance;
37}
38
39PseudoDistanceSelectorBase::EdgeCache::EdgeCache() : absDistance(0), aDomainDistance(0), bDomainDistance(0), aPseudoDistance(0), bPseudoDistance(0) { }
40
41bool PseudoDistanceSelectorBase::getPseudoDistance(double &distance, const Vector2 &ep, const Vector2 &edgeDir) {
42 double ts = dotProduct(ep, edgeDir);
43 if (ts > 0) {
44 double pseudoDistance = crossProduct(ep, edgeDir);
45 if (fabs(pseudoDistance) < fabs(distance)) {
46 distance = pseudoDistance;
47 return true;
48 }
49 }
50 return false;
51}
52
53PseudoDistanceSelectorBase::PseudoDistanceSelectorBase() : minNegativePseudoDistance(-fabs(minTrueDistance.distance)), minPositivePseudoDistance(fabs(minTrueDistance.distance)), nearEdge(NULL), nearEdgeParam(0) { }
54
55void PseudoDistanceSelectorBase::reset(double delta) {
56 minTrueDistance.distance += nonZeroSign(minTrueDistance.distance)*delta;
57 minNegativePseudoDistance = -fabs(minTrueDistance.distance);
58 minPositivePseudoDistance = fabs(minTrueDistance.distance);
59 nearEdge = NULL;
60 nearEdgeParam = 0;
61}
62
63bool PseudoDistanceSelectorBase::isEdgeRelevant(const EdgeCache &cache, const EdgeSegment *edge, const Point2 &p) const {
64 double delta = DISTANCE_DELTA_FACTOR*(p-cache.point).length();
65 return (
66 cache.absDistance-delta <= fabs(minTrueDistance.distance) ||
67 fabs(cache.aDomainDistance) < delta ||
68 fabs(cache.bDomainDistance) < delta ||
69 (cache.aDomainDistance > 0 && (cache.aPseudoDistance < 0 ?
70 cache.aPseudoDistance+delta >= minNegativePseudoDistance :
71 cache.aPseudoDistance-delta <= minPositivePseudoDistance
72 )) ||
73 (cache.bDomainDistance > 0 && (cache.bPseudoDistance < 0 ?
74 cache.bPseudoDistance+delta >= minNegativePseudoDistance :
75 cache.bPseudoDistance-delta <= minPositivePseudoDistance
76 ))
77 );
78}
79
80void PseudoDistanceSelectorBase::addEdgeTrueDistance(const EdgeSegment *edge, const SignedDistance &distance, double param) {
81 if (distance < minTrueDistance) {
82 minTrueDistance = distance;
83 nearEdge = edge;
84 nearEdgeParam = param;
85 }
86}
87
88void PseudoDistanceSelectorBase::addEdgePseudoDistance(double distance) {
89 if (distance <= 0 && distance > minNegativePseudoDistance)
90 minNegativePseudoDistance = distance;
91 if (distance >= 0 && distance < minPositivePseudoDistance)
92 minPositivePseudoDistance = distance;
93}
94
95void PseudoDistanceSelectorBase::merge(const PseudoDistanceSelectorBase &other) {
96 if (other.minTrueDistance < minTrueDistance) {
97 minTrueDistance = other.minTrueDistance;
98 nearEdge = other.nearEdge;
99 nearEdgeParam = other.nearEdgeParam;
100 }
101 if (other.minNegativePseudoDistance > minNegativePseudoDistance)
102 minNegativePseudoDistance = other.minNegativePseudoDistance;
103 if (other.minPositivePseudoDistance < minPositivePseudoDistance)
104 minPositivePseudoDistance = other.minPositivePseudoDistance;
105}
106
107double PseudoDistanceSelectorBase::computeDistance(const Point2 &p) const {
108 double minDistance = minTrueDistance.distance < 0 ? minNegativePseudoDistance : minPositivePseudoDistance;
109 if (nearEdge) {
110 SignedDistance distance = minTrueDistance;
111 nearEdge->distanceToPseudoDistance(distance, p, nearEdgeParam);
112 if (fabs(distance.distance) < fabs(minDistance))
113 minDistance = distance.distance;
114 }
115 return minDistance;
116}
117
118SignedDistance PseudoDistanceSelectorBase::trueDistance() const {
119 return minTrueDistance;
120}
121
122void PseudoDistanceSelector::reset(const Point2 &p) {
123 double delta = DISTANCE_DELTA_FACTOR*(p-this->p).length();
124 PseudoDistanceSelectorBase::reset(delta);
125 this->p = p;
126}
127
128void PseudoDistanceSelector::addEdge(EdgeCache &cache, const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge) {
129 if (isEdgeRelevant(cache, edge, p)) {
130 double param;
131 SignedDistance distance = edge->signedDistance(p, param);
132 addEdgeTrueDistance(edge, distance, param);
133 cache.point = p;
134 cache.absDistance = fabs(distance.distance);
135
136 Vector2 ap = p-edge->point(0);
137 Vector2 bp = p-edge->point(1);
138 Vector2 aDir = edge->direction(0).normalize(true);
139 Vector2 bDir = edge->direction(1).normalize(true);
140 Vector2 prevDir = prevEdge->direction(1).normalize(true);
141 Vector2 nextDir = nextEdge->direction(0).normalize(true);
142 double add = dotProduct(ap, (prevDir+aDir).normalize(true));
143 double bdd = -dotProduct(bp, (bDir+nextDir).normalize(true));
144 if (add > 0) {
145 double pd = distance.distance;
146 if (getPseudoDistance(pd, ap, -aDir))
147 addEdgePseudoDistance(pd = -pd);
148 cache.aPseudoDistance = pd;
149 }
150 if (bdd > 0) {
151 double pd = distance.distance;
152 if (getPseudoDistance(pd, bp, bDir))
153 addEdgePseudoDistance(pd);
154 cache.bPseudoDistance = pd;
155 }
156 cache.aDomainDistance = add;
157 cache.bDomainDistance = bdd;
158 }
159}
160
161PseudoDistanceSelector::DistanceType PseudoDistanceSelector::distance() const {
162 return computeDistance(p);
163}
164
165void MultiDistanceSelector::reset(const Point2 &p) {
166 double delta = DISTANCE_DELTA_FACTOR*(p-this->p).length();
167 r.reset(delta);
168 g.reset(delta);
169 b.reset(delta);
170 this->p = p;
171}
172
173void MultiDistanceSelector::addEdge(EdgeCache &cache, const EdgeSegment *prevEdge, const EdgeSegment *edge, const EdgeSegment *nextEdge) {
174 if (
175 (edge->color&RED && r.isEdgeRelevant(cache, edge, p)) ||
176 (edge->color&GREEN && g.isEdgeRelevant(cache, edge, p)) ||
177 (edge->color&BLUE && b.isEdgeRelevant(cache, edge, p))
178 ) {
179 double param;
180 SignedDistance distance = edge->signedDistance(p, param);
181 if (edge->color&RED)
182 r.addEdgeTrueDistance(edge, distance, param);
183 if (edge->color&GREEN)
184 g.addEdgeTrueDistance(edge, distance, param);
185 if (edge->color&BLUE)
186 b.addEdgeTrueDistance(edge, distance, param);
187 cache.point = p;
188 cache.absDistance = fabs(distance.distance);
189
190 Vector2 ap = p-edge->point(0);
191 Vector2 bp = p-edge->point(1);
192 Vector2 aDir = edge->direction(0).normalize(true);
193 Vector2 bDir = edge->direction(1).normalize(true);
194 Vector2 prevDir = prevEdge->direction(1).normalize(true);
195 Vector2 nextDir = nextEdge->direction(0).normalize(true);
196 double add = dotProduct(ap, (prevDir+aDir).normalize(true));
197 double bdd = -dotProduct(bp, (bDir+nextDir).normalize(true));
198 if (add > 0) {
199 double pd = distance.distance;
200 if (PseudoDistanceSelectorBase::getPseudoDistance(pd, ap, -aDir)) {
201 pd = -pd;
202 if (edge->color&RED)
203 r.addEdgePseudoDistance(pd);
204 if (edge->color&GREEN)
205 g.addEdgePseudoDistance(pd);
206 if (edge->color&BLUE)
207 b.addEdgePseudoDistance(pd);
208 }
209 cache.aPseudoDistance = pd;
210 }
211 if (bdd > 0) {
212 double pd = distance.distance;
213 if (PseudoDistanceSelectorBase::getPseudoDistance(pd, bp, bDir)) {
214 if (edge->color&RED)
215 r.addEdgePseudoDistance(pd);
216 if (edge->color&GREEN)
217 g.addEdgePseudoDistance(pd);
218 if (edge->color&BLUE)
219 b.addEdgePseudoDistance(pd);
220 }
221 cache.bPseudoDistance = pd;
222 }
223 cache.aDomainDistance = add;
224 cache.bDomainDistance = bdd;
225 }
226}
227
228void MultiDistanceSelector::merge(const MultiDistanceSelector &other) {
229 r.merge(other.r);
230 g.merge(other.g);
231 b.merge(other.b);
232}
233
234MultiDistanceSelector::DistanceType MultiDistanceSelector::distance() const {
235 MultiDistance multiDistance;
236 multiDistance.r = r.computeDistance(p);
237 multiDistance.g = g.computeDistance(p);
238 multiDistance.b = b.computeDistance(p);
239 return multiDistance;
240}
241
242SignedDistance MultiDistanceSelector::trueDistance() const {
243 SignedDistance distance = r.trueDistance();
244 if (g.trueDistance() < distance)
245 distance = g.trueDistance();
246 if (b.trueDistance() < distance)
247 distance = b.trueDistance();
248 return distance;
249}
250
251MultiAndTrueDistanceSelector::DistanceType MultiAndTrueDistanceSelector::distance() const {
252 MultiDistance multiDistance = MultiDistanceSelector::distance();
253 MultiAndTrueDistance mtd;
254 mtd.r = multiDistance.r;
255 mtd.g = multiDistance.g;
256 mtd.b = multiDistance.b;
257 mtd.a = trueDistance().distance;
258 return mtd;
259}
260
261}
262