1 /* $NoKeywords: $ */
2 /*
3 //
4 // Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved.
5 // OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert
6 // McNeel & Associates.
7 //
8 // THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
9 // ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF
10 // MERCHANTABILITY ARE HEREBY DISCLAIMED.
11 //
12 // For complete openNURBS copyright information see <http://www.opennurbs.org>.
13 //
14 ////////////////////////////////////////////////////////////////
15 */
16 
17 #include "pcl/surface/3rdparty/opennurbs/opennurbs.h"
18 
19 //////////////////////////////////////////////////////////////////////////////
20 
ON_TextLog()21 ON_TextLog::ON_TextLog() : m_pFile(0), m_pString(0), m_indent(""), m_beginning_of_line(1), m_indent_size(0)
22 {
23   SetFloatFormat("%g");
24   SetDoubleFormat("%.17g");
25 }
26 
ON_TextLog(FILE * pFile)27 ON_TextLog::ON_TextLog( FILE* pFile ) : m_pFile(pFile), m_pString(0), m_indent(""), m_beginning_of_line(1), m_indent_size(0)
28 {
29   SetFloatFormat("%g");
30   SetDoubleFormat("%.17g");
31 }
32 
ON_TextLog(ON_wString & wstr)33 ON_TextLog::ON_TextLog( ON_wString& wstr ) : m_pFile(0), m_pString(&wstr), m_indent(""), m_beginning_of_line(1), m_indent_size(0)
34 {
35   SetFloatFormat("%g");
36   SetDoubleFormat("%.17g");
37 }
38 
~ON_TextLog()39 ON_TextLog::~ON_TextLog()
40 {
41 }
42 
SetDoubleFormat(const char * sFormat)43 void ON_TextLog::SetDoubleFormat(const char* sFormat)
44 {
45   m_double_format = sFormat;
46   m_double2_format = m_double_format + ", " + m_double_format;
47   m_double3_format = m_double2_format + ", " + m_double_format;
48   m_double4_format = m_double3_format + ", " + m_double_format;
49 }
50 
GetDoubleFormat(ON_String & s) const51 void ON_TextLog::GetDoubleFormat( ON_String& s ) const
52 {
53   s = m_double_format;
54 }
55 
SetFloatFormat(const char * sFormat)56 void ON_TextLog::SetFloatFormat(const char* sFormat)
57 {
58   m_float_format = sFormat;
59   m_float2_format = m_float_format + ", " + m_float_format;
60   m_float3_format = m_float2_format + ", " + m_float_format;
61   m_float4_format = m_float3_format + ", " + m_float_format;
62 }
63 
GetFloatFormat(ON_String & s) const64 void ON_TextLog::GetFloatFormat( ON_String& s ) const
65 {
66   s = m_float_format;
67 }
68 
PushIndent()69 void ON_TextLog::PushIndent()
70 {
71   if ( m_indent_size > 0 ) {
72     int i;
73     for ( i = 0; i < m_indent_size; i++ ) {
74       m_indent += ' ';
75     }
76   }
77   else {
78     m_indent += "\t";
79   }
80 }
81 
PopIndent()82 void ON_TextLog::PopIndent()
83 {
84   const int length = m_indent.Length();
85   const int indent_lenth = m_indent_size>0 ? m_indent_size : 1;
86   if ( length >= indent_lenth ) {
87     m_indent.SetLength(length-indent_lenth);
88   }
89   else {
90     m_indent.Destroy();
91   }
92 }
93 
IndentSize() const94 int ON_TextLog::IndentSize() const
95 {
96   //  0: one tab per indent
97   // >0: number of spaces per indent
98   return m_indent_size;
99 }
100 
SetIndentSize(int s)101 void ON_TextLog::SetIndentSize(int s)
102 {
103   m_indent_size = (s>0) ? s : 0;
104 }
105 
Print(const char * format,...)106 void ON_TextLog::Print( const char* format, ... )
107 {
108   // format message and append it to the log
109   const int MAX_MSG_LENGTH = 2047;
110   char s[MAX_MSG_LENGTH+1];
111   va_list args;
112 
113   s[0] = 0;
114   if (format)
115   {
116     va_start(args, format);
117     on_vsnprintf( s, MAX_MSG_LENGTH-1, format, args);
118     va_end(args);
119     s[MAX_MSG_LENGTH] = 0;
120   }
121   if ( *s )
122   {
123     char* s0 = s;
124     char* s1 = s;
125     for ( s1 = s0; *s1; s1++) {
126       if ( *s1 == '\n' ) {
127         *s1 = 0;
128         if ( m_beginning_of_line && m_indent && m_indent[0] )
129           AppendText( m_indent );
130         if (*s0)
131           AppendText(s0);
132         AppendText("\n");
133         m_beginning_of_line = 1;
134         s0 = s1+1;
135       }
136     }
137     if (*s0) {
138       if ( m_beginning_of_line && m_indent && m_indent[0] )
139         AppendText( m_indent );
140       AppendText(s0);
141       m_beginning_of_line = 0;
142     }
143   }
144 }
145 
Print(const wchar_t * wformat,...)146 void ON_TextLog::Print( const wchar_t* wformat, ... )
147 {
148   // format message and append it to the log
149   const int MAX_MSG_LENGTH = 2047;
150   wchar_t s[MAX_MSG_LENGTH+1];
151   va_list args;
152 
153   s[0] = 0;
154   if (wformat)
155   {
156     va_start(args, wformat);
157     on_vsnwprintf( s, MAX_MSG_LENGTH-1, wformat, args);
158     va_end(args);
159     s[MAX_MSG_LENGTH] = 0;
160   }
161   if ( *s )
162   {
163     wchar_t* s0 = s;
164     wchar_t* s1 = s;
165     for ( s1 = s0; *s1; s1++) {
166       if ( *s1 == '\n' ) {
167         *s1 = 0;
168         if ( m_beginning_of_line && m_indent && m_indent[0] )
169           AppendText( m_indent );
170         if (*s0)
171           AppendText(s0);
172         AppendText("\n");
173         m_beginning_of_line = 1;
174         s0 = s1+1;
175       }
176     }
177     if (*s0) {
178       if ( m_beginning_of_line && m_indent && m_indent[0] )
179         AppendText( m_indent );
180       AppendText(s0);
181       m_beginning_of_line = 0;
182     }
183   }
184 }
185 
186 
AppendText(const char * s)187 void ON_TextLog::AppendText( const char* s )
188 {
189   // This is a virtual function
190   if ( s && *s )
191   {
192     if ( m_pString )
193     {
194       (*m_pString) += s;
195     }
196     else if ( m_pFile )
197     {
198       fputs( s, m_pFile );
199     }
200     else
201     {
202       printf("%s",s);
203     }
204   }
205 }
206 
AppendText(const wchar_t * s)207 void ON_TextLog::AppendText( const wchar_t* s )
208 {
209   // This is a virtual function
210   if ( m_pString )
211   {
212     (*m_pString) += s;
213   }
214   else
215   {
216     // If sizeof(wchar_t) = 2, str = s performs
217     // performs UTF-16 to UTF-8 conversion.
218     // If sizeof(wchar_t) = 4, str = s performs
219     // performs UTF-32 to UTF-8 conversion.
220     ON_String str = s;
221     AppendText(str.Array());
222   }
223 }
224 
Print(float x)225 void ON_TextLog::Print( float x )
226 {
227   if ( ON_UNSET_FLOAT == x )
228     Print("ON_UNSET_FLOAT");
229   else
230     Print(m_float_format,x);
231 }
232 
Print(double x)233 void ON_TextLog::Print( double x )
234 {
235   if ( ON_UNSET_VALUE == x )
236     Print("ON_UNSET_VALUE");
237   else
238     Print(m_double_format,x);
239 }
240 
Print(const ON_2dPoint & p)241 void ON_TextLog::Print( const ON_2dPoint& p )
242 {
243   Print("(");
244   Print(m_double2_format, p.x, p.y);
245   Print(")");
246 }
247 
Print(const ON_3dPoint & p)248 void ON_TextLog::Print( const ON_3dPoint& p )
249 {
250   Print("(");
251   if ( ON_3dPoint::UnsetPoint == p )
252     Print("UnsetPoint");
253   else
254     Print(m_double3_format, p.x, p.y, p.z );
255   Print(")");
256 }
257 
Print(const ON_4dPoint & p)258 void ON_TextLog::Print( const ON_4dPoint& p )
259 {
260   Print("[");
261   Print(m_double4_format, p.x, p.y, p.z, p.w );
262   Print("]");
263 }
264 
Print(const ON_2dVector & p)265 void ON_TextLog::Print( const ON_2dVector& p )
266 {
267   Print("<");
268   Print(m_double2_format, p.x, p.y);
269   Print(">");
270 }
271 
Print(const ON_3dVector & p)272 void ON_TextLog::Print( const ON_3dVector& p )
273 {
274   Print("<");
275   if ( ON_3dVector::UnsetVector == p )
276     Print("UnsetVector");
277   else
278     Print(m_double3_format, p.x, p.y, p.z);
279   Print(">");
280 }
281 
Print(const ON_Xform & xform)282 void ON_TextLog::Print( const ON_Xform& xform )
283 {
284   if ( xform.IsIdentity() )
285   {
286     Print("identity transformation\n");
287   }
288   else if ( xform.IsZero() )
289   {
290     Print("zero transformation\n");
291   }
292   else
293   {
294     Print(m_double4_format,xform[0][0],xform[0][1],xform[0][2],xform[0][3]);
295     Print("\n");
296     Print(m_double4_format,xform[1][0],xform[1][1],xform[1][2],xform[1][3]);
297     Print("\n");
298     Print(m_double4_format,xform[2][0],xform[2][1],xform[2][2],xform[2][3]);
299     Print("\n");
300     Print(m_double4_format,xform[3][0],xform[3][1],xform[3][2],xform[3][3]);
301     Print("\n");
302   }
303 }
304 
Print(const ON_UUID & uuid)305 void ON_TextLog::Print( const ON_UUID& uuid )
306 {
307   Print("%08X-%04X-%04x-%02X%02X-%02X%02X%02X%02X%02X%02X",
308         uuid.Data1, uuid.Data2, uuid.Data3,
309         uuid.Data4[0], uuid.Data4[1], uuid.Data4[2], uuid.Data4[3],
310         uuid.Data4[4], uuid.Data4[5], uuid.Data4[6], uuid.Data4[7]
311         );
312 }
313 
Print(const ON_COMPONENT_INDEX & ci)314 void ON_TextLog::Print( const ON_COMPONENT_INDEX& ci )
315 {
316   switch( ci.m_type )
317   {
318     case ON_COMPONENT_INDEX::invalid_type:
319       Print("invalid_type(%d)",ci.m_index);
320       break;
321     case ON_COMPONENT_INDEX::brep_vertex:
322       Print("brep_vertex(%d)",ci.m_index);
323       break;
324     case ON_COMPONENT_INDEX::brep_edge:
325       Print("brep_edge(%d)",ci.m_index);
326       break;
327     case ON_COMPONENT_INDEX::brep_face:
328       Print("brep_face(%d)",ci.m_index);
329       break;
330     case ON_COMPONENT_INDEX::brep_trim:
331       Print("brep_trim(%d)",ci.m_index);
332       break;
333     case ON_COMPONENT_INDEX::brep_loop:
334       Print("brep_loop(%d)",ci.m_index);
335       break;
336     case ON_COMPONENT_INDEX::mesh_vertex:
337       Print("mesh_vertex(%d)",ci.m_index);
338       break;
339     case ON_COMPONENT_INDEX::meshtop_vertex:
340       Print("meshtop_vertex(%d)",ci.m_index);
341       break;
342     case ON_COMPONENT_INDEX::meshtop_edge:
343       Print("meshtop_edge(%d)",ci.m_index);
344       break;
345     case ON_COMPONENT_INDEX::mesh_face:
346       Print("mesh_face(%d)",ci.m_index);
347       break;
348     case ON_COMPONENT_INDEX::idef_part:
349       Print("idef_part(%d)",ci.m_index);
350       break;
351     case ON_COMPONENT_INDEX::polycurve_segment:
352       Print("polycurve_segment(%d)",ci.m_index);
353       break;
354     case ON_COMPONENT_INDEX::pointcloud_point:
355       Print("pointcloud_point(%d)",ci.m_index);
356       break;
357     case ON_COMPONENT_INDEX::group_member:
358       Print("group_member(%d)",ci.m_index);
359       break;
360     case ON_COMPONENT_INDEX::no_type:
361       Print("no_type(%d)",ci.m_index);
362       break;
363     default:
364       Print("ON_COMPONENT_INDEX(%d,%d)",ci.m_type,ci.m_index);
365       break;
366   }
367 }
368 
Print(const ON_wString & string)369 void ON_TextLog::Print( const ON_wString& string )
370 {
371   const wchar_t* s = string;
372   if ( s && *s )
373     AppendText(s);
374 }
375 
Print(const ON_String & string)376 void ON_TextLog::Print( const ON_String& string )
377 {
378   const char* s = string;
379   if ( s && *s )
380     AppendText(s);
381 }
382 
PrintString(const char * s)383 void ON_TextLog::PrintString( const char* s )
384 {
385   if ( s && *s )
386     AppendText(s);
387 }
388 
PrintNewLine()389 void ON_TextLog::PrintNewLine()
390 {
391   Print("\n");
392 }
393 
394 
PrintString(const wchar_t * s)395 void ON_TextLog::PrintString( const wchar_t* s )
396 {
397   if ( s && *s )
398     AppendText(s);
399 }
400 
PrintRGB(const ON_Color & color)401 void ON_TextLog::PrintRGB( const ON_Color& color )
402 {
403   if ( color == ON_UNSET_COLOR )
404     Print("ON_UNSET_COLOR");
405   else
406     Print("%d %d %d",color.Red(),color.Green(),color.Blue());
407 }
408 
PrintTime(const struct tm & t)409 void ON_TextLog::PrintTime( const struct tm& t )
410 {
411   if (   0 != t.tm_sec
412       || 0 != t.tm_min
413       || 0 != t.tm_hour
414       || 0 != t.tm_mday
415       || 0 != t.tm_mon
416       || 0 != t.tm_year
417       || 0 != t.tm_wday
418     )
419   {
420     const char* sDayName[8] = {"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","<invalid day>"};
421     const char* sMonName[13] = {"January","February","March","April","May","June",
422                                "July","August","September","October","November","December","<invalid month>"};
423     int wday = t.tm_wday;
424     if ( wday < 0 || wday > 6 )
425       wday = 7;
426     int mon = t.tm_mon;
427     if ( mon < 0 || mon > 11 )
428       mon = 12;
429 
430     Print("%s %s %02d %02d:%02d:%02d %4d",
431                 sDayName[wday],
432                 sMonName[mon],
433                 t.tm_mday,
434                 t.tm_hour,
435                 t.tm_min,
436                 t.tm_sec,
437                 t.tm_year+1900);
438   }
439 }
440 
441 
PrintPointList(int dim,int is_rat,int count,int stride,const double * P,const char * sPreamble)442 void ON_TextLog::PrintPointList( int dim, int is_rat, int count, int stride, const double* P,
443                                 const char* sPreamble )
444 {
445   double w, x;
446   int i, j, cvdim;
447 
448   ON_String preamble = "";
449   if ( sPreamble && *sPreamble )
450     preamble += sPreamble;
451   cvdim = (is_rat) ? dim+1 : dim;
452 
453   if ( count == 0 ) {
454     Print( "%sEMPTY point list\n", preamble.Array() );
455   }
456   else if ( !P ) {
457     Print( "%sNULL point list\n", preamble.Array() );
458   }
459 
460   for ( i = 0; i < count; i++ ) {
461     Print( "%s[%2d] %c", preamble.Array(), i, (is_rat) ? '[' : '(' );
462     Print( m_double_format, P[0] );
463     for ( j = 1; j < cvdim; j++ ) {
464       Print( ", ");
465       Print(m_double_format, P[j] );
466     }
467     Print("%c", (is_rat) ? ']' : ')' );
468     if ( is_rat )
469     {
470       w = P[dim];
471       if ( w != 0.0 )
472       {
473         // print euclidean coordinates
474         w = 1.0/w;
475         x = w*P[0];
476         Print( " = (");
477         Print( m_double_format, x );
478         for ( j = 1; j < dim; j++ )
479         {
480           x = w*P[j];
481           Print( ", ");
482           Print( m_double_format, x );
483         }
484         Print(")");
485       }
486     }
487     Print("\n");
488     P += stride;
489   }
490 }
491 
PrintPointGrid(int dim,int is_rat,int point_count0,int point_count1,int point_stride0,int point_stride1,const double * P,const char * sPreamble)492 void ON_TextLog::PrintPointGrid( int dim, int is_rat,
493                                 int point_count0, int point_count1,
494                                 int point_stride0, int point_stride1,
495                                 const double* P,
496                                 const char* sPreamble )
497 {
498   char s[1024];
499   int i;
500   if (!sPreamble || !sPreamble[0])
501     sPreamble = "point";
502   for ( i = 0; i < point_count0; i++ ) {
503     sprintf( s,  "%s[%2d]", sPreamble, i );
504     PrintPointList( dim, is_rat, point_count1, point_stride1, P + i*point_stride0, s );
505   }
506 }
507 
PrintKnotVector(int order,int cv_count,const double * knot)508 void ON_TextLog::PrintKnotVector( int order, int cv_count, const double* knot )
509 {
510   int i, i0, mult, knot_count;
511   if ( !knot )
512     Print("NULL knot vector\n");
513   if ( order < 2 )
514     Print("knot vector order < 2\n");
515   if ( cv_count < order )
516     Print("knot vector cv_count < order\n");
517   if ( order >= 2 && cv_count >= order && knot ) {
518     knot_count = ON_KnotCount( order, cv_count );
519     i = i0 = 0;
520     Print("index                     value  mult       delta\n");
521     while ( i < knot_count ) {
522       mult = 1;
523       while ( i+mult < knot_count && knot[i] == knot[i+mult] )
524         mult++;
525       if ( i == 0 ) {
526         Print( "%5d  %23.17g  %4d\n", i, knot[i], mult );
527       }
528       else {
529         Print( "%5d  %23.17g  %4d  %10.4g\n", i, knot[i], mult, knot[i]-knot[i0] );
530       }
531       i0 = i;
532       i += mult;
533     }
534   }
535 }
536 
Print(const ON_3dPointArray & a,const char * sPreamble)537 void ON_TextLog::Print( const ON_3dPointArray& a, const char* sPreamble )
538 {
539   const double* p = (a.Array() ? &a.Array()[0].x : NULL );
540   PrintPointList( 3, false, a.Count(), 3, p, sPreamble );
541 }
542 
Print(const ON_Matrix & M,const char * sPreamble,int precision)543 void ON_TextLog::Print( const ON_Matrix& M, const char* sPreamble, int precision )
544 {
545   double x;
546   char digit[10] = {'0','1','2','3','4','5','6','7','8','9'};
547   char* sRow;
548   char* sIJ;
549   int xi, row_count, column_count, row_index, column_index;
550 
551   row_count = M.RowCount();
552   column_count = M.ColCount();
553 
554   sRow = (char*)alloca( (5*column_count + 2 + 64)*sizeof(*sRow) );
555 
556   if ( !sPreamble )
557     sPreamble = "Matrix";
558 
559   Print("%s (%d rows %d columns)\n",sPreamble,row_count,column_count);
560   for ( row_index = 0; row_index < row_count; row_index++ ) {
561     sIJ = sRow;
562     Print("%5d:",row_index);
563     if ( precision > 3 ) {
564       for ( column_index = 0; column_index < column_count; column_index++ ) {
565         x = M.m[row_index][column_index];
566         Print( " %8f",x);
567       }
568       Print("\n");
569     }
570     else {
571       for ( column_index = 0; column_index < column_count; column_index++ ) {
572         x = M.m[row_index][column_index];
573         if ( x == 0.0 ) {
574           strcpy( sIJ, "  0   " );
575           sIJ += 4;
576         }
577         else {
578           *sIJ++ = ' ';
579           *sIJ++ = ( x >0.0 ) ? '+' : '-';
580           x = fabs( x );
581           if      ( x >= 10.0 ) {
582             *sIJ++ = '*';
583             *sIJ++ = ' ';
584             *sIJ++ = ' ';
585           }
586           else if ( x <= ON_SQRT_EPSILON) {
587             *sIJ++ = '0';
588             *sIJ++ = ' ';
589             *sIJ++ = ' ';
590           }
591           else if ( x < 0.1) {
592             *sIJ++ = '~';
593             *sIJ++ = ' ';
594             *sIJ++ = ' ';
595           }
596           else if ( x < .95 ) {
597             *sIJ++ = '.';
598             xi = (int)floor(x*10.0);
599             if ( xi > 9 )
600               xi = 9;
601             else if (xi < 1)
602               xi = 1;
603             *sIJ++ = digit[xi];
604             *sIJ++ = '~';
605           }
606           else {
607             xi = (int)floor(x);
608             if ( xi < 1 )
609               xi = 1;
610             else if (xi > 9)
611               xi = 9;
612             *sIJ++ = digit[xi];
613             if ( x == floor(x) ) {
614               *sIJ++ = ' ';
615               *sIJ++ = ' ';
616             }
617             else {
618               *sIJ++ = '.';
619               *sIJ++ = '~';
620             }
621           }
622         }
623       }
624       *sIJ = 0;
625       Print("%s\n",sRow);
626     }
627   }
628 }
629 
operator <<(const char * s)630 ON_TextLog& ON_TextLog::operator<<(const char* s)
631 {
632   Print( "%s", s );
633   return *this;
634 }
635 
operator <<(char c)636 ON_TextLog& ON_TextLog::operator<<(char c)
637 {
638   Print( "%c", c );
639   return *this;
640 }
641 
operator <<(short i)642 ON_TextLog& ON_TextLog::operator<<(short i)
643 {
644   int ii = (int)i;
645   Print("%d", ii );
646   return *this;
647 }
648 
operator <<(int i)649 ON_TextLog& ON_TextLog::operator<<(int i)
650 {
651   Print("%d",i);
652   return *this;
653 }
654 
operator <<(float x)655 ON_TextLog& ON_TextLog::operator<<(float x)
656 {
657   Print(m_float_format,x);
658   return *this;
659 }
660 
operator <<(double x)661 ON_TextLog& ON_TextLog::operator<<(double x)
662 {
663   Print(m_double_format,x);
664   return *this;
665 }
666 
operator <<(const ON_2dPoint & p)667 ON_TextLog& ON_TextLog::operator<<( const ON_2dPoint& p )
668 {
669   Print(p);
670   return *this;
671 }
672 
operator <<(const ON_3dPoint & p)673 ON_TextLog& ON_TextLog::operator<<( const ON_3dPoint& p )
674 {
675   Print(p);
676   return *this;
677 }
678 
operator <<(const ON_4dPoint & p)679 ON_TextLog& ON_TextLog::operator<<( const ON_4dPoint& p )
680 {
681   Print(p);
682   return *this;
683 }
684 
operator <<(const ON_2dVector & p)685 ON_TextLog& ON_TextLog::operator<<( const ON_2dVector& p )
686 {
687   Print(p);
688   return *this;
689 }
690 
operator <<(const ON_3dVector & p)691 ON_TextLog& ON_TextLog::operator<<( const ON_3dVector& p )
692 {
693   Print(p);
694   return *this;
695 }
696 
operator <<(const ON_Xform & xform)697 ON_TextLog& ON_TextLog::operator<<( const ON_Xform& xform )
698 {
699   Print(xform);
700   return *this;
701 }
702 
PrintWrappedText(const char * s,int line_length)703 void ON_TextLog::PrintWrappedText( const char* s, int line_length )
704 {
705   ON_wString ws = s;
706   PrintWrappedText(ws,line_length);
707 }
708 
wsncpy(wchar_t * dst,const wchar_t * src,int n)709 static void wsncpy(wchar_t* dst, const wchar_t* src, int n)
710 {
711   // can't use _wcsncpy() because this has to compile on UNIX boxes
712   if ( dst && n > 0 ) {
713     if ( src ) {
714       while ( 0 != (*dst++ = *src++) && n-- > 0 );
715     }
716     else
717       *dst = 0;
718   }
719 }
720 
721 
PrintWrappedText(const wchar_t * s,int line_length)722 void ON_TextLog::PrintWrappedText( const wchar_t* s, int line_length )
723 {
724   ON_Workspace ws;
725   if ( s && *s && line_length > 0 ) {
726     const int max_line_length = line_length+255;
727     wchar_t* sLine = (wchar_t*)ws.GetMemory((max_line_length+1)*sizeof(*sLine));
728     const int wrap_length = line_length;
729     int i  = 0;
730     int i1 = 0;
731     int isp = 0;
732     ON_BOOL32 bPrintLine = false;
733     while ( s[i] ) {
734       i1 = i;
735       if ( s[i] == 10 || s[i] == 13 ) {
736         // hard break at CR or LF
737         i++;
738         if ( s[i] == 10 && s[i-1] == 13 ) {
739           // it's a CR+LF hard end of line - skip LF too
740           i++;
741         }
742         bPrintLine = true;
743       }
744       else if ( i && s[i] == 32 ) {
745         if ( !isp ) {
746           isp = i++;
747         }
748         if ( i < wrap_length ) {
749           isp = i++;
750         }
751         else {
752           bPrintLine = true;
753           if ( isp ) {
754             i1 = i = isp;
755             while ( s[i] == 32 )
756               i++;
757           }
758           else {
759             i++;
760           }
761         }
762       }
763       else {
764         i++;
765       }
766       if ( bPrintLine ) {
767         if ( i1 >= max_line_length )
768           i1 = max_line_length-1;
769         if ( i1 > 0 ) {
770           wsncpy( sLine, s, i1 );
771           sLine[i1] = 0;
772           Print( "%ls\n", sLine );
773         }
774         else {
775           Print("\n");
776         }
777 
778         s += i;
779         i = i1 = isp = 0;
780         bPrintLine = false;
781       }
782     }
783     if ( s[0] ) {
784       Print( "%ls", s );
785     }
786   }
787 }
788 
789