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