1*2d1272b8SAndroid Build Coastguard Worker /*
2*2d1272b8SAndroid Build Coastguard Worker * Copyright © 2023 Behdad Esfahbod
3*2d1272b8SAndroid Build Coastguard Worker *
4*2d1272b8SAndroid Build Coastguard Worker * This is part of HarfBuzz, a text shaping library.
5*2d1272b8SAndroid Build Coastguard Worker *
6*2d1272b8SAndroid Build Coastguard Worker * Permission is hereby granted, without written agreement and without
7*2d1272b8SAndroid Build Coastguard Worker * license or royalty fees, to use, copy, modify, and distribute this
8*2d1272b8SAndroid Build Coastguard Worker * software and its documentation for any purpose, provided that the
9*2d1272b8SAndroid Build Coastguard Worker * above copyright notice and the following two paragraphs appear in
10*2d1272b8SAndroid Build Coastguard Worker * all copies of this software.
11*2d1272b8SAndroid Build Coastguard Worker *
12*2d1272b8SAndroid Build Coastguard Worker * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13*2d1272b8SAndroid Build Coastguard Worker * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14*2d1272b8SAndroid Build Coastguard Worker * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15*2d1272b8SAndroid Build Coastguard Worker * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
16*2d1272b8SAndroid Build Coastguard Worker * DAMAGE.
17*2d1272b8SAndroid Build Coastguard Worker *
18*2d1272b8SAndroid Build Coastguard Worker * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19*2d1272b8SAndroid Build Coastguard Worker * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20*2d1272b8SAndroid Build Coastguard Worker * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
21*2d1272b8SAndroid Build Coastguard Worker * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22*2d1272b8SAndroid Build Coastguard Worker * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
23*2d1272b8SAndroid Build Coastguard Worker */
24*2d1272b8SAndroid Build Coastguard Worker
25*2d1272b8SAndroid Build Coastguard Worker #include "hb-subset-instancer-solver.hh"
26*2d1272b8SAndroid Build Coastguard Worker
27*2d1272b8SAndroid Build Coastguard Worker /* This file is a straight port of the following:
28*2d1272b8SAndroid Build Coastguard Worker *
29*2d1272b8SAndroid Build Coastguard Worker * https://github.com/fonttools/fonttools/blob/f73220816264fc383b8a75f2146e8d69e455d398/Lib/fontTools/varLib/instancer/solver.py
30*2d1272b8SAndroid Build Coastguard Worker *
31*2d1272b8SAndroid Build Coastguard Worker * Where that file returns None for a triple, we return Triple{}.
32*2d1272b8SAndroid Build Coastguard Worker * This should be safe.
33*2d1272b8SAndroid Build Coastguard Worker */
34*2d1272b8SAndroid Build Coastguard Worker
35*2d1272b8SAndroid Build Coastguard Worker constexpr static double EPSILON = 1.0 / (1 << 14);
36*2d1272b8SAndroid Build Coastguard Worker constexpr static double MAX_F2DOT14 = double (0x7FFF) / (1 << 14);
37*2d1272b8SAndroid Build Coastguard Worker
_reverse_negate(const Triple & v)38*2d1272b8SAndroid Build Coastguard Worker static inline Triple _reverse_negate(const Triple &v)
39*2d1272b8SAndroid Build Coastguard Worker { return {-v.maximum, -v.middle, -v.minimum}; }
40*2d1272b8SAndroid Build Coastguard Worker
41*2d1272b8SAndroid Build Coastguard Worker
supportScalar(double coord,const Triple & tent)42*2d1272b8SAndroid Build Coastguard Worker static inline double supportScalar (double coord, const Triple &tent)
43*2d1272b8SAndroid Build Coastguard Worker {
44*2d1272b8SAndroid Build Coastguard Worker /* Copied from VarRegionAxis::evaluate() */
45*2d1272b8SAndroid Build Coastguard Worker double start = tent.minimum, peak = tent.middle, end = tent.maximum;
46*2d1272b8SAndroid Build Coastguard Worker
47*2d1272b8SAndroid Build Coastguard Worker if (unlikely (start > peak || peak > end))
48*2d1272b8SAndroid Build Coastguard Worker return 1.;
49*2d1272b8SAndroid Build Coastguard Worker if (unlikely (start < 0 && end > 0 && peak != 0))
50*2d1272b8SAndroid Build Coastguard Worker return 1.;
51*2d1272b8SAndroid Build Coastguard Worker
52*2d1272b8SAndroid Build Coastguard Worker if (peak == 0 || coord == peak)
53*2d1272b8SAndroid Build Coastguard Worker return 1.;
54*2d1272b8SAndroid Build Coastguard Worker
55*2d1272b8SAndroid Build Coastguard Worker if (coord <= start || end <= coord)
56*2d1272b8SAndroid Build Coastguard Worker return 0.;
57*2d1272b8SAndroid Build Coastguard Worker
58*2d1272b8SAndroid Build Coastguard Worker /* Interpolate */
59*2d1272b8SAndroid Build Coastguard Worker if (coord < peak)
60*2d1272b8SAndroid Build Coastguard Worker return (coord - start) / (peak - start);
61*2d1272b8SAndroid Build Coastguard Worker else
62*2d1272b8SAndroid Build Coastguard Worker return (end - coord) / (end - peak);
63*2d1272b8SAndroid Build Coastguard Worker }
64*2d1272b8SAndroid Build Coastguard Worker
65*2d1272b8SAndroid Build Coastguard Worker static inline rebase_tent_result_t
_solve(Triple tent,Triple axisLimit,bool negative=false)66*2d1272b8SAndroid Build Coastguard Worker _solve (Triple tent, Triple axisLimit, bool negative = false)
67*2d1272b8SAndroid Build Coastguard Worker {
68*2d1272b8SAndroid Build Coastguard Worker double axisMin = axisLimit.minimum;
69*2d1272b8SAndroid Build Coastguard Worker double axisDef = axisLimit.middle;
70*2d1272b8SAndroid Build Coastguard Worker double axisMax = axisLimit.maximum;
71*2d1272b8SAndroid Build Coastguard Worker double lower = tent.minimum;
72*2d1272b8SAndroid Build Coastguard Worker double peak = tent.middle;
73*2d1272b8SAndroid Build Coastguard Worker double upper = tent.maximum;
74*2d1272b8SAndroid Build Coastguard Worker
75*2d1272b8SAndroid Build Coastguard Worker // Mirror the problem such that axisDef <= peak
76*2d1272b8SAndroid Build Coastguard Worker if (axisDef > peak)
77*2d1272b8SAndroid Build Coastguard Worker {
78*2d1272b8SAndroid Build Coastguard Worker rebase_tent_result_t vec = _solve (_reverse_negate (tent),
79*2d1272b8SAndroid Build Coastguard Worker _reverse_negate (axisLimit),
80*2d1272b8SAndroid Build Coastguard Worker !negative);
81*2d1272b8SAndroid Build Coastguard Worker
82*2d1272b8SAndroid Build Coastguard Worker for (auto &p : vec)
83*2d1272b8SAndroid Build Coastguard Worker p = hb_pair (p.first, _reverse_negate (p.second));
84*2d1272b8SAndroid Build Coastguard Worker
85*2d1272b8SAndroid Build Coastguard Worker return vec;
86*2d1272b8SAndroid Build Coastguard Worker }
87*2d1272b8SAndroid Build Coastguard Worker // axisDef <= peak
88*2d1272b8SAndroid Build Coastguard Worker
89*2d1272b8SAndroid Build Coastguard Worker /* case 1: The whole deltaset falls outside the new limit; we can drop it
90*2d1272b8SAndroid Build Coastguard Worker *
91*2d1272b8SAndroid Build Coastguard Worker * peak
92*2d1272b8SAndroid Build Coastguard Worker * 1.........................................o..........
93*2d1272b8SAndroid Build Coastguard Worker * / \
94*2d1272b8SAndroid Build Coastguard Worker * / \
95*2d1272b8SAndroid Build Coastguard Worker * / \
96*2d1272b8SAndroid Build Coastguard Worker * / \
97*2d1272b8SAndroid Build Coastguard Worker * 0---|-----------|----------|-------- o o----1
98*2d1272b8SAndroid Build Coastguard Worker * axisMin axisDef axisMax lower upper
99*2d1272b8SAndroid Build Coastguard Worker */
100*2d1272b8SAndroid Build Coastguard Worker if (axisMax <= lower && axisMax < peak)
101*2d1272b8SAndroid Build Coastguard Worker return rebase_tent_result_t{}; // No overlap
102*2d1272b8SAndroid Build Coastguard Worker
103*2d1272b8SAndroid Build Coastguard Worker /* case 2: Only the peak and outermost bound fall outside the new limit;
104*2d1272b8SAndroid Build Coastguard Worker * we keep the deltaset, update peak and outermost bound and scale deltas
105*2d1272b8SAndroid Build Coastguard Worker * by the scalar value for the restricted axis at the new limit, and solve
106*2d1272b8SAndroid Build Coastguard Worker * recursively.
107*2d1272b8SAndroid Build Coastguard Worker *
108*2d1272b8SAndroid Build Coastguard Worker * |peak
109*2d1272b8SAndroid Build Coastguard Worker * 1...............................|.o..........
110*2d1272b8SAndroid Build Coastguard Worker * |/ \
111*2d1272b8SAndroid Build Coastguard Worker * / \
112*2d1272b8SAndroid Build Coastguard Worker * /| \
113*2d1272b8SAndroid Build Coastguard Worker * / | \
114*2d1272b8SAndroid Build Coastguard Worker * 0--------------------------- o | o----1
115*2d1272b8SAndroid Build Coastguard Worker * lower | upper
116*2d1272b8SAndroid Build Coastguard Worker * |
117*2d1272b8SAndroid Build Coastguard Worker * axisMax
118*2d1272b8SAndroid Build Coastguard Worker *
119*2d1272b8SAndroid Build Coastguard Worker * Convert to:
120*2d1272b8SAndroid Build Coastguard Worker *
121*2d1272b8SAndroid Build Coastguard Worker * 1............................................
122*2d1272b8SAndroid Build Coastguard Worker * |
123*2d1272b8SAndroid Build Coastguard Worker * o peak
124*2d1272b8SAndroid Build Coastguard Worker * /|
125*2d1272b8SAndroid Build Coastguard Worker * /x|
126*2d1272b8SAndroid Build Coastguard Worker * 0--------------------------- o o upper ----1
127*2d1272b8SAndroid Build Coastguard Worker * lower |
128*2d1272b8SAndroid Build Coastguard Worker * |
129*2d1272b8SAndroid Build Coastguard Worker * axisMax
130*2d1272b8SAndroid Build Coastguard Worker */
131*2d1272b8SAndroid Build Coastguard Worker if (axisMax < peak)
132*2d1272b8SAndroid Build Coastguard Worker {
133*2d1272b8SAndroid Build Coastguard Worker double mult = supportScalar (axisMax, tent);
134*2d1272b8SAndroid Build Coastguard Worker tent = Triple{lower, axisMax, axisMax};
135*2d1272b8SAndroid Build Coastguard Worker
136*2d1272b8SAndroid Build Coastguard Worker rebase_tent_result_t vec = _solve (tent, axisLimit);
137*2d1272b8SAndroid Build Coastguard Worker
138*2d1272b8SAndroid Build Coastguard Worker for (auto &p : vec)
139*2d1272b8SAndroid Build Coastguard Worker p = hb_pair (p.first * mult, p.second);
140*2d1272b8SAndroid Build Coastguard Worker
141*2d1272b8SAndroid Build Coastguard Worker return vec;
142*2d1272b8SAndroid Build Coastguard Worker }
143*2d1272b8SAndroid Build Coastguard Worker
144*2d1272b8SAndroid Build Coastguard Worker // lower <= axisDef <= peak <= axisMax
145*2d1272b8SAndroid Build Coastguard Worker
146*2d1272b8SAndroid Build Coastguard Worker double gain = supportScalar (axisDef, tent);
147*2d1272b8SAndroid Build Coastguard Worker rebase_tent_result_t out {hb_pair (gain, Triple{})};
148*2d1272b8SAndroid Build Coastguard Worker
149*2d1272b8SAndroid Build Coastguard Worker // First, the positive side
150*2d1272b8SAndroid Build Coastguard Worker
151*2d1272b8SAndroid Build Coastguard Worker // outGain is the scalar of axisMax at the tent.
152*2d1272b8SAndroid Build Coastguard Worker double outGain = supportScalar (axisMax, tent);
153*2d1272b8SAndroid Build Coastguard Worker
154*2d1272b8SAndroid Build Coastguard Worker /* Case 3a: Gain is more than outGain. The tent down-slope crosses
155*2d1272b8SAndroid Build Coastguard Worker * the axis into negative. We have to split it into multiples.
156*2d1272b8SAndroid Build Coastguard Worker *
157*2d1272b8SAndroid Build Coastguard Worker * | peak |
158*2d1272b8SAndroid Build Coastguard Worker * 1...................|.o.....|..............
159*2d1272b8SAndroid Build Coastguard Worker * |/x\_ |
160*2d1272b8SAndroid Build Coastguard Worker * gain................+....+_.|..............
161*2d1272b8SAndroid Build Coastguard Worker * /| |y\|
162*2d1272b8SAndroid Build Coastguard Worker * ................../.|....|..+_......outGain
163*2d1272b8SAndroid Build Coastguard Worker * / | | | \
164*2d1272b8SAndroid Build Coastguard Worker * 0---|-----------o | | | o----------1
165*2d1272b8SAndroid Build Coastguard Worker * axisMin lower | | | upper
166*2d1272b8SAndroid Build Coastguard Worker * | | |
167*2d1272b8SAndroid Build Coastguard Worker * axisDef | axisMax
168*2d1272b8SAndroid Build Coastguard Worker * |
169*2d1272b8SAndroid Build Coastguard Worker * crossing
170*2d1272b8SAndroid Build Coastguard Worker */
171*2d1272b8SAndroid Build Coastguard Worker if (gain >= outGain)
172*2d1272b8SAndroid Build Coastguard Worker {
173*2d1272b8SAndroid Build Coastguard Worker // Note that this is the branch taken if both gain and outGain are 0.
174*2d1272b8SAndroid Build Coastguard Worker
175*2d1272b8SAndroid Build Coastguard Worker // Crossing point on the axis.
176*2d1272b8SAndroid Build Coastguard Worker double crossing = peak + (1 - gain) * (upper - peak);
177*2d1272b8SAndroid Build Coastguard Worker
178*2d1272b8SAndroid Build Coastguard Worker Triple loc{hb_max (lower, axisDef), peak, crossing};
179*2d1272b8SAndroid Build Coastguard Worker double scalar = 1.0;
180*2d1272b8SAndroid Build Coastguard Worker
181*2d1272b8SAndroid Build Coastguard Worker // The part before the crossing point.
182*2d1272b8SAndroid Build Coastguard Worker out.push (hb_pair (scalar - gain, loc));
183*2d1272b8SAndroid Build Coastguard Worker
184*2d1272b8SAndroid Build Coastguard Worker /* The part after the crossing point may use one or two tents,
185*2d1272b8SAndroid Build Coastguard Worker * depending on whether upper is before axisMax or not, in one
186*2d1272b8SAndroid Build Coastguard Worker * case we need to keep it down to eternity.
187*2d1272b8SAndroid Build Coastguard Worker *
188*2d1272b8SAndroid Build Coastguard Worker * Case 3a1, similar to case 1neg; just one tent needed, as in
189*2d1272b8SAndroid Build Coastguard Worker * the drawing above.
190*2d1272b8SAndroid Build Coastguard Worker */
191*2d1272b8SAndroid Build Coastguard Worker if (upper >= axisMax)
192*2d1272b8SAndroid Build Coastguard Worker {
193*2d1272b8SAndroid Build Coastguard Worker Triple loc {crossing, axisMax, axisMax};
194*2d1272b8SAndroid Build Coastguard Worker double scalar = outGain;
195*2d1272b8SAndroid Build Coastguard Worker
196*2d1272b8SAndroid Build Coastguard Worker out.push (hb_pair (scalar - gain, loc));
197*2d1272b8SAndroid Build Coastguard Worker }
198*2d1272b8SAndroid Build Coastguard Worker
199*2d1272b8SAndroid Build Coastguard Worker /* Case 3a2: Similar to case 2neg; two tents needed, to keep
200*2d1272b8SAndroid Build Coastguard Worker * down to eternity.
201*2d1272b8SAndroid Build Coastguard Worker *
202*2d1272b8SAndroid Build Coastguard Worker * | peak |
203*2d1272b8SAndroid Build Coastguard Worker * 1...................|.o................|...
204*2d1272b8SAndroid Build Coastguard Worker * |/ \_ |
205*2d1272b8SAndroid Build Coastguard Worker * gain................+....+_............|...
206*2d1272b8SAndroid Build Coastguard Worker * /| | \xxxxxxxxxxy|
207*2d1272b8SAndroid Build Coastguard Worker * / | | \_xxxxxyyyy|
208*2d1272b8SAndroid Build Coastguard Worker * / | | \xxyyyyyy|
209*2d1272b8SAndroid Build Coastguard Worker * 0---|-----------o | | o-------|--1
210*2d1272b8SAndroid Build Coastguard Worker * axisMin lower | | upper |
211*2d1272b8SAndroid Build Coastguard Worker * | | |
212*2d1272b8SAndroid Build Coastguard Worker * axisDef | axisMax
213*2d1272b8SAndroid Build Coastguard Worker * |
214*2d1272b8SAndroid Build Coastguard Worker * crossing
215*2d1272b8SAndroid Build Coastguard Worker */
216*2d1272b8SAndroid Build Coastguard Worker else
217*2d1272b8SAndroid Build Coastguard Worker {
218*2d1272b8SAndroid Build Coastguard Worker // A tent's peak cannot fall on axis default. Nudge it.
219*2d1272b8SAndroid Build Coastguard Worker if (upper == axisDef)
220*2d1272b8SAndroid Build Coastguard Worker upper += EPSILON;
221*2d1272b8SAndroid Build Coastguard Worker
222*2d1272b8SAndroid Build Coastguard Worker // Downslope.
223*2d1272b8SAndroid Build Coastguard Worker Triple loc1 {crossing, upper, axisMax};
224*2d1272b8SAndroid Build Coastguard Worker double scalar1 = 0.0;
225*2d1272b8SAndroid Build Coastguard Worker
226*2d1272b8SAndroid Build Coastguard Worker // Eternity justify.
227*2d1272b8SAndroid Build Coastguard Worker Triple loc2 {upper, axisMax, axisMax};
228*2d1272b8SAndroid Build Coastguard Worker double scalar2 = 0.0;
229*2d1272b8SAndroid Build Coastguard Worker
230*2d1272b8SAndroid Build Coastguard Worker out.push (hb_pair (scalar1 - gain, loc1));
231*2d1272b8SAndroid Build Coastguard Worker out.push (hb_pair (scalar2 - gain, loc2));
232*2d1272b8SAndroid Build Coastguard Worker }
233*2d1272b8SAndroid Build Coastguard Worker }
234*2d1272b8SAndroid Build Coastguard Worker
235*2d1272b8SAndroid Build Coastguard Worker else
236*2d1272b8SAndroid Build Coastguard Worker {
237*2d1272b8SAndroid Build Coastguard Worker // Special-case if peak is at axisMax.
238*2d1272b8SAndroid Build Coastguard Worker if (axisMax == peak)
239*2d1272b8SAndroid Build Coastguard Worker upper = peak;
240*2d1272b8SAndroid Build Coastguard Worker
241*2d1272b8SAndroid Build Coastguard Worker /* Case 3:
242*2d1272b8SAndroid Build Coastguard Worker * we keep deltas as is and only scale the axis upper to achieve
243*2d1272b8SAndroid Build Coastguard Worker * the desired new tent if feasible.
244*2d1272b8SAndroid Build Coastguard Worker *
245*2d1272b8SAndroid Build Coastguard Worker * peak
246*2d1272b8SAndroid Build Coastguard Worker * 1.....................o....................
247*2d1272b8SAndroid Build Coastguard Worker * / \_|
248*2d1272b8SAndroid Build Coastguard Worker * ..................../....+_.........outGain
249*2d1272b8SAndroid Build Coastguard Worker * / | \
250*2d1272b8SAndroid Build Coastguard Worker * gain..............+......|..+_.............
251*2d1272b8SAndroid Build Coastguard Worker * /| | | \
252*2d1272b8SAndroid Build Coastguard Worker * 0---|-----------o | | | o----------1
253*2d1272b8SAndroid Build Coastguard Worker * axisMin lower| | | upper
254*2d1272b8SAndroid Build Coastguard Worker * | | newUpper
255*2d1272b8SAndroid Build Coastguard Worker * axisDef axisMax
256*2d1272b8SAndroid Build Coastguard Worker */
257*2d1272b8SAndroid Build Coastguard Worker double newUpper = peak + (1 - gain) * (upper - peak);
258*2d1272b8SAndroid Build Coastguard Worker assert (axisMax <= newUpper); // Because outGain > gain
259*2d1272b8SAndroid Build Coastguard Worker /* Disabled because ots doesn't like us:
260*2d1272b8SAndroid Build Coastguard Worker * https://github.com/fonttools/fonttools/issues/3350 */
261*2d1272b8SAndroid Build Coastguard Worker
262*2d1272b8SAndroid Build Coastguard Worker if (false && (newUpper <= axisDef + (axisMax - axisDef) * 2))
263*2d1272b8SAndroid Build Coastguard Worker {
264*2d1272b8SAndroid Build Coastguard Worker upper = newUpper;
265*2d1272b8SAndroid Build Coastguard Worker if (!negative && axisDef + (axisMax - axisDef) * MAX_F2DOT14 < upper)
266*2d1272b8SAndroid Build Coastguard Worker {
267*2d1272b8SAndroid Build Coastguard Worker // we clamp +2.0 to the max F2Dot14 (~1.99994) for convenience
268*2d1272b8SAndroid Build Coastguard Worker upper = axisDef + (axisMax - axisDef) * MAX_F2DOT14;
269*2d1272b8SAndroid Build Coastguard Worker assert (peak < upper);
270*2d1272b8SAndroid Build Coastguard Worker }
271*2d1272b8SAndroid Build Coastguard Worker
272*2d1272b8SAndroid Build Coastguard Worker Triple loc {hb_max (axisDef, lower), peak, upper};
273*2d1272b8SAndroid Build Coastguard Worker double scalar = 1.0;
274*2d1272b8SAndroid Build Coastguard Worker
275*2d1272b8SAndroid Build Coastguard Worker out.push (hb_pair (scalar - gain, loc));
276*2d1272b8SAndroid Build Coastguard Worker }
277*2d1272b8SAndroid Build Coastguard Worker
278*2d1272b8SAndroid Build Coastguard Worker /* Case 4: New limit doesn't fit; we need to chop into two tents,
279*2d1272b8SAndroid Build Coastguard Worker * because the shape of a triangle with part of one side cut off
280*2d1272b8SAndroid Build Coastguard Worker * cannot be represented as a triangle itself.
281*2d1272b8SAndroid Build Coastguard Worker *
282*2d1272b8SAndroid Build Coastguard Worker * | peak |
283*2d1272b8SAndroid Build Coastguard Worker * 1.........|......o.|....................
284*2d1272b8SAndroid Build Coastguard Worker * ..........|...../x\|.............outGain
285*2d1272b8SAndroid Build Coastguard Worker * | |xxy|\_
286*2d1272b8SAndroid Build Coastguard Worker * | /xxxy| \_
287*2d1272b8SAndroid Build Coastguard Worker * | |xxxxy| \_
288*2d1272b8SAndroid Build Coastguard Worker * | /xxxxy| \_
289*2d1272b8SAndroid Build Coastguard Worker * 0---|-----|-oxxxxxx| o----------1
290*2d1272b8SAndroid Build Coastguard Worker * axisMin | lower | upper
291*2d1272b8SAndroid Build Coastguard Worker * | |
292*2d1272b8SAndroid Build Coastguard Worker * axisDef axisMax
293*2d1272b8SAndroid Build Coastguard Worker */
294*2d1272b8SAndroid Build Coastguard Worker else
295*2d1272b8SAndroid Build Coastguard Worker {
296*2d1272b8SAndroid Build Coastguard Worker Triple loc1 {hb_max (axisDef, lower), peak, axisMax};
297*2d1272b8SAndroid Build Coastguard Worker double scalar1 = 1.0;
298*2d1272b8SAndroid Build Coastguard Worker
299*2d1272b8SAndroid Build Coastguard Worker Triple loc2 {peak, axisMax, axisMax};
300*2d1272b8SAndroid Build Coastguard Worker double scalar2 = outGain;
301*2d1272b8SAndroid Build Coastguard Worker
302*2d1272b8SAndroid Build Coastguard Worker out.push (hb_pair (scalar1 - gain, loc1));
303*2d1272b8SAndroid Build Coastguard Worker // Don't add a dirac delta!
304*2d1272b8SAndroid Build Coastguard Worker if (peak < axisMax)
305*2d1272b8SAndroid Build Coastguard Worker out.push (hb_pair (scalar2 - gain, loc2));
306*2d1272b8SAndroid Build Coastguard Worker }
307*2d1272b8SAndroid Build Coastguard Worker }
308*2d1272b8SAndroid Build Coastguard Worker
309*2d1272b8SAndroid Build Coastguard Worker /* Now, the negative side
310*2d1272b8SAndroid Build Coastguard Worker *
311*2d1272b8SAndroid Build Coastguard Worker * Case 1neg: Lower extends beyond axisMin: we chop. Simple.
312*2d1272b8SAndroid Build Coastguard Worker *
313*2d1272b8SAndroid Build Coastguard Worker * | |peak
314*2d1272b8SAndroid Build Coastguard Worker * 1..................|...|.o.................
315*2d1272b8SAndroid Build Coastguard Worker * | |/ \
316*2d1272b8SAndroid Build Coastguard Worker * gain...............|...+...\...............
317*2d1272b8SAndroid Build Coastguard Worker * |x_/| \
318*2d1272b8SAndroid Build Coastguard Worker * |/ | \
319*2d1272b8SAndroid Build Coastguard Worker * _/| | \
320*2d1272b8SAndroid Build Coastguard Worker * 0---------------o | | o----------1
321*2d1272b8SAndroid Build Coastguard Worker * lower | | upper
322*2d1272b8SAndroid Build Coastguard Worker * | |
323*2d1272b8SAndroid Build Coastguard Worker * axisMin axisDef
324*2d1272b8SAndroid Build Coastguard Worker */
325*2d1272b8SAndroid Build Coastguard Worker if (lower <= axisMin)
326*2d1272b8SAndroid Build Coastguard Worker {
327*2d1272b8SAndroid Build Coastguard Worker Triple loc {axisMin, axisMin, axisDef};
328*2d1272b8SAndroid Build Coastguard Worker double scalar = supportScalar (axisMin, tent);
329*2d1272b8SAndroid Build Coastguard Worker
330*2d1272b8SAndroid Build Coastguard Worker out.push (hb_pair (scalar - gain, loc));
331*2d1272b8SAndroid Build Coastguard Worker }
332*2d1272b8SAndroid Build Coastguard Worker
333*2d1272b8SAndroid Build Coastguard Worker /* Case 2neg: Lower is betwen axisMin and axisDef: we add two
334*2d1272b8SAndroid Build Coastguard Worker * tents to keep it down all the way to eternity.
335*2d1272b8SAndroid Build Coastguard Worker *
336*2d1272b8SAndroid Build Coastguard Worker * | |peak
337*2d1272b8SAndroid Build Coastguard Worker * 1...|...............|.o.................
338*2d1272b8SAndroid Build Coastguard Worker * | |/ \
339*2d1272b8SAndroid Build Coastguard Worker * gain|...............+...\...............
340*2d1272b8SAndroid Build Coastguard Worker * |yxxxxxxxxxxxxx/| \
341*2d1272b8SAndroid Build Coastguard Worker * |yyyyyyxxxxxxx/ | \
342*2d1272b8SAndroid Build Coastguard Worker * |yyyyyyyyyyyx/ | \
343*2d1272b8SAndroid Build Coastguard Worker * 0---|-----------o | o----------1
344*2d1272b8SAndroid Build Coastguard Worker * axisMin lower | upper
345*2d1272b8SAndroid Build Coastguard Worker * |
346*2d1272b8SAndroid Build Coastguard Worker * axisDef
347*2d1272b8SAndroid Build Coastguard Worker */
348*2d1272b8SAndroid Build Coastguard Worker else
349*2d1272b8SAndroid Build Coastguard Worker {
350*2d1272b8SAndroid Build Coastguard Worker // A tent's peak cannot fall on axis default. Nudge it.
351*2d1272b8SAndroid Build Coastguard Worker if (lower == axisDef)
352*2d1272b8SAndroid Build Coastguard Worker lower -= EPSILON;
353*2d1272b8SAndroid Build Coastguard Worker
354*2d1272b8SAndroid Build Coastguard Worker // Downslope.
355*2d1272b8SAndroid Build Coastguard Worker Triple loc1 {axisMin, lower, axisDef};
356*2d1272b8SAndroid Build Coastguard Worker double scalar1 = 0.0;
357*2d1272b8SAndroid Build Coastguard Worker
358*2d1272b8SAndroid Build Coastguard Worker // Eternity justify.
359*2d1272b8SAndroid Build Coastguard Worker Triple loc2 {axisMin, axisMin, lower};
360*2d1272b8SAndroid Build Coastguard Worker double scalar2 = 0.0;
361*2d1272b8SAndroid Build Coastguard Worker
362*2d1272b8SAndroid Build Coastguard Worker out.push (hb_pair (scalar1 - gain, loc1));
363*2d1272b8SAndroid Build Coastguard Worker out.push (hb_pair (scalar2 - gain, loc2));
364*2d1272b8SAndroid Build Coastguard Worker }
365*2d1272b8SAndroid Build Coastguard Worker
366*2d1272b8SAndroid Build Coastguard Worker return out;
367*2d1272b8SAndroid Build Coastguard Worker }
368*2d1272b8SAndroid Build Coastguard Worker
_reverse_triple_distances(const TripleDistances & v)369*2d1272b8SAndroid Build Coastguard Worker static inline TripleDistances _reverse_triple_distances (const TripleDistances &v)
370*2d1272b8SAndroid Build Coastguard Worker { return TripleDistances (v.positive, v.negative); }
371*2d1272b8SAndroid Build Coastguard Worker
renormalizeValue(double v,const Triple & triple,const TripleDistances & triple_distances,bool extrapolate)372*2d1272b8SAndroid Build Coastguard Worker double renormalizeValue (double v, const Triple &triple,
373*2d1272b8SAndroid Build Coastguard Worker const TripleDistances &triple_distances, bool extrapolate)
374*2d1272b8SAndroid Build Coastguard Worker {
375*2d1272b8SAndroid Build Coastguard Worker double lower = triple.minimum, def = triple.middle, upper = triple.maximum;
376*2d1272b8SAndroid Build Coastguard Worker assert (lower <= def && def <= upper);
377*2d1272b8SAndroid Build Coastguard Worker
378*2d1272b8SAndroid Build Coastguard Worker if (!extrapolate)
379*2d1272b8SAndroid Build Coastguard Worker v = hb_clamp (v, lower, upper);
380*2d1272b8SAndroid Build Coastguard Worker
381*2d1272b8SAndroid Build Coastguard Worker if (v == def)
382*2d1272b8SAndroid Build Coastguard Worker return 0.0;
383*2d1272b8SAndroid Build Coastguard Worker
384*2d1272b8SAndroid Build Coastguard Worker if (def < 0.0)
385*2d1272b8SAndroid Build Coastguard Worker return -renormalizeValue (-v, _reverse_negate (triple),
386*2d1272b8SAndroid Build Coastguard Worker _reverse_triple_distances (triple_distances), extrapolate);
387*2d1272b8SAndroid Build Coastguard Worker
388*2d1272b8SAndroid Build Coastguard Worker /* default >= 0 and v != default */
389*2d1272b8SAndroid Build Coastguard Worker if (v > def)
390*2d1272b8SAndroid Build Coastguard Worker return (v - def) / (upper - def);
391*2d1272b8SAndroid Build Coastguard Worker
392*2d1272b8SAndroid Build Coastguard Worker /* v < def */
393*2d1272b8SAndroid Build Coastguard Worker if (lower >= 0.0)
394*2d1272b8SAndroid Build Coastguard Worker return (v - def) / (def - lower);
395*2d1272b8SAndroid Build Coastguard Worker
396*2d1272b8SAndroid Build Coastguard Worker /* lower < 0 and v < default */
397*2d1272b8SAndroid Build Coastguard Worker double total_distance = triple_distances.negative * (-lower) + triple_distances.positive * def;
398*2d1272b8SAndroid Build Coastguard Worker
399*2d1272b8SAndroid Build Coastguard Worker double v_distance;
400*2d1272b8SAndroid Build Coastguard Worker if (v >= 0.0)
401*2d1272b8SAndroid Build Coastguard Worker v_distance = (def - v) * triple_distances.positive;
402*2d1272b8SAndroid Build Coastguard Worker else
403*2d1272b8SAndroid Build Coastguard Worker v_distance = (-v) * triple_distances.negative + triple_distances.positive * def;
404*2d1272b8SAndroid Build Coastguard Worker
405*2d1272b8SAndroid Build Coastguard Worker return (-v_distance) /total_distance;
406*2d1272b8SAndroid Build Coastguard Worker }
407*2d1272b8SAndroid Build Coastguard Worker
408*2d1272b8SAndroid Build Coastguard Worker rebase_tent_result_t
rebase_tent(Triple tent,Triple axisLimit,TripleDistances axis_triple_distances)409*2d1272b8SAndroid Build Coastguard Worker rebase_tent (Triple tent, Triple axisLimit, TripleDistances axis_triple_distances)
410*2d1272b8SAndroid Build Coastguard Worker {
411*2d1272b8SAndroid Build Coastguard Worker assert (-1.0 <= axisLimit.minimum && axisLimit.minimum <= axisLimit.middle && axisLimit.middle <= axisLimit.maximum && axisLimit.maximum <= +1.0);
412*2d1272b8SAndroid Build Coastguard Worker assert (-2.0 <= tent.minimum && tent.minimum <= tent.middle && tent.middle <= tent.maximum && tent.maximum <= +2.0);
413*2d1272b8SAndroid Build Coastguard Worker assert (tent.middle != 0.0);
414*2d1272b8SAndroid Build Coastguard Worker
415*2d1272b8SAndroid Build Coastguard Worker rebase_tent_result_t sols = _solve (tent, axisLimit);
416*2d1272b8SAndroid Build Coastguard Worker
417*2d1272b8SAndroid Build Coastguard Worker auto n = [&axisLimit, &axis_triple_distances] (double v) { return renormalizeValue (v, axisLimit, axis_triple_distances); };
418*2d1272b8SAndroid Build Coastguard Worker
419*2d1272b8SAndroid Build Coastguard Worker rebase_tent_result_t out;
420*2d1272b8SAndroid Build Coastguard Worker for (auto &p : sols)
421*2d1272b8SAndroid Build Coastguard Worker {
422*2d1272b8SAndroid Build Coastguard Worker if (!p.first) continue;
423*2d1272b8SAndroid Build Coastguard Worker if (p.second == Triple{})
424*2d1272b8SAndroid Build Coastguard Worker {
425*2d1272b8SAndroid Build Coastguard Worker out.push (p);
426*2d1272b8SAndroid Build Coastguard Worker continue;
427*2d1272b8SAndroid Build Coastguard Worker }
428*2d1272b8SAndroid Build Coastguard Worker Triple t = p.second;
429*2d1272b8SAndroid Build Coastguard Worker out.push (hb_pair (p.first,
430*2d1272b8SAndroid Build Coastguard Worker Triple{n (t.minimum), n (t.middle), n (t.maximum)}));
431*2d1272b8SAndroid Build Coastguard Worker }
432*2d1272b8SAndroid Build Coastguard Worker
433*2d1272b8SAndroid Build Coastguard Worker return out;
434*2d1272b8SAndroid Build Coastguard Worker }
435