Tesseract  3.02
tesseract-ocr/classify/intproto.cpp
Go to the documentation of this file.
00001 /******************************************************************************
00002  ** Filename:    intproto.c
00003  ** Purpose:     Definition of data structures for integer protos.
00004  ** Author:      Dan Johnson
00005  ** History:     Thu Feb  7 14:38:16 1991, DSJ, Created.
00006  **
00007  ** (c) Copyright Hewlett-Packard Company, 1988.
00008  ** Licensed under the Apache License, Version 2.0 (the "License");
00009  ** you may not use this file except in compliance with the License.
00010  ** You may obtain a copy of the License at
00011  ** http://www.apache.org/licenses/LICENSE-2.0
00012  ** Unless required by applicable law or agreed to in writing, software
00013  ** distributed under the License is distributed on an "AS IS" BASIS,
00014  ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00015  ** See the License for the specific language governing permissions and
00016  ** limitations under the License.
00017  ******************************************************************************/
00018 /*-----------------------------------------------------------------------------
00019           Include Files and Type Defines
00020 -----------------------------------------------------------------------------*/
00021 
00022 #include <math.h>
00023 #include <stdio.h>
00024 #include <assert.h>
00025 #ifdef __UNIX__
00026 #include <unistd.h>
00027 #endif
00028 
00029 #include "classify.h"
00030 #include "const.h"
00031 #include "emalloc.h"
00032 #include "fontinfo.h"
00033 #include "genericvector.h"
00034 #include "globals.h"
00035 #include "helpers.h"
00036 #include "intproto.h"
00037 #include "mfoutline.h"
00038 #include "ndminx.h"
00039 #include "picofeat.h"
00040 #include "shapetable.h"
00041 #include "svmnode.h"
00042 
00043 // Include automatically generated configuration file if running autoconf.
00044 #ifdef HAVE_CONFIG_H
00045 #include "config_auto.h"
00046 #endif
00047 
00048 using tesseract::FontInfo;
00049 using tesseract::FontSet;
00050 using tesseract::FontSpacingInfo;
00051 
00052 /* match debug display constants*/
00053 #define PROTO_PRUNER_SCALE  (4.0)
00054 
00055 #define INT_DESCENDER (0.0  * INT_CHAR_NORM_RANGE)
00056 #define INT_BASELINE  (0.25 * INT_CHAR_NORM_RANGE)
00057 #define INT_XHEIGHT (0.75 * INT_CHAR_NORM_RANGE)
00058 #define INT_CAPHEIGHT (1.0  * INT_CHAR_NORM_RANGE)
00059 
00060 #define INT_XCENTER (0.5  * INT_CHAR_NORM_RANGE)
00061 #define INT_YCENTER (0.5  * INT_CHAR_NORM_RANGE)
00062 #define INT_XRADIUS (0.2  * INT_CHAR_NORM_RANGE)
00063 #define INT_YRADIUS (0.2  * INT_CHAR_NORM_RANGE)
00064 #define INT_MIN_X 0
00065 #define INT_MIN_Y 0
00066 #define INT_MAX_X INT_CHAR_NORM_RANGE
00067 #define INT_MAX_Y INT_CHAR_NORM_RANGE
00068 
00070 #define HV_TOLERANCE  (0.0025)   /* approx 0.9 degrees */
00071 
00072 typedef enum
00073 { StartSwitch, EndSwitch, LastSwitch }
00074 SWITCH_TYPE;
00075 #define MAX_NUM_SWITCHES  3
00076 
00077 typedef struct
00078 {
00079   SWITCH_TYPE Type;
00080   inT8 X, Y;
00081   inT16 YInit;
00082   inT16 Delta;
00083 }
00084 
00085 
00086 FILL_SWITCH;
00087 
00088 typedef struct
00089 {
00090   uinT8 NextSwitch;
00091   uinT8 AngleStart, AngleEnd;
00092   inT8 X;
00093   inT16 YStart, YEnd;
00094   inT16 StartDelta, EndDelta;
00095   FILL_SWITCH Switch[MAX_NUM_SWITCHES];
00096 }
00097 
00098 
00099 TABLE_FILLER;
00100 
00101 typedef struct
00102 {
00103   inT8 X;
00104   inT8 YStart, YEnd;
00105   uinT8 AngleStart, AngleEnd;
00106 }
00107 
00108 
00109 FILL_SPEC;
00110 
00111 
00112 /* constants for conversion from old inttemp format */
00113 #define OLD_MAX_NUM_CONFIGS      32
00114 #define OLD_WERDS_PER_CONFIG_VEC ((OLD_MAX_NUM_CONFIGS + BITS_PER_WERD - 1) /\
00115                                   BITS_PER_WERD)
00116 
00117 /*-----------------------------------------------------------------------------
00118             Macros
00119 -----------------------------------------------------------------------------*/
00121 #define CircularIncrement(i,r)  (((i) < (r) - 1)?((i)++):((i) = 0))
00122 
00124 #define MapParam(P,O,N)   (floor (((P) + (O)) * (N)))
00125 
00126 /*---------------------------------------------------------------------------
00127             Private Function Prototypes
00128 ----------------------------------------------------------------------------*/
00129 FLOAT32 BucketStart(int Bucket, FLOAT32 Offset, int NumBuckets);
00130 
00131 FLOAT32 BucketEnd(int Bucket, FLOAT32 Offset, int NumBuckets);
00132 
00133 void DoFill(FILL_SPEC *FillSpec,
00134             CLASS_PRUNER_STRUCT* Pruner,
00135             register uinT32 ClassMask,
00136             register uinT32 ClassCount,
00137             register uinT32 WordIndex);
00138 
00139 BOOL8 FillerDone(TABLE_FILLER *Filler);
00140 
00141 void FillPPCircularBits(uinT32
00142                         ParamTable[NUM_PP_BUCKETS][WERDS_PER_PP_VECTOR],
00143                         int Bit, FLOAT32 Center, FLOAT32 Spread, bool debug);
00144 
00145 void FillPPLinearBits(uinT32 ParamTable[NUM_PP_BUCKETS][WERDS_PER_PP_VECTOR],
00146                       int Bit, FLOAT32 Center, FLOAT32 Spread, bool debug);
00147 
00148 void GetCPPadsForLevel(int Level,
00149                        FLOAT32 *EndPad,
00150                        FLOAT32 *SidePad,
00151                        FLOAT32 *AnglePad);
00152 
00153 ScrollView::Color GetMatchColorFor(FLOAT32 Evidence);
00154 
00155 void GetNextFill(TABLE_FILLER *Filler, FILL_SPEC *Fill);
00156 
00157 void InitTableFiller(FLOAT32 EndPad,
00158                      FLOAT32 SidePad,
00159                      FLOAT32 AnglePad,
00160                      PROTO Proto,
00161                      TABLE_FILLER *Filler);
00162 
00163 #ifndef GRAPHICS_DISABLED
00164 void RenderIntFeature(ScrollView *window, const INT_FEATURE_STRUCT* Feature,
00165                       ScrollView::Color color);
00166 
00167 void RenderIntProto(ScrollView *window,
00168                     INT_CLASS Class,
00169                     PROTO_ID ProtoId,
00170                     ScrollView::Color color);
00171 #endif  // GRAPHICS_DISABLED
00172 
00173 int TruncateParam(FLOAT32 Param, int Min, int Max, char *Id);
00174 
00175 /*-----------------------------------------------------------------------------
00176         Global Data Definitions and Declarations
00177 -----------------------------------------------------------------------------*/
00178 
00179 /* global display lists used to display proto and feature match information*/
00180 ScrollView *IntMatchWindow = NULL;
00181 ScrollView *FeatureDisplayWindow = NULL;
00182 ScrollView *ProtoDisplayWindow = NULL;
00183 
00184 /*-----------------------------------------------------------------------------
00185         Variables
00186 -----------------------------------------------------------------------------*/
00187 
00188 /* control knobs */
00189 INT_VAR(classify_num_cp_levels, 3, "Number of Class Pruner Levels");
00190 double_VAR(classify_cp_angle_pad_loose, 45.0,
00191            "Class Pruner Angle Pad Loose");
00192 double_VAR(classify_cp_angle_pad_medium, 20.0,
00193            "Class Pruner Angle Pad Medium");
00194 double_VAR(classify_cp_angle_pad_tight, 10.0,
00195            "CLass Pruner Angle Pad Tight");
00196 double_VAR(classify_cp_end_pad_loose, 0.5, "Class Pruner End Pad Loose");
00197 double_VAR(classify_cp_end_pad_medium, 0.5, "Class Pruner End Pad Medium");
00198 double_VAR(classify_cp_end_pad_tight, 0.5, "Class Pruner End Pad Tight");
00199 double_VAR(classify_cp_side_pad_loose, 2.5, "Class Pruner Side Pad Loose");
00200 double_VAR(classify_cp_side_pad_medium, 1.2, "Class Pruner Side Pad Medium");
00201 double_VAR(classify_cp_side_pad_tight, 0.6, "Class Pruner Side Pad Tight");
00202 double_VAR(classify_pp_angle_pad, 45.0, "Proto Pruner Angle Pad");
00203 double_VAR(classify_pp_end_pad, 0.5, "Proto Prune End Pad");
00204 double_VAR(classify_pp_side_pad, 2.5, "Proto Pruner Side Pad");
00205 
00206 /*-----------------------------------------------------------------------------
00207               Public Code
00208 -----------------------------------------------------------------------------*/
00209 /*---------------------------------------------------------------------------*/
00224 void AddIntClass(INT_TEMPLATES Templates, CLASS_ID ClassId, INT_CLASS Class) {
00225   int Pruner;
00226 
00227   assert (LegalClassId (ClassId));
00228   if (ClassId != Templates->NumClasses) {
00229     fprintf(stderr, "Please make sure that classes are added to templates");
00230     fprintf(stderr, " in increasing order of ClassIds\n");
00231     exit(1);
00232   }
00233   ClassForClassId (Templates, ClassId) = Class;
00234   Templates->NumClasses++;
00235 
00236   if (Templates->NumClasses > MaxNumClassesIn (Templates)) {
00237     Pruner = Templates->NumClassPruners++;
00238     Templates->ClassPruners[Pruner] = new CLASS_PRUNER_STRUCT;
00239     memset(Templates->ClassPruners[Pruner], 0, sizeof(CLASS_PRUNER_STRUCT));
00240   }
00241 }                                /* AddIntClass */
00242 
00243 
00244 /*---------------------------------------------------------------------------*/
00257 int AddIntConfig(INT_CLASS Class) {
00258   int Index;
00259 
00260   assert(Class->NumConfigs < MAX_NUM_CONFIGS);
00261 
00262   Index = Class->NumConfigs++;
00263   Class->ConfigLengths[Index] = 0;
00264   return Index;
00265 }                                /* AddIntConfig */
00266 
00267 
00268 /*---------------------------------------------------------------------------*/
00281 int AddIntProto(INT_CLASS Class) {
00282   int Index;
00283   int ProtoSetId;
00284   PROTO_SET ProtoSet;
00285   INT_PROTO Proto;
00286   register uinT32 *Word;
00287 
00288   if (Class->NumProtos >= MAX_NUM_PROTOS)
00289     return (NO_PROTO);
00290 
00291   Index = Class->NumProtos++;
00292 
00293   if (Class->NumProtos > MaxNumIntProtosIn(Class)) {
00294     ProtoSetId = Class->NumProtoSets++;
00295 
00296     ProtoSet = (PROTO_SET) Emalloc(sizeof(PROTO_SET_STRUCT));
00297     Class->ProtoSets[ProtoSetId] = ProtoSet;
00298     memset(ProtoSet, 0, sizeof(*ProtoSet));
00299 
00300     /* reallocate space for the proto lengths and install in class */
00301     Class->ProtoLengths =
00302       (uinT8 *)Erealloc(Class->ProtoLengths,
00303                         MaxNumIntProtosIn(Class) * sizeof(uinT8));
00304     memset(&Class->ProtoLengths[Index], 0,
00305            sizeof(*Class->ProtoLengths) * (MaxNumIntProtosIn(Class) - Index));
00306   }
00307 
00308   /* initialize proto so its length is zero and it isn't in any configs */
00309   Class->ProtoLengths[Index] = 0;
00310   Proto = ProtoForProtoId (Class, Index);
00311   for (Word = Proto->Configs;
00312        Word < Proto->Configs + WERDS_PER_CONFIG_VEC; *Word++ = 0);
00313 
00314   return (Index);
00315 
00316 }                                /* AddIntProto */
00317 
00318 
00319 /*---------------------------------------------------------------------------*/
00320 void AddProtoToClassPruner (PROTO Proto, CLASS_ID ClassId,
00321                             INT_TEMPLATES Templates)
00322 /*
00323  ** Parameters:
00324  **   Proto   floating-pt proto to add to class pruner
00325  **   ClassId   class id corresponding to Proto
00326  **   Templates set of templates containing class pruner
00327  ** Globals:
00328  **   classify_num_cp_levels number of levels used in the class pruner
00329  ** Operation: This routine adds Proto to the class pruning tables
00330  **   for the specified class in Templates.
00331  ** Return: none
00332  ** Exceptions: none
00333  ** History: Wed Feb 13 08:49:54 1991, DSJ, Created.
00334  */
00335 #define MAX_LEVEL     2
00336 {
00337   CLASS_PRUNER_STRUCT* Pruner;
00338   uinT32 ClassMask;
00339   uinT32 ClassCount;
00340   uinT32 WordIndex;
00341   int Level;
00342   FLOAT32 EndPad, SidePad, AnglePad;
00343   TABLE_FILLER TableFiller;
00344   FILL_SPEC FillSpec;
00345 
00346   Pruner = CPrunerFor (Templates, ClassId);
00347   WordIndex = CPrunerWordIndexFor (ClassId);
00348   ClassMask = CPrunerMaskFor (MAX_LEVEL, ClassId);
00349 
00350   for (Level = classify_num_cp_levels - 1; Level >= 0; Level--) {
00351     GetCPPadsForLevel(Level, &EndPad, &SidePad, &AnglePad);
00352     ClassCount = CPrunerMaskFor (Level, ClassId);
00353     InitTableFiller(EndPad, SidePad, AnglePad, Proto, &TableFiller);
00354 
00355     while (!FillerDone (&TableFiller)) {
00356       GetNextFill(&TableFiller, &FillSpec);
00357       DoFill(&FillSpec, Pruner, ClassMask, ClassCount, WordIndex);
00358     }
00359   }
00360 }                                /* AddProtoToClassPruner */
00361 
00362 
00363 /*---------------------------------------------------------------------------*/
00364 void AddProtoToProtoPruner(PROTO Proto, int ProtoId,
00365                            INT_CLASS Class, bool debug) {
00366 /*
00367  ** Parameters:
00368  **   Proto floating-pt proto to be added to proto pruner
00369  **   ProtoId id of proto
00370  **   Class integer class that contains desired proto pruner
00371  ** Globals: none
00372  ** Operation: This routine updates the proto pruner lookup tables
00373  **   for Class to include a new proto identified by ProtoId
00374  **   and described by Proto.
00375  ** Return: none
00376  ** Exceptions: none
00377  ** History: Fri Feb  8 13:07:19 1991, DSJ, Created.
00378  */
00379   FLOAT32 Angle, X, Y, Length;
00380   FLOAT32 Pad;
00381   int Index;
00382   PROTO_SET ProtoSet;
00383 
00384   if (ProtoId >= Class->NumProtos)
00385     cprintf("AddProtoToProtoPruner:assert failed: %d < %d",
00386             ProtoId, Class->NumProtos);
00387   assert(ProtoId < Class->NumProtos);
00388 
00389   Index = IndexForProto (ProtoId);
00390   ProtoSet = Class->ProtoSets[SetForProto (ProtoId)];
00391 
00392   Angle = Proto->Angle;
00393 #ifndef _WIN32
00394   assert(!isnan(Angle));
00395 #endif
00396 
00397   FillPPCircularBits (ProtoSet->ProtoPruner[PRUNER_ANGLE], Index,
00398                       Angle + ANGLE_SHIFT, classify_pp_angle_pad / 360.0,
00399                       debug);
00400 
00401   Angle *= 2.0 * PI;
00402   Length = Proto->Length;
00403 
00404   X = Proto->X + X_SHIFT;
00405   Pad = MAX (fabs (cos (Angle)) * (Length / 2.0 +
00406                                    classify_pp_end_pad *
00407                                    GetPicoFeatureLength ()),
00408              fabs (sin (Angle)) * (classify_pp_side_pad *
00409                                    GetPicoFeatureLength ()));
00410 
00411   FillPPLinearBits(ProtoSet->ProtoPruner[PRUNER_X], Index, X, Pad, debug);
00412 
00413   Y = Proto->Y + Y_SHIFT;
00414   Pad = MAX (fabs (sin (Angle)) * (Length / 2.0 +
00415                                    classify_pp_end_pad *
00416                                    GetPicoFeatureLength ()),
00417              fabs (cos (Angle)) * (classify_pp_side_pad *
00418                                    GetPicoFeatureLength ()));
00419 
00420   FillPPLinearBits(ProtoSet->ProtoPruner[PRUNER_Y], Index, Y, Pad, debug);
00421 }                                /* AddProtoToProtoPruner */
00422 
00423 
00424 /*---------------------------------------------------------------------------*/
00425 int BucketFor(FLOAT32 Param, FLOAT32 Offset, int NumBuckets) {
00426 /*
00427  ** Parameters:
00428  **   Param   parameter value to map into a bucket number
00429  **   Offset    amount to shift param before mapping it
00430  **   NumBuckets  number of buckets to map param into
00431  ** Globals: none
00432  ** Operation: This routine maps a parameter value into a bucket between
00433  **   0 and NumBuckets-1.  Offset is added to the parameter
00434  **   before mapping it.  Values which map to buckets outside
00435  **   the range are truncated to fit within the range.  Mapping
00436  **   is done by truncating rather than rounding.
00437  ** Return: Bucket number corresponding to Param + Offset.
00438  ** Exceptions: none
00439  ** History: Thu Feb 14 13:24:33 1991, DSJ, Created.
00440  */
00441   return ClipToRange(static_cast<int>(MapParam(Param, Offset, NumBuckets)),
00442                      0, NumBuckets - 1);
00443 }                                /* BucketFor */
00444 
00445 
00446 /*---------------------------------------------------------------------------*/
00447 int CircBucketFor(FLOAT32 Param, FLOAT32 Offset, int NumBuckets) {
00448 /*
00449  ** Parameters:
00450  **   Param   parameter value to map into a circular bucket
00451  **   Offset    amount to shift param before mapping it
00452  **   NumBuckets  number of buckets to map param into
00453  ** Globals: none
00454  ** Operation: This routine maps a parameter value into a bucket between
00455  **   0 and NumBuckets-1.  Offset is added to the parameter
00456  **   before mapping it.  Values which map to buckets outside
00457  **   the range are wrapped to a new value in a circular fashion.
00458  **   Mapping is done by truncating rather than rounding.
00459  ** Return: Bucket number corresponding to Param + Offset.
00460  ** Exceptions: none
00461  ** History: Thu Feb 14 13:24:33 1991, DSJ, Created.
00462  */
00463   int Bucket;
00464 
00465   Bucket = static_cast<int>(MapParam(Param, Offset, NumBuckets));
00466   if (Bucket < 0)
00467     Bucket += NumBuckets;
00468   else if (Bucket >= NumBuckets)
00469     Bucket -= NumBuckets;
00470   return Bucket;
00471 }                                /* CircBucketFor */
00472 
00473 
00474 /*---------------------------------------------------------------------------*/
00475 #ifndef GRAPHICS_DISABLED
00476 void UpdateMatchDisplay() {
00477 /*
00478  ** Parameters: none
00479  ** Globals:
00480  **   FeatureShapes display list for features
00481  **   ProtoShapes display list for protos
00482  ** Operation: This routine clears the global feature and proto
00483  **   display lists.
00484  ** Return: none
00485  ** Exceptions: none
00486  ** History: Thu Mar 21 15:40:19 1991, DSJ, Created.
00487  */
00488   if (IntMatchWindow != NULL)
00489     IntMatchWindow->Update();
00490 }                                /* ClearMatchDisplay */
00491 #endif
00492 
00493 /*---------------------------------------------------------------------------*/
00494 void ConvertConfig(BIT_VECTOR Config, int ConfigId, INT_CLASS Class) {
00495 /*
00496  ** Parameters:
00497  **   Config    config to be added to class
00498  **   ConfigId  id to be used for new config
00499  **   Class   class to add new config to
00500  ** Globals: none
00501  ** Operation: This operation updates the config vectors of all protos
00502  **   in Class to indicate that the protos with 1's in Config
00503  **   belong to a new configuration identified by ConfigId.
00504  **   It is assumed that the length of the Config bit vector is
00505  **   equal to the number of protos in Class.
00506  ** Return: none
00507  ** Exceptions: none
00508  ** History: Mon Feb 11 14:57:31 1991, DSJ, Created.
00509  */
00510   int ProtoId;
00511   INT_PROTO Proto;
00512   int TotalLength;
00513 
00514   for (ProtoId = 0, TotalLength = 0;
00515     ProtoId < Class->NumProtos; ProtoId++) {
00516     if (test_bit(Config, ProtoId)) {
00517       Proto = ProtoForProtoId(Class, ProtoId);
00518       SET_BIT(Proto->Configs, ConfigId);
00519       TotalLength += Class->ProtoLengths[ProtoId];
00520     }
00521   }
00522   Class->ConfigLengths[ConfigId] = TotalLength;
00523 }                                /* ConvertConfig */
00524 
00525 
00526 namespace tesseract {
00527 /*---------------------------------------------------------------------------*/
00528 void Classify::ConvertProto(PROTO Proto, int ProtoId, INT_CLASS Class) {
00529 /*
00530  ** Parameters:
00531  **   Proto floating-pt proto to be converted to integer format
00532  **   ProtoId id of proto
00533  **   Class integer class to add converted proto to
00534  ** Globals: none
00535  ** Operation: This routine converts Proto to integer format and
00536  **   installs it as ProtoId in Class.
00537  ** Return: none
00538  ** Exceptions: none
00539  ** History: Fri Feb  8 11:22:43 1991, DSJ, Created.
00540  */
00541   INT_PROTO P;
00542   FLOAT32 Param;
00543 
00544   assert(ProtoId < Class->NumProtos);
00545 
00546   P = ProtoForProtoId(Class, ProtoId);
00547 
00548   Param = Proto->A * 128;
00549   P->A = TruncateParam(Param, -128, 127, NULL);
00550 
00551   Param = -Proto->B * 256;
00552   P->B = TruncateParam(Param, 0, 255, NULL);
00553 
00554   Param = Proto->C * 128;
00555   P->C = TruncateParam(Param, -128, 127, NULL);
00556 
00557   Param = Proto->Angle * 256;
00558   if (Param < 0 || Param >= 256)
00559     P->Angle = 0;
00560   else
00561     P->Angle = (uinT8) Param;
00562 
00563   /* round proto length to nearest integer number of pico-features */
00564   Param = (Proto->Length / GetPicoFeatureLength()) + 0.5;
00565   Class->ProtoLengths[ProtoId] = TruncateParam(Param, 1, 255, NULL);
00566   if (classify_learning_debug_level >= 2)
00567     cprintf("Converted ffeat to (A=%d,B=%d,C=%d,L=%d)",
00568             P->A, P->B, P->C, Class->ProtoLengths[ProtoId]);
00569 }                                /* ConvertProto */
00570 
00571 
00572 /*---------------------------------------------------------------------------*/
00573 INT_TEMPLATES Classify::CreateIntTemplates(CLASSES FloatProtos,
00574                                            const UNICHARSET&
00575                                            target_unicharset) {
00576 /*
00577  ** Parameters:
00578  **   FloatProtos prototypes in old floating pt format
00579  ** Globals: none
00580  ** Operation: This routine converts from the old floating point format
00581  **   to the new integer format.
00582  ** Return: New set of training templates in integer format.
00583  ** Exceptions: none
00584  ** History: Thu Feb  7 14:40:42 1991, DSJ, Created.
00585  */
00586   INT_TEMPLATES IntTemplates;
00587   CLASS_TYPE FClass;
00588   INT_CLASS IClass;
00589   int ClassId;
00590   int ProtoId;
00591   int ConfigId;
00592 
00593   IntTemplates = NewIntTemplates();
00594 
00595   for (ClassId = 0; ClassId < target_unicharset.size(); ClassId++) {
00596     FClass = &(FloatProtos[ClassId]);
00597     if (FClass->NumProtos == 0 && FClass->NumConfigs == 0 &&
00598         strcmp(target_unicharset.id_to_unichar(ClassId), " ") != 0) {
00599       cprintf("Warning: no protos/configs for %s in CreateIntTemplates()\n",
00600               target_unicharset.id_to_unichar(ClassId));
00601     }
00602     assert(UnusedClassIdIn(IntTemplates, ClassId));
00603     IClass = NewIntClass(FClass->NumProtos, FClass->NumConfigs);
00604     FontSet fs;
00605     fs.size = FClass->font_set.size();
00606     fs.configs = new int[fs.size];
00607     for (int i = 0; i < fs.size; ++i) {
00608       fs.configs[i] = FClass->font_set.get(i);
00609     }
00610     if (this->fontset_table_.contains(fs)) {
00611       IClass->font_set_id = this->fontset_table_.get_id(fs);
00612       delete[] fs.configs;
00613     } else {
00614       IClass->font_set_id = this->fontset_table_.push_back(fs);
00615     }
00616     AddIntClass(IntTemplates, ClassId, IClass);
00617 
00618     for (ProtoId = 0; ProtoId < FClass->NumProtos; ProtoId++) {
00619       AddIntProto(IClass);
00620       ConvertProto(ProtoIn(FClass, ProtoId), ProtoId, IClass);
00621       AddProtoToProtoPruner(ProtoIn(FClass, ProtoId), ProtoId, IClass,
00622                             classify_learning_debug_level >= 2);
00623       AddProtoToClassPruner(ProtoIn(FClass, ProtoId), ClassId, IntTemplates);
00624     }
00625 
00626     for (ConfigId = 0; ConfigId < FClass->NumConfigs; ConfigId++) {
00627       AddIntConfig(IClass);
00628       ConvertConfig(FClass->Configurations[ConfigId], ConfigId, IClass);
00629     }
00630   }
00631   return (IntTemplates);
00632 }                                /* CreateIntTemplates */
00633 }  // namespace tesseract
00634 
00635 
00636 /*---------------------------------------------------------------------------*/
00637 #ifndef GRAPHICS_DISABLED
00638 void DisplayIntFeature(const INT_FEATURE_STRUCT* Feature, FLOAT32 Evidence) {
00639 /*
00640  ** Parameters:
00641  **   Feature   pico-feature to be displayed
00642  **   Evidence  best evidence for this feature (0-1)
00643  ** Globals:
00644  **   FeatureShapes global display list for features
00645  ** Operation: This routine renders the specified feature into a
00646  **   global display list.
00647  ** Return: none
00648  ** Exceptions: none
00649  ** History: Thu Mar 21 14:45:04 1991, DSJ, Created.
00650  */
00651   ScrollView::Color color = GetMatchColorFor(Evidence);
00652   RenderIntFeature(IntMatchWindow, Feature, color);
00653   if (FeatureDisplayWindow) {
00654     RenderIntFeature(FeatureDisplayWindow, Feature, color);
00655   }
00656 }                                /* DisplayIntFeature */
00657 
00658 
00659 /*---------------------------------------------------------------------------*/
00660 void DisplayIntProto(INT_CLASS Class, PROTO_ID ProtoId, FLOAT32 Evidence) {
00661 /*
00662  ** Parameters:
00663  **   Class   class to take proto from
00664  **   ProtoId   id of proto in Class to be displayed
00665  **   Evidence  total evidence for proto (0-1)
00666  ** Globals:
00667  **   ProtoShapes global display list for protos
00668  ** Operation: This routine renders the specified proto into a
00669  **   global display list.
00670  ** Return: none
00671  ** Exceptions: none
00672  ** History: Thu Mar 21 14:45:04 1991, DSJ, Created.
00673  */
00674   ScrollView::Color color = GetMatchColorFor(Evidence);
00675   RenderIntProto(IntMatchWindow, Class, ProtoId, color);
00676   if (ProtoDisplayWindow) {
00677     RenderIntProto(ProtoDisplayWindow, Class, ProtoId, color);
00678   }
00679 }                                /* DisplayIntProto */
00680 #endif
00681 
00682 /*---------------------------------------------------------------------------*/
00683 INT_CLASS NewIntClass(int MaxNumProtos, int MaxNumConfigs) {
00684 /*
00685  ** Parameters:
00686  **   MaxNumProtos  number of protos to allocate space for
00687  **   MaxNumConfigs number of configs to allocate space for
00688  ** Globals: none
00689  ** Operation: This routine creates a new integer class data structure
00690  **   and returns it.  Sufficient space is allocated
00691  **   to handle the specified number of protos and configs.
00692  ** Return: New class created.
00693  ** Exceptions: none
00694  ** History: Fri Feb  8 10:51:23 1991, DSJ, Created.
00695  */
00696   INT_CLASS Class;
00697   PROTO_SET ProtoSet;
00698   int i;
00699 
00700   assert(MaxNumConfigs <= MAX_NUM_CONFIGS);
00701 
00702   Class = (INT_CLASS) Emalloc(sizeof(INT_CLASS_STRUCT));
00703   Class->NumProtoSets = ((MaxNumProtos + PROTOS_PER_PROTO_SET - 1) /
00704                             PROTOS_PER_PROTO_SET);
00705 
00706   assert(Class->NumProtoSets <= MAX_NUM_PROTO_SETS);
00707 
00708   Class->NumProtos = 0;
00709   Class->NumConfigs = 0;
00710 
00711   for (i = 0; i < Class->NumProtoSets; i++) {
00712     /* allocate space for a proto set, install in class, and initialize */
00713     ProtoSet = (PROTO_SET) Emalloc(sizeof(PROTO_SET_STRUCT));
00714     memset(ProtoSet, 0, sizeof(*ProtoSet));
00715     Class->ProtoSets[i] = ProtoSet;
00716 
00717     /* allocate space for the proto lengths and install in class */
00718   }
00719   if (MaxNumIntProtosIn (Class) > 0) {
00720     Class->ProtoLengths =
00721       (uinT8 *)Emalloc(MaxNumIntProtosIn (Class) * sizeof (uinT8));
00722     memset(Class->ProtoLengths, 0,
00723            MaxNumIntProtosIn(Class) * sizeof(*Class->ProtoLengths));
00724   } else {
00725     Class->ProtoLengths = NULL;
00726   }
00727   memset(Class->ConfigLengths, 0, sizeof(Class->ConfigLengths));
00728 
00729   return (Class);
00730 
00731 }                                /* NewIntClass */
00732 
00733 
00734 /*-------------------------------------------------------------------------*/
00735 void free_int_class(INT_CLASS int_class) {
00736   int i;
00737 
00738   for (i = 0; i < int_class->NumProtoSets; i++) {
00739     Efree (int_class->ProtoSets[i]);
00740   }
00741   if (int_class->ProtoLengths != NULL) {
00742     Efree (int_class->ProtoLengths);
00743   }
00744   Efree(int_class);
00745 }
00746 
00747 
00748 /*---------------------------------------------------------------------------*/
00749 INT_TEMPLATES NewIntTemplates() {
00750 /*
00751  ** Parameters: none
00752  ** Globals: none
00753  ** Operation: This routine allocates a new set of integer templates
00754  **   initialized to hold 0 classes.
00755  ** Return: The integer templates created.
00756  ** Exceptions: none
00757  ** History: Fri Feb  8 08:38:51 1991, DSJ, Created.
00758  */
00759   INT_TEMPLATES T;
00760   int i;
00761 
00762   T = (INT_TEMPLATES) Emalloc (sizeof (INT_TEMPLATES_STRUCT));
00763   T->NumClasses = 0;
00764   T->NumClassPruners = 0;
00765 
00766   for (i = 0; i < MAX_NUM_CLASSES; i++)
00767     ClassForClassId (T, i) = NULL;
00768 
00769   return (T);
00770 }                                /* NewIntTemplates */
00771 
00772 
00773 /*---------------------------------------------------------------------------*/
00774 void free_int_templates(INT_TEMPLATES templates) {
00775   int i;
00776 
00777   for (i = 0; i < templates->NumClasses; i++)
00778     free_int_class(templates->Class[i]);
00779   for (i = 0; i < templates->NumClassPruners; i++)
00780     delete templates->ClassPruners[i];
00781   Efree(templates);
00782 }
00783 
00784 
00785 namespace tesseract {
00786 INT_TEMPLATES Classify::ReadIntTemplates(FILE *File) {
00787 /*
00788  ** Parameters:
00789  **   File    open file to read templates from
00790  ** Globals: none
00791  ** Operation: This routine reads a set of integer templates from
00792  **   File.  File must already be open and must be in the
00793  **   correct binary format.
00794  ** Return: Pointer to integer templates read from File.
00795  ** Exceptions: none
00796  ** History: Wed Feb 27 11:48:46 1991, DSJ, Created.
00797  */
00798   int i, j, w, x, y, z;
00799   BOOL8 swap;
00800   int nread;
00801   int unicharset_size;
00802   int version_id = 0;
00803   INT_TEMPLATES Templates;
00804   CLASS_PRUNER_STRUCT* Pruner;
00805   INT_CLASS Class;
00806   uinT8 *Lengths;
00807   PROTO_SET ProtoSet;
00808 
00809   /* variables for conversion from older inttemp formats */
00810   int b, bit_number, last_cp_bit_number, new_b, new_i, new_w;
00811   CLASS_ID class_id, max_class_id;
00812   inT16 *IndexFor = new inT16[MAX_NUM_CLASSES];
00813   CLASS_ID *ClassIdFor = new CLASS_ID[MAX_NUM_CLASSES];
00814   CLASS_PRUNER_STRUCT **TempClassPruner =
00815       new CLASS_PRUNER_STRUCT*[MAX_NUM_CLASS_PRUNERS];
00816   uinT32 SetBitsForMask =           // word with NUM_BITS_PER_CLASS
00817     (1 << NUM_BITS_PER_CLASS) - 1;  // set starting at bit 0
00818   uinT32 Mask, NewMask, ClassBits;
00819   int MaxNumConfigs = MAX_NUM_CONFIGS;
00820   int WerdsPerConfigVec = WERDS_PER_CONFIG_VEC;
00821 
00822   /* first read the high level template struct */
00823   Templates = NewIntTemplates();
00824   // Read Templates in parts for 64 bit compatibility.
00825   if (fread(&unicharset_size, sizeof(int), 1, File) != 1)
00826     cprintf("Bad read of inttemp!\n");
00827   if (fread(&Templates->NumClasses,
00828             sizeof(Templates->NumClasses), 1, File) != 1 ||
00829       fread(&Templates->NumClassPruners,
00830             sizeof(Templates->NumClassPruners), 1, File) != 1)
00831     cprintf("Bad read of inttemp!\n");
00832   // Swap status is determined automatically.
00833   swap = Templates->NumClassPruners < 0 ||
00834     Templates->NumClassPruners > MAX_NUM_CLASS_PRUNERS;
00835   if (swap) {
00836     Reverse32(&Templates->NumClassPruners);
00837     Reverse32(&Templates->NumClasses);
00838     Reverse32(&unicharset_size);
00839   }
00840   if (Templates->NumClasses < 0) {
00841     // This file has a version id!
00842     version_id = -Templates->NumClasses;
00843     if (fread(&Templates->NumClasses, sizeof(Templates->NumClasses),
00844               1, File) != 1)
00845       cprintf("Bad read of inttemp!\n");
00846     if (swap)
00847       Reverse32(&Templates->NumClasses);
00848   }
00849 
00850   if (version_id < 3) {
00851     MaxNumConfigs = OLD_MAX_NUM_CONFIGS;
00852     WerdsPerConfigVec = OLD_WERDS_PER_CONFIG_VEC;
00853   }
00854 
00855   if (version_id < 2) {
00856     for (i = 0; i < unicharset_size; ++i) {
00857       if (fread(&IndexFor[i], sizeof(inT16), 1, File) != 1)
00858         cprintf("Bad read of inttemp!\n");
00859     }
00860     for (i = 0; i < Templates->NumClasses; ++i) {
00861       if (fread(&ClassIdFor[i], sizeof(CLASS_ID), 1, File) != 1)
00862         cprintf("Bad read of inttemp!\n");
00863     }
00864     if (swap) {
00865       for (i = 0; i < Templates->NumClasses; i++)
00866         Reverse16(&IndexFor[i]);
00867       for (i = 0; i < Templates->NumClasses; i++)
00868         Reverse32(&ClassIdFor[i]);
00869     }
00870   }
00871 
00872   /* then read in the class pruners */
00873   for (i = 0; i < Templates->NumClassPruners; i++) {
00874     Pruner = new CLASS_PRUNER_STRUCT;
00875     if ((nread =
00876          fread(Pruner, 1, sizeof(CLASS_PRUNER_STRUCT),
00877                 File)) != sizeof(CLASS_PRUNER_STRUCT))
00878       cprintf("Bad read of inttemp!\n");
00879     if (swap) {
00880       for (x = 0; x < NUM_CP_BUCKETS; x++) {
00881         for (y = 0; y < NUM_CP_BUCKETS; y++) {
00882           for (z = 0; z < NUM_CP_BUCKETS; z++) {
00883             for (w = 0; w < WERDS_PER_CP_VECTOR; w++) {
00884               Reverse32(&Pruner->p[x][y][z][w]);
00885             }
00886           }
00887         }
00888       }
00889     }
00890     if (version_id < 2) {
00891       TempClassPruner[i] = Pruner;
00892     } else {
00893       Templates->ClassPruners[i] = Pruner;
00894     }
00895   }
00896 
00897   /* fix class pruners if they came from an old version of inttemp */
00898   if (version_id < 2) {
00899     // Allocate enough class pruners to cover all the class ids.
00900     max_class_id = 0;
00901     for (i = 0; i < Templates->NumClasses; i++)
00902       if (ClassIdFor[i] > max_class_id)
00903         max_class_id = ClassIdFor[i];
00904     for (i = 0; i <= CPrunerIdFor(max_class_id); i++) {
00905       Templates->ClassPruners[i] = new CLASS_PRUNER_STRUCT;
00906       memset(Templates->ClassPruners[i], 0, sizeof(CLASS_PRUNER_STRUCT));
00907     }
00908     // Convert class pruners from the old format (indexed by class index)
00909     // to the new format (indexed by class id).
00910     last_cp_bit_number = NUM_BITS_PER_CLASS * Templates->NumClasses - 1;
00911     for (i = 0; i < Templates->NumClassPruners; i++) {
00912       for (x = 0; x < NUM_CP_BUCKETS; x++)
00913         for (y = 0; y < NUM_CP_BUCKETS; y++)
00914           for (z = 0; z < NUM_CP_BUCKETS; z++)
00915             for (w = 0; w < WERDS_PER_CP_VECTOR; w++) {
00916               if (TempClassPruner[i]->p[x][y][z][w] == 0)
00917                 continue;
00918               for (b = 0; b < BITS_PER_WERD; b += NUM_BITS_PER_CLASS) {
00919                 bit_number = i * BITS_PER_CP_VECTOR + w * BITS_PER_WERD + b;
00920                 if (bit_number > last_cp_bit_number)
00921                   break; // the rest of the bits in this word are not used
00922                 class_id = ClassIdFor[bit_number / NUM_BITS_PER_CLASS];
00923                 // Single out NUM_BITS_PER_CLASS bits relating to class_id.
00924                 Mask = SetBitsForMask << b;
00925                 ClassBits = TempClassPruner[i]->p[x][y][z][w] & Mask;
00926                 // Move these bits to the new position in which they should
00927                 // appear (indexed corresponding to the class_id).
00928                 new_i = CPrunerIdFor(class_id);
00929                 new_w = CPrunerWordIndexFor(class_id);
00930                 new_b = CPrunerBitIndexFor(class_id) * NUM_BITS_PER_CLASS;
00931                 if (new_b > b) {
00932                   ClassBits <<= (new_b - b);
00933                 } else {
00934                   ClassBits >>= (b - new_b);
00935                 }
00936                 // Copy bits relating to class_id to the correct position
00937                 // in Templates->ClassPruner.
00938                 NewMask = SetBitsForMask << new_b;
00939                 Templates->ClassPruners[new_i]->p[x][y][z][new_w] &= ~NewMask;
00940                 Templates->ClassPruners[new_i]->p[x][y][z][new_w] |= ClassBits;
00941               }
00942             }
00943     }
00944     for (i = 0; i < Templates->NumClassPruners; i++) {
00945       delete TempClassPruner[i];
00946     }
00947   }
00948 
00949   /* then read in each class */
00950   for (i = 0; i < Templates->NumClasses; i++) {
00951     /* first read in the high level struct for the class */
00952     Class = (INT_CLASS) Emalloc (sizeof (INT_CLASS_STRUCT));
00953     if (fread(&Class->NumProtos, sizeof(Class->NumProtos), 1, File) != 1 ||
00954         fread(&Class->NumProtoSets, sizeof(Class->NumProtoSets), 1, File) != 1 ||
00955         fread(&Class->NumConfigs, sizeof(Class->NumConfigs), 1, File) != 1)
00956       cprintf ("Bad read of inttemp!\n");
00957     if (version_id == 0) {
00958       // Only version 0 writes 5 pointless pointers to the file.
00959       for (j = 0; j < 5; ++j) {
00960         int junk;
00961         if (fread(&junk, sizeof(junk), 1, File) != 1)
00962           cprintf ("Bad read of inttemp!\n");
00963       }
00964     }
00965     if (version_id < 4) {
00966       for (j = 0; j < MaxNumConfigs; ++j) {
00967         if (fread(&Class->ConfigLengths[j], sizeof(uinT16), 1, File) != 1)
00968           cprintf ("Bad read of inttemp!\n");
00969       }
00970       if (swap) {
00971         Reverse16(&Class->NumProtos);
00972         for (j = 0; j < MaxNumConfigs; j++)
00973           Reverse16(&Class->ConfigLengths[j]);
00974       }
00975     } else {
00976       ASSERT_HOST(Class->NumConfigs < MaxNumConfigs);
00977       for (j = 0; j < Class->NumConfigs; ++j) {
00978         if (fread(&Class->ConfigLengths[j], sizeof(uinT16), 1, File) != 1)
00979           cprintf ("Bad read of inttemp!\n");
00980       }
00981       if (swap) {
00982         Reverse16(&Class->NumProtos);
00983         for (j = 0; j < MaxNumConfigs; j++)
00984           Reverse16(&Class->ConfigLengths[j]);
00985       }
00986     }
00987     if (version_id < 2) {
00988       ClassForClassId (Templates, ClassIdFor[i]) = Class;
00989     } else {
00990       ClassForClassId (Templates, i) = Class;
00991     }
00992 
00993     /* then read in the proto lengths */
00994     Lengths = NULL;
00995     if (MaxNumIntProtosIn (Class) > 0) {
00996       Lengths = (uinT8 *)Emalloc(sizeof(uinT8) * MaxNumIntProtosIn(Class));
00997       if ((nread =
00998            fread((char *)Lengths, sizeof(uinT8),
00999                  MaxNumIntProtosIn(Class), File)) != MaxNumIntProtosIn (Class))
01000         cprintf ("Bad read of inttemp!\n");
01001     }
01002     Class->ProtoLengths = Lengths;
01003 
01004     /* then read in the proto sets */
01005     for (j = 0; j < Class->NumProtoSets; j++) {
01006       ProtoSet = (PROTO_SET)Emalloc(sizeof(PROTO_SET_STRUCT));
01007       if (version_id < 3) {
01008         if ((nread =
01009              fread((char *) &ProtoSet->ProtoPruner, 1,
01010                     sizeof(PROTO_PRUNER), File)) != sizeof(PROTO_PRUNER))
01011           cprintf("Bad read of inttemp!\n");
01012         for (x = 0; x < PROTOS_PER_PROTO_SET; x++) {
01013           if ((nread = fread((char *) &ProtoSet->Protos[x].A, 1,
01014                              sizeof(inT8), File)) != sizeof(inT8) ||
01015               (nread = fread((char *) &ProtoSet->Protos[x].B, 1,
01016                              sizeof(uinT8), File)) != sizeof(uinT8) ||
01017               (nread = fread((char *) &ProtoSet->Protos[x].C, 1,
01018                              sizeof(inT8), File)) != sizeof(inT8) ||
01019               (nread = fread((char *) &ProtoSet->Protos[x].Angle, 1,
01020                              sizeof(uinT8), File)) != sizeof(uinT8))
01021             cprintf("Bad read of inttemp!\n");
01022           for (y = 0; y < WerdsPerConfigVec; y++)
01023             if ((nread = fread((char *) &ProtoSet->Protos[x].Configs[y], 1,
01024                                sizeof(uinT32), File)) != sizeof(uinT32))
01025               cprintf("Bad read of inttemp!\n");
01026         }
01027       } else {
01028         if ((nread =
01029              fread((char *) ProtoSet, 1, sizeof(PROTO_SET_STRUCT),
01030                    File)) != sizeof(PROTO_SET_STRUCT))
01031           cprintf("Bad read of inttemp!\n");
01032       }
01033       if (swap) {
01034         for (x = 0; x < NUM_PP_PARAMS; x++)
01035           for (y = 0; y < NUM_PP_BUCKETS; y++)
01036             for (z = 0; z < WERDS_PER_PP_VECTOR; z++)
01037               Reverse32(&ProtoSet->ProtoPruner[x][y][z]);
01038         for (x = 0; x < PROTOS_PER_PROTO_SET; x++)
01039           for (y = 0; y < WerdsPerConfigVec; y++)
01040             Reverse32(&ProtoSet->Protos[x].Configs[y]);
01041       }
01042       Class->ProtoSets[j] = ProtoSet;
01043     }
01044     if (version_id < 4)
01045       Class->font_set_id = -1;
01046     else {
01047       fread(&Class->font_set_id, sizeof(int), 1, File);
01048       if (swap)
01049         Reverse32(&Class->font_set_id);
01050     }
01051   }
01052 
01053   if (version_id < 2) {
01054     /* add an empty NULL class with class id 0 */
01055     assert(UnusedClassIdIn (Templates, 0));
01056     ClassForClassId (Templates, 0) = NewIntClass (1, 1);
01057     ClassForClassId (Templates, 0)->font_set_id = -1;
01058     Templates->NumClasses++;
01059     /* make sure the classes are contiguous */
01060     for (i = 0; i < MAX_NUM_CLASSES; i++) {
01061       if (i < Templates->NumClasses) {
01062         if (ClassForClassId (Templates, i) == NULL) {
01063           fprintf(stderr, "Non-contiguous class ids in inttemp\n");
01064           exit(1);
01065         }
01066       } else {
01067         if (ClassForClassId (Templates, i) != NULL) {
01068           fprintf(stderr, "Class id %d exceeds NumClassesIn (Templates) %d\n",
01069                   i, Templates->NumClasses);
01070           exit(1);
01071         }
01072       }
01073     }
01074   }
01075   if (version_id >= 4) {
01076     this->fontinfo_table_.read(File, NewPermanentTessCallback(read_info), swap);
01077     if (version_id >= 5) {
01078       this->fontinfo_table_.read(File,
01079                                  NewPermanentTessCallback(read_spacing_info),
01080                                  swap);
01081     }
01082     this->fontset_table_.read(File, NewPermanentTessCallback(read_set), swap);
01083   }
01084 
01085   // Clean up.
01086   delete[] IndexFor;
01087   delete[] ClassIdFor;
01088   delete[] TempClassPruner;
01089 
01090   return (Templates);
01091 }                                /* ReadIntTemplates */
01092 
01093 
01094 /*---------------------------------------------------------------------------*/
01095 #ifndef GRAPHICS_DISABLED
01096 void Classify::ShowMatchDisplay() {
01097 /*
01098  ** Parameters: none
01099  ** Globals:
01100  **   FeatureShapes display list containing feature matches
01101  **   ProtoShapes display list containing proto matches
01102  ** Operation: This routine sends the shapes in the global display
01103  **   lists to the match debugger window.
01104  ** Return: none
01105  ** Exceptions: none
01106  ** History: Thu Mar 21 15:47:33 1991, DSJ, Created.
01107  */
01108   InitIntMatchWindowIfReqd();
01109   if (ProtoDisplayWindow) {
01110     ProtoDisplayWindow->Clear();
01111   }
01112   if (FeatureDisplayWindow) {
01113     FeatureDisplayWindow->Clear();
01114   }
01115   ClearFeatureSpaceWindow(
01116       static_cast<NORM_METHOD>(static_cast<int>(classify_norm_method)),
01117       IntMatchWindow);
01118   IntMatchWindow->ZoomToRectangle(INT_MIN_X, INT_MIN_Y,
01119                                   INT_MAX_X, INT_MAX_Y);
01120   if (ProtoDisplayWindow) {
01121     ProtoDisplayWindow->ZoomToRectangle(INT_MIN_X, INT_MIN_Y,
01122                                         INT_MAX_X, INT_MAX_Y);
01123   }
01124   if (FeatureDisplayWindow) {
01125     FeatureDisplayWindow->ZoomToRectangle(INT_MIN_X, INT_MIN_Y,
01126                                           INT_MAX_X, INT_MAX_Y);
01127   }
01128 }                                /* ShowMatchDisplay */
01129 
01130 // Clears the given window and draws the featurespace guides for the
01131 // appropriate normalization method.
01132 void ClearFeatureSpaceWindow(NORM_METHOD norm_method, ScrollView* window) {
01133   window->Clear();
01134 
01135   window->Pen(ScrollView::GREY);
01136   // Draw the feature space limit rectangle.
01137   window->Rectangle(0, 0, INT_MAX_X, INT_MAX_Y);
01138   if (norm_method == baseline) {
01139     window->SetCursor(0, INT_DESCENDER);
01140     window->DrawTo(INT_MAX_X, INT_DESCENDER);
01141     window->SetCursor(0, INT_BASELINE);
01142     window->DrawTo(INT_MAX_X, INT_BASELINE);
01143     window->SetCursor(0, INT_XHEIGHT);
01144     window->DrawTo(INT_MAX_X, INT_XHEIGHT);
01145     window->SetCursor(0, INT_CAPHEIGHT);
01146     window->DrawTo(INT_MAX_X, INT_CAPHEIGHT);
01147   } else {
01148     window->Rectangle(INT_XCENTER - INT_XRADIUS, INT_YCENTER - INT_YRADIUS,
01149                       INT_XCENTER + INT_XRADIUS, INT_YCENTER + INT_YRADIUS);
01150   }
01151 }
01152 #endif
01153 
01154 /*---------------------------------------------------------------------------*/
01155 void Classify::WriteIntTemplates(FILE *File, INT_TEMPLATES Templates,
01156                                  const UNICHARSET& target_unicharset) {
01157 /*
01158  ** Parameters:
01159  **   File    open file to write templates to
01160  **   Templates templates to save into File
01161  ** Globals: none
01162  ** Operation: This routine writes Templates to File.  The format
01163  **   is an efficient binary format.  File must already be open
01164  **   for writing.
01165  ** Return: none
01166  ** Exceptions: none
01167  ** History: Wed Feb 27 11:48:46 1991, DSJ, Created.
01168  */
01169   int i, j;
01170   INT_CLASS Class;
01171   int unicharset_size = target_unicharset.size();
01172   int version_id = -5;  // When negated by the reader -1 becomes +1 etc.
01173 
01174   if (Templates->NumClasses != unicharset_size) {
01175     cprintf("Warning: executing WriteIntTemplates() with %d classes in"
01176             " Templates, while target_unicharset size is %d\n",
01177             Templates->NumClasses, unicharset_size);
01178   }
01179 
01180   /* first write the high level template struct */
01181   fwrite(&unicharset_size, sizeof(unicharset_size), 1, File);
01182   fwrite(&version_id, sizeof(version_id), 1, File);
01183   fwrite(&Templates->NumClassPruners, sizeof(Templates->NumClassPruners),
01184          1, File);
01185   fwrite(&Templates->NumClasses, sizeof(Templates->NumClasses), 1, File);
01186 
01187   /* then write out the class pruners */
01188   for (i = 0; i < Templates->NumClassPruners; i++)
01189     fwrite(Templates->ClassPruners[i],
01190            sizeof(CLASS_PRUNER_STRUCT), 1, File);
01191 
01192   /* then write out each class */
01193   for (i = 0; i < Templates->NumClasses; i++) {
01194     Class = Templates->Class[i];
01195 
01196     /* first write out the high level struct for the class */
01197     fwrite(&Class->NumProtos, sizeof(Class->NumProtos), 1, File);
01198     fwrite(&Class->NumProtoSets, sizeof(Class->NumProtoSets), 1, File);
01199     ASSERT_HOST(Class->NumConfigs == this->fontset_table_.get(Class->font_set_id).size);
01200     fwrite(&Class->NumConfigs, sizeof(Class->NumConfigs), 1, File);
01201     for (j = 0; j < Class->NumConfigs; ++j) {
01202       fwrite(&Class->ConfigLengths[j], sizeof(uinT16), 1, File);
01203     }
01204 
01205     /* then write out the proto lengths */
01206     if (MaxNumIntProtosIn (Class) > 0) {
01207       fwrite ((char *) (Class->ProtoLengths), sizeof (uinT8),
01208               MaxNumIntProtosIn (Class), File);
01209     }
01210 
01211     /* then write out the proto sets */
01212     for (j = 0; j < Class->NumProtoSets; j++)
01213       fwrite ((char *) Class->ProtoSets[j],
01214               sizeof (PROTO_SET_STRUCT), 1, File);
01215 
01216     /* then write the fonts info */
01217     fwrite(&Class->font_set_id, sizeof(int), 1, File);
01218   }
01219 
01220   /* Write the fonts info tables */
01221   this->fontinfo_table_.write(File, NewPermanentTessCallback(write_info));
01222   this->fontinfo_table_.write(File,
01223                               NewPermanentTessCallback(write_spacing_info));
01224   this->fontset_table_.write(File, NewPermanentTessCallback(write_set));
01225 }                                /* WriteIntTemplates */
01226 } // namespace tesseract
01227 
01228 
01229 /*-----------------------------------------------------------------------------
01230               Private Code
01231 -----------------------------------------------------------------------------*/
01232 /*---------------------------------------------------------------------------*/
01233 FLOAT32 BucketStart(int Bucket, FLOAT32 Offset, int NumBuckets) {
01234 /*
01235  ** Parameters:
01236  **   Bucket    bucket whose start is to be computed
01237  **   Offset    offset used to map params to buckets
01238  **   NumBuckets  total number of buckets
01239  ** Globals: none
01240  ** Operation: This routine returns the parameter value which
01241  **   corresponds to the beginning of the specified bucket.
01242  **   The bucket number should have been generated using the
01243  **   BucketFor() function with parameters Offset and NumBuckets.
01244  ** Return: Param value corresponding to start position of Bucket.
01245  ** Exceptions: none
01246  ** History: Thu Feb 14 13:24:33 1991, DSJ, Created.
01247  */
01248   return (((FLOAT32) Bucket / NumBuckets) - Offset);
01249 
01250 }                                /* BucketStart */
01251 
01252 
01253 /*---------------------------------------------------------------------------*/
01254 FLOAT32 BucketEnd(int Bucket, FLOAT32 Offset, int NumBuckets) {
01255 /*
01256  ** Parameters:
01257  **   Bucket    bucket whose end is to be computed
01258  **   Offset    offset used to map params to buckets
01259  **   NumBuckets  total number of buckets
01260  ** Globals: none
01261  ** Operation: This routine returns the parameter value which
01262  **   corresponds to the end of the specified bucket.
01263  **   The bucket number should have been generated using the
01264  **   BucketFor() function with parameters Offset and NumBuckets.
01265  ** Return: Param value corresponding to end position of Bucket.
01266  ** Exceptions: none
01267  ** History: Thu Feb 14 13:24:33 1991, DSJ, Created.
01268  */
01269   return (((FLOAT32) (Bucket + 1) / NumBuckets) - Offset);
01270 }                                /* BucketEnd */
01271 
01272 
01273 /*---------------------------------------------------------------------------*/
01274 void DoFill(FILL_SPEC *FillSpec,
01275             CLASS_PRUNER_STRUCT* Pruner,
01276             register uinT32 ClassMask,
01277             register uinT32 ClassCount,
01278             register uinT32 WordIndex) {
01279 /*
01280  ** Parameters:
01281  **   FillSpec  specifies which bits to fill in pruner
01282  **   Pruner    class pruner to be filled
01283  **   ClassMask indicates which bits to change in each word
01284  **   ClassCount  indicates what to change bits to
01285  **   WordIndex indicates which word to change
01286  ** Globals: none
01287  ** Operation: This routine fills in the section of a class pruner
01288  **   corresponding to a single x value for a single proto of
01289  **   a class.
01290  ** Return: none
01291  ** Exceptions: none
01292  ** History: Tue Feb 19 11:11:29 1991, DSJ, Created.
01293  */
01294   register int X, Y, Angle;
01295   register uinT32 OldWord;
01296 
01297   X = FillSpec->X;
01298   if (X < 0)
01299     X = 0;
01300   if (X >= NUM_CP_BUCKETS)
01301     X = NUM_CP_BUCKETS - 1;
01302 
01303   if (FillSpec->YStart < 0)
01304     FillSpec->YStart = 0;
01305   if (FillSpec->YEnd >= NUM_CP_BUCKETS)
01306     FillSpec->YEnd = NUM_CP_BUCKETS - 1;
01307 
01308   for (Y = FillSpec->YStart; Y <= FillSpec->YEnd; Y++)
01309     for (Angle = FillSpec->AngleStart;
01310          TRUE; CircularIncrement (Angle, NUM_CP_BUCKETS)) {
01311       OldWord = Pruner->p[X][Y][Angle][WordIndex];
01312       if (ClassCount > (OldWord & ClassMask)) {
01313         OldWord &= ~ClassMask;
01314         OldWord |= ClassCount;
01315         Pruner->p[X][Y][Angle][WordIndex] = OldWord;
01316       }
01317       if (Angle == FillSpec->AngleEnd)
01318         break;
01319     }
01320 }                                /* DoFill */
01321 
01322 
01323 /*---------------------------------------------------------------------------*/
01324 BOOL8 FillerDone(TABLE_FILLER *Filler) {
01325 /*
01326  ** Parameters:
01327  **   Filler    table filler to check if done
01328  ** Globals: none
01329  ** Operation: Return TRUE if the specified table filler is done, i.e.
01330  **   if it has no more lines to fill.
01331  ** Return: TRUE if no more lines to fill, FALSE otherwise.
01332  ** Exceptions: none
01333  ** History: Tue Feb 19 10:08:05 1991, DSJ, Created.
01334  */
01335   FILL_SWITCH *Next;
01336 
01337   Next = &(Filler->Switch[Filler->NextSwitch]);
01338 
01339   if (Filler->X > Next->X && Next->Type == LastSwitch)
01340     return (TRUE);
01341   else
01342     return (FALSE);
01343 
01344 }                                /* FillerDone */
01345 
01346 
01347 /*---------------------------------------------------------------------------*/
01348 void FillPPCircularBits(uinT32 ParamTable[NUM_PP_BUCKETS][WERDS_PER_PP_VECTOR],
01349                         int Bit, FLOAT32 Center, FLOAT32 Spread, bool debug) {
01350 /*
01351  ** Parameters:
01352  **   ParamTable  table of bit vectors, one per param bucket
01353  **   Bit   bit position in vectors to be filled
01354  **   Center    center of filled area
01355  **   Spread    spread of filled area
01356  ** Globals: none
01357  ** Operation: This routine sets Bit in each bit vector whose
01358  **   bucket lies within the range Center +- Spread.  The fill
01359  **   is done for a circular dimension, i.e. bucket 0 is adjacent
01360  **   to the last bucket.  It is assumed that Center and Spread
01361  **   are expressed in a circular coordinate system whose range
01362  **   is 0 to 1.
01363  ** Return: none
01364  ** Exceptions: none
01365  ** History: Tue Oct 16 09:26:54 1990, DSJ, Created.
01366  */
01367   int i, FirstBucket, LastBucket;
01368 
01369   if (Spread > 0.5)
01370     Spread = 0.5;
01371 
01372   FirstBucket = (int) floor ((Center - Spread) * NUM_PP_BUCKETS);
01373   if (FirstBucket < 0)
01374     FirstBucket += NUM_PP_BUCKETS;
01375 
01376   LastBucket = (int) floor ((Center + Spread) * NUM_PP_BUCKETS);
01377   if (LastBucket >= NUM_PP_BUCKETS)
01378     LastBucket -= NUM_PP_BUCKETS;
01379   if (debug) tprintf("Circular fill from %d to %d", FirstBucket, LastBucket);
01380   for (i = FirstBucket; TRUE; CircularIncrement (i, NUM_PP_BUCKETS)) {
01381     SET_BIT (ParamTable[i], Bit);
01382 
01383     /* exit loop after we have set the bit for the last bucket */
01384     if (i == LastBucket)
01385       break;
01386   }
01387 
01388 }                                /* FillPPCircularBits */
01389 
01390 
01391 /*---------------------------------------------------------------------------*/
01392 void FillPPLinearBits(uinT32 ParamTable[NUM_PP_BUCKETS][WERDS_PER_PP_VECTOR],
01393                       int Bit, FLOAT32 Center, FLOAT32 Spread, bool debug) {
01394 /*
01395  ** Parameters:
01396  **   ParamTable  table of bit vectors, one per param bucket
01397  **   Bit   bit number being filled
01398  **   Center    center of filled area
01399  **   Spread    spread of filled area
01400  ** Globals: none
01401  ** Operation: This routine sets Bit in each bit vector whose
01402  **   bucket lies within the range Center +- Spread.  The fill
01403  **   is done for a linear dimension, i.e. there is no wrap-around
01404  **   for this dimension.  It is assumed that Center and Spread
01405  **   are expressed in a linear coordinate system whose range
01406  **   is approximately 0 to 1.  Values outside this range will
01407  **   be clipped.
01408  ** Return: none
01409  ** Exceptions: none
01410  ** History: Tue Oct 16 09:26:54 1990, DSJ, Created.
01411  */
01412   int i, FirstBucket, LastBucket;
01413 
01414   FirstBucket = (int) floor ((Center - Spread) * NUM_PP_BUCKETS);
01415   if (FirstBucket < 0)
01416     FirstBucket = 0;
01417 
01418   LastBucket = (int) floor ((Center + Spread) * NUM_PP_BUCKETS);
01419   if (LastBucket >= NUM_PP_BUCKETS)
01420     LastBucket = NUM_PP_BUCKETS - 1;
01421 
01422   if (debug) tprintf("Linear fill from %d to %d", FirstBucket, LastBucket);
01423   for (i = FirstBucket; i <= LastBucket; i++)
01424     SET_BIT (ParamTable[i], Bit);
01425 
01426 }                                /* FillPPLinearBits */
01427 
01428 
01429 /*---------------------------------------------------------------------------*/
01430 #ifndef GRAPHICS_DISABLED
01431 namespace tesseract {
01432 CLASS_ID Classify::GetClassToDebug(const char *Prompt, bool* adaptive_on,
01433                                    bool* pretrained_on, int* shape_id) {
01434 /*
01435  ** Parameters:
01436  **   Prompt  prompt to print while waiting for input from window
01437  ** Globals: none
01438  ** Operation: This routine prompts the user with Prompt and waits
01439  **   for the user to enter something in the debug window.
01440  ** Return: Character entered in the debug window.
01441  ** Exceptions: none
01442  ** History: Thu Mar 21 16:55:13 1991, DSJ, Created.
01443  */
01444   tprintf("%s\n", Prompt);
01445   SVEvent* ev;
01446   SVEventType ev_type;
01447   int unichar_id = INVALID_UNICHAR_ID;
01448   // Wait until a click or popup event.
01449   do {
01450     ev = IntMatchWindow->AwaitEvent(SVET_ANY);
01451     ev_type = ev->type;
01452     if (ev_type == SVET_POPUP) {
01453       if (ev->command_id == IDA_SHAPE_INDEX) {
01454         if (shape_table_ != NULL) {
01455           *shape_id = atoi(ev->parameter);
01456           *adaptive_on = false;
01457           *pretrained_on = true;
01458           if (*shape_id >= 0 && *shape_id < shape_table_->NumShapes()) {
01459             int font_id;
01460             shape_table_->GetFirstUnicharAndFont(*shape_id, &unichar_id,
01461                                                  &font_id);
01462             tprintf("Shape %d, first unichar=%d, font=%d\n",
01463                     *shape_id, unichar_id, font_id);
01464             return unichar_id;
01465           }
01466           tprintf("Shape index '%s' not found in shape table\n", ev->parameter);
01467         } else {
01468           tprintf("No shape table loaded!\n");
01469         }
01470       } else {
01471         if (unicharset.contains_unichar(ev->parameter)) {
01472           unichar_id = unicharset.unichar_to_id(ev->parameter);
01473           if (ev->command_id == IDA_ADAPTIVE) {
01474             *adaptive_on = true;
01475             *pretrained_on = false;
01476             *shape_id = -1;
01477           } else if (ev->command_id == IDA_STATIC) {
01478             *adaptive_on = false;
01479             *pretrained_on = true;
01480           } else {
01481             *adaptive_on = true;
01482             *pretrained_on = true;
01483           }
01484           if (ev->command_id == IDA_ADAPTIVE || shape_table_ == NULL) {
01485             *shape_id = -1;
01486             return unichar_id;
01487           }
01488           for (int s = 0; s < shape_table_->NumShapes(); ++s) {
01489             if (shape_table_->GetShape(s).ContainsUnichar(unichar_id)) {
01490               tprintf("%s\n", shape_table_->DebugStr(s).string());
01491             }
01492           }
01493         } else {
01494           tprintf("Char class '%s' not found in unicharset",
01495                   ev->parameter);
01496         }
01497       }
01498     }
01499     delete ev;
01500   } while (ev_type != SVET_CLICK);
01501   return 0;
01502 }                                /* GetClassToDebug */
01503 
01504 }  // namespace tesseract
01505 #endif
01506 
01507 /*---------------------------------------------------------------------------*/
01508 void GetCPPadsForLevel(int Level,
01509                        FLOAT32 *EndPad,
01510                        FLOAT32 *SidePad,
01511                        FLOAT32 *AnglePad) {
01512 /*
01513  ** Parameters:
01514  **   Level   "tightness" level to return pads for
01515  **   EndPad    place to put end pad for Level
01516  **   SidePad   place to put side pad for Level
01517  **   AnglePad  place to put angle pad for Level
01518  ** Globals: none
01519  ** Operation: This routine copies the appropriate global pad variables
01520  **   into EndPad, SidePad, and AnglePad.  This is a kludge used
01521  **   to get around the fact that global control variables cannot
01522  **   be arrays.  If the specified level is illegal, the tightest
01523  **   possible pads are returned.
01524  ** Return: none (results are returned in EndPad, SidePad, and AnglePad.
01525  ** Exceptions: none
01526  ** History: Thu Feb 14 08:26:49 1991, DSJ, Created.
01527  */
01528   switch (Level) {
01529     case 0:
01530       *EndPad = classify_cp_end_pad_loose * GetPicoFeatureLength ();
01531       *SidePad = classify_cp_side_pad_loose * GetPicoFeatureLength ();
01532       *AnglePad = classify_cp_angle_pad_loose / 360.0;
01533       break;
01534 
01535     case 1:
01536       *EndPad = classify_cp_end_pad_medium * GetPicoFeatureLength ();
01537       *SidePad = classify_cp_side_pad_medium * GetPicoFeatureLength ();
01538       *AnglePad = classify_cp_angle_pad_medium / 360.0;
01539       break;
01540 
01541     case 2:
01542       *EndPad = classify_cp_end_pad_tight * GetPicoFeatureLength ();
01543       *SidePad = classify_cp_side_pad_tight * GetPicoFeatureLength ();
01544       *AnglePad = classify_cp_angle_pad_tight / 360.0;
01545       break;
01546 
01547     default:
01548       *EndPad = classify_cp_end_pad_tight * GetPicoFeatureLength ();
01549       *SidePad = classify_cp_side_pad_tight * GetPicoFeatureLength ();
01550       *AnglePad = classify_cp_angle_pad_tight / 360.0;
01551       break;
01552   }
01553   if (*AnglePad > 0.5)
01554     *AnglePad = 0.5;
01555 
01556 }                                /* GetCPPadsForLevel */
01557 
01558 
01559 /*---------------------------------------------------------------------------*/
01560 ScrollView::Color GetMatchColorFor(FLOAT32 Evidence) {
01561 /*
01562  ** Parameters:
01563  **   Evidence  evidence value to return color for
01564  ** Globals: none
01565  ** Operation:
01566  ** Return: Color which corresponds to specified Evidence value.
01567  ** Exceptions: none
01568  ** History: Thu Mar 21 15:24:52 1991, DSJ, Created.
01569  */
01570 
01571   assert (Evidence >= 0.0);
01572   assert (Evidence <= 1.0);
01573 
01574   if (Evidence >= 0.90)
01575     return ScrollView::WHITE;
01576   else if (Evidence >= 0.75)
01577     return ScrollView::GREEN;
01578   else if (Evidence >= 0.50)
01579     return ScrollView::RED;
01580   else
01581     return ScrollView::BLUE;
01582 }                                /* GetMatchColorFor */
01583 
01584 
01585 /*---------------------------------------------------------------------------*/
01586 void GetNextFill(TABLE_FILLER *Filler, FILL_SPEC *Fill) {
01587 /*
01588  ** Parameters:
01589  **   Filler    filler to get next fill spec from
01590  **   Fill    place to put spec for next fill
01591  ** Globals: none
01592  ** Operation: This routine returns (in Fill) the specification of
01593  **   the next line to be filled from Filler.  FillerDone() should
01594  **   always be called before GetNextFill() to ensure that we
01595  **   do not run past the end of the fill table.
01596  ** Return: none (results are returned in Fill)
01597  ** Exceptions: none
01598  ** History: Tue Feb 19 10:17:42 1991, DSJ, Created.
01599  */
01600   FILL_SWITCH *Next;
01601 
01602   /* compute the fill assuming no switches will be encountered */
01603   Fill->AngleStart = Filler->AngleStart;
01604   Fill->AngleEnd = Filler->AngleEnd;
01605   Fill->X = Filler->X;
01606   Fill->YStart = Filler->YStart >> 8;
01607   Fill->YEnd = Filler->YEnd >> 8;
01608 
01609   /* update the fill info and the filler for ALL switches at this X value */
01610   Next = &(Filler->Switch[Filler->NextSwitch]);
01611   while (Filler->X >= Next->X) {
01612     Fill->X = Filler->X = Next->X;
01613     if (Next->Type == StartSwitch) {
01614       Fill->YStart = Next->Y;
01615       Filler->StartDelta = Next->Delta;
01616       Filler->YStart = Next->YInit;
01617     }
01618     else if (Next->Type == EndSwitch) {
01619       Fill->YEnd = Next->Y;
01620       Filler->EndDelta = Next->Delta;
01621       Filler->YEnd = Next->YInit;
01622     }
01623     else {                       /* Type must be LastSwitch */
01624       break;
01625     }
01626     Filler->NextSwitch++;
01627     Next = &(Filler->Switch[Filler->NextSwitch]);
01628   }
01629 
01630   /* prepare the filler for the next call to this routine */
01631   Filler->X++;
01632   Filler->YStart += Filler->StartDelta;
01633   Filler->YEnd += Filler->EndDelta;
01634 
01635 }                                /* GetNextFill */
01636 
01637 
01638 /*---------------------------------------------------------------------------*/
01654 void InitTableFiller (FLOAT32 EndPad, FLOAT32 SidePad,
01655                       FLOAT32 AnglePad, PROTO Proto, TABLE_FILLER * Filler)
01656 #define XS          X_SHIFT
01657 #define YS          Y_SHIFT
01658 #define AS          ANGLE_SHIFT
01659 #define NB          NUM_CP_BUCKETS
01660 {
01661   FLOAT32 Angle;
01662   FLOAT32 X, Y, HalfLength;
01663   FLOAT32 Cos, Sin;
01664   FLOAT32 XAdjust, YAdjust;
01665   FPOINT Start, Switch1, Switch2, End;
01666   int S1 = 0;
01667   int S2 = 1;
01668 
01669   Angle = Proto->Angle;
01670   X = Proto->X;
01671   Y = Proto->Y;
01672   HalfLength = Proto->Length / 2.0;
01673 
01674   Filler->AngleStart = CircBucketFor(Angle - AnglePad, AS, NB);
01675   Filler->AngleEnd = CircBucketFor(Angle + AnglePad, AS, NB);
01676   Filler->NextSwitch = 0;
01677 
01678   if (fabs (Angle - 0.0) < HV_TOLERANCE || fabs (Angle - 0.5) < HV_TOLERANCE) {
01679     /* horizontal proto - handle as special case */
01680     Filler->X = BucketFor(X - HalfLength - EndPad, XS, NB);
01681     Filler->YStart = BucketFor(Y - SidePad, YS, NB * 256);
01682     Filler->YEnd = BucketFor(Y + SidePad, YS, NB * 256);
01683     Filler->StartDelta = 0;
01684     Filler->EndDelta = 0;
01685     Filler->Switch[0].Type = LastSwitch;
01686     Filler->Switch[0].X = BucketFor(X + HalfLength + EndPad, XS, NB);
01687   } else if (fabs(Angle - 0.25) < HV_TOLERANCE ||
01688            fabs(Angle - 0.75) < HV_TOLERANCE) {
01689     /* vertical proto - handle as special case */
01690     Filler->X = BucketFor(X - SidePad, XS, NB);
01691     Filler->YStart = BucketFor(Y - HalfLength - EndPad, YS, NB * 256);
01692     Filler->YEnd = BucketFor(Y + HalfLength + EndPad, YS, NB * 256);
01693     Filler->StartDelta = 0;
01694     Filler->EndDelta = 0;
01695     Filler->Switch[0].Type = LastSwitch;
01696     Filler->Switch[0].X = BucketFor(X + SidePad, XS, NB);
01697   } else {
01698     /* diagonal proto */
01699 
01700     if ((Angle > 0.0 && Angle < 0.25) || (Angle > 0.5 && Angle < 0.75)) {
01701       /* rising diagonal proto */
01702       Angle *= 2.0 * PI;
01703       Cos = fabs(cos(Angle));
01704       Sin = fabs(sin(Angle));
01705 
01706       /* compute the positions of the corners of the acceptance region */
01707       Start.x = X - (HalfLength + EndPad) * Cos - SidePad * Sin;
01708       Start.y = Y - (HalfLength + EndPad) * Sin + SidePad * Cos;
01709       End.x = 2.0 * X - Start.x;
01710       End.y = 2.0 * Y - Start.y;
01711       Switch1.x = X - (HalfLength + EndPad) * Cos + SidePad * Sin;
01712       Switch1.y = Y - (HalfLength + EndPad) * Sin - SidePad * Cos;
01713       Switch2.x = 2.0 * X - Switch1.x;
01714       Switch2.y = 2.0 * Y - Switch1.y;
01715 
01716       if (Switch1.x > Switch2.x) {
01717         S1 = 1;
01718         S2 = 0;
01719       }
01720 
01721       /* translate into bucket positions and deltas */
01722       Filler->X = (inT8) MapParam(Start.x, XS, NB);
01723       Filler->StartDelta = -(inT16) ((Cos / Sin) * 256);
01724       Filler->EndDelta = (inT16) ((Sin / Cos) * 256);
01725 
01726       XAdjust = BucketEnd(Filler->X, XS, NB) - Start.x;
01727       YAdjust = XAdjust * Cos / Sin;
01728       Filler->YStart = (inT16) MapParam(Start.y - YAdjust, YS, NB * 256);
01729       YAdjust = XAdjust * Sin / Cos;
01730       Filler->YEnd = (inT16) MapParam(Start.y + YAdjust, YS, NB * 256);
01731 
01732       Filler->Switch[S1].Type = StartSwitch;
01733       Filler->Switch[S1].X = (inT8) MapParam(Switch1.x, XS, NB);
01734       Filler->Switch[S1].Y = (inT8) MapParam(Switch1.y, YS, NB);
01735       XAdjust = Switch1.x - BucketStart(Filler->Switch[S1].X, XS, NB);
01736       YAdjust = XAdjust * Sin / Cos;
01737       Filler->Switch[S1].YInit =
01738         (inT16) MapParam(Switch1.y - YAdjust, YS, NB * 256);
01739       Filler->Switch[S1].Delta = Filler->EndDelta;
01740 
01741       Filler->Switch[S2].Type = EndSwitch;
01742       Filler->Switch[S2].X = (inT8) MapParam(Switch2.x, XS, NB);
01743       Filler->Switch[S2].Y = (inT8) MapParam(Switch2.y, YS, NB);
01744       XAdjust = Switch2.x - BucketStart(Filler->Switch[S2].X, XS, NB);
01745       YAdjust = XAdjust * Cos / Sin;
01746       Filler->Switch[S2].YInit =
01747         (inT16) MapParam(Switch2.y + YAdjust, YS, NB * 256);
01748       Filler->Switch[S2].Delta = Filler->StartDelta;
01749 
01750       Filler->Switch[2].Type = LastSwitch;
01751       Filler->Switch[2].X = (inT8)MapParam(End.x, XS, NB);
01752     } else {
01753       /* falling diagonal proto */
01754       Angle *= 2.0 * PI;
01755       Cos = fabs(cos(Angle));
01756       Sin = fabs(sin(Angle));
01757 
01758       /* compute the positions of the corners of the acceptance region */
01759       Start.x = X - (HalfLength + EndPad) * Cos - SidePad * Sin;
01760       Start.y = Y + (HalfLength + EndPad) * Sin - SidePad * Cos;
01761       End.x = 2.0 * X - Start.x;
01762       End.y = 2.0 * Y - Start.y;
01763       Switch1.x = X - (HalfLength + EndPad) * Cos + SidePad * Sin;
01764       Switch1.y = Y + (HalfLength + EndPad) * Sin + SidePad * Cos;
01765       Switch2.x = 2.0 * X - Switch1.x;
01766       Switch2.y = 2.0 * Y - Switch1.y;
01767 
01768       if (Switch1.x > Switch2.x) {
01769         S1 = 1;
01770         S2 = 0;
01771       }
01772 
01773       /* translate into bucket positions and deltas */
01774       Filler->X = (inT8) MapParam(Start.x, XS, NB);
01775       Filler->StartDelta = -(inT16) ((Sin / Cos) * 256);
01776       Filler->EndDelta = (inT16) ((Cos / Sin) * 256);
01777 
01778       XAdjust = BucketEnd(Filler->X, XS, NB) - Start.x;
01779       YAdjust = XAdjust * Sin / Cos;
01780       Filler->YStart = (inT16) MapParam(Start.y - YAdjust, YS, NB * 256);
01781       YAdjust = XAdjust * Cos / Sin;
01782       Filler->YEnd = (inT16) MapParam(Start.y + YAdjust, YS, NB * 256);
01783 
01784       Filler->Switch[S1].Type = EndSwitch;
01785       Filler->Switch[S1].X = (inT8) MapParam(Switch1.x, XS, NB);
01786       Filler->Switch[S1].Y = (inT8) MapParam(Switch1.y, YS, NB);
01787       XAdjust = Switch1.x - BucketStart(Filler->Switch[S1].X, XS, NB);
01788       YAdjust = XAdjust * Sin / Cos;
01789       Filler->Switch[S1].YInit =
01790         (inT16) MapParam(Switch1.y + YAdjust, YS, NB * 256);
01791       Filler->Switch[S1].Delta = Filler->StartDelta;
01792 
01793       Filler->Switch[S2].Type = StartSwitch;
01794       Filler->Switch[S2].X = (inT8) MapParam(Switch2.x, XS, NB);
01795       Filler->Switch[S2].Y = (inT8) MapParam(Switch2.y, YS, NB);
01796       XAdjust = Switch2.x - BucketStart(Filler->Switch[S2].X, XS, NB);
01797       YAdjust = XAdjust * Cos / Sin;
01798       Filler->Switch[S2].YInit =
01799         (inT16) MapParam(Switch2.y - YAdjust, YS, NB * 256);
01800       Filler->Switch[S2].Delta = Filler->EndDelta;
01801 
01802       Filler->Switch[2].Type = LastSwitch;
01803       Filler->Switch[2].X = (inT8) MapParam(End.x, XS, NB);
01804     }
01805   }
01806 }                                /* InitTableFiller */
01807 
01808 
01809 /*---------------------------------------------------------------------------*/
01810 #ifndef GRAPHICS_DISABLED
01811 /*
01812  * Parameters:
01813  *   ShapeList shape list to add feature rendering to
01814  *   Feature   feature to be rendered
01815  *   Color   color to use for feature rendering
01816  * Globals: none
01817  * Operation: This routine renders the specified feature into ShapeList.
01818  * Return: New shape list with rendering of Feature added.
01819  * @note Exceptions: none
01820  * @note History: Thu Mar 21 14:57:41 1991, DSJ, Created.
01821  */
01822 void RenderIntFeature(ScrollView *window, const INT_FEATURE_STRUCT* Feature,
01823                       ScrollView::Color color) {
01824   FLOAT32 X, Y, Dx, Dy, Length;
01825 
01826   window->Pen(color);
01827   assert(Feature != NULL);
01828   assert(color != 0);
01829 
01830   X = Feature->X;
01831   Y = Feature->Y;
01832   Length = GetPicoFeatureLength() * 0.7 * INT_CHAR_NORM_RANGE;
01833   // The -PI has no significant effect here, but the value of Theta is computed
01834   // using BinaryAnglePlusPi in intfx.cpp.
01835   Dx = (Length / 2.0) * cos((Feature->Theta / 256.0) * 2.0 * PI - PI);
01836   Dy = (Length / 2.0) * sin((Feature->Theta / 256.0) * 2.0 * PI - PI);
01837 
01838   window->SetCursor(X, Y);
01839   window->DrawTo(X + Dx, Y + Dy);
01840 }                                /* RenderIntFeature */
01841 
01842 
01843 /*---------------------------------------------------------------------------*/
01844 /*
01845  * This routine extracts the parameters of the specified
01846  * proto from the class description and adds a rendering of
01847  * the proto onto the ShapeList.
01848  *
01849  * @param ShapeList   shape list to append proto rendering onto
01850  * @param Class   class that proto is contained in
01851  * @param ProtoId   id of proto to be rendered
01852  * @param color   color to render proto in
01853  *
01854  * Globals: none
01855  *
01856  * @return New shape list with a rendering of one proto added.
01857  * @note Exceptions: none
01858  * @note History: Thu Mar 21 10:21:09 1991, DSJ, Created.
01859  */
01860 void RenderIntProto(ScrollView *window,
01861                     INT_CLASS Class,
01862                     PROTO_ID ProtoId,
01863                     ScrollView::Color color) {
01864   PROTO_SET ProtoSet;
01865   INT_PROTO Proto;
01866   int ProtoSetIndex;
01867   int ProtoWordIndex;
01868   FLOAT32 Length;
01869   int Xmin, Xmax, Ymin, Ymax;
01870   FLOAT32 X, Y, Dx, Dy;
01871   uinT32 ProtoMask;
01872   int Bucket;
01873 
01874   assert(ProtoId >= 0);
01875   assert(Class != NULL);
01876   assert(ProtoId < Class->NumProtos);
01877   assert(color != 0);
01878   window->Pen(color);
01879 
01880   ProtoSet = Class->ProtoSets[SetForProto(ProtoId)];
01881   ProtoSetIndex = IndexForProto(ProtoId);
01882   Proto = &(ProtoSet->Protos[ProtoSetIndex]);
01883   Length = (Class->ProtoLengths[ProtoId] *
01884     GetPicoFeatureLength() * INT_CHAR_NORM_RANGE);
01885   ProtoMask = PPrunerMaskFor(ProtoId);
01886   ProtoWordIndex = PPrunerWordIndexFor(ProtoId);
01887 
01888   // find the x and y extent of the proto from the proto pruning table
01889   Xmin = Ymin = NUM_PP_BUCKETS;
01890   Xmax = Ymax = 0;
01891   for (Bucket = 0; Bucket < NUM_PP_BUCKETS; Bucket++) {
01892     if (ProtoMask & ProtoSet->ProtoPruner[PRUNER_X][Bucket][ProtoWordIndex]) {
01893       UpdateRange(Bucket, &Xmin, &Xmax);
01894     }
01895 
01896     if (ProtoMask & ProtoSet->ProtoPruner[PRUNER_Y][Bucket][ProtoWordIndex]) {
01897       UpdateRange(Bucket, &Ymin, &Ymax);
01898     }
01899   }
01900   X = (Xmin + Xmax + 1) / 2.0 * PROTO_PRUNER_SCALE;
01901   Y = (Ymin + Ymax + 1) / 2.0 * PROTO_PRUNER_SCALE;
01902   // The -PI has no significant effect here, but the value of Theta is computed
01903   // using BinaryAnglePlusPi in intfx.cpp.
01904   Dx = (Length / 2.0) * cos((Proto->Angle / 256.0) * 2.0 * PI - PI);
01905   Dy = (Length / 2.0) * sin((Proto->Angle / 256.0) * 2.0 * PI - PI);
01906 
01907   window->SetCursor(X - Dx, Y - Dy);
01908   window->DrawTo(X + Dx, Y + Dy);
01909 }                                /* RenderIntProto */
01910 #endif
01911 
01912 /*---------------------------------------------------------------------------*/
01928 int TruncateParam(FLOAT32 Param, int Min, int Max, char *Id) {
01929   if (Param < Min) {
01930     if (Id)
01931       cprintf("Warning: Param %s truncated from %f to %d!\n",
01932               Id, Param, Min);
01933     Param = Min;
01934   } else if (Param > Max) {
01935     if (Id)
01936       cprintf("Warning: Param %s truncated from %f to %d!\n",
01937               Id, Param, Max);
01938     Param = Max;
01939   }
01940   return static_cast<int>(floor(Param));
01941 }                                /* TruncateParam */
01942 
01943 
01944 /*---------------------------------------------------------------------------*/
01945 #ifndef GRAPHICS_DISABLED
01946 
01950 void InitIntMatchWindowIfReqd() {
01951   if (IntMatchWindow == NULL) {
01952     IntMatchWindow = CreateFeatureSpaceWindow("IntMatchWindow", 50, 200);
01953     SVMenuNode* popup_menu = new SVMenuNode();
01954 
01955     popup_menu->AddChild("Debug Adapted classes", IDA_ADAPTIVE,
01956                          "x", "Class to debug");
01957     popup_menu->AddChild("Debug Static classes", IDA_STATIC,
01958                          "x", "Class to debug");
01959     popup_menu->AddChild("Debug Both", IDA_BOTH,
01960                          "x", "Class to debug");
01961     popup_menu->AddChild("Debug Shape Index", IDA_SHAPE_INDEX,
01962                          "0", "Index to debug");
01963     popup_menu->BuildMenu(IntMatchWindow, false);
01964   }
01965 }
01966 
01971 void InitProtoDisplayWindowIfReqd() {
01972   if (ProtoDisplayWindow == NULL) {
01973     ProtoDisplayWindow = CreateFeatureSpaceWindow("ProtoDisplayWindow",
01974                                                   550, 200);
01975  }
01976 }
01977 
01982 void InitFeatureDisplayWindowIfReqd() {
01983   if (FeatureDisplayWindow == NULL) {
01984     FeatureDisplayWindow = CreateFeatureSpaceWindow("FeatureDisplayWindow",
01985                                                     50, 700);
01986   }
01987 }
01988 
01989 // Creates a window of the appropriate size for displaying elements
01990 // in feature space.
01991 ScrollView* CreateFeatureSpaceWindow(const char* name, int xpos, int ypos) {
01992   return new ScrollView(name, xpos, ypos, 520, 520, 260, 260, true);
01993 }
01994 #endif  // GRAPHICS_DISABLED