home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / odtlktv4.zip / ODTLKT / TOOLKIT / BETA / SAMPLES / OPENDOC / PUBUTILS / ALTPOLY.CPP < prev    next >
Text File  |  1995-11-29  |  18KB  |  698 lines

  1. /********************************************************************/
  2. /*  Licensed Materials - Property of IBM                            */
  3. /*                                                                  */
  4. /*                                                                  */
  5. /* Copyright (C) International Business Machines Corp., 1994.       */
  6. /* Copyright (C) Apple Computer, Inc., 1994                         */
  7. /*                                                                  */
  8. /*  US Government Users Restricted Rights -                         */
  9. /*  Use, duplication, or disclosure restricted                      */
  10. /*  by GSA ADP Schedule Contract with IBM Corp.                     */
  11. /*                                                                  */
  12. /*  File:    AltPoly.cpp                                            */
  13. /*                                                                  */
  14. /*  Contains:  OpenDoc polygon: optional C++ savvy classes          */
  15. /*                                                                  */
  16. /*  To Do:                                                          */
  17. /*                                                                  */
  18. /*    Improve the equality tests for contours and polygons. See     */
  19. /*    comments in the two "operator==" methods for details.         */
  20. /*                                                                  */
  21. /********************************************************************/
  22.  
  23. #ifndef _ALTPOINT_
  24. #include "AltPoint.h"
  25. #endif
  26.  
  27. #ifndef _ALTPOLY_
  28. #include "AltPoly.h"
  29. #endif
  30.  
  31. #ifndef _EXCEPT_
  32. #include "Except.h"
  33. #endif
  34.  
  35. #ifndef _ODDEBUG_
  36. #include "ODDebug.h"
  37. #endif
  38.  
  39. #ifndef _LINEOPS_
  40. #include "LineOps.h"
  41. #endif
  42.  
  43. #define INCL_GPI
  44. #define INCL_ODAPI
  45. #define INCL_ODSTORAGEUNIT
  46. #define INCL_ODTRANSFORM
  47. #include <os2.h>
  48.  
  49. #ifndef _STDTYPIO_
  50. #include <StdTypIO.h>
  51. #endif
  52.  
  53. #include <stddef.h>          // Defines offsetof() macro
  54.  
  55.  
  56. const ODSLong kMaxLong  = 0x7FFFFFFF;
  57.  
  58. // private error code:
  59. const ODError kODErrShapeTooComplex        = 125;    // Polygon shape is too huge for QuickDraw
  60. // This error code was not used anywhere else, and since
  61. // AltPoly is private utility, its error codes should not be public
  62.  
  63. //==============================================================================
  64. // Destructos
  65. //==============================================================================
  66.  
  67.  
  68. ODTempPolygonPtr::ODTempPolygonPtr( )
  69.   :fPoly(kODNULL)
  70. {
  71. }
  72.  
  73.  
  74. ODTempPolygonPtr::ODTempPolygonPtr( ODPolygon *p )
  75.   :fPoly(p)
  76. {
  77. }
  78.  
  79.  
  80. ODTempPolygonPtr::~ODTempPolygonPtr( )
  81. {
  82.   delete fPoly;
  83.   fPoly = kODNULL;
  84. }
  85.  
  86. /******************************************************************************/
  87. //**  ALLOCATION
  88. /******************************************************************************/
  89.  
  90.  
  91. ODPolygon::ODPolygon( )
  92.   :_maximum(0),
  93.    _length(0),
  94.    _buf(kODNULL)
  95. {
  96. }
  97.  
  98. void
  99. ODPolygon::Delete( )
  100. {
  101.   SOMFree(_buf);
  102.   delete this;
  103. }
  104.  
  105.  
  106. void
  107. ODPolygon::Clear( )
  108. {
  109.   SOMFree(_buf);
  110.   _buf = kODNULL;
  111.   _length = _maximum = 0;
  112. }
  113.  
  114.  
  115. static ODULong
  116. CalcDataSize( ODSLong nVertices )
  117. {
  118.   if( nVertices==0 )
  119.     return 0;
  120.   else
  121.     return sizeof(ODPolygonData)+(nVertices-1)*sizeof(ODPoint);
  122. //  return offsetof(ODPolygonData,firstContour.vertex[nVertices]);
  123. }
  124.  
  125.  
  126. void
  127. ODPolygon::Realloc( ODULong dataSize )
  128. {
  129.   if( _buf!=kODNULL && dataSize>=_length && dataSize<=_maximum )
  130.     _length = dataSize;
  131.   else {
  132.     ODPtr newData;
  133.     if( dataSize!=0 )
  134.       newData = (ODPtr)SOMMalloc(dataSize);
  135.     SOMFree(_buf);
  136.     if( dataSize!=0 )
  137.       _buf = (ODPolygonData*)newData;
  138.     else
  139.       _buf = kODNULL;
  140.     _length = _maximum = dataSize;
  141.   }
  142. }
  143.  
  144.  
  145. void
  146. ODPolygon::SetData( const ODPolygonData *data )
  147. {
  148.   _length = sizeof(ODULong) * (1+data->nContours);
  149.   const ODContour *c = &data->firstContour;
  150.   for( ODULong i=data->nContours; i!=0; i-- ) {
  151.     _length += c->nVertices * sizeof(ODPoint);
  152.     c = c->NextContour();
  153.   }
  154.  
  155.   SOMFree(_buf);
  156.   _buf = (ODPolygonData*)data;
  157.   _maximum = _length;
  158. }
  159.  
  160.  
  161. ODPolygon*
  162. ODPolygon::SetNVertices( ODSLong nVertices )
  163. {
  164.   ASSERT(nVertices>=0,kODErrInvalidValue);
  165.  
  166.   this->Realloc(CalcDataSize(nVertices));
  167.   if( nVertices>0 ) {
  168.     _buf->nContours = 1;
  169.     _buf->firstContour.nVertices = nVertices;
  170.   }
  171.   return this;
  172. }
  173.  
  174.  
  175. ODPolygon*
  176. ODPolygon::SetVertices( ODSLong nVertices, const ODPoint *vertices )
  177. {
  178.   ASSERT(nVertices>=0,kODErrInvalidValue);
  179.   ASSERT(vertices!=kODNULL,kODErrInvalidValue);
  180.  
  181.   this->SetNVertices(nVertices);
  182.   if( nVertices>0 )
  183.     memcpy( _buf->firstContour.vertex, (void *) vertices, nVertices*sizeof(ODPoint) );
  184.   return this;
  185. }
  186.  
  187.  
  188. ODPolygon*
  189. ODPolygon::SetContours( ODSLong nContours, const ODSLong *contourVertices )
  190. {
  191.   ASSERT(nContours>=0,kODErrInvalidValue);
  192.   if( nContours==0 )
  193.     return this->SetNVertices(0);
  194.   else {
  195.     ASSERT(contourVertices!=kODNULL,kODErrInvalidValue);
  196.     ODULong totalVertices = 0;
  197.     ODSLong i;
  198.     for( i=nContours-1; i>=0; i-- )
  199.       totalVertices += contourVertices[i];
  200.     this->Realloc( offsetof(ODPolygonData,firstContour)
  201.            + offsetof(ODContour,vertex[0]) * nContours
  202.            + sizeof(ODPoint)*totalVertices );
  203.     _buf->nContours = nContours;
  204.     ODContour *cont = this->FirstContour();
  205.     for( i=0; i<nContours; i++ ) {
  206.       cont->nVertices = contourVertices[i];
  207.       cont = cont->NextContour();
  208.     }
  209.     return this;
  210.   }
  211. }
  212.  
  213.  
  214. ODPolygon*
  215. ODPolygon::SetRect( const ODRect &r )
  216. {
  217.   if( r.IsEmpty() )
  218.     return this->SetNVertices(0);
  219.   else {
  220.     this->SetNVertices(4);
  221.     _buf->firstContour.vertex[0] = r.BotLeft();
  222.     _buf->firstContour.vertex[1].Set(r.left,r.top);
  223.     _buf->firstContour.vertex[2] = r.TopRight();
  224.     _buf->firstContour.vertex[3].Set(r.right,r.bottom);
  225.   }
  226.   return this;
  227. }
  228.  
  229.  
  230. ODPolygon*
  231. ODPolygon::CopyFrom( const ODPolygon &poly )
  232. {
  233.   if( poly._buf != _buf ) {
  234.     ODULong size = poly.GetDataSize();
  235.     this->Realloc(size);
  236.     memcpy(_buf,poly.GetData(),size);         
  237.   }
  238.   return this;
  239. }
  240.  
  241.  
  242. ODPolygon*
  243. ODPolygon::MoveFrom( ODPolygon &poly )
  244. {
  245.   if( poly._buf != _buf ) {
  246.     SOMFree(_buf);
  247.     _buf = poly._buf;
  248.     _length = poly._length;
  249.     _maximum = poly._maximum;
  250.   }
  251.   if( &poly._buf != &_buf ) {      // Don't clear poly if it's myself!
  252.     poly._buf = kODNULL;
  253.     poly._length = poly._maximum = 0;
  254.   }
  255.   return this;
  256. }
  257.  
  258. ODPolygon*
  259. ODPolygon::ReadFrom( Environment *ev, ODStorageUnit *su )
  260. {
  261.  
  262.   if( !su->Exists(ev,kODNULL,kODPolygon,kODPosUndefined) ) {
  263.     this->Clear();
  264.   } else {
  265.     ODPropertyName propName = su->GetProperty(ev);
  266.     ODGetPolygonProp(ev, su, propName, kODPolygon, this);
  267.     SOMFree(propName);
  268.   }
  269.  
  270.   return this;
  271. }
  272.  
  273.  
  274. ODPolygon*
  275. ODPolygon::WriteTo( Environment *ev, ODStorageUnit *su )  const
  276. {
  277.   ODPropertyName propName = su->GetProperty(ev);
  278.   ODSetPolygonProp(ev, su, propName, kODPolygon, this);
  279.   SOMFree(propName);
  280.  
  281.   return (ODPolygon*)this;
  282. }
  283.  
  284.  
  285. /******************************************************************************/
  286. //**  POLYGON STUFF
  287. /******************************************************************************/
  288.  
  289.  
  290. ODSLong
  291. ODPolygon::GetNContours( )  const
  292. {
  293.   return _length>0 ?_buf->nContours :0;
  294. }
  295.  
  296.  
  297. const ODContour*
  298. ODPolygon::FirstContour( )  const
  299. {
  300.   return _length>0 ?&_buf->firstContour :kODNULL;
  301. }
  302.  
  303.  
  304. ODContour*
  305. ODPolygon::FirstContour( )
  306. {
  307.   return _length>0 ?&_buf->firstContour :kODNULL;
  308. }
  309.  
  310.  
  311. ODPolygon*
  312. ODPolygon::Copy( ) const
  313. {
  314.   ODTempPolygonPtr poly = new ODPolygon;
  315.     poly->CopyFrom(*this);
  316.   return poly.DontDelete();
  317. }
  318.  
  319.  
  320. void
  321. ODPolygon::ComputeBoundingBox( ODRect *bbox ) const
  322. {
  323.   ASSERT(bbox!=kODNULL,kODErrInvalidValue);
  324.  
  325.   if( _buf==kODNULL || _buf->nContours <= 0 ) {
  326.     bbox->Clear();
  327.     return;
  328.   }
  329.  
  330.   // Start bbox out as maximally empty:
  331.   bbox->left  = bbox->bottom  =  kMaxLong;
  332.   bbox->right = bbox->top     = -kMaxLong;
  333.  
  334.   const ODContour *c = this->FirstContour();
  335.   for( ODSLong i=this->GetNContours(); i>0; i-- ) {
  336.     ODPoint *pt = (ODPoint*)c->vertex;
  337.     for( ODSLong v=c->nVertices; v>0; v--,pt++ ) {
  338.       if( pt->x < bbox->left )  bbox->left  = pt->x;
  339.       if( pt->x > bbox->right )  bbox->right  = pt->x;
  340.       if( pt->y > bbox->top )    bbox->top  = pt->y;
  341.       if( pt->y < bbox->bottom )  bbox->bottom= pt->y;
  342.     }
  343.     if( i>1 )
  344.       c = c->NextContour();
  345.   }
  346. }
  347.  
  348.  
  349. ODBoolean
  350. ODContour::operator== ( const ODContour &cont ) const
  351. {
  352.   /*  This test is complicated by the fact that the two contours might have the
  353.     same points, but out of phase. Therefore we have to compare the points
  354.     in sequence, once per possible phase difference.  */
  355.  
  356.   ODSLong nv = this->nVertices;
  357.   if( nv != cont.nVertices )
  358.     return kODFalse;
  359.  
  360.   for( ODSLong phase=0; phase<nv; phase++ ) {
  361.     const ODPoint *p0 = &this->vertex[0];
  362.     const ODPoint *p1 = &cont.vertex[phase];
  363.     ODSLong i;
  364.     for( i=nVertices; i>0; i-- ) {
  365.       if( i==phase )
  366.         p1 = &cont.vertex[0];
  367.       if( ! (p0++)->ApproxEquals(*p1++) )    // Coords may differ very slightly
  368.         break;
  369.     }
  370.     if( i==0 )
  371.       return kODTrue;
  372.   }
  373.   return kODFalse;
  374. }
  375.  
  376.  
  377. ODBoolean
  378. ODPolygon::operator== ( ODPolygon &poly ) const
  379. {
  380.   /*  This test is complicated by the fact that the two polygons may not have their
  381.     contours in the same order. Our approach is to step through my contours in
  382.     order, trying to match each to a unique contour in the target. To ensure
  383.     uniqueness, the sign of the nVertices field in a target contour is flipped
  384.     after it's matched.  */
  385.  
  386.   if( this->GetNContours() != poly.GetNContours() )
  387.     return kODFalse;
  388.   if( &poly == this )
  389.     return kODTrue;
  390.  
  391.   ODBoolean result = kODTrue;
  392.   const ODContour * c = this->FirstContour();
  393.   ODContour *pc;
  394.  
  395.   ODSLong i;
  396.   for( i=this->GetNContours(); i>0; i-- ) {
  397.     pc = poly.FirstContour();
  398.     ODSLong j;
  399.     for( j=poly.GetNContours(); j>0; j-- ) {
  400.       if( pc->nVertices>0 && *c==*pc ) {    // Compare contours!
  401.         pc->nVertices = -pc->nVertices;    // Use sign bit as a flag (yech)
  402.         break;
  403.       }
  404.       if( j>1 )
  405.         pc = pc->NextContour();
  406.     }
  407.     if( j<=0 ) {
  408.       result = kODFalse;          // No match for contour
  409.       break;
  410.     }
  411.  
  412.     if( i>1 )
  413.       c = c->NextContour();
  414.   }
  415.  
  416.   // Now that we know, clear all the sign bits:
  417.   pc = poly.FirstContour();
  418.   for( i=poly.GetNContours(); i>0; i-- ) {
  419.     if( pc->nVertices<0 )
  420.       pc->nVertices = -pc->nVertices;
  421.     pc = pc->NextContour();
  422.   }
  423.   return result;
  424. }
  425.  
  426.  
  427. ODBoolean
  428. ODPolygon::IsEmpty( ) const
  429. {
  430.   // FIX: This is not very smart. It will probably be necessary to compute the area
  431.   // of each contour...
  432.  
  433.   return _buf==kODNULL || _buf->nContours==0;
  434. }
  435.  
  436.  
  437. ODBoolean
  438. ODPolygon::IsRectangular( ) const
  439. {
  440.   return _buf==kODNULL || _buf->nContours==0 ||
  441.       (_buf->nContours==1  && _buf->firstContour.IsRectangular());
  442. }
  443.  
  444.  
  445. ODBoolean
  446. ODPolygon::AsRectangle( ODRect *r ) const
  447. {
  448.   if( _buf==kODNULL || _buf->nContours==0 ) {
  449.     r->Clear();
  450.     return kODTrue;
  451.   } else if( _buf->nContours==1 )
  452.     return _buf->firstContour.AsRectangle(r);
  453.   else
  454.     return kODFalse;
  455. }
  456.  
  457.  
  458. ODBoolean
  459. ODContour::IsRectangular( ) const
  460. {
  461.   if( nVertices != 4 )
  462.     return kODFalse;
  463.   else if( vertex[0].x == vertex[1].x )      // 1st edge is vertical
  464.     return vertex[1].y==vertex[2].y
  465.         && vertex[2].x==vertex[3].x
  466.         && vertex[3].y==vertex[0].y;
  467.   else if( vertex[0].y == vertex[1].y )      // 1st edge is horizontal
  468.     return vertex[1].x==vertex[2].x
  469.         && vertex[2].y==vertex[3].y
  470.         && vertex[3].x==vertex[0].x;
  471.   else
  472.     return kODFalse;
  473. }
  474.  
  475.  
  476. ODBoolean
  477. ODContour::AsRectangle( ODRect *r ) const
  478. {
  479.   ASSERT(r!=kODNULL,kODErrInvalidValue);
  480.  
  481.   if( this->IsRectangular() ) {
  482.     ODRect r2(vertex[0],vertex[2]);      // C'tor properly orders the coords
  483.     *r = r2;
  484.     return kODTrue;
  485.   } else
  486.     return kODFalse;
  487. }
  488.  
  489.  
  490. /******************************************************************************/
  491. //**  REGION CONVERSION
  492. /******************************************************************************/
  493.  
  494.  
  495. ODBoolean
  496. ODContour::HasExactRegion( ) const
  497. {
  498.   const ODPoint *b = &vertex[0];
  499.   for( ODSLong i=nVertices; i>=0; i-- ) {
  500.     const ODPoint *a = &vertex[i];
  501.     if( (a->x & 0xFFFF) || (a->y & 0xFFFF) )
  502.       return kODFalse;            // Non-integer coordinates
  503.     if( a->x!=b->x && a->y!=b->y )
  504.       return kODFalse;            // Diagonal line
  505.     b = a;
  506.   }
  507.   return kODTrue;
  508. }
  509.  
  510.  
  511. ODBoolean
  512. ODPolygon::HasExactRegion( ) const
  513. {
  514.   const ODContour *c = this->FirstContour();
  515.   for( long i=this->GetNContours(); i>0; i-- ) {
  516.     if( !c->HasExactRegion() )
  517.       return kODFalse;
  518.     if( i>1 )
  519.       c = c->NextContour();
  520.   }
  521.   return kODTrue;
  522. }
  523.  
  524.  
  525. void ODContour::AsPOLYGON( POLYGON& p) const
  526. {
  527.   p.aPointl = (PPOINTL)SOMMalloc(nVertices * sizeof(POINTL));
  528.  
  529.   const ODPoint *src = &vertex[0];
  530.   POINTL *dst = p.aPointl;
  531.   for( ODSLong i=nVertices; i>0; i-- ) {
  532.     *dst = (src++)->AsPOINTL();
  533.     (dst++)->y -= 1;              // OS/2 polygons are bottom-right exclusive
  534.                                   // while OpenDoc polygons are top-right
  535.                                   // exclusive.
  536.   }
  537.   p.ulPoints = nVertices;
  538. }
  539.  
  540.  
  541. // The canvas (HPS) is assumed to be associated with the target device
  542. // for the which the region will be used, and already set up with the
  543. // desired transforms.
  544.  
  545. HRGN
  546. ODPolygon::AsRegion(HPS hps) const
  547. {
  548.   ODRgnHandle rgn;
  549.   POLYGON poly;
  550.  
  551.   if( !this->HasData() )
  552.     return GpiCreateRegion(hps, 0, 0);
  553.  
  554.   GpiSavePS(hps);
  555.   GpiBeginPath(hps, 1);
  556.   TRY {
  557.     const ODContour *cont = this->FirstContour();
  558.     for( ODSLong i=_buf->nContours; i>0; i-- ) {
  559.       POLYGON poly;
  560.       cont->AsPOLYGON( poly );
  561.       GpiSetCurrentPosition(hps, &poly.aPointl[poly.ulPoints-1]);
  562.       GpiPolyLine(hps, poly.ulPoints, poly.aPointl);
  563.       delete poly.aPointl;
  564.       cont = cont->NextContour();
  565.     }
  566.   }CATCH_ALL{
  567.     delete[] poly.aPointl;
  568.     GpiEndPath(hps);
  569.     GpiRestorePS(hps, -1);
  570.     RERAISE;
  571.   }ENDTRY
  572.  
  573.   GpiEndPath(hps);
  574.   rgn = GpiPathToRegion(hps, 1, FPATH_WINDING | FPATH_EXCL);
  575.   GpiRestorePS(hps, -1);
  576.   THROW_IF_NULL(rgn);
  577.   return rgn;
  578. }
  579.  
  580. void
  581. ODPolygon::Transform( Environment *ev, ODTransform *xform )
  582. {
  583.   if( this->HasData() ) {
  584.     ODContour *c = &_buf->firstContour;
  585.     for( ODSLong i=_buf->nContours; i>0; i-- ) {
  586.       ODByteArray verticies;
  587.       UseByteArray(&verticies, c->vertex, c->nVertices * sizeof(ODPoint));
  588.         xform->TransformPoints(ev,&verticies, c->nVertices);
  589.       if( i>1 )
  590.         c = c->NextContour();
  591.     }
  592.   }
  593. }
  594.  
  595.  
  596. /******************************************************************************/
  597. //**  CONTAINMENT TEST
  598. /******************************************************************************/
  599.  
  600.  
  601. //------------------------------------------------------------------------------
  602. // ODPolygon::Contains
  603. //
  604. // Does a polygon contain a point?
  605. // We determine this by drawing a ray from the point to the right towards
  606. // infinity, and finding the polygon edges that intersect this ray. For each
  607. // such edge, count it as 1 if its y value is increasing, -1 if decreasing.
  608. // The sum of these values is 0 if the point is outside the polygon.
  609. //------------------------------------------------------------------------------
  610.  
  611. ODSLong
  612. ODPolygon::Contains( ODPoint point ) const
  613. {
  614.   if( !this->HasData() )
  615.     return kODFalse;
  616.  
  617.   ODSLong count = 0;
  618.   const ODPoint *pp1, *pp2;
  619.   ODPoint p1, p2;
  620.   ODPoint ray = point;
  621.  
  622.   for( PolyEdgeIterator polyIter (this); polyIter.IsNotComplete(); polyIter.Next() ) {
  623.     polyIter.CurrentEdge(pp1,pp2);
  624.     p1 = *pp1;
  625.     p2 = *pp2;
  626.  
  627.     if( p1.y==p2.y ) {                      // Horizontal line: ignore
  628.       if( p1.y==point.y && InRange(point.x, p1.x,p2.x) ) {  // unless point is on it
  629.         return 0;
  630.       }
  631.     } else {
  632.       ray.x = Max(p1.x,p2.x);
  633.       ODPoint sect;
  634.       if( ray.x >= point.x )
  635.         if( IntersectSegments(p1,p2, point,ray, §) ) {
  636.           if( WithinEpsilon(point.x,sect.x) && WithinEpsilon(point.y,sect.y) ) {
  637.             return 0;
  638.           }
  639.           if( p2.y > p1.y )
  640.             count++;
  641.           else
  642.             count--;
  643.         }
  644.     }
  645.   }
  646.  
  647.   return count;
  648. }
  649.  
  650.  
  651. /******************************************************************************/
  652. //**  POLYGON EDGE ITERATOR
  653. /******************************************************************************/
  654.  
  655.  
  656. PolyEdgeIterator::PolyEdgeIterator( const ODPolygon *poly )
  657.   :fPoly (poly)
  658. {
  659.   fCurContour = poly->FirstContour();
  660.   fCurContourIndex = 0;
  661.   fCurVertex = 0;
  662. }
  663.  
  664.  
  665. void
  666. PolyEdgeIterator::CurrentEdge( const ODPoint* &v1, const ODPoint* &v2 )
  667. {
  668.   v1 = &fCurContour->vertex[fCurVertex];
  669.   if( fCurVertex+1 < fCurContour->nVertices )
  670.     v2 = v1+1;
  671.   else
  672.     v2 = &fCurContour->vertex[0];
  673. }
  674.  
  675.  
  676. ODBoolean
  677. PolyEdgeIterator::Next( )
  678. {
  679.   if( !fCurContour )                  // Was already finished
  680.     return kODFalse;
  681.   if( ++fCurVertex >= fCurContour->nVertices )    // Next vertex; if past contour:
  682.     if( ++fCurContourIndex >= fPoly->GetNContours() ) {    // Next contour; if past end:
  683.       fCurContour = kODNULL;
  684.       return kODFalse;                //...we're done.
  685.     } else {
  686.       fCurContour = fCurContour->NextContour();      // Else go to start of contour
  687.       fCurVertex = 0;
  688.     }
  689.   return kODTrue;
  690. }
  691.  
  692.  
  693. ODBoolean
  694. PolyEdgeIterator::IsNotComplete( )
  695. {
  696.   return fCurContour!=kODNULL;
  697. }
  698.