blob: 3a6e0752b09db17eed555876da4b76eb634df517 [file] [log] [blame]
David Turner3469d0d2000-07-19 20:02:14 +00001/***************************************************************************/
2/* */
Werner Lembergc3dd1512000-07-26 14:11:15 +00003/* ahoptim.c */
David Turner3469d0d2000-07-19 20:02:14 +00004/* */
Werner Lembergc3dd1512000-07-26 14:11:15 +00005/* FreeType auto hinting outline optimization (body). */
David Turner3469d0d2000-07-19 20:02:14 +00006/* */
Werner Lemberg415235d2001-06-28 17:49:10 +00007/* Copyright 2000-2001 Catharon Productions Inc. */
David Turner3469d0d2000-07-19 20:02:14 +00008/* Author: David Turner */
9/* */
10/* This file is part of the Catharon Typography Project and shall only */
11/* be used, modified, and distributed under the terms of the Catharon */
12/* Open Source License that should come with this file under the name */
Werner Lembergc3dd1512000-07-26 14:11:15 +000013/* `CatharonLicense.txt'. By continuing to use, modify, or distribute */
David Turner3469d0d2000-07-19 20:02:14 +000014/* this file you indicate that you have read the license and */
15/* understand and accept it fully. */
16/* */
Werner Lembergc3dd1512000-07-26 14:11:15 +000017/* Note that this license is compatible with the FreeType license. */
David Turner3469d0d2000-07-19 20:02:14 +000018/* */
19/***************************************************************************/
20
Werner Lembergc3dd1512000-07-26 14:11:15 +000021
22 /*************************************************************************/
23 /* */
24 /* This module is in charge of optimising the outlines produced by the */
25 /* auto-hinter in direct mode. This is required at small pixel sizes in */
26 /* order to ensure coherent spacing, among other things.. */
27 /* */
28 /* The technique used in this module is a simplified simulated */
29 /* annealing. */
30 /* */
31 /*************************************************************************/
32
Werner Lembergcc069be2000-12-08 16:17:16 +000033
34#include <ft2build.h>
David Turnere459d742002-03-22 13:52:37 +000035#include FT_INTERNAL_OBJECTS_H /* for FT_ALLOC_ARRAY() and FT_FREE() */
David Turner8d3a4012001-03-20 11:14:24 +000036#include "ahoptim.h"
Werner Lembergcc069be2000-12-08 16:17:16 +000037
David Turner3469d0d2000-07-19 20:02:14 +000038
Werner Lembergc3dd1512000-07-26 14:11:15 +000039 /* define this macro to use brute force optimisation -- this is slow, */
40 /* but a good way to perfect the distortion function `by hand' through */
41 /* tweaking */
42#define AH_BRUTE_FORCE
43
44
45#define xxxAH_DEBUG_OPTIM
46
David Turner3469d0d2000-07-19 20:02:14 +000047
48#undef LOG
Werner Lembergc3dd1512000-07-26 14:11:15 +000049#ifdef AH_DEBUG_OPTIM
David Turner3469d0d2000-07-19 20:02:14 +000050
Werner Lemberg16a60e82000-12-12 16:29:46 +000051#define LOG( x ) optim_log ## x
Werner Lembergc3dd1512000-07-26 14:11:15 +000052
53#else
54
55#define LOG( x )
56
57#endif /* AH_DEBUG_OPTIM */
58
59
60#ifdef AH_DEBUG_OPTIM
61
David Turner3469d0d2000-07-19 20:02:14 +000062#include <stdarg.h>
63#include <stdlib.h>
64#include <string.h>
65
Werner Lembergc3dd1512000-07-26 14:11:15 +000066#define FLOAT( x ) ( (float)( (x) / 64.0 ) )
David Turner3469d0d2000-07-19 20:02:14 +000067
Werner Lembergf814d0f2001-06-27 16:18:10 +000068 static void
69 optim_log( const char* fmt, ... )
Werner Lembergc3dd1512000-07-26 14:11:15 +000070 {
71 va_list ap;
David Turner3469d0d2000-07-19 20:02:14 +000072
73
Werner Lembergc3dd1512000-07-26 14:11:15 +000074 va_start( ap, fmt );
75 vprintf( fmt, ap );
76 va_end( ap );
77 }
David Turner3469d0d2000-07-19 20:02:14 +000078
79
Werner Lembergf814d0f2001-06-27 16:18:10 +000080 static void
81 AH_Dump_Stems( AH_Optimizer* optimizer )
David Turner3469d0d2000-07-19 20:02:14 +000082 {
83 int n;
84 AH_Stem* stem;
85
Werner Lembergc3dd1512000-07-26 14:11:15 +000086
David Turner3469d0d2000-07-19 20:02:14 +000087 stem = optimizer->stems;
88 for ( n = 0; n < optimizer->num_stems; n++, stem++ )
89 {
Werner Lembergc3dd1512000-07-26 14:11:15 +000090 LOG(( " %c%2d [%.1f:%.1f]={%.1f:%.1f}="
91 "<%1.f..%1.f> force=%.1f speed=%.1f\n",
David Turner3469d0d2000-07-19 20:02:14 +000092 optimizer->vertical ? 'V' : 'H', n,
Werner Lembergc3dd1512000-07-26 14:11:15 +000093 FLOAT( stem->edge1->opos ), FLOAT( stem->edge2->opos ),
94 FLOAT( stem->edge1->pos ), FLOAT( stem->edge2->pos ),
95 FLOAT( stem->min_pos ), FLOAT( stem->max_pos ),
96 FLOAT( stem->force ), FLOAT( stem->velocity ) ));
David Turner3469d0d2000-07-19 20:02:14 +000097 }
98 }
99
Werner Lembergc3dd1512000-07-26 14:11:15 +0000100
Werner Lembergf814d0f2001-06-27 16:18:10 +0000101 static void
102 AH_Dump_Stems2( AH_Optimizer* optimizer )
David Turner3469d0d2000-07-19 20:02:14 +0000103 {
104 int n;
105 AH_Stem* stem;
106
Werner Lembergc3dd1512000-07-26 14:11:15 +0000107
David Turner3469d0d2000-07-19 20:02:14 +0000108 stem = optimizer->stems;
109 for ( n = 0; n < optimizer->num_stems; n++, stem++ )
110 {
111 LOG(( " %c%2d [%.1f]=<%1.f..%1.f> force=%.1f speed=%.1f\n",
112 optimizer->vertical ? 'V' : 'H', n,
Werner Lembergc3dd1512000-07-26 14:11:15 +0000113 FLOAT( stem->pos ),
114 FLOAT( stem->min_pos ), FLOAT( stem->max_pos ),
115 FLOAT( stem->force ), FLOAT( stem->velocity ) ));
David Turner3469d0d2000-07-19 20:02:14 +0000116 }
117 }
118
Werner Lembergc3dd1512000-07-26 14:11:15 +0000119
Werner Lembergf814d0f2001-06-27 16:18:10 +0000120 static void
121 AH_Dump_Springs( AH_Optimizer* optimizer )
David Turner3469d0d2000-07-19 20:02:14 +0000122 {
123 int n;
124 AH_Spring* spring;
125 AH_Stem* stems;
126
Werner Lembergc3dd1512000-07-26 14:11:15 +0000127
David Turner3469d0d2000-07-19 20:02:14 +0000128 spring = optimizer->springs;
129 stems = optimizer->stems;
130 LOG(( "%cSprings ", optimizer->vertical ? 'V' : 'H' ));
Werner Lembergc3dd1512000-07-26 14:11:15 +0000131
David Turner3469d0d2000-07-19 20:02:14 +0000132 for ( n = 0; n < optimizer->num_springs; n++, spring++ )
133 {
Werner Lembergc3dd1512000-07-26 14:11:15 +0000134 LOG(( " [%d-%d:%.1f:%1.f:%.1f]",
135 spring->stem1 - stems, spring->stem2 - stems,
136 FLOAT( spring->owidth ),
Werner Lemberge4b32a52000-10-31 20:42:18 +0000137 FLOAT( spring->stem2->pos -
Werner Lembergc3dd1512000-07-26 14:11:15 +0000138 ( spring->stem1->pos + spring->stem1->width ) ),
139 FLOAT( spring->tension ) ));
David Turner3469d0d2000-07-19 20:02:14 +0000140 }
141
142 LOG(( "\n" ));
143 }
Werner Lembergc3dd1512000-07-26 14:11:15 +0000144
145#endif /* AH_DEBUG_OPTIM */
David Turner3469d0d2000-07-19 20:02:14 +0000146
147
148 /*************************************************************************/
149 /*************************************************************************/
150 /*************************************************************************/
151 /**** ****/
152 /**** COMPUTE STEMS AND SPRINGS IN AN OUTLINE ****/
153 /**** ****/
David Turner3469d0d2000-07-19 20:02:14 +0000154 /*************************************************************************/
155 /*************************************************************************/
156 /*************************************************************************/
157
Werner Lembergc3dd1512000-07-26 14:11:15 +0000158
Werner Lembergf814d0f2001-06-27 16:18:10 +0000159 static int
160 valid_stem_segments( AH_Segment* seg1,
161 AH_Segment* seg2 )
David Turner3469d0d2000-07-19 20:02:14 +0000162 {
Werner Lembergc3dd1512000-07-26 14:11:15 +0000163 return seg1->serif == 0 &&
164 seg2 &&
165 seg2->link == seg1 &&
166 seg1->pos < seg2->pos &&
David Turner3469d0d2000-07-19 20:02:14 +0000167 seg1->min_coord <= seg2->max_coord &&
168 seg2->min_coord <= seg1->max_coord;
169 }
170
Werner Lembergc3dd1512000-07-26 14:11:15 +0000171
David Turner3469d0d2000-07-19 20:02:14 +0000172 /* compute all stems in an outline */
Werner Lembergf814d0f2001-06-27 16:18:10 +0000173 static int
174 optim_compute_stems( AH_Optimizer* optimizer )
David Turner3469d0d2000-07-19 20:02:14 +0000175 {
176 AH_Outline* outline = optimizer->outline;
177 FT_Fixed scale;
178 FT_Memory memory = optimizer->memory;
179 FT_Error error = 0;
180 FT_Int dimension;
181 AH_Edge* edges;
182 AH_Edge* edge_limit;
183 AH_Stem** p_stems;
184 FT_Int* p_num_stems;
185
Werner Lembergc3dd1512000-07-26 14:11:15 +0000186
David Turner3469d0d2000-07-19 20:02:14 +0000187 edges = outline->horz_edges;
188 edge_limit = edges + outline->num_hedges;
189 scale = outline->y_scale;
190
191 p_stems = &optimizer->horz_stems;
192 p_num_stems = &optimizer->num_hstems;
193
194 for ( dimension = 1; dimension >= 0; dimension-- )
195 {
196 AH_Stem* stems = 0;
197 FT_Int num_stems = 0;
198 AH_Edge* edge;
199
Werner Lembergc3dd1512000-07-26 14:11:15 +0000200
David Turner3469d0d2000-07-19 20:02:14 +0000201 /* first of all, count the number of stems in this direction */
202 for ( edge = edges; edge < edge_limit; edge++ )
203 {
204 AH_Segment* seg = edge->first;
Werner Lembergc3dd1512000-07-26 14:11:15 +0000205
206
David Turner3469d0d2000-07-19 20:02:14 +0000207 do
208 {
Werner Lembergc3dd1512000-07-26 14:11:15 +0000209 if (valid_stem_segments( seg, seg->link ) )
David Turner3469d0d2000-07-19 20:02:14 +0000210 num_stems++;
211
212 seg = seg->edge_next;
213
Werner Lembergc3dd1512000-07-26 14:11:15 +0000214 } while ( seg != edge->first );
David Turner3469d0d2000-07-19 20:02:14 +0000215 }
216
217 /* now allocate the stems and build their table */
Werner Lembergc3dd1512000-07-26 14:11:15 +0000218 if ( num_stems > 0 )
David Turner3469d0d2000-07-19 20:02:14 +0000219 {
220 AH_Stem* stem;
221
Werner Lembergc3dd1512000-07-26 14:11:15 +0000222
David Turnere459d742002-03-22 13:52:37 +0000223 if ( FT_NEW_ARRAY( stems, num_stems ) )
David Turner3469d0d2000-07-19 20:02:14 +0000224 goto Exit;
225
226 stem = stems;
227 for ( edge = edges; edge < edge_limit; edge++ )
228 {
229 AH_Segment* seg = edge->first;
230 AH_Segment* seg2;
Werner Lembergc3dd1512000-07-26 14:11:15 +0000231
232
David Turner3469d0d2000-07-19 20:02:14 +0000233 do
234 {
235 seg2 = seg->link;
Werner Lembergc3dd1512000-07-26 14:11:15 +0000236 if ( valid_stem_segments( seg, seg2 ) )
David Turner3469d0d2000-07-19 20:02:14 +0000237 {
238 AH_Edge* edge1 = seg->edge;
239 AH_Edge* edge2 = seg2->edge;
240
Werner Lembergc3dd1512000-07-26 14:11:15 +0000241
David Turner3469d0d2000-07-19 20:02:14 +0000242 stem->edge1 = edge1;
243 stem->edge2 = edge2;
244 stem->opos = edge1->opos;
245 stem->pos = edge1->pos;
246 stem->owidth = edge2->opos - edge1->opos;
247 stem->width = edge2->pos - edge1->pos;
248
249 /* compute min_coord and max_coord */
250 {
251 FT_Pos min_coord = seg->min_coord;
252 FT_Pos max_coord = seg->max_coord;
253
Werner Lembergc3dd1512000-07-26 14:11:15 +0000254
255 if ( seg2->min_coord > min_coord )
David Turner3469d0d2000-07-19 20:02:14 +0000256 min_coord = seg2->min_coord;
257
Werner Lembergc3dd1512000-07-26 14:11:15 +0000258 if ( seg2->max_coord < max_coord )
David Turner3469d0d2000-07-19 20:02:14 +0000259 max_coord = seg2->max_coord;
260
261 stem->min_coord = min_coord;
262 stem->max_coord = max_coord;
263 }
264
Werner Lembergc3dd1512000-07-26 14:11:15 +0000265 /* compute minimum and maximum positions for stem -- */
David Turner3469d0d2000-07-19 20:02:14 +0000266 /* note that the left-most/bottom-most stem has always */
Werner Lembergc3dd1512000-07-26 14:11:15 +0000267 /* a fixed position */
268 if ( stem == stems || edge1->blue_edge || edge2->blue_edge )
David Turner3469d0d2000-07-19 20:02:14 +0000269 {
Werner Lembergc3dd1512000-07-26 14:11:15 +0000270 /* this stem cannot move; it is snapped to a blue edge */
David Turner3469d0d2000-07-19 20:02:14 +0000271 stem->min_pos = stem->pos;
272 stem->max_pos = stem->pos;
273 }
274 else
275 {
Werner Lembergc3dd1512000-07-26 14:11:15 +0000276 /* this edge can move; compute its min and max positions */
David Turner3469d0d2000-07-19 20:02:14 +0000277 FT_Pos pos1 = stem->opos;
278 FT_Pos pos2 = pos1 + stem->owidth - stem->width;
Werner Lembergc3dd1512000-07-26 14:11:15 +0000279 FT_Pos min1 = pos1 & -64;
280 FT_Pos min2 = pos2 & -64;
281
David Turner3469d0d2000-07-19 20:02:14 +0000282
283 stem->min_pos = min1;
Werner Lembergc3dd1512000-07-26 14:11:15 +0000284 stem->max_pos = min1 + 64;
285 if ( min2 < min1 )
David Turner3469d0d2000-07-19 20:02:14 +0000286 stem->min_pos = min2;
287 else
Werner Lembergc3dd1512000-07-26 14:11:15 +0000288 stem->max_pos = min2 + 64;
David Turner3469d0d2000-07-19 20:02:14 +0000289
Werner Lembergc3dd1512000-07-26 14:11:15 +0000290 /* XXX: just to see what it does */
David Turner3469d0d2000-07-19 20:02:14 +0000291 stem->max_pos += 64;
292
Werner Lembergc3dd1512000-07-26 14:11:15 +0000293 /* just for the case where direct hinting did some */
294 /* incredible things (e.g. blue edge shifts) */
295 if ( stem->min_pos > stem->pos )
David Turner3469d0d2000-07-19 20:02:14 +0000296 stem->min_pos = stem->pos;
297
Werner Lembergc3dd1512000-07-26 14:11:15 +0000298 if ( stem->max_pos < stem->pos )
David Turner3469d0d2000-07-19 20:02:14 +0000299 stem->max_pos = stem->pos;
300 }
301
302 stem->velocity = 0;
303 stem->force = 0;
304
305 stem++;
306 }
307 seg = seg->edge_next;
Werner Lembergc3dd1512000-07-26 14:11:15 +0000308
309 } while ( seg != edge->first );
David Turner3469d0d2000-07-19 20:02:14 +0000310 }
311 }
312
313 *p_stems = stems;
314 *p_num_stems = num_stems;
315
316 edges = outline->vert_edges;
317 edge_limit = edges + outline->num_vedges;
318 scale = outline->x_scale;
319
320 p_stems = &optimizer->vert_stems;
321 p_num_stems = &optimizer->num_vstems;
322 }
Werner Lembergc3dd1512000-07-26 14:11:15 +0000323
David Turner3469d0d2000-07-19 20:02:14 +0000324 Exit:
Werner Lembergc3dd1512000-07-26 14:11:15 +0000325
326#ifdef AH_DEBUG_OPTIM
327 AH_Dump_Stems( optimizer );
328#endif
329
David Turner3469d0d2000-07-19 20:02:14 +0000330 return error;
331 }
332
333
334 /* returns the spring area between two stems, 0 if none */
Werner Lembergf814d0f2001-06-27 16:18:10 +0000335 static FT_Pos
336 stem_spring_area( AH_Stem* stem1,
337 AH_Stem* stem2 )
David Turner3469d0d2000-07-19 20:02:14 +0000338 {
339 FT_Pos area1 = stem1->max_coord - stem1->min_coord;
340 FT_Pos area2 = stem2->max_coord - stem2->min_coord;
341 FT_Pos min = stem1->min_coord;
342 FT_Pos max = stem1->max_coord;
343 FT_Pos area;
344
Werner Lembergc3dd1512000-07-26 14:11:15 +0000345
David Turner3469d0d2000-07-19 20:02:14 +0000346 /* order stems */
Werner Lembergc3dd1512000-07-26 14:11:15 +0000347 if ( stem2->opos <= stem1->opos + stem1->owidth )
David Turner3469d0d2000-07-19 20:02:14 +0000348 return 0;
349
Werner Lembergc3dd1512000-07-26 14:11:15 +0000350 if ( min < stem2->min_coord )
David Turner3469d0d2000-07-19 20:02:14 +0000351 min = stem2->min_coord;
352
Werner Lembergc3dd1512000-07-26 14:11:15 +0000353 if ( max < stem2->max_coord )
David Turner3469d0d2000-07-19 20:02:14 +0000354 max = stem2->max_coord;
355
Werner Lembergc3dd1512000-07-26 14:11:15 +0000356 area = ( max-min );
357 if ( 2 * area < area1 && 2 * area < area2 )
David Turner3469d0d2000-07-19 20:02:14 +0000358 area = 0;
359
360 return area;
361 }
362
363
364 /* compute all springs in an outline */
Werner Lembergf814d0f2001-06-27 16:18:10 +0000365 static int
366 optim_compute_springs( AH_Optimizer* optimizer )
David Turner3469d0d2000-07-19 20:02:14 +0000367 {
368 /* basically, a spring exists between two stems if most of their */
Werner Lembergc3dd1512000-07-26 14:11:15 +0000369 /* surface is aligned */
David Turner3469d0d2000-07-19 20:02:14 +0000370 FT_Memory memory = optimizer->memory;
371
372 AH_Stem* stems;
373 AH_Stem* stem_limit;
374 AH_Stem* stem;
375 int dimension;
376 int error = 0;
377
378 FT_Int* p_num_springs;
379 AH_Spring** p_springs;
380
Werner Lembergc3dd1512000-07-26 14:11:15 +0000381
David Turner3469d0d2000-07-19 20:02:14 +0000382 stems = optimizer->horz_stems;
383 stem_limit = stems + optimizer->num_hstems;
384
385 p_springs = &optimizer->horz_springs;
386 p_num_springs = &optimizer->num_hsprings;
387
388 for ( dimension = 1; dimension >= 0; dimension-- )
389 {
390 FT_Int num_springs = 0;
391 AH_Spring* springs = 0;
392
Werner Lembergc3dd1512000-07-26 14:11:15 +0000393
David Turner3469d0d2000-07-19 20:02:14 +0000394 /* first of all, count stem springs */
Werner Lembergc3dd1512000-07-26 14:11:15 +0000395 for ( stem = stems; stem + 1 < stem_limit; stem++ )
David Turner3469d0d2000-07-19 20:02:14 +0000396 {
397 AH_Stem* stem2;
Werner Lembergc3dd1512000-07-26 14:11:15 +0000398
399
David Turner3469d0d2000-07-19 20:02:14 +0000400 for ( stem2 = stem+1; stem2 < stem_limit; stem2++ )
Werner Lembergc3dd1512000-07-26 14:11:15 +0000401 if ( stem_spring_area( stem, stem2 ) )
David Turner3469d0d2000-07-19 20:02:14 +0000402 num_springs++;
403 }
404
405 /* then allocate and build the springs table */
Werner Lembergc3dd1512000-07-26 14:11:15 +0000406 if ( num_springs > 0 )
David Turner3469d0d2000-07-19 20:02:14 +0000407 {
408 AH_Spring* spring;
409
Werner Lembergc3dd1512000-07-26 14:11:15 +0000410
David Turner3469d0d2000-07-19 20:02:14 +0000411 /* allocate table of springs */
David Turnere459d742002-03-22 13:52:37 +0000412 if ( FT_NEW_ARRAY( springs, num_springs ) )
David Turner3469d0d2000-07-19 20:02:14 +0000413 goto Exit;
414
415 /* fill the springs table */
416 spring = springs;
417 for ( stem = stems; stem+1 < stem_limit; stem++ )
418 {
419 AH_Stem* stem2;
420 FT_Pos area;
421
Werner Lembergc3dd1512000-07-26 14:11:15 +0000422
423 for ( stem2 = stem + 1; stem2 < stem_limit; stem2++ )
David Turner3469d0d2000-07-19 20:02:14 +0000424 {
Werner Lembergc3dd1512000-07-26 14:11:15 +0000425 area = stem_spring_area( stem, stem2 );
426 if ( area )
David Turner3469d0d2000-07-19 20:02:14 +0000427 {
428 /* add a new spring here */
429 spring->stem1 = stem;
430 spring->stem2 = stem2;
Werner Lembergc3dd1512000-07-26 14:11:15 +0000431 spring->owidth = stem2->opos - ( stem->opos + stem->owidth );
David Turner3469d0d2000-07-19 20:02:14 +0000432 spring->tension = 0;
433
434 spring++;
435 }
436 }
437 }
438 }
439 *p_num_springs = num_springs;
440 *p_springs = springs;
441
442 stems = optimizer->vert_stems;
443 stem_limit = stems + optimizer->num_vstems;
444
445 p_springs = &optimizer->vert_springs;
446 p_num_springs = &optimizer->num_vsprings;
447 }
448
449 Exit:
Werner Lembergc3dd1512000-07-26 14:11:15 +0000450
451#ifdef AH_DEBUG_OPTIM
452 AH_Dump_Springs( optimizer );
453#endif
454
David Turner3469d0d2000-07-19 20:02:14 +0000455 return error;
456 }
457
Werner Lembergc3dd1512000-07-26 14:11:15 +0000458
David Turner3469d0d2000-07-19 20:02:14 +0000459 /*************************************************************************/
460 /*************************************************************************/
461 /*************************************************************************/
462 /**** ****/
Werner Lembergc3dd1512000-07-26 14:11:15 +0000463 /**** OPTIMIZE THROUGH MY STRANGE SIMULATED ANNEALING ALGO ;-) ****/
David Turner3469d0d2000-07-19 20:02:14 +0000464 /**** ****/
465 /*************************************************************************/
466 /*************************************************************************/
467 /*************************************************************************/
468
Werner Lembergc3dd1512000-07-26 14:11:15 +0000469#ifndef AH_BRUTE_FORCE
470
David Turner3469d0d2000-07-19 20:02:14 +0000471 /* compute all spring tensions */
Werner Lembergf814d0f2001-06-27 16:18:10 +0000472 static void
473 optim_compute_tensions( AH_Optimizer* optimizer )
David Turner3469d0d2000-07-19 20:02:14 +0000474 {
475 AH_Spring* spring = optimizer->springs;
476 AH_Spring* limit = spring + optimizer->num_springs;
Werner Lembergc3dd1512000-07-26 14:11:15 +0000477
478
David Turner3469d0d2000-07-19 20:02:14 +0000479 for ( ; spring < limit; spring++ )
480 {
481 AH_Stem* stem1 = spring->stem1;
482 AH_Stem* stem2 = spring->stem2;
483 FT_Int status;
484
485 FT_Pos width;
486 FT_Pos tension;
487 FT_Pos sign;
488
Werner Lembergc3dd1512000-07-26 14:11:15 +0000489
490 /* compute the tension; it simply is -K*(new_width-old_width) */
491 width = stem2->pos - ( stem1->pos + stem1->width );
492 tension = width - spring->owidth;
David Turner3469d0d2000-07-19 20:02:14 +0000493
494 sign = 1;
Werner Lembergc3dd1512000-07-26 14:11:15 +0000495 if ( tension < 0 )
David Turner3469d0d2000-07-19 20:02:14 +0000496 {
497 sign = -1;
498 tension = -tension;
499 }
500
Werner Lembergc3dd1512000-07-26 14:11:15 +0000501 if ( width <= 0 )
David Turner3469d0d2000-07-19 20:02:14 +0000502 tension = 32000;
503 else
Werner Lembergc3dd1512000-07-26 14:11:15 +0000504 tension = ( tension << 10 ) / width;
David Turner3469d0d2000-07-19 20:02:14 +0000505
Werner Lembergc3dd1512000-07-26 14:11:15 +0000506 tension = -sign * FT_MulFix( tension, optimizer->tension_scale );
David Turner3469d0d2000-07-19 20:02:14 +0000507 spring->tension = tension;
508
509 /* now, distribute tension among the englobing stems, if they */
Werner Lembergc3dd1512000-07-26 14:11:15 +0000510 /* are able to move */
David Turner3469d0d2000-07-19 20:02:14 +0000511 status = 0;
Werner Lembergc3dd1512000-07-26 14:11:15 +0000512 if ( stem1->pos <= stem1->min_pos )
David Turner3469d0d2000-07-19 20:02:14 +0000513 status |= 1;
Werner Lembergc3dd1512000-07-26 14:11:15 +0000514 if ( stem2->pos >= stem2->max_pos )
David Turner3469d0d2000-07-19 20:02:14 +0000515 status |= 2;
516
Werner Lembergc3dd1512000-07-26 14:11:15 +0000517 if ( !status )
David Turner3469d0d2000-07-19 20:02:14 +0000518 tension /= 2;
519
Werner Lembergc3dd1512000-07-26 14:11:15 +0000520 if ( ( status & 1 ) == 0 )
David Turner3469d0d2000-07-19 20:02:14 +0000521 stem1->force -= tension;
522
Werner Lembergc3dd1512000-07-26 14:11:15 +0000523 if ( ( status & 2 ) == 0 )
David Turner3469d0d2000-07-19 20:02:14 +0000524 stem2->force += tension;
525 }
526 }
527
528
Werner Lembergc3dd1512000-07-26 14:11:15 +0000529 /* compute all stem movements -- returns 0 if nothing moved */
Werner Lembergf814d0f2001-06-27 16:18:10 +0000530 static int
531 optim_compute_stem_movements( AH_Optimizer* optimizer )
David Turner3469d0d2000-07-19 20:02:14 +0000532 {
533 AH_Stem* stems = optimizer->stems;
534 AH_Stem* limit = stems + optimizer->num_stems;
535 AH_Stem* stem = stems;
536 int moved = 0;
537
Werner Lembergc3dd1512000-07-26 14:11:15 +0000538
David Turner3469d0d2000-07-19 20:02:14 +0000539 /* set initial forces to velocity */
540 for ( stem = stems; stem < limit; stem++ )
541 {
542 stem->force = stem->velocity;
Werner Lembergc3dd1512000-07-26 14:11:15 +0000543 stem->velocity /= 2; /* XXX: Heuristics */
David Turner3469d0d2000-07-19 20:02:14 +0000544 }
545
546 /* compute the sum of forces applied on each stem */
547 optim_compute_tensions( optimizer );
Werner Lembergc3dd1512000-07-26 14:11:15 +0000548
549#ifdef AH_DEBUG_OPTIM
David Turner3469d0d2000-07-19 20:02:14 +0000550 AH_Dump_Springs( optimizer );
551 AH_Dump_Stems2( optimizer );
Werner Lembergc3dd1512000-07-26 14:11:15 +0000552#endif
David Turner3469d0d2000-07-19 20:02:14 +0000553
Werner Lembergc3dd1512000-07-26 14:11:15 +0000554 /* now, see whether something can move */
David Turner3469d0d2000-07-19 20:02:14 +0000555 for ( stem = stems; stem < limit; stem++ )
556 {
Werner Lembergc3dd1512000-07-26 14:11:15 +0000557 if ( stem->force > optimizer->tension_threshold )
David Turner3469d0d2000-07-19 20:02:14 +0000558 {
559 /* there is enough tension to move the stem to the right */
Werner Lembergc3dd1512000-07-26 14:11:15 +0000560 if ( stem->pos < stem->max_pos )
David Turner3469d0d2000-07-19 20:02:14 +0000561 {
Werner Lembergc3dd1512000-07-26 14:11:15 +0000562 stem->pos += 64;
563 stem->velocity = stem->force / 2;
564 moved = 1;
David Turner3469d0d2000-07-19 20:02:14 +0000565 }
566 else
567 stem->velocity = 0;
568 }
Werner Lembergc3dd1512000-07-26 14:11:15 +0000569 else if ( stem->force < optimizer->tension_threshold )
David Turner3469d0d2000-07-19 20:02:14 +0000570 {
571 /* there is enough tension to move the stem to the left */
Werner Lembergc3dd1512000-07-26 14:11:15 +0000572 if ( stem->pos > stem->min_pos )
David Turner3469d0d2000-07-19 20:02:14 +0000573 {
Werner Lembergc3dd1512000-07-26 14:11:15 +0000574 stem->pos -= 64;
575 stem->velocity = stem->force / 2;
576 moved = 1;
David Turner3469d0d2000-07-19 20:02:14 +0000577 }
578 else
579 stem->velocity = 0;
580 }
581 }
Werner Lembergc3dd1512000-07-26 14:11:15 +0000582
David Turner3469d0d2000-07-19 20:02:14 +0000583 /* return 0 if nothing moved */
584 return moved;
585 }
586
Werner Lembergc3dd1512000-07-26 14:11:15 +0000587#endif /* AH_BRUTE_FORCE */
David Turner3469d0d2000-07-19 20:02:14 +0000588
589
590 /* compute current global distortion from springs */
Werner Lembergf814d0f2001-06-27 16:18:10 +0000591 static FT_Pos
592 optim_compute_distortion( AH_Optimizer* optimizer )
David Turner3469d0d2000-07-19 20:02:14 +0000593 {
594 AH_Spring* spring = optimizer->springs;
595 AH_Spring* limit = spring + optimizer->num_springs;
Werner Lembergc3dd1512000-07-26 14:11:15 +0000596 FT_Pos distortion = 0;
597
David Turner3469d0d2000-07-19 20:02:14 +0000598
599 for ( ; spring < limit; spring++ )
600 {
601 AH_Stem* stem1 = spring->stem1;
602 AH_Stem* stem2 = spring->stem2;
603 FT_Pos width;
604
Werner Lembergc3dd1512000-07-26 14:11:15 +0000605 width = stem2->pos - ( stem1->pos + stem1->width );
606 width -= spring->owidth;
607 if ( width < 0 )
David Turner3469d0d2000-07-19 20:02:14 +0000608 width = -width;
609
Werner Lembergc3dd1512000-07-26 14:11:15 +0000610 distortion += width;
David Turner3469d0d2000-07-19 20:02:14 +0000611 }
Werner Lembergc3dd1512000-07-26 14:11:15 +0000612
613 return distortion;
David Turner3469d0d2000-07-19 20:02:14 +0000614 }
615
616
Werner Lembergc3dd1512000-07-26 14:11:15 +0000617 /* record stems configuration in `best of' history */
Werner Lembergf814d0f2001-06-27 16:18:10 +0000618 static void
619 optim_record_configuration( AH_Optimizer* optimizer )
David Turner3469d0d2000-07-19 20:02:14 +0000620 {
Werner Lembergc3dd1512000-07-26 14:11:15 +0000621 FT_Pos distortion;
David Turner3469d0d2000-07-19 20:02:14 +0000622 AH_Configuration* configs = optimizer->configs;
623 AH_Configuration* limit = configs + optimizer->num_configs;
624 AH_Configuration* config;
625
Werner Lembergc3dd1512000-07-26 14:11:15 +0000626
627 distortion = optim_compute_distortion( optimizer );
628 LOG(( "config distortion = %.1f ", FLOAT( distortion * 64 ) ));
David Turner3469d0d2000-07-19 20:02:14 +0000629
630 /* check that we really need to add this configuration to our */
Werner Lembergc3dd1512000-07-26 14:11:15 +0000631 /* sorted history */
632 if ( limit > configs && limit[-1].distortion < distortion )
David Turner3469d0d2000-07-19 20:02:14 +0000633 {
634 LOG(( "ejected\n" ));
635 return;
636 }
637
638 /* add new configuration at the end of the table */
639 {
640 int n;
641
Werner Lembergc3dd1512000-07-26 14:11:15 +0000642
David Turner3469d0d2000-07-19 20:02:14 +0000643 config = limit;
Werner Lembergc3dd1512000-07-26 14:11:15 +0000644 if ( optimizer->num_configs < AH_MAX_CONFIGS )
David Turner3469d0d2000-07-19 20:02:14 +0000645 optimizer->num_configs++;
646 else
647 config--;
648
Werner Lembergc3dd1512000-07-26 14:11:15 +0000649 config->distortion = distortion;
David Turner3469d0d2000-07-19 20:02:14 +0000650
651 for ( n = 0; n < optimizer->num_stems; n++ )
652 config->positions[n] = optimizer->stems[n].pos;
653 }
654
655 /* move the current configuration towards the front of the list */
Werner Lembergc3dd1512000-07-26 14:11:15 +0000656 /* when necessary -- yes this is slow bubble sort ;-) */
657 while ( config > configs && config[0].distortion < config[-1].distortion )
David Turner3469d0d2000-07-19 20:02:14 +0000658 {
659 AH_Configuration temp;
Werner Lembergc3dd1512000-07-26 14:11:15 +0000660
661
David Turner3469d0d2000-07-19 20:02:14 +0000662 config--;
663 temp = config[0];
664 config[0] = config[1];
665 config[1] = temp;
666 }
Werner Lembergc3dd1512000-07-26 14:11:15 +0000667 LOG(( "recorded!\n" ));
David Turner3469d0d2000-07-19 20:02:14 +0000668 }
669
670
Werner Lembergc3dd1512000-07-26 14:11:15 +0000671#ifdef AH_BRUTE_FORCE
672
David Turner3469d0d2000-07-19 20:02:14 +0000673 /* optimize outline in a single direction */
Werner Lembergf814d0f2001-06-27 16:18:10 +0000674 static void
675 optim_compute( AH_Optimizer* optimizer )
David Turner3469d0d2000-07-19 20:02:14 +0000676 {
Werner Lembergc3dd1512000-07-26 14:11:15 +0000677 int n;
678 FT_Bool moved;
David Turner3469d0d2000-07-19 20:02:14 +0000679
680 AH_Stem* stem = optimizer->stems;
681 AH_Stem* limit = stem + optimizer->num_stems;
682
Werner Lembergc3dd1512000-07-26 14:11:15 +0000683
David Turner3469d0d2000-07-19 20:02:14 +0000684 /* empty, exit */
Werner Lembergc3dd1512000-07-26 14:11:15 +0000685 if ( stem >= limit )
David Turner3469d0d2000-07-19 20:02:14 +0000686 return;
687
688 optimizer->num_configs = 0;
689
690 stem = optimizer->stems;
691 for ( ; stem < limit; stem++ )
692 stem->pos = stem->min_pos;
693
694 do
695 {
696 /* record current configuration */
Werner Lembergc3dd1512000-07-26 14:11:15 +0000697 optim_record_configuration( optimizer );
David Turner3469d0d2000-07-19 20:02:14 +0000698
699 /* now change configuration */
700 moved = 0;
701 for ( stem = optimizer->stems; stem < limit; stem++ )
702 {
Werner Lembergc3dd1512000-07-26 14:11:15 +0000703 if ( stem->pos < stem->max_pos )
David Turner3469d0d2000-07-19 20:02:14 +0000704 {
705 stem->pos += 64;
706 moved = 1;
707 break;
708 }
709
710 stem->pos = stem->min_pos;
711 }
Werner Lembergc3dd1512000-07-26 14:11:15 +0000712 } while ( moved );
David Turner3469d0d2000-07-19 20:02:14 +0000713
714 /* now, set the best stem positions */
715 for ( n = 0; n < optimizer->num_stems; n++ )
716 {
717 AH_Stem* stem = optimizer->stems + n;
718 FT_Pos pos = optimizer->configs[0].positions[n];
719
Werner Lembergc3dd1512000-07-26 14:11:15 +0000720
David Turner3469d0d2000-07-19 20:02:14 +0000721 stem->edge1->pos = pos;
722 stem->edge2->pos = pos + stem->width;
723
724 stem->edge1->flags |= ah_edge_done;
725 stem->edge2->flags |= ah_edge_done;
726 }
727 }
Werner Lembergc3dd1512000-07-26 14:11:15 +0000728
729#else /* AH_BRUTE_FORCE */
730
David Turner3469d0d2000-07-19 20:02:14 +0000731 /* optimize outline in a single direction */
Werner Lembergf814d0f2001-06-27 16:18:10 +0000732 static void
733 optim_compute( AH_Optimizer* optimizer )
David Turner3469d0d2000-07-19 20:02:14 +0000734 {
735 int n, counter, counter2;
736
Werner Lembergc3dd1512000-07-26 14:11:15 +0000737
David Turner3469d0d2000-07-19 20:02:14 +0000738 optimizer->num_configs = 0;
739 optimizer->tension_scale = 0x80000L;
740 optimizer->tension_threshold = 64;
741
742 /* record initial configuration threshold */
Werner Lembergc3dd1512000-07-26 14:11:15 +0000743 optim_record_configuration( optimizer );
744
David Turner3469d0d2000-07-19 20:02:14 +0000745 counter = 0;
746 for ( counter2 = optimizer->num_stems*8; counter2 >= 0; counter2-- )
747 {
Werner Lembergc3dd1512000-07-26 14:11:15 +0000748 if ( counter == 0 )
749 counter = 2 * optimizer->num_stems;
David Turner3469d0d2000-07-19 20:02:14 +0000750
Werner Lembergc3dd1512000-07-26 14:11:15 +0000751 if ( !optim_compute_stem_movements( optimizer ) )
David Turner3469d0d2000-07-19 20:02:14 +0000752 break;
753
Werner Lembergc3dd1512000-07-26 14:11:15 +0000754 optim_record_configuration( optimizer );
755
David Turner3469d0d2000-07-19 20:02:14 +0000756 counter--;
Werner Lembergc3dd1512000-07-26 14:11:15 +0000757 if ( counter == 0 )
David Turner3469d0d2000-07-19 20:02:14 +0000758 optimizer->tension_scale /= 2;
759 }
760
761 /* now, set the best stem positions */
762 for ( n = 0; n < optimizer->num_stems; n++ )
763 {
764 AH_Stem* stem = optimizer->stems + n;
765 FT_Pos pos = optimizer->configs[0].positions[n];
766
Werner Lembergc3dd1512000-07-26 14:11:15 +0000767
David Turner3469d0d2000-07-19 20:02:14 +0000768 stem->edge1->pos = pos;
769 stem->edge2->pos = pos + stem->width;
770
771 stem->edge1->flags |= ah_edge_done;
772 stem->edge2->flags |= ah_edge_done;
773 }
774 }
Werner Lembergc3dd1512000-07-26 14:11:15 +0000775
776#endif /* AH_BRUTE_FORCE */
777
David Turner3469d0d2000-07-19 20:02:14 +0000778
779 /*************************************************************************/
780 /*************************************************************************/
781 /*************************************************************************/
782 /**** ****/
783 /**** HIGH-LEVEL OPTIMIZER API ****/
784 /**** ****/
David Turner3469d0d2000-07-19 20:02:14 +0000785 /*************************************************************************/
786 /*************************************************************************/
787 /*************************************************************************/
788
789
Werner Lembergc3dd1512000-07-26 14:11:15 +0000790 /* releases the optimization data */
Werner Lembergf814d0f2001-06-27 16:18:10 +0000791 void
792 AH_Optimizer_Done( AH_Optimizer* optimizer )
David Turner3469d0d2000-07-19 20:02:14 +0000793 {
Werner Lembergc3dd1512000-07-26 14:11:15 +0000794 if ( optimizer )
David Turner3469d0d2000-07-19 20:02:14 +0000795 {
796 FT_Memory memory = optimizer->memory;
Werner Lembergc3dd1512000-07-26 14:11:15 +0000797
798
David Turnere459d742002-03-22 13:52:37 +0000799 FT_FREE( optimizer->horz_stems );
800 FT_FREE( optimizer->vert_stems );
801 FT_FREE( optimizer->horz_springs );
802 FT_FREE( optimizer->vert_springs );
803 FT_FREE( optimizer->positions );
David Turner3469d0d2000-07-19 20:02:14 +0000804 }
805 }
806
Werner Lembergc3dd1512000-07-26 14:11:15 +0000807
David Turner3469d0d2000-07-19 20:02:14 +0000808 /* loads the outline into the optimizer */
Werner Lembergf814d0f2001-06-27 16:18:10 +0000809 int
810 AH_Optimizer_Init( AH_Optimizer* optimizer,
811 AH_Outline* outline,
812 FT_Memory memory )
David Turner3469d0d2000-07-19 20:02:14 +0000813 {
814 FT_Error error;
815
Werner Lembergc3dd1512000-07-26 14:11:15 +0000816
David Turnere459d742002-03-22 13:52:37 +0000817 FT_MEM_SET( optimizer, 0, sizeof ( *optimizer ) );
David Turner3469d0d2000-07-19 20:02:14 +0000818 optimizer->outline = outline;
819 optimizer->memory = memory;
820
821 LOG(( "initializing new optimizer\n" ));
822 /* compute stems and springs */
823 error = optim_compute_stems ( optimizer ) ||
824 optim_compute_springs( optimizer );
Werner Lembergc3dd1512000-07-26 14:11:15 +0000825 if ( error )
826 goto Fail;
David Turner3469d0d2000-07-19 20:02:14 +0000827
828 /* allocate stem positions history and configurations */
829 {
830 int n, max_stems;
831
Werner Lembergc3dd1512000-07-26 14:11:15 +0000832
David Turner3469d0d2000-07-19 20:02:14 +0000833 max_stems = optimizer->num_hstems;
Werner Lembergc3dd1512000-07-26 14:11:15 +0000834 if ( max_stems < optimizer->num_vstems )
David Turner3469d0d2000-07-19 20:02:14 +0000835 max_stems = optimizer->num_vstems;
836
David Turnere459d742002-03-22 13:52:37 +0000837 if ( FT_NEW_ARRAY( optimizer->positions, max_stems * AH_MAX_CONFIGS ) )
David Turner3469d0d2000-07-19 20:02:14 +0000838 goto Fail;
839
840 optimizer->num_configs = 0;
841 for ( n = 0; n < AH_MAX_CONFIGS; n++ )
Werner Lembergc3dd1512000-07-26 14:11:15 +0000842 optimizer->configs[n].positions = optimizer->positions +
843 n * max_stems;
David Turner3469d0d2000-07-19 20:02:14 +0000844 }
845
846 return error;
847
848 Fail:
849 AH_Optimizer_Done( optimizer );
850 return error;
851 }
852
853
854 /* compute optimal outline */
Werner Lembergf814d0f2001-06-27 16:18:10 +0000855 void
856 AH_Optimizer_Compute( AH_Optimizer* optimizer )
David Turner3469d0d2000-07-19 20:02:14 +0000857 {
858 optimizer->num_stems = optimizer->num_hstems;
859 optimizer->stems = optimizer->horz_stems;
860 optimizer->num_springs = optimizer->num_hsprings;
861 optimizer->springs = optimizer->horz_springs;
862
Werner Lembergc3dd1512000-07-26 14:11:15 +0000863 if ( optimizer->num_springs > 0 )
David Turner3469d0d2000-07-19 20:02:14 +0000864 {
Werner Lembergc3dd1512000-07-26 14:11:15 +0000865 LOG(( "horizontal optimization ------------------------\n" ));
David Turner3469d0d2000-07-19 20:02:14 +0000866 optim_compute( optimizer );
867 }
868
869 optimizer->num_stems = optimizer->num_vstems;
870 optimizer->stems = optimizer->vert_stems;
871 optimizer->num_springs = optimizer->num_vsprings;
872 optimizer->springs = optimizer->vert_springs;
873
Werner Lembergc3dd1512000-07-26 14:11:15 +0000874 if ( optimizer->num_springs )
David Turner3469d0d2000-07-19 20:02:14 +0000875 {
Werner Lembergc3dd1512000-07-26 14:11:15 +0000876 LOG(( "vertical optimization --------------------------\n" ));
David Turner3469d0d2000-07-19 20:02:14 +0000877 optim_compute( optimizer );
878 }
879 }
880
881
Werner Lembergc3dd1512000-07-26 14:11:15 +0000882/* END */