Tesseract  3.02
 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).
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.
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?
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 /*---------------------------------------------------------------------------*/
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 /*---------------------------------------------------------------------------*/
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.
282  return ((MFEDGEPT *) alloc_struct(sizeof(MFEDGEPT), "MFEDGEPT"));
283 }
284 
285 
286 /*---------------------------------------------------------------------------*/
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 /*---------------------------------------------------------------------------*/
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 {
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 /*---------------------------------------------------------------------------*/
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 /*---------------------------------------------------------------------------*/
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 /*---------------------------------------------------------------------------*/
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 */