Tesseract
3.02
|
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 */