xref: /inferno-os/libfreetype/ahoptim.c (revision 37da2899f40661e3e9631e497da8dc59b971cbd0)
1*37da2899SCharles.Forsyth /***************************************************************************/
2*37da2899SCharles.Forsyth /*                                                                         */
3*37da2899SCharles.Forsyth /*  ahoptim.c                                                              */
4*37da2899SCharles.Forsyth /*                                                                         */
5*37da2899SCharles.Forsyth /*    FreeType auto hinting outline optimization (body).                   */
6*37da2899SCharles.Forsyth /*                                                                         */
7*37da2899SCharles.Forsyth /*  Copyright 2000-2001, 2002 Catharon Productions Inc.                    */
8*37da2899SCharles.Forsyth /*  Author: David Turner                                                   */
9*37da2899SCharles.Forsyth /*                                                                         */
10*37da2899SCharles.Forsyth /*  This file is part of the Catharon Typography Project and shall only    */
11*37da2899SCharles.Forsyth /*  be used, modified, and distributed under the terms of the Catharon     */
12*37da2899SCharles.Forsyth /*  Open Source License that should come with this file under the name     */
13*37da2899SCharles.Forsyth /*  `CatharonLicense.txt'.  By continuing to use, modify, or distribute    */
14*37da2899SCharles.Forsyth /*  this file you indicate that you have read the license and              */
15*37da2899SCharles.Forsyth /*  understand and accept it fully.                                        */
16*37da2899SCharles.Forsyth /*                                                                         */
17*37da2899SCharles.Forsyth /*  Note that this license is compatible with the FreeType license.        */
18*37da2899SCharles.Forsyth /*                                                                         */
19*37da2899SCharles.Forsyth /***************************************************************************/
20*37da2899SCharles.Forsyth 
21*37da2899SCharles.Forsyth 
22*37da2899SCharles.Forsyth   /*************************************************************************/
23*37da2899SCharles.Forsyth   /*                                                                       */
24*37da2899SCharles.Forsyth   /* This module is in charge of optimising the outlines produced by the   */
25*37da2899SCharles.Forsyth   /* auto-hinter in direct mode. This is required at small pixel sizes in  */
26*37da2899SCharles.Forsyth   /* order to ensure coherent spacing, among other things..                */
27*37da2899SCharles.Forsyth   /*                                                                       */
28*37da2899SCharles.Forsyth   /* The technique used in this module is a simplified simulated           */
29*37da2899SCharles.Forsyth   /* annealing.                                                            */
30*37da2899SCharles.Forsyth   /*                                                                       */
31*37da2899SCharles.Forsyth   /*************************************************************************/
32*37da2899SCharles.Forsyth 
33*37da2899SCharles.Forsyth 
34*37da2899SCharles.Forsyth #include <ft2build.h>
35*37da2899SCharles.Forsyth #include FT_INTERNAL_OBJECTS_H       /* for FT_ALLOC_ARRAY() and FT_FREE() */
36*37da2899SCharles.Forsyth #include "ahoptim.h"
37*37da2899SCharles.Forsyth 
38*37da2899SCharles.Forsyth 
39*37da2899SCharles.Forsyth   /* define this macro to use brute force optimization -- this is slow,  */
40*37da2899SCharles.Forsyth   /* but a good way to perfect the distortion function `by hand' through */
41*37da2899SCharles.Forsyth   /* tweaking                                                            */
42*37da2899SCharles.Forsyth #define AH_BRUTE_FORCE
43*37da2899SCharles.Forsyth 
44*37da2899SCharles.Forsyth 
45*37da2899SCharles.Forsyth #define xxxAH_DEBUG_OPTIM
46*37da2899SCharles.Forsyth 
47*37da2899SCharles.Forsyth 
48*37da2899SCharles.Forsyth #undef LOG
49*37da2899SCharles.Forsyth #ifdef AH_DEBUG_OPTIM
50*37da2899SCharles.Forsyth 
51*37da2899SCharles.Forsyth #define LOG( x )  optim_log ## x
52*37da2899SCharles.Forsyth 
53*37da2899SCharles.Forsyth #else
54*37da2899SCharles.Forsyth 
55*37da2899SCharles.Forsyth #define LOG( x )
56*37da2899SCharles.Forsyth 
57*37da2899SCharles.Forsyth #endif /* AH_DEBUG_OPTIM */
58*37da2899SCharles.Forsyth 
59*37da2899SCharles.Forsyth 
60*37da2899SCharles.Forsyth #ifdef AH_DEBUG_OPTIM
61*37da2899SCharles.Forsyth 
62*37da2899SCharles.Forsyth #include <stdarg.h>
63*37da2899SCharles.Forsyth #include <stdlib.h>
64*37da2899SCharles.Forsyth 
65*37da2899SCharles.Forsyth #define FLOAT( x )  ( (float)( (x) / 64.0 ) )
66*37da2899SCharles.Forsyth 
67*37da2899SCharles.Forsyth 
68*37da2899SCharles.Forsyth   static void
optim_log(const char * fmt,...)69*37da2899SCharles.Forsyth   optim_log( const char*  fmt, ... )
70*37da2899SCharles.Forsyth   {
71*37da2899SCharles.Forsyth     va_list  ap;
72*37da2899SCharles.Forsyth 
73*37da2899SCharles.Forsyth 
74*37da2899SCharles.Forsyth     va_start( ap, fmt );
75*37da2899SCharles.Forsyth     vprintf( fmt, ap );
76*37da2899SCharles.Forsyth     va_end( ap );
77*37da2899SCharles.Forsyth   }
78*37da2899SCharles.Forsyth 
79*37da2899SCharles.Forsyth 
80*37da2899SCharles.Forsyth   static void
AH_Dump_Stems(AH_Optimizer * optimizer)81*37da2899SCharles.Forsyth   AH_Dump_Stems( AH_Optimizer*  optimizer )
82*37da2899SCharles.Forsyth   {
83*37da2899SCharles.Forsyth     int       n;
84*37da2899SCharles.Forsyth     AH_Stem*  stem;
85*37da2899SCharles.Forsyth 
86*37da2899SCharles.Forsyth 
87*37da2899SCharles.Forsyth     stem = optimizer->stems;
88*37da2899SCharles.Forsyth     for ( n = 0; n < optimizer->num_stems; n++, stem++ )
89*37da2899SCharles.Forsyth     {
90*37da2899SCharles.Forsyth       LOG(( " %c%2d [%.1f:%.1f]={%.1f:%.1f}="
91*37da2899SCharles.Forsyth             "<%1.f..%1.f> force=%.1f speed=%.1f\n",
92*37da2899SCharles.Forsyth             optimizer->vertical ? 'V' : 'H', n,
93*37da2899SCharles.Forsyth             FLOAT( stem->edge1->opos ), FLOAT( stem->edge2->opos ),
94*37da2899SCharles.Forsyth             FLOAT( stem->edge1->pos ),  FLOAT( stem->edge2->pos ),
95*37da2899SCharles.Forsyth             FLOAT( stem->min_pos ),     FLOAT( stem->max_pos ),
96*37da2899SCharles.Forsyth             FLOAT( stem->force ),       FLOAT( stem->velocity ) ));
97*37da2899SCharles.Forsyth     }
98*37da2899SCharles.Forsyth   }
99*37da2899SCharles.Forsyth 
100*37da2899SCharles.Forsyth 
101*37da2899SCharles.Forsyth   static void
AH_Dump_Stems2(AH_Optimizer * optimizer)102*37da2899SCharles.Forsyth   AH_Dump_Stems2( AH_Optimizer*  optimizer )
103*37da2899SCharles.Forsyth   {
104*37da2899SCharles.Forsyth     int       n;
105*37da2899SCharles.Forsyth     AH_Stem*  stem;
106*37da2899SCharles.Forsyth 
107*37da2899SCharles.Forsyth 
108*37da2899SCharles.Forsyth     stem = optimizer->stems;
109*37da2899SCharles.Forsyth     for ( n = 0; n < optimizer->num_stems; n++, stem++ )
110*37da2899SCharles.Forsyth     {
111*37da2899SCharles.Forsyth       LOG(( " %c%2d [%.1f]=<%1.f..%1.f> force=%.1f speed=%.1f\n",
112*37da2899SCharles.Forsyth             optimizer->vertical ? 'V' : 'H', n,
113*37da2899SCharles.Forsyth             FLOAT( stem->pos ),
114*37da2899SCharles.Forsyth             FLOAT( stem->min_pos ), FLOAT( stem->max_pos ),
115*37da2899SCharles.Forsyth             FLOAT( stem->force ),   FLOAT( stem->velocity ) ));
116*37da2899SCharles.Forsyth     }
117*37da2899SCharles.Forsyth   }
118*37da2899SCharles.Forsyth 
119*37da2899SCharles.Forsyth 
120*37da2899SCharles.Forsyth   static void
AH_Dump_Springs(AH_Optimizer * optimizer)121*37da2899SCharles.Forsyth   AH_Dump_Springs( AH_Optimizer*  optimizer )
122*37da2899SCharles.Forsyth   {
123*37da2899SCharles.Forsyth     int  n;
124*37da2899SCharles.Forsyth     AH_Spring*  spring;
125*37da2899SCharles.Forsyth     AH_Stem*    stems;
126*37da2899SCharles.Forsyth 
127*37da2899SCharles.Forsyth 
128*37da2899SCharles.Forsyth     spring = optimizer->springs;
129*37da2899SCharles.Forsyth     stems  = optimizer->stems;
130*37da2899SCharles.Forsyth     LOG(( "%cSprings ", optimizer->vertical ? 'V' : 'H' ));
131*37da2899SCharles.Forsyth 
132*37da2899SCharles.Forsyth     for ( n = 0; n < optimizer->num_springs; n++, spring++ )
133*37da2899SCharles.Forsyth     {
134*37da2899SCharles.Forsyth       LOG(( " [%d-%d:%.1f:%1.f:%.1f]",
135*37da2899SCharles.Forsyth             spring->stem1 - stems, spring->stem2 - stems,
136*37da2899SCharles.Forsyth             FLOAT( spring->owidth ),
137*37da2899SCharles.Forsyth             FLOAT( spring->stem2->pos -
138*37da2899SCharles.Forsyth                    ( spring->stem1->pos + spring->stem1->width ) ),
139*37da2899SCharles.Forsyth             FLOAT( spring->tension ) ));
140*37da2899SCharles.Forsyth     }
141*37da2899SCharles.Forsyth 
142*37da2899SCharles.Forsyth     LOG(( "\n" ));
143*37da2899SCharles.Forsyth   }
144*37da2899SCharles.Forsyth 
145*37da2899SCharles.Forsyth #endif /* AH_DEBUG_OPTIM */
146*37da2899SCharles.Forsyth 
147*37da2899SCharles.Forsyth 
148*37da2899SCharles.Forsyth   /*************************************************************************/
149*37da2899SCharles.Forsyth   /*************************************************************************/
150*37da2899SCharles.Forsyth   /*************************************************************************/
151*37da2899SCharles.Forsyth   /****                                                                 ****/
152*37da2899SCharles.Forsyth   /****   COMPUTE STEMS AND SPRINGS IN AN OUTLINE                       ****/
153*37da2899SCharles.Forsyth   /****                                                                 ****/
154*37da2899SCharles.Forsyth   /*************************************************************************/
155*37da2899SCharles.Forsyth   /*************************************************************************/
156*37da2899SCharles.Forsyth   /*************************************************************************/
157*37da2899SCharles.Forsyth 
158*37da2899SCharles.Forsyth 
159*37da2899SCharles.Forsyth   static int
valid_stem_segments(AH_Segment seg1,AH_Segment seg2)160*37da2899SCharles.Forsyth   valid_stem_segments( AH_Segment  seg1,
161*37da2899SCharles.Forsyth                        AH_Segment  seg2 )
162*37da2899SCharles.Forsyth   {
163*37da2899SCharles.Forsyth     return seg1->serif == 0                   &&
164*37da2899SCharles.Forsyth            seg2                               &&
165*37da2899SCharles.Forsyth            seg2->link == seg1                 &&
166*37da2899SCharles.Forsyth            seg1->pos < seg2->pos              &&
167*37da2899SCharles.Forsyth            seg1->min_coord <= seg2->max_coord &&
168*37da2899SCharles.Forsyth            seg2->min_coord <= seg1->max_coord;
169*37da2899SCharles.Forsyth   }
170*37da2899SCharles.Forsyth 
171*37da2899SCharles.Forsyth 
172*37da2899SCharles.Forsyth   /* compute all stems in an outline */
173*37da2899SCharles.Forsyth   static int
optim_compute_stems(AH_Optimizer * optimizer)174*37da2899SCharles.Forsyth   optim_compute_stems( AH_Optimizer*  optimizer )
175*37da2899SCharles.Forsyth   {
176*37da2899SCharles.Forsyth     AH_Outline  outline = optimizer->outline;
177*37da2899SCharles.Forsyth     FT_Fixed    scale;
178*37da2899SCharles.Forsyth     FT_Memory   memory  = optimizer->memory;
179*37da2899SCharles.Forsyth     FT_Error    error   = 0;
180*37da2899SCharles.Forsyth     FT_Int      dimension;
181*37da2899SCharles.Forsyth     AH_Edge     edges;
182*37da2899SCharles.Forsyth     AH_Edge     edge_limit;
183*37da2899SCharles.Forsyth     AH_Stem**   p_stems;
184*37da2899SCharles.Forsyth     FT_Int*     p_num_stems;
185*37da2899SCharles.Forsyth 
186*37da2899SCharles.Forsyth 
187*37da2899SCharles.Forsyth     edges      = outline->horz_edges;
188*37da2899SCharles.Forsyth     edge_limit = edges + outline->num_hedges;
189*37da2899SCharles.Forsyth     scale      = outline->y_scale;
190*37da2899SCharles.Forsyth 
191*37da2899SCharles.Forsyth     p_stems     = &optimizer->horz_stems;
192*37da2899SCharles.Forsyth     p_num_stems = &optimizer->num_hstems;
193*37da2899SCharles.Forsyth 
194*37da2899SCharles.Forsyth     for ( dimension = 1; dimension >= 0; dimension-- )
195*37da2899SCharles.Forsyth     {
196*37da2899SCharles.Forsyth       AH_Stem*  stems     = 0;
197*37da2899SCharles.Forsyth       FT_Int    num_stems = 0;
198*37da2899SCharles.Forsyth       AH_Edge   edge;
199*37da2899SCharles.Forsyth 
200*37da2899SCharles.Forsyth 
201*37da2899SCharles.Forsyth       /* first of all, count the number of stems in this direction */
202*37da2899SCharles.Forsyth       for ( edge = edges; edge < edge_limit; edge++ )
203*37da2899SCharles.Forsyth       {
204*37da2899SCharles.Forsyth         AH_Segment  seg = edge->first;
205*37da2899SCharles.Forsyth 
206*37da2899SCharles.Forsyth 
207*37da2899SCharles.Forsyth         do
208*37da2899SCharles.Forsyth         {
209*37da2899SCharles.Forsyth           if (valid_stem_segments( seg, seg->link ) )
210*37da2899SCharles.Forsyth             num_stems++;
211*37da2899SCharles.Forsyth 
212*37da2899SCharles.Forsyth           seg = seg->edge_next;
213*37da2899SCharles.Forsyth 
214*37da2899SCharles.Forsyth         } while ( seg != edge->first );
215*37da2899SCharles.Forsyth       }
216*37da2899SCharles.Forsyth 
217*37da2899SCharles.Forsyth       /* now allocate the stems and build their table */
218*37da2899SCharles.Forsyth       if ( num_stems > 0 )
219*37da2899SCharles.Forsyth       {
220*37da2899SCharles.Forsyth         AH_Stem*  stem;
221*37da2899SCharles.Forsyth 
222*37da2899SCharles.Forsyth 
223*37da2899SCharles.Forsyth         if ( FT_NEW_ARRAY( stems, num_stems ) )
224*37da2899SCharles.Forsyth           goto Exit;
225*37da2899SCharles.Forsyth 
226*37da2899SCharles.Forsyth         stem = stems;
227*37da2899SCharles.Forsyth         for ( edge = edges; edge < edge_limit; edge++ )
228*37da2899SCharles.Forsyth         {
229*37da2899SCharles.Forsyth           AH_Segment  seg = edge->first;
230*37da2899SCharles.Forsyth           AH_Segment  seg2;
231*37da2899SCharles.Forsyth 
232*37da2899SCharles.Forsyth 
233*37da2899SCharles.Forsyth           do
234*37da2899SCharles.Forsyth           {
235*37da2899SCharles.Forsyth             seg2 = seg->link;
236*37da2899SCharles.Forsyth             if ( valid_stem_segments( seg, seg2 ) )
237*37da2899SCharles.Forsyth             {
238*37da2899SCharles.Forsyth               AH_Edge  edge1 = seg->edge;
239*37da2899SCharles.Forsyth               AH_Edge  edge2 = seg2->edge;
240*37da2899SCharles.Forsyth 
241*37da2899SCharles.Forsyth 
242*37da2899SCharles.Forsyth               stem->edge1  = edge1;
243*37da2899SCharles.Forsyth               stem->edge2  = edge2;
244*37da2899SCharles.Forsyth               stem->opos   = edge1->opos;
245*37da2899SCharles.Forsyth               stem->pos    = edge1->pos;
246*37da2899SCharles.Forsyth               stem->owidth = edge2->opos - edge1->opos;
247*37da2899SCharles.Forsyth               stem->width  = edge2->pos  - edge1->pos;
248*37da2899SCharles.Forsyth 
249*37da2899SCharles.Forsyth               /* compute min_coord and max_coord */
250*37da2899SCharles.Forsyth               {
251*37da2899SCharles.Forsyth                 FT_Pos  min_coord = seg->min_coord;
252*37da2899SCharles.Forsyth                 FT_Pos  max_coord = seg->max_coord;
253*37da2899SCharles.Forsyth 
254*37da2899SCharles.Forsyth 
255*37da2899SCharles.Forsyth                 if ( seg2->min_coord > min_coord )
256*37da2899SCharles.Forsyth                   min_coord = seg2->min_coord;
257*37da2899SCharles.Forsyth 
258*37da2899SCharles.Forsyth                 if ( seg2->max_coord < max_coord )
259*37da2899SCharles.Forsyth                   max_coord = seg2->max_coord;
260*37da2899SCharles.Forsyth 
261*37da2899SCharles.Forsyth                 stem->min_coord = min_coord;
262*37da2899SCharles.Forsyth                 stem->max_coord = max_coord;
263*37da2899SCharles.Forsyth               }
264*37da2899SCharles.Forsyth 
265*37da2899SCharles.Forsyth               /* compute minimum and maximum positions for stem --   */
266*37da2899SCharles.Forsyth               /* note that the left-most/bottom-most stem has always */
267*37da2899SCharles.Forsyth               /* a fixed position                                    */
268*37da2899SCharles.Forsyth               if ( stem == stems || edge1->blue_edge || edge2->blue_edge )
269*37da2899SCharles.Forsyth               {
270*37da2899SCharles.Forsyth                 /* this stem cannot move; it is snapped to a blue edge */
271*37da2899SCharles.Forsyth                 stem->min_pos = stem->pos;
272*37da2899SCharles.Forsyth                 stem->max_pos = stem->pos;
273*37da2899SCharles.Forsyth               }
274*37da2899SCharles.Forsyth               else
275*37da2899SCharles.Forsyth               {
276*37da2899SCharles.Forsyth                 /* this edge can move; compute its min and max positions */
277*37da2899SCharles.Forsyth                 FT_Pos  pos1 = stem->opos;
278*37da2899SCharles.Forsyth                 FT_Pos  pos2 = pos1 + stem->owidth - stem->width;
279*37da2899SCharles.Forsyth                 FT_Pos  min1 = pos1 & -64;
280*37da2899SCharles.Forsyth                 FT_Pos  min2 = pos2 & -64;
281*37da2899SCharles.Forsyth 
282*37da2899SCharles.Forsyth 
283*37da2899SCharles.Forsyth                 stem->min_pos = min1;
284*37da2899SCharles.Forsyth                 stem->max_pos = min1 + 64;
285*37da2899SCharles.Forsyth                 if ( min2 < min1 )
286*37da2899SCharles.Forsyth                   stem->min_pos = min2;
287*37da2899SCharles.Forsyth                 else
288*37da2899SCharles.Forsyth                   stem->max_pos = min2 + 64;
289*37da2899SCharles.Forsyth 
290*37da2899SCharles.Forsyth                 /* XXX: just to see what it does */
291*37da2899SCharles.Forsyth                 stem->max_pos += 64;
292*37da2899SCharles.Forsyth 
293*37da2899SCharles.Forsyth                 /* just for the case where direct hinting did some */
294*37da2899SCharles.Forsyth                 /* incredible things (e.g. blue edge shifts)       */
295*37da2899SCharles.Forsyth                 if ( stem->min_pos > stem->pos )
296*37da2899SCharles.Forsyth                   stem->min_pos = stem->pos;
297*37da2899SCharles.Forsyth 
298*37da2899SCharles.Forsyth                 if ( stem->max_pos < stem->pos )
299*37da2899SCharles.Forsyth                   stem->max_pos = stem->pos;
300*37da2899SCharles.Forsyth               }
301*37da2899SCharles.Forsyth 
302*37da2899SCharles.Forsyth               stem->velocity = 0;
303*37da2899SCharles.Forsyth               stem->force    = 0;
304*37da2899SCharles.Forsyth 
305*37da2899SCharles.Forsyth               stem++;
306*37da2899SCharles.Forsyth             }
307*37da2899SCharles.Forsyth             seg = seg->edge_next;
308*37da2899SCharles.Forsyth 
309*37da2899SCharles.Forsyth           } while ( seg != edge->first );
310*37da2899SCharles.Forsyth         }
311*37da2899SCharles.Forsyth       }
312*37da2899SCharles.Forsyth 
313*37da2899SCharles.Forsyth       *p_stems     = stems;
314*37da2899SCharles.Forsyth       *p_num_stems = num_stems;
315*37da2899SCharles.Forsyth 
316*37da2899SCharles.Forsyth       edges      = outline->vert_edges;
317*37da2899SCharles.Forsyth       edge_limit = edges + outline->num_vedges;
318*37da2899SCharles.Forsyth       scale      = outline->x_scale;
319*37da2899SCharles.Forsyth 
320*37da2899SCharles.Forsyth       p_stems     = &optimizer->vert_stems;
321*37da2899SCharles.Forsyth       p_num_stems = &optimizer->num_vstems;
322*37da2899SCharles.Forsyth     }
323*37da2899SCharles.Forsyth 
324*37da2899SCharles.Forsyth   Exit:
325*37da2899SCharles.Forsyth 
326*37da2899SCharles.Forsyth #ifdef AH_DEBUG_OPTIM
327*37da2899SCharles.Forsyth     AH_Dump_Stems( optimizer );
328*37da2899SCharles.Forsyth #endif
329*37da2899SCharles.Forsyth 
330*37da2899SCharles.Forsyth     return error;
331*37da2899SCharles.Forsyth   }
332*37da2899SCharles.Forsyth 
333*37da2899SCharles.Forsyth 
334*37da2899SCharles.Forsyth   /* returns the spring area between two stems, 0 if none */
335*37da2899SCharles.Forsyth   static FT_Pos
stem_spring_area(AH_Stem * stem1,AH_Stem * stem2)336*37da2899SCharles.Forsyth   stem_spring_area( AH_Stem*  stem1,
337*37da2899SCharles.Forsyth                     AH_Stem*  stem2 )
338*37da2899SCharles.Forsyth   {
339*37da2899SCharles.Forsyth     FT_Pos  area1 = stem1->max_coord - stem1->min_coord;
340*37da2899SCharles.Forsyth     FT_Pos  area2 = stem2->max_coord - stem2->min_coord;
341*37da2899SCharles.Forsyth     FT_Pos  min   = stem1->min_coord;
342*37da2899SCharles.Forsyth     FT_Pos  max   = stem1->max_coord;
343*37da2899SCharles.Forsyth     FT_Pos  area;
344*37da2899SCharles.Forsyth 
345*37da2899SCharles.Forsyth 
346*37da2899SCharles.Forsyth     /* order stems */
347*37da2899SCharles.Forsyth     if ( stem2->opos <= stem1->opos + stem1->owidth )
348*37da2899SCharles.Forsyth       return 0;
349*37da2899SCharles.Forsyth 
350*37da2899SCharles.Forsyth     if ( min < stem2->min_coord )
351*37da2899SCharles.Forsyth       min = stem2->min_coord;
352*37da2899SCharles.Forsyth 
353*37da2899SCharles.Forsyth     if ( max < stem2->max_coord )
354*37da2899SCharles.Forsyth       max = stem2->max_coord;
355*37da2899SCharles.Forsyth 
356*37da2899SCharles.Forsyth     area = ( max-min );
357*37da2899SCharles.Forsyth     if ( 2 * area < area1 && 2 * area < area2 )
358*37da2899SCharles.Forsyth       area = 0;
359*37da2899SCharles.Forsyth 
360*37da2899SCharles.Forsyth     return area;
361*37da2899SCharles.Forsyth   }
362*37da2899SCharles.Forsyth 
363*37da2899SCharles.Forsyth 
364*37da2899SCharles.Forsyth   /* compute all springs in an outline */
365*37da2899SCharles.Forsyth   static int
optim_compute_springs(AH_Optimizer * optimizer)366*37da2899SCharles.Forsyth   optim_compute_springs( AH_Optimizer*  optimizer )
367*37da2899SCharles.Forsyth   {
368*37da2899SCharles.Forsyth     /* basically, a spring exists between two stems if most of their */
369*37da2899SCharles.Forsyth     /* surface is aligned                                            */
370*37da2899SCharles.Forsyth     FT_Memory    memory  = optimizer->memory;
371*37da2899SCharles.Forsyth 
372*37da2899SCharles.Forsyth     AH_Stem*     stems;
373*37da2899SCharles.Forsyth     AH_Stem*     stem_limit;
374*37da2899SCharles.Forsyth     AH_Stem*     stem;
375*37da2899SCharles.Forsyth     int          dimension;
376*37da2899SCharles.Forsyth     int          error = 0;
377*37da2899SCharles.Forsyth 
378*37da2899SCharles.Forsyth     FT_Int*      p_num_springs;
379*37da2899SCharles.Forsyth     AH_Spring**  p_springs;
380*37da2899SCharles.Forsyth 
381*37da2899SCharles.Forsyth 
382*37da2899SCharles.Forsyth     stems      = optimizer->horz_stems;
383*37da2899SCharles.Forsyth     stem_limit = stems + optimizer->num_hstems;
384*37da2899SCharles.Forsyth 
385*37da2899SCharles.Forsyth     p_springs     = &optimizer->horz_springs;
386*37da2899SCharles.Forsyth     p_num_springs = &optimizer->num_hsprings;
387*37da2899SCharles.Forsyth 
388*37da2899SCharles.Forsyth     for ( dimension = 1; dimension >= 0; dimension-- )
389*37da2899SCharles.Forsyth     {
390*37da2899SCharles.Forsyth       FT_Int      num_springs = 0;
391*37da2899SCharles.Forsyth       AH_Spring*  springs     = 0;
392*37da2899SCharles.Forsyth 
393*37da2899SCharles.Forsyth 
394*37da2899SCharles.Forsyth       /* first of all, count stem springs */
395*37da2899SCharles.Forsyth       for ( stem = stems; stem + 1 < stem_limit; stem++ )
396*37da2899SCharles.Forsyth       {
397*37da2899SCharles.Forsyth         AH_Stem*  stem2;
398*37da2899SCharles.Forsyth 
399*37da2899SCharles.Forsyth 
400*37da2899SCharles.Forsyth         for ( stem2 = stem+1; stem2 < stem_limit; stem2++ )
401*37da2899SCharles.Forsyth           if ( stem_spring_area( stem, stem2 ) )
402*37da2899SCharles.Forsyth             num_springs++;
403*37da2899SCharles.Forsyth       }
404*37da2899SCharles.Forsyth 
405*37da2899SCharles.Forsyth       /* then allocate and build the springs table */
406*37da2899SCharles.Forsyth       if ( num_springs > 0 )
407*37da2899SCharles.Forsyth       {
408*37da2899SCharles.Forsyth         AH_Spring*  spring;
409*37da2899SCharles.Forsyth 
410*37da2899SCharles.Forsyth 
411*37da2899SCharles.Forsyth         /* allocate table of springs */
412*37da2899SCharles.Forsyth         if ( FT_NEW_ARRAY( springs, num_springs ) )
413*37da2899SCharles.Forsyth           goto Exit;
414*37da2899SCharles.Forsyth 
415*37da2899SCharles.Forsyth         /* fill the springs table */
416*37da2899SCharles.Forsyth         spring = springs;
417*37da2899SCharles.Forsyth         for ( stem = stems; stem+1 < stem_limit; stem++ )
418*37da2899SCharles.Forsyth         {
419*37da2899SCharles.Forsyth           AH_Stem*  stem2;
420*37da2899SCharles.Forsyth           FT_Pos    area;
421*37da2899SCharles.Forsyth 
422*37da2899SCharles.Forsyth 
423*37da2899SCharles.Forsyth           for ( stem2 = stem + 1; stem2 < stem_limit; stem2++ )
424*37da2899SCharles.Forsyth           {
425*37da2899SCharles.Forsyth             area = stem_spring_area( stem, stem2 );
426*37da2899SCharles.Forsyth             if ( area )
427*37da2899SCharles.Forsyth             {
428*37da2899SCharles.Forsyth               /* add a new spring here */
429*37da2899SCharles.Forsyth               spring->stem1   = stem;
430*37da2899SCharles.Forsyth               spring->stem2   = stem2;
431*37da2899SCharles.Forsyth               spring->owidth  = stem2->opos - ( stem->opos + stem->owidth );
432*37da2899SCharles.Forsyth               spring->tension = 0;
433*37da2899SCharles.Forsyth 
434*37da2899SCharles.Forsyth               spring++;
435*37da2899SCharles.Forsyth             }
436*37da2899SCharles.Forsyth           }
437*37da2899SCharles.Forsyth         }
438*37da2899SCharles.Forsyth       }
439*37da2899SCharles.Forsyth       *p_num_springs = num_springs;
440*37da2899SCharles.Forsyth       *p_springs     = springs;
441*37da2899SCharles.Forsyth 
442*37da2899SCharles.Forsyth       stems      = optimizer->vert_stems;
443*37da2899SCharles.Forsyth       stem_limit = stems + optimizer->num_vstems;
444*37da2899SCharles.Forsyth 
445*37da2899SCharles.Forsyth       p_springs     = &optimizer->vert_springs;
446*37da2899SCharles.Forsyth       p_num_springs = &optimizer->num_vsprings;
447*37da2899SCharles.Forsyth     }
448*37da2899SCharles.Forsyth 
449*37da2899SCharles.Forsyth   Exit:
450*37da2899SCharles.Forsyth 
451*37da2899SCharles.Forsyth #ifdef AH_DEBUG_OPTIM
452*37da2899SCharles.Forsyth     AH_Dump_Springs( optimizer );
453*37da2899SCharles.Forsyth #endif
454*37da2899SCharles.Forsyth 
455*37da2899SCharles.Forsyth     return error;
456*37da2899SCharles.Forsyth   }
457*37da2899SCharles.Forsyth 
458*37da2899SCharles.Forsyth 
459*37da2899SCharles.Forsyth   /*************************************************************************/
460*37da2899SCharles.Forsyth   /*************************************************************************/
461*37da2899SCharles.Forsyth   /*************************************************************************/
462*37da2899SCharles.Forsyth   /****                                                                 ****/
463*37da2899SCharles.Forsyth   /****   OPTIMIZE THROUGH MY STRANGE SIMULATED ANNEALING ALGO ;-)      ****/
464*37da2899SCharles.Forsyth   /****                                                                 ****/
465*37da2899SCharles.Forsyth   /*************************************************************************/
466*37da2899SCharles.Forsyth   /*************************************************************************/
467*37da2899SCharles.Forsyth   /*************************************************************************/
468*37da2899SCharles.Forsyth 
469*37da2899SCharles.Forsyth #ifndef AH_BRUTE_FORCE
470*37da2899SCharles.Forsyth 
471*37da2899SCharles.Forsyth   /* compute all spring tensions */
472*37da2899SCharles.Forsyth   static void
optim_compute_tensions(AH_Optimizer * optimizer)473*37da2899SCharles.Forsyth   optim_compute_tensions( AH_Optimizer*  optimizer )
474*37da2899SCharles.Forsyth   {
475*37da2899SCharles.Forsyth     AH_Spring*  spring = optimizer->springs;
476*37da2899SCharles.Forsyth     AH_Spring*  limit  = spring + optimizer->num_springs;
477*37da2899SCharles.Forsyth 
478*37da2899SCharles.Forsyth 
479*37da2899SCharles.Forsyth     for ( ; spring < limit; spring++ )
480*37da2899SCharles.Forsyth     {
481*37da2899SCharles.Forsyth       AH_Stem*  stem1 = spring->stem1;
482*37da2899SCharles.Forsyth       AH_Stem*  stem2 = spring->stem2;
483*37da2899SCharles.Forsyth       FT_Int    status;
484*37da2899SCharles.Forsyth 
485*37da2899SCharles.Forsyth       FT_Pos  width;
486*37da2899SCharles.Forsyth       FT_Pos  tension;
487*37da2899SCharles.Forsyth       FT_Pos  sign;
488*37da2899SCharles.Forsyth 
489*37da2899SCharles.Forsyth 
490*37da2899SCharles.Forsyth       /* compute the tension; it simply is -K*(new_width-old_width) */
491*37da2899SCharles.Forsyth       width   = stem2->pos - ( stem1->pos + stem1->width );
492*37da2899SCharles.Forsyth       tension = width - spring->owidth;
493*37da2899SCharles.Forsyth 
494*37da2899SCharles.Forsyth       sign = 1;
495*37da2899SCharles.Forsyth       if ( tension < 0 )
496*37da2899SCharles.Forsyth       {
497*37da2899SCharles.Forsyth         sign    = -1;
498*37da2899SCharles.Forsyth         tension = -tension;
499*37da2899SCharles.Forsyth       }
500*37da2899SCharles.Forsyth 
501*37da2899SCharles.Forsyth       if ( width <= 0 )
502*37da2899SCharles.Forsyth         tension = 32000;
503*37da2899SCharles.Forsyth       else
504*37da2899SCharles.Forsyth         tension = ( tension << 10 ) / width;
505*37da2899SCharles.Forsyth 
506*37da2899SCharles.Forsyth       tension = -sign * FT_MulFix( tension, optimizer->tension_scale );
507*37da2899SCharles.Forsyth       spring->tension = tension;
508*37da2899SCharles.Forsyth 
509*37da2899SCharles.Forsyth       /* now, distribute tension among the englobing stems, if they */
510*37da2899SCharles.Forsyth       /* are able to move                                           */
511*37da2899SCharles.Forsyth       status = 0;
512*37da2899SCharles.Forsyth       if ( stem1->pos <= stem1->min_pos )
513*37da2899SCharles.Forsyth         status |= 1;
514*37da2899SCharles.Forsyth       if ( stem2->pos >= stem2->max_pos )
515*37da2899SCharles.Forsyth         status |= 2;
516*37da2899SCharles.Forsyth 
517*37da2899SCharles.Forsyth       if ( !status )
518*37da2899SCharles.Forsyth         tension /= 2;
519*37da2899SCharles.Forsyth 
520*37da2899SCharles.Forsyth       if ( ( status & 1 ) == 0 )
521*37da2899SCharles.Forsyth         stem1->force -= tension;
522*37da2899SCharles.Forsyth 
523*37da2899SCharles.Forsyth       if ( ( status & 2 ) == 0 )
524*37da2899SCharles.Forsyth         stem2->force += tension;
525*37da2899SCharles.Forsyth     }
526*37da2899SCharles.Forsyth   }
527*37da2899SCharles.Forsyth 
528*37da2899SCharles.Forsyth 
529*37da2899SCharles.Forsyth   /* compute all stem movements -- returns 0 if nothing moved */
530*37da2899SCharles.Forsyth   static int
optim_compute_stem_movements(AH_Optimizer * optimizer)531*37da2899SCharles.Forsyth   optim_compute_stem_movements( AH_Optimizer*  optimizer )
532*37da2899SCharles.Forsyth   {
533*37da2899SCharles.Forsyth     AH_Stem*  stems = optimizer->stems;
534*37da2899SCharles.Forsyth     AH_Stem*  limit = stems + optimizer->num_stems;
535*37da2899SCharles.Forsyth     AH_Stem*  stem  = stems;
536*37da2899SCharles.Forsyth     int       moved = 0;
537*37da2899SCharles.Forsyth 
538*37da2899SCharles.Forsyth 
539*37da2899SCharles.Forsyth     /* set initial forces to velocity */
540*37da2899SCharles.Forsyth     for ( stem = stems; stem < limit; stem++ )
541*37da2899SCharles.Forsyth     {
542*37da2899SCharles.Forsyth       stem->force     = stem->velocity;
543*37da2899SCharles.Forsyth       stem->velocity /= 2;                  /* XXX: Heuristics */
544*37da2899SCharles.Forsyth     }
545*37da2899SCharles.Forsyth 
546*37da2899SCharles.Forsyth     /* compute the sum of forces applied on each stem */
547*37da2899SCharles.Forsyth     optim_compute_tensions( optimizer );
548*37da2899SCharles.Forsyth 
549*37da2899SCharles.Forsyth #ifdef AH_DEBUG_OPTIM
550*37da2899SCharles.Forsyth     AH_Dump_Springs( optimizer );
551*37da2899SCharles.Forsyth     AH_Dump_Stems2( optimizer );
552*37da2899SCharles.Forsyth #endif
553*37da2899SCharles.Forsyth 
554*37da2899SCharles.Forsyth     /* now, see whether something can move */
555*37da2899SCharles.Forsyth     for ( stem = stems; stem < limit; stem++ )
556*37da2899SCharles.Forsyth     {
557*37da2899SCharles.Forsyth       if ( stem->force > optimizer->tension_threshold )
558*37da2899SCharles.Forsyth       {
559*37da2899SCharles.Forsyth         /* there is enough tension to move the stem to the right */
560*37da2899SCharles.Forsyth         if ( stem->pos < stem->max_pos )
561*37da2899SCharles.Forsyth         {
562*37da2899SCharles.Forsyth           stem->pos     += 64;
563*37da2899SCharles.Forsyth           stem->velocity = stem->force / 2;
564*37da2899SCharles.Forsyth           moved          = 1;
565*37da2899SCharles.Forsyth         }
566*37da2899SCharles.Forsyth         else
567*37da2899SCharles.Forsyth           stem->velocity = 0;
568*37da2899SCharles.Forsyth       }
569*37da2899SCharles.Forsyth       else if ( stem->force < optimizer->tension_threshold )
570*37da2899SCharles.Forsyth       {
571*37da2899SCharles.Forsyth         /* there is enough tension to move the stem to the left */
572*37da2899SCharles.Forsyth         if ( stem->pos > stem->min_pos )
573*37da2899SCharles.Forsyth         {
574*37da2899SCharles.Forsyth           stem->pos     -= 64;
575*37da2899SCharles.Forsyth           stem->velocity = stem->force / 2;
576*37da2899SCharles.Forsyth           moved          = 1;
577*37da2899SCharles.Forsyth         }
578*37da2899SCharles.Forsyth         else
579*37da2899SCharles.Forsyth           stem->velocity = 0;
580*37da2899SCharles.Forsyth       }
581*37da2899SCharles.Forsyth     }
582*37da2899SCharles.Forsyth 
583*37da2899SCharles.Forsyth     /* return 0 if nothing moved */
584*37da2899SCharles.Forsyth     return moved;
585*37da2899SCharles.Forsyth   }
586*37da2899SCharles.Forsyth 
587*37da2899SCharles.Forsyth #endif /* AH_BRUTE_FORCE */
588*37da2899SCharles.Forsyth 
589*37da2899SCharles.Forsyth 
590*37da2899SCharles.Forsyth   /* compute current global distortion from springs */
591*37da2899SCharles.Forsyth   static FT_Pos
optim_compute_distortion(AH_Optimizer * optimizer)592*37da2899SCharles.Forsyth   optim_compute_distortion( AH_Optimizer*  optimizer )
593*37da2899SCharles.Forsyth   {
594*37da2899SCharles.Forsyth     AH_Spring*  spring = optimizer->springs;
595*37da2899SCharles.Forsyth     AH_Spring*  limit  = spring + optimizer->num_springs;
596*37da2899SCharles.Forsyth     FT_Pos      distortion = 0;
597*37da2899SCharles.Forsyth 
598*37da2899SCharles.Forsyth 
599*37da2899SCharles.Forsyth     for ( ; spring < limit; spring++ )
600*37da2899SCharles.Forsyth     {
601*37da2899SCharles.Forsyth       AH_Stem*  stem1 = spring->stem1;
602*37da2899SCharles.Forsyth       AH_Stem*  stem2 = spring->stem2;
603*37da2899SCharles.Forsyth       FT_Pos  width;
604*37da2899SCharles.Forsyth 
605*37da2899SCharles.Forsyth       width  = stem2->pos - ( stem1->pos + stem1->width );
606*37da2899SCharles.Forsyth       width -= spring->owidth;
607*37da2899SCharles.Forsyth       if ( width < 0 )
608*37da2899SCharles.Forsyth         width = -width;
609*37da2899SCharles.Forsyth 
610*37da2899SCharles.Forsyth       distortion += width;
611*37da2899SCharles.Forsyth     }
612*37da2899SCharles.Forsyth 
613*37da2899SCharles.Forsyth     return distortion;
614*37da2899SCharles.Forsyth   }
615*37da2899SCharles.Forsyth 
616*37da2899SCharles.Forsyth 
617*37da2899SCharles.Forsyth   /* record stems configuration in `best of' history */
618*37da2899SCharles.Forsyth   static void
optim_record_configuration(AH_Optimizer * optimizer)619*37da2899SCharles.Forsyth   optim_record_configuration( AH_Optimizer*  optimizer )
620*37da2899SCharles.Forsyth   {
621*37da2899SCharles.Forsyth     FT_Pos             distortion;
622*37da2899SCharles.Forsyth     AH_Configuration*  configs = optimizer->configs;
623*37da2899SCharles.Forsyth     AH_Configuration*  limit   = configs + optimizer->num_configs;
624*37da2899SCharles.Forsyth     AH_Configuration*  config;
625*37da2899SCharles.Forsyth 
626*37da2899SCharles.Forsyth 
627*37da2899SCharles.Forsyth     distortion = optim_compute_distortion( optimizer );
628*37da2899SCharles.Forsyth     LOG(( "config distortion = %.1f ", FLOAT( distortion * 64 ) ));
629*37da2899SCharles.Forsyth 
630*37da2899SCharles.Forsyth     /* check that we really need to add this configuration to our */
631*37da2899SCharles.Forsyth     /* sorted history                                             */
632*37da2899SCharles.Forsyth     if ( limit > configs && limit[-1].distortion < distortion )
633*37da2899SCharles.Forsyth     {
634*37da2899SCharles.Forsyth       LOG(( "ejected\n" ));
635*37da2899SCharles.Forsyth       return;
636*37da2899SCharles.Forsyth     }
637*37da2899SCharles.Forsyth 
638*37da2899SCharles.Forsyth     /* add new configuration at the end of the table */
639*37da2899SCharles.Forsyth     {
640*37da2899SCharles.Forsyth       int  n;
641*37da2899SCharles.Forsyth 
642*37da2899SCharles.Forsyth 
643*37da2899SCharles.Forsyth       config = limit;
644*37da2899SCharles.Forsyth       if ( optimizer->num_configs < AH_MAX_CONFIGS )
645*37da2899SCharles.Forsyth         optimizer->num_configs++;
646*37da2899SCharles.Forsyth       else
647*37da2899SCharles.Forsyth         config--;
648*37da2899SCharles.Forsyth 
649*37da2899SCharles.Forsyth       config->distortion = distortion;
650*37da2899SCharles.Forsyth 
651*37da2899SCharles.Forsyth       for ( n = 0; n < optimizer->num_stems; n++ )
652*37da2899SCharles.Forsyth         config->positions[n] = optimizer->stems[n].pos;
653*37da2899SCharles.Forsyth     }
654*37da2899SCharles.Forsyth 
655*37da2899SCharles.Forsyth     /* move the current configuration towards the front of the list */
656*37da2899SCharles.Forsyth     /* when necessary -- yes this is slow bubble sort ;-)           */
657*37da2899SCharles.Forsyth     while ( config > configs && config[0].distortion < config[-1].distortion )
658*37da2899SCharles.Forsyth     {
659*37da2899SCharles.Forsyth       AH_Configuration  temp;
660*37da2899SCharles.Forsyth 
661*37da2899SCharles.Forsyth 
662*37da2899SCharles.Forsyth       config--;
663*37da2899SCharles.Forsyth       temp      = config[0];
664*37da2899SCharles.Forsyth       config[0] = config[1];
665*37da2899SCharles.Forsyth       config[1] = temp;
666*37da2899SCharles.Forsyth     }
667*37da2899SCharles.Forsyth     LOG(( "recorded!\n" ));
668*37da2899SCharles.Forsyth   }
669*37da2899SCharles.Forsyth 
670*37da2899SCharles.Forsyth 
671*37da2899SCharles.Forsyth #ifdef AH_BRUTE_FORCE
672*37da2899SCharles.Forsyth 
673*37da2899SCharles.Forsyth   /* optimize outline in a single direction */
674*37da2899SCharles.Forsyth   static void
optim_compute(AH_Optimizer * optimizer)675*37da2899SCharles.Forsyth   optim_compute( AH_Optimizer*  optimizer )
676*37da2899SCharles.Forsyth   {
677*37da2899SCharles.Forsyth     int       n;
678*37da2899SCharles.Forsyth     FT_Bool   moved;
679*37da2899SCharles.Forsyth 
680*37da2899SCharles.Forsyth     AH_Stem*  stem  = optimizer->stems;
681*37da2899SCharles.Forsyth     AH_Stem*  limit = stem + optimizer->num_stems;
682*37da2899SCharles.Forsyth 
683*37da2899SCharles.Forsyth 
684*37da2899SCharles.Forsyth     /* empty, exit */
685*37da2899SCharles.Forsyth     if ( stem >= limit )
686*37da2899SCharles.Forsyth       return;
687*37da2899SCharles.Forsyth 
688*37da2899SCharles.Forsyth     optimizer->num_configs = 0;
689*37da2899SCharles.Forsyth 
690*37da2899SCharles.Forsyth     stem = optimizer->stems;
691*37da2899SCharles.Forsyth     for ( ; stem < limit; stem++ )
692*37da2899SCharles.Forsyth       stem->pos = stem->min_pos;
693*37da2899SCharles.Forsyth 
694*37da2899SCharles.Forsyth     do
695*37da2899SCharles.Forsyth     {
696*37da2899SCharles.Forsyth       /* record current configuration */
697*37da2899SCharles.Forsyth       optim_record_configuration( optimizer );
698*37da2899SCharles.Forsyth 
699*37da2899SCharles.Forsyth       /* now change configuration */
700*37da2899SCharles.Forsyth       moved = 0;
701*37da2899SCharles.Forsyth       for ( stem = optimizer->stems; stem < limit; stem++ )
702*37da2899SCharles.Forsyth       {
703*37da2899SCharles.Forsyth         if ( stem->pos < stem->max_pos )
704*37da2899SCharles.Forsyth         {
705*37da2899SCharles.Forsyth           stem->pos += 64;
706*37da2899SCharles.Forsyth           moved      = 1;
707*37da2899SCharles.Forsyth           break;
708*37da2899SCharles.Forsyth         }
709*37da2899SCharles.Forsyth 
710*37da2899SCharles.Forsyth         stem->pos = stem->min_pos;
711*37da2899SCharles.Forsyth       }
712*37da2899SCharles.Forsyth     } while ( moved );
713*37da2899SCharles.Forsyth 
714*37da2899SCharles.Forsyth     /* now, set the best stem positions */
715*37da2899SCharles.Forsyth     for ( n = 0; n < optimizer->num_stems; n++ )
716*37da2899SCharles.Forsyth     {
717*37da2899SCharles.Forsyth       AH_Stem*  stem = optimizer->stems + n;
718*37da2899SCharles.Forsyth       FT_Pos    pos  = optimizer->configs[0].positions[n];
719*37da2899SCharles.Forsyth 
720*37da2899SCharles.Forsyth 
721*37da2899SCharles.Forsyth       stem->edge1->pos = pos;
722*37da2899SCharles.Forsyth       stem->edge2->pos = pos + stem->width;
723*37da2899SCharles.Forsyth 
724*37da2899SCharles.Forsyth       stem->edge1->flags |= AH_EDGE_DONE;
725*37da2899SCharles.Forsyth       stem->edge2->flags |= AH_EDGE_DONE;
726*37da2899SCharles.Forsyth     }
727*37da2899SCharles.Forsyth   }
728*37da2899SCharles.Forsyth 
729*37da2899SCharles.Forsyth #else /* AH_BRUTE_FORCE */
730*37da2899SCharles.Forsyth 
731*37da2899SCharles.Forsyth   /* optimize outline in a single direction */
732*37da2899SCharles.Forsyth   static void
optim_compute(AH_Optimizer * optimizer)733*37da2899SCharles.Forsyth   optim_compute( AH_Optimizer*  optimizer )
734*37da2899SCharles.Forsyth   {
735*37da2899SCharles.Forsyth     int  n, counter, counter2;
736*37da2899SCharles.Forsyth 
737*37da2899SCharles.Forsyth 
738*37da2899SCharles.Forsyth     optimizer->num_configs       = 0;
739*37da2899SCharles.Forsyth     optimizer->tension_scale     = 0x80000L;
740*37da2899SCharles.Forsyth     optimizer->tension_threshold = 64;
741*37da2899SCharles.Forsyth 
742*37da2899SCharles.Forsyth     /* record initial configuration threshold */
743*37da2899SCharles.Forsyth     optim_record_configuration( optimizer );
744*37da2899SCharles.Forsyth 
745*37da2899SCharles.Forsyth     counter = 0;
746*37da2899SCharles.Forsyth     for ( counter2 = optimizer->num_stems*8; counter2 >= 0; counter2-- )
747*37da2899SCharles.Forsyth     {
748*37da2899SCharles.Forsyth       if ( counter == 0 )
749*37da2899SCharles.Forsyth         counter = 2 * optimizer->num_stems;
750*37da2899SCharles.Forsyth 
751*37da2899SCharles.Forsyth       if ( !optim_compute_stem_movements( optimizer ) )
752*37da2899SCharles.Forsyth         break;
753*37da2899SCharles.Forsyth 
754*37da2899SCharles.Forsyth       optim_record_configuration( optimizer );
755*37da2899SCharles.Forsyth 
756*37da2899SCharles.Forsyth       counter--;
757*37da2899SCharles.Forsyth       if ( counter == 0 )
758*37da2899SCharles.Forsyth         optimizer->tension_scale /= 2;
759*37da2899SCharles.Forsyth     }
760*37da2899SCharles.Forsyth 
761*37da2899SCharles.Forsyth     /* now, set the best stem positions */
762*37da2899SCharles.Forsyth     for ( n = 0; n < optimizer->num_stems; n++ )
763*37da2899SCharles.Forsyth     {
764*37da2899SCharles.Forsyth       AH_Stem*  stem = optimizer->stems + n;
765*37da2899SCharles.Forsyth       FT_Pos    pos  = optimizer->configs[0].positions[n];
766*37da2899SCharles.Forsyth 
767*37da2899SCharles.Forsyth 
768*37da2899SCharles.Forsyth       stem->edge1->pos = pos;
769*37da2899SCharles.Forsyth       stem->edge2->pos = pos + stem->width;
770*37da2899SCharles.Forsyth 
771*37da2899SCharles.Forsyth       stem->edge1->flags |= AH_EDGE_DONE;
772*37da2899SCharles.Forsyth       stem->edge2->flags |= AH_EDGE_DONE;
773*37da2899SCharles.Forsyth     }
774*37da2899SCharles.Forsyth   }
775*37da2899SCharles.Forsyth 
776*37da2899SCharles.Forsyth #endif /* AH_BRUTE_FORCE */
777*37da2899SCharles.Forsyth 
778*37da2899SCharles.Forsyth 
779*37da2899SCharles.Forsyth   /*************************************************************************/
780*37da2899SCharles.Forsyth   /*************************************************************************/
781*37da2899SCharles.Forsyth   /*************************************************************************/
782*37da2899SCharles.Forsyth   /****                                                                 ****/
783*37da2899SCharles.Forsyth   /****   HIGH-LEVEL OPTIMIZER API                                      ****/
784*37da2899SCharles.Forsyth   /****                                                                 ****/
785*37da2899SCharles.Forsyth   /*************************************************************************/
786*37da2899SCharles.Forsyth   /*************************************************************************/
787*37da2899SCharles.Forsyth   /*************************************************************************/
788*37da2899SCharles.Forsyth 
789*37da2899SCharles.Forsyth 
790*37da2899SCharles.Forsyth   /* releases the optimization data */
791*37da2899SCharles.Forsyth   void
AH_Optimizer_Done(AH_Optimizer * optimizer)792*37da2899SCharles.Forsyth   AH_Optimizer_Done( AH_Optimizer*  optimizer )
793*37da2899SCharles.Forsyth   {
794*37da2899SCharles.Forsyth     if ( optimizer )
795*37da2899SCharles.Forsyth     {
796*37da2899SCharles.Forsyth       FT_Memory  memory = optimizer->memory;
797*37da2899SCharles.Forsyth 
798*37da2899SCharles.Forsyth 
799*37da2899SCharles.Forsyth       FT_FREE( optimizer->horz_stems );
800*37da2899SCharles.Forsyth       FT_FREE( optimizer->vert_stems );
801*37da2899SCharles.Forsyth       FT_FREE( optimizer->horz_springs );
802*37da2899SCharles.Forsyth       FT_FREE( optimizer->vert_springs );
803*37da2899SCharles.Forsyth       FT_FREE( optimizer->positions );
804*37da2899SCharles.Forsyth     }
805*37da2899SCharles.Forsyth   }
806*37da2899SCharles.Forsyth 
807*37da2899SCharles.Forsyth 
808*37da2899SCharles.Forsyth   /* loads the outline into the optimizer */
809*37da2899SCharles.Forsyth   int
AH_Optimizer_Init(AH_Optimizer * optimizer,AH_Outline outline,FT_Memory memory)810*37da2899SCharles.Forsyth   AH_Optimizer_Init( AH_Optimizer*  optimizer,
811*37da2899SCharles.Forsyth                      AH_Outline     outline,
812*37da2899SCharles.Forsyth                      FT_Memory      memory )
813*37da2899SCharles.Forsyth   {
814*37da2899SCharles.Forsyth     FT_Error  error;
815*37da2899SCharles.Forsyth 
816*37da2899SCharles.Forsyth 
817*37da2899SCharles.Forsyth     FT_MEM_ZERO( optimizer, sizeof ( *optimizer ) );
818*37da2899SCharles.Forsyth     optimizer->outline = outline;
819*37da2899SCharles.Forsyth     optimizer->memory  = memory;
820*37da2899SCharles.Forsyth 
821*37da2899SCharles.Forsyth     LOG(( "initializing new optimizer\n" ));
822*37da2899SCharles.Forsyth     /* compute stems and springs */
823*37da2899SCharles.Forsyth     error = optim_compute_stems  ( optimizer ) ||
824*37da2899SCharles.Forsyth             optim_compute_springs( optimizer );
825*37da2899SCharles.Forsyth     if ( error )
826*37da2899SCharles.Forsyth       goto Fail;
827*37da2899SCharles.Forsyth 
828*37da2899SCharles.Forsyth     /* allocate stem positions history and configurations */
829*37da2899SCharles.Forsyth     {
830*37da2899SCharles.Forsyth       int  n, max_stems;
831*37da2899SCharles.Forsyth 
832*37da2899SCharles.Forsyth 
833*37da2899SCharles.Forsyth       max_stems = optimizer->num_hstems;
834*37da2899SCharles.Forsyth       if ( max_stems < optimizer->num_vstems )
835*37da2899SCharles.Forsyth         max_stems = optimizer->num_vstems;
836*37da2899SCharles.Forsyth 
837*37da2899SCharles.Forsyth       if ( FT_NEW_ARRAY( optimizer->positions, max_stems * AH_MAX_CONFIGS ) )
838*37da2899SCharles.Forsyth         goto Fail;
839*37da2899SCharles.Forsyth 
840*37da2899SCharles.Forsyth       optimizer->num_configs = 0;
841*37da2899SCharles.Forsyth       for ( n = 0; n < AH_MAX_CONFIGS; n++ )
842*37da2899SCharles.Forsyth         optimizer->configs[n].positions = optimizer->positions +
843*37da2899SCharles.Forsyth                                           n * max_stems;
844*37da2899SCharles.Forsyth     }
845*37da2899SCharles.Forsyth 
846*37da2899SCharles.Forsyth     return error;
847*37da2899SCharles.Forsyth 
848*37da2899SCharles.Forsyth   Fail:
849*37da2899SCharles.Forsyth     AH_Optimizer_Done( optimizer );
850*37da2899SCharles.Forsyth     return error;
851*37da2899SCharles.Forsyth   }
852*37da2899SCharles.Forsyth 
853*37da2899SCharles.Forsyth 
854*37da2899SCharles.Forsyth   /* compute optimal outline */
855*37da2899SCharles.Forsyth   void
AH_Optimizer_Compute(AH_Optimizer * optimizer)856*37da2899SCharles.Forsyth   AH_Optimizer_Compute( AH_Optimizer*  optimizer )
857*37da2899SCharles.Forsyth   {
858*37da2899SCharles.Forsyth     optimizer->num_stems   = optimizer->num_hstems;
859*37da2899SCharles.Forsyth     optimizer->stems       = optimizer->horz_stems;
860*37da2899SCharles.Forsyth     optimizer->num_springs = optimizer->num_hsprings;
861*37da2899SCharles.Forsyth     optimizer->springs     = optimizer->horz_springs;
862*37da2899SCharles.Forsyth 
863*37da2899SCharles.Forsyth     if ( optimizer->num_springs > 0 )
864*37da2899SCharles.Forsyth     {
865*37da2899SCharles.Forsyth       LOG(( "horizontal optimization ------------------------\n" ));
866*37da2899SCharles.Forsyth       optim_compute( optimizer );
867*37da2899SCharles.Forsyth     }
868*37da2899SCharles.Forsyth 
869*37da2899SCharles.Forsyth     optimizer->num_stems   = optimizer->num_vstems;
870*37da2899SCharles.Forsyth     optimizer->stems       = optimizer->vert_stems;
871*37da2899SCharles.Forsyth     optimizer->num_springs = optimizer->num_vsprings;
872*37da2899SCharles.Forsyth     optimizer->springs     = optimizer->vert_springs;
873*37da2899SCharles.Forsyth 
874*37da2899SCharles.Forsyth     if ( optimizer->num_springs )
875*37da2899SCharles.Forsyth     {
876*37da2899SCharles.Forsyth       LOG(( "vertical optimization --------------------------\n" ));
877*37da2899SCharles.Forsyth       optim_compute( optimizer );
878*37da2899SCharles.Forsyth     }
879*37da2899SCharles.Forsyth   }
880*37da2899SCharles.Forsyth 
881*37da2899SCharles.Forsyth 
882*37da2899SCharles.Forsyth /* END */
883