Tesseract  3.02
tesseract-ocr/classify/mfoutline.cpp
Go to the documentation of this file.
00001 /******************************************************************************
00002  ** Filename:    mfoutline.c
00003  ** Purpose:     Interface to outline struct used for extracting features
00004  ** Author:      Dan Johnson
00005  ** History:     Thu May 17 08:14:18 1990, 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 #include "clusttool.h"           //If remove you get cought in a loop somewhere
00022 #include "emalloc.h"
00023 #include "mfoutline.h"
00024 #include "blobs.h"
00025 #include "const.h"
00026 #include "mfx.h"
00027 #include "params.h"
00028 #include "classify.h"
00029 
00030 #include <math.h>
00031 #include <stdio.h>
00032 
00033 #define MIN_INERTIA (0.00001)
00034 
00035 /*----------------------------------------------------------------------------
00036               Public Code
00037 ----------------------------------------------------------------------------*/
00038 
00039 /*---------------------------------------------------------------------------*/
00040 // Convert a blob into a list of MFOUTLINEs (float-based microfeature format).
00041 LIST ConvertBlob(TBLOB *blob) {
00042   LIST outlines = NIL_LIST;
00043   return (blob == NULL)
00044       ? NIL_LIST
00045       : ConvertOutlines(blob->outlines, outlines, outer);
00046 }
00047 
00048 
00049 /*---------------------------------------------------------------------------*/
00050 // Convert a TESSLINE into the float-based MFOUTLINE micro-feature format.
00051 MFOUTLINE ConvertOutline(TESSLINE *outline) {
00052   MFEDGEPT *NewPoint;
00053   MFOUTLINE MFOutline = NIL_LIST;
00054   EDGEPT *EdgePoint;
00055   EDGEPT *StartPoint;
00056   EDGEPT *NextPoint;
00057 
00058   if (outline == NULL || outline->loop == NULL)
00059     return MFOutline;
00060 
00061   StartPoint = outline->loop;
00062   EdgePoint = StartPoint;
00063   do {
00064     NextPoint = EdgePoint->next;
00065 
00066     /* filter out duplicate points */
00067     if (EdgePoint->pos.x != NextPoint->pos.x ||
00068         EdgePoint->pos.y != NextPoint->pos.y) {
00069       NewPoint = NewEdgePoint();
00070       ClearMark(NewPoint);
00071       NewPoint->Hidden = EdgePoint->IsHidden();
00072       NewPoint->Point.x = EdgePoint->pos.x;
00073       NewPoint->Point.y = EdgePoint->pos.y;
00074       MFOutline = push(MFOutline, NewPoint);
00075     }
00076     EdgePoint = NextPoint;
00077   } while (EdgePoint != StartPoint);
00078 
00079   if (MFOutline != NULL)
00080     MakeOutlineCircular(MFOutline);
00081   return MFOutline;
00082 }
00083 
00084 
00085 /*---------------------------------------------------------------------------*/
00086 // Convert a tree of outlines to a list of MFOUTLINEs (lists of MFEDGEPTs).
00087 //
00088 // Parameters:
00089 //   outline      first outline to be converted
00090 //   mf_outlines  list to add converted outlines to
00091 //   outline_type  are the outlines outer or holes?
00092 LIST ConvertOutlines(TESSLINE *outline,
00093                      LIST mf_outlines,
00094                      OUTLINETYPE outline_type) {
00095   MFOUTLINE mf_outline;
00096 
00097   while (outline != NULL) {
00098     mf_outline = ConvertOutline(outline);
00099     if (mf_outline != NULL)
00100       mf_outlines = push(mf_outlines, mf_outline);
00101     outline = outline->next;
00102   }
00103   return mf_outlines;
00104 }
00105 
00106 
00107 /*---------------------------------------------------------------------------*/
00108 void ComputeOutlineStats(LIST Outlines, OUTLINE_STATS *OutlineStats) {
00109 /*
00110  ** Parameters:
00111  **   Outlines  list of outlines to compute stats for
00112  **   OutlineStats  place to put results
00113  ** Globals: none
00114  ** Operation: This routine computes several statistics about the outlines
00115  **   in Outlines.  These statistics are usually used to perform
00116  **   anistropic normalization of all of the outlines.  The
00117  **   statistics generated are:
00118  **     first moments about x and y axes
00119  **     total length of all outlines
00120  **     center of mass of all outlines
00121  **     second moments about center of mass axes
00122  **     radius of gyration about center of mass axes
00123  ** Return: none (results are returned in OutlineStats)
00124  ** Exceptions: none
00125  ** History: Fri Dec 14 08:32:03 1990, DSJ, Created.
00126  */
00127   MFOUTLINE Outline;
00128   MFOUTLINE EdgePoint;
00129   MFEDGEPT *Current;
00130   MFEDGEPT *Last;
00131 
00132   InitOutlineStats(OutlineStats);
00133   iterate(Outlines) {
00134     Outline = (MFOUTLINE) first_node (Outlines);
00135 
00136     Last = PointAt (Outline);
00137     Outline = NextPointAfter (Outline);
00138     EdgePoint = Outline;
00139     do {
00140       Current = PointAt (EdgePoint);
00141 
00142       UpdateOutlineStats (OutlineStats,
00143         Last->Point.x, Last->Point.y,
00144         Current->Point.x, Current->Point.y);
00145 
00146       Last = Current;
00147       EdgePoint = NextPointAfter (EdgePoint);
00148     }
00149     while (EdgePoint != Outline);
00150   }
00151   FinishOutlineStats(OutlineStats);
00152 
00153 }                                /* ComputeOutlineStats */
00154 
00155 
00156 /*---------------------------------------------------------------------------*/
00157 void FindDirectionChanges(MFOUTLINE Outline,
00158                           FLOAT32 MinSlope,
00159                           FLOAT32 MaxSlope) {
00160 /*
00161  ** Parameters:
00162  **   Outline   micro-feature outline to analyze
00163  **   MinSlope  controls "snapping" of segments to horizontal
00164  **   MaxSlope  controls "snapping" of segments to vertical
00165  ** Globals: none
00166  ** Operation:
00167  **   This routine searches thru the specified outline, computes
00168  **   a slope for each vector in the outline, and marks each
00169  **   vector as having one of the following directions:
00170  **     N, S, E, W, NE, NW, SE, SW
00171  **   This information is then stored in the outline and the
00172  **   outline is returned.
00173  ** Return: none
00174  ** Exceptions: none
00175  ** History: 7/21/89, DSJ, Created.
00176  */
00177   MFEDGEPT *Current;
00178   MFEDGEPT *Last;
00179   MFOUTLINE EdgePoint;
00180 
00181   if (DegenerateOutline (Outline))
00182     return;
00183 
00184   Last = PointAt (Outline);
00185   Outline = NextPointAfter (Outline);
00186   EdgePoint = Outline;
00187   do {
00188     Current = PointAt (EdgePoint);
00189     ComputeDirection(Last, Current, MinSlope, MaxSlope);
00190 
00191     Last = Current;
00192     EdgePoint = NextPointAfter (EdgePoint);
00193   }
00194   while (EdgePoint != Outline);
00195 
00196 }                                /* FindDirectionChanges */
00197 
00198 
00199 /*---------------------------------------------------------------------------*/
00200 void FreeMFOutline(void *arg) {  //MFOUTLINE                             Outline)
00201 /*
00202  ** Parameters:
00203  **   Outline   micro-feature outline to be freed
00204  ** Globals: none
00205  ** Operation:
00206  **   This routine deallocates all of the memory consumed by
00207  **   a micro-feature outline.
00208  ** Return: none
00209  ** Exceptions: none
00210  ** History: 7/27/89, DSJ, Created.
00211  */
00212   MFOUTLINE Start;
00213   MFOUTLINE Outline = (MFOUTLINE) arg;
00214 
00215   /* break the circular outline so we can use std. techniques to deallocate */
00216   Start = list_rest (Outline);
00217   set_rest(Outline, NIL_LIST);
00218   while (Start != NULL) {
00219     free_struct (first_node (Start), sizeof (MFEDGEPT), "MFEDGEPT");
00220     Start = pop (Start);
00221   }
00222 
00223 }                                /* FreeMFOutline */
00224 
00225 
00226 /*---------------------------------------------------------------------------*/
00227 void FreeOutlines(LIST Outlines) {
00228 /*
00229  ** Parameters:
00230  **   Outlines  list of mf-outlines to be freed
00231  ** Globals: none
00232  ** Operation: Release all memory consumed by the specified list
00233  **   of outlines.
00234  ** Return: none
00235  ** Exceptions: none
00236  ** History: Thu Dec 13 16:14:50 1990, DSJ, Created.
00237  */
00238   destroy_nodes(Outlines, FreeMFOutline);
00239 }                                /* FreeOutlines */
00240 
00241 
00242 /*---------------------------------------------------------------------------*/
00243 void MarkDirectionChanges(MFOUTLINE Outline) {
00244 /*
00245  ** Parameters:
00246  **   Outline   micro-feature outline to analyze
00247  ** Globals: none
00248  ** Operation:
00249  **   This routine searches thru the specified outline and finds
00250  **   the points at which the outline changes direction.  These
00251  **   points are then marked as "extremities".  This routine is
00252  **   used as an alternative to FindExtremities().  It forces the
00253  **   endpoints of the microfeatures to be at the direction
00254  **   changes rather than at the midpoint between direction
00255  **   changes.
00256  ** Return: none
00257  ** Exceptions: none
00258  ** History: 6/29/90, DSJ, Created.
00259  */
00260   MFOUTLINE Current;
00261   MFOUTLINE Last;
00262   MFOUTLINE First;
00263 
00264   if (DegenerateOutline (Outline))
00265     return;
00266 
00267   First = NextDirectionChange (Outline);
00268   Last = First;
00269   do {
00270     Current = NextDirectionChange (Last);
00271     MarkPoint (PointAt (Current));
00272     Last = Current;
00273   }
00274   while (Last != First);
00275 
00276 }                                /* MarkDirectionChanges */
00277 
00278 
00279 /*---------------------------------------------------------------------------*/
00280 // Return a new edge point for a micro-feature outline.
00281 MFEDGEPT *NewEdgePoint() {
00282   return ((MFEDGEPT *) alloc_struct(sizeof(MFEDGEPT), "MFEDGEPT"));
00283 }
00284 
00285 
00286 /*---------------------------------------------------------------------------*/
00287 MFOUTLINE NextExtremity(MFOUTLINE EdgePoint) {
00288 /*
00289  ** Parameters:
00290  **   EdgePoint start search from this point
00291  ** Globals: none
00292  ** Operation:
00293  **   This routine returns the next point in the micro-feature
00294  **   outline that is an extremity.  The search starts after
00295  **   EdgePoint.  The routine assumes that the outline being
00296  **   searched is not a degenerate outline (i.e. it must have
00297  **   2 or more edge points).
00298  ** Return: Next extremity in the outline after EdgePoint.
00299  ** Exceptions: none
00300  ** History: 7/26/89, DSJ, Created.
00301  */
00302   EdgePoint = NextPointAfter(EdgePoint);
00303   while (!PointAt(EdgePoint)->ExtremityMark)
00304     EdgePoint = NextPointAfter(EdgePoint);
00305 
00306   return (EdgePoint);
00307 
00308 }                                /* NextExtremity */
00309 
00310 
00311 /*---------------------------------------------------------------------------*/
00312 void NormalizeOutline(MFOUTLINE Outline,
00313                       FLOAT32 XOrigin) {
00314 /*
00315  ** Parameters:
00316  **   Outline   outline to be normalized
00317  **   XOrigin   x-origin of text
00318  ** Globals: none
00319  ** Operation:
00320  **   This routine normalizes the coordinates of the specified
00321  **   outline so that the outline is deskewed down to the
00322  **   baseline, translated so that x=0 is at XOrigin, and scaled
00323  **   so that the height of a character cell from descender to
00324  **   ascender is 1.  Of this height, 0.25 is for the descender,
00325  **   0.25 for the ascender, and 0.5 for the x-height.  The
00326  **   y coordinate of the baseline is 0.
00327  ** Return: none
00328  ** Exceptions: none
00329  ** History: 8/2/89, DSJ, Created.
00330  */
00331   if (Outline == NIL_LIST)
00332     return;
00333 
00334   MFOUTLINE EdgePoint = Outline;
00335   do {
00336     MFEDGEPT *Current = PointAt(EdgePoint);
00337     Current->Point.y = MF_SCALE_FACTOR * (Current->Point.y - BASELINE_OFFSET);
00338     Current->Point.x = MF_SCALE_FACTOR * (Current->Point.x - XOrigin);
00339     EdgePoint = NextPointAfter(EdgePoint);
00340   } while (EdgePoint != Outline);
00341 }                                /* NormalizeOutline */
00342 
00343 
00344 /*---------------------------------------------------------------------------*/
00345 namespace tesseract {
00346 void Classify::NormalizeOutlines(LIST Outlines,
00347                                  FLOAT32 *XScale,
00348                                  FLOAT32 *YScale) {
00349 /*
00350  ** Parameters:
00351  **   Outlines  list of outlines to be normalized
00352  **   XScale    x-direction scale factor used by routine
00353  **   YScale    y-direction scale factor used by routine
00354  ** Globals:
00355  **   classify_norm_method  method being used for normalization
00356  **   classify_char_norm_range map radius of gyration to this value
00357  ** Operation: This routine normalizes every outline in Outlines
00358  **   according to the currently selected normalization method.
00359  **   It also returns the scale factors that it used to do this
00360  **   scaling.  The scale factors returned represent the x and
00361  **   y sizes in the normalized coordinate system that correspond
00362  **   to 1 pixel in the original coordinate system.
00363  ** Return: none (Outlines are changed and XScale and YScale are updated)
00364  ** Exceptions: none
00365  ** History: Fri Dec 14 08:14:55 1990, DSJ, Created.
00366  */
00367   MFOUTLINE Outline;
00368   OUTLINE_STATS OutlineStats;
00369   FLOAT32 BaselineScale;
00370 
00371   switch (classify_norm_method) {
00372     case character:
00373       ComputeOutlineStats(Outlines, &OutlineStats);
00374 
00375       /* limit scale factor to avoid overscaling small blobs (.,`'),
00376          thin blobs (l1ift), and merged blobs */
00377       *XScale = *YScale = BaselineScale = MF_SCALE_FACTOR;
00378       *XScale *= OutlineStats.Ry;
00379       *YScale *= OutlineStats.Rx;
00380       if (*XScale < classify_min_norm_scale_x)
00381         *XScale = classify_min_norm_scale_x;
00382       if (*YScale < classify_min_norm_scale_y)
00383         *YScale = classify_min_norm_scale_y;
00384       if (*XScale > classify_max_norm_scale_x &&
00385           *YScale <= classify_max_norm_scale_y)
00386         *XScale = classify_max_norm_scale_x;
00387       *XScale = classify_char_norm_range * BaselineScale / *XScale;
00388       *YScale = classify_char_norm_range * BaselineScale / *YScale;
00389 
00390       iterate(Outlines) {
00391         Outline = (MFOUTLINE) first_node (Outlines);
00392         CharNormalizeOutline (Outline,
00393           OutlineStats.x, OutlineStats.y,
00394           *XScale, *YScale);
00395       }
00396       break;
00397 
00398     case baseline:
00399       iterate(Outlines) {
00400         Outline = (MFOUTLINE) first_node(Outlines);
00401         NormalizeOutline(Outline, 0.0);
00402       }
00403       *XScale = *YScale = MF_SCALE_FACTOR;
00404       break;
00405   }
00406 }                                /* NormalizeOutlines */
00407 }  // namespace tesseract
00408 
00412 /*---------------------------------------------------------------------------*/
00413 void ChangeDirection(MFOUTLINE Start, MFOUTLINE End, DIRECTION Direction) {
00414 /*
00415  ** Parameters:
00416  **   Start, End  defines segment of outline to be modified
00417  **   Direction new direction to assign to segment
00418  ** Globals: none
00419  ** Operation: Change the direction of every vector in the specified
00420  **   outline segment to Direction.  The segment to be changed
00421  **   starts at Start and ends at End.  Note that the previous
00422  **   direction of End must also be changed to reflect the
00423  **   change in direction of the point before it.
00424  ** Return: none
00425  ** Exceptions: none
00426  ** History: Fri May  4 10:42:04 1990, DSJ, Created.
00427  */
00428   MFOUTLINE Current;
00429 
00430   for (Current = Start; Current != End; Current = NextPointAfter (Current))
00431     PointAt (Current)->Direction = Direction;
00432 
00433   PointAt (End)->PreviousDirection = Direction;
00434 
00435 }                                /* ChangeDirection */
00436 
00437 
00438 /*---------------------------------------------------------------------------*/
00439 void CharNormalizeOutline(MFOUTLINE Outline,
00440                           FLOAT32 XCenter,
00441                           FLOAT32 YCenter,
00442                           FLOAT32 XScale,
00443                           FLOAT32 YScale) {
00444 /*
00445  ** Parameters:
00446  **   Outline     outline to be character normalized
00447  **   XCenter, YCenter  center point for normalization
00448  **   XScale, YScale    scale factors for normalization
00449  ** Globals: none
00450  ** Operation: This routine normalizes each point in Outline by
00451  **   translating it to the specified center and scaling it
00452  **   anisotropically according to the given scale factors.
00453  ** Return: none
00454  ** Exceptions: none
00455  ** History: Fri Dec 14 10:27:11 1990, DSJ, Created.
00456  */
00457   MFOUTLINE First, Current;
00458   MFEDGEPT *CurrentPoint;
00459 
00460   if (Outline == NIL_LIST)
00461     return;
00462 
00463   First = Outline;
00464   Current = First;
00465   do {
00466     CurrentPoint = PointAt (Current);
00467     CurrentPoint->Point.x =
00468       (CurrentPoint->Point.x - XCenter) * XScale;
00469     CurrentPoint->Point.y =
00470       (CurrentPoint->Point.y - YCenter) * YScale;
00471 
00472     Current = NextPointAfter (Current);
00473   }
00474   while (Current != First);
00475 
00476 }                                /* CharNormalizeOutline */
00477 
00478 
00479 /*---------------------------------------------------------------------------*/
00480 void ComputeDirection(MFEDGEPT *Start,
00481                       MFEDGEPT *Finish,
00482                       FLOAT32 MinSlope,
00483                       FLOAT32 MaxSlope) {
00484 /*
00485  ** Parameters:
00486  **   Start   starting point to compute direction from
00487  **   Finish    finishing point to compute direction to
00488  **   MinSlope  slope below which lines are horizontal
00489  **   MaxSlope  slope above which lines are vertical
00490  ** Globals: none
00491  ** Operation:
00492  **   This routine computes the slope from Start to Finish and
00493  **   and then computes the approximate direction of the line
00494  **   segment from Start to Finish.  The direction is quantized
00495  **   into 8 buckets:
00496  **     N, S, E, W, NE, NW, SE, SW
00497  **   Both the slope and the direction are then stored into
00498  **   the appropriate fields of the Start edge point.  The
00499  **   direction is also stored into the PreviousDirection field
00500  **   of the Finish edge point.
00501  ** Return: none
00502  ** Exceptions: none
00503  ** History: 7/25/89, DSJ, Created.
00504  */
00505   FVECTOR Delta;
00506 
00507   Delta.x = Finish->Point.x - Start->Point.x;
00508   Delta.y = Finish->Point.y - Start->Point.y;
00509   if (Delta.x == 0)
00510   if (Delta.y < 0) {
00511     Start->Slope = -MAX_FLOAT32;
00512     Start->Direction = south;
00513   }
00514   else {
00515     Start->Slope = MAX_FLOAT32;
00516     Start->Direction = north;
00517   }
00518   else {
00519     Start->Slope = Delta.y / Delta.x;
00520     if (Delta.x > 0)
00521       if (Delta.y > 0)
00522         if (Start->Slope > MinSlope)
00523           if (Start->Slope < MaxSlope)
00524             Start->Direction = northeast;
00525     else
00526       Start->Direction = north;
00527     else
00528       Start->Direction = east;
00529     else if (Start->Slope < -MinSlope)
00530     if (Start->Slope > -MaxSlope)
00531       Start->Direction = southeast;
00532     else
00533       Start->Direction = south;
00534     else
00535       Start->Direction = east;
00536     else if (Delta.y > 0)
00537     if (Start->Slope < -MinSlope)
00538       if (Start->Slope > -MaxSlope)
00539         Start->Direction = northwest;
00540     else
00541       Start->Direction = north;
00542     else
00543       Start->Direction = west;
00544     else if (Start->Slope > MinSlope)
00545     if (Start->Slope < MaxSlope)
00546       Start->Direction = southwest;
00547     else
00548       Start->Direction = south;
00549     else
00550       Start->Direction = west;
00551   }
00552   Finish->PreviousDirection = Start->Direction;
00553 }                                /* ComputeDirection */
00554 
00555 
00556 /*---------------------------------------------------------------------------*/
00557 void FinishOutlineStats(register OUTLINE_STATS *OutlineStats) {
00558 /*
00559  ** Parameters:
00560  **   OutlineStats  statistics about a set of outlines
00561  ** Globals: none
00562  ** Operation: Use the preliminary statistics accumulated in OutlineStats
00563  **   to compute the final statistics.
00564  **   (see Dan Johnson's Tesseract lab
00565  **   notebook #2, pgs. 74-78).
00566  ** Return: none
00567  ** Exceptions: none
00568  ** History: Fri Dec 14 10:13:36 1990, DSJ, Created.
00569  */
00570   OutlineStats->x = 0.5 * OutlineStats->My / OutlineStats->L;
00571   OutlineStats->y = 0.5 * OutlineStats->Mx / OutlineStats->L;
00572 
00573   OutlineStats->Ix = (OutlineStats->Ix / 3.0 -
00574     OutlineStats->y * OutlineStats->Mx +
00575     OutlineStats->y * OutlineStats->y * OutlineStats->L);
00576 
00577   OutlineStats->Iy = (OutlineStats->Iy / 3.0 -
00578     OutlineStats->x * OutlineStats->My +
00579     OutlineStats->x * OutlineStats->x * OutlineStats->L);
00580 
00581   /* Ix and/or Iy could possibly be negative due to roundoff error */
00582   if (OutlineStats->Ix < 0.0)
00583     OutlineStats->Ix = MIN_INERTIA;
00584   if (OutlineStats->Iy < 0.0)
00585     OutlineStats->Iy = MIN_INERTIA;
00586 
00587   OutlineStats->Rx = sqrt (OutlineStats->Ix / OutlineStats->L);
00588   OutlineStats->Ry = sqrt (OutlineStats->Iy / OutlineStats->L);
00589 
00590   OutlineStats->Mx *= 0.5;
00591   OutlineStats->My *= 0.5;
00592 
00593 }                                /* FinishOutlineStats */
00594 
00595 
00596 /*---------------------------------------------------------------------------*/
00597 void InitOutlineStats(OUTLINE_STATS *OutlineStats) {
00598 /*
00599  ** Parameters:
00600  **   OutlineStats  stats data structure to be initialized
00601  ** Globals: none
00602  ** Operation: Initialize the outline statistics data structure so
00603  **   that it is ready to start accumulating statistics.
00604  ** Return: none
00605  ** Exceptions: none
00606  ** History: Fri Dec 14 08:55:22 1990, DSJ, Created.
00607  */
00608   OutlineStats->Mx = 0.0;
00609   OutlineStats->My = 0.0;
00610   OutlineStats->L = 0.0;
00611   OutlineStats->x = 0.0;
00612   OutlineStats->y = 0.0;
00613   OutlineStats->Ix = 0.0;
00614   OutlineStats->Iy = 0.0;
00615   OutlineStats->Rx = 0.0;
00616   OutlineStats->Ry = 0.0;
00617 }                                /* InitOutlineStats */
00618 
00619 
00620 /*---------------------------------------------------------------------------*/
00621 MFOUTLINE NextDirectionChange(MFOUTLINE EdgePoint) {
00622 /*
00623  ** Parameters:
00624  **   EdgePoint start search from this point
00625  ** Globals: none
00626  ** Operation:
00627  **   This routine returns the next point in the micro-feature
00628  **   outline that has a direction different than EdgePoint.  The
00629  **   routine assumes that the outline being searched is not a
00630  **   degenerate outline (i.e. it must have 2 or more edge points).
00631  ** Return: Point of next direction change in micro-feature outline.
00632  ** Exceptions: none
00633  ** History: 7/25/89, DSJ, Created.
00634  */
00635   DIRECTION InitialDirection;
00636 
00637   InitialDirection = PointAt (EdgePoint)->Direction;
00638 
00639   MFOUTLINE next_pt = NULL;
00640   do {
00641     EdgePoint = NextPointAfter(EdgePoint);
00642     next_pt = NextPointAfter(EdgePoint);
00643   } while (PointAt(EdgePoint)->Direction == InitialDirection &&
00644            !PointAt(EdgePoint)->Hidden &&
00645            next_pt != NULL && !PointAt(next_pt)->Hidden);
00646 
00647   return (EdgePoint);
00648 }                                /* NextDirectionChange */
00649 
00650 
00651 /*---------------------------------------------------------------------------*/
00652 void UpdateOutlineStats(register OUTLINE_STATS *OutlineStats,
00653                         register FLOAT32 x1,
00654                         register FLOAT32 x2,
00655                         register FLOAT32 y1,
00656                         register FLOAT32 y2) {
00657 /*
00658  ** Parameters:
00659  **   OutlineStats  statistics to add this segment to
00660  **   x1, y1, x2, y2  segment to be added to statistics
00661  ** Globals: none
00662  ** Operation: This routine adds the statistics for the specified
00663  **   line segment to OutlineStats.  The statistics that are
00664  **   kept are:
00665  **     sum of length of all segments
00666  **     sum of 2*Mx for all segments
00667  **     sum of 2*My for all segments
00668  **     sum of 2*Mx*(y1+y2) - L*y1*y2 for all segments
00669  **     sum of 2*My*(x1+x2) - L*x1*x2 for all segments
00670  **   These numbers, once collected can later be used to easily
00671  **   compute the center of mass, first and second moments,
00672  **   and radii of gyration.  (see Dan Johnson's Tesseract lab
00673  **   notebook #2, pgs. 74-78).
00674  ** Return: none
00675  ** Exceptions: none
00676  ** History: Fri Dec 14 08:59:17 1990, DSJ, Created.
00677  */
00678   register FLOAT64 L;
00679   register FLOAT64 Mx2;
00680   register FLOAT64 My2;
00681 
00682   /* compute length of segment */
00683   L = sqrt ((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
00684   OutlineStats->L += L;
00685 
00686   /* compute 2Mx and 2My components */
00687   Mx2 = L * (y1 + y2);
00688   My2 = L * (x1 + x2);
00689   OutlineStats->Mx += Mx2;
00690   OutlineStats->My += My2;
00691 
00692   /* compute second moment component */
00693   OutlineStats->Ix += Mx2 * (y1 + y2) - L * y1 * y2;
00694   OutlineStats->Iy += My2 * (x1 + x2) - L * x1 * x2;
00695 
00696 }                                /* UpdateOutlineStats */