0025279: OCCT fails to read VRML file created by OCCT
[occt.git] / src / VrmlData / VrmlData_Scene.cxx
1 // Created on: 2006-05-25
2 // Created by: Alexander GRIGORIEV
3 // Copyright (c) 2006-2014 OPEN CASCADE SAS
4 //
5 // This file is part of Open CASCADE Technology software library.
6 //
7 // This library is free software; you can redistribute it and/or modify it under
8 // the terms of the GNU Lesser General Public License version 2.1 as published
9 // by the Free Software Foundation, with special exception defined in the file
10 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
11 // distribution for complete text of the license and disclaimer of any warranty.
12 //
13 // Alternatively, this file may be used under the terms of Open CASCADE
14 // commercial license or contractual agreement.
15
16 #include <VrmlData_Scene.hxx>
17 #include <VrmlData_InBuffer.hxx>
18 #include <VrmlData_Appearance.hxx>
19 #include <VrmlData_Box.hxx>
20 #include <VrmlData_Color.hxx>
21 #include <VrmlData_Cone.hxx>
22 #include <VrmlData_Coordinate.hxx>
23 #include <VrmlData_Cylinder.hxx>
24 #include <VrmlData_DataMapOfShapeAppearance.hxx>
25 #include <VrmlData_Group.hxx>
26 #include <VrmlData_ImageTexture.hxx>
27 #include <VrmlData_InBuffer.hxx>
28 #include <VrmlData_IndexedFaceSet.hxx>
29 #include <VrmlData_IndexedLineSet.hxx>
30 #include <VrmlData_Material.hxx>
31 #include <VrmlData_Normal.hxx>
32 #include <VrmlData_Scene.hxx>
33 #include <VrmlData_ShapeNode.hxx>
34 #include <VrmlData_Sphere.hxx>
35 #include <VrmlData_TextureCoordinate.hxx>
36 #include <VrmlData_UnknownNode.hxx>
37 //#include <VrmlData_WorldInfo.hxx>
38 #include <NCollection_Vector.hxx>
39 #include <TopoDS_TFace.hxx>
40 #include <TopoDS.hxx>
41 #include <TopoDS_Face.hxx>
42 #include <TopExp_Explorer.hxx>
43 #include <BRep_Builder.hxx>
44 #include <Precision.hxx>
45 #include <Standard_Version.hxx>
46
47 #ifdef WNT
48 #define _CRT_SECURE_NO_DEPRECATE
49 #pragma warning (disable:4996)
50 #endif
51
52 static void     dumpNode        (Standard_OStream&              theStream,
53                                  const Handle(VrmlData_Node)&   theNode,
54                                  const TCollection_AsciiString& theIndent);
55
56 static void     dumpNodeHeader  (Standard_OStream&              theStream,
57                                  const TCollection_AsciiString& theIndent,
58                                  const char *                   theType,
59                                  const char *                   theName);
60
61 //=======================================================================
62 //function : VrmlData_Scene
63 //purpose  : Constructor
64 //=======================================================================
65
66 VrmlData_Scene::VrmlData_Scene
67         (const Handle(NCollection_IncAllocator)& theAlloc)
68   : myLinearScale     (1.),
69     myStatus          (VrmlData_StatusOK),
70     myAllocator       (theAlloc.IsNull() ?
71                        new NCollection_IncAllocator : theAlloc.operator->()),
72     myLineError       (0),
73     myOutput          (0L),
74     myIndent          (2),
75     myCurrentIndent   (0),
76     myAutoNameCounter (0)
77 {
78   myWorldInfo = new VrmlData_WorldInfo (* this);
79   Standard_CString anInfo = "Generated by Open CASCADE Technology " OCC_VERSION_STRING;
80   myWorldInfo->AddInfo (anInfo);
81   myLstNodes.Append (myWorldInfo);
82   myAllNodes.Append (myWorldInfo);
83 }
84
85 //=======================================================================
86 //function : AddNode
87 //purpose  : 
88 //=======================================================================
89
90 const Handle(VrmlData_Node)& VrmlData_Scene::AddNode
91                                 (const Handle(VrmlData_Node)& theN,
92                                  const Standard_Boolean       isTopLevel)
93 {
94   if (theN.IsNull() == Standard_False)
95     if (theN->IsKind (STANDARD_TYPE(VrmlData_WorldInfo)) == Standard_False) {
96       myMutex.Lock();
97       const Handle(VrmlData_Node)& aNode =
98         myAllNodes.Append ((&theN->Scene()== this) ? theN : theN->Clone (NULL));
99       // Name is checked for uniqueness. If not, letter 'D' is appended until
100       // the name proves to be unique.
101       if (aNode->Name()[0] != '\0')
102         while (myNamedNodes.Add (aNode) == Standard_False)
103           aNode->setName (aNode->Name(), "D");
104       if (isTopLevel)
105         myLstNodes.Append (aNode);
106       myMutex.Unlock();
107       return aNode;
108     }
109   static Handle(VrmlData_Node) aNullNode;
110   aNullNode.Nullify();
111   return aNullNode;
112 }
113
114 //=======================================================================
115 //function : operator <<
116 //purpose  : Export to text stream (file or else)
117 //=======================================================================
118
119 Standard_OStream& operator << (Standard_OStream&     theOutput,
120                                const VrmlData_Scene& theScene)
121 {
122   VrmlData_Scene& aScene = const_cast <VrmlData_Scene&> (theScene);
123   aScene.myMutex.Lock();
124   aScene.myCurrentIndent = 0;
125   aScene.myLineError = 0;
126   aScene.myOutput = 0L;
127   aScene.myNamedNodesOut.Clear();
128   aScene.myUnnamedNodesOut.Clear();
129   aScene.myAutoNameCounter = 0;
130
131   // Dummy write
132
133   VrmlData_Scene::Iterator anIterD(aScene.myLstNodes);
134   for (; anIterD.More(); anIterD.Next()) {
135     const Handle(VrmlData_Node)& aNode = anIterD.Value();
136     if (aNode.IsNull() == Standard_False) {
137       const VrmlData_ErrorStatus aStatus = aScene.WriteNode (0L, aNode);
138       if (aStatus != VrmlData_StatusOK &&
139           aStatus != VrmlData_NotImplemented)
140         break;
141     }
142   }
143
144   aScene.myOutput = &theOutput;
145   aScene.myNamedNodesOut.Clear();
146   theOutput << "#VRML V2.0 utf8" << endl << endl;
147
148   // Real write
149
150   VrmlData_Scene::Iterator anIter(aScene.myLstNodes);
151   for (; anIter.More(); anIter.Next()) {
152     const Handle(VrmlData_Node)& aNode = anIter.Value();
153     if (aNode.IsNull() == Standard_False) {
154       const VrmlData_ErrorStatus aStatus = aScene.WriteNode (0L, aNode);
155       if (aStatus != VrmlData_StatusOK &&
156           aStatus != VrmlData_NotImplemented)
157         break;
158     }
159   }
160   aScene.myOutput = 0L;
161   aScene.myNamedNodesOut.Clear();
162   aScene.myUnnamedNodesOut.Clear();
163   aScene.myMutex.Unlock();
164   return theOutput;
165 }
166
167 //=======================================================================
168 //function : SetVrmlDir
169 //purpose  : 
170 //=======================================================================
171
172 void VrmlData_Scene::SetVrmlDir (const TCollection_ExtendedString& theDir)
173 {
174   TCollection_ExtendedString& aDir = myVrmlDir.Append (theDir);
175   const Standard_ExtCharacter aTerminator = aDir.Value(aDir.Length());
176   if (aTerminator != Standard_ExtCharacter('\\') &&
177       aTerminator != Standard_ExtCharacter('/'))
178 #ifdef WNT
179     aDir += TCollection_ExtendedString ("\\");
180 #else
181     aDir += TCollection_ExtendedString ("/");
182 #endif
183 }
184
185 //=======================================================================
186 //function : WorldInfo
187 //purpose  : 
188 //=======================================================================
189
190 const Handle(VrmlData_WorldInfo)& VrmlData_Scene::WorldInfo() const
191 {
192   return myWorldInfo;
193 }
194
195 //=======================================================================
196 //function : readLine
197 //purpose  : 
198 //=======================================================================
199
200 VrmlData_ErrorStatus VrmlData_Scene::readLine (VrmlData_InBuffer& theBuffer)
201 {
202   VrmlData_ErrorStatus aStatus = VrmlData_StatusOK;
203   if (theBuffer.Input.eof())
204     aStatus = VrmlData_EndOfFile;
205   else {
206     theBuffer.Input.getline (theBuffer.Line, sizeof(theBuffer.Line));
207     theBuffer.LineCount++;
208     const int stat = theBuffer.Input.rdstate();
209     if (stat & ios::badbit) {
210       aStatus = VrmlData_UnrecoverableError;
211     }
212     else if (stat & ios::failbit) {
213       if (stat & ios::eofbit) {
214         aStatus = VrmlData_EndOfFile;
215       }
216       else {
217         aStatus = VrmlData_GeneralError;
218       }
219     }
220     theBuffer.LinePtr = &theBuffer.Line[0];
221     theBuffer.IsProcessed = Standard_False;
222   }
223   return aStatus;
224 }
225
226 //=======================================================================
227 //function : ReadLine
228 //purpose  : 
229 //=======================================================================
230
231 VrmlData_ErrorStatus VrmlData_Scene::ReadLine (VrmlData_InBuffer& theBuffer)
232 {
233   VrmlData_ErrorStatus aStatus (VrmlData_StatusOK); 
234
235   while (aStatus == VrmlData_StatusOK) {
236     // Find the first significant character of the line
237     for (; * theBuffer.LinePtr != '\0'; theBuffer.LinePtr++) {
238       if (* theBuffer.LinePtr != ' ' && * theBuffer.LinePtr != '\t'
239           && * theBuffer.LinePtr != ',')
240       {
241         if (* theBuffer.LinePtr == '\n' || * theBuffer.LinePtr == '\r' ||
242             * theBuffer.LinePtr == '#')
243           // go requesting the next line
244           break;
245         goto nonempty_line;
246       }
247     }
248     // the line is empty here (no significant characters). Read the next one.
249     aStatus = readLine (theBuffer);
250   }
251
252   // error or EOF detected
253   return aStatus;
254
255  nonempty_line:
256   // Try to detect comment
257   if (theBuffer.IsProcessed == Standard_False) {
258     Standard_Boolean isQuoted (Standard_False);
259     Standard_Integer anOffset (0);
260     char * ptr = theBuffer.LinePtr;
261     for (; * ptr != '\0'; ptr++) {
262       if (anOffset)
263         * ptr = ptr[anOffset];
264       if (* ptr == '\n' || * ptr == '\r' || * ptr == '#') {
265         if (isQuoted == Standard_False) {
266           * ptr = '\0';
267           break;
268         }
269       } else if (* ptr == '\\' && isQuoted)
270         ptr[0] = ptr[++anOffset];
271       else if (* ptr == '\"')
272         isQuoted = !isQuoted;
273     }
274     theBuffer.IsProcessed = Standard_True;
275   }
276   return aStatus;
277 }
278
279 //=======================================================================
280 //function : readHeader
281 //purpose  : 
282 //=======================================================================
283
284 VrmlData_ErrorStatus VrmlData_Scene::readHeader (VrmlData_InBuffer& theBuffer)
285 {
286   VrmlData_ErrorStatus aStat = readLine (theBuffer);
287   if (aStat == VrmlData_StatusOK &&
288       !VRMLDATA_LCOMPARE(theBuffer.LinePtr, "#VRML V2.0"))
289     aStat = VrmlData_NotVrmlFile;
290   else 
291     aStat = readLine(theBuffer);
292   return aStat;
293 }
294
295 //=======================================================================
296 //function : operator <<
297 //purpose  : Import from text stream (file or else)
298 //=======================================================================
299
300 VrmlData_Scene& VrmlData_Scene::operator << (Standard_IStream& theInput)
301 {
302   VrmlData_InBuffer aBuffer (theInput);
303   myMutex.Lock();
304   // Read the VRML header
305   myStatus = readHeader (aBuffer);
306   const Handle(VrmlData_UnknownNode) aNullNode= new VrmlData_UnknownNode(*this);
307 //   if (myStatus == StatusOK)
308 //     myStatus = ReadLine (aBuffer);
309   // Read VRML data by nodes
310   for(;;) {
311     if (!VrmlData_Node::OK(myStatus, ReadLine(aBuffer))) {
312       if (myStatus == VrmlData_EndOfFile)
313         myStatus = VrmlData_StatusOK;
314       break;
315     }
316     // this line provides the method ReadNode in the present context
317     Handle(VrmlData_Node) aNode;
318     myStatus = aNullNode->ReadNode (aBuffer, aNode);
319     // Unknown nodes are not stored however they do not generate error
320     if (myStatus != VrmlData_StatusOK)
321       break;
322     if (aNode.IsNull() == Standard_False /*&&
323         !aNode->IsKind (STANDARD_TYPE(VrmlData_UnknownNode))*/)
324     {
325       if (aNode->IsKind (STANDARD_TYPE(VrmlData_WorldInfo)) == Standard_False)
326         myLstNodes.Append (aNode);
327       else if (aNode->IsDefault() == Standard_False) {
328         const Handle(VrmlData_WorldInfo) aInfo =
329           Handle(VrmlData_WorldInfo)::DownCast (aNode);
330         myWorldInfo->SetTitle (aInfo->Title());
331         NCollection_List <const char *>::Iterator anIterInfo =
332           aInfo->InfoIterator();
333         for (; anIterInfo.More(); anIterInfo.Next())
334           myWorldInfo->AddInfo (anIterInfo.Value());
335       }
336     }
337   }
338   if (myStatus != VrmlData_StatusOK)
339     myLineError = aBuffer.LineCount;
340   myMutex.Unlock();
341   return * this;
342 }
343
344 //=======================================================================
345 //function : FindNode
346 //purpose  : 
347 //=======================================================================
348
349 Handle(VrmlData_Node) VrmlData_Scene::FindNode
350                                 (const char                   * theName,
351                                  const Handle(Standard_Type)& /*theType*/) const
352 {
353   Handle(VrmlData_Node) aResult;
354 #ifdef USE_LIST_API
355   Iterator anIter (myAllNodes);
356   for (; anIter.More(); anIter.Next())
357     if (!strcmp (anIter.Value()->Name(), theName)) {
358       aResult = anIter.Value();
359       if (theType.IsNull())
360         break;
361       if (aResult->IsKind(theType))
362         break;
363       aResult.Nullify();
364     }
365 #else
366   const Handle(VrmlData_UnknownNode) aDummyNode = new VrmlData_UnknownNode;
367   aDummyNode->myName = theName;
368   if (myNamedNodes.Contains (aDummyNode))
369     aResult = const_cast<VrmlData_MapOfNode&>(myNamedNodes).Added(aDummyNode);
370 #endif
371   return aResult;
372 }
373
374 //=======================================================================
375 //function : FindNode
376 //purpose  : 
377 //=======================================================================
378
379 Handle(VrmlData_Node) VrmlData_Scene::FindNode
380                                         (const char *   theName,
381                                          gp_Trsf&       theLocation) const
382 {
383   gp_Trsf aLoc;
384   Handle(VrmlData_Node) aResult;
385   Iterator anIter (myLstNodes);
386   for (; anIter.More(); anIter.Next()) {
387     const Handle(VrmlData_Node)& aNode = anIter.Value();
388     if (aNode.IsNull())
389       continue;
390     // Match a top-level node name
391     if (strcmp(aNode->Name(), theName) == 0) {
392       aResult = aNode;
393       theLocation = aLoc;
394       break;
395     }
396     // Try a Group type of node
397     if (aNode->IsKind(STANDARD_TYPE(VrmlData_Group)))
398     {
399       const Handle(VrmlData_Group) aGroup =
400         Handle(VrmlData_Group)::DownCast (aNode);
401       if (aGroup.IsNull() == Standard_False) {
402         aResult = aGroup->FindNode(theName, theLocation);
403         if (aResult.IsNull() == Standard_False)
404           break;
405       }
406     }
407   }
408   return aResult;
409 }
410
411 //=======================================================================
412 //function : ReadWord
413 //purpose  : 
414 //=======================================================================
415
416 VrmlData_ErrorStatus VrmlData_Scene::ReadWord
417                                       (VrmlData_InBuffer&           theBuffer,
418                                        TCollection_AsciiString&     theWord)
419 {
420   VrmlData_ErrorStatus aStatus = ReadLine(theBuffer);
421   if (aStatus == VrmlData_StatusOK) {
422     char * ptr = theBuffer.LinePtr;
423     while (* ptr != '\0' && * ptr != '\n' && * ptr != '\r' &&
424            * ptr != ' '  && * ptr != '\t' && * ptr != '{' && * ptr != '}' &&
425            * ptr != ','  && * ptr != '['  && * ptr != ']')
426       ptr++;
427     const Standard_Integer aLen = Standard_Integer(ptr - theBuffer.LinePtr);
428     if (aLen <= 0)
429       aStatus = VrmlData_StringInputError;
430     else {
431       theWord = TCollection_AsciiString ((Standard_CString)theBuffer.LinePtr,
432                                          aLen);
433       theBuffer.LinePtr = ptr;
434     }
435   }
436   return aStatus;
437 }
438
439 //=======================================================================
440 //function : createNode
441 //purpose  : 
442 //=======================================================================
443
444 VrmlData_ErrorStatus VrmlData_Scene::createNode
445                                       (VrmlData_InBuffer&           theBuffer,
446                                        Handle(VrmlData_Node)&       theNode,
447                                        const Handle(Standard_Type)& theType)
448 {
449   VrmlData_ErrorStatus    aStatus;
450   Handle(VrmlData_Node)   aNode;
451   TCollection_AsciiString aName;
452
453   // Read the DEF token to assign the node name
454   if (VrmlData_Node::OK(aStatus, ReadLine(theBuffer))) {
455     if (VRMLDATA_LCOMPARE(theBuffer.LinePtr, "DEF")) {
456       if (VrmlData_Node::OK(aStatus, ReadWord (theBuffer, aName)))
457         aStatus = ReadLine(theBuffer);
458     } else if (VRMLDATA_LCOMPARE(theBuffer.LinePtr, "NULL")) {
459       theNode.Nullify();
460       return aStatus;
461     }
462   }
463
464   const char * strName = aName.ToCString();
465   if (aStatus == VrmlData_StatusOK) {
466     // create the new node
467     if (VRMLDATA_LCOMPARE(theBuffer.LinePtr, "Appearance"))
468       aNode = new VrmlData_Appearance     (* this, strName);
469     else if (VRMLDATA_LCOMPARE(theBuffer.LinePtr, "Shape"))
470       aNode = new VrmlData_ShapeNode      (* this, strName);
471     else if (VRMLDATA_LCOMPARE(theBuffer.LinePtr, "Box"))
472       aNode = new VrmlData_Box            (* this, strName);
473     else if (VRMLDATA_LCOMPARE(theBuffer.LinePtr, "Color"))
474       aNode = new VrmlData_Color          (* this, strName);
475     else if (VRMLDATA_LCOMPARE(theBuffer.LinePtr, "Cone"))
476       aNode = new VrmlData_Cone           (* this, strName);
477     else if (VRMLDATA_LCOMPARE(theBuffer.LinePtr, "Coordinate")) {
478       aNode = new VrmlData_Coordinate     (* this, strName);
479       
480       // Check for "Coordinate3"
481       if (VRMLDATA_LCOMPARE (theBuffer.LinePtr, "3"))
482         theBuffer.LinePtr++;
483     }
484     else if (VRMLDATA_LCOMPARE(theBuffer.LinePtr, "Cylinder"))
485       aNode = new VrmlData_Cylinder       (* this, strName);
486     else if (VRMLDATA_LCOMPARE(theBuffer.LinePtr, "Group"))
487       aNode = new VrmlData_Group          (* this, strName,
488                                            Standard_False);
489     else if (VRMLDATA_LCOMPARE(theBuffer.LinePtr, "Transform"))
490       aNode = new VrmlData_Group          (* this, strName,
491                                            Standard_True);
492     else if (VRMLDATA_LCOMPARE(theBuffer.LinePtr, "Inline"))
493       aNode = new VrmlData_Group          (* this, strName,
494                                            Standard_False);
495     else if (VRMLDATA_LCOMPARE(theBuffer.LinePtr, "Separator"))
496       aNode = new VrmlData_Group          (* this, strName,
497                                            Standard_False);
498     else if (VRMLDATA_LCOMPARE(theBuffer.LinePtr, "Switch"))
499       aNode = new VrmlData_Group          (* this, strName,
500                                            Standard_False);
501     else if (VRMLDATA_LCOMPARE(theBuffer.LinePtr, "ImageTexture"))
502       aNode = new VrmlData_ImageTexture   (* this, strName);
503     else if (VRMLDATA_LCOMPARE(theBuffer.LinePtr, "IndexedFaceSet"))
504       aNode = new VrmlData_IndexedFaceSet (* this, strName);
505     else if (VRMLDATA_LCOMPARE(theBuffer.LinePtr, "IndexedLineSet"))
506       aNode = new VrmlData_IndexedLineSet (* this, strName);
507     else if (VRMLDATA_LCOMPARE(theBuffer.LinePtr, "Material"))
508       aNode = new VrmlData_Material       (* this, strName);
509     else if (VRMLDATA_LCOMPARE(theBuffer.LinePtr, "Normal"))
510       aNode = new VrmlData_Normal         (* this, strName);
511     else if (VRMLDATA_LCOMPARE(theBuffer.LinePtr, "Sphere"))
512       aNode = new VrmlData_Sphere         (* this, strName);
513     else if (VRMLDATA_LCOMPARE(theBuffer.LinePtr, "TextureCoordinate"))
514       aNode = new VrmlData_TextureCoordinate(* this, strName);
515     else if (VRMLDATA_LCOMPARE(theBuffer.LinePtr, "WorldInfo"))
516       aNode = new VrmlData_WorldInfo      (* this, strName);
517     else {
518       void * isProto = VRMLDATA_LCOMPARE(theBuffer.LinePtr, "PROTO");
519       TCollection_AsciiString aTitle;
520       aStatus = ReadWord (theBuffer, aTitle);
521       if (isProto) {
522         aStatus = ReadLine(theBuffer);
523         if (aStatus == VrmlData_StatusOK) {
524           if (theBuffer.LinePtr[0] != '[')
525             aStatus = VrmlData_VrmlFormatError;
526           else {
527             theBuffer.LinePtr++;
528             Standard_Integer aLevelCounter(0);
529    // This loop searches for any opening bracket '['.
530    // Such bracket increments the level counter. A closing bracket decrements
531    // the counter. The loop terminates when the counter becomes negative.
532             while (aLevelCounter >= 0 &&
533                    (aStatus = ReadLine(theBuffer)) == VrmlData_StatusOK) {
534               int aChar;
535               while ((aChar = theBuffer.LinePtr[0]) != '\0') {
536                 theBuffer.LinePtr++;
537                 if        (aChar == '[') {
538                   aLevelCounter++;
539                   break;
540                 } else if (aChar == ']') {
541                   aLevelCounter--;
542                   break;
543                 }
544               }
545             }
546           }
547         }
548       }
549       if (aStatus == VrmlData_StatusOK)
550         aNode = new VrmlData_UnknownNode(* this,
551                                          strName,
552                                          aTitle.ToCString());
553     }
554   }
555   aStatus = ReadLine(theBuffer);
556   if (aNode.IsNull() == Standard_False) {
557     if (aNode->Name()[0] != '\0') 
558       myNamedNodes.Add (aNode);
559     if (theType.IsNull() == Standard_False)
560       if (aNode->IsKind(theType) == Standard_False)
561         aStatus = VrmlData_VrmlFormatError;
562   }
563   if (aStatus == VrmlData_StatusOK) {
564     if (theBuffer.LinePtr[0] == '{') {
565       theBuffer.LinePtr++;
566       theNode = aNode;
567       myAllNodes.Append(aNode);
568     } else {
569       aStatus = VrmlData_VrmlFormatError;
570     }
571   }
572   return aStatus;
573 }
574
575 //=======================================================================
576 //function : operator TopoDS_Shape
577 //purpose  : 
578 //=======================================================================
579
580 VrmlData_Scene::operator TopoDS_Shape () const
581 {
582   TopoDS_Shape aShape;
583   VrmlData_Scene::createShape (aShape, myLstNodes, 0L);
584   return aShape;
585 }
586
587 //=======================================================================
588 //function : GetShape
589 //purpose  : 
590 //=======================================================================
591
592 TopoDS_Shape VrmlData_Scene::GetShape (VrmlData_DataMapOfShapeAppearance& aMap)
593 {
594   TopoDS_Shape aShape;
595   VrmlData_Scene::createShape (aShape, myLstNodes, &aMap);
596   return aShape;
597 }
598
599 //=======================================================================
600 //function : createShape
601 //purpose  : 
602 //=======================================================================
603
604 void VrmlData_Scene::createShape
605                 (TopoDS_Shape&                      outShape,
606                  const VrmlData_ListOfNode&         lstNodes,
607                  VrmlData_DataMapOfShapeAppearance* pMapShapeApp)
608 {
609   TopoDS_Shape aSingleShape;  // used when there is a single ShapeNode
610   Standard_Boolean isSingleShape (Standard_True);
611   BRep_Builder aBuilder;
612   outShape.Nullify();
613   aBuilder.MakeCompound(TopoDS::Compound(outShape));
614   aSingleShape.Orientation(TopAbs_FORWARD);
615
616   Iterator anIter (lstNodes);
617   for (; anIter.More(); anIter.Next()) {
618     // Try a Shape type of node
619     const Handle(VrmlData_ShapeNode) aNodeShape =
620       Handle(VrmlData_ShapeNode)::DownCast (anIter.Value());
621     if (aNodeShape.IsNull() == Standard_False) {
622       const Handle(VrmlData_Geometry) aNodeGeom =
623         Handle(VrmlData_Geometry)::DownCast(aNodeShape->Geometry());
624       if (aNodeGeom.IsNull() == Standard_False) {
625         if (aSingleShape.IsNull() == Standard_False)
626           isSingleShape = Standard_False;
627         const Handle(TopoDS_TShape) aTShape = aNodeGeom->TShape();
628         aSingleShape.TShape(aTShape);
629         if (aSingleShape.IsNull() == Standard_False) {
630           aBuilder.Add (outShape, aSingleShape);
631           if (pMapShapeApp != 0L) {
632             const Handle(VrmlData_Appearance)& anAppearance =
633               aNodeShape->Appearance();
634             if (anAppearance.IsNull() == Standard_False) {
635               // Check if the current topology is a single face
636               if (aTShape->IsKind(STANDARD_TYPE(TopoDS_TFace)))
637                 pMapShapeApp->Bind(aTShape, anAppearance);
638               else {
639                 // This is not a face, explode it in faces and bind each face
640                 TopoDS_Shape aCurShape;
641                 aCurShape.TShape(aTShape);
642                 TopExp_Explorer anExp(aCurShape, TopAbs_FACE);
643                 for (; anExp.More(); anExp.Next()) {
644                   const TopoDS_Face& aFace = TopoDS::Face(anExp.Current());
645                   pMapShapeApp->Bind(aFace.TShape(), anAppearance);
646                 }
647               }
648             }
649           }
650         }
651       }
652       continue;
653     }
654     // Try a Group type of node
655     const Handle(VrmlData_Group) aNodeGroup =
656       Handle(VrmlData_Group)::DownCast (anIter.Value());
657     if (aNodeGroup.IsNull() == Standard_False) {
658       TopoDS_Shape aShape;
659       aNodeGroup->Shape(aShape, pMapShapeApp);
660       if (aShape.IsNull() == Standard_False) {
661         aBuilder.Add (outShape, aShape);
662         isSingleShape = Standard_False;
663       }
664     }
665   }
666   if (isSingleShape)
667     outShape = aSingleShape;
668 }
669
670 //=======================================================================
671 //function : ReadReal
672 //purpose  : 
673 //=======================================================================
674
675 VrmlData_ErrorStatus VrmlData_Scene::ReadReal
676                                 (VrmlData_InBuffer& theBuffer,
677                                  Standard_Real&     theResult,
678                                  Standard_Boolean   isScale,
679                                  Standard_Boolean   isOnlyPositive) const
680 {
681   Standard_Real aResult(0.);
682   VrmlData_ErrorStatus aStatus;
683   if (VrmlData_Node::OK(aStatus, VrmlData_Scene::ReadLine(theBuffer))) {
684     char * endptr;
685     aResult = Strtod (theBuffer.LinePtr, &endptr);
686     if (endptr == theBuffer.LinePtr)
687       aStatus = VrmlData_NumericInputError;
688     else if (isOnlyPositive && aResult < 0.001*Precision::Confusion())
689       aStatus = VrmlData_IrrelevantNumber;
690     else {
691       theResult = isScale ? (aResult * myLinearScale) : aResult;
692       theBuffer.LinePtr = endptr;
693     }
694   }
695   return aStatus;
696 }
697
698 //=======================================================================
699 //function : ReadXYZ
700 //purpose  : 
701 //=======================================================================
702
703 VrmlData_ErrorStatus VrmlData_Scene::ReadXYZ
704                                 (VrmlData_InBuffer&     theBuffer,
705                                  gp_XYZ&                theXYZ,
706                                  Standard_Boolean       isScale,
707                                  Standard_Boolean       isOnlyPos) const
708 {
709   Standard_Real aVal[3] = {0., 0., 0.};
710   VrmlData_ErrorStatus aStatus = VrmlData_StatusOK;
711   for (Standard_Integer i = 0; i < 3; i++) {
712     if (!VrmlData_Node::OK(aStatus, VrmlData_Scene::ReadLine(theBuffer)))
713       break;
714     char * endptr;
715     aVal[i] = Strtod (theBuffer.LinePtr, &endptr);
716     if (endptr == theBuffer.LinePtr) {
717       aStatus = VrmlData_NumericInputError;
718       break;
719     } else {
720       if (isOnlyPos && aVal[i] < 0.001*Precision::Confusion()) {
721         aStatus = VrmlData_IrrelevantNumber;
722         break;
723       }
724       theBuffer.LinePtr = endptr;
725     }
726   }
727   if (aStatus == VrmlData_StatusOK) {
728     if (isScale) {
729       theXYZ.SetCoord (aVal[0] * myLinearScale,
730                        aVal[1] * myLinearScale,
731                        aVal[2] * myLinearScale);
732     }
733     else {
734       theXYZ.SetCoord (aVal[0], aVal[1], aVal[2]);
735     }
736   }
737   return aStatus;
738 }
739
740 //=======================================================================
741 //function : ReadXY
742 //purpose  : 
743 //=======================================================================
744
745 VrmlData_ErrorStatus VrmlData_Scene::ReadXY
746                                 (VrmlData_InBuffer&     theBuffer,
747                                  gp_XY&                 theXY,
748                                  Standard_Boolean       isScale,
749                                  Standard_Boolean       isOnlyPos) const
750 {
751   Standard_Real aVal[2] = {0., 0.};
752   VrmlData_ErrorStatus aStatus = VrmlData_StatusOK;
753   for (Standard_Integer i = 0; i < 2; i++) {
754     if (!VrmlData_Node::OK(aStatus, VrmlData_Scene::ReadLine(theBuffer)))
755       break;
756     char * endptr;
757     aVal[i] = Strtod (theBuffer.LinePtr, &endptr);
758     if (endptr == theBuffer.LinePtr) {
759       aStatus = VrmlData_NumericInputError;
760       break;
761     } else {
762       if (isOnlyPos && aVal[i] < 0.001*Precision::Confusion()) {
763         aStatus = VrmlData_IrrelevantNumber;
764         break;
765       }
766       theBuffer.LinePtr = endptr;
767     }
768   }
769   if (aStatus == VrmlData_StatusOK) {
770     if (isScale)
771       theXY.SetCoord (aVal[0] * myLinearScale, aVal[1] * myLinearScale);
772     else
773       theXY.SetCoord (aVal[0], aVal[1]);
774   }
775   return aStatus;
776 }
777
778 //=======================================================================
779 //function : ReadArrIndex
780 //purpose  : Read the body of the data node (comma-separated list of int
781 //           multiplets)
782 //=======================================================================
783
784 VrmlData_ErrorStatus VrmlData_Scene::ReadArrIndex
785                                   (VrmlData_InBuffer&         theBuffer,
786                                    const Standard_Integer **& theArray,
787                                    Standard_Size&             theNBlocks) const
788 {
789   VrmlData_ErrorStatus aStatus;
790   theNBlocks = 0;
791   if (VrmlData_Node::OK(aStatus, ReadLine(theBuffer))) {
792     if (theBuffer.LinePtr[0] != '[')  // opening bracket
793       aStatus = VrmlData_VrmlFormatError;
794     else {
795       theBuffer.LinePtr++;
796       NCollection_Vector<const Standard_Integer *> vecIndice;
797       NCollection_Vector<Standard_Integer>         vecInt;
798       Standard_Boolean isMore (Standard_True);
799       long             anIntValue;
800
801       // Loop reading integers from the stream
802       while (isMore && VrmlData_Node::OK(aStatus, ReadLine(theBuffer)))
803       {
804         // closing bracket, in case that it follows a comma
805         if (theBuffer.LinePtr[0] == ']') {
806           theBuffer.LinePtr++;
807           break;
808         }
809         if (!VrmlData_Node::OK(aStatus, VrmlData_Node::ReadInteger(theBuffer,
810                                                                    anIntValue)))
811           break;
812         // Check for valid delimiter (']' or ',') 
813         if (!VrmlData_Node::OK(aStatus, ReadLine(theBuffer)))
814           break;
815         if (theBuffer.LinePtr[0] == ']') {
816           theBuffer.LinePtr++;
817           isMore = Standard_False;
818         }
819         if (anIntValue >= 0)
820           // The input value is a node index, store it in the buffer vector
821           vecInt.Append (static_cast<Standard_Integer> (anIntValue));
822         if ((anIntValue < 0 || isMore == Standard_False)
823             && vecInt.Length() > 0)
824         {
825           const Standard_Integer aLen = vecInt.Length();
826           // The input is the end-of-face, store and close this face
827           Standard_Integer * bufFace = static_cast <Standard_Integer *>
828             (myAllocator->Allocate((aLen+1) * sizeof(Standard_Integer)));
829           if (bufFace == 0L) {
830             aStatus = VrmlData_UnrecoverableError;
831             break;
832           }
833           bufFace[0] = aLen;
834           for (Standard_Integer i = 0; i < aLen; i++)
835             bufFace[i+1] = vecInt(i);
836           vecInt.Clear();
837           vecIndice.Append(bufFace);
838         }
839       }
840       if (aStatus == VrmlData_StatusOK) {
841         const Standard_Size aNbBlocks =
842           static_cast <Standard_Size> (vecIndice.Length());
843         if (aNbBlocks) {
844           const Standard_Integer ** anArray =
845             static_cast <const Standard_Integer **>
846             (myAllocator->Allocate (aNbBlocks * sizeof(Standard_Integer *)));
847           if (anArray == 0L)
848             aStatus = VrmlData_UnrecoverableError;
849           else {
850             for (size_t i = 0; i < aNbBlocks; i++)
851               anArray[i] = vecIndice((Standard_Integer)i);
852             theNBlocks = aNbBlocks;
853             theArray = anArray;
854           } 
855         }
856       }
857     }
858   }
859   return aStatus;
860 }
861
862 //=======================================================================
863 //function : writeArrIndex
864 //purpose  : 
865 //=======================================================================
866
867 VrmlData_ErrorStatus VrmlData_Scene::WriteArrIndex
868                                 (const char *              thePrefix,
869                                  const Standard_Integer ** theArrIndex,
870                                  const Standard_Size       theNbBlocks) const
871 {
872   VrmlData_ErrorStatus aStatus (VrmlData_StatusOK);
873   if (theNbBlocks && (IsDummyWrite() == Standard_False)) {
874     if (VrmlData_Node::OK (aStatus,
875                            WriteLine (thePrefix, "[", 1)))
876     {
877       const size_t aLineLimit = (myCurrentIndent < 41) ? 36 : 100;
878       char buf[256];
879       for (Standard_Size iBlock = 0; iBlock < theNbBlocks; iBlock++) {
880         const Standard_Integer nVal (* theArrIndex[iBlock]);
881         const Standard_Integer * arrVal = theArrIndex[iBlock]+1;
882         switch (nVal) {
883         case 1:
884           Sprintf (buf, "%d,", arrVal[0]);
885           break;
886         case 2:
887           Sprintf (buf, "%d,%d,", arrVal[0], arrVal[1]);
888           break;
889         case 3:
890           Sprintf (buf, "%d,%d,%d,", arrVal[0], arrVal[1], arrVal[2]);
891           break;
892         case 4:
893           Sprintf (buf, "%d,%d,%d,%d,",
894                    arrVal[0], arrVal[1], arrVal[2], arrVal[3]);
895           break;
896         default:
897           if (nVal > 0) {
898             char * ptr = &buf[0];
899             for (Standard_Integer i = 0; i < nVal; i++) {
900               Sprintf (ptr, "%d,", arrVal[i]);
901               ptr = strchr (ptr, ',') + 1;
902               if ((ptr - &buf[0]) > (ptrdiff_t)aLineLimit) {
903                 WriteLine(buf);
904                 ptr = &buf[0];
905               }
906             }
907           }
908         }
909         WriteLine (buf, iBlock < theNbBlocks-1 ? "-1," : "-1");
910       }
911       if (aStatus == VrmlData_StatusOK)
912         aStatus = WriteLine ("]", 0L, -1);
913     }
914   }
915   return aStatus;
916 }
917
918 //=======================================================================
919 //function : WriteXYZ
920 //purpose  : 
921 //=======================================================================
922
923 VrmlData_ErrorStatus VrmlData_Scene::WriteXYZ
924                                 (const gp_XYZ&          theXYZ,
925                                  const Standard_Boolean isApplyScale,
926                                  const char             * thePostfix) const
927 {
928   char buf[240];
929   if (IsDummyWrite() == Standard_False) {
930     if (isApplyScale && myLinearScale > Precision::Confusion())
931       Sprintf (buf, "%.12g %.12g %.12g%s", theXYZ.X() / myLinearScale,
932                theXYZ.Y() / myLinearScale, theXYZ.Z() / myLinearScale,
933                thePostfix ? thePostfix : "");
934     else
935       Sprintf (buf, "%.12g %.12g %.12g%s", theXYZ.X(), theXYZ.Y(), theXYZ.Z(),
936                thePostfix ? thePostfix : "");
937   }
938   return WriteLine (buf);
939 }
940
941 //=======================================================================
942 //function : WriteLine
943 //purpose  : write the given string prepending the current indentation
944 //=======================================================================
945
946 VrmlData_ErrorStatus VrmlData_Scene::WriteLine
947                                         (const char             * theLin0,
948                                          const char             * theLin1,
949                                          const Standard_Integer theIndent) const
950 {
951   static const char spaces[] = "                                        "
952                                "                                        ";
953   VrmlData_ErrorStatus& aStatus =
954     const_cast <VrmlData_ErrorStatus&> (myStatus);
955   if (IsDummyWrite())
956     aStatus = VrmlData_StatusOK;
957   else {
958     Standard_Integer& aCurrentIndent =
959       const_cast <Standard_Integer&> (myCurrentIndent);
960     if (theIndent < 0)
961       aCurrentIndent -= myIndent;
962     if (aCurrentIndent < 0)
963       aCurrentIndent = 0;
964     if (theLin0 == 0L && theLin1 == 0L)
965       (* myOutput) << endl;
966     else {
967       const Standard_Integer nSpaces = Min (aCurrentIndent, sizeof(spaces)-1);
968       (* myOutput) << &spaces[sizeof(spaces)-1 - nSpaces];
969       if (theLin0) {
970         (* myOutput) << theLin0;
971         if (theLin1)
972           (* myOutput) << ' ' << theLin1;
973       } else
974         (* myOutput) << theLin1;
975       (* myOutput) << endl;
976     }
977     const int stat = myOutput->rdstate();
978     if (stat & ios::badbit)
979       aStatus = VrmlData_UnrecoverableError;
980     else if (stat & ios::failbit)
981 //       if (stat & ios::eofbit)
982 //         aStatus = VrmlData_EndOfFile;
983 //       else
984       aStatus = VrmlData_GeneralError;
985     if (theIndent > 0)
986       aCurrentIndent += myIndent;
987   }
988   return myStatus;
989 }
990
991 //=======================================================================
992 //function : WriteNode
993 //purpose  : 
994 //=======================================================================
995
996 VrmlData_ErrorStatus VrmlData_Scene::WriteNode
997                                 (const char *                 thePrefix,
998                                  const Handle(VrmlData_Node)& theNode) const
999 {
1000   VrmlData_ErrorStatus aStatus (VrmlData_StatusOK);
1001   Standard_Boolean isNoName (Standard_False);
1002   if (theNode->Name() == 0L)
1003     isNoName = Standard_True;
1004   else if (theNode->Name()[0] == '\0')
1005     isNoName = Standard_True;
1006
1007   if (theNode.IsNull() == Standard_False)
1008     if (theNode->IsDefault() == Standard_False) {
1009       if (isNoName && IsDummyWrite()) {
1010         // We are in a tentative 'write' session (nothing is written).
1011         // The goal is to identify multiply referred nodes.
1012         Standard_Address addrNode = theNode.operator->();
1013         if (!const_cast<NCollection_Map<Standard_Address>&>(myUnnamedNodesOut)
1014             .Add (addrNode))
1015         {
1016           Handle(VrmlData_UnknownNode) bidNode = new VrmlData_UnknownNode;
1017           char buf[32];
1018           do {
1019             Sprintf (buf, "_%d",
1020                      ++const_cast<Standard_Integer&>(myAutoNameCounter));
1021             bidNode->myName = &buf[0];
1022           } while (myNamedNodes.Contains (bidNode));
1023           // We found the vacant automatic name, let us assign to it.
1024           theNode->setName (&buf[0]);
1025           const_cast<VrmlData_MapOfNode&>(myNamedNodes).Add (theNode);
1026           return aStatus; // do not search under already duplicated node
1027         }
1028       }
1029       if (isNoName)
1030         aStatus = theNode->Write (thePrefix);
1031       else {
1032         // If the node name consists of blank characters, we do not write it
1033         const char * nptr = theNode->Name();
1034         for (; * nptr != '\0'; nptr++)
1035           if (* nptr != ' ' && * nptr != '\t')
1036             break;
1037         if (* nptr == '\0')
1038           aStatus = theNode->Write (thePrefix);
1039         else {
1040           // Name is written under DEF clause
1041           TCollection_AsciiString buf;
1042           if (myNamedNodesOut.Contains (theNode))
1043           {
1044             buf += "USE ";
1045             buf += theNode->Name();
1046             aStatus = WriteLine (thePrefix, buf.ToCString());
1047           } 
1048           else 
1049           {
1050             if (thePrefix)
1051             {
1052               buf += thePrefix;
1053               buf += ' ';
1054             }
1055             buf += "DEF ";
1056             buf += theNode->Name();
1057             aStatus = theNode->Write (buf.ToCString());
1058             const_cast<VrmlData_MapOfNode&>(myNamedNodesOut).Add (theNode);
1059           }
1060         }
1061       }
1062     }
1063   return aStatus;
1064 }
1065
1066 //=======================================================================
1067 //function : Dump
1068 //purpose  : 
1069 //=======================================================================
1070
1071 void VrmlData_Scene::Dump (Standard_OStream& theStream) const
1072 {
1073   theStream << " ===== Diagnostic Dump of a Scene (" << myAllNodes.Extent()
1074             << " nodes)" << endl;
1075
1076   /*
1077   Iterator anIterA(myAllNodes);
1078   for (; anIterA.More(); anIterA.Next())
1079     dumpNode(theStream, anIterA.Value(), "");
1080   */
1081   Iterator anIter(myLstNodes);
1082   for (; anIter.More(); anIter.Next())
1083     dumpNode(theStream, anIter.Value(), "  ");
1084 }
1085
1086 //=======================================================================
1087 //function : dumpNode
1088 //purpose  : static (local) function
1089 //=======================================================================
1090
1091 void dumpNode (Standard_OStream&                theStream,
1092                const Handle(VrmlData_Node)&     theNode,
1093                const TCollection_AsciiString&   theIndent)
1094 {
1095   if (theNode.IsNull())
1096     return;
1097   TCollection_AsciiString aNewIndent = 
1098     theIndent.IsEmpty() ? theIndent : theIndent + "  "; 
1099   if (theNode->IsKind(STANDARD_TYPE(VrmlData_Appearance))) {
1100     const Handle(VrmlData_Appearance) anAppearance = 
1101       Handle(VrmlData_Appearance)::DownCast (theNode);
1102     dumpNodeHeader (theStream, theIndent, "Appearance", theNode->Name());
1103     if (theIndent.IsEmpty() == Standard_False) {
1104       dumpNode (theStream, anAppearance->Material(), aNewIndent);
1105       dumpNode (theStream, anAppearance->Texture(), aNewIndent);
1106       dumpNode (theStream, anAppearance->TextureTransform(), aNewIndent);
1107     }
1108   } else if (theNode->IsKind(STANDARD_TYPE(VrmlData_ShapeNode))) {
1109     const Handle(VrmlData_ShapeNode) aShape = 
1110       Handle(VrmlData_ShapeNode)::DownCast (theNode);
1111     dumpNodeHeader (theStream, theIndent, "Shape", theNode->Name());
1112     if (theIndent.IsEmpty() == Standard_False) {
1113       dumpNode (theStream, aShape->Appearance(), aNewIndent);
1114       dumpNode (theStream, aShape->Geometry(), aNewIndent);
1115     }
1116   } else if (theNode->IsKind(STANDARD_TYPE(VrmlData_Box)))
1117     dumpNodeHeader (theStream, theIndent, "Box", theNode->Name());
1118   else if (theNode->IsKind(STANDARD_TYPE(VrmlData_Cylinder)))
1119     dumpNodeHeader (theStream, theIndent, "Cylinder", theNode->Name());
1120   else if (theNode->IsKind(STANDARD_TYPE(VrmlData_Sphere)))
1121     dumpNodeHeader (theStream, theIndent, "Sphere", theNode->Name());
1122   else if (theNode->IsKind(STANDARD_TYPE(VrmlData_Cone)))
1123     dumpNodeHeader (theStream, theIndent, "Cone", theNode->Name());
1124   else if (theNode->IsKind(STANDARD_TYPE(VrmlData_Coordinate)))
1125     dumpNodeHeader (theStream, theIndent, "Coordinate", theNode->Name());
1126   else if (theNode->IsKind(STANDARD_TYPE(VrmlData_Group))) {
1127     const Handle(VrmlData_Group) aGroup = 
1128       Handle(VrmlData_Group)::DownCast (theNode);
1129     char buf[64];
1130     Sprintf (buf, "Group (%s)",
1131              aGroup->IsTransform() ? "Transform" : "Group");
1132     dumpNodeHeader (theStream, theIndent, buf, theNode->Name());
1133     if (theIndent.IsEmpty() == Standard_False) {
1134       VrmlData_ListOfNode::Iterator anIter = aGroup->NodeIterator();
1135       for (; anIter.More(); anIter.Next())
1136         dumpNode (theStream, anIter.Value(), aNewIndent);
1137     }
1138   } else if (theNode->IsKind(STANDARD_TYPE(VrmlData_ImageTexture)))
1139     dumpNodeHeader (theStream, theIndent, "ImageTexture", theNode->Name());
1140   else if (theNode->IsKind(STANDARD_TYPE(VrmlData_IndexedFaceSet))) {
1141     const Handle(VrmlData_IndexedFaceSet) aNode =
1142       Handle(VrmlData_IndexedFaceSet)::DownCast(theNode);
1143     const Standard_Integer ** ppDummy; 
1144     const Standard_Size nCoord = aNode->Coordinates()->Length();
1145     const Standard_Size nPoly  = aNode->Polygons (ppDummy);
1146     char buf[80];
1147     Sprintf (buf, "IndexedFaceSet (%" PRIuPTR " vertices, %" PRIuPTR " polygons)",
1148              nCoord, nPoly);
1149
1150     dumpNodeHeader (theStream, theIndent, buf, theNode->Name());
1151   } else if (theNode->IsKind(STANDARD_TYPE(VrmlData_IndexedLineSet))) {
1152     const Handle(VrmlData_IndexedLineSet) aNode =
1153       Handle(VrmlData_IndexedLineSet)::DownCast(theNode);
1154     const Standard_Integer ** ppDummy; 
1155     const Standard_Size nCoord = aNode->Coordinates()->Length();
1156     const Standard_Size nPoly  = aNode->Polygons (ppDummy);
1157
1158     char buf[80];
1159     Sprintf(buf, "IndexedLineSet (%" PRIuPTR " vertices, %" PRIuPTR " polygons)",
1160             nCoord, nPoly);
1161
1162     dumpNodeHeader (theStream, theIndent, buf, theNode->Name());
1163   } else if (theNode->IsKind(STANDARD_TYPE(VrmlData_Material))) {
1164 //     const Handle(VrmlData_Material) aMaterial = 
1165 //       Handle(VrmlData_Material)::DownCast (theNode);
1166     dumpNodeHeader (theStream, theIndent, "Material", theNode->Name());
1167   }
1168   else if (theNode->IsKind(STANDARD_TYPE(VrmlData_Normal)))
1169     dumpNodeHeader (theStream, theIndent, "Normal", theNode->Name());
1170   else if (theNode->IsKind(STANDARD_TYPE(VrmlData_TextureCoordinate)))
1171     dumpNodeHeader (theStream, theIndent, "TextureCoordinate", theNode->Name());
1172   else if (theNode->IsKind(STANDARD_TYPE(VrmlData_WorldInfo)))
1173     dumpNodeHeader (theStream, theIndent, "WorldInfo", theNode->Name());
1174   else if (theNode->IsKind(STANDARD_TYPE(VrmlData_UnknownNode))) {
1175     const Handle(VrmlData_UnknownNode) anUnknown = 
1176       Handle(VrmlData_UnknownNode)::DownCast (theNode);
1177     char buf[64];
1178     Sprintf (buf, "Unknown (%s)", anUnknown->GetTitle().ToCString());
1179     dumpNodeHeader (theStream, theIndent, buf, theNode->Name());
1180   }
1181 }
1182
1183 //=======================================================================
1184 //function : dumpNodeHeader
1185 //purpose  : 
1186 //=======================================================================
1187
1188 void dumpNodeHeader (Standard_OStream&                  theStream,
1189                      const TCollection_AsciiString&     theIndent,
1190                      const char *                       theType,
1191                      const char *                       theName)
1192 {
1193   theStream << theIndent << theType <<" node";
1194   if (theName[0] == '\0')
1195     theStream << endl;
1196   else
1197     theStream << ": \"" << theName << '\"' << endl;
1198 }