1 /* GRAPHITE2 LICENSING
2
3 Copyright 2010, SIL International
4 All rights reserved.
5
6 This library is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published
8 by the Free Software Foundation; either version 2.1 of License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should also have received a copy of the GNU Lesser General Public
17 License along with this library in the file named "LICENSE".
18 If not, write to the Free Software Foundation, 51 Franklin Street,
19 Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
20 internet at http://www.fsf.org/licenses/lgpl.html.
21
22 Alternatively, the contents of this file may be used under the terms of the
23 Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
24 License, as published by the Free Software Foundation, either version 2
25 of the License or (at your option) any later version.
26 */
27 #include <algorithm>
28 #include <cmath>
29 #include <limits>
30
31 #include "inc/Intervals.h"
32 #include "inc/Segment.h"
33 #include "inc/Slot.h"
34 #include "inc/debug.h"
35 #include "inc/bits.h"
36
37 using namespace graphite2;
38
39 #include <cmath>
40
41 inline
split_at(float p)42 Zones::Exclusion Zones::Exclusion::split_at(float p) {
43 Exclusion r(*this);
44 r.xm = x = p;
45 return r;
46 }
47
48 inline
left_trim(float p)49 void Zones::Exclusion::left_trim(float p) {
50 x = p;
51 }
52
53 inline
operator +=(Exclusion const & rhs)54 Zones::Exclusion & Zones::Exclusion::operator += (Exclusion const & rhs) {
55 c += rhs.c; sm += rhs.sm; smx += rhs.smx; open = false;
56 return *this;
57 }
58
59 inline
outcode(float val) const60 uint8 Zones::Exclusion::outcode(float val) const {
61 float p = val;
62 //float d = std::numeric_limits<float>::epsilon();
63 float d = 0.;
64 return ((p - xm >= d) << 1) | (x - p > d);
65 }
66
exclude_with_margins(float xmin,float xmax,int axis)67 void Zones::exclude_with_margins(float xmin, float xmax, int axis) {
68 remove(xmin, xmax);
69 weightedAxis(axis, xmin-_margin_len, xmin, 0, 0, _margin_weight, xmin-_margin_len, 0, 0, false);
70 weightedAxis(axis, xmax, xmax+_margin_len, 0, 0, _margin_weight, xmax+_margin_len, 0, 0, false);
71 }
72
73 namespace
74 {
75
76 inline
separated(float a,float b)77 bool separated(float a, float b) {
78 return a != b;
79 //int exp;
80 //float res = frexpf(fabs(a - b), &exp);
81 //return (*(unsigned int *)(&res) > 4);
82 //return std::fabs(a-b) > std::numeric_limits<float>::epsilon(); // std::epsilon may not work. but 0.5 fails exising 64 bit tests
83 //return std::fabs(a-b) > 0.5f;
84 }
85
86 }
87
insert(Exclusion e)88 void Zones::insert(Exclusion e)
89 {
90 #if !defined GRAPHITE2_NTRACING
91 addDebug(&e);
92 #endif
93 e.x = max(e.x, _pos);
94 e.xm = min(e.xm, _posm);
95 if (e.x >= e.xm) return;
96
97 for (iterator i = _exclusions.begin(), ie = _exclusions.end(); i != ie && e.x < e.xm; ++i)
98 {
99 const uint8 oca = e.outcode(i->x),
100 ocb = e.outcode(i->xm);
101 if ((oca & ocb) != 0) continue;
102
103 switch (oca ^ ocb) // What kind of overlap?
104 {
105 case 0: // e completely covers i
106 // split e at i.x into e1,e2
107 // split e2 at i.mx into e2,e3
108 // drop e1 ,i+e2, e=e3
109 *i += e;
110 e.left_trim(i->xm);
111 break;
112 case 1: // e overlaps on the rhs of i
113 // split i at e->x into i1,i2
114 // split e at i.mx into e1,e2
115 // trim i1, insert i2+e1, e=e2
116 if (!separated(i->xm, e.x)) break;
117 if (separated(i->x,e.x)) { i = _exclusions.insert(i,i->split_at(e.x)); ++i; }
118 *i += e;
119 e.left_trim(i->xm);
120 break;
121 case 2: // e overlaps on the lhs of i
122 // split e at i->x into e1,e2
123 // split i at e.mx into i1,i2
124 // drop e1, insert e2+i1, trim i2
125 if (!separated(e.xm, i->x)) return;
126 if (separated(e.xm, i->xm)) i = _exclusions.insert(i,i->split_at(e.xm));
127 *i += e;
128 return;
129 case 3: // i completely covers e
130 // split i at e.x into i1,i2
131 // split i2 at e.mx into i2,i3
132 // insert i1, insert e+i2
133 if (separated(e.xm, i->xm)) i = _exclusions.insert(i,i->split_at(e.xm));
134 i = _exclusions.insert(i, i->split_at(e.x));
135 *++i += e;
136 return;
137 }
138
139 ie = _exclusions.end();
140 }
141 }
142
143
remove(float x,float xm)144 void Zones::remove(float x, float xm)
145 {
146 #if !defined GRAPHITE2_NTRACING
147 removeDebug(x, xm);
148 #endif
149 x = max(x, _pos);
150 xm = min(xm, _posm);
151 if (x >= xm) return;
152
153 for (iterator i = _exclusions.begin(), ie = _exclusions.end(); i != ie; ++i)
154 {
155 const uint8 oca = i->outcode(x),
156 ocb = i->outcode(xm);
157 if ((oca & ocb) != 0) continue;
158
159 switch (oca ^ ocb) // What kind of overlap?
160 {
161 case 0: // i completely covers e
162 if (separated(i->x, x)) { i = _exclusions.insert(i,i->split_at(x)); ++i; }
163 GR_FALLTHROUGH;
164 // no break
165 case 1: // i overlaps on the rhs of e
166 i->left_trim(xm);
167 return;
168 case 2: // i overlaps on the lhs of e
169 i->xm = x;
170 if (separated(i->x, i->xm)) break;
171 GR_FALLTHROUGH;
172 // no break
173 case 3: // e completely covers i
174 i = _exclusions.erase(i);
175 --i;
176 break;
177 }
178
179 ie = _exclusions.end();
180 }
181 }
182
183
find_exclusion_under(float x) const184 Zones::const_iterator Zones::find_exclusion_under(float x) const
185 {
186 size_t l = 0, h = _exclusions.size();
187
188 while (l < h)
189 {
190 size_t const p = (l+h) >> 1;
191 switch (_exclusions[p].outcode(x))
192 {
193 case 0 : return _exclusions.begin()+p;
194 case 1 : h = p; break;
195 case 2 :
196 case 3 : l = p+1; break;
197 }
198 }
199
200 return _exclusions.begin()+l;
201 }
202
203
closest(float origin,float & cost) const204 float Zones::closest(float origin, float & cost) const
205 {
206 float best_c = std::numeric_limits<float>::max(),
207 best_x = 0;
208
209 const const_iterator start = find_exclusion_under(origin);
210
211 // Forward scan looking for lowest cost
212 for (const_iterator i = start, ie = _exclusions.end(); i != ie; ++i)
213 if (i->track_cost(best_c, best_x, origin)) break;
214
215 // Backward scan looking for lowest cost
216 // We start from the exclusion to the immediate left of start since we've
217 // already tested start with the right most scan above.
218 for (const_iterator i = start-1, ie = _exclusions.begin()-1; i != ie; --i)
219 if (i->track_cost(best_c, best_x, origin)) break;
220
221 cost = (best_c == std::numeric_limits<float>::max() ? -1 : best_c);
222 return best_x;
223 }
224
225
226 // Cost and test position functions
227
track_cost(float & best_cost,float & best_pos,float origin) const228 bool Zones::Exclusion::track_cost(float & best_cost, float & best_pos, float origin) const {
229 const float p = test_position(origin),
230 localc = cost(p - origin);
231 if (open && localc > best_cost) return true;
232
233 if (localc < best_cost)
234 {
235 best_cost = localc;
236 best_pos = p;
237 }
238 return false;
239 }
240
241 inline
cost(float p) const242 float Zones::Exclusion::cost(float p) const {
243 return (sm * p - 2 * smx) * p + c;
244 }
245
246
test_position(float origin) const247 float Zones::Exclusion::test_position(float origin) const {
248 if (sm < 0)
249 {
250 // sigh, test both ends and perhaps the middle too!
251 float res = x;
252 float cl = cost(x);
253 if (x < origin && xm > origin)
254 {
255 float co = cost(origin);
256 if (co < cl)
257 {
258 cl = co;
259 res = origin;
260 }
261 }
262 float cr = cost(xm);
263 return cl > cr ? xm : res;
264 }
265 else
266 {
267 float zerox = smx / sm + origin;
268 if (zerox < x) return x;
269 else if (zerox > xm) return xm;
270 else return zerox;
271 }
272 }
273
274
275 #if !defined GRAPHITE2_NTRACING
276
jsonDbgOut(Segment * seg) const277 void Zones::jsonDbgOut(Segment *seg) const {
278
279 if (_dbg)
280 {
281 for (Zones::idebugs s = dbgs_begin(), e = dbgs_end(); s != e; ++s)
282 {
283 *_dbg << json::flat << json::array
284 << objectid(dslot(seg, (Slot *)(s->_env[0])))
285 << reinterpret_cast<ptrdiff_t>(s->_env[1]);
286 if (s->_isdel)
287 *_dbg << "remove" << Position(s->_excl.x, s->_excl.xm);
288 else
289 *_dbg << "exclude" << json::flat << json::array
290 << s->_excl.x << s->_excl.xm
291 << s->_excl.sm << s->_excl.smx << s->_excl.c
292 << json::close;
293 *_dbg << json::close;
294 }
295 }
296 }
297
298 #endif
299