Tesseract  3.02
 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 
49 using tesseract::FontSet;
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
75 #define MAX_NUM_SWITCHES 3
76 
77 typedef struct
78 {
80  inT8 X, Y;
83 }
84 
85 
87 
88 typedef struct
89 {
91  uinT8 AngleStart, AngleEnd;
93  inT16 YStart, YEnd;
94  inT16 StartDelta, EndDelta;
96 }
97 
98 
100 
101 typedef struct
102 {
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 
142  ParamTable[NUM_PP_BUCKETS][WERDS_PER_PP_VECTOR],
143  int Bit, FLOAT32 Center, FLOAT32 Spread, bool debug);
144 
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 
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*/
183 
184 /*-----------------------------------------------------------------------------
185  Variables
186 -----------------------------------------------------------------------------*/
187 
188 /* control knobs */
189 INT_VAR(classify_num_cp_levels, 3, "Number of Class Pruner Levels");
191  "Class Pruner Angle Pad Loose");
193  "Class Pruner Angle Pad Medium");
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 /*---------------------------------------------------------------------------*/
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 +
408  fabs (sin (Angle)) * (classify_pp_side_pad *
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 +
417  fabs (cos (Angle)) * (classify_pp_side_pad *
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
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);
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 /*---------------------------------------------------------------------------*/
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,
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) /
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 /*---------------------------------------------------------------------------*/
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 /*---------------------------------------------------------------------------*/
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 {
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 =
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 ||
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) {
1077  if (version_id >= 5) {
1078  this->fontinfo_table_.read(File,
1080  swap);
1081  }
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
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  */
1109  if (ProtoDisplayWindow) {
1110  ProtoDisplayWindow->Clear();
1111  }
1112  if (FeatureDisplayWindow) {
1113  FeatureDisplayWindow->Clear();
1114  }
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 {
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 */
1222  this->fontinfo_table_.write(File,
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;
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 /*---------------------------------------------------------------------------*/
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 /*---------------------------------------------------------------------------*/
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 /*---------------------------------------------------------------------------*/
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 {
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:
1532  *AnglePad = classify_cp_angle_pad_loose / 360.0;
1533  break;
1534 
1535  case 1:
1538  *AnglePad = classify_cp_angle_pad_medium / 360.0;
1539  break;
1540 
1541  case 2:
1544  *AnglePad = classify_cp_angle_pad_tight / 360.0;
1545  break;
1546 
1547  default:
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 /*---------------------------------------------------------------------------*/
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  */
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] *
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 
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 
1971  if (ProtoDisplayWindow == NULL) {
1972  ProtoDisplayWindow = CreateFeatureSpaceWindow("ProtoDisplayWindow",
1973  550, 200);
1974  }
1975 }
1976 
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