xref: /aosp_15_r20/external/harfbuzz_ng/src/hb-subset-instancer-solver.cc (revision 2d1272b857b1f7575e6e246373e1cb218663db8a)
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