1 | |
2 | #include "edge-selectors.h" |
3 | |
4 | #include "arithmetics.hpp" |
5 | |
6 | namespace msdfgen { |
7 | |
8 | #define DISTANCE_DELTA_FACTOR 1.001 |
9 | |
10 | TrueDistanceSelector::EdgeCache::EdgeCache() : absDistance(0) { } |
11 | |
12 | void 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 | |
18 | void 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 | |
30 | void TrueDistanceSelector::merge(const TrueDistanceSelector &other) { |
31 | if (other.minDistance < minDistance) |
32 | minDistance = other.minDistance; |
33 | } |
34 | |
35 | TrueDistanceSelector::DistanceType TrueDistanceSelector::distance() const { |
36 | return minDistance.distance; |
37 | } |
38 | |
39 | PseudoDistanceSelectorBase::EdgeCache::EdgeCache() : absDistance(0), aDomainDistance(0), bDomainDistance(0), aPseudoDistance(0), bPseudoDistance(0) { } |
40 | |
41 | bool 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 | |
53 | PseudoDistanceSelectorBase::PseudoDistanceSelectorBase() : minNegativePseudoDistance(-fabs(minTrueDistance.distance)), minPositivePseudoDistance(fabs(minTrueDistance.distance)), nearEdge(NULL), nearEdgeParam(0) { } |
54 | |
55 | void 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 | |
63 | bool 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 | |
80 | void 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 | |
88 | void PseudoDistanceSelectorBase::addEdgePseudoDistance(double distance) { |
89 | if (distance <= 0 && distance > minNegativePseudoDistance) |
90 | minNegativePseudoDistance = distance; |
91 | if (distance >= 0 && distance < minPositivePseudoDistance) |
92 | minPositivePseudoDistance = distance; |
93 | } |
94 | |
95 | void 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 | |
107 | double 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 | |
118 | SignedDistance PseudoDistanceSelectorBase::trueDistance() const { |
119 | return minTrueDistance; |
120 | } |
121 | |
122 | void 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 | |
128 | void 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 | |
161 | PseudoDistanceSelector::DistanceType PseudoDistanceSelector::distance() const { |
162 | return computeDistance(p); |
163 | } |
164 | |
165 | void 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 | |
173 | void 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 | |
228 | void MultiDistanceSelector::merge(const MultiDistanceSelector &other) { |
229 | r.merge(other.r); |
230 | g.merge(other.g); |
231 | b.merge(other.b); |
232 | } |
233 | |
234 | MultiDistanceSelector::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 | |
242 | SignedDistance 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 | |
251 | MultiAndTrueDistanceSelector::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 | |