Path: blob/master/thirdparty/graphite/src/Intervals.cpp
9903 views
// SPDX-License-Identifier: MIT OR MPL-2.0 OR LGPL-2.1-or-later OR GPL-2.0-or-later1// Copyright 2010, SIL International, All rights reserved.23#include <algorithm>4#include <cmath>5#include <limits>67#include "inc/Intervals.h"8#include "inc/Segment.h"9#include "inc/Slot.h"10#include "inc/debug.h"11#include "inc/bits.h"1213using namespace graphite2;1415#include <cmath>1617inline18Zones::Exclusion Zones::Exclusion::split_at(float p) {19Exclusion r(*this);20r.xm = x = p;21return r;22}2324inline25void Zones::Exclusion::left_trim(float p) {26x = p;27}2829inline30Zones::Exclusion & Zones::Exclusion::operator += (Exclusion const & rhs) {31c += rhs.c; sm += rhs.sm; smx += rhs.smx; open = false;32return *this;33}3435inline36uint8 Zones::Exclusion::outcode(float val) const {37float p = val;38//float d = std::numeric_limits<float>::epsilon();39float d = 0.;40return ((p - xm >= d) << 1) | (x - p > d);41}4243void Zones::exclude_with_margins(float xmin, float xmax, int axis) {44remove(xmin, xmax);45weightedAxis(axis, xmin-_margin_len, xmin, 0, 0, _margin_weight, xmin-_margin_len, 0, 0, false);46weightedAxis(axis, xmax, xmax+_margin_len, 0, 0, _margin_weight, xmax+_margin_len, 0, 0, false);47}4849namespace50{5152inline53bool separated(float a, float b) {54return a != b;55//int exp;56//float res = frexpf(fabs(a - b), &exp);57//return (*(unsigned int *)(&res) > 4);58//return std::fabs(a-b) > std::numeric_limits<float>::epsilon(); // std::epsilon may not work. but 0.5 fails exising 64 bit tests59//return std::fabs(a-b) > 0.5f;60}6162}6364void Zones::insert(Exclusion e)65{66#if !defined GRAPHITE2_NTRACING67addDebug(&e);68#endif69e.x = max(e.x, _pos);70e.xm = min(e.xm, _posm);71if (e.x >= e.xm) return;7273for (iterator i = _exclusions.begin(), ie = _exclusions.end(); i != ie && e.x < e.xm; ++i)74{75const uint8 oca = e.outcode(i->x),76ocb = e.outcode(i->xm);77if ((oca & ocb) != 0) continue;7879switch (oca ^ ocb) // What kind of overlap?80{81case 0: // e completely covers i82// split e at i.x into e1,e283// split e2 at i.mx into e2,e384// drop e1 ,i+e2, e=e385*i += e;86e.left_trim(i->xm);87break;88case 1: // e overlaps on the rhs of i89// split i at e->x into i1,i290// split e at i.mx into e1,e291// trim i1, insert i2+e1, e=e292if (!separated(i->xm, e.x)) break;93if (separated(i->x,e.x)) { i = _exclusions.insert(i,i->split_at(e.x)); ++i; }94*i += e;95e.left_trim(i->xm);96break;97case 2: // e overlaps on the lhs of i98// split e at i->x into e1,e299// split i at e.mx into i1,i2100// drop e1, insert e2+i1, trim i2101if (!separated(e.xm, i->x)) return;102if (separated(e.xm, i->xm)) i = _exclusions.insert(i,i->split_at(e.xm));103*i += e;104return;105case 3: // i completely covers e106// split i at e.x into i1,i2107// split i2 at e.mx into i2,i3108// insert i1, insert e+i2109if (separated(e.xm, i->xm)) i = _exclusions.insert(i,i->split_at(e.xm));110i = _exclusions.insert(i, i->split_at(e.x));111*++i += e;112return;113}114115ie = _exclusions.end();116}117}118119120void Zones::remove(float x, float xm)121{122#if !defined GRAPHITE2_NTRACING123removeDebug(x, xm);124#endif125x = max(x, _pos);126xm = min(xm, _posm);127if (x >= xm) return;128129for (iterator i = _exclusions.begin(), ie = _exclusions.end(); i != ie; ++i)130{131const uint8 oca = i->outcode(x),132ocb = i->outcode(xm);133if ((oca & ocb) != 0) continue;134135switch (oca ^ ocb) // What kind of overlap?136{137case 0: // i completely covers e138if (separated(i->x, x)) { i = _exclusions.insert(i,i->split_at(x)); ++i; }139GR_FALLTHROUGH;140// no break141case 1: // i overlaps on the rhs of e142i->left_trim(xm);143return;144case 2: // i overlaps on the lhs of e145i->xm = x;146if (separated(i->x, i->xm)) break;147GR_FALLTHROUGH;148// no break149case 3: // e completely covers i150i = _exclusions.erase(i);151--i;152break;153}154155ie = _exclusions.end();156}157}158159160Zones::const_iterator Zones::find_exclusion_under(float x) const161{162size_t l = 0, h = _exclusions.size();163164while (l < h)165{166size_t const p = (l+h) >> 1;167switch (_exclusions[p].outcode(x))168{169case 0 : return _exclusions.begin()+p;170case 1 : h = p; break;171case 2 :172case 3 : l = p+1; break;173}174}175176return _exclusions.begin()+l;177}178179180float Zones::closest(float origin, float & cost) const181{182float best_c = std::numeric_limits<float>::max(),183best_x = 0;184185const const_iterator start = find_exclusion_under(origin);186187// Forward scan looking for lowest cost188for (const_iterator i = start, ie = _exclusions.end(); i != ie; ++i)189if (i->track_cost(best_c, best_x, origin)) break;190191// Backward scan looking for lowest cost192// We start from the exclusion to the immediate left of start since we've193// already tested start with the right most scan above.194for (const_iterator i = start-1, ie = _exclusions.begin()-1; i != ie; --i)195if (i->track_cost(best_c, best_x, origin)) break;196197cost = (best_c == std::numeric_limits<float>::max() ? -1 : best_c);198return best_x;199}200201202// Cost and test position functions203204bool Zones::Exclusion::track_cost(float & best_cost, float & best_pos, float origin) const {205const float p = test_position(origin),206localc = cost(p - origin);207if (open && localc > best_cost) return true;208209if (localc < best_cost)210{211best_cost = localc;212best_pos = p;213}214return false;215}216217inline218float Zones::Exclusion::cost(float p) const {219return (sm * p - 2 * smx) * p + c;220}221222223float Zones::Exclusion::test_position(float origin) const {224if (sm < 0)225{226// sigh, test both ends and perhaps the middle too!227float res = x;228float cl = cost(x);229if (x < origin && xm > origin)230{231float co = cost(origin);232if (co < cl)233{234cl = co;235res = origin;236}237}238float cr = cost(xm);239return cl > cr ? xm : res;240}241else242{243float zerox = smx / sm + origin;244if (zerox < x) return x;245else if (zerox > xm) return xm;246else return zerox;247}248}249250251#if !defined GRAPHITE2_NTRACING252253void Zones::jsonDbgOut(Segment *seg) const {254255if (_dbg)256{257for (Zones::idebugs s = dbgs_begin(), e = dbgs_end(); s != e; ++s)258{259*_dbg << json::flat << json::array260<< objectid(dslot(seg, (Slot *)(s->_env[0])))261<< reinterpret_cast<ptrdiff_t>(s->_env[1]);262if (s->_isdel)263*_dbg << "remove" << Position(s->_excl.x, s->_excl.xm);264else265*_dbg << "exclude" << json::flat << json::array266<< s->_excl.x << s->_excl.xm267<< s->_excl.sm << s->_excl.smx << s->_excl.c268<< json::close;269*_dbg << json::close;270}271}272}273274#endif275276277