Tesseract 3.02

- Main Page
- Related Pages
- Modules
- Namespaces
- Classes
- Files

- File List
- File Members

All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
intproto.cpp
Go to the documentation of this file.
1 /******************************************************************************
2 ** Filename: intproto.c
3 ** Purpose: Definition of data structures for integer protos.
4 ** Author: Dan Johnson
5 ** History: Thu Feb 7 14:38:16 1991, DSJ, Created.
6 **
7 ** (c) Copyright Hewlett-Packard Company, 1988.
8 ** Licensed under the Apache License, Version 2.0 (the "License");
9 ** you may not use this file except in compliance with the License.
10 ** You may obtain a copy of the License at
11 ** http://www.apache.org/licenses/LICENSE-2.0
12 ** Unless required by applicable law or agreed to in writing, software
13 ** distributed under the License is distributed on an "AS IS" BASIS,
14 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 ** See the License for the specific language governing permissions and
16 ** limitations under the License.
17 ******************************************************************************/
18 /*-----------------------------------------------------------------------------
19 Include Files and Type Defines
20 -----------------------------------------------------------------------------*/
21
22 #include <math.h>
23 #include <stdio.h>
24 #include <assert.h>
25 #ifdef __UNIX__
26 #include <unistd.h>
27 #endif
28
29 #include "classify.h"
30 #include "const.h"
31 #include "emalloc.h"
32 #include "fontinfo.h"
33 #include "genericvector.h"
34 #include "globals.h"
35 #include "helpers.h"
36 #include "intproto.h"
37 #include "mfoutline.h"
38 #include "ndminx.h"
39 #include "picofeat.h"
40 #include "shapetable.h"
41 #include "svmnode.h"
42
43 // Include automatically generated configuration file if running autoconf.
44 #ifdef HAVE_CONFIG_H
45 #include "config_auto.h"
46 #endif
47
48 using tesseract::FontInfo;
49 using tesseract::FontSet;
50 using tesseract::FontSpacingInfo;
51
52 /* match debug display constants*/
53 #define PROTO_PRUNER_SCALE (4.0)
54
55 #define INT_DESCENDER (0.0 * INT_CHAR_NORM_RANGE)
56 #define INT_BASELINE (0.25 * INT_CHAR_NORM_RANGE)
57 #define INT_XHEIGHT (0.75 * INT_CHAR_NORM_RANGE)
58 #define INT_CAPHEIGHT (1.0 * INT_CHAR_NORM_RANGE)
59
60 #define INT_XCENTER (0.5 * INT_CHAR_NORM_RANGE)
61 #define INT_YCENTER (0.5 * INT_CHAR_NORM_RANGE)
62 #define INT_XRADIUS (0.2 * INT_CHAR_NORM_RANGE)
63 #define INT_YRADIUS (0.2 * INT_CHAR_NORM_RANGE)
64 #define INT_MIN_X 0
65 #define INT_MIN_Y 0
66 #define INT_MAX_X INT_CHAR_NORM_RANGE
67 #define INT_MAX_Y INT_CHAR_NORM_RANGE
68
70 #define HV_TOLERANCE (0.0025) /* approx 0.9 degrees */
71
72 typedef enum
73 { StartSwitch, EndSwitch, LastSwitch }
74 SWITCH_TYPE;
75 #define MAX_NUM_SWITCHES 3
76
77 typedef struct
78 {
79 SWITCH_TYPE Type;
80 inT8 X, Y;
81 inT16 YInit;
82 inT16 Delta;
83 }
84
85
86 FILL_SWITCH;
87
88 typedef struct
89 {
90 uinT8 NextSwitch;
91 uinT8 AngleStart, AngleEnd;
92 inT8 X;
93 inT16 YStart, YEnd;
94 inT16 StartDelta, EndDelta;
95 FILL_SWITCH Switch[MAX_NUM_SWITCHES];
96 }
97
98
99 TABLE_FILLER;
100
101 typedef struct
102 {
103 inT8 X;
104 inT8 YStart, YEnd;
105 uinT8 AngleStart, AngleEnd;
106 }
107
108
109 FILL_SPEC;
110
111
112 /* constants for conversion from old inttemp format */
113 #define OLD_MAX_NUM_CONFIGS 32
114 #define OLD_WERDS_PER_CONFIG_VEC ((OLD_MAX_NUM_CONFIGS + BITS_PER_WERD - 1) /\
115 BITS_PER_WERD)
116
117 /*-----------------------------------------------------------------------------
118 Macros
119 -----------------------------------------------------------------------------*/
121 #define CircularIncrement(i,r) (((i) < (r) - 1)?((i)++):((i) = 0))
122
124 #define MapParam(P,O,N) (floor (((P) + (O)) * (N)))
125
126 /*---------------------------------------------------------------------------
127 Private Function Prototypes
128 ----------------------------------------------------------------------------*/
129 FLOAT32 BucketStart(int Bucket, FLOAT32 Offset, int NumBuckets);
130
131 FLOAT32 BucketEnd(int Bucket, FLOAT32 Offset, int NumBuckets);
132
133 void DoFill(FILL_SPEC *FillSpec,
134 CLASS_PRUNER_STRUCT* Pruner,
135 register uinT32 ClassMask,
136 register uinT32 ClassCount,
137 register uinT32 WordIndex);
138
139 BOOL8 FillerDone(TABLE_FILLER *Filler);
140
141 void FillPPCircularBits(uinT32
142 ParamTable[NUM_PP_BUCKETS][WERDS_PER_PP_VECTOR],
143 int Bit, FLOAT32 Center, FLOAT32 Spread, bool debug);
144
145 void FillPPLinearBits(uinT32 ParamTable[NUM_PP_BUCKETS][WERDS_PER_PP_VECTOR],
146 int Bit, FLOAT32 Center, FLOAT32 Spread, bool debug);
147
148 void GetCPPadsForLevel(int Level,
149 FLOAT32 *EndPad,
150 FLOAT32 *SidePad,
151 FLOAT32 *AnglePad);
152
153 ScrollView::Color GetMatchColorFor(FLOAT32 Evidence);
154
155 void GetNextFill(TABLE_FILLER *Filler, FILL_SPEC *Fill);
156
157 void InitTableFiller(FLOAT32 EndPad,
158 FLOAT32 SidePad,
159 FLOAT32 AnglePad,
160 PROTO Proto,
161 TABLE_FILLER *Filler);
162
163 #ifndef GRAPHICS_DISABLED
164 void RenderIntFeature(ScrollView *window, const INT_FEATURE_STRUCT* Feature,
165 ScrollView::Color color);
166
167 void RenderIntProto(ScrollView *window,
168 INT_CLASS Class,
169 PROTO_ID ProtoId,
170 ScrollView::Color color);
171 #endif // GRAPHICS_DISABLED
172
173 int TruncateParam(FLOAT32 Param, int Min, int Max, char *Id);
174
175 /*-----------------------------------------------------------------------------
176 Global Data Definitions and Declarations
177 -----------------------------------------------------------------------------*/
178
179 /* global display lists used to display proto and feature match information*/
180 ScrollView *IntMatchWindow = NULL;
181 ScrollView *FeatureDisplayWindow = NULL;
182 ScrollView *ProtoDisplayWindow = NULL;
183
184 /*-----------------------------------------------------------------------------
185 Variables
186 -----------------------------------------------------------------------------*/
187
188 /* control knobs */
189 INT_VAR(classify_num_cp_levels, 3, "Number of Class Pruner Levels");
190 double_VAR(classify_cp_angle_pad_loose, 45.0,
191 "Class Pruner Angle Pad Loose");
192 double_VAR(classify_cp_angle_pad_medium, 20.0,
193 "Class Pruner Angle Pad Medium");
194 double_VAR(classify_cp_angle_pad_tight, 10.0,
195 "CLass Pruner Angle Pad Tight");
196 double_VAR(classify_cp_end_pad_loose, 0.5, "Class Pruner End Pad Loose");
197 double_VAR(classify_cp_end_pad_medium, 0.5, "Class Pruner End Pad Medium");
198 double_VAR(classify_cp_end_pad_tight, 0.5, "Class Pruner End Pad Tight");
199 double_VAR(classify_cp_side_pad_loose, 2.5, "Class Pruner Side Pad Loose");
200 double_VAR(classify_cp_side_pad_medium, 1.2, "Class Pruner Side Pad Medium");
201 double_VAR(classify_cp_side_pad_tight, 0.6, "Class Pruner Side Pad Tight");
202 double_VAR(classify_pp_angle_pad, 45.0, "Proto Pruner Angle Pad");
203 double_VAR(classify_pp_end_pad, 0.5, "Proto Prune End Pad");
204 double_VAR(classify_pp_side_pad, 2.5, "Proto Pruner Side Pad");
205
206 /*-----------------------------------------------------------------------------
207 Public Code
208 -----------------------------------------------------------------------------*/
209 /*---------------------------------------------------------------------------*/
224 void AddIntClass(INT_TEMPLATES Templates, CLASS_ID ClassId, INT_CLASS Class) {
225 int Pruner;
226
227 assert (LegalClassId (ClassId));
228 if (ClassId != Templates->NumClasses) {
229 fprintf(stderr, "Please make sure that classes are added to templates");
230 fprintf(stderr, " in increasing order of ClassIds\n");
231 exit(1);
232 }
233 ClassForClassId (Templates, ClassId) = Class;
234 Templates->NumClasses++;
235
236 if (Templates->NumClasses > MaxNumClassesIn (Templates)) {
237 Pruner = Templates->NumClassPruners++;
238 Templates->ClassPruners[Pruner] = new CLASS_PRUNER_STRUCT;
239 memset(Templates->ClassPruners[Pruner], 0, sizeof(CLASS_PRUNER_STRUCT));
240 }
241 } /* AddIntClass */
242
243
244 /*---------------------------------------------------------------------------*/
257 int AddIntConfig(INT_CLASS Class) {
258 int Index;
259
260 assert(Class->NumConfigs < MAX_NUM_CONFIGS);
261
262 Index = Class->NumConfigs++;
263 Class->ConfigLengths[Index] = 0;
264 return Index;
265 } /* AddIntConfig */
266
267
268 /*---------------------------------------------------------------------------*/
281 int AddIntProto(INT_CLASS Class) {
282 int Index;
283 int ProtoSetId;
284 PROTO_SET ProtoSet;
285 INT_PROTO Proto;
286 register uinT32 *Word;
287
288 if (Class->NumProtos >= MAX_NUM_PROTOS)
289 return (NO_PROTO);
290
291 Index = Class->NumProtos++;
292
293 if (Class->NumProtos > MaxNumIntProtosIn(Class)) {
294 ProtoSetId = Class->NumProtoSets++;
295
296 ProtoSet = (PROTO_SET) Emalloc(sizeof(PROTO_SET_STRUCT));
297 Class->ProtoSets[ProtoSetId] = ProtoSet;
298 memset(ProtoSet, 0, sizeof(*ProtoSet));
299
300 /* reallocate space for the proto lengths and install in class */
301 Class->ProtoLengths =
302 (uinT8 *)Erealloc(Class->ProtoLengths,
303 MaxNumIntProtosIn(Class) * sizeof(uinT8));
304 memset(&Class->ProtoLengths[Index], 0,
305 sizeof(*Class->ProtoLengths) * (MaxNumIntProtosIn(Class) - Index));
306 }
307
308 /* initialize proto so its length is zero and it isn't in any configs */
309 Class->ProtoLengths[Index] = 0;
310 Proto = ProtoForProtoId (Class, Index);
311 for (Word = Proto->Configs;
312 Word < Proto->Configs + WERDS_PER_CONFIG_VEC; *Word++ = 0);
313
314 return (Index);
315
316 } /* AddIntProto */
317
318
319 /*---------------------------------------------------------------------------*/
320 void AddProtoToClassPruner (PROTO Proto, CLASS_ID ClassId,
321 INT_TEMPLATES Templates)
322 /*
323 ** Parameters:
324 ** Proto floating-pt proto to add to class pruner
325 ** ClassId class id corresponding to Proto
326 ** Templates set of templates containing class pruner
327 ** Globals:
328 ** classify_num_cp_levels number of levels used in the class pruner
329 ** Operation: This routine adds Proto to the class pruning tables
330 ** for the specified class in Templates.
331 ** Return: none
332 ** Exceptions: none
333 ** History: Wed Feb 13 08:49:54 1991, DSJ, Created.
334 */
335 #define MAX_LEVEL 2
336 {
337 CLASS_PRUNER_STRUCT* Pruner;
338 uinT32 ClassMask;
339 uinT32 ClassCount;
340 uinT32 WordIndex;
341 int Level;
342 FLOAT32 EndPad, SidePad, AnglePad;
343 TABLE_FILLER TableFiller;
344 FILL_SPEC FillSpec;
345
346 Pruner = CPrunerFor (Templates, ClassId);
347 WordIndex = CPrunerWordIndexFor (ClassId);
348 ClassMask = CPrunerMaskFor (MAX_LEVEL, ClassId);
349
350 for (Level = classify_num_cp_levels - 1; Level >= 0; Level--) {
351 GetCPPadsForLevel(Level, &EndPad, &SidePad, &AnglePad);
352 ClassCount = CPrunerMaskFor (Level, ClassId);
353 InitTableFiller(EndPad, SidePad, AnglePad, Proto, &TableFiller);
354
355 while (!FillerDone (&TableFiller)) {
356 GetNextFill(&TableFiller, &FillSpec);
357 DoFill(&FillSpec, Pruner, ClassMask, ClassCount, WordIndex);
358 }
359 }
360 } /* AddProtoToClassPruner */
361
362
363 /*---------------------------------------------------------------------------*/
364 void AddProtoToProtoPruner(PROTO Proto, int ProtoId,
365 INT_CLASS Class, bool debug) {
366 /*
367 ** Parameters:
368 ** Proto floating-pt proto to be added to proto pruner
369 ** ProtoId id of proto
370 ** Class integer class that contains desired proto pruner
371 ** Globals: none
372 ** Operation: This routine updates the proto pruner lookup tables
373 ** for Class to include a new proto identified by ProtoId
374 ** and described by Proto.
375 ** Return: none
376 ** Exceptions: none
377 ** History: Fri Feb 8 13:07:19 1991, DSJ, Created.
378 */
379 FLOAT32 Angle, X, Y, Length;
380 FLOAT32 Pad;
381 int Index;
382 PROTO_SET ProtoSet;
383
384 if (ProtoId >= Class->NumProtos)
385 cprintf("AddProtoToProtoPruner:assert failed: %d < %d",
386 ProtoId, Class->NumProtos);
387 assert(ProtoId < Class->NumProtos);
388
389 Index = IndexForProto (ProtoId);
390 ProtoSet = Class->ProtoSets[SetForProto (ProtoId)];
391
392 Angle = Proto->Angle;
393 #ifndef _WIN32
394 assert(!isnan(Angle));
395 #endif
396
397 FillPPCircularBits (ProtoSet->ProtoPruner[PRUNER_ANGLE], Index,
398 Angle + ANGLE_SHIFT, classify_pp_angle_pad / 360.0,
399 debug);
400
401 Angle *= 2.0 * PI;
402 Length = Proto->Length;
403
404 X = Proto->X + X_SHIFT;
405 Pad = MAX (fabs (cos (Angle)) * (Length / 2.0 +
406 classify_pp_end_pad *
407 GetPicoFeatureLength ()),
408 fabs (sin (Angle)) * (classify_pp_side_pad *
409 GetPicoFeatureLength ()));
410
411 FillPPLinearBits(ProtoSet->ProtoPruner[PRUNER_X], Index, X, Pad, debug);
412
413 Y = Proto->Y + Y_SHIFT;
414 Pad = MAX (fabs (sin (Angle)) * (Length / 2.0 +
415 classify_pp_end_pad *
416 GetPicoFeatureLength ()),
417 fabs (cos (Angle)) * (classify_pp_side_pad *
418 GetPicoFeatureLength ()));
419
420 FillPPLinearBits(ProtoSet->ProtoPruner[PRUNER_Y], Index, Y, Pad, debug);
421 } /* AddProtoToProtoPruner */
422
423
424 /*---------------------------------------------------------------------------*/
425 int BucketFor(FLOAT32 Param, FLOAT32 Offset, int NumBuckets) {
426 /*
427 ** Parameters:
428 ** Param parameter value to map into a bucket number
429 ** Offset amount to shift param before mapping it
430 ** NumBuckets number of buckets to map param into
431 ** Globals: none
432 ** Operation: This routine maps a parameter value into a bucket between
433 ** 0 and NumBuckets-1. Offset is added to the parameter
434 ** before mapping it. Values which map to buckets outside
435 ** the range are truncated to fit within the range. Mapping
436 ** is done by truncating rather than rounding.
437 ** Return: Bucket number corresponding to Param + Offset.
438 ** Exceptions: none
439 ** History: Thu Feb 14 13:24:33 1991, DSJ, Created.
440 */
441 return ClipToRange(static_cast<int>(MapParam(Param, Offset, NumBuckets)),
442 0, NumBuckets - 1);
443 } /* BucketFor */
444
445
446 /*---------------------------------------------------------------------------*/
447 int CircBucketFor(FLOAT32 Param, FLOAT32 Offset, int NumBuckets) {
448 /*
449 ** Parameters:
450 ** Param parameter value to map into a circular bucket
451 ** Offset amount to shift param before mapping it
452 ** NumBuckets number of buckets to map param into
453 ** Globals: none
454 ** Operation: This routine maps a parameter value into a bucket between
455 ** 0 and NumBuckets-1. Offset is added to the parameter
456 ** before mapping it. Values which map to buckets outside
457 ** the range are wrapped to a new value in a circular fashion.
458 ** Mapping is done by truncating rather than rounding.
459 ** Return: Bucket number corresponding to Param + Offset.
460 ** Exceptions: none
461 ** History: Thu Feb 14 13:24:33 1991, DSJ, Created.
462 */
463 int Bucket;
464
465 Bucket = static_cast<int>(MapParam(Param, Offset, NumBuckets));
466 if (Bucket < 0)
467 Bucket += NumBuckets;
468 else if (Bucket >= NumBuckets)
469 Bucket -= NumBuckets;
470 return Bucket;
471 } /* CircBucketFor */
472
473
474 /*---------------------------------------------------------------------------*/
475 #ifndef GRAPHICS_DISABLED
476 void UpdateMatchDisplay() {
477 /*
478 ** Parameters: none
479 ** Globals:
480 ** FeatureShapes display list for features
481 ** ProtoShapes display list for protos
482 ** Operation: This routine clears the global feature and proto
483 ** display lists.
484 ** Return: none
485 ** Exceptions: none
486 ** History: Thu Mar 21 15:40:19 1991, DSJ, Created.
487 */
488 if (IntMatchWindow != NULL)
489 IntMatchWindow->Update();
490 } /* ClearMatchDisplay */
491 #endif
492
493 /*---------------------------------------------------------------------------*/
494 void ConvertConfig(BIT_VECTOR Config, int ConfigId, INT_CLASS Class) {
495 /*
496 ** Parameters:
497 ** Config config to be added to class
498 ** ConfigId id to be used for new config
499 ** Class class to add new config to
500 ** Globals: none
501 ** Operation: This operation updates the config vectors of all protos
502 ** in Class to indicate that the protos with 1's in Config
503 ** belong to a new configuration identified by ConfigId.
504 ** It is assumed that the length of the Config bit vector is
505 ** equal to the number of protos in Class.
506 ** Return: none
507 ** Exceptions: none
508 ** History: Mon Feb 11 14:57:31 1991, DSJ, Created.
509 */
510 int ProtoId;
511 INT_PROTO Proto;
512 int TotalLength;
513
514 for (ProtoId = 0, TotalLength = 0;
515 ProtoId < Class->NumProtos; ProtoId++) {
516 if (test_bit(Config, ProtoId)) {
517 Proto = ProtoForProtoId(Class, ProtoId);
518 SET_BIT(Proto->Configs, ConfigId);
519 TotalLength += Class->ProtoLengths[ProtoId];
520 }
521 }
522 Class->ConfigLengths[ConfigId] = TotalLength;
523 } /* ConvertConfig */
524
525
526 namespace tesseract {
527 /*---------------------------------------------------------------------------*/
528 void Classify::ConvertProto(PROTO Proto, int ProtoId, INT_CLASS Class) {
529 /*
530 ** Parameters:
531 ** Proto floating-pt proto to be converted to integer format
532 ** ProtoId id of proto
533 ** Class integer class to add converted proto to
534 ** Globals: none
535 ** Operation: This routine converts Proto to integer format and
536 ** installs it as ProtoId in Class.
537 ** Return: none
538 ** Exceptions: none
539 ** History: Fri Feb 8 11:22:43 1991, DSJ, Created.
540 */
541 INT_PROTO P;
542 FLOAT32 Param;
543
544 assert(ProtoId < Class->NumProtos);
545
546 P = ProtoForProtoId(Class, ProtoId);
547
548 Param = Proto->A * 128;
549 P->A = TruncateParam(Param, -128, 127, NULL);
550
551 Param = -Proto->B * 256;
552 P->B = TruncateParam(Param, 0, 255, NULL);
553
554 Param = Proto->C * 128;
555 P->C = TruncateParam(Param, -128, 127, NULL);
556
557 Param = Proto->Angle * 256;
558 if (Param < 0 || Param >= 256)
559 P->Angle = 0;
560 else
561 P->Angle = (uinT8) Param;
562
563 /* round proto length to nearest integer number of pico-features */
564 Param = (Proto->Length / GetPicoFeatureLength()) + 0.5;
565 Class->ProtoLengths[ProtoId] = TruncateParam(Param, 1, 255, NULL);
566 if (classify_learning_debug_level >= 2)
567 cprintf("Converted ffeat to (A=%d,B=%d,C=%d,L=%d)",
568 P->A, P->B, P->C, Class->ProtoLengths[ProtoId]);
569 } /* ConvertProto */
570
571
572 /*---------------------------------------------------------------------------*/
573 INT_TEMPLATES Classify::CreateIntTemplates(CLASSES FloatProtos,
574 const UNICHARSET&
575 target_unicharset) {
576 /*
577 ** Parameters:
578 ** FloatProtos prototypes in old floating pt format
579 ** Globals: none
580 ** Operation: This routine converts from the old floating point format
581 ** to the new integer format.
582 ** Return: New set of training templates in integer format.
583 ** Exceptions: none
584 ** History: Thu Feb 7 14:40:42 1991, DSJ, Created.
585 */
586 INT_TEMPLATES IntTemplates;
587 CLASS_TYPE FClass;
588 INT_CLASS IClass;
589 int ClassId;
590 int ProtoId;
591 int ConfigId;
592
593 IntTemplates = NewIntTemplates();
594
595 for (ClassId = 0; ClassId < target_unicharset.size(); ClassId++) {
596 FClass = &(FloatProtos[ClassId]);
597 if (FClass->NumProtos == 0 && FClass->NumConfigs == 0 &&
598 strcmp(target_unicharset.id_to_unichar(ClassId), " ") != 0) {
599 cprintf("Warning: no protos/configs for %s in CreateIntTemplates()\n",
600 target_unicharset.id_to_unichar(ClassId));
601 }
602 assert(UnusedClassIdIn(IntTemplates, ClassId));
603 IClass = NewIntClass(FClass->NumProtos, FClass->NumConfigs);
604 FontSet fs;
605 fs.size = FClass->font_set.size();
606 fs.configs = new int[fs.size];
607 for (int i = 0; i < fs.size; ++i) {
608 fs.configs[i] = FClass->font_set.get(i);
609 }
610 if (this->fontset_table_.contains(fs)) {
611 IClass->font_set_id = this->fontset_table_.get_id(fs);
612 delete[] fs.configs;
613 } else {
614 IClass->font_set_id = this->fontset_table_.push_back(fs);
615 }
616 AddIntClass(IntTemplates, ClassId, IClass);
617
618 for (ProtoId = 0; ProtoId < FClass->NumProtos; ProtoId++) {
619 AddIntProto(IClass);
620 ConvertProto(ProtoIn(FClass, ProtoId), ProtoId, IClass);
621 AddProtoToProtoPruner(ProtoIn(FClass, ProtoId), ProtoId, IClass,
622 classify_learning_debug_level >= 2);
623 AddProtoToClassPruner(ProtoIn(FClass, ProtoId), ClassId, IntTemplates);
624 }
625
626 for (ConfigId = 0; ConfigId < FClass->NumConfigs; ConfigId++) {
627 AddIntConfig(IClass);
628 ConvertConfig(FClass->Configurations[ConfigId], ConfigId, IClass);
629 }
630 }
631 return (IntTemplates);
632 } /* CreateIntTemplates */
633 } // namespace tesseract
634
635
636 /*---------------------------------------------------------------------------*/
637 #ifndef GRAPHICS_DISABLED
638 void DisplayIntFeature(const INT_FEATURE_STRUCT* Feature, FLOAT32 Evidence) {
639 /*
640 ** Parameters:
641 ** Feature pico-feature to be displayed
642 ** Evidence best evidence for this feature (0-1)
643 ** Globals:
644 ** FeatureShapes global display list for features
645 ** Operation: This routine renders the specified feature into a
646 ** global display list.
647 ** Return: none
648 ** Exceptions: none
649 ** History: Thu Mar 21 14:45:04 1991, DSJ, Created.
650 */
651 ScrollView::Color color = GetMatchColorFor(Evidence);
652 RenderIntFeature(IntMatchWindow, Feature, color);
653 if (FeatureDisplayWindow) {
654 RenderIntFeature(FeatureDisplayWindow, Feature, color);
655 }
656 } /* DisplayIntFeature */
657
658
659 /*---------------------------------------------------------------------------*/
660 void DisplayIntProto(INT_CLASS Class, PROTO_ID ProtoId, FLOAT32 Evidence) {
661 /*
662 ** Parameters:
663 ** Class class to take proto from
664 ** ProtoId id of proto in Class to be displayed
665 ** Evidence total evidence for proto (0-1)
666 ** Globals:
667 ** ProtoShapes global display list for protos
668 ** Operation: This routine renders the specified proto into a
669 ** global display list.
670 ** Return: none
671 ** Exceptions: none
672 ** History: Thu Mar 21 14:45:04 1991, DSJ, Created.
673 */
674 ScrollView::Color color = GetMatchColorFor(Evidence);
675 RenderIntProto(IntMatchWindow, Class, ProtoId, color);
676 if (ProtoDisplayWindow) {
677 RenderIntProto(ProtoDisplayWindow, Class, ProtoId, color);
678 }
679 } /* DisplayIntProto */
680 #endif
681
682 /*---------------------------------------------------------------------------*/
683 INT_CLASS NewIntClass(int MaxNumProtos, int MaxNumConfigs) {
684 /*
685 ** Parameters:
686 ** MaxNumProtos number of protos to allocate space for
687 ** MaxNumConfigs number of configs to allocate space for
688 ** Globals: none
689 ** Operation: This routine creates a new integer class data structure
690 ** and returns it. Sufficient space is allocated
691 ** to handle the specified number of protos and configs.
692 ** Return: New class created.
693 ** Exceptions: none
694 ** History: Fri Feb 8 10:51:23 1991, DSJ, Created.
695 */
696 INT_CLASS Class;
697 PROTO_SET ProtoSet;
698 int i;
699
700 assert(MaxNumConfigs <= MAX_NUM_CONFIGS);
701
702 Class = (INT_CLASS) Emalloc(sizeof(INT_CLASS_STRUCT));
703 Class->NumProtoSets = ((MaxNumProtos + PROTOS_PER_PROTO_SET - 1) /
704 PROTOS_PER_PROTO_SET);
705
706 assert(Class->NumProtoSets <= MAX_NUM_PROTO_SETS);
707
708 Class->NumProtos = 0;
709 Class->NumConfigs = 0;
710
711 for (i = 0; i < Class->NumProtoSets; i++) {
712 /* allocate space for a proto set, install in class, and initialize */
713 ProtoSet = (PROTO_SET) Emalloc(sizeof(PROTO_SET_STRUCT));
714 memset(ProtoSet, 0, sizeof(*ProtoSet));
715 Class->ProtoSets[i] = ProtoSet;
716
717 /* allocate space for the proto lengths and install in class */
718 }
719 if (MaxNumIntProtosIn (Class) > 0) {
720 Class->ProtoLengths =
721 (uinT8 *)Emalloc(MaxNumIntProtosIn (Class) * sizeof (uinT8));
722 memset(Class->ProtoLengths, 0,
723 MaxNumIntProtosIn(Class) * sizeof(*Class->ProtoLengths));
724 } else {
725 Class->ProtoLengths = NULL;
726 }
727 memset(Class->ConfigLengths, 0, sizeof(Class->ConfigLengths));
728
729 return (Class);
730
731 } /* NewIntClass */
732
733
734 /*-------------------------------------------------------------------------*/
735 void free_int_class(INT_CLASS int_class) {
736 int i;
737
738 for (i = 0; i < int_class->NumProtoSets; i++) {
739 Efree (int_class->ProtoSets[i]);
740 }
741 if (int_class->ProtoLengths != NULL) {
742 Efree (int_class->ProtoLengths);
743 }
744 Efree(int_class);
745 }
746
747
748 /*---------------------------------------------------------------------------*/
749 INT_TEMPLATES NewIntTemplates() {
750 /*
751 ** Parameters: none
752 ** Globals: none
753 ** Operation: This routine allocates a new set of integer templates
754 ** initialized to hold 0 classes.
755 ** Return: The integer templates created.
756 ** Exceptions: none
757 ** History: Fri Feb 8 08:38:51 1991, DSJ, Created.
758 */
759 INT_TEMPLATES T;
760 int i;
761
762 T = (INT_TEMPLATES) Emalloc (sizeof (INT_TEMPLATES_STRUCT));
763 T->NumClasses = 0;
764 T->NumClassPruners = 0;
765
766 for (i = 0; i < MAX_NUM_CLASSES; i++)
767 ClassForClassId (T, i) = NULL;
768
769 return (T);
770 } /* NewIntTemplates */
771
772
773 /*---------------------------------------------------------------------------*/
774 void free_int_templates(INT_TEMPLATES templates) {
775 int i;
776
777 for (i = 0; i < templates->NumClasses; i++)
778 free_int_class(templates->Class[i]);
779 for (i = 0; i < templates->NumClassPruners; i++)
780 delete templates->ClassPruners[i];
781 Efree(templates);
782 }
783
784
785 namespace tesseract {
786 INT_TEMPLATES Classify::ReadIntTemplates(FILE *File) {
787 /*
788 ** Parameters:
789 ** File open file to read templates from
790 ** Globals: none
791 ** Operation: This routine reads a set of integer templates from
792 ** File. File must already be open and must be in the
793 ** correct binary format.
794 ** Return: Pointer to integer templates read from File.
795 ** Exceptions: none
796 ** History: Wed Feb 27 11:48:46 1991, DSJ, Created.
797 */
798 int i, j, w, x, y, z;
799 BOOL8 swap;
800 int nread;
801 int unicharset_size;
802 int version_id = 0;
803 INT_TEMPLATES Templates;
804 CLASS_PRUNER_STRUCT* Pruner;
805 INT_CLASS Class;
806 uinT8 *Lengths;
807 PROTO_SET ProtoSet;
808
809 /* variables for conversion from older inttemp formats */
810 int b, bit_number, last_cp_bit_number, new_b, new_i, new_w;
811 CLASS_ID class_id, max_class_id;
812 inT16 *IndexFor = new inT16[MAX_NUM_CLASSES];
813 CLASS_ID *ClassIdFor = new CLASS_ID[MAX_NUM_CLASSES];
814 CLASS_PRUNER_STRUCT **TempClassPruner =
815 new CLASS_PRUNER_STRUCT*[MAX_NUM_CLASS_PRUNERS];
816 uinT32 SetBitsForMask = // word with NUM_BITS_PER_CLASS
817 (1 << NUM_BITS_PER_CLASS) - 1; // set starting at bit 0
818 uinT32 Mask, NewMask, ClassBits;
819 int MaxNumConfigs = MAX_NUM_CONFIGS;
820 int WerdsPerConfigVec = WERDS_PER_CONFIG_VEC;
821
822 /* first read the high level template struct */
823 Templates = NewIntTemplates();
824 // Read Templates in parts for 64 bit compatibility.
825 if (fread(&unicharset_size, sizeof(int), 1, File) != 1)
826 cprintf("Bad read of inttemp!\n");
827 if (fread(&Templates->NumClasses,
828 sizeof(Templates->NumClasses), 1, File) != 1 ||
829 fread(&Templates->NumClassPruners,
830 sizeof(Templates->NumClassPruners), 1, File) != 1)
831 cprintf("Bad read of inttemp!\n");
832 // Swap status is determined automatically.
833 swap = Templates->NumClassPruners < 0 ||
834 Templates->NumClassPruners > MAX_NUM_CLASS_PRUNERS;
835 if (swap) {
836 Reverse32(&Templates->NumClassPruners);
837 Reverse32(&Templates->NumClasses);
838 Reverse32(&unicharset_size);
839 }
840 if (Templates->NumClasses < 0) {
841 // This file has a version id!
842 version_id = -Templates->NumClasses;
843 if (fread(&Templates->NumClasses, sizeof(Templates->NumClasses),
844 1, File) != 1)
845 cprintf("Bad read of inttemp!\n");
846 if (swap)
847 Reverse32(&Templates->NumClasses);
848 }
849
850 if (version_id < 3) {
851 MaxNumConfigs = OLD_MAX_NUM_CONFIGS;
852 WerdsPerConfigVec = OLD_WERDS_PER_CONFIG_VEC;
853 }
854
855 if (version_id < 2) {
856 for (i = 0; i < unicharset_size; ++i) {
857 if (fread(&IndexFor[i], sizeof(inT16), 1, File) != 1)
858 cprintf("Bad read of inttemp!\n");
859 }
860 for (i = 0; i < Templates->NumClasses; ++i) {
861 if (fread(&ClassIdFor[i], sizeof(CLASS_ID), 1, File) != 1)
862 cprintf("Bad read of inttemp!\n");
863 }
864 if (swap) {
865 for (i = 0; i < Templates->NumClasses; i++)
866 Reverse16(&IndexFor[i]);
867 for (i = 0; i < Templates->NumClasses; i++)
868 Reverse32(&ClassIdFor[i]);
869 }
870 }
871
872 /* then read in the class pruners */
873 for (i = 0; i < Templates->NumClassPruners; i++) {
874 Pruner = new CLASS_PRUNER_STRUCT;
875 if ((nread =
876 fread(Pruner, 1, sizeof(CLASS_PRUNER_STRUCT),
877 File)) != sizeof(CLASS_PRUNER_STRUCT))
878 cprintf("Bad read of inttemp!\n");
879 if (swap) {
880 for (x = 0; x < NUM_CP_BUCKETS; x++) {
881 for (y = 0; y < NUM_CP_BUCKETS; y++) {
882 for (z = 0; z < NUM_CP_BUCKETS; z++) {
883 for (w = 0; w < WERDS_PER_CP_VECTOR; w++) {
884 Reverse32(&Pruner->p[x][y][z][w]);
885 }
886 }
887 }
888 }
889 }
890 if (version_id < 2) {
891 TempClassPruner[i] = Pruner;
892 } else {
893 Templates->ClassPruners[i] = Pruner;
894 }
895 }
896
897 /* fix class pruners if they came from an old version of inttemp */
898 if (version_id < 2) {
899 // Allocate enough class pruners to cover all the class ids.
900 max_class_id = 0;
901 for (i = 0; i < Templates->NumClasses; i++)
902 if (ClassIdFor[i] > max_class_id)
903 max_class_id = ClassIdFor[i];
904 for (i = 0; i <= CPrunerIdFor(max_class_id); i++) {
905 Templates->ClassPruners[i] = new CLASS_PRUNER_STRUCT;
906 memset(Templates->ClassPruners[i], 0, sizeof(CLASS_PRUNER_STRUCT));
907 }
908 // Convert class pruners from the old format (indexed by class index)
909 // to the new format (indexed by class id).
910 last_cp_bit_number = NUM_BITS_PER_CLASS * Templates->NumClasses - 1;
911 for (i = 0; i < Templates->NumClassPruners; i++) {
912 for (x = 0; x < NUM_CP_BUCKETS; x++)
913 for (y = 0; y < NUM_CP_BUCKETS; y++)
914 for (z = 0; z < NUM_CP_BUCKETS; z++)
915 for (w = 0; w < WERDS_PER_CP_VECTOR; w++) {
916 if (TempClassPruner[i]->p[x][y][z][w] == 0)
917 continue;
918 for (b = 0; b < BITS_PER_WERD; b += NUM_BITS_PER_CLASS) {
919 bit_number = i * BITS_PER_CP_VECTOR + w * BITS_PER_WERD + b;
920 if (bit_number > last_cp_bit_number)
921 break; // the rest of the bits in this word are not used
922 class_id = ClassIdFor[bit_number / NUM_BITS_PER_CLASS];
923 // Single out NUM_BITS_PER_CLASS bits relating to class_id.
924 Mask = SetBitsForMask << b;
925 ClassBits = TempClassPruner[i]->p[x][y][z][w] & Mask;
926 // Move these bits to the new position in which they should
927 // appear (indexed corresponding to the class_id).
928 new_i = CPrunerIdFor(class_id);
929 new_w = CPrunerWordIndexFor(class_id);
930 new_b = CPrunerBitIndexFor(class_id) * NUM_BITS_PER_CLASS;
931 if (new_b > b) {
932 ClassBits <<= (new_b - b);
933 } else {
934 ClassBits >>= (b - new_b);
935 }
936 // Copy bits relating to class_id to the correct position
937 // in Templates->ClassPruner.
938 NewMask = SetBitsForMask << new_b;
939 Templates->ClassPruners[new_i]->p[x][y][z][new_w] &= ~NewMask;
940 Templates->ClassPruners[new_i]->p[x][y][z][new_w] |= ClassBits;
941 }
942 }
943 }
944 for (i = 0; i < Templates->NumClassPruners; i++) {
945 delete TempClassPruner[i];
946 }
947 }
948
949 /* then read in each class */
950 for (i = 0; i < Templates->NumClasses; i++) {
951 /* first read in the high level struct for the class */
952 Class = (INT_CLASS) Emalloc (sizeof (INT_CLASS_STRUCT));
953 if (fread(&Class->NumProtos, sizeof(Class->NumProtos), 1, File) != 1 ||
954 fread(&Class->NumProtoSets, sizeof(Class->NumProtoSets), 1, File) != 1 ||
955 fread(&Class->NumConfigs, sizeof(Class->NumConfigs), 1, File) != 1)
956 cprintf ("Bad read of inttemp!\n");
957 if (version_id == 0) {
958 // Only version 0 writes 5 pointless pointers to the file.
959 for (j = 0; j < 5; ++j) {
960 int junk;
961 if (fread(&junk, sizeof(junk), 1, File) != 1)
962 cprintf ("Bad read of inttemp!\n");
963 }
964 }
965 if (version_id < 4) {
966 for (j = 0; j < MaxNumConfigs; ++j) {
967 if (fread(&Class->ConfigLengths[j], sizeof(uinT16), 1, File) != 1)
968 cprintf ("Bad read of inttemp!\n");
969 }
970 if (swap) {
971 Reverse16(&Class->NumProtos);
972 for (j = 0; j < MaxNumConfigs; j++)
973 Reverse16(&Class->ConfigLengths[j]);
974 }
975 } else {
976 ASSERT_HOST(Class->NumConfigs < MaxNumConfigs);
977 for (j = 0; j < Class->NumConfigs; ++j) {
978 if (fread(&Class->ConfigLengths[j], sizeof(uinT16), 1, File) != 1)
979 cprintf ("Bad read of inttemp!\n");
980 }
981 if (swap) {
982 Reverse16(&Class->NumProtos);
983 for (j = 0; j < MaxNumConfigs; j++)
984 Reverse16(&Class->ConfigLengths[j]);
985 }
986 }
987 if (version_id < 2) {
988 ClassForClassId (Templates, ClassIdFor[i]) = Class;
989 } else {
990 ClassForClassId (Templates, i) = Class;
991 }
992
993 /* then read in the proto lengths */
994 Lengths = NULL;
995 if (MaxNumIntProtosIn (Class) > 0) {
996 Lengths = (uinT8 *)Emalloc(sizeof(uinT8) * MaxNumIntProtosIn(Class));
997 if ((nread =
998 fread((char *)Lengths, sizeof(uinT8),
999 MaxNumIntProtosIn(Class), File)) != MaxNumIntProtosIn (Class))
1000 cprintf ("Bad read of inttemp!\n");
1001 }
1002 Class->ProtoLengths = Lengths;
1003
1004 /* then read in the proto sets */
1005 for (j = 0; j < Class->NumProtoSets; j++) {
1006 ProtoSet = (PROTO_SET)Emalloc(sizeof(PROTO_SET_STRUCT));
1007 if (version_id < 3) {
1008 if ((nread =
1009 fread((char *) &ProtoSet->ProtoPruner, 1,
1010 sizeof(PROTO_PRUNER), File)) != sizeof(PROTO_PRUNER))
1011 cprintf("Bad read of inttemp!\n");
1012 for (x = 0; x < PROTOS_PER_PROTO_SET; x++) {
1013 if ((nread = fread((char *) &ProtoSet->Protos[x].A, 1,
1014 sizeof(inT8), File)) != sizeof(inT8) ||
1015 (nread = fread((char *) &ProtoSet->Protos[x].B, 1,
1016 sizeof(uinT8), File)) != sizeof(uinT8) ||
1017 (nread = fread((char *) &ProtoSet->Protos[x].C, 1,
1018 sizeof(inT8), File)) != sizeof(inT8) ||
1019 (nread = fread((char *) &ProtoSet->Protos[x].Angle, 1,
1020 sizeof(uinT8), File)) != sizeof(uinT8))
1021 cprintf("Bad read of inttemp!\n");
1022 for (y = 0; y < WerdsPerConfigVec; y++)
1023 if ((nread = fread((char *) &ProtoSet->Protos[x].Configs[y], 1,
1024 sizeof(uinT32), File)) != sizeof(uinT32))
1025 cprintf("Bad read of inttemp!\n");
1026 }
1027 } else {
1028 if ((nread =
1029 fread((char *) ProtoSet, 1, sizeof(PROTO_SET_STRUCT),
1030 File)) != sizeof(PROTO_SET_STRUCT))
1031 cprintf("Bad read of inttemp!\n");
1032 }
1033 if (swap) {
1034 for (x = 0; x < NUM_PP_PARAMS; x++)
1035 for (y = 0; y < NUM_PP_BUCKETS; y++)
1036 for (z = 0; z < WERDS_PER_PP_VECTOR; z++)
1037 Reverse32(&ProtoSet->ProtoPruner[x][y][z]);
1038 for (x = 0; x < PROTOS_PER_PROTO_SET; x++)
1039 for (y = 0; y < WerdsPerConfigVec; y++)
1040 Reverse32(&ProtoSet->Protos[x].Configs[y]);
1041 }
1042 Class->ProtoSets[j] = ProtoSet;
1043 }
1044 if (version_id < 4)
1045 Class->font_set_id = -1;
1046 else {
1047 fread(&Class->font_set_id, sizeof(int), 1, File);
1048 if (swap)
1049 Reverse32(&Class->font_set_id);
1050 }
1051 }
1052
1053 if (version_id < 2) {
1054 /* add an empty NULL class with class id 0 */
1055 assert(UnusedClassIdIn (Templates, 0));
1056 ClassForClassId (Templates, 0) = NewIntClass (1, 1);
1057 ClassForClassId (Templates, 0)->font_set_id = -1;
1058 Templates->NumClasses++;
1059 /* make sure the classes are contiguous */
1060 for (i = 0; i < MAX_NUM_CLASSES; i++) {
1061 if (i < Templates->NumClasses) {
1062 if (ClassForClassId (Templates, i) == NULL) {
1063 fprintf(stderr, "Non-contiguous class ids in inttemp\n");
1064 exit(1);
1065 }
1066 } else {
1067 if (ClassForClassId (Templates, i) != NULL) {
1068 fprintf(stderr, "Class id %d exceeds NumClassesIn (Templates) %d\n",
1069 i, Templates->NumClasses);
1070 exit(1);
1071 }
1072 }
1073 }
1074 }
1075 if (version_id >= 4) {
1076 this->fontinfo_table_.read(File, NewPermanentTessCallback(read_info), swap);
1077 if (version_id >= 5) {
1078 this->fontinfo_table_.read(File,
1079 NewPermanentTessCallback(read_spacing_info),
1080 swap);
1081 }
1082 this->fontset_table_.read(File, NewPermanentTessCallback(read_set), swap);
1083 }
1084
1085 // Clean up.
1086 delete[] IndexFor;
1087 delete[] ClassIdFor;
1088 delete[] TempClassPruner;
1089
1090 return (Templates);
1091 } /* ReadIntTemplates */
1092
1093
1094 /*---------------------------------------------------------------------------*/
1095 #ifndef GRAPHICS_DISABLED
1096 void Classify::ShowMatchDisplay() {
1097 /*
1098 ** Parameters: none
1099 ** Globals:
1100 ** FeatureShapes display list containing feature matches
1101 ** ProtoShapes display list containing proto matches
1102 ** Operation: This routine sends the shapes in the global display
1103 ** lists to the match debugger window.
1104 ** Return: none
1105 ** Exceptions: none
1106 ** History: Thu Mar 21 15:47:33 1991, DSJ, Created.
1107 */
1108 InitIntMatchWindowIfReqd();
1109 if (ProtoDisplayWindow) {
1110 ProtoDisplayWindow->Clear();
1111 }
1112 if (FeatureDisplayWindow) {
1113 FeatureDisplayWindow->Clear();
1114 }
1115 ClearFeatureSpaceWindow(
1116 static_cast<NORM_METHOD>(static_cast<int>(classify_norm_method)),
1117 IntMatchWindow);
1118 IntMatchWindow->ZoomToRectangle(INT_MIN_X, INT_MIN_Y,
1119 INT_MAX_X, INT_MAX_Y);
1120 if (ProtoDisplayWindow) {
1121 ProtoDisplayWindow->ZoomToRectangle(INT_MIN_X, INT_MIN_Y,
1122 INT_MAX_X, INT_MAX_Y);
1123 }
1124 if (FeatureDisplayWindow) {
1125 FeatureDisplayWindow->ZoomToRectangle(INT_MIN_X, INT_MIN_Y,
1126 INT_MAX_X, INT_MAX_Y);
1127 }
1128 } /* ShowMatchDisplay */
1129
1130 // Clears the given window and draws the featurespace guides for the
1131 // appropriate normalization method.
1132 void ClearFeatureSpaceWindow(NORM_METHOD norm_method, ScrollView* window) {
1133 window->Clear();
1134
1135 window->Pen(ScrollView::GREY);
1136 // Draw the feature space limit rectangle.
1137 window->Rectangle(0, 0, INT_MAX_X, INT_MAX_Y);
1138 if (norm_method == baseline) {
1139 window->SetCursor(0, INT_DESCENDER);
1140 window->DrawTo(INT_MAX_X, INT_DESCENDER);
1141 window->SetCursor(0, INT_BASELINE);
1142 window->DrawTo(INT_MAX_X, INT_BASELINE);
1143 window->SetCursor(0, INT_XHEIGHT);
1144 window->DrawTo(INT_MAX_X, INT_XHEIGHT);
1145 window->SetCursor(0, INT_CAPHEIGHT);
1146 window->DrawTo(INT_MAX_X, INT_CAPHEIGHT);
1147 } else {
1148 window->Rectangle(INT_XCENTER - INT_XRADIUS, INT_YCENTER - INT_YRADIUS,
1149 INT_XCENTER + INT_XRADIUS, INT_YCENTER + INT_YRADIUS);
1150 }
1151 }
1152 #endif
1153
1154 /*---------------------------------------------------------------------------*/
1155 void Classify::WriteIntTemplates(FILE *File, INT_TEMPLATES Templates,
1156 const UNICHARSET& target_unicharset) {
1157 /*
1158 ** Parameters:
1159 ** File open file to write templates to
1160 ** Templates templates to save into File
1161 ** Globals: none
1162 ** Operation: This routine writes Templates to File. The format
1163 ** is an efficient binary format. File must already be open
1164 ** for writing.
1165 ** Return: none
1166 ** Exceptions: none
1167 ** History: Wed Feb 27 11:48:46 1991, DSJ, Created.
1168 */
1169 int i, j;
1170 INT_CLASS Class;
1171 int unicharset_size = target_unicharset.size();
1172 int version_id = -5; // When negated by the reader -1 becomes +1 etc.
1173
1174 if (Templates->NumClasses != unicharset_size) {
1175 cprintf("Warning: executing WriteIntTemplates() with %d classes in"
1176 " Templates, while target_unicharset size is %d\n",
1177 Templates->NumClasses, unicharset_size);
1178 }
1179
1180 /* first write the high level template struct */
1181 fwrite(&unicharset_size, sizeof(unicharset_size), 1, File);
1182 fwrite(&version_id, sizeof(version_id), 1, File);
1183 fwrite(&Templates->NumClassPruners, sizeof(Templates->NumClassPruners),
1184 1, File);
1185 fwrite(&Templates->NumClasses, sizeof(Templates->NumClasses), 1, File);
1186
1187 /* then write out the class pruners */
1188 for (i = 0; i < Templates->NumClassPruners; i++)
1189 fwrite(Templates->ClassPruners[i],
1190 sizeof(CLASS_PRUNER_STRUCT), 1, File);
1191
1192 /* then write out each class */
1193 for (i = 0; i < Templates->NumClasses; i++) {
1194 Class = Templates->Class[i];
1195
1196 /* first write out the high level struct for the class */
1197 fwrite(&Class->NumProtos, sizeof(Class->NumProtos), 1, File);
1198 fwrite(&Class->NumProtoSets, sizeof(Class->NumProtoSets), 1, File);
1199 ASSERT_HOST(Class->NumConfigs == this->fontset_table_.get(Class->font_set_id).size);
1200 fwrite(&Class->NumConfigs, sizeof(Class->NumConfigs), 1, File);
1201 for (j = 0; j < Class->NumConfigs; ++j) {
1202 fwrite(&Class->ConfigLengths[j], sizeof(uinT16), 1, File);
1203 }
1204
1205 /* then write out the proto lengths */
1206 if (MaxNumIntProtosIn (Class) > 0) {
1207 fwrite ((char *) (Class->ProtoLengths), sizeof (uinT8),
1208 MaxNumIntProtosIn (Class), File);
1209 }
1210
1211 /* then write out the proto sets */
1212 for (j = 0; j < Class->NumProtoSets; j++)
1213 fwrite ((char *) Class->ProtoSets[j],
1214 sizeof (PROTO_SET_STRUCT), 1, File);
1215
1216 /* then write the fonts info */
1217 fwrite(&Class->font_set_id, sizeof(int), 1, File);
1218 }
1219
1220 /* Write the fonts info tables */
1221 this->fontinfo_table_.write(File, NewPermanentTessCallback(write_info));
1222 this->fontinfo_table_.write(File,
1223 NewPermanentTessCallback(write_spacing_info));
1224 this->fontset_table_.write(File, NewPermanentTessCallback(write_set));
1225 } /* WriteIntTemplates */
1226 } // namespace tesseract
1227
1228
1229 /*-----------------------------------------------------------------------------
1230 Private Code
1231 -----------------------------------------------------------------------------*/
1232 /*---------------------------------------------------------------------------*/
1233 FLOAT32 BucketStart(int Bucket, FLOAT32 Offset, int NumBuckets) {
1234 /*
1235 ** Parameters:
1236 ** Bucket bucket whose start is to be computed
1237 ** Offset offset used to map params to buckets
1238 ** NumBuckets total number of buckets
1239 ** Globals: none
1240 ** Operation: This routine returns the parameter value which
1241 ** corresponds to the beginning of the specified bucket.
1242 ** The bucket number should have been generated using the
1243 ** BucketFor() function with parameters Offset and NumBuckets.
1244 ** Return: Param value corresponding to start position of Bucket.
1245 ** Exceptions: none
1246 ** History: Thu Feb 14 13:24:33 1991, DSJ, Created.
1247 */
1248 return (((FLOAT32) Bucket / NumBuckets) - Offset);
1249
1250 } /* BucketStart */
1251
1252
1253 /*---------------------------------------------------------------------------*/
1254 FLOAT32 BucketEnd(int Bucket, FLOAT32 Offset, int NumBuckets) {
1255 /*
1256 ** Parameters:
1257 ** Bucket bucket whose end is to be computed
1258 ** Offset offset used to map params to buckets
1259 ** NumBuckets total number of buckets
1260 ** Globals: none
1261 ** Operation: This routine returns the parameter value which
1262 ** corresponds to the end of the specified bucket.
1263 ** The bucket number should have been generated using the
1264 ** BucketFor() function with parameters Offset and NumBuckets.
1265 ** Return: Param value corresponding to end position of Bucket.
1266 ** Exceptions: none
1267 ** History: Thu Feb 14 13:24:33 1991, DSJ, Created.
1268 */
1269 return (((FLOAT32) (Bucket + 1) / NumBuckets) - Offset);
1270 } /* BucketEnd */
1271
1272
1273 /*---------------------------------------------------------------------------*/
1274 void DoFill(FILL_SPEC *FillSpec,
1275 CLASS_PRUNER_STRUCT* Pruner,
1276 register uinT32 ClassMask,
1277 register uinT32 ClassCount,
1278 register uinT32 WordIndex) {
1279 /*
1280 ** Parameters:
1281 ** FillSpec specifies which bits to fill in pruner
1282 ** Pruner class pruner to be filled
1283 ** ClassMask indicates which bits to change in each word
1284 ** ClassCount indicates what to change bits to
1285 ** WordIndex indicates which word to change
1286 ** Globals: none
1287 ** Operation: This routine fills in the section of a class pruner
1288 ** corresponding to a single x value for a single proto of
1289 ** a class.
1290 ** Return: none
1291 ** Exceptions: none
1292 ** History: Tue Feb 19 11:11:29 1991, DSJ, Created.
1293 */
1294 register int X, Y, Angle;
1295 register uinT32 OldWord;
1296
1297 X = FillSpec->X;
1298 if (X < 0)
1299 X = 0;
1300 if (X >= NUM_CP_BUCKETS)
1301 X = NUM_CP_BUCKETS - 1;
1302
1303 if (FillSpec->YStart < 0)
1304 FillSpec->YStart = 0;
1305 if (FillSpec->YEnd >= NUM_CP_BUCKETS)
1306 FillSpec->YEnd = NUM_CP_BUCKETS - 1;
1307
1308 for (Y = FillSpec->YStart; Y <= FillSpec->YEnd; Y++)
1309 for (Angle = FillSpec->AngleStart;
1310 TRUE; CircularIncrement (Angle, NUM_CP_BUCKETS)) {
1311 OldWord = Pruner->p[X][Y][Angle][WordIndex];
1312 if (ClassCount > (OldWord & ClassMask)) {
1313 OldWord &= ~ClassMask;
1314 OldWord |= ClassCount;
1315 Pruner->p[X][Y][Angle][WordIndex] = OldWord;
1316 }
1317 if (Angle == FillSpec->AngleEnd)
1318 break;
1319 }
1320 } /* DoFill */
1321
1322
1323 /*---------------------------------------------------------------------------*/
1324 BOOL8 FillerDone(TABLE_FILLER *Filler) {
1325 /*
1326 ** Parameters:
1327 ** Filler table filler to check if done
1328 ** Globals: none
1329 ** Operation: Return TRUE if the specified table filler is done, i.e.
1330 ** if it has no more lines to fill.
1331 ** Return: TRUE if no more lines to fill, FALSE otherwise.
1332 ** Exceptions: none
1333 ** History: Tue Feb 19 10:08:05 1991, DSJ, Created.
1334 */
1335 FILL_SWITCH *Next;
1336
1337 Next = &(Filler->Switch[Filler->NextSwitch]);
1338
1339 if (Filler->X > Next->X && Next->Type == LastSwitch)
1340 return (TRUE);
1341 else
1342 return (FALSE);
1343
1344 } /* FillerDone */
1345
1346
1347 /*---------------------------------------------------------------------------*/
1348 void FillPPCircularBits(uinT32 ParamTable[NUM_PP_BUCKETS][WERDS_PER_PP_VECTOR],
1349 int Bit, FLOAT32 Center, FLOAT32 Spread, bool debug) {
1350 /*
1351 ** Parameters:
1352 ** ParamTable table of bit vectors, one per param bucket
1353 ** Bit bit position in vectors to be filled
1354 ** Center center of filled area
1355 ** Spread spread of filled area
1356 ** Globals: none
1357 ** Operation: This routine sets Bit in each bit vector whose
1358 ** bucket lies within the range Center +- Spread. The fill
1359 ** is done for a circular dimension, i.e. bucket 0 is adjacent
1360 ** to the last bucket. It is assumed that Center and Spread
1361 ** are expressed in a circular coordinate system whose range
1362 ** is 0 to 1.
1363 ** Return: none
1364 ** Exceptions: none
1365 ** History: Tue Oct 16 09:26:54 1990, DSJ, Created.
1366 */
1367 int i, FirstBucket, LastBucket;
1368
1369 if (Spread > 0.5)
1370 Spread = 0.5;
1371
1372 FirstBucket = (int) floor ((Center - Spread) * NUM_PP_BUCKETS);
1373 if (FirstBucket < 0)
1374 FirstBucket += NUM_PP_BUCKETS;
1375
1376 LastBucket = (int) floor ((Center + Spread) * NUM_PP_BUCKETS);
1377 if (LastBucket >= NUM_PP_BUCKETS)
1378 LastBucket -= NUM_PP_BUCKETS;
1379 if (debug) tprintf("Circular fill from %d to %d", FirstBucket, LastBucket);
1380 for (i = FirstBucket; TRUE; CircularIncrement (i, NUM_PP_BUCKETS)) {
1381 SET_BIT (ParamTable[i], Bit);
1382
1383 /* exit loop after we have set the bit for the last bucket */
1384 if (i == LastBucket)
1385 break;
1386 }
1387
1388 } /* FillPPCircularBits */
1389
1390
1391 /*---------------------------------------------------------------------------*/
1392 void FillPPLinearBits(uinT32 ParamTable[NUM_PP_BUCKETS][WERDS_PER_PP_VECTOR],
1393 int Bit, FLOAT32 Center, FLOAT32 Spread, bool debug) {
1394 /*
1395 ** Parameters:
1396 ** ParamTable table of bit vectors, one per param bucket
1397 ** Bit bit number being filled
1398 ** Center center of filled area
1399 ** Spread spread of filled area
1400 ** Globals: none
1401 ** Operation: This routine sets Bit in each bit vector whose
1402 ** bucket lies within the range Center +- Spread. The fill
1403 ** is done for a linear dimension, i.e. there is no wrap-around
1404 ** for this dimension. It is assumed that Center and Spread
1405 ** are expressed in a linear coordinate system whose range
1406 ** is approximately 0 to 1. Values outside this range will
1407 ** be clipped.
1408 ** Return: none
1409 ** Exceptions: none
1410 ** History: Tue Oct 16 09:26:54 1990, DSJ, Created.
1411 */
1412 int i, FirstBucket, LastBucket;
1413
1414 FirstBucket = (int) floor ((Center - Spread) * NUM_PP_BUCKETS);
1415 if (FirstBucket < 0)
1416 FirstBucket = 0;
1417
1418 LastBucket = (int) floor ((Center + Spread) * NUM_PP_BUCKETS);
1419 if (LastBucket >= NUM_PP_BUCKETS)
1420 LastBucket = NUM_PP_BUCKETS - 1;
1421
1422 if (debug) tprintf("Linear fill from %d to %d", FirstBucket, LastBucket);
1423 for (i = FirstBucket; i <= LastBucket; i++)
1424 SET_BIT (ParamTable[i], Bit);
1425
1426 } /* FillPPLinearBits */
1427
1428
1429 /*---------------------------------------------------------------------------*/
1430 #ifndef GRAPHICS_DISABLED
1431 namespace tesseract {
1432 CLASS_ID Classify::GetClassToDebug(const char *Prompt, bool* adaptive_on,
1433 bool* pretrained_on, int* shape_id) {
1434 /*
1435 ** Parameters:
1436 ** Prompt prompt to print while waiting for input from window
1437 ** Globals: none
1438 ** Operation: This routine prompts the user with Prompt and waits
1439 ** for the user to enter something in the debug window.
1440 ** Return: Character entered in the debug window.
1441 ** Exceptions: none
1442 ** History: Thu Mar 21 16:55:13 1991, DSJ, Created.
1443 */
1444 tprintf("%s\n", Prompt);
1445 SVEvent* ev;
1446 SVEventType ev_type;
1447 int unichar_id = INVALID_UNICHAR_ID;
1448 // Wait until a click or popup event.
1449 do {
1450 ev = IntMatchWindow->AwaitEvent(SVET_ANY);
1451 ev_type = ev->type;
1452 if (ev_type == SVET_POPUP) {
1453 if (ev->command_id == IDA_SHAPE_INDEX) {
1454 if (shape_table_ != NULL) {
1455 *shape_id = atoi(ev->parameter);
1456 *adaptive_on = false;
1457 *pretrained_on = true;
1458 if (*shape_id >= 0 && *shape_id < shape_table_->NumShapes()) {
1459 int font_id;
1460 shape_table_->GetFirstUnicharAndFont(*shape_id, &unichar_id,
1461 &font_id);
1462 tprintf("Shape %d, first unichar=%d, font=%d\n",
1463 *shape_id, unichar_id, font_id);
1464 return unichar_id;
1465 }
1466 tprintf("Shape index '%s' not found in shape table\n", ev->parameter);
1467 } else {
1468 tprintf("No shape table loaded!\n");
1469 }
1470 } else {
1471 if (unicharset.contains_unichar(ev->parameter)) {
1472 unichar_id = unicharset.unichar_to_id(ev->parameter);
1473 if (ev->command_id == IDA_ADAPTIVE) {
1474 *adaptive_on = true;
1475 *pretrained_on = false;
1476 *shape_id = -1;
1477 } else if (ev->command_id == IDA_STATIC) {
1478 *adaptive_on = false;
1479 *pretrained_on = true;
1480 } else {
1481 *adaptive_on = true;
1482 *pretrained_on = true;
1483 }
1484 if (ev->command_id == IDA_ADAPTIVE || shape_table_ == NULL) {
1485 *shape_id = -1;
1486 return unichar_id;
1487 }
1488 for (int s = 0; s < shape_table_->NumShapes(); ++s) {
1489 if (shape_table_->GetShape(s).ContainsUnichar(unichar_id)) {
1490 tprintf("%s\n", shape_table_->DebugStr(s).string());
1491 }
1492 }
1493 } else {
1494 tprintf("Char class '%s' not found in unicharset",
1495 ev->parameter);
1496 }
1497 }
1498 }
1499 delete ev;
1500 } while (ev_type != SVET_CLICK);
1501 return 0;
1502 } /* GetClassToDebug */
1503
1504 } // namespace tesseract
1505 #endif
1506
1507 /*---------------------------------------------------------------------------*/
1508 void GetCPPadsForLevel(int Level,
1509 FLOAT32 *EndPad,
1510 FLOAT32 *SidePad,
1511 FLOAT32 *AnglePad) {
1512 /*
1513 ** Parameters:
1514 ** Level "tightness" level to return pads for
1515 ** EndPad place to put end pad for Level
1516 ** SidePad place to put side pad for Level
1517 ** AnglePad place to put angle pad for Level
1518 ** Globals: none
1519 ** Operation: This routine copies the appropriate global pad variables
1520 ** into EndPad, SidePad, and AnglePad. This is a kludge used
1521 ** to get around the fact that global control variables cannot
1522 ** be arrays. If the specified level is illegal, the tightest
1523 ** possible pads are returned.
1524 ** Return: none (results are returned in EndPad, SidePad, and AnglePad.
1525 ** Exceptions: none
1526 ** History: Thu Feb 14 08:26:49 1991, DSJ, Created.
1527 */
1528 switch (Level) {
1529 case 0:
1530 *EndPad = classify_cp_end_pad_loose * GetPicoFeatureLength ();
1531 *SidePad = classify_cp_side_pad_loose * GetPicoFeatureLength ();
1532 *AnglePad = classify_cp_angle_pad_loose / 360.0;
1533 break;
1534
1535 case 1:
1536 *EndPad = classify_cp_end_pad_medium * GetPicoFeatureLength ();
1537 *SidePad = classify_cp_side_pad_medium * GetPicoFeatureLength ();
1538 *AnglePad = classify_cp_angle_pad_medium / 360.0;
1539 break;
1540
1541 case 2:
1542 *EndPad = classify_cp_end_pad_tight * GetPicoFeatureLength ();
1543 *SidePad = classify_cp_side_pad_tight * GetPicoFeatureLength ();
1544 *AnglePad = classify_cp_angle_pad_tight / 360.0;
1545 break;
1546
1547 default:
1548 *EndPad = classify_cp_end_pad_tight * GetPicoFeatureLength ();
1549 *SidePad = classify_cp_side_pad_tight * GetPicoFeatureLength ();
1550 *AnglePad = classify_cp_angle_pad_tight / 360.0;
1551 break;
1552 }
1553 if (*AnglePad > 0.5)
1554 *AnglePad = 0.5;
1555
1556 } /* GetCPPadsForLevel */
1557
1558
1559 /*---------------------------------------------------------------------------*/
1560 ScrollView::Color GetMatchColorFor(FLOAT32 Evidence) {
1561 /*
1562 ** Parameters:
1563 ** Evidence evidence value to return color for
1564 ** Globals: none
1565 ** Operation:
1566 ** Return: Color which corresponds to specified Evidence value.
1567 ** Exceptions: none
1568 ** History: Thu Mar 21 15:24:52 1991, DSJ, Created.
1569 */
1570
1571 assert (Evidence >= 0.0);
1572 assert (Evidence <= 1.0);
1573
1574 if (Evidence >= 0.90)
1575 return ScrollView::WHITE;
1576 else if (Evidence >= 0.75)
1577 return ScrollView::GREEN;
1578 else if (Evidence >= 0.50)
1579 return ScrollView::RED;
1580 else
1581 return ScrollView::BLUE;
1582 } /* GetMatchColorFor */
1583
1584
1585 /*---------------------------------------------------------------------------*/
1586 void GetNextFill(TABLE_FILLER *Filler, FILL_SPEC *Fill) {
1587 /*
1588 ** Parameters:
1589 ** Filler filler to get next fill spec from
1590 ** Fill place to put spec for next fill
1591 ** Globals: none
1592 ** Operation: This routine returns (in Fill) the specification of
1593 ** the next line to be filled from Filler. FillerDone() should
1594 ** always be called before GetNextFill() to ensure that we
1595 ** do not run past the end of the fill table.
1596 ** Return: none (results are returned in Fill)
1597 ** Exceptions: none
1598 ** History: Tue Feb 19 10:17:42 1991, DSJ, Created.
1599 */
1600 FILL_SWITCH *Next;
1601
1602 /* compute the fill assuming no switches will be encountered */
1603 Fill->AngleStart = Filler->AngleStart;
1604 Fill->AngleEnd = Filler->AngleEnd;
1605 Fill->X = Filler->X;
1606 Fill->YStart = Filler->YStart >> 8;
1607 Fill->YEnd = Filler->YEnd >> 8;
1608
1609 /* update the fill info and the filler for ALL switches at this X value */
1610 Next = &(Filler->Switch[Filler->NextSwitch]);
1611 while (Filler->X >= Next->X) {
1612 Fill->X = Filler->X = Next->X;
1613 if (Next->Type == StartSwitch) {
1614 Fill->YStart = Next->Y;
1615 Filler->StartDelta = Next->Delta;
1616 Filler->YStart = Next->YInit;
1617 }
1618 else if (Next->Type == EndSwitch) {
1619 Fill->YEnd = Next->Y;
1620 Filler->EndDelta = Next->Delta;
1621 Filler->YEnd = Next->YInit;
1622 }
1623 else { /* Type must be LastSwitch */
1624 break;
1625 }
1626 Filler->NextSwitch++;
1627 Next = &(Filler->Switch[Filler->NextSwitch]);
1628 }
1629
1630 /* prepare the filler for the next call to this routine */
1631 Filler->X++;
1632 Filler->YStart += Filler->StartDelta;
1633 Filler->YEnd += Filler->EndDelta;
1634
1635 } /* GetNextFill */
1636
1637
1638 /*---------------------------------------------------------------------------*/
1654 void InitTableFiller (FLOAT32 EndPad, FLOAT32 SidePad,
1655 FLOAT32 AnglePad, PROTO Proto, TABLE_FILLER * Filler)
1656 #define XS X_SHIFT
1657 #define YS Y_SHIFT
1658 #define AS ANGLE_SHIFT
1659 #define NB NUM_CP_BUCKETS
1660 {
1661 FLOAT32 Angle;
1662 FLOAT32 X, Y, HalfLength;
1663 FLOAT32 Cos, Sin;
1664 FLOAT32 XAdjust, YAdjust;
1665 FPOINT Start, Switch1, Switch2, End;
1666 int S1 = 0;
1667 int S2 = 1;
1668
1669 Angle = Proto->Angle;
1670 X = Proto->X;
1671 Y = Proto->Y;
1672 HalfLength = Proto->Length / 2.0;
1673
1674 Filler->AngleStart = CircBucketFor(Angle - AnglePad, AS, NB);
1675 Filler->AngleEnd = CircBucketFor(Angle + AnglePad, AS, NB);
1676 Filler->NextSwitch = 0;
1677
1678 if (fabs (Angle - 0.0) < HV_TOLERANCE || fabs (Angle - 0.5) < HV_TOLERANCE) {
1679 /* horizontal proto - handle as special case */
1680 Filler->X = BucketFor(X - HalfLength - EndPad, XS, NB);
1681 Filler->YStart = BucketFor(Y - SidePad, YS, NB * 256);
1682 Filler->YEnd = BucketFor(Y + SidePad, YS, NB * 256);
1683 Filler->StartDelta = 0;
1684 Filler->EndDelta = 0;
1685 Filler->Switch[0].Type = LastSwitch;
1686 Filler->Switch[0].X = BucketFor(X + HalfLength + EndPad, XS, NB);
1687 } else if (fabs(Angle - 0.25) < HV_TOLERANCE ||
1688 fabs(Angle - 0.75) < HV_TOLERANCE) {
1689 /* vertical proto - handle as special case */
1690 Filler->X = BucketFor(X - SidePad, XS, NB);
1691 Filler->YStart = BucketFor(Y - HalfLength - EndPad, YS, NB * 256);
1692 Filler->YEnd = BucketFor(Y + HalfLength + EndPad, YS, NB * 256);
1693 Filler->StartDelta = 0;
1694 Filler->EndDelta = 0;
1695 Filler->Switch[0].Type = LastSwitch;
1696 Filler->Switch[0].X = BucketFor(X + SidePad, XS, NB);
1697 } else {
1698 /* diagonal proto */
1699
1700 if ((Angle > 0.0 && Angle < 0.25) || (Angle > 0.5 && Angle < 0.75)) {
1701 /* rising diagonal proto */
1702 Angle *= 2.0 * PI;
1703 Cos = fabs(cos(Angle));
1704 Sin = fabs(sin(Angle));
1705
1706 /* compute the positions of the corners of the acceptance region */
1707 Start.x = X - (HalfLength + EndPad) * Cos - SidePad * Sin;
1708 Start.y = Y - (HalfLength + EndPad) * Sin + SidePad * Cos;
1709 End.x = 2.0 * X - Start.x;
1710 End.y = 2.0 * Y - Start.y;
1711 Switch1.x = X - (HalfLength + EndPad) * Cos + SidePad * Sin;
1712 Switch1.y = Y - (HalfLength + EndPad) * Sin - SidePad * Cos;
1713 Switch2.x = 2.0 * X - Switch1.x;
1714 Switch2.y = 2.0 * Y - Switch1.y;
1715
1716 if (Switch1.x > Switch2.x) {
1717 S1 = 1;
1718 S2 = 0;
1719 }
1720
1721 /* translate into bucket positions and deltas */
1722 Filler->X = (inT8) MapParam(Start.x, XS, NB);
1723 Filler->StartDelta = -(inT16) ((Cos / Sin) * 256);
1724 Filler->EndDelta = (inT16) ((Sin / Cos) * 256);
1725
1726 XAdjust = BucketEnd(Filler->X, XS, NB) - Start.x;
1727 YAdjust = XAdjust * Cos / Sin;
1728 Filler->YStart = (inT16) MapParam(Start.y - YAdjust, YS, NB * 256);
1729 YAdjust = XAdjust * Sin / Cos;
1730 Filler->YEnd = (inT16) MapParam(Start.y + YAdjust, YS, NB * 256);
1731
1732 Filler->Switch[S1].Type = StartSwitch;
1733 Filler->Switch[S1].X = (inT8) MapParam(Switch1.x, XS, NB);
1734 Filler->Switch[S1].Y = (inT8) MapParam(Switch1.y, YS, NB);
1735 XAdjust = Switch1.x - BucketStart(Filler->Switch[S1].X, XS, NB);
1736 YAdjust = XAdjust * Sin / Cos;
1737 Filler->Switch[S1].YInit =
1738 (inT16) MapParam(Switch1.y - YAdjust, YS, NB * 256);
1739 Filler->Switch[S1].Delta = Filler->EndDelta;
1740
1741 Filler->Switch[S2].Type = EndSwitch;
1742 Filler->Switch[S2].X = (inT8) MapParam(Switch2.x, XS, NB);
1743 Filler->Switch[S2].Y = (inT8) MapParam(Switch2.y, YS, NB);
1744 XAdjust = Switch2.x - BucketStart(Filler->Switch[S2].X, XS, NB);
1745 YAdjust = XAdjust * Cos / Sin;
1746 Filler->Switch[S2].YInit =
1747 (inT16) MapParam(Switch2.y + YAdjust, YS, NB * 256);
1748 Filler->Switch[S2].Delta = Filler->StartDelta;
1749
1750 Filler->Switch[2].Type = LastSwitch;
1751 Filler->Switch[2].X = (inT8)MapParam(End.x, XS, NB);
1752 } else {
1753 /* falling diagonal proto */
1754 Angle *= 2.0 * PI;
1755 Cos = fabs(cos(Angle));
1756 Sin = fabs(sin(Angle));
1757
1758 /* compute the positions of the corners of the acceptance region */
1759 Start.x = X - (HalfLength + EndPad) * Cos - SidePad * Sin;
1760 Start.y = Y + (HalfLength + EndPad) * Sin - SidePad * Cos;
1761 End.x = 2.0 * X - Start.x;
1762 End.y = 2.0 * Y - Start.y;
1763 Switch1.x = X - (HalfLength + EndPad) * Cos + SidePad * Sin;
1764 Switch1.y = Y + (HalfLength + EndPad) * Sin + SidePad * Cos;
1765 Switch2.x = 2.0 * X - Switch1.x;
1766 Switch2.y = 2.0 * Y - Switch1.y;
1767
1768 if (Switch1.x > Switch2.x) {
1769 S1 = 1;
1770 S2 = 0;
1771 }
1772
1773 /* translate into bucket positions and deltas */
1774 Filler->X = (inT8) MapParam(Start.x, XS, NB);
1775 Filler->StartDelta = -(inT16) ((Sin / Cos) * 256);
1776 Filler->EndDelta = (inT16) ((Cos / Sin) * 256);
1777
1778 XAdjust = BucketEnd(Filler->X, XS, NB) - Start.x;
1779 YAdjust = XAdjust * Sin / Cos;
1780 Filler->YStart = (inT16) MapParam(Start.y - YAdjust, YS, NB * 256);
1781 YAdjust = XAdjust * Cos / Sin;
1782 Filler->YEnd = (inT16) MapParam(Start.y + YAdjust, YS, NB * 256);
1783
1784 Filler->Switch[S1].Type = EndSwitch;
1785 Filler->Switch[S1].X = (inT8) MapParam(Switch1.x, XS, NB);
1786 Filler->Switch[S1].Y = (inT8) MapParam(Switch1.y, YS, NB);
1787 XAdjust = Switch1.x - BucketStart(Filler->Switch[S1].X, XS, NB);
1788 YAdjust = XAdjust * Sin / Cos;
1789 Filler->Switch[S1].YInit =
1790 (inT16) MapParam(Switch1.y + YAdjust, YS, NB * 256);
1791 Filler->Switch[S1].Delta = Filler->StartDelta;
1792
1793 Filler->Switch[S2].Type = StartSwitch;
1794 Filler->Switch[S2].X = (inT8) MapParam(Switch2.x, XS, NB);
1795 Filler->Switch[S2].Y = (inT8) MapParam(Switch2.y, YS, NB);
1796 XAdjust = Switch2.x - BucketStart(Filler->Switch[S2].X, XS, NB);
1797 YAdjust = XAdjust * Cos / Sin;
1798 Filler->Switch[S2].YInit =
1799 (inT16) MapParam(Switch2.y - YAdjust, YS, NB * 256);
1800 Filler->Switch[S2].Delta = Filler->EndDelta;
1801
1802 Filler->Switch[2].Type = LastSwitch;
1803 Filler->Switch[2].X = (inT8) MapParam(End.x, XS, NB);
1804 }
1805 }
1806 } /* InitTableFiller */
1807
1808
1809 /*---------------------------------------------------------------------------*/
1810 #ifndef GRAPHICS_DISABLED
1811 /*
1812 * Parameters:
1813 * ShapeList shape list to add feature rendering to
1814 * Feature feature to be rendered
1815 * Color color to use for feature rendering
1816 * Globals: none
1817 * Operation: This routine renders the specified feature into ShapeList.
1818 * Return: New shape list with rendering of Feature added.
1819 * @note Exceptions: none
1820 * @note History: Thu Mar 21 14:57:41 1991, DSJ, Created.
1821 */
1822 void RenderIntFeature(ScrollView *window, const INT_FEATURE_STRUCT* Feature,
1823 ScrollView::Color color) {
1824 FLOAT32 X, Y, Dx, Dy, Length;
1825
1826 window->Pen(color);
1827 assert(Feature != NULL);
1828 assert(color != 0);
1829
1830 X = Feature->X;
1831 Y = Feature->Y;
1832 Length = GetPicoFeatureLength() * 0.7 * INT_CHAR_NORM_RANGE;
1833 // The -PI has no significant effect here, but the value of Theta is computed
1834 // using BinaryAnglePlusPi in intfx.cpp.
1835 Dx = (Length / 2.0) * cos((Feature->Theta / 256.0) * 2.0 * PI - PI);
1836 Dy = (Length / 2.0) * sin((Feature->Theta / 256.0) * 2.0 * PI - PI);
1837
1838 window->SetCursor(X, Y);
1839 window->DrawTo(X + Dx, Y + Dy);
1840 } /* RenderIntFeature */
1841
1842
1843 /*---------------------------------------------------------------------------*/
1844 /*
1845 * This routine extracts the parameters of the specified
1846 * proto from the class description and adds a rendering of
1847 * the proto onto the ShapeList.
1848 *
1849 * @param Class class that proto is contained in
1850 * @param ProtoId id of proto to be rendered
1851 * @param color color to render proto in
1852 *
1853 * Globals: none
1854 *
1855 * @return New shape list with a rendering of one proto added.
1856 * @note Exceptions: none
1857 * @note History: Thu Mar 21 10:21:09 1991, DSJ, Created.
1858 */
1859 void RenderIntProto(ScrollView *window,
1860 INT_CLASS Class,
1861 PROTO_ID ProtoId,
1862 ScrollView::Color color) {
1863 PROTO_SET ProtoSet;
1864 INT_PROTO Proto;
1865 int ProtoSetIndex;
1866 int ProtoWordIndex;
1867 FLOAT32 Length;
1868 int Xmin, Xmax, Ymin, Ymax;
1869 FLOAT32 X, Y, Dx, Dy;
1870 uinT32 ProtoMask;
1871 int Bucket;
1872
1873 assert(ProtoId >= 0);
1874 assert(Class != NULL);
1875 assert(ProtoId < Class->NumProtos);
1876 assert(color != 0);
1877 window->Pen(color);
1878
1879 ProtoSet = Class->ProtoSets[SetForProto(ProtoId)];
1880 ProtoSetIndex = IndexForProto(ProtoId);
1881 Proto = &(ProtoSet->Protos[ProtoSetIndex]);
1882 Length = (Class->ProtoLengths[ProtoId] *
1883 GetPicoFeatureLength() * INT_CHAR_NORM_RANGE);
1884 ProtoMask = PPrunerMaskFor(ProtoId);
1885 ProtoWordIndex = PPrunerWordIndexFor(ProtoId);
1886
1887 // find the x and y extent of the proto from the proto pruning table
1888 Xmin = Ymin = NUM_PP_BUCKETS;
1889 Xmax = Ymax = 0;
1890 for (Bucket = 0; Bucket < NUM_PP_BUCKETS; Bucket++) {
1891 if (ProtoMask & ProtoSet->ProtoPruner[PRUNER_X][Bucket][ProtoWordIndex]) {
1892 UpdateRange(Bucket, &Xmin, &Xmax);
1893 }
1894
1895 if (ProtoMask & ProtoSet->ProtoPruner[PRUNER_Y][Bucket][ProtoWordIndex]) {
1896 UpdateRange(Bucket, &Ymin, &Ymax);
1897 }
1898 }
1899 X = (Xmin + Xmax + 1) / 2.0 * PROTO_PRUNER_SCALE;
1900 Y = (Ymin + Ymax + 1) / 2.0 * PROTO_PRUNER_SCALE;
1901 // The -PI has no significant effect here, but the value of Theta is computed
1902 // using BinaryAnglePlusPi in intfx.cpp.
1903 Dx = (Length / 2.0) * cos((Proto->Angle / 256.0) * 2.0 * PI - PI);
1904 Dy = (Length / 2.0) * sin((Proto->Angle / 256.0) * 2.0 * PI - PI);
1905
1906 window->SetCursor(X - Dx, Y - Dy);
1907 window->DrawTo(X + Dx, Y + Dy);
1908 } /* RenderIntProto */
1909 #endif
1910
1911 /*---------------------------------------------------------------------------*/
1927 int TruncateParam(FLOAT32 Param, int Min, int Max, char *Id) {
1928 if (Param < Min) {
1929 if (Id)
1930 cprintf("Warning: Param %s truncated from %f to %d!\n",
1931 Id, Param, Min);
1932 Param = Min;
1933 } else if (Param > Max) {
1934 if (Id)
1935 cprintf("Warning: Param %s truncated from %f to %d!\n",
1936 Id, Param, Max);
1937 Param = Max;
1938 }
1939 return static_cast<int>(floor(Param));
1940 } /* TruncateParam */
1941
1942
1943 /*---------------------------------------------------------------------------*/
1944 #ifndef GRAPHICS_DISABLED
1945
1949 void InitIntMatchWindowIfReqd() {
1950 if (IntMatchWindow == NULL) {
1951 IntMatchWindow = CreateFeatureSpaceWindow("IntMatchWindow", 50, 200);
1952 SVMenuNode* popup_menu = new SVMenuNode();
1953
1954 popup_menu->AddChild("Debug Adapted classes", IDA_ADAPTIVE,
1955 "x", "Class to debug");
1956 popup_menu->AddChild("Debug Static classes", IDA_STATIC,
1957 "x", "Class to debug");
1958 popup_menu->AddChild("Debug Both", IDA_BOTH,
1959 "x", "Class to debug");
1960 popup_menu->AddChild("Debug Shape Index", IDA_SHAPE_INDEX,
1961 "0", "Index to debug");
1962 popup_menu->BuildMenu(IntMatchWindow, false);
1963 }
1964 }
1965
1970 void InitProtoDisplayWindowIfReqd() {
1971 if (ProtoDisplayWindow == NULL) {
1972 ProtoDisplayWindow = CreateFeatureSpaceWindow("ProtoDisplayWindow",
1973 550, 200);
1974 }
1975 }
1976
1981 void InitFeatureDisplayWindowIfReqd() {
1982 if (FeatureDisplayWindow == NULL) {
1983 FeatureDisplayWindow = CreateFeatureSpaceWindow("FeatureDisplayWindow",
1984 50, 700);
1985 }
1986 }
1987
1988 // Creates a window of the appropriate size for displaying elements
1989 // in feature space.
1990 ScrollView* CreateFeatureSpaceWindow(const char* name, int xpos, int ypos) {
1991 return new ScrollView(name, xpos, ypos, 520, 520, 260, 260, true);
1992 }
1993 #endif // GRAPHICS_DISABLED