Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/graphite/src/Intervals.cpp
9903 views
1
// SPDX-License-Identifier: MIT OR MPL-2.0 OR LGPL-2.1-or-later OR GPL-2.0-or-later
2
// Copyright 2010, SIL International, All rights reserved.
3
4
#include <algorithm>
5
#include <cmath>
6
#include <limits>
7
8
#include "inc/Intervals.h"
9
#include "inc/Segment.h"
10
#include "inc/Slot.h"
11
#include "inc/debug.h"
12
#include "inc/bits.h"
13
14
using namespace graphite2;
15
16
#include <cmath>
17
18
inline
19
Zones::Exclusion Zones::Exclusion::split_at(float p) {
20
Exclusion r(*this);
21
r.xm = x = p;
22
return r;
23
}
24
25
inline
26
void Zones::Exclusion::left_trim(float p) {
27
x = p;
28
}
29
30
inline
31
Zones::Exclusion & Zones::Exclusion::operator += (Exclusion const & rhs) {
32
c += rhs.c; sm += rhs.sm; smx += rhs.smx; open = false;
33
return *this;
34
}
35
36
inline
37
uint8 Zones::Exclusion::outcode(float val) const {
38
float p = val;
39
//float d = std::numeric_limits<float>::epsilon();
40
float d = 0.;
41
return ((p - xm >= d) << 1) | (x - p > d);
42
}
43
44
void Zones::exclude_with_margins(float xmin, float xmax, int axis) {
45
remove(xmin, xmax);
46
weightedAxis(axis, xmin-_margin_len, xmin, 0, 0, _margin_weight, xmin-_margin_len, 0, 0, false);
47
weightedAxis(axis, xmax, xmax+_margin_len, 0, 0, _margin_weight, xmax+_margin_len, 0, 0, false);
48
}
49
50
namespace
51
{
52
53
inline
54
bool separated(float a, float b) {
55
return a != b;
56
//int exp;
57
//float res = frexpf(fabs(a - b), &exp);
58
//return (*(unsigned int *)(&res) > 4);
59
//return std::fabs(a-b) > std::numeric_limits<float>::epsilon(); // std::epsilon may not work. but 0.5 fails exising 64 bit tests
60
//return std::fabs(a-b) > 0.5f;
61
}
62
63
}
64
65
void Zones::insert(Exclusion e)
66
{
67
#if !defined GRAPHITE2_NTRACING
68
addDebug(&e);
69
#endif
70
e.x = max(e.x, _pos);
71
e.xm = min(e.xm, _posm);
72
if (e.x >= e.xm) return;
73
74
for (iterator i = _exclusions.begin(), ie = _exclusions.end(); i != ie && e.x < e.xm; ++i)
75
{
76
const uint8 oca = e.outcode(i->x),
77
ocb = e.outcode(i->xm);
78
if ((oca & ocb) != 0) continue;
79
80
switch (oca ^ ocb) // What kind of overlap?
81
{
82
case 0: // e completely covers i
83
// split e at i.x into e1,e2
84
// split e2 at i.mx into e2,e3
85
// drop e1 ,i+e2, e=e3
86
*i += e;
87
e.left_trim(i->xm);
88
break;
89
case 1: // e overlaps on the rhs of i
90
// split i at e->x into i1,i2
91
// split e at i.mx into e1,e2
92
// trim i1, insert i2+e1, e=e2
93
if (!separated(i->xm, e.x)) break;
94
if (separated(i->x,e.x)) { i = _exclusions.insert(i,i->split_at(e.x)); ++i; }
95
*i += e;
96
e.left_trim(i->xm);
97
break;
98
case 2: // e overlaps on the lhs of i
99
// split e at i->x into e1,e2
100
// split i at e.mx into i1,i2
101
// drop e1, insert e2+i1, trim i2
102
if (!separated(e.xm, i->x)) return;
103
if (separated(e.xm, i->xm)) i = _exclusions.insert(i,i->split_at(e.xm));
104
*i += e;
105
return;
106
case 3: // i completely covers e
107
// split i at e.x into i1,i2
108
// split i2 at e.mx into i2,i3
109
// insert i1, insert e+i2
110
if (separated(e.xm, i->xm)) i = _exclusions.insert(i,i->split_at(e.xm));
111
i = _exclusions.insert(i, i->split_at(e.x));
112
*++i += e;
113
return;
114
}
115
116
ie = _exclusions.end();
117
}
118
}
119
120
121
void Zones::remove(float x, float xm)
122
{
123
#if !defined GRAPHITE2_NTRACING
124
removeDebug(x, xm);
125
#endif
126
x = max(x, _pos);
127
xm = min(xm, _posm);
128
if (x >= xm) return;
129
130
for (iterator i = _exclusions.begin(), ie = _exclusions.end(); i != ie; ++i)
131
{
132
const uint8 oca = i->outcode(x),
133
ocb = i->outcode(xm);
134
if ((oca & ocb) != 0) continue;
135
136
switch (oca ^ ocb) // What kind of overlap?
137
{
138
case 0: // i completely covers e
139
if (separated(i->x, x)) { i = _exclusions.insert(i,i->split_at(x)); ++i; }
140
GR_FALLTHROUGH;
141
// no break
142
case 1: // i overlaps on the rhs of e
143
i->left_trim(xm);
144
return;
145
case 2: // i overlaps on the lhs of e
146
i->xm = x;
147
if (separated(i->x, i->xm)) break;
148
GR_FALLTHROUGH;
149
// no break
150
case 3: // e completely covers i
151
i = _exclusions.erase(i);
152
--i;
153
break;
154
}
155
156
ie = _exclusions.end();
157
}
158
}
159
160
161
Zones::const_iterator Zones::find_exclusion_under(float x) const
162
{
163
size_t l = 0, h = _exclusions.size();
164
165
while (l < h)
166
{
167
size_t const p = (l+h) >> 1;
168
switch (_exclusions[p].outcode(x))
169
{
170
case 0 : return _exclusions.begin()+p;
171
case 1 : h = p; break;
172
case 2 :
173
case 3 : l = p+1; break;
174
}
175
}
176
177
return _exclusions.begin()+l;
178
}
179
180
181
float Zones::closest(float origin, float & cost) const
182
{
183
float best_c = std::numeric_limits<float>::max(),
184
best_x = 0;
185
186
const const_iterator start = find_exclusion_under(origin);
187
188
// Forward scan looking for lowest cost
189
for (const_iterator i = start, ie = _exclusions.end(); i != ie; ++i)
190
if (i->track_cost(best_c, best_x, origin)) break;
191
192
// Backward scan looking for lowest cost
193
// We start from the exclusion to the immediate left of start since we've
194
// already tested start with the right most scan above.
195
for (const_iterator i = start-1, ie = _exclusions.begin()-1; i != ie; --i)
196
if (i->track_cost(best_c, best_x, origin)) break;
197
198
cost = (best_c == std::numeric_limits<float>::max() ? -1 : best_c);
199
return best_x;
200
}
201
202
203
// Cost and test position functions
204
205
bool Zones::Exclusion::track_cost(float & best_cost, float & best_pos, float origin) const {
206
const float p = test_position(origin),
207
localc = cost(p - origin);
208
if (open && localc > best_cost) return true;
209
210
if (localc < best_cost)
211
{
212
best_cost = localc;
213
best_pos = p;
214
}
215
return false;
216
}
217
218
inline
219
float Zones::Exclusion::cost(float p) const {
220
return (sm * p - 2 * smx) * p + c;
221
}
222
223
224
float Zones::Exclusion::test_position(float origin) const {
225
if (sm < 0)
226
{
227
// sigh, test both ends and perhaps the middle too!
228
float res = x;
229
float cl = cost(x);
230
if (x < origin && xm > origin)
231
{
232
float co = cost(origin);
233
if (co < cl)
234
{
235
cl = co;
236
res = origin;
237
}
238
}
239
float cr = cost(xm);
240
return cl > cr ? xm : res;
241
}
242
else
243
{
244
float zerox = smx / sm + origin;
245
if (zerox < x) return x;
246
else if (zerox > xm) return xm;
247
else return zerox;
248
}
249
}
250
251
252
#if !defined GRAPHITE2_NTRACING
253
254
void Zones::jsonDbgOut(Segment *seg) const {
255
256
if (_dbg)
257
{
258
for (Zones::idebugs s = dbgs_begin(), e = dbgs_end(); s != e; ++s)
259
{
260
*_dbg << json::flat << json::array
261
<< objectid(dslot(seg, (Slot *)(s->_env[0])))
262
<< reinterpret_cast<ptrdiff_t>(s->_env[1]);
263
if (s->_isdel)
264
*_dbg << "remove" << Position(s->_excl.x, s->_excl.xm);
265
else
266
*_dbg << "exclude" << json::flat << json::array
267
<< s->_excl.x << s->_excl.xm
268
<< s->_excl.sm << s->_excl.smx << s->_excl.c
269
<< json::close;
270
*_dbg << json::close;
271
}
272
}
273
}
274
275
#endif
276
277