Tesseract 3.02

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

- File List
- File Members

All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
mfoutline.cpp
Go to the documentation of this file.
1 /******************************************************************************
2 ** Filename: mfoutline.c
3 ** Purpose: Interface to outline struct used for extracting features
4 ** Author: Dan Johnson
5 ** History: Thu May 17 08:14:18 1990, 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 #include "clusttool.h" //If remove you get cought in a loop somewhere
22 #include "emalloc.h"
23 #include "mfoutline.h"
24 #include "blobs.h"
25 #include "const.h"
26 #include "mfx.h"
27 #include "params.h"
28 #include "classify.h"
29
30 #include <math.h>
31 #include <stdio.h>
32
33 #define MIN_INERTIA (0.00001)
34
35 /*----------------------------------------------------------------------------
36 Public Code
37 ----------------------------------------------------------------------------*/
38
39 /*---------------------------------------------------------------------------*/
40 // Convert a blob into a list of MFOUTLINEs (float-based microfeature format).
41 LIST ConvertBlob(TBLOB *blob) {
42 LIST outlines = NIL_LIST;
43 return (blob == NULL)
44 ? NIL_LIST
45 : ConvertOutlines(blob->outlines, outlines, outer);
46 }
47
48
49 /*---------------------------------------------------------------------------*/
50 // Convert a TESSLINE into the float-based MFOUTLINE micro-feature format.
51 MFOUTLINE ConvertOutline(TESSLINE *outline) {
52 MFEDGEPT *NewPoint;
53 MFOUTLINE MFOutline = NIL_LIST;
54 EDGEPT *EdgePoint;
55 EDGEPT *StartPoint;
56 EDGEPT *NextPoint;
57
58 if (outline == NULL || outline->loop == NULL)
59 return MFOutline;
60
61 StartPoint = outline->loop;
62 EdgePoint = StartPoint;
63 do {
64 NextPoint = EdgePoint->next;
65
66 /* filter out duplicate points */
67 if (EdgePoint->pos.x != NextPoint->pos.x ||
68 EdgePoint->pos.y != NextPoint->pos.y) {
69 NewPoint = NewEdgePoint();
70 ClearMark(NewPoint);
71 NewPoint->Hidden = EdgePoint->IsHidden();
72 NewPoint->Point.x = EdgePoint->pos.x;
73 NewPoint->Point.y = EdgePoint->pos.y;
74 MFOutline = push(MFOutline, NewPoint);
75 }
76 EdgePoint = NextPoint;
77 } while (EdgePoint != StartPoint);
78
79 if (MFOutline != NULL)
80 MakeOutlineCircular(MFOutline);
81 return MFOutline;
82 }
83
84
85 /*---------------------------------------------------------------------------*/
86 // Convert a tree of outlines to a list of MFOUTLINEs (lists of MFEDGEPTs).
87 //
88 // Parameters:
89 // outline first outline to be converted
90 // mf_outlines list to add converted outlines to
91 // outline_type are the outlines outer or holes?
92 LIST ConvertOutlines(TESSLINE *outline,
93 LIST mf_outlines,
94 OUTLINETYPE outline_type) {
95 MFOUTLINE mf_outline;
96
97 while (outline != NULL) {
98 mf_outline = ConvertOutline(outline);
99 if (mf_outline != NULL)
100 mf_outlines = push(mf_outlines, mf_outline);
101 outline = outline->next;
102 }
103 return mf_outlines;
104 }
105
106
107 /*---------------------------------------------------------------------------*/
108 void ComputeOutlineStats(LIST Outlines, OUTLINE_STATS *OutlineStats) {
109 /*
110 ** Parameters:
111 ** Outlines list of outlines to compute stats for
112 ** OutlineStats place to put results
113 ** Globals: none
114 ** Operation: This routine computes several statistics about the outlines
115 ** in Outlines. These statistics are usually used to perform
116 ** anistropic normalization of all of the outlines. The
117 ** statistics generated are:
118 ** first moments about x and y axes
119 ** total length of all outlines
120 ** center of mass of all outlines
121 ** second moments about center of mass axes
122 ** radius of gyration about center of mass axes
123 ** Return: none (results are returned in OutlineStats)
124 ** Exceptions: none
125 ** History: Fri Dec 14 08:32:03 1990, DSJ, Created.
126 */
127 MFOUTLINE Outline;
128 MFOUTLINE EdgePoint;
129 MFEDGEPT *Current;
130 MFEDGEPT *Last;
131
132 InitOutlineStats(OutlineStats);
133 iterate(Outlines) {
134 Outline = (MFOUTLINE) first_node (Outlines);
135
136 Last = PointAt (Outline);
137 Outline = NextPointAfter (Outline);
138 EdgePoint = Outline;
139 do {
140 Current = PointAt (EdgePoint);
141
142 UpdateOutlineStats (OutlineStats,
143 Last->Point.x, Last->Point.y,
144 Current->Point.x, Current->Point.y);
145
146 Last = Current;
147 EdgePoint = NextPointAfter (EdgePoint);
148 }
149 while (EdgePoint != Outline);
150 }
151 FinishOutlineStats(OutlineStats);
152
153 } /* ComputeOutlineStats */
154
155
156 /*---------------------------------------------------------------------------*/
157 void FindDirectionChanges(MFOUTLINE Outline,
158 FLOAT32 MinSlope,
159 FLOAT32 MaxSlope) {
160 /*
161 ** Parameters:
162 ** Outline micro-feature outline to analyze
163 ** MinSlope controls "snapping" of segments to horizontal
164 ** MaxSlope controls "snapping" of segments to vertical
165 ** Globals: none
166 ** Operation:
167 ** This routine searches thru the specified outline, computes
168 ** a slope for each vector in the outline, and marks each
169 ** vector as having one of the following directions:
170 ** N, S, E, W, NE, NW, SE, SW
171 ** This information is then stored in the outline and the
172 ** outline is returned.
173 ** Return: none
174 ** Exceptions: none
175 ** History: 7/21/89, DSJ, Created.
176 */
177 MFEDGEPT *Current;
178 MFEDGEPT *Last;
179 MFOUTLINE EdgePoint;
180
181 if (DegenerateOutline (Outline))
182 return;
183
184 Last = PointAt (Outline);
185 Outline = NextPointAfter (Outline);
186 EdgePoint = Outline;
187 do {
188 Current = PointAt (EdgePoint);
189 ComputeDirection(Last, Current, MinSlope, MaxSlope);
190
191 Last = Current;
192 EdgePoint = NextPointAfter (EdgePoint);
193 }
194 while (EdgePoint != Outline);
195
196 } /* FindDirectionChanges */
197
198
199 /*---------------------------------------------------------------------------*/
200 void FreeMFOutline(void *arg) { //MFOUTLINE Outline)
201 /*
202 ** Parameters:
203 ** Outline micro-feature outline to be freed
204 ** Globals: none
205 ** Operation:
206 ** This routine deallocates all of the memory consumed by
207 ** a micro-feature outline.
208 ** Return: none
209 ** Exceptions: none
210 ** History: 7/27/89, DSJ, Created.
211 */
212 MFOUTLINE Start;
213 MFOUTLINE Outline = (MFOUTLINE) arg;
214
215 /* break the circular outline so we can use std. techniques to deallocate */
216 Start = list_rest (Outline);
217 set_rest(Outline, NIL_LIST);
218 while (Start != NULL) {
219 free_struct (first_node (Start), sizeof (MFEDGEPT), "MFEDGEPT");
220 Start = pop (Start);
221 }
222
223 } /* FreeMFOutline */
224
225
226 /*---------------------------------------------------------------------------*/
227 void FreeOutlines(LIST Outlines) {
228 /*
229 ** Parameters:
230 ** Outlines list of mf-outlines to be freed
231 ** Globals: none
232 ** Operation: Release all memory consumed by the specified list
233 ** of outlines.
234 ** Return: none
235 ** Exceptions: none
236 ** History: Thu Dec 13 16:14:50 1990, DSJ, Created.
237 */
238 destroy_nodes(Outlines, FreeMFOutline);
239 } /* FreeOutlines */
240
241
242 /*---------------------------------------------------------------------------*/
243 void MarkDirectionChanges(MFOUTLINE Outline) {
244 /*
245 ** Parameters:
246 ** Outline micro-feature outline to analyze
247 ** Globals: none
248 ** Operation:
249 ** This routine searches thru the specified outline and finds
250 ** the points at which the outline changes direction. These
251 ** points are then marked as "extremities". This routine is
252 ** used as an alternative to FindExtremities(). It forces the
253 ** endpoints of the microfeatures to be at the direction
254 ** changes rather than at the midpoint between direction
255 ** changes.
256 ** Return: none
257 ** Exceptions: none
258 ** History: 6/29/90, DSJ, Created.
259 */
260 MFOUTLINE Current;
261 MFOUTLINE Last;
262 MFOUTLINE First;
263
264 if (DegenerateOutline (Outline))
265 return;
266
267 First = NextDirectionChange (Outline);
268 Last = First;
269 do {
270 Current = NextDirectionChange (Last);
271 MarkPoint (PointAt (Current));
272 Last = Current;
273 }
274 while (Last != First);
275
276 } /* MarkDirectionChanges */
277
278
279 /*---------------------------------------------------------------------------*/
280 // Return a new edge point for a micro-feature outline.
281 MFEDGEPT *NewEdgePoint() {
282 return ((MFEDGEPT *) alloc_struct(sizeof(MFEDGEPT), "MFEDGEPT"));
283 }
284
285
286 /*---------------------------------------------------------------------------*/
287 MFOUTLINE NextExtremity(MFOUTLINE EdgePoint) {
288 /*
289 ** Parameters:
290 ** EdgePoint start search from this point
291 ** Globals: none
292 ** Operation:
293 ** This routine returns the next point in the micro-feature
294 ** outline that is an extremity. The search starts after
295 ** EdgePoint. The routine assumes that the outline being
296 ** searched is not a degenerate outline (i.e. it must have
297 ** 2 or more edge points).
298 ** Return: Next extremity in the outline after EdgePoint.
299 ** Exceptions: none
300 ** History: 7/26/89, DSJ, Created.
301 */
302 EdgePoint = NextPointAfter(EdgePoint);
303 while (!PointAt(EdgePoint)->ExtremityMark)
304 EdgePoint = NextPointAfter(EdgePoint);
305
306 return (EdgePoint);
307
308 } /* NextExtremity */
309
310
311 /*---------------------------------------------------------------------------*/
312 void NormalizeOutline(MFOUTLINE Outline,
313 FLOAT32 XOrigin) {
314 /*
315 ** Parameters:
316 ** Outline outline to be normalized
317 ** XOrigin x-origin of text
318 ** Globals: none
319 ** Operation:
320 ** This routine normalizes the coordinates of the specified
321 ** outline so that the outline is deskewed down to the
322 ** baseline, translated so that x=0 is at XOrigin, and scaled
323 ** so that the height of a character cell from descender to
324 ** ascender is 1. Of this height, 0.25 is for the descender,
325 ** 0.25 for the ascender, and 0.5 for the x-height. The
326 ** y coordinate of the baseline is 0.
327 ** Return: none
328 ** Exceptions: none
329 ** History: 8/2/89, DSJ, Created.
330 */
331 if (Outline == NIL_LIST)
332 return;
333
334 MFOUTLINE EdgePoint = Outline;
335 do {
336 MFEDGEPT *Current = PointAt(EdgePoint);
337 Current->Point.y = MF_SCALE_FACTOR * (Current->Point.y - BASELINE_OFFSET);
338 Current->Point.x = MF_SCALE_FACTOR * (Current->Point.x - XOrigin);
339 EdgePoint = NextPointAfter(EdgePoint);
340 } while (EdgePoint != Outline);
341 } /* NormalizeOutline */
342
343
344 /*---------------------------------------------------------------------------*/
345 namespace tesseract {
346 void Classify::NormalizeOutlines(LIST Outlines,
347 FLOAT32 *XScale,
348 FLOAT32 *YScale) {
349 /*
350 ** Parameters:
351 ** Outlines list of outlines to be normalized
352 ** XScale x-direction scale factor used by routine
353 ** YScale y-direction scale factor used by routine
354 ** Globals:
355 ** classify_norm_method method being used for normalization
356 ** classify_char_norm_range map radius of gyration to this value
357 ** Operation: This routine normalizes every outline in Outlines
358 ** according to the currently selected normalization method.
359 ** It also returns the scale factors that it used to do this
360 ** scaling. The scale factors returned represent the x and
361 ** y sizes in the normalized coordinate system that correspond
362 ** to 1 pixel in the original coordinate system.
363 ** Return: none (Outlines are changed and XScale and YScale are updated)
364 ** Exceptions: none
365 ** History: Fri Dec 14 08:14:55 1990, DSJ, Created.
366 */
367 MFOUTLINE Outline;
368 OUTLINE_STATS OutlineStats;
369 FLOAT32 BaselineScale;
370
371 switch (classify_norm_method) {
372 case character:
373 ComputeOutlineStats(Outlines, &OutlineStats);
374
375 /* limit scale factor to avoid overscaling small blobs (.,`'),
376 thin blobs (l1ift), and merged blobs */
377 *XScale = *YScale = BaselineScale = MF_SCALE_FACTOR;
378 *XScale *= OutlineStats.Ry;
379 *YScale *= OutlineStats.Rx;
380 if (*XScale < classify_min_norm_scale_x)
381 *XScale = classify_min_norm_scale_x;
382 if (*YScale < classify_min_norm_scale_y)
383 *YScale = classify_min_norm_scale_y;
384 if (*XScale > classify_max_norm_scale_x &&
385 *YScale <= classify_max_norm_scale_y)
386 *XScale = classify_max_norm_scale_x;
387 *XScale = classify_char_norm_range * BaselineScale / *XScale;
388 *YScale = classify_char_norm_range * BaselineScale / *YScale;
389
390 iterate(Outlines) {
391 Outline = (MFOUTLINE) first_node (Outlines);
392 CharNormalizeOutline (Outline,
393 OutlineStats.x, OutlineStats.y,
394 *XScale, *YScale);
395 }
396 break;
397
398 case baseline:
399 iterate(Outlines) {
400 Outline = (MFOUTLINE) first_node(Outlines);
401 NormalizeOutline(Outline, 0.0);
402 }
403 *XScale = *YScale = MF_SCALE_FACTOR;
404 break;
405 }
406 } /* NormalizeOutlines */
407 } // namespace tesseract
408
412 /*---------------------------------------------------------------------------*/
413 void ChangeDirection(MFOUTLINE Start, MFOUTLINE End, DIRECTION Direction) {
414 /*
415 ** Parameters:
416 ** Start, End defines segment of outline to be modified
417 ** Direction new direction to assign to segment
418 ** Globals: none
419 ** Operation: Change the direction of every vector in the specified
420 ** outline segment to Direction. The segment to be changed
421 ** starts at Start and ends at End. Note that the previous
422 ** direction of End must also be changed to reflect the
423 ** change in direction of the point before it.
424 ** Return: none
425 ** Exceptions: none
426 ** History: Fri May 4 10:42:04 1990, DSJ, Created.
427 */
428 MFOUTLINE Current;
429
430 for (Current = Start; Current != End; Current = NextPointAfter (Current))
431 PointAt (Current)->Direction = Direction;
432
433 PointAt (End)->PreviousDirection = Direction;
434
435 } /* ChangeDirection */
436
437
438 /*---------------------------------------------------------------------------*/
439 void CharNormalizeOutline(MFOUTLINE Outline,
440 FLOAT32 XCenter,
441 FLOAT32 YCenter,
442 FLOAT32 XScale,
443 FLOAT32 YScale) {
444 /*
445 ** Parameters:
446 ** Outline outline to be character normalized
447 ** XCenter, YCenter center point for normalization
448 ** XScale, YScale scale factors for normalization
449 ** Globals: none
450 ** Operation: This routine normalizes each point in Outline by
451 ** translating it to the specified center and scaling it
452 ** anisotropically according to the given scale factors.
453 ** Return: none
454 ** Exceptions: none
455 ** History: Fri Dec 14 10:27:11 1990, DSJ, Created.
456 */
457 MFOUTLINE First, Current;
458 MFEDGEPT *CurrentPoint;
459
460 if (Outline == NIL_LIST)
461 return;
462
463 First = Outline;
464 Current = First;
465 do {
466 CurrentPoint = PointAt (Current);
467 CurrentPoint->Point.x =
468 (CurrentPoint->Point.x - XCenter) * XScale;
469 CurrentPoint->Point.y =
470 (CurrentPoint->Point.y - YCenter) * YScale;
471
472 Current = NextPointAfter (Current);
473 }
474 while (Current != First);
475
476 } /* CharNormalizeOutline */
477
478
479 /*---------------------------------------------------------------------------*/
480 void ComputeDirection(MFEDGEPT *Start,
481 MFEDGEPT *Finish,
482 FLOAT32 MinSlope,
483 FLOAT32 MaxSlope) {
484 /*
485 ** Parameters:
486 ** Start starting point to compute direction from
487 ** Finish finishing point to compute direction to
488 ** MinSlope slope below which lines are horizontal
489 ** MaxSlope slope above which lines are vertical
490 ** Globals: none
491 ** Operation:
492 ** This routine computes the slope from Start to Finish and
493 ** and then computes the approximate direction of the line
494 ** segment from Start to Finish. The direction is quantized
495 ** into 8 buckets:
496 ** N, S, E, W, NE, NW, SE, SW
497 ** Both the slope and the direction are then stored into
498 ** the appropriate fields of the Start edge point. The
499 ** direction is also stored into the PreviousDirection field
500 ** of the Finish edge point.
501 ** Return: none
502 ** Exceptions: none
503 ** History: 7/25/89, DSJ, Created.
504 */
505 FVECTOR Delta;
506
507 Delta.x = Finish->Point.x - Start->Point.x;
508 Delta.y = Finish->Point.y - Start->Point.y;
509 if (Delta.x == 0)
510 if (Delta.y < 0) {
511 Start->Slope = -MAX_FLOAT32;
512 Start->Direction = south;
513 }
514 else {
515 Start->Slope = MAX_FLOAT32;
516 Start->Direction = north;
517 }
518 else {
519 Start->Slope = Delta.y / Delta.x;
520 if (Delta.x > 0)
521 if (Delta.y > 0)
522 if (Start->Slope > MinSlope)
523 if (Start->Slope < MaxSlope)
524 Start->Direction = northeast;
525 else
526 Start->Direction = north;
527 else
528 Start->Direction = east;
529 else if (Start->Slope < -MinSlope)
530 if (Start->Slope > -MaxSlope)
531 Start->Direction = southeast;
532 else
533 Start->Direction = south;
534 else
535 Start->Direction = east;
536 else if (Delta.y > 0)
537 if (Start->Slope < -MinSlope)
538 if (Start->Slope > -MaxSlope)
539 Start->Direction = northwest;
540 else
541 Start->Direction = north;
542 else
543 Start->Direction = west;
544 else if (Start->Slope > MinSlope)
545 if (Start->Slope < MaxSlope)
546 Start->Direction = southwest;
547 else
548 Start->Direction = south;
549 else
550 Start->Direction = west;
551 }
552 Finish->PreviousDirection = Start->Direction;
553 } /* ComputeDirection */
554
555
556 /*---------------------------------------------------------------------------*/
557 void FinishOutlineStats(register OUTLINE_STATS *OutlineStats) {
558 /*
559 ** Parameters:
560 ** OutlineStats statistics about a set of outlines
561 ** Globals: none
562 ** Operation: Use the preliminary statistics accumulated in OutlineStats
563 ** to compute the final statistics.
564 ** (see Dan Johnson's Tesseract lab
565 ** notebook #2, pgs. 74-78).
566 ** Return: none
567 ** Exceptions: none
568 ** History: Fri Dec 14 10:13:36 1990, DSJ, Created.
569 */
570 OutlineStats->x = 0.5 * OutlineStats->My / OutlineStats->L;
571 OutlineStats->y = 0.5 * OutlineStats->Mx / OutlineStats->L;
572
573 OutlineStats->Ix = (OutlineStats->Ix / 3.0 -
574 OutlineStats->y * OutlineStats->Mx +
575 OutlineStats->y * OutlineStats->y * OutlineStats->L);
576
577 OutlineStats->Iy = (OutlineStats->Iy / 3.0 -
578 OutlineStats->x * OutlineStats->My +
579 OutlineStats->x * OutlineStats->x * OutlineStats->L);
580
581 /* Ix and/or Iy could possibly be negative due to roundoff error */
582 if (OutlineStats->Ix < 0.0)
583 OutlineStats->Ix = MIN_INERTIA;
584 if (OutlineStats->Iy < 0.0)
585 OutlineStats->Iy = MIN_INERTIA;
586
587 OutlineStats->Rx = sqrt (OutlineStats->Ix / OutlineStats->L);
588 OutlineStats->Ry = sqrt (OutlineStats->Iy / OutlineStats->L);
589
590 OutlineStats->Mx *= 0.5;
591 OutlineStats->My *= 0.5;
592
593 } /* FinishOutlineStats */
594
595
596 /*---------------------------------------------------------------------------*/
597 void InitOutlineStats(OUTLINE_STATS *OutlineStats) {
598 /*
599 ** Parameters:
600 ** OutlineStats stats data structure to be initialized
601 ** Globals: none
602 ** Operation: Initialize the outline statistics data structure so
603 ** that it is ready to start accumulating statistics.
604 ** Return: none
605 ** Exceptions: none
606 ** History: Fri Dec 14 08:55:22 1990, DSJ, Created.
607 */
608 OutlineStats->Mx = 0.0;
609 OutlineStats->My = 0.0;
610 OutlineStats->L = 0.0;
611 OutlineStats->x = 0.0;
612 OutlineStats->y = 0.0;
613 OutlineStats->Ix = 0.0;
614 OutlineStats->Iy = 0.0;
615 OutlineStats->Rx = 0.0;
616 OutlineStats->Ry = 0.0;
617 } /* InitOutlineStats */
618
619
620 /*---------------------------------------------------------------------------*/
621 MFOUTLINE NextDirectionChange(MFOUTLINE EdgePoint) {
622 /*
623 ** Parameters:
624 ** EdgePoint start search from this point
625 ** Globals: none
626 ** Operation:
627 ** This routine returns the next point in the micro-feature
628 ** outline that has a direction different than EdgePoint. The
629 ** routine assumes that the outline being searched is not a
630 ** degenerate outline (i.e. it must have 2 or more edge points).
631 ** Return: Point of next direction change in micro-feature outline.
632 ** Exceptions: none
633 ** History: 7/25/89, DSJ, Created.
634 */
635 DIRECTION InitialDirection;
636
637 InitialDirection = PointAt (EdgePoint)->Direction;
638
639 MFOUTLINE next_pt = NULL;
640 do {
641 EdgePoint = NextPointAfter(EdgePoint);
642 next_pt = NextPointAfter(EdgePoint);
643 } while (PointAt(EdgePoint)->Direction == InitialDirection &&
644 !PointAt(EdgePoint)->Hidden &&
645 next_pt != NULL && !PointAt(next_pt)->Hidden);
646
647 return (EdgePoint);
648 } /* NextDirectionChange */
649
650
651 /*---------------------------------------------------------------------------*/
652 void UpdateOutlineStats(register OUTLINE_STATS *OutlineStats,
653 register FLOAT32 x1,
654 register FLOAT32 x2,
655 register FLOAT32 y1,
656 register FLOAT32 y2) {
657 /*
658 ** Parameters:
659 ** OutlineStats statistics to add this segment to
660 ** x1, y1, x2, y2 segment to be added to statistics
661 ** Globals: none
662 ** Operation: This routine adds the statistics for the specified
663 ** line segment to OutlineStats. The statistics that are
664 ** kept are:
665 ** sum of length of all segments
666 ** sum of 2*Mx for all segments
667 ** sum of 2*My for all segments
668 ** sum of 2*Mx*(y1+y2) - L*y1*y2 for all segments
669 ** sum of 2*My*(x1+x2) - L*x1*x2 for all segments
670 ** These numbers, once collected can later be used to easily
671 ** compute the center of mass, first and second moments,
672 ** and radii of gyration. (see Dan Johnson's Tesseract lab
673 ** notebook #2, pgs. 74-78).
674 ** Return: none
675 ** Exceptions: none
676 ** History: Fri Dec 14 08:59:17 1990, DSJ, Created.
677 */
678 register FLOAT64 L;
679 register FLOAT64 Mx2;
680 register FLOAT64 My2;
681
682 /* compute length of segment */
683 L = sqrt ((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
684 OutlineStats->L += L;
685
686 /* compute 2Mx and 2My components */
687 Mx2 = L * (y1 + y2);
688 My2 = L * (x1 + x2);
689 OutlineStats->Mx += Mx2;
690 OutlineStats->My += My2;
691
692 /* compute second moment component */
693 OutlineStats->Ix += Mx2 * (y1 + y2) - L * y1 * y2;
694 OutlineStats->Iy += My2 * (x1 + x2) - L * x1 * x2;
695
696 } /* UpdateOutlineStats */