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