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