0030692: Data Exchange - introduce base framework RWMesh for importing mesh data...
authorkgv <kgv@opencascade.com>
Fri, 3 May 2019 14:50:28 +0000 (17:50 +0300)
committerbugmaster <bugmaster@opencascade.com>
Fri, 7 Jun 2019 12:02:38 +0000 (15:02 +0300)
RWMesh_CafReader - added new interface class for common workflow for reading mesh data files into XDE document.

OSD_Path - added auxiliary methods splitting path into folder+file pair
and checking relative/absolute path semantically:
OSD_Path::FolderAndFileFromPath(), ::IsRelativePath(), ::IsAbsolutePath().

V3d_TypeOfOrientation enumeration has been extended with aliases
(like front/left) for Z-up and Y-up conventions.
V3d_View::SetProj() now accepts argument for asking Y-up instead of Z-up.

Added command vviewproj defining standard camera direction.
Commands vaxo, vleft, vright, vtop, vbottom, vfront, vbottom now redirect to vviewproj.

TCollection_AsciiString::SubString() now uses Standard_OutOfRange_Always_Raise_if() to suppress GCC warning.

Eliminated gcc 4.4 compilation errors within Standard_OutOfRange_Raise_if,Standard_RangeError_Raise_if.

39 files changed:
adm/MODULES
adm/UDLIST
src/FSD/FILES
src/FSD/FSD_Base64Decoder.cxx [new file with mode: 0644]
src/FSD/FSD_Base64Decoder.hxx [new file with mode: 0644]
src/Image/FILES
src/Image/Image_Texture.cxx [new file with mode: 0644]
src/Image/Image_Texture.hxx [new file with mode: 0644]
src/OS/DataExchange.tcl
src/OSD/OSD_Path.cxx
src/OSD/OSD_Path.hxx
src/QANCollection/QANCollection_Test.cxx
src/RWMesh/FILES [new file with mode: 0644]
src/RWMesh/RWMesh_CafReader.cxx [new file with mode: 0644]
src/RWMesh/RWMesh_CafReader.hxx [new file with mode: 0644]
src/RWMesh/RWMesh_CoordinateSystem.hxx [new file with mode: 0644]
src/RWMesh/RWMesh_CoordinateSystemConverter.cxx [new file with mode: 0644]
src/RWMesh/RWMesh_CoordinateSystemConverter.hxx [new file with mode: 0644]
src/RWMesh/RWMesh_NodeAttributes.hxx [new file with mode: 0644]
src/Standard/FILES
src/Standard/Standard_OutOfRange.hxx
src/Standard/Standard_RangeError.hxx
src/Standard/Standard_ReadBuffer.hxx [new file with mode: 0644]
src/TCollection/TCollection_AsciiString.lxx
src/TKRWMesh/CMakeLists.txt [new file with mode: 0644]
src/TKRWMesh/EXTERNLIB [new file with mode: 0644]
src/TKRWMesh/FILES [new file with mode: 0644]
src/TKRWMesh/PACKAGES [new file with mode: 0644]
src/TKXSDRAW/EXTERNLIB
src/V3d/V3d_TypeOfOrientation.hxx
src/V3d/V3d_View.cxx
src/V3d/V3d_View.hxx
src/ViewerTest/ViewerTest_ViewerCommands.cxx
src/XCAFPrs/FILES
src/XCAFPrs/XCAFPrs_DocumentExplorer.cxx [new file with mode: 0644]
src/XCAFPrs/XCAFPrs_DocumentExplorer.hxx [new file with mode: 0644]
src/XCAFPrs/XCAFPrs_DocumentIdIterator.hxx [new file with mode: 0644]
src/XCAFPrs/XCAFPrs_DocumentNode.hxx [new file with mode: 0644]
tests/collections/n/osdpath [new file with mode: 0644]

index e65fb37..2ec6ef3 100644 (file)
@@ -3,5 +3,5 @@ ModelingData TKG2d TKG3d TKGeomBase TKBRep
 ModelingAlgorithms TKGeomAlgo TKTopAlgo TKPrim TKBO TKBool TKHLR TKFillet TKOffset TKFeat TKMesh TKXMesh TKShHealing
 Visualization TKService TKV3d TKOpenGl TKMeshVS TKIVtk TKD3DHost
 ApplicationFramework TKCDF TKLCAF TKCAF TKBinL TKXmlL TKBin TKXml TKStdL TKStd TKTObj TKBinTObj TKXmlTObj TKVCAF
-DataExchange TKXSBase TKSTEPBase TKSTEPAttr TKSTEP209 TKSTEP TKIGES TKXCAF TKXDEIGES TKXDESTEP TKSTL TKVRML TKXmlXCAF TKBinXCAF
+DataExchange TKXSBase TKSTEPBase TKSTEPAttr TKSTEP209 TKSTEP TKIGES TKXCAF TKXDEIGES TKXDESTEP TKSTL TKVRML TKXmlXCAF TKBinXCAF TKRWMesh
 Draw TKDraw TKTopTest TKViewerTest TKXSDRAW TKDCAF TKXDEDRAW TKTObjDRAW TKQADraw TKIVtkDraw DRAWEXE
index 79e58c7..032662b 100644 (file)
@@ -439,3 +439,5 @@ n Geom2dEvaluator
 t TKVCAF
 n XCAFView
 n XCAFNoteObjects
+t TKRWMesh
+n RWMesh
index 5b2934e..20ecdc5 100755 (executable)
@@ -1,4 +1,6 @@
 FILES
+FSD_Base64Decoder.cxx
+FSD_Base64Decoder.hxx
 FSD_BinaryFile.cxx
 FSD_BinaryFile.hxx
 FSD_BStream.hxx
diff --git a/src/FSD/FSD_Base64Decoder.cxx b/src/FSD/FSD_Base64Decoder.cxx
new file mode 100644 (file)
index 0000000..0959f1e
--- /dev/null
@@ -0,0 +1,87 @@
+// Author: Kirill Gavrilov
+// Copyright (c) 2016-2019 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include <FSD_Base64Decoder.hxx>
+
+#include <Message.hxx>
+#include <Message_Messenger.hxx>
+
+// =======================================================================
+// function : Decode
+// purpose  :
+// =======================================================================
+Handle(NCollection_Buffer) FSD_Base64Decoder::Decode (const Standard_Byte* theStr,
+                                                      const Standard_Size  theLen)
+{
+  //! Look-up table for decoding base64 stream.
+  static const Standard_Byte THE_BASE64_FROM[128] =
+  {
+    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,  62, 255,  62, 255,  63,
+    52,  53,  54,  55,  56,  57,  58,  59,  60,  61, 255, 255,   0, 255, 255, 255,
+    255,   0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,  13,  14,
+    15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25, 255, 255, 255, 255,  63,
+    255,  26,  27,  28,  29,  30,  31,  32,  33,  34,  35,  36,  37,  38,  39,  40,
+    41,  42,  43,  44,  45,  46,  47,  48,  49,  50,  51, 255, 255, 255, 255, 255
+  };
+
+  Handle(NCollection_Buffer) aData = new NCollection_Buffer (NCollection_BaseAllocator::CommonBaseAllocator());
+  if (!aData->Allocate (3 * theLen / 4))
+  {
+    Message::DefaultMessenger()->Send ("Fail to allocate memory.", Message_Fail);
+    return Handle(NCollection_Buffer)();
+  }
+
+  Standard_Byte* aDataPtr = aData->ChangeData();
+  const Standard_Byte* anEnd = theStr + theLen;
+  for (const Standard_Byte* aByteIter = theStr; aByteIter < anEnd; aByteIter += 4)
+  {
+    // get values for each group of four base 64 characters
+    const Standard_Byte b4[4] =
+    {
+      aByteIter + 0 < anEnd && aByteIter[0] <= 'z' ? THE_BASE64_FROM[aByteIter[0]] : Standard_Byte(0xFF),
+      aByteIter + 1 < anEnd && aByteIter[1] <= 'z' ? THE_BASE64_FROM[aByteIter[1]] : Standard_Byte(0xFF),
+      aByteIter + 2 < anEnd && aByteIter[2] <= 'z' ? THE_BASE64_FROM[aByteIter[2]] : Standard_Byte(0xFF),
+      aByteIter + 3 < anEnd && aByteIter[3] <= 'z' ? THE_BASE64_FROM[aByteIter[3]] : Standard_Byte(0xFF)
+    };
+
+    // transform into a group of three bytes
+    const Standard_Byte b3[3] =
+    {
+      Standard_Byte(((b4[0] & 0x3F) << 2) + ((b4[1] & 0x30) >> 4)),
+      Standard_Byte(((b4[1] & 0x0F) << 4) + ((b4[2] & 0x3C) >> 2)),
+      Standard_Byte(((b4[2] & 0x03) << 6) + ((b4[3] & 0x3F) >> 0))
+    };
+
+    // add the byte to the return value if it isn't part of an '=' character (indicated by 0xFF)
+    if (b4[1] != 0xFF)
+    {
+      *aDataPtr = b3[0];
+      ++aDataPtr;
+    }
+    if (b4[2] != 0xFF)
+    {
+      *aDataPtr = b3[1];
+      ++aDataPtr;
+    }
+    if (b4[3] != 0xFF)
+    {
+      *aDataPtr = b3[2];
+      ++aDataPtr;
+    }
+  }
+
+  return aData;
+}
diff --git a/src/FSD/FSD_Base64Decoder.hxx b/src/FSD/FSD_Base64Decoder.hxx
new file mode 100644 (file)
index 0000000..1c1abc3
--- /dev/null
@@ -0,0 +1,30 @@
+// Author: Kirill Gavrilov
+// Copyright (c) 2016-2019 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _FSD_Base64Decoder_HeaderFile
+#define _FSD_Base64Decoder_HeaderFile
+
+#include <NCollection_Buffer.hxx>
+
+//! Tool decoding base64 stream.
+class FSD_Base64Decoder
+{
+public:
+
+  //! Function decoding base64 stream.
+  Standard_EXPORT static Handle(NCollection_Buffer) Decode (const Standard_Byte* theStr,
+                                                            const Standard_Size  theLen);
+};
+
+#endif // _FSD_Base64Decoder_HeaderFile
index 40118d5..8055875 100755 (executable)
@@ -8,5 +8,7 @@ Image_PixMap.cxx
 Image_PixMap.hxx
 Image_PixMapData.hxx
 Image_PixMapTypedData.hxx
+Image_Texture.cxx
+Image_Texture.hxx
 Image_VideoRecorder.cxx
 Image_VideoRecorder.hxx
diff --git a/src/Image/Image_Texture.cxx b/src/Image/Image_Texture.cxx
new file mode 100644 (file)
index 0000000..6e9e752
--- /dev/null
@@ -0,0 +1,304 @@
+// Author: Kirill Gavrilov
+// Copyright (c) 2015-2019 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include <Image_Texture.hxx>
+
+#include <Image_AlienPixMap.hxx>
+#include <Message.hxx>
+#include <Message_Messenger.hxx>
+#include <OSD_OpenFile.hxx>
+
+IMPLEMENT_STANDARD_RTTIEXT(Image_Texture, Standard_Transient)
+
+// ================================================================
+// Function : Image_Texture
+// Purpose  :
+// ================================================================
+Image_Texture::Image_Texture (const TCollection_AsciiString& theFileName)
+: myImagePath (theFileName),
+  myOffset (-1),
+  myLength (-1)
+{
+  // share textures with unique file paths
+  if (!theFileName.IsEmpty())
+  {
+    myTextureId = TCollection_AsciiString ("texture://") + theFileName;
+  }
+}
+
+// ================================================================
+// Function : Image_Texture
+// Purpose  :
+// ================================================================
+Image_Texture::Image_Texture (const TCollection_AsciiString& theFileName,
+                              int64_t theOffset,
+                              int64_t theLength)
+: myImagePath (theFileName),
+  myOffset (theOffset),
+  myLength (theLength)
+{
+  // share textures with unique file paths
+  if (!theFileName.IsEmpty())
+  {
+    char aBuff[60];
+    Sprintf (aBuff, ";%" PRId64 ",%" PRId64, theOffset, theLength);
+    myTextureId = TCollection_AsciiString ("texture://") + theFileName + aBuff;
+  }
+}
+
+// ================================================================
+// Function : Image_Texture
+// Purpose  :
+// ================================================================
+Image_Texture::Image_Texture (const Handle(NCollection_Buffer)& theBuffer,
+                              const TCollection_AsciiString& theId)
+: myBuffer (theBuffer),
+  myOffset (-1),
+  myLength (-1)
+{
+  if (!theId.IsEmpty())
+  {
+    myTextureId = TCollection_AsciiString ("texturebuf://") + theId;
+  }
+}
+
+// ================================================================
+// Function : ReadImage
+// Purpose  :
+// ================================================================
+Handle(Image_PixMap) Image_Texture::ReadImage() const
+{
+  Handle(Image_PixMap) anImage;
+  if (!myBuffer.IsNull())
+  {
+    anImage = loadImageBuffer (myBuffer, myTextureId);
+  }
+  else if (myOffset >= 0)
+  {
+    anImage = loadImageOffset (myImagePath, myOffset, myLength);
+  }
+  else
+  {
+    anImage = loadImageFile (myImagePath);
+  }
+
+  if (anImage.IsNull())
+  {
+    return Handle(Image_PixMap)();
+  }
+  return anImage;
+}
+
+// ================================================================
+// Function : loadImageFile
+// Purpose  :
+// ================================================================
+Handle(Image_PixMap) Image_Texture::loadImageFile (const TCollection_AsciiString& thePath) const
+{
+  Handle(Image_AlienPixMap) anImage = new Image_AlienPixMap();
+  if (!anImage->Load (thePath))
+  {
+    return Handle(Image_PixMap)();
+  }
+  return anImage;
+}
+
+// ================================================================
+// Function : loadImageBuffer
+// Purpose  :
+// ================================================================
+Handle(Image_PixMap) Image_Texture::loadImageBuffer (const Handle(NCollection_Buffer)& theBuffer,
+                                                     const TCollection_AsciiString& theId) const
+{
+  if (theBuffer.IsNull())
+  {
+    return Handle(Image_PixMap)();
+  }
+  else if (theBuffer->Size() > (Standard_Size )IntegerLast())
+  {
+    Message::DefaultMessenger()->Send (TCollection_AsciiString ("Error: Image file size is too big '") + theId + "'.", Message_Fail);
+    return Handle(Image_PixMap)();
+  }
+
+  Handle(Image_AlienPixMap) anImage = new Image_AlienPixMap();
+  if (!anImage->Load (theBuffer->Data(), (int )theBuffer->Size(), theId))
+  {
+    return Handle(Image_PixMap)();
+  }
+  return anImage;
+}
+
+// ================================================================
+// Function : loadImageOffset
+// Purpose  :
+// ================================================================
+Handle(Image_PixMap) Image_Texture::loadImageOffset (const TCollection_AsciiString& thePath,
+                                                     int64_t theOffset,
+                                                     int64_t theLength) const
+{
+  if (theLength > IntegerLast())
+  {
+    Message::DefaultMessenger()->Send (TCollection_AsciiString ("Error: Image file size is too big '") + thePath + "'.", Message_Fail);
+    return Handle(Image_PixMap)();
+  }
+
+  std::ifstream aFile;
+  OSD_OpenStream (aFile, thePath.ToCString(), std::ios::in | std::ios::binary);
+  if (!aFile)
+  {
+    Message::DefaultMessenger()->Send (TCollection_AsciiString ("Error: Image file '") + thePath + "' cannot be opened.", Message_Fail);
+    return Handle(Image_PixMap)();
+  }
+  aFile.seekg ((std::streamoff )theOffset, std::ios_base::beg);
+  if (!aFile.good())
+  {
+    Message::DefaultMessenger()->Send (TCollection_AsciiString ("Error: Image is defined with invalid file offset '") + thePath + "'.", Message_Fail);
+    return Handle(Image_PixMap)();
+  }
+
+  Handle(Image_AlienPixMap) anImage = new Image_AlienPixMap();
+  if (!anImage->Load (aFile, thePath))
+  {
+    return Handle(Image_PixMap)();
+  }
+  return anImage;
+}
+
+// ================================================================
+// Function : ProbeImageFileFormat
+// Purpose  :
+// ================================================================
+TCollection_AsciiString Image_Texture::ProbeImageFileFormat() const
+{
+  static const int THE_PROBE_SIZE = 20;
+  char aBuffer[THE_PROBE_SIZE];
+  if (!myBuffer.IsNull())
+  {
+    memcpy (aBuffer, myBuffer->Data(), myBuffer->Size() < THE_PROBE_SIZE ? myBuffer->Size() : THE_PROBE_SIZE);
+  }
+  else
+  {
+    std::ifstream aFileIn;
+    OSD_OpenStream (aFileIn, myImagePath.ToCString(), std::ios::in | std::ios::binary);
+    if (!aFileIn)
+    {
+      Message::DefaultMessenger()->Send (TCollection_AsciiString ("Error: Unable to open file ") + myImagePath + "!", Message_Fail);
+      return false;
+    }
+    if (myOffset >= 0)
+    {
+      aFileIn.seekg ((std::streamoff )myOffset, std::ios_base::beg);
+      if (!aFileIn.good())
+      {
+        Message::DefaultMessenger()->Send (TCollection_AsciiString ("Error: Image is defined with invalid file offset '") + myImagePath + "'.", Message_Fail);
+        return false;
+      }
+    }
+
+    if (!aFileIn.read (aBuffer, THE_PROBE_SIZE))
+    {
+      Message::DefaultMessenger()->Send (TCollection_AsciiString ("Error: unable to read image file '") + myImagePath + "'", Message_Fail);
+      return false;
+    }
+  }
+
+  if (memcmp (aBuffer, "\x89" "PNG\r\n" "\x1A" "\n", 8) == 0)
+  {
+    return "png";
+  }
+  else if (memcmp (aBuffer, "\xFF\xD8\xFF", 3) == 0)
+  {
+    return "jpg";
+  }
+  else if (memcmp (aBuffer, "GIF87a", 6) == 0
+        || memcmp (aBuffer, "GIF89a", 6) == 0)
+  {
+    return "gif";
+  }
+  else if (memcmp (aBuffer, "II\x2A\x00", 4) == 0
+        || memcmp (aBuffer, "MM\x00\x2A", 4) == 0)
+  {
+    return "tiff";
+  }
+  else if (memcmp (aBuffer, "BM", 2) == 0)
+  {
+    return "bmp";
+  }
+  else if (memcmp (aBuffer,     "RIFF", 4) == 0
+        && memcmp (aBuffer + 8, "WEBP", 4) == 0)
+  {
+    return "webp";
+  }
+  return "";
+}
+
+// ================================================================
+// Function : WriteImage
+// Purpose  :
+// ================================================================
+Standard_Boolean Image_Texture::WriteImage (const TCollection_AsciiString& theFile)
+{
+  Handle(NCollection_Buffer) aBuffer = myBuffer;
+  if (myBuffer.IsNull())
+  {
+    std::ifstream aFileIn;
+    OSD_OpenStream (aFileIn, myImagePath.ToCString(), std::ios::in | std::ios::binary);
+    if (!aFileIn)
+    {
+      Message::DefaultMessenger()->Send (TCollection_AsciiString ("Error: Unable to open file ") + myImagePath + "!", Message_Fail);
+      return Standard_False;
+    }
+
+    Standard_Size aLen = (Standard_Size )myLength;
+    if (myOffset >= 0)
+    {
+      aFileIn.seekg ((std::streamoff )myOffset, std::ios_base::beg);
+      if (!aFileIn.good())
+      {
+        Message::DefaultMessenger()->Send (TCollection_AsciiString ("Error: Image is defined with invalid file offset '") + myImagePath + "'.", Message_Fail);
+        return Standard_False;
+      }
+    }
+    else
+    {
+      aFileIn.seekg (0, std::ios_base::end);
+      aLen = (Standard_Size )aFileIn.tellg();
+      aFileIn.seekg (0, std::ios_base::beg);
+    }
+
+    aBuffer = new NCollection_Buffer (NCollection_BaseAllocator::CommonBaseAllocator(), aLen);
+    if (!aFileIn.read ((char* )aBuffer->ChangeData(), aBuffer->Size()))
+    {
+      Message::DefaultMessenger()->Send (TCollection_AsciiString ("Error: unable to read image file '") + myImagePath + "'", Message_Fail);
+      return Standard_False;
+    }
+  }
+
+  std::ofstream aFileOut;
+  OSD_OpenStream (aFileOut, theFile.ToCString(), std::ios::out | std::ios::binary | std::ios::trunc);
+  if (!aFileOut)
+  {
+    Message::DefaultMessenger()->Send (TCollection_AsciiString ("Error: Unable to create file ") + theFile + "!", Message_Fail);
+    return Standard_False;
+  }
+
+  aFileOut.write ((const char* )aBuffer->Data(), aBuffer->Size());
+  aFileOut.close();
+  if (!aFileOut.good())
+  {
+    Message::DefaultMessenger()->Send (TCollection_AsciiString ("Error: Unable to write file ") + theFile + "!", Message_Fail);
+    return Standard_False;
+  }
+  return Standard_True;
+}
diff --git a/src/Image/Image_Texture.hxx b/src/Image/Image_Texture.hxx
new file mode 100644 (file)
index 0000000..f55458b
--- /dev/null
@@ -0,0 +1,115 @@
+// Author: Kirill Gavrilov
+// Copyright (c) 2015-2019 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _Image_Texture_HeaderFile
+#define _Image_Texture_HeaderFile
+
+#include <NCollection_Buffer.hxx>
+#include <TCollection_AsciiString.hxx>
+
+class Image_PixMap;
+
+//! Texture image definition.
+//! The image can be stored as path to image file, as file path with the given offset and as a data buffer of encoded image.
+class Image_Texture : public Standard_Transient
+{
+  DEFINE_STANDARD_RTTIEXT(Image_Texture, Standard_Transient)
+public:
+
+  //! Constructor pointing to file location.
+  Standard_EXPORT explicit Image_Texture (const TCollection_AsciiString& theFileName);
+
+  //! Constructor pointing to file part.
+  Standard_EXPORT explicit Image_Texture (const TCollection_AsciiString& theFileName,
+                                          int64_t theOffset,
+                                          int64_t theLength);
+
+  //! Constructor pointing to buffer.
+  Standard_EXPORT explicit Image_Texture (const Handle(NCollection_Buffer)& theBuffer,
+                                          const TCollection_AsciiString& theId);
+
+  //! Return generated texture id.
+  const TCollection_AsciiString& TextureId() const { return myTextureId; }
+
+  //! Return image file path.
+  const TCollection_AsciiString& FilePath() const { return myImagePath; }
+
+  //! Return offset within file.
+  int64_t FileOffset() const { return myOffset; }
+
+  //! Return length of image data within the file after offset.
+  int64_t FileLength() const { return myLength; }
+
+  //! Return buffer holding encoded image content.
+  const Handle(NCollection_Buffer)& DataBuffer() const { return myBuffer; }
+
+  //! Return image file format.
+  Standard_EXPORT TCollection_AsciiString ProbeImageFileFormat() const;
+
+  //! Image reader.
+  Standard_EXPORT virtual Handle(Image_PixMap) ReadImage() const;
+
+  //! Write image to specified file without decoding data.
+  Standard_EXPORT virtual Standard_Boolean WriteImage (const TCollection_AsciiString& theFile);
+
+public: //! @name hasher interface
+
+  //! Hash value, for Map interface.
+  static int HashCode (const Handle(Image_Texture)& theTexture, const int theUpper)
+  {
+    return !theTexture.IsNull()
+          ? TCollection_AsciiString::HashCode (theTexture->myTextureId, theUpper)
+          : 0;
+  }
+
+  //! Matching two instances, for Map interface.
+  static Standard_Boolean IsEqual (const Handle(Image_Texture)& theTex1,
+                                   const Handle(Image_Texture)& theTex2)
+  {
+    if (theTex1.IsNull() != theTex2.IsNull())
+    {
+      return Standard_False;
+    }
+    else if (theTex1.IsNull())
+    {
+      return Standard_True;
+    }
+    return theTex1->myTextureId.IsEqual (theTex2->myTextureId);
+  }
+
+protected:
+
+  //! Read image from normal image file.
+  Standard_EXPORT virtual Handle(Image_PixMap) loadImageFile (const TCollection_AsciiString& thePath) const;
+
+  //! Read image from file with some offset.
+  Standard_EXPORT virtual Handle(Image_PixMap) loadImageOffset (const TCollection_AsciiString& thePath,
+                                                                int64_t theOffset,
+                                                                int64_t theLength) const;
+
+  //! Read image from buffer.
+  Standard_EXPORT virtual Handle(Image_PixMap) loadImageBuffer (const Handle(NCollection_Buffer)& theBuffer,
+                                                                const TCollection_AsciiString& theId) const;
+
+protected:
+
+  TCollection_AsciiString myTextureId; //!< generated texture id
+  TCollection_AsciiString myImagePath; //!< image file path
+  Handle(NCollection_Buffer) myBuffer; //!< image buffer
+  int64_t                    myOffset; //!< offset within file
+  int64_t                    myLength; //!< length within file
+
+};
+
+#endif // _Image_Texture_HeaderFile
index 75cb56b..e72af9b 100644 (file)
@@ -17,7 +17,7 @@
 proc DataExchange:toolkits { } {
     return [list TKXSBase TKSTEPBase TKSTEPAttr TKSTEP209 TKSTEP TKIGES \
                TKXCAF TKXDEIGES TKXDESTEP \
-               TKSTL TKVRML TKXmlXCAF TKBinXCAF]
+               TKSTL TKVRML TKXmlXCAF TKBinXCAF TKRWMesh]
 }
 ;#
 ;# Autres UDs a prendre.
index 397fadb..edd5189 100644 (file)
@@ -1635,3 +1635,40 @@ Standard_Boolean LocateExecFile(OSD_Path& )
 {
   return Standard_False ;
 }
+
+// =======================================================================
+// function : FolderAndFileFromPath
+// purpose  :
+// =======================================================================
+void OSD_Path::FolderAndFileFromPath (const TCollection_AsciiString& theFilePath,
+                                      TCollection_AsciiString&       theFolder,
+                                      TCollection_AsciiString&       theFileName)
+{
+  Standard_Integer aLastSplit = -1;
+  Standard_CString aString = theFilePath.ToCString();
+  for (Standard_Integer anIter = 0; anIter < theFilePath.Length(); ++anIter)
+  {
+    if (aString[anIter] == '/'
+     || aString[anIter] == '\\')
+    {
+      aLastSplit = anIter;
+    }
+  }
+
+  if (aLastSplit == -1)
+  {
+    theFolder.Clear();
+    theFileName = theFilePath;
+    return;
+  }
+
+  theFolder = theFilePath.SubString (1, aLastSplit + 1);
+  if (aLastSplit + 2 <= theFilePath.Length())
+  {
+    theFileName = theFilePath.SubString (aLastSplit + 2, theFilePath.Length());
+  }
+  else
+  {
+    theFileName.Clear();
+  }
+}
index 66cc192..163c804 100644 (file)
 #include <Standard.hxx>
 #include <Standard_DefineAlloc.hxx>
 #include <Standard_Handle.hxx>
-
 #include <TCollection_AsciiString.hxx>
-#include <Standard_Boolean.hxx>
 #include <OSD_SysType.hxx>
-#include <Standard_Integer.hxx>
-class Standard_ConstructionError;
-class Standard_NullObject;
-class OSD_OSDError;
-class Standard_NumericError;
-class Standard_ProgramError;
-class TCollection_AsciiString;
-
-
 
 class OSD_Path 
 {
 public:
-
   DEFINE_STANDARD_ALLOC
 
-  
   //! Creates a Path object initialized to an empty string.
   //! i.e. current directory.
   Standard_EXPORT OSD_Path();
@@ -199,10 +186,12 @@ public:
   //! "which" Unix utility. Uses the path environment variable.
   //! Returns False if executable file not found.
   Standard_EXPORT Standard_Boolean LocateExecFile (OSD_Path& aPath);
-  
+
+public:
+
   //! Returns the relative file path between the absolute directory
   //! path <DirPath>  and the absolute file path <AbsFilePath>.
-  //! If <DirPath> starts with "/", pathes are handled as
+  //! If <DirPath> starts with "/", paths are handled as
   //! on Unix, if it starts with a letter followed by ":", as on
   //! WNT. In particular on WNT directory names are not key sensitive.
   //! If handling fails, an empty string is returned.
@@ -215,18 +204,108 @@ public:
   //! If handling fails, an empty string is returned.
   Standard_EXPORT static TCollection_AsciiString AbsolutePath (const TCollection_AsciiString& DirPath, const TCollection_AsciiString& RelFilePath);
 
+  //! Split absolute filepath into folder path and file name.
+  //! Example: IN  theFilePath ='/media/cdrom/image.jpg'
+  //!          OUT theFolder   ='/media/cdrom/'
+  //!          OUT theFileName ='image.jpg'
+  //! @param theFilePath [in]  file path
+  //! @param theFolder   [out] folder path (with trailing separator)
+  //! @param theFileName [out] file name
+  Standard_EXPORT static void FolderAndFileFromPath (const TCollection_AsciiString& theFilePath,
+                                                     TCollection_AsciiString&       theFolder,
+                                                     TCollection_AsciiString&       theFileName);
 
+  //! Detect absolute DOS-path also used in Windows.
+  //! The total path length is limited to 256 characters.
+  //! Sample path:
+  //!   C:\folder\file
+  //! @return true if DOS path syntax detected.
+  static Standard_Boolean IsDosPath (const char* thePath) { return thePath[0] != '\0' && thePath[1] == ':'; }
 
+  //! Detect extended-length NT path (can be only absolute).
+  //! Approximate maximum path is 32767 characters.
+  //! Sample path:
+  //!   \\?\D:\very long path
+  //! File I/O functions in the Windows API convert "/" to "\" as part of converting the name to an NT-style name, except when using the "\\?\" prefix.
+  //! @return true if extended-length NT path syntax detected.
+  static Standard_Boolean IsNtExtendedPath (const char* thePath) { return ::memcmp (thePath, "\\\\?\\", 4) == 0; }
 
-protected:
+  //! UNC is a naming convention used primarily to specify and map network drives in Microsoft Windows.
+  //! Sample path:
+  //!   \\server\share\file
+  //! @return true if UNC path syntax detected.
+  static Standard_Boolean IsUncPath (const char* thePath)
+  {
+    if (::memcmp (thePath, "\\\\", 2) == 0)
+    {
+      return thePath[2] != '?'
+          || IsUncExtendedPath (thePath);
+    }
+    return ::memcmp (thePath, "//",   2) == 0;
+  }
 
+  //! Detect extended-length UNC path.
+  //! Sample path:
+  //!   \\?\UNC\server\share
+  //! @return true if extended-length UNC path syntax detected.
+  static Standard_Boolean IsUncExtendedPath (const char* thePath) { return ::memcmp (thePath, "\\\\?\\UNC\\", 8) == 0; }
 
+  //! Detect absolute UNIX-path.
+  //! Sample path:
+  //!   /media/cdrom/file
+  //! @return true if UNIX path syntax detected.
+  static Standard_Boolean IsUnixPath (const char* thePath) { return thePath[0] == '/' && thePath[1] != '/'; }
 
+  //! Detect special URLs on Android platform.
+  //! Sample path:
+  //!   content://filename
+  //! @return true if content path syntax detected
+  static Standard_Boolean IsContentProtocolPath (const char* thePath) { return ::memcmp (thePath, "content://", 10) == 0; }
 
+  //! Detect remote protocol path (http / ftp / ...).
+  //! Actually shouldn't be remote...
+  //! Sample path:
+  //!   http://domain/path/file
+  //! @return true if remote protocol path syntax detected.
+  static Standard_Boolean IsRemoteProtocolPath (const char* thePath)
+  {
+    const char* anIter = thePath;
+    if (*anIter == ':')
+    {
+      return false;
+    }
+    for (; *anIter != '\0'; ++anIter)
+    {
+      if (*anIter == ':')
+      {
+        return *(++anIter) == '/'
+            && *(++anIter) == '/';
+      }
+    }
+    return false;
+  }
 
-private:
+  //! Method to recognize path is absolute or not.
+  //! Detection is based on path syntax - no any filesystem / network access performed.
+  //! @return true if path is incomplete (relative).
+  static Standard_Boolean IsRelativePath (const char* thePath)
+  {
+    return !IsUncPath  (thePath)
+        && !IsDosPath  (thePath)
+        && !IsNtExtendedPath (thePath)
+        && !IsUnixPath (thePath)
+        && !IsRemoteProtocolPath (thePath);
+  }
 
+  //! Method to recognize path is absolute or not.
+  //! Detection is based on path syntax - no any filesystem / network access performed.
+  //! @return true if path is complete (absolute)
+  static Standard_Boolean IsAbsolutePath (const char* thePath)
+  {
+    return !IsRelativePath (thePath);
+  }
 
+private:
 
   TCollection_AsciiString myNode;
   TCollection_AsciiString myUserName;
@@ -238,13 +317,6 @@ private:
   Standard_Boolean myUNCFlag;
   OSD_SysType mySysDep;
 
-
 };
 
-
-
-
-
-
-
 #endif // _OSD_Path_HeaderFile
index 918c969..e09c1d4 100644 (file)
@@ -21,6 +21,7 @@
 
 #include <gp_Pnt.hxx>
 
+#include <OSD_Path.hxx>
 #include <Precision.hxx>
 #include <Standard_Overflow.hxx>
 
@@ -1224,6 +1225,114 @@ static Standard_Integer QANColTestVec4 (Draw_Interpretor& theDI, Standard_Intege
   return 0;
 }
 
+//! Print file path flags deduced from path string.
+static Standard_Integer QAOsdPathType (Draw_Interpretor& theDI, Standard_Integer theNbArgs, const char** theArgVec)
+{
+  if (theNbArgs != 2)
+  {
+    std::cout << "Syntax error: wrong number of arguments\n";
+    return 1;
+  }
+
+  TCollection_AsciiString aPath (theArgVec[1]);
+  if (OSD_Path::IsAbsolutePath (aPath.ToCString()))
+  {
+    theDI << "absolute ";
+  }
+  if (OSD_Path::IsRelativePath (aPath.ToCString()))
+  {
+    theDI << "relative ";
+  }
+  if (OSD_Path::IsUnixPath (aPath.ToCString()))
+  {
+    theDI << "unix ";
+  }
+  if (OSD_Path::IsDosPath (aPath.ToCString()))
+  {
+    theDI << "dos ";
+  }
+  if (OSD_Path::IsUncPath (aPath.ToCString()))
+  {
+    theDI << "unc ";
+  }
+  if (OSD_Path::IsNtExtendedPath (aPath.ToCString()))
+  {
+    theDI << "ntextended ";
+  }
+  if (OSD_Path::IsUncExtendedPath (aPath.ToCString()))
+  {
+    theDI << "uncextended ";
+  }
+  if (OSD_Path::IsRemoteProtocolPath (aPath.ToCString()))
+  {
+    theDI << "protocol ";
+  }
+  if (OSD_Path::IsContentProtocolPath (aPath.ToCString()))
+  {
+    theDI << "content ";
+  }
+  return 0;
+}
+
+//! Print file path part deduced from path string.
+static Standard_Integer QAOsdPathPart (Draw_Interpretor& theDI, Standard_Integer theNbArgs, const char** theArgVec)
+{
+  TCollection_AsciiString aPath;
+  enum PathPart
+  {
+    PathPart_NONE,
+    PathPart_Folder,
+    PathPart_FileName,
+  };
+  PathPart aPart = PathPart_NONE;
+  for (Standard_Integer anArgIter = 1; anArgIter < theNbArgs; ++anArgIter)
+  {
+    TCollection_AsciiString anArgCase (theArgVec[anArgIter]);
+    anArgCase.LowerCase();
+    if (aPart == PathPart_NONE
+     && anArgCase == "-folder")
+    {
+      aPart = PathPart_Folder;
+    }
+    else if (aPart == PathPart_NONE
+          && anArgCase == "-filename")
+    {
+      aPart = PathPart_FileName;
+    }
+    else if (aPath.IsEmpty())
+    {
+      aPath = theArgVec[anArgIter];
+    }
+    else
+    {
+      std::cout << "Syntax error at '" << theArgVec[anArgIter] << "'\n";
+      return 1;
+    }
+  }
+  if (aPath.IsEmpty()
+   || aPart == PathPart_NONE)
+  {
+    std::cout << "Syntax error: wrong number of arguments\n";
+    return 1;
+  }
+
+  TCollection_AsciiString aFolder, aFileName;
+  OSD_Path::FolderAndFileFromPath (aPath, aFolder, aFileName);
+  switch (aPart)
+  {
+    case PathPart_Folder:
+      theDI << aFolder;
+      return 0;
+    case PathPart_FileName:
+      theDI << aFileName;
+      return 0;
+    case PathPart_NONE:
+    default:
+      return 1;
+  }
+}
+
+
 void QANCollection::CommandsTest(Draw_Interpretor& theCommands) {
   const char *group = "QANCollection";
 
@@ -1242,4 +1351,6 @@ void QANCollection::CommandsTest(Draw_Interpretor& theCommands) {
   theCommands.Add("QANColTestArrayMove",      "QANColTestArrayMove (is expected to give error)", __FILE__, QANColTestArrayMove, group);  
   theCommands.Add("QANColTestVec4",           "QANColTestVec4 test Vec4 implementation", __FILE__, QANColTestVec4, group);
   theCommands.Add("QATestAtof", "QATestAtof [nbvalues [nbdigits [min [max]]]]", __FILE__, QATestAtof, group);
+  theCommands.Add("QAOsdPathType",  "QAOsdPathType path : Print file path flags deduced from path string", __FILE__, QAOsdPathType, group);
+  theCommands.Add("QAOsdPathPart",  "QAOsdPathPart path [-folder][-fileName] : Print file path part", __FILE__, QAOsdPathPart, group);
 }
diff --git a/src/RWMesh/FILES b/src/RWMesh/FILES
new file mode 100644 (file)
index 0000000..902571c
--- /dev/null
@@ -0,0 +1,6 @@
+RWMesh_CoordinateSystem.hxx
+RWMesh_CoordinateSystemConverter.cxx
+RWMesh_CoordinateSystemConverter.hxx
+RWMesh_CafReader.cxx
+RWMesh_CafReader.hxx
+RWMesh_NodeAttributes.hxx
diff --git a/src/RWMesh/RWMesh_CafReader.cxx b/src/RWMesh/RWMesh_CafReader.cxx
new file mode 100644 (file)
index 0000000..6db946c
--- /dev/null
@@ -0,0 +1,387 @@
+// Author: Kirill Gavrilov
+// Copyright (c) 2016-2019 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include <RWMesh_CafReader.hxx>
+
+#include <XCAFPrs_DocumentExplorer.hxx>
+
+#include <Message.hxx>
+#include <Message_Messenger.hxx>
+#include <Message_ProgressIndicator.hxx>
+#include <BRep_Builder.hxx>
+#include <OSD_Path.hxx>
+#include <OSD_Timer.hxx>
+#include <TDataStd_Name.hxx>
+#include <TDocStd_Document.hxx>
+#include <TopExp_Explorer.hxx>
+#include <TopoDS.hxx>
+#include <TopoDS_Iterator.hxx>
+#include <XCAFDoc_ColorTool.hxx>
+#include <XCAFDoc_ColorType.hxx>
+#include <XCAFDoc_DocumentTool.hxx>
+#include <XCAFDoc_ShapeMapTool.hxx>
+#include <XCAFDoc_ShapeTool.hxx>
+
+IMPLEMENT_STANDARD_RTTIEXT(RWMesh_CafReader, Standard_Transient)
+
+// =======================================================================
+// function : RWMesh_CafReader
+// purpose  :
+// =======================================================================
+RWMesh_CafReader::RWMesh_CafReader()
+: myToFillDoc (Standard_True),
+  myToFillIncomplete (Standard_True),
+  myMemoryLimitMiB (-1),
+  myExtraStatus (RWMesh_CafReaderStatusEx_NONE)
+{
+  //
+}
+
+// =======================================================================
+// function : ~RWMesh_CafReader
+// purpose  :
+// =======================================================================
+RWMesh_CafReader::~RWMesh_CafReader()
+{
+  //
+}
+
+// =======================================================================
+// function : SingleShape
+// purpose  :
+// =======================================================================
+TopoDS_Shape RWMesh_CafReader::SingleShape() const
+{
+  if (myRootShapes.Size() > 1)
+  {
+    BRep_Builder aBuilder;
+    TopoDS_Compound aCompound;
+    aBuilder.MakeCompound (aCompound);
+    for (TopTools_SequenceOfShape::Iterator aRootIter (myRootShapes); aRootIter.More(); aRootIter.Next())
+    {
+      aBuilder.Add (aCompound, aRootIter.Value());
+    }
+    return aCompound;
+  }
+  else if (!myRootShapes.IsEmpty())
+  {
+    return myRootShapes.First();
+  }
+  return TopoDS_Shape();
+}
+
+// =======================================================================
+// function : perform
+// purpose  :
+// =======================================================================
+Standard_Boolean RWMesh_CafReader::perform (const TCollection_AsciiString& theFile,
+                                            const Handle(Message_ProgressIndicator)& theProgress,
+                                            const Standard_Boolean theToProbe)
+{
+  Standard_Integer aNewRootsLower = 1;
+  Handle(XCAFDoc_ShapeTool) aShapeTool = !myXdeDoc.IsNull()
+                                       ? XCAFDoc_DocumentTool::ShapeTool (myXdeDoc->Main())
+                                       : Handle(XCAFDoc_ShapeTool)();
+  if (!myXdeDoc.IsNull())
+  {
+    TDF_LabelSequence aRootLabels;
+    aShapeTool->GetFreeShapes (aRootLabels);
+    aNewRootsLower = aRootLabels.Upper() + 1;
+  }
+
+  OSD_Timer aLoadingTimer;
+  aLoadingTimer.Start();
+  const Standard_Boolean isDone = performMesh (theFile, theProgress, theToProbe);
+  if (theToProbe
+   || (!theProgress.IsNull() && theProgress->UserBreak()))
+  {
+    return isDone;
+  }
+  else if (!isDone)
+  {
+    if (!myToFillIncomplete)
+    {
+      return Standard_False;
+    }
+
+    myExtraStatus |= RWMesh_CafReaderStatusEx_Partial;
+  }
+
+  BRep_Builder aBuilder;
+  TopoDS_Shape aShape;
+  if (myRootShapes.Size() > 1)
+  {
+    TopoDS_Compound aCompound;
+    aBuilder.MakeCompound (aCompound);
+    for (TopTools_SequenceOfShape::Iterator aRootIter (myRootShapes); aRootIter.More(); aRootIter.Next())
+    {
+      aBuilder.Add (aCompound, aRootIter.Value());
+    }
+    aShape = aCompound;
+  }
+  else if (!myRootShapes.IsEmpty())
+  {
+    aShape = myRootShapes.First();
+  }
+
+  Standard_Integer aNbNodes = 0, aNbElems = 0, aNbFaces = 0;
+  if (!aShape.IsNull())
+  {
+    TopLoc_Location aDummyLoc;
+    for (TopExp_Explorer aFaceIter (aShape, TopAbs_FACE); aFaceIter.More(); aFaceIter.Next())
+    {
+      const TopoDS_Face& aFace = TopoDS::Face (aFaceIter.Current());
+      if (const Handle(Poly_Triangulation)& aPolyTri = BRep_Tool::Triangulation (aFace, aDummyLoc))
+      {
+        ++aNbFaces;
+        aNbNodes += aPolyTri->NbNodes();
+        aNbElems += aPolyTri->NbTriangles();
+      }
+    }
+  }
+  if (!isDone && aNbElems < 100)
+  {
+    return Standard_False;
+  }
+
+  if (myToFillDoc
+  && !myXdeDoc.IsNull())
+  {
+    const Standard_Boolean wasAutoNaming = aShapeTool->AutoNaming();
+    aShapeTool->SetAutoNaming (Standard_False);
+    const TCollection_AsciiString aRootName; // = generateRootName (theFile);
+    for (TopTools_SequenceOfShape::Iterator aRootIter (myRootShapes); aRootIter.More(); aRootIter.Next())
+    {
+      addShapeIntoDoc (aRootIter.Value(), TDF_Label(), aRootName);
+    }
+    aShapeTool->UpdateAssemblies();
+    aShapeTool->SetAutoNaming (wasAutoNaming);
+  }
+  if (!myXdeDoc.IsNull())
+  {
+    generateNames (theFile, aNewRootsLower, Standard_False);
+  }
+
+  aLoadingTimer.Stop();
+
+  TCollection_AsciiString aStats = TCollection_AsciiString("[") + aNbNodes + " nodes] [" + aNbElems + " 2d elements]";
+  if (!isDone)
+  {
+    Message::DefaultMessenger()->Send (TCollection_AsciiString ("Mesh ") + theFile
+                                     + "\n" + aStats
+                                     + "\n[PARTIALLY read in " + aLoadingTimer.ElapsedTime() + " s]", Message_Info);
+  }
+  else
+  {
+    Message::DefaultMessenger()->Send (TCollection_AsciiString ("Mesh ") + theFile
+                                     + "\n" + aStats
+                                     + "\n[read in " + aLoadingTimer.ElapsedTime() + " s]", Message_Info);
+  }
+  return Standard_True;
+}
+
+// =======================================================================
+// function : addShapeIntoDoc
+// purpose  :
+// =======================================================================
+Standard_Boolean RWMesh_CafReader::addShapeIntoDoc (const TopoDS_Shape& theShape,
+                                                    const TDF_Label& theLabel,
+                                                    const TCollection_AsciiString& theParentName)
+{
+  if (theShape.IsNull()
+   || myXdeDoc.IsNull())
+  {
+    return Standard_False;
+  }
+
+  Handle(XCAFDoc_ShapeTool) aShapeTool = XCAFDoc_DocumentTool::ShapeTool (myXdeDoc->Main());
+
+  TopLoc_Location aDummyLoc;
+
+  const TopAbs_ShapeEnum aShapeType = theShape.ShapeType();
+  TopoDS_Shape aShapeToAdd = theShape;
+  Standard_Boolean toMakeAssembly = Standard_False;
+  if (theShape.ShapeType() == TopAbs_COMPOUND)
+  {
+    TCollection_AsciiString aFirstName;
+    RWMesh_NodeAttributes aSubFaceAttribs;
+    for (TopoDS_Iterator aSubShapeIter (theShape, Standard_True, Standard_False); !toMakeAssembly && aSubShapeIter.More(); aSubShapeIter.Next())
+    {
+      if (aSubShapeIter.Value().ShapeType() != TopAbs_FACE)
+      {
+        toMakeAssembly = Standard_True;
+        break;
+      }
+
+      const TopoDS_Face& aFace = TopoDS::Face (aSubShapeIter.Value());
+      toMakeAssembly = toMakeAssembly
+                    || (myAttribMap.Find (aFace, aSubFaceAttribs) && !aSubFaceAttribs.Name.IsEmpty());
+    }
+
+    // create empty compound to add as assembly
+    if (toMakeAssembly)
+    {
+      TopoDS_Compound aCompound;
+      BRep_Builder aBuilder;
+      aBuilder.MakeCompound (aCompound);
+      aCompound.Location (theShape.Location());
+      aShapeToAdd = aCompound;
+    }
+  }
+
+  TDF_Label aNewLabel;
+  if (theLabel.IsNull())
+  {
+    // add new shape
+    aNewLabel = aShapeTool->AddShape (aShapeToAdd, toMakeAssembly);
+  }
+  else if (aShapeTool->IsAssembly (theLabel))
+  {
+    // add shape as component
+    aNewLabel = aShapeTool->AddComponent (theLabel, aShapeToAdd, toMakeAssembly);
+  }
+  else
+  {
+    // add shape as sub-shape
+    aNewLabel = aShapeTool->AddSubShape (theLabel, theShape);
+    if (!aNewLabel.IsNull())
+    {
+      Handle(XCAFDoc_ShapeMapTool) aShapeMapTool = XCAFDoc_ShapeMapTool::Set (aNewLabel);
+      aShapeMapTool->SetShape (theShape);
+    }
+  }
+  if (aNewLabel.IsNull())
+  {
+    return Standard_False;
+  }
+
+  // if new label is a reference get referred shape
+  TDF_Label aNewRefLabel = aNewLabel;
+  aShapeTool->GetReferredShape (aNewLabel, aNewRefLabel);
+
+  // store name
+  RWMesh_NodeAttributes aShapeAttribs;
+  myAttribMap.Find (theShape, aShapeAttribs);
+  if (aShapeAttribs.Name.IsEmpty())
+  {
+    if (theLabel.IsNull())
+    {
+      aShapeAttribs.Name = theParentName;
+    }
+    if (aShapeAttribs.Name.IsEmpty()
+    && !theLabel.IsNull())
+    {
+      aShapeAttribs.Name = shapeTypeToString (aShapeType);
+    }
+  }
+  if (!aShapeAttribs.Name.IsEmpty())
+  {
+    TDataStd_Name::Set (aNewRefLabel, aShapeAttribs.Name);
+  }
+
+  // store color
+  Handle(XCAFDoc_ColorTool) aColorTool = XCAFDoc_DocumentTool::ColorTool (myXdeDoc->Main());
+  if (aShapeAttribs.Style.IsSetColorSurf())
+  {
+    aColorTool->SetColor (aNewRefLabel, aShapeAttribs.Style.GetColorSurfRGBA(), XCAFDoc_ColorSurf);
+  }
+  if (aShapeAttribs.Style.IsSetColorCurv())
+  {
+    aColorTool->SetColor (aNewRefLabel, aShapeAttribs.Style.GetColorCurv(), XCAFDoc_ColorCurv);
+  }
+
+  // store sub-shapes (iterator is set to ignore Location)
+  TCollection_AsciiString aDummyName;
+  for (TopoDS_Iterator aSubShapeIter (theShape, Standard_True, Standard_False); aSubShapeIter.More(); aSubShapeIter.Next())
+  {
+    addShapeIntoDoc (aSubShapeIter.Value(), aNewRefLabel, aDummyName);
+  }
+  return Standard_True;
+}
+
+// =======================================================================
+// function : generateNames
+// purpose  :
+// =======================================================================
+void RWMesh_CafReader::generateNames (const TCollection_AsciiString& theFile,
+                                      const Standard_Integer theRootLower,
+                                      const Standard_Boolean theWithSubLabels)
+{
+  if (myXdeDoc.IsNull())
+  {
+    return;
+  }
+
+  TCollection_AsciiString aDummyFolder, aFileName;
+  OSD_Path::FolderAndFileFromPath (theFile, aDummyFolder, aFileName);
+  const TCollection_AsciiString aRootName = myRootPrefix + aFileName;
+
+  Handle(XCAFDoc_ShapeTool) aShapeTool = XCAFDoc_DocumentTool::ShapeTool (myXdeDoc->Main());
+  TDF_LabelSequence aRootLabels;
+  aShapeTool->GetFreeShapes (aRootLabels);
+  if (aRootLabels.Upper() < theRootLower)
+  {
+    return;
+  }
+
+  // replace empty names
+  Handle(TDataStd_Name) aNodeName;
+  Standard_Integer aRootIndex = aRootLabels.Lower();
+  TDF_LabelSequence aNewRootLabels;
+  for (TDF_LabelSequence::Iterator aRootIter (aRootLabels); aRootIter.More(); ++aRootIndex, aRootIter.Next())
+  {
+    if (aRootIndex < theRootLower)
+    {
+      continue;
+    }
+    else if (theWithSubLabels)
+    {
+      aNewRootLabels.Append (aRootIter.Value());
+    }
+
+    const TDF_Label aLabel = aRootIter.Value();
+    TDF_Label aRefLab = aLabel;
+    XCAFDoc_ShapeTool::GetReferredShape (aLabel, aRefLab);
+    if (!aRefLab.FindAttribute (TDataStd_Name::GetID(), aNodeName))
+    {
+      TDataStd_Name::Set (aRefLab, aRootName);
+    }
+    if (aLabel != aRefLab
+    && !aLabel.FindAttribute (TDataStd_Name::GetID(), aNodeName))
+    {
+      TDataStd_Name::Set (aLabel, aRootName);
+    }
+  }
+
+  if (theWithSubLabels)
+  {
+    for (XCAFPrs_DocumentExplorer aDocIter (myXdeDoc,
+                                            aNewRootLabels,
+                                            XCAFPrs_DocumentExplorerFlags_OnlyLeafNodes | XCAFPrs_DocumentExplorerFlags_NoStyle);
+         aDocIter.More(); aDocIter.Next())
+    {
+      if (aDocIter.CurrentDepth() == 0
+       || aDocIter.Current().RefLabel.FindAttribute (TDataStd_Name::GetID(), aNodeName))
+      {
+        continue;
+      }
+
+      const TopoDS_Shape aShape = XCAFDoc_ShapeTool::GetShape (aDocIter.Current().RefLabel);
+      if (!aShape.IsNull())
+      {
+        TDataStd_Name::Set (aDocIter.Current().RefLabel, shapeTypeToString (aShape.ShapeType()));
+      }
+    }
+  }
+}
diff --git a/src/RWMesh/RWMesh_CafReader.hxx b/src/RWMesh/RWMesh_CafReader.hxx
new file mode 100644 (file)
index 0000000..ef5b58b
--- /dev/null
@@ -0,0 +1,228 @@
+// Author: Kirill Gavrilov
+// Copyright (c) 2016-2019 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _RWMesh_CafReader_HeaderFile
+#define _RWMesh_CafReader_HeaderFile
+
+#include <NCollection_IndexedMap.hxx>
+#include <RWMesh_CoordinateSystemConverter.hxx>
+#include <RWMesh_NodeAttributes.hxx>
+#include <TColStd_IndexedDataMapOfStringString.hxx>
+#include <TDF_Label.hxx>
+#include <TopTools_SequenceOfShape.hxx>
+
+class Message_ProgressIndicator;
+class TDocStd_Document;
+
+//! Extended status bits.
+enum RWMesh_CafReaderStatusEx
+{
+  RWMesh_CafReaderStatusEx_NONE    = 0,    //!< empty status
+  RWMesh_CafReaderStatusEx_Partial = 0x01, //!< partial read (due to unexpected EOF, syntax error, memory limit)
+};
+
+//! The general interface for importing mesh data into XDE document.
+//!
+//! The tool implements auxiliary structures for creating an XDE document in two steps:
+//! 1) Creating TopoDS_Shape hierarchy (myRootShapes)
+//!    and Shape attributes (myAttribMap) separately within performMesh().
+//!    Attributes include names and styles.
+//! 2) Filling XDE document from these auxiliary structures.
+//!    Named elements are expanded within document structure, while Compounds having no named children will remain collapsed.
+//!    In addition, unnamed nodes can be filled with generated names like "Face", "Compound" via generateNames() method,
+//!    and the very root unnamed node can be filled from file name like "MyModel.obj".
+class RWMesh_CafReader : public Standard_Transient
+{
+  DEFINE_STANDARD_RTTIEXT(RWMesh_CafReader, Standard_Transient)
+public:
+
+  //! Empty constructor.
+  Standard_EXPORT RWMesh_CafReader();
+
+  //! Destructor.
+  Standard_EXPORT virtual ~RWMesh_CafReader();
+
+  //! Return target document.
+  const Handle(TDocStd_Document)& Document() const { return myXdeDoc; }
+
+  //! Set target document.
+  void SetDocument (const Handle(TDocStd_Document)& theDoc) { myXdeDoc = theDoc; }
+
+  //! Return prefix for generating root labels names.
+  const TCollection_AsciiString& RootPrefix() const { return myRootPrefix; }
+
+  //! Set prefix for generating root labels names
+  void SetRootPrefix (const TCollection_AsciiString& theRootPrefix) { myRootPrefix = theRootPrefix; }
+
+  //! Flag indicating if partially read file content should be put into the XDE document, TRUE by default.
+  //!
+  //! Partial read means unexpected end of file, critical parsing syntax errors in the middle of file, or reached memory limit
+  //! indicated by performMesh() returning FALSE.
+  //! Partial read allows importing a model even in case of formal reading failure,
+  //! so that it will be up to user to decide if processed data has any value.
+  //!
+  //! In case of partial read (performMesh() returns FALSE, but there are some data that could be put into document),
+  //! Perform() will return TRUE and result flag will have failure bit set.
+  //! @sa MemoryLimitMiB(), ExtraStatus().
+  Standard_Boolean ToFillIncompleteDocument() const { return myToFillIncomplete; }
+
+  //! Set flag allowing partially read file content to be put into the XDE document.
+  void SetFillIncompleteDocument (Standard_Boolean theToFillIncomplete) { myToFillIncomplete = theToFillIncomplete; }
+
+  //! Return memory usage limit in MiB, -1 by default which means no limit.
+  Standard_Integer MemoryLimitMiB() const { return myMemoryLimitMiB; }
+
+  //! Set memory usage limit in MiB; can be ignored by reader implementation!
+  void SetMemoryLimitMiB (Standard_Integer theLimitMiB) { myMemoryLimitMiB = theLimitMiB; }
+
+public:
+
+  //! Return coordinate system converter.
+  const RWMesh_CoordinateSystemConverter& CoordinateSystemConverter() const { return myCoordSysConverter; }
+
+  //! Set coordinate system converter.
+  void SetCoordinateSystemConverter (const RWMesh_CoordinateSystemConverter& theConverter) { myCoordSysConverter = theConverter; }
+
+  //! Return the length unit to convert into while reading the file, defined as scale factor for m (meters);
+  //! -1.0 by default, which means that NO conversion will be applied.
+  Standard_Real SystemLengthUnit() const { return myCoordSysConverter.OutputLengthUnit(); }
+
+  //! Set system length units to convert into while reading the file, defined as scale factor for m (meters).
+  void SetSystemLengthUnit (Standard_Real theUnits) { myCoordSysConverter.SetOutputLengthUnit (theUnits); }
+
+  //! Return TRUE if system coordinate system has been defined; FALSE by default.
+  Standard_Boolean HasSystemCoordinateSystem() const { return myCoordSysConverter.HasOutputCoordinateSystem(); }
+
+  //! Return system coordinate system; UNDEFINED by default, which means that no conversion will be done.
+  const gp_Ax3& SystemCoordinateSystem() const { return myCoordSysConverter.OutputCoordinateSystem(); }
+
+  //! Set system origin coordinate system to perform conversion into during read.
+  void SetSystemCoordinateSystem (const gp_Ax3& theCS) { myCoordSysConverter.SetOutputCoordinateSystem (theCS); }
+
+  //! Set system origin coordinate system to perform conversion into during read.
+  void SetSystemCoordinateSystem (RWMesh_CoordinateSystem theCS) { myCoordSysConverter.SetOutputCoordinateSystem (theCS); }
+
+  //! Return the length unit to convert from while reading the file, defined as scale factor for m (meters).
+  //! Can be undefined (-1.0) if file format is unitless.
+  Standard_Real FileLengthUnit() const { return myCoordSysConverter.InputLengthUnit(); }
+
+  //! Set (override) file length units to convert from while reading the file, defined as scale factor for m (meters).
+  void SetFileLengthUnit (Standard_Real theUnits) { myCoordSysConverter.SetInputLengthUnit (theUnits); }
+
+  //! Return TRUE if file origin coordinate system has been defined.
+  Standard_Boolean HasFileCoordinateSystem() const { return myCoordSysConverter.HasInputCoordinateSystem(); }
+
+  //! Return file origin coordinate system; can be UNDEFINED, which means no conversion will be done.
+  const gp_Ax3& FileCoordinateSystem() const { return myCoordSysConverter.InputCoordinateSystem(); }
+
+  //! Set (override) file origin coordinate system to perform conversion during read.
+  void SetFileCoordinateSystem (const gp_Ax3& theCS) { myCoordSysConverter.SetInputCoordinateSystem (theCS); }
+
+  //! Set (override) file origin coordinate system to perform conversion during read.
+  void SetFileCoordinateSystem (RWMesh_CoordinateSystem theCS) { myCoordSysConverter.SetInputCoordinateSystem (theCS); }
+
+public:
+
+  //! Read the data from specified file.
+  //! The Document instance should be set beforehand.
+  bool Perform (const TCollection_AsciiString& theFile,
+                const Handle(Message_ProgressIndicator)& theProgress)
+  {
+    return perform (theFile, theProgress, Standard_False);
+  }
+
+  //! Return extended status flags.
+  //! @sa RWMesh_CafReaderStatusEx enumeration.
+  Standard_Integer ExtraStatus() const { return myExtraStatus; }
+
+public:
+
+  //! Return result as a single shape.
+  Standard_EXPORT TopoDS_Shape SingleShape() const;
+
+  //! Return the list of complementary files - external references (textures, data, etc.).
+  const NCollection_IndexedMap<TCollection_AsciiString>& ExternalFiles() const { return myExternalFiles; }
+
+  //! Return metadata map.
+  const TColStd_IndexedDataMapOfStringString& Metadata() const { return myMetadata; }
+
+  //! Read the header data from specified file without reading entire model.
+  //! The main purpose is collecting metadata and external references - for copying model into a new location, for example.
+  //! Can be NOT implemented (unsupported by format / reader).
+  Standard_Boolean ProbeHeader (const TCollection_AsciiString& theFile,
+                                const Handle(Message_ProgressIndicator)& theProgress = Handle(Message_ProgressIndicator)())
+  {
+    return perform (theFile, theProgress, Standard_True);
+  }
+
+protected:
+
+  //! Read the data from specified file.
+  //! Default implementation calls performMesh() and fills XDE document from collected shapes.
+  //! @param theFile    file to read
+  //! @param optional   progress indicator
+  //! @param theToProbe flag indicating that mesh data should be skipped and only basing information to be read
+  virtual Standard_Boolean perform (const TCollection_AsciiString& theFile,
+                                    const Handle(Message_ProgressIndicator)& theProgress,
+                                    const Standard_Boolean theToProbe);
+
+  //! Read the mesh from specified file - interface to be implemented by sub-classes.
+  virtual Standard_Boolean performMesh (const TCollection_AsciiString& theFile,
+                                        const Handle(Message_ProgressIndicator)& theProgress,
+                                        const Standard_Boolean theToProbe) = 0;
+
+//! @name tools for filling XDE document
+protected:
+
+  //! Append new shape into the document (recursively).
+  Standard_EXPORT Standard_Boolean addShapeIntoDoc (const TopoDS_Shape& theShape,
+                                                    const TDF_Label& theLabel,
+                                                    const TCollection_AsciiString& theParentName);
+
+  //! Generate names for root labels starting from specified index.
+  Standard_EXPORT void generateNames (const TCollection_AsciiString& theFile,
+                                      const Standard_Integer theRootLower,
+                                      const Standard_Boolean theWithSubLabels);
+
+  //! Return shape type as string.
+  //! @sa TopAbs::ShapeTypeToString()
+  static TCollection_AsciiString shapeTypeToString (TopAbs_ShapeEnum theType)
+  {
+    TCollection_AsciiString aString = TopAbs::ShapeTypeToString (theType);
+    aString.Capitalize();
+    return aString;
+  }
+
+protected:
+
+  Handle(TDocStd_Document)  myXdeDoc;            //!< target document
+
+  TColStd_IndexedDataMapOfStringString
+                            myMetadata;          //!< metadata map
+  NCollection_IndexedMap<TCollection_AsciiString>
+                            myExternalFiles;     //!< the list of complementary files - external references (textures, data, etc.)
+  TCollection_AsciiString   myRootPrefix;        //!< root folder for generating root labels names
+  TopTools_SequenceOfShape  myRootShapes;        //!< sequence of result root shapes
+  RWMesh_NodeAttributeMap   myAttribMap;         //!< map of per-shape attributes
+
+  RWMesh_CoordinateSystemConverter
+                            myCoordSysConverter; //!< coordinate system converter
+  Standard_Boolean          myToFillDoc;         //!< fill document from shape sequence
+  Standard_Boolean          myToFillIncomplete;  //!< fill the document with partially retrieved data even if reader has failed with error
+  Standard_Integer          myMemoryLimitMiB;    //!< memory usage limit
+  Standard_Integer          myExtraStatus;       //!< extra status bitmask
+
+};
+
+#endif // _RWMesh_CafReader_HeaderFile
diff --git a/src/RWMesh/RWMesh_CoordinateSystem.hxx b/src/RWMesh/RWMesh_CoordinateSystem.hxx
new file mode 100644 (file)
index 0000000..6a06967
--- /dev/null
@@ -0,0 +1,34 @@
+// Author: Kirill Gavrilov
+// Copyright: Open CASCADE 2015-2019
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _RWMesh_CoordinateSystem_HeaderFile
+#define _RWMesh_CoordinateSystem_HeaderFile
+
+//! Standard coordinate system definition.
+//! Open CASCADE does not force application using specific coordinate system,
+//! although Draw Harness and samples define +Z-up +Y-forward coordinate system for camera view manipulation.
+//! This enumeration defines two commonly used conventions - Z-up and Y-up..
+enum RWMesh_CoordinateSystem
+{
+  RWMesh_CoordinateSystem_Undefined      = -1, //!< undefined
+  RWMesh_CoordinateSystem_posYfwd_posZup =  0, //!< +YForward+Zup+Xright
+  RWMesh_CoordinateSystem_negZfwd_posYup,      //!< -ZForward+Yup+Xright
+
+  RWMesh_CoordinateSystem_Blender = RWMesh_CoordinateSystem_posYfwd_posZup, //!< coordinate system used by Blender (+YForward+Zup+Xright)
+  RWMesh_CoordinateSystem_glTF    = RWMesh_CoordinateSystem_negZfwd_posYup, //!< coordinate system used by glTF    (-ZForward+Yup+Xright)
+  RWMesh_CoordinateSystem_Zup     = RWMesh_CoordinateSystem_Blender,        //!< Z-up coordinate system (+YForward+Zup+Xright)
+  RWMesh_CoordinateSystem_Yup     = RWMesh_CoordinateSystem_glTF,           //!< Y-up coordinate system (-ZForward+Yup+Xright)
+};
+
+#endif // _RWMesh_CoordinateSystem_HeaderFile
diff --git a/src/RWMesh/RWMesh_CoordinateSystemConverter.cxx b/src/RWMesh/RWMesh_CoordinateSystemConverter.cxx
new file mode 100644 (file)
index 0000000..1978f61
--- /dev/null
@@ -0,0 +1,77 @@
+// Author: Kirill Gavrilov
+// Copyright (c) 2015-2019 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include <RWMesh_CoordinateSystemConverter.hxx>
+
+#include <gp_Quaternion.hxx>
+
+// ================================================================
+// Function : RWMesh_CoordinateSystemConverter
+// Purpose  :
+// ================================================================
+RWMesh_CoordinateSystemConverter::RWMesh_CoordinateSystemConverter()
+: myInputLengthUnit (-1.0),
+  myOutputLengthUnit(-1.0),
+  myHasInputAx3 (Standard_False),
+  myHasOutputAx3(Standard_False),
+  //
+  myUnitFactor (1),
+  myHasScale (Standard_False),
+  myIsEmpty  (Standard_True)
+{
+  //
+}
+
+// ================================================================
+// Function : Init
+// Purpose  :
+// ================================================================
+void RWMesh_CoordinateSystemConverter::Init (const gp_Ax3& theInputSystem,
+                                             Standard_Real theInputLengthUnit,
+                                             const gp_Ax3& theOutputSystem,
+                                             Standard_Real theOutputLengthUnit)
+{
+  myInputLengthUnit  = theInputLengthUnit;
+  myOutputLengthUnit = theOutputLengthUnit;
+  myInputAx3         = theInputSystem;
+  myOutputAx3        = theOutputSystem;
+  if (theInputLengthUnit  > 0.0
+   && theOutputLengthUnit > 0.0)
+  {
+    myUnitFactor = theInputLengthUnit / theOutputLengthUnit;
+    myHasScale = Abs(myUnitFactor - 1.0) > gp::Resolution();
+  }
+  else
+  {
+    myUnitFactor = 1.0;
+    myHasScale = Standard_False;
+  }
+
+  gp_Trsf aTrsf;
+  if (myHasInputAx3
+   && myHasOutputAx3)
+  {
+    aTrsf.SetTransformation (theOutputSystem, theInputSystem);
+    if (aTrsf.TranslationPart().IsEqual (gp_XYZ (0.0, 0.0, 0.0), gp::Resolution())
+     && aTrsf.GetRotation().IsEqual (gp_Quaternion()))
+    {
+      aTrsf = gp_Trsf();
+    }
+  }
+
+  myTrsf    = aTrsf;
+  myTrsfInv = aTrsf.Inverted();
+  myTrsf.GetMat4 (myNormTrsf);
+  myIsEmpty = !myHasScale && myTrsf.Form() == gp_Identity;
+}
diff --git a/src/RWMesh/RWMesh_CoordinateSystemConverter.hxx b/src/RWMesh/RWMesh_CoordinateSystemConverter.hxx
new file mode 100644 (file)
index 0000000..295a770
--- /dev/null
@@ -0,0 +1,196 @@
+// Author: Kirill Gavrilov
+// Copyright: Open CASCADE 2015-2019
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _RWMesh_CoordinateSystemConverter_HeaderFile
+#define _RWMesh_CoordinateSystemConverter_HeaderFile
+
+#include <RWMesh_CoordinateSystem.hxx>
+
+#include <gp.hxx>
+#include <gp_Ax3.hxx>
+#include <gp_XYZ.hxx>
+#include <gp_Trsf.hxx>
+#include <Graphic3d_Mat4.hxx>
+#include <Graphic3d_Vec.hxx>
+
+//! Coordinate system converter defining the following tools:
+//! - Initialization for commonly used coordinate systems Z-up and Y-up.
+//! - Perform length unit conversion (scaling).
+//! - Conversion of three basic elements:
+//!   a) mesh node Positions,
+//!   b) mesh node Normals,
+//!   c) model nodes Transformations (locations).
+//!
+//! RWMesh_CoordinateSystem enumeration is used for convenient conversion between two commonly
+//! used coordinate systems, to make sure that imported model is oriented up.
+//! But gp_Ax3 can be used instead for defining a conversion between arbitrary systems (e.g. including non-zero origin).
+//!
+//! The converter requires defining explicitly both input and output systems,
+//! so that if either input or output is undefined, then conversion will be skipped.
+//! Length units conversion and coordinate system conversion are decomposed,
+//! so that application might specify no length units conversion but Y-up to Z-up coordinate system conversion.
+//!
+//! Class defines dedicated methods for parameters of input and output systems.
+//! This allows passing tool through several initialization steps,
+//! so that a reader can initialize input length units (only if file format defines such information),
+//! while application specifies output length units, and conversion will be done only when both defined.
+class RWMesh_CoordinateSystemConverter
+{
+public:
+
+  //! Return a standard coordinate system definition.
+  static gp_Ax3 StandardCoordinateSystem (RWMesh_CoordinateSystem theSys)
+  {
+    switch (theSys)
+    {
+      case RWMesh_CoordinateSystem_posYfwd_posZup: return gp_Ax3 (gp::Origin(), gp::DZ(), gp::DX());
+      case RWMesh_CoordinateSystem_negZfwd_posYup: return gp_Ax3 (gp::Origin(), gp::DY(), gp::DX());
+      case RWMesh_CoordinateSystem_Undefined: break;
+    }
+    return gp_Ax3();
+  }
+
+public:
+
+  //! Empty constructor.
+  Standard_EXPORT RWMesh_CoordinateSystemConverter();
+
+  //! Return TRUE if there is no transformation (target and current coordinates systems are same).
+  Standard_Boolean IsEmpty() const { return myIsEmpty; }
+
+  //! Return source length units, defined as scale factor to m (meters).
+  //! -1.0 by default, which means that NO conversion will be applied (regardless output length unit).
+  Standard_Real InputLengthUnit() const { return myInputLengthUnit; }
+
+  //! Set source length units as scale factor to m (meters).
+  void SetInputLengthUnit (Standard_Real theInputScale)
+  {
+    Init (myInputAx3, theInputScale, myOutputAx3, myOutputLengthUnit);
+  }
+
+  //! Return destination length units, defined as scale factor to m (meters).
+  //! -1.0 by default, which means that NO conversion will be applied (regardless input length unit).
+  Standard_Real OutputLengthUnit() const { return myOutputLengthUnit; }
+
+  //! Set destination length units as scale factor to m (meters).
+  void SetOutputLengthUnit (Standard_Real theOutputScale)
+  {
+    Init (myInputAx3, myInputLengthUnit, myOutputAx3, theOutputScale);
+  }
+
+  //! Return TRUE if source coordinate system has been set; FALSE by default.
+  Standard_Boolean HasInputCoordinateSystem() const { return myHasInputAx3; }
+
+  //! Source coordinate system; UNDEFINED by default.
+  const gp_Ax3& InputCoordinateSystem() const { return myInputAx3; }
+
+  //! Set source coordinate system.
+  void SetInputCoordinateSystem (const gp_Ax3& theSysFrom)
+  {
+    myHasInputAx3 = Standard_True;
+    Init (theSysFrom, myInputLengthUnit, myOutputAx3, myOutputLengthUnit);
+  }
+
+  //! Set source coordinate system.
+  void SetInputCoordinateSystem (RWMesh_CoordinateSystem theSysFrom)
+  {
+    myHasInputAx3 = theSysFrom != RWMesh_CoordinateSystem_Undefined;
+    Init (StandardCoordinateSystem (theSysFrom), myInputLengthUnit, myOutputAx3, myOutputLengthUnit);
+  }
+
+  //! Return TRUE if destination coordinate system has been set; FALSE by default.
+  Standard_Boolean HasOutputCoordinateSystem() const { return myHasOutputAx3; }
+
+  //! Destination coordinate system; UNDEFINED by default.
+  const gp_Ax3& OutputCoordinateSystem() const { return myOutputAx3; }
+
+  //! Set destination coordinate system.
+  void SetOutputCoordinateSystem (const gp_Ax3& theSysTo)
+  {
+    myHasOutputAx3 = Standard_True;
+    Init (myInputAx3, myInputLengthUnit, theSysTo, myOutputLengthUnit);
+  }
+
+  //! Set destination coordinate system.
+  void SetOutputCoordinateSystem (RWMesh_CoordinateSystem theSysTo)
+  {
+    myHasOutputAx3 = theSysTo != RWMesh_CoordinateSystem_Undefined;
+    Init (myInputAx3, myInputLengthUnit, StandardCoordinateSystem (theSysTo), myOutputLengthUnit);
+  }
+
+  //! Initialize transformation.
+  Standard_EXPORT void Init (const gp_Ax3& theInputSystem,
+                             Standard_Real theInputLengthUnit,
+                             const gp_Ax3& theOutputSystem,
+                             Standard_Real theOutputLengthUnit);
+
+public:
+
+  //! Transform transformation.
+  void TransformTransformation (gp_Trsf& theTrsf) const
+  {
+    if (myHasScale)
+    {
+      gp_XYZ aTransPart = theTrsf.TranslationPart();
+      aTransPart *= myUnitFactor;
+      theTrsf.SetTranslationPart (aTransPart);
+    }
+    if (myTrsf.Form() != gp_Identity)
+    {
+      theTrsf = myTrsf * theTrsf * myTrsfInv;
+    }
+  }
+
+  //! Transform position.
+  void TransformPosition (gp_XYZ& thePos) const
+  {
+    if (myHasScale)
+    {
+      thePos *= myUnitFactor;
+    }
+    if (myTrsf.Form() != gp_Identity)
+    {
+      myTrsf.Transforms (thePos);
+    }
+  }
+
+  //! Transform normal (e.g. exclude translation/scale part of transformation).
+  void TransformNormal (Graphic3d_Vec3& theNorm) const
+  {
+    if (myTrsf.Form() != gp_Identity)
+    {
+      const Graphic3d_Vec4 aNorm = myNormTrsf * Graphic3d_Vec4 (theNorm, 0.0f);
+      theNorm = aNorm.xyz();
+    }
+  }
+
+private:
+
+  gp_Ax3           myInputAx3;         //!< source      coordinate system
+  gp_Ax3           myOutputAx3;        //!< destination coordinate system
+  Standard_Real    myInputLengthUnit;  //!< source      length units, defined as scale factor to m (meters); -1.0 by default which means UNDEFINED
+  Standard_Real    myOutputLengthUnit; //!< destination length units, defined as scale factor to m (meters); -1.0 by default which means UNDEFINED
+  Standard_Boolean myHasInputAx3;      //!< flag indicating if source coordinate system is defined or not
+  Standard_Boolean myHasOutputAx3;     //!< flag indicating if destination coordinate system is defined or not
+
+  gp_Trsf          myTrsf;             //!< transformation from input Ax3 to output Ax3
+  gp_Trsf          myTrsfInv;          //!< inversed transformation from input Ax3 to output Ax3
+  Graphic3d_Mat4   myNormTrsf;         //!< transformation 4x4 matrix from input Ax3 to output Ax3
+  Standard_Real    myUnitFactor;       //!< unit scale factor
+  Standard_Boolean myHasScale;         //!< flag indicating that length unit transformation should be performed
+  Standard_Boolean myIsEmpty;          //!< flag indicating that transformation is empty
+
+};
+
+#endif // _RWMesh_CoordinateSystemConverter_HeaderFile
diff --git a/src/RWMesh/RWMesh_NodeAttributes.hxx b/src/RWMesh/RWMesh_NodeAttributes.hxx
new file mode 100644 (file)
index 0000000..adc7110
--- /dev/null
@@ -0,0 +1,32 @@
+// Author: Kirill Gavrilov
+// Copyright (c) 2016-2019 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _RWMesh_NodeAttributes_HeaderFile
+#define _RWMesh_NodeAttributes_HeaderFile
+
+#include <NCollection_DataMap.hxx>
+#include <TCollection_AsciiString.hxx>
+#include <TopTools_ShapeMapHasher.hxx>
+#include <XCAFPrs_Style.hxx>
+
+//! Attributes of the node.
+struct RWMesh_NodeAttributes
+{
+  TCollection_AsciiString Name;    //!< name for the user
+  TCollection_AsciiString RawName; //!< name within low-level format structure
+  XCAFPrs_Style           Style;   //!< presentation style
+};
+typedef NCollection_DataMap<TopoDS_Shape, RWMesh_NodeAttributes, TopTools_ShapeMapHasher> RWMesh_NodeAttributeMap;
+
+#endif // _RWMesh_NodeAttributes_HeaderFile
index 96f430f..1199f5f 100755 (executable)
@@ -76,6 +76,7 @@ Standard_PExtCharacter.hxx
 Standard_PrimitiveTypes.hxx
 Standard_ProgramError.hxx
 Standard_RangeError.hxx
+Standard_ReadBuffer.hxx
 Standard_Real.cxx
 Standard_Real.hxx
 Standard_ShortReal.cxx
index 0d2cf80..25b07d6 100644 (file)
 class Standard_OutOfRange;
 DEFINE_STANDARD_HANDLE(Standard_OutOfRange, Standard_RangeError)
 
-#if !defined No_Exception && !defined No_Standard_OutOfRange
-#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2))
+#if (defined(__GNUC__) && __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
   // suppress false-positive warnings produced by GCC optimizer
-  #define Standard_OutOfRange_Raise_if(CONDITION, MESSAGE) \
+  #define Standard_OutOfRange_Always_Raise_if(CONDITION, MESSAGE) \
   _Pragma("GCC diagnostic push") \
   _Pragma("GCC diagnostic ignored \"-Wstrict-overflow\"") \
   if (CONDITION) throw Standard_OutOfRange(MESSAGE); \
   _Pragma("GCC diagnostic pop")
 #else
-  #define Standard_OutOfRange_Raise_if(CONDITION, MESSAGE) \
+  #define Standard_OutOfRange_Always_Raise_if(CONDITION, MESSAGE) \
   if (CONDITION) throw Standard_OutOfRange(MESSAGE);
 #endif
+
+#if !defined No_Exception && !defined No_Standard_OutOfRange
+  #define Standard_OutOfRange_Raise_if(CONDITION, MESSAGE) Standard_OutOfRange_Always_Raise_if(CONDITION, MESSAGE)
 #else
   #define Standard_OutOfRange_Raise_if(CONDITION, MESSAGE)
 #endif
index 52477fb..719924c 100644 (file)
@@ -26,7 +26,7 @@ class Standard_RangeError;
 DEFINE_STANDARD_HANDLE(Standard_RangeError, Standard_DomainError)
 
 #if !defined No_Exception && !defined No_Standard_RangeError
-#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2))
+#if (defined(__GNUC__) && __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
   // suppress false-positive warnings produced by GCC optimizer
   #define Standard_RangeError_Raise_if(CONDITION, MESSAGE) \
   _Pragma("GCC diagnostic push") \
diff --git a/src/Standard/Standard_ReadBuffer.hxx b/src/Standard/Standard_ReadBuffer.hxx
new file mode 100644 (file)
index 0000000..e741e39
--- /dev/null
@@ -0,0 +1,145 @@
+// Copyright (c) 2017-2019 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement..
+
+#ifndef _Standard_ReadBuffer_HeaderFile
+#define _Standard_ReadBuffer_HeaderFile
+
+#include <Standard_ProgramError.hxx>
+
+#include <iostream>
+
+//! Auxiliary tool for buffered reading from input stream within chunks of constant size.
+class Standard_ReadBuffer
+{
+public:
+
+  //! Constructor with initialization.
+  Standard_ReadBuffer (int64_t theDataLen,
+                       size_t  theChunkLen)
+  : myBufferPtr(NULL),
+    myBufferEnd(NULL),
+    myDataLen  (0),
+    myDataRead (0),
+    myChunkLen (0),
+    myNbChunks (0),
+    myBufferLen(0)
+  {
+    Init (theDataLen, theChunkLen);
+  }
+
+  //! Initialize the buffer.
+  //! @param theDataLen  the full length of input data to read from stream.
+  //! @param theChunkLen the length of single chunk to read
+  void Init (int64_t theDataLen,
+             size_t  theChunkLen)
+  {
+    myDataRead  = 0;
+    myDataLen   = theDataLen - theDataLen % int64_t(theChunkLen);
+    myChunkLen  = theChunkLen;
+    myNbChunks  = sizeof(myBuffer) / theChunkLen;
+    myBufferLen = theChunkLen * myNbChunks;
+
+    myBufferEnd = myBuffer + sizeof(myBuffer);
+    myBufferPtr = myBuffer + sizeof(myBuffer);
+    memset (myBuffer, 0, sizeof(myBuffer));
+
+    if (theChunkLen > sizeof(myBuffer))
+    {
+      Standard_ProgramError::Raise ("Internal error - chunk size is greater then preallocated buffer");
+    }
+  }
+
+  //! Return TRUE if amount of read bytes is equal to requested length of entire data.
+  bool IsDone() const
+  {
+    return myDataRead == myDataLen;
+  }
+
+  //! Read next chunk.
+  //! @return pointer to the chunk or NULL on error / end of reading buffer
+  template<typename Chunk_T, typename Stream_T>
+  Chunk_T* ReadChunk (Stream_T& theStream)
+  {
+    return reinterpret_cast<Chunk_T*> (readRawDataChunk (theStream));
+  }
+
+  //! Read next chunk.
+  //! @return pointer to the chunk or NULL on error / end of reading buffer
+  template<typename Stream_T>
+  char* ReadDataChunk (Stream_T& theStream)
+  {
+    return readRawDataChunk (theStream);
+  }
+
+private:
+
+  //! Read next chunk.
+  //! @return pointer to the chunk or NULL on error / end of reading buffer
+  template<typename Stream_T>
+  char* readRawDataChunk (Stream_T& theStream)
+  {
+    myBufferPtr += myChunkLen;
+    if (myBufferPtr < myBufferEnd)
+    {
+      return myBufferPtr;
+    }
+
+    const int64_t aDataLeft = myDataLen - myDataRead;
+    if (aDataLeft == 0) // myDataLen should be multiple of myChunkLen
+    {
+      myBufferPtr = NULL;
+      return NULL;
+    }
+
+    const size_t aDataToRead = int64_t(myBufferLen) > aDataLeft ? size_t(aDataLeft) : myBufferLen;
+    if (!readStream (theStream, aDataToRead))
+    {
+      myBufferPtr = NULL;
+      return NULL;
+    }
+
+    myBufferPtr = myBuffer;
+    myBufferEnd = myBuffer + aDataToRead;
+    myDataRead += aDataToRead;
+    return myBufferPtr;
+  }
+
+  //! Read from stl stream.
+  bool readStream (std::istream& theStream,
+                   size_t theLen)
+  {
+    theStream.read (myBuffer, theLen);
+    return theStream.good();
+  }
+
+  //! Read from FILE stream.
+  bool readStream (FILE* theStream,
+                   size_t theLen)
+  {
+    return ::fread (myBuffer, 1, theLen, theStream) == theLen;
+  }
+
+private:
+
+  char        myBuffer[4096]; //!< data cache
+  char*       myBufferPtr;    //!< current position within the buffer
+  const char* myBufferEnd;    //!< end of the buffer
+  int64_t     myDataLen;      //!< length of entire data to read
+  int64_t     myDataRead;     //!< amount of data already processed
+  size_t      myChunkLen;     //!< length of single chunk that caller would like to read (e.g. iterator increment)
+  size_t      myNbChunks;     //!< number of cached chunks
+  size_t      myBufferLen;    //!< effective length of the buffer to be read at once (multiple of chunk length)
+
+};
+
+#endif // _Standard_ReadBuffer_HeaderFile
index 6cc80dc..2eef2fd 100644 (file)
@@ -114,9 +114,9 @@ inline Standard_Boolean TCollection_AsciiString::IsEqual(const TCollection_Ascii
 inline TCollection_AsciiString TCollection_AsciiString::SubString(const Standard_Integer FromIndex,
                                                           const Standard_Integer ToIndex) const
 {
-
-  if (ToIndex > mylength || FromIndex <= 0 || FromIndex > ToIndex ) throw Standard_OutOfRange();
-
+  // note the we are doing here weird casts just to suppress annoying and meaningless warning -Wstrict-overflow
+  Standard_OutOfRange_Always_Raise_if(FromIndex <= 0 || ToIndex <= 0 || (unsigned int)ToIndex > (unsigned int)mylength || (unsigned int)FromIndex > (unsigned int)ToIndex,
+                                      "TCollection_AsciiString::SubString() out of range");
   return TCollection_AsciiString( &mystring [ FromIndex - 1 ] ,
                                   ToIndex - FromIndex + 1 ) ;
 }
diff --git a/src/TKRWMesh/CMakeLists.txt b/src/TKRWMesh/CMakeLists.txt
new file mode 100644 (file)
index 0000000..3c70004
--- /dev/null
@@ -0,0 +1,3 @@
+project(TKRWMesh)
+
+OCCT_INCLUDE_CMAKE_FILE (adm/cmake/occt_toolkit)
diff --git a/src/TKRWMesh/EXTERNLIB b/src/TKRWMesh/EXTERNLIB
new file mode 100644 (file)
index 0000000..ab12f64
--- /dev/null
@@ -0,0 +1,9 @@
+TKernel
+TKMath
+TKMesh
+TKXCAF
+TKLCAF
+TKV3d
+TKBRep
+TKG3d
+TKService
diff --git a/src/TKRWMesh/FILES b/src/TKRWMesh/FILES
new file mode 100644 (file)
index 0000000..ca4f0e5
--- /dev/null
@@ -0,0 +1,2 @@
+EXTERNLIB
+PACKAGES
diff --git a/src/TKRWMesh/PACKAGES b/src/TKRWMesh/PACKAGES
new file mode 100644 (file)
index 0000000..9330e3e
--- /dev/null
@@ -0,0 +1 @@
+RWMesh
index 77584f8..f72b131 100755 (executable)
@@ -19,3 +19,5 @@ TKIGES
 TKSTL
 TKVRML
 TKLCAF
+TKDCAF
+TKRWMesh
index fb3a4f5..a586abe 100644 (file)
 #ifndef _V3d_TypeOfOrientation_HeaderFile
 #define _V3d_TypeOfOrientation_HeaderFile
 
-//! Determines the type of orientation.
+//! Determines the type of orientation as a combination of standard DX/DY/DZ directions.
+//! This enumeration defines a model orientation looking towards the user's eye, which is an opposition to Camera main direction.
+//! For example, V3d_Xneg defines +X Camera main direction.
+//!
+//! This enumeration defines only main Camera direction, so that the Camera up direction should be defined elsewhere for unambiguous Camera definition.
+//! Open CASCADE does not force application using specific coordinate system, although Draw Harness and samples define +Z-up +Y-forward coordinate system for camera view manipulation.
+//! Therefore, this enumeration also defines V3d_TypeOfOrientation_Zup_* aliases defining front/back/left/top camera orientations for +Z-up convention
+//! as well as V3d_TypeOfOrientation_Yup_* aliases for another commonly used in other systems +Y-up convention.
+//! Applications using other coordinate system can define their own enumeration, when found suitable.
 enum V3d_TypeOfOrientation
 {
-V3d_Xpos,
-V3d_Ypos,
-V3d_Zpos,
-V3d_Xneg,
-V3d_Yneg,
-V3d_Zneg,
-V3d_XposYpos,
-V3d_XposZpos,
-V3d_YposZpos,
-V3d_XnegYneg,
-V3d_XnegYpos,
-V3d_XnegZneg,
-V3d_XnegZpos,
-V3d_YnegZneg,
-V3d_YnegZpos,
-V3d_XposYneg,
-V3d_XposZneg,
-V3d_YposZneg,
-V3d_XposYposZpos,
-V3d_XposYnegZpos,
-V3d_XposYposZneg,
-V3d_XnegYposZpos,
-V3d_XposYnegZneg,
-V3d_XnegYposZneg,
-V3d_XnegYnegZpos,
-V3d_XnegYnegZneg
+  V3d_Xpos, //!< (+Y+Z) view
+  V3d_Ypos, //!< (-X+Z) view
+  V3d_Zpos, //!< (+X+Y) view
+  V3d_Xneg, //!< (-Y+Z) view
+  V3d_Yneg, //!< (+X+Z) view
+  V3d_Zneg, //!< (+X-Y) view
+
+  V3d_XposYpos,
+  V3d_XposZpos,
+  V3d_YposZpos,
+  V3d_XnegYneg,
+  V3d_XnegYpos,
+  V3d_XnegZneg,
+  V3d_XnegZpos,
+  V3d_YnegZneg,
+  V3d_YnegZpos,
+  V3d_XposYneg,
+  V3d_XposZneg,
+  V3d_YposZneg,
+  V3d_XposYposZpos,
+  V3d_XposYnegZpos,
+  V3d_XposYposZneg,
+  V3d_XnegYposZpos,
+  V3d_XposYnegZneg,
+  V3d_XnegYposZneg,
+  V3d_XnegYnegZpos,
+  V3d_XnegYnegZneg,
+
+  // +Z-up +Y-forward convention
+  V3d_TypeOfOrientation_Zup_AxoLeft  = V3d_XnegYnegZpos,  //!< +Z-up +Y-forward Left +Front+Top
+  V3d_TypeOfOrientation_Zup_AxoRight = V3d_XposYnegZpos,  //!< +Z-up +Y-forward Right+Front+Top
+  V3d_TypeOfOrientation_Zup_Front    = V3d_Yneg,          //!< +Z-up +Y-forward Front  (+X+Z) view
+  V3d_TypeOfOrientation_Zup_Back     = V3d_Ypos,          //!< +Z-up +Y-forward Back   (-X+Z) view
+  V3d_TypeOfOrientation_Zup_Top      = V3d_Zpos,          //!< +Z-up +Y-forward Top    (+X+Y) view
+  V3d_TypeOfOrientation_Zup_Bottom   = V3d_Zneg,          //!< +Z-up +Y-forward Bottom (+X-Y) view
+  V3d_TypeOfOrientation_Zup_Left     = V3d_Xneg,          //!< +Z-up +Y-forward Left   (-Y+Z) view
+  V3d_TypeOfOrientation_Zup_Right    = V3d_Xpos,          //!< +Z-up +Y-forward Right  (+Y+Z) view
+
+  // +Y-up -Z-forward convention
+  V3d_TypeOfOrientation_Yup_AxoLeft  = V3d_XnegYposZpos,  //!< +Y-up -Z-forward Left +Front+Top
+  V3d_TypeOfOrientation_Yup_AxoRight = V3d_XposYposZpos,  //!< +Y-up -Z-forward Right+Front+Top
+  V3d_TypeOfOrientation_Yup_Front    = V3d_Zpos,          //!< +Y-up -Z-forward Front  (+X+Y) view
+  V3d_TypeOfOrientation_Yup_Back     = V3d_Zneg,          //!< +Y-up -Z-forward Back   (-X+Y) view
+  V3d_TypeOfOrientation_Yup_Top      = V3d_Ypos,          //!< +Y-up -Z-forward Top    (+X-Z) view
+  V3d_TypeOfOrientation_Yup_Bottom   = V3d_Yneg,          //!< +Y-up -Z-forward Bottom (-X-Z) view
+  V3d_TypeOfOrientation_Yup_Left     = V3d_Xpos,          //!< +Y-up -Z-forward Left   (-Z+Y) view
+  V3d_TypeOfOrientation_Yup_Right    = V3d_Xneg,          //!< +Y-up -Z-forward Right  (+Z+Y) view
 };
 
 #endif // _V3d_TypeOfOrientation_HeaderFile
index af34004..b67123a 100644 (file)
@@ -1019,38 +1019,42 @@ void V3d_View::SetProj( const Standard_Real Vx,const Standard_Real Vy, const Sta
 //function : SetProj
 //purpose  :
 //=============================================================================
-void V3d_View::SetProj( const V3d_TypeOfOrientation Orientation )
+void V3d_View::SetProj (const V3d_TypeOfOrientation theOrientation,
+                        const Standard_Boolean theIsYup)
 {
-  Standard_Real Xpn=0;
-  Standard_Real Ypn=0;
-  Standard_Real Zpn=0;
-
-  switch (Orientation) {
-  case V3d_Zpos :
-    Ypn = 1.;
-    break;
-  case V3d_Zneg :
-    Ypn = -1.;
-    break;
-  default:
-    Zpn = 1.;
+  Graphic3d_Vec3d anUp = theIsYup ? Graphic3d_Vec3d (0.0, 1.0, 0.0) : Graphic3d_Vec3d (0.0, 0.0, 1.0);
+  if (theIsYup)
+  {
+    if (theOrientation == V3d_Ypos
+     || theOrientation == V3d_Yneg)
+    {
+      anUp.SetValues (0.0, 0.0, -1.0);
+    }
+  }
+  else
+  {
+    if (theOrientation == V3d_Zpos)
+    {
+      anUp.SetValues (0.0, 1.0, 0.0);
+    }
+    else if (theOrientation == V3d_Zneg)
+    {
+      anUp.SetValues (0.0, -1.0, 0.0);
+    }
   }
 
-  const gp_Dir aBck = V3d::GetProjAxis (Orientation);
+  const gp_Dir aBck = V3d::GetProjAxis (theOrientation);
 
   // retain camera panning from origin when switching projection
-  Handle(Graphic3d_Camera) aCamera = Camera();
-
-  gp_Pnt anOriginVCS  = aCamera->ConvertWorld2View (gp::Origin());
-  Standard_Real aPanX = anOriginVCS.X();
-  Standard_Real aPanY = anOriginVCS.Y();
+  const Handle(Graphic3d_Camera)& aCamera = Camera();
+  const gp_Pnt anOriginVCS = aCamera->ConvertWorld2View (gp::Origin());
 
   aCamera->SetCenter (gp_Pnt (0, 0, 0));
   aCamera->SetDirection (gp_Dir (aBck.X(), aBck.Y(), aBck.Z()).Reversed());
-  aCamera->SetUp (gp_Dir (Xpn, Ypn, Zpn));
+  aCamera->SetUp (gp_Dir (anUp.x(), anUp.y(), anUp.z()));
   aCamera->OrthogonalizeUp();
 
-  Panning (aPanX, aPanY);
+  Panning (anOriginVCS.X(), anOriginVCS.Y());
 
   AutoZFit();
 
index 9249d97..d1e6129 100644 (file)
@@ -410,7 +410,10 @@ public:
   Standard_EXPORT void SetProj (const Standard_Real Vx, const Standard_Real Vy, const Standard_Real Vz);
 
   //! Defines the orientation of the projection .
-  Standard_EXPORT void SetProj (const V3d_TypeOfOrientation Orientation);
+  //! @param theOrientation camera direction
+  //! @param theIsYup       flag indicating Y-up (TRUE) or Z-up (FALSE) convention
+  Standard_EXPORT void SetProj (const V3d_TypeOfOrientation theOrientation,
+                                const Standard_Boolean theIsYup = Standard_False);
 
   //! Defines the position of the view point.
   Standard_EXPORT void SetAt (const Standard_Real X, const Standard_Real Y, const Standard_Real Z);
index 465947f..523c03b 100644 (file)
@@ -3180,97 +3180,307 @@ void ViewerTest::GetMousePosition(Standard_Integer& Xpix,Standard_Integer& Ypix)
 }
 
 //==============================================================================
-//function : ViewProject: implements VAxo, VTop, VLeft, ...
-//purpose  : Switches to an axonometric, top, left and other views
+//function : VViewProj
+//purpose  : Switch view projection
 //==============================================================================
-
-static int ViewProject(Draw_Interpretor& di, const V3d_TypeOfOrientation ori)
+static int VViewProj (Draw_Interpretor& ,
+                      Standard_Integer theNbArgs,
+                      const char** theArgVec)
 {
-  if ( ViewerTest::CurrentView().IsNull() )
+  static Standard_Boolean isYup = Standard_False;
+  const Handle(V3d_View)& aView = ViewerTest::CurrentView();
+  if (aView.IsNull())
   {
-    di<<"Call vinit before this command, please\n";
+    std::cout << "Error: no active view\n";
     return 1;
   }
 
-  ViewerTest::CurrentView()->SetProj(ori);
-  return 0;
-}
-
-//==============================================================================
-//function : VAxo
-//purpose  : Switch to an Axonometric view
-//Draw arg : No args
-//==============================================================================
-
-static int VAxo(Draw_Interpretor& di, Standard_Integer , const char** )
-{
-  return ViewProject(di, V3d_XposYnegZpos);
-}
-
-//==============================================================================
-//function : VTop
-//purpose  : Switch to a Top View
-//Draw arg : No args
-//==============================================================================
-
-static int VTop(Draw_Interpretor& di, Standard_Integer , const char** )
-{
-  return ViewProject(di, V3d_Zpos);
-}
-
-//==============================================================================
-//function : VBottom
-//purpose  : Switch to a Bottom View
-//Draw arg : No args
-//==============================================================================
-
-static int VBottom(Draw_Interpretor& di, Standard_Integer , const char** )
-{
-  return ViewProject(di, V3d_Zneg);
-}
-
-//==============================================================================
-//function : VLeft
-//purpose  : Switch to a Left View
-//Draw arg : No args
-//==============================================================================
-
-static int VLeft(Draw_Interpretor& di, Standard_Integer , const char** )
-{
-  return ViewProject(di, V3d_Xneg);
-}
-
-//==============================================================================
-//function : VRight
-//purpose  : Switch to a Right View
-//Draw arg : No args
-//==============================================================================
+  TCollection_AsciiString aCmdName (theArgVec[0]);
+  Standard_Boolean isGeneralCmd = Standard_False;
+  if (aCmdName == "vfront")
+  {
+    aView->SetProj (isYup ? V3d_TypeOfOrientation_Yup_Front : V3d_TypeOfOrientation_Zup_Front, isYup);
+  }
+  else if (aCmdName == "vback")
+  {
+    aView->SetProj (isYup ? V3d_TypeOfOrientation_Yup_Back : V3d_TypeOfOrientation_Zup_Back, isYup);
+  }
+  else if (aCmdName == "vtop")
+  {
+    aView->SetProj (isYup ? V3d_TypeOfOrientation_Yup_Top : V3d_TypeOfOrientation_Zup_Top, isYup);
+  }
+  else if (aCmdName == "vbottom")
+  {
+    aView->SetProj (isYup ? V3d_TypeOfOrientation_Yup_Bottom : V3d_TypeOfOrientation_Zup_Bottom, isYup);
+  }
+  else if (aCmdName == "vleft")
+  {
+    aView->SetProj (isYup ? V3d_TypeOfOrientation_Yup_Left : V3d_TypeOfOrientation_Zup_Left, isYup);
+  }
+  else if (aCmdName == "vright")
+  {
+    aView->SetProj (isYup ? V3d_TypeOfOrientation_Yup_Right : V3d_TypeOfOrientation_Zup_Right, isYup);
+  }
+  else if (aCmdName == "vaxo")
+  {
+    aView->SetProj (isYup ? V3d_TypeOfOrientation_Yup_AxoRight : V3d_TypeOfOrientation_Zup_AxoRight, isYup);
+  }
+  else
+  {
+    isGeneralCmd = Standard_True;
+    for (Standard_Integer anArgIter = 1; anArgIter < theNbArgs; ++anArgIter)
+    {
+      TCollection_AsciiString anArgCase (theArgVec[anArgIter]);
+      anArgCase.LowerCase();
+      if (anArgCase == "-zup")
+      {
+        isYup = Standard_False;
+      }
+      else if (anArgCase == "-yup")
+      {
+        isYup = Standard_True;
+      }
+      else if (anArgCase == "-front"
+            || anArgCase == "front"
+            || anArgCase == "-f"
+            || anArgCase == "f")
+      {
+        aView->SetProj (isYup ? V3d_TypeOfOrientation_Yup_Front : V3d_TypeOfOrientation_Zup_Front, isYup);
+      }
+      else if (anArgCase == "-back"
+            || anArgCase == "back"
+            || anArgCase == "-b"
+            || anArgCase == "b")
+      {
+        aView->SetProj (isYup ? V3d_TypeOfOrientation_Yup_Back : V3d_TypeOfOrientation_Zup_Back, isYup);
+      }
+      else if (anArgCase == "-top"
+            || anArgCase == "top"
+            || anArgCase == "-t"
+            || anArgCase == "t")
+      {
+        aView->SetProj (isYup ? V3d_TypeOfOrientation_Yup_Top : V3d_TypeOfOrientation_Zup_Top, isYup);
+      }
+      else if (anArgCase == "-bottom"
+            || anArgCase == "bottom"
+            || anArgCase == "-bot"
+            || anArgCase == "bot"
+            || anArgCase == "-b"
+            || anArgCase == "b")
+      {
+        aView->SetProj (isYup ? V3d_TypeOfOrientation_Yup_Bottom : V3d_TypeOfOrientation_Zup_Bottom, isYup);
+      }
+      else if (anArgCase == "-left"
+            || anArgCase == "left"
+            || anArgCase == "-l"
+            || anArgCase == "l")
+      {
+        aView->SetProj (isYup ? V3d_TypeOfOrientation_Yup_Left : V3d_TypeOfOrientation_Zup_Left, isYup);
+      }
+      else if (anArgCase == "-right"
+            || anArgCase == "right"
+            || anArgCase == "-r"
+            || anArgCase == "r")
+      {
+        aView->SetProj (isYup ? V3d_TypeOfOrientation_Yup_Right : V3d_TypeOfOrientation_Zup_Right, isYup);
+      }
+      else if (anArgCase == "-axoleft"
+            || anArgCase == "-leftaxo"
+            || anArgCase == "axoleft"
+            || anArgCase == "leftaxo")
+      {
+        aView->SetProj (isYup ? V3d_TypeOfOrientation_Yup_AxoLeft : V3d_TypeOfOrientation_Zup_AxoLeft, isYup);
+      }
+      else if (anArgCase == "-axo"
+            || anArgCase == "axo"
+            || anArgCase == "-a"
+            || anArgCase == "a"
+            || anArgCase == "-axoright"
+            || anArgCase == "-rightaxo"
+            || anArgCase == "axoright"
+            || anArgCase == "rightaxo")
+      {
+        aView->SetProj (isYup ? V3d_TypeOfOrientation_Yup_AxoRight : V3d_TypeOfOrientation_Zup_AxoRight, isYup);
+      }
+      else if (anArgCase == "+x")
+      {
+        aView->SetProj (V3d_Xpos, isYup);
+      }
+      else if (anArgCase == "-x")
+      {
+        aView->SetProj (V3d_Xneg, isYup);
+      }
+      else if (anArgCase == "+y")
+      {
+        aView->SetProj (V3d_Ypos, isYup);
+      }
+      else if (anArgCase == "-y")
+      {
+        aView->SetProj (V3d_Yneg, isYup);
+      }
+      else if (anArgCase == "+z")
+      {
+        aView->SetProj (V3d_Zpos, isYup);
+      }
+      else if (anArgCase == "-z")
+      {
+        aView->SetProj (V3d_Zneg, isYup);
+      }
+      else if (anArgCase == "+x+y+z")
+      {
+        aView->SetProj (V3d_XposYposZpos, isYup);
+      }
+      else if (anArgCase == "+x+y-z")
+      {
+        aView->SetProj (V3d_XposYposZneg, isYup);
+      }
+      else if (anArgCase == "+x-y+z")
+      {
+        aView->SetProj (V3d_XposYnegZpos, isYup);
+      }
+      else if (anArgCase == "+x-y-z")
+      {
+        aView->SetProj (V3d_XposYnegZneg, isYup);
+      }
+      else if (anArgCase == "-x+y+z")
+      {
+        aView->SetProj (V3d_XnegYposZpos, isYup);
+      }
+      else if (anArgCase == "-x+y-z")
+      {
+        aView->SetProj (V3d_XnegYposZneg, isYup);
+      }
+      else if (anArgCase == "-x-y+z")
+      {
+        aView->SetProj (V3d_XnegYnegZpos, isYup);
+      }
+      else if (anArgCase == "-x-y-z")
+      {
+        aView->SetProj (V3d_XnegYnegZneg, isYup);
+      }
+      else if (anArgCase == "+x+y")
+      {
+        aView->SetProj (V3d_XposYpos, isYup);
+      }
+      else if (anArgCase == "+x-y")
+      {
+        aView->SetProj (V3d_XposYneg, isYup);
+      }
+      else if (anArgCase == "-x+y")
+      {
+        aView->SetProj (V3d_XnegYpos, isYup);
+      }
+      else if (anArgCase == "-x-y")
+      {
+        aView->SetProj (V3d_XnegYneg, isYup);
+      }
+      else if (anArgCase == "+x+z")
+      {
+        aView->SetProj (V3d_XposZpos, isYup);
+      }
+      else if (anArgCase == "+x-z")
+      {
+        aView->SetProj (V3d_XposZneg, isYup);
+      }
+      else if (anArgCase == "-x+z")
+      {
+        aView->SetProj (V3d_XnegZpos, isYup);
+      }
+      else if (anArgCase == "-x-z")
+      {
+        aView->SetProj (V3d_XnegZneg, isYup);
+      }
+      else if (anArgCase == "+y+z")
+      {
+        aView->SetProj (V3d_YposZpos, isYup);
+      }
+      else if (anArgCase == "+y-z")
+      {
+        aView->SetProj (V3d_YposZneg, isYup);
+      }
+      else if (anArgCase == "-y+z")
+      {
+        aView->SetProj (V3d_YnegZpos, isYup);
+      }
+      else if (anArgCase == "-y-z")
+      {
+        aView->SetProj (V3d_YnegZneg, isYup);
+      }
+      else if (anArgIter + 1 < theNbArgs
+            && anArgCase == "-frame"
+            && TCollection_AsciiString (theArgVec[anArgIter + 1]).Length() == 4)
+      {
+        TCollection_AsciiString aFrameDef (theArgVec[++anArgIter]);
+        aFrameDef.LowerCase();
+        gp_Dir aRight, anUp;
+        if (aFrameDef.Value (2) == aFrameDef.Value (4))
+        {
+          std::cout << "Syntax error at '" << theArgVec[anArgIter] << "'\n";
+          return 1;
+        }
 
-static int VRight(Draw_Interpretor& di, Standard_Integer , const char** )
-{
-  return ViewProject(di, V3d_Xpos);
-}
+        if (aFrameDef.Value (2) == 'x')
+        {
+          aRight = aFrameDef.Value (1) == '+' ? gp::DX() : -gp::DX();
+        }
+        else if (aFrameDef.Value (2) == 'y')
+        {
+          aRight = aFrameDef.Value (1) == '+' ? gp::DY() : -gp::DY();
+        }
+        else if (aFrameDef.Value (2) == 'z')
+        {
+          aRight = aFrameDef.Value (1) == '+' ? gp::DZ() : -gp::DZ();
+        }
+        else
+        {
+          std::cout << "Syntax error at '" << theArgVec[anArgIter] << "'\n";
+          return 1;
+        }
 
-//==============================================================================
-//function : VFront
-//purpose  : Switch to a Front View
-//Draw arg : No args
-//==============================================================================
+        if (aFrameDef.Value (4) == 'x')
+        {
+          anUp = aFrameDef.Value (3) == '+' ? gp::DX() : -gp::DX();
+        }
+        else if (aFrameDef.Value (4) == 'y')
+        {
+          anUp = aFrameDef.Value (3) == '+' ? gp::DY() : -gp::DY();
+        }
+        else if (aFrameDef.Value (4) == 'z')
+        {
+          anUp = aFrameDef.Value (3) == '+' ? gp::DZ() : -gp::DZ();
+        }
+        else
+        {
+          std::cout << "Syntax error at '" << theArgVec[anArgIter] << "'\n";
+          return 1;
+        }
 
-static int VFront(Draw_Interpretor& di, Standard_Integer , const char** )
-{
-  return ViewProject(di, V3d_Yneg);
-}
+        const Handle(Graphic3d_Camera)& aCamera = aView->Camera();
+        const gp_Pnt anOriginVCS = aCamera->ConvertWorld2View (gp::Origin());
+        const gp_Dir aDir = anUp.Crossed (aRight);
+        aCamera->SetCenter (gp_Pnt (0, 0, 0));
+        aCamera->SetDirection (aDir);
+        aCamera->SetUp (anUp);
+        aCamera->OrthogonalizeUp();
 
-//==============================================================================
-//function : VBack
-//purpose  : Switch to a Back View
-//Draw arg : No args
-//==============================================================================
+        aView->Panning (anOriginVCS.X(), anOriginVCS.Y());
+        aView->Update();
+      }
+      else
+      {
+        std::cout << "Syntax error at '" << theArgVec[anArgIter] << "'\n";
+        return 1;
+      }
+    }
+  }
 
-static int VBack(Draw_Interpretor& di, Standard_Integer , const char** )
-{
-  return ViewProject(di, V3d_Ypos);
+  if (!isGeneralCmd
+    && theNbArgs != 1)
+  {
+    std::cout << "Syntax error: wrong number of arguments\n";
+    return 1;
+  }
+  return 0;
 }
 
 //==============================================================================
@@ -13543,27 +13753,38 @@ void ViewerTest::ViewerCommands(Draw_Interpretor& theCommands)
   theCommands.Add("vhelp" ,
     "vhelp            : display help on the viewer commands",
     __FILE__,VHelp,group);
+  theCommands.Add("vviewproj",
+          "vviewproj [top|bottom|left|right|front|back|axoLeft|axoRight]"
+    "\n\t\t:         [+-X+-Y+-Z] [-Zup|-Yup] [-frame +-X+-Y]"
+    "\n\t\t: Setup view direction"
+    "\n\t\t:   -Yup      use Y-up convention instead of Zup (which is default)."
+    "\n\t\t:   +-X+-Y+-Z define direction as combination of DX, DY and DZ;"
+    "\n\t\t:             for example '+Z' will show front of the model,"
+    "\n\t\t:             '-X-Y+Z' will define left axonometrical view."
+    "\n\t\t:   -frame    define camera Up and Right directions (regardless Up convention);"
+    "\n\t\t:             for example '+X+Z' will show front of the model with Z-up."
+    __FILE__,VViewProj,group);
   theCommands.Add("vtop" ,
     "vtop or <T>      : Top view. Orientation +X+Y" ,
-    __FILE__,VTop,group);
+    __FILE__,VViewProj,group);
   theCommands.Add("vbottom" ,
     "vbottom          : Bottom view. Orientation +X-Y" ,
-    __FILE__,VBottom,group);
+    __FILE__,VViewProj,group);
   theCommands.Add("vleft" ,
     "vleft            : Left view. Orientation -Y+Z" ,
-    __FILE__,VLeft,group);
+    __FILE__,VViewProj,group);
   theCommands.Add("vright" ,
     "vright           : Right view. Orientation +Y+Z" ,
-    __FILE__,VRight,group);
+    __FILE__,VViewProj,group);
   theCommands.Add("vaxo" ,
     " vaxo or <A>     : Axonometric view. Orientation +X-Y+Z",
-    __FILE__,VAxo,group);
+    __FILE__,VViewProj,group);
   theCommands.Add("vfront" ,
     "vfront           : Front view. Orientation +X+Z" ,
-    __FILE__,VFront,group);
+    __FILE__,VViewProj,group);
   theCommands.Add("vback" ,
     "vback            : Back view. Orientation -X+Z" ,
-    __FILE__,VBack,group);
+    __FILE__,VViewProj,group);
   theCommands.Add("vpick" ,
     "vpick           : vpick X Y Z [shape subshape] ( all variables as string )",
     VPick,group);
index a018592..77d00e0 100644 (file)
@@ -8,6 +8,10 @@ XCAFPrs_DataMapIteratorOfDataMapOfStyleTransient.hxx
 XCAFPrs_IndexedDataMapOfShapeStyle.hxx
 XCAFPrs_DataMapOfStyleShape.hxx
 XCAFPrs_DataMapOfStyleTransient.hxx
+XCAFPrs_DocumentExplorer.cxx
+XCAFPrs_DocumentExplorer.hxx
+XCAFPrs_DocumentIdIterator.hxx
+XCAFPrs_DocumentNode.hxx
 XCAFPrs_Driver.cxx
 XCAFPrs_Driver.hxx
 XCAFPrs_Style.cxx
diff --git a/src/XCAFPrs/XCAFPrs_DocumentExplorer.cxx b/src/XCAFPrs/XCAFPrs_DocumentExplorer.cxx
new file mode 100644 (file)
index 0000000..c5fbb70
--- /dev/null
@@ -0,0 +1,441 @@
+// Author: Kirill Gavrilov
+// Copyright (c) 2017-2019 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include <XCAFPrs_DocumentExplorer.hxx>
+
+#include <TDF_Tool.hxx>
+#include <TDocStd_Document.hxx>
+#include <XCAFDoc_ColorTool.hxx>
+#include <XCAFDoc_DocumentTool.hxx>
+#include <XCAFDoc_ShapeTool.hxx>
+#include <XCAFPrs_DocumentIdIterator.hxx>
+
+namespace
+{
+  //! Return merged style for the child node.
+  static XCAFPrs_Style mergedStyle (const Handle(XCAFDoc_ColorTool)& theColorTool,
+                                    const XCAFPrs_Style& theParenStyle,
+                                    const TDF_Label& theLabel,
+                                    const TDF_Label& theRefLabel)
+  {
+    if (theColorTool.IsNull())
+    {
+      return theParenStyle;
+    }
+
+    XCAFPrs_Style aStyle = theParenStyle;
+    Quantity_ColorRGBA aColor;
+    if (theColorTool->GetColor (theRefLabel, XCAFDoc_ColorGen, aColor))
+    {
+      aStyle.SetColorCurv (aColor.GetRGB());
+      aStyle.SetColorSurf (aColor);
+    }
+    if (theColorTool->GetColor (theRefLabel, XCAFDoc_ColorSurf, aColor))
+    {
+      aStyle.SetColorSurf (aColor);
+    }
+    if (theColorTool->GetColor (theRefLabel, XCAFDoc_ColorCurv, aColor))
+    {
+      aStyle.SetColorCurv (aColor.GetRGB());
+    }
+
+    if (theLabel != theRefLabel)
+    {
+      // override Reference style with Instance style when defined (bad model?)
+      if (theColorTool->GetColor (theLabel, XCAFDoc_ColorGen, aColor))
+      {
+        aStyle.SetColorCurv (aColor.GetRGB());
+        aStyle.SetColorSurf (aColor);
+      }
+      if (theColorTool->GetColor (theLabel, XCAFDoc_ColorSurf, aColor))
+      {
+        aStyle.SetColorSurf (aColor);
+      }
+      if (theColorTool->GetColor (theLabel, XCAFDoc_ColorCurv, aColor))
+      {
+        aStyle.SetColorCurv (aColor.GetRGB());
+      }
+    }
+
+    return aStyle;
+  }
+}
+
+// =======================================================================
+// function : DefineChildId
+// purpose  :
+// =======================================================================
+TCollection_AsciiString XCAFPrs_DocumentExplorer::DefineChildId (const TDF_Label& theLabel,
+                                                                 const TCollection_AsciiString& theParentId)
+{
+  TCollection_AsciiString anEntryId;
+  TDF_Tool::Entry (theLabel, anEntryId);
+  return !theParentId.IsEmpty()
+        ? theParentId + "/" + anEntryId + "."
+        : anEntryId + ".";
+}
+
+// =======================================================================
+// function : FindLabelFromPathId
+// purpose  :
+// =======================================================================
+TDF_Label XCAFPrs_DocumentExplorer::FindLabelFromPathId (const Handle(TDocStd_Document)& theDocument,
+                                                         const TCollection_AsciiString& theId,
+                                                         TopLoc_Location& theParentLocation,
+                                                         TopLoc_Location& theLocation)
+{
+  theParentLocation = TopLoc_Location();
+  theLocation = TopLoc_Location();
+  TDF_Label anInstanceLabel;
+  for (XCAFPrs_DocumentIdIterator anPathIter (theId); anPathIter.More();)
+  {
+    TDF_Label aSubLabel;
+    {
+      const TCollection_AsciiString& anOcafId = anPathIter.Value();
+      TDF_Tool::Label (theDocument->Main().Data(), anOcafId, aSubLabel);
+      if (aSubLabel.IsNull())
+      {
+        return TDF_Label();
+      }
+    }
+
+    anPathIter.Next();
+    if (!anPathIter.More())
+    {
+      theParentLocation = theLocation;
+    }
+
+    TopLoc_Location aLocTrsf = XCAFDoc_ShapeTool::GetLocation (aSubLabel);
+    theLocation = theLocation * aLocTrsf;
+    anInstanceLabel = aSubLabel;
+  }
+  return anInstanceLabel;
+}
+
+// =======================================================================
+// function : FindShapeFromPathId
+// purpose  :
+// =======================================================================
+TopoDS_Shape XCAFPrs_DocumentExplorer::FindShapeFromPathId (const Handle(TDocStd_Document)& theDocument,
+                                                            const TCollection_AsciiString& theId)
+{
+  TopLoc_Location aLocation;
+  TDF_Label anInstanceLabel = FindLabelFromPathId (theDocument, theId, aLocation);
+  if (anInstanceLabel.IsNull())
+  {
+    return TopoDS_Shape();
+  }
+
+  TDF_Label aRefLabel = anInstanceLabel;
+  XCAFDoc_ShapeTool::GetReferredShape (anInstanceLabel, aRefLabel);
+  if (aRefLabel.IsNull())
+  {
+    return TopoDS_Shape();
+  }
+
+  TopoDS_Shape aShape = XCAFDoc_ShapeTool::GetShape (aRefLabel);
+  if (aShape.IsNull())
+  {
+    return TopoDS_Shape();
+  }
+
+  aShape.Location (aLocation);
+  return aShape;
+}
+
+// =======================================================================
+// function : XCAFPrs_DocumentExplorer
+// purpose  :
+// =======================================================================
+XCAFPrs_DocumentExplorer::XCAFPrs_DocumentExplorer()
+: myTop (-1),
+  myHasMore (Standard_False),
+  myFlags (XCAFPrs_DocumentExplorerFlags_None)
+{
+  //
+}
+
+// =======================================================================
+// function : XCAFPrs_DocumentExplorer
+// purpose  :
+// =======================================================================
+XCAFPrs_DocumentExplorer::XCAFPrs_DocumentExplorer (const Handle(TDocStd_Document)& theDocument,
+                                                    const XCAFPrs_DocumentExplorerFlags theFlags,
+                                                    const XCAFPrs_Style& theDefStyle)
+: myTop (-1),
+  myHasMore (Standard_False),
+  myFlags (XCAFPrs_DocumentExplorerFlags_None)
+{
+  Handle(XCAFDoc_ShapeTool) aShapeTool = XCAFDoc_DocumentTool::ShapeTool (theDocument->Main());
+  TDF_LabelSequence aRootLabels;
+  aShapeTool->GetFreeShapes (aRootLabels);
+  Init (theDocument, aRootLabels, theFlags, theDefStyle);
+}
+
+// =======================================================================
+// function : XCAFPrs_DocumentExplorer
+// purpose  :
+// =======================================================================
+XCAFPrs_DocumentExplorer::XCAFPrs_DocumentExplorer (const Handle(TDocStd_Document)& theDocument,
+                                                    const TDF_LabelSequence& theRoots,
+                                                    const XCAFPrs_DocumentExplorerFlags theFlags,
+                                                    const XCAFPrs_Style& theDefStyle)
+: myTop (-1),
+  myHasMore (Standard_False),
+  myFlags (XCAFPrs_DocumentExplorerFlags_None)
+{
+  Init (theDocument, theRoots, theFlags, theDefStyle);
+}
+
+// =======================================================================
+// function : Init
+// purpose  :
+// =======================================================================
+void XCAFPrs_DocumentExplorer::Init (const Handle(TDocStd_Document)& theDocument,
+                                     const TDF_Label& theRoot,
+                                     const XCAFPrs_DocumentExplorerFlags theFlags,
+                                     const XCAFPrs_Style& theDefStyle)
+{
+  TDF_LabelSequence aSeq;
+  aSeq.Append (theRoot);
+  Init (theDocument, aSeq, theFlags, theDefStyle);
+}
+
+// =======================================================================
+// function : Init
+// purpose  :
+// =======================================================================
+void XCAFPrs_DocumentExplorer::Init (const Handle(TDocStd_Document)& theDocument,
+                                     const TDF_LabelSequence& theRoots,
+                                     const XCAFPrs_DocumentExplorerFlags theFlags,
+                                     const XCAFPrs_Style& theDefStyle)
+{
+  if ((theFlags & XCAFPrs_DocumentExplorerFlags_NoStyle) != 0)
+  {
+    myColorTool = XCAFDoc_DocumentTool::ColorTool (theDocument->Main());
+  }
+  else
+  {
+    myColorTool.Nullify();
+  }
+
+  ///myColorTool = theColorTool;
+  myDefStyle  = theDefStyle;
+  myRoots     = theRoots;
+  myRootIter  = TDF_LabelSequence::Iterator (myRoots);
+  myFlags     = theFlags;
+  initRoot();
+}
+
+// =======================================================================
+// function : initRoot
+// purpose  :
+// =======================================================================
+void XCAFPrs_DocumentExplorer::initRoot()
+{
+  for (;;)
+  {
+    // reset the stack
+    for (Standard_Integer aStackIter = 0; aStackIter <= myTop; ++aStackIter)
+    {
+      myNodeStack.SetValue (aStackIter, XCAFPrs_DocumentNode());
+    }
+    myTop = -1;
+    if (!myRootIter.More())
+    {
+      myHasMore = Standard_False;
+      initCurrent (Standard_False);
+      return;
+    }
+
+    const TDF_Label& aRootLab = myRootIter.Value();
+    if (aRootLab.IsNull())
+    {
+      // assert - invalid input
+      //Standard_ProgramError::Raise ("CadDocumentExplorer - NULL label in the input");
+      myRootIter.Next();
+      continue;
+    }
+
+    myHasMore = Standard_True;
+    TDF_Label aRefLabel = aRootLab;
+    XCAFDoc_ShapeTool::GetReferredShape (aRootLab, aRefLabel);
+    if (XCAFDoc_ShapeTool::IsAssembly (aRefLabel))
+    {
+      Next();
+    }
+    else
+    {
+      initCurrent (Standard_False);
+    }
+    return;
+  }
+}
+
+// =======================================================================
+// function : initCurrent
+// purpose  :
+// =======================================================================
+void XCAFPrs_DocumentExplorer::initCurrent (Standard_Boolean theIsAssmebly)
+{
+  myCurrent = XCAFPrs_DocumentNode();
+  if (theIsAssmebly)
+  {
+    if (myTop < 0)
+    {
+      Standard_ProgramError::Raise ("CadDocumentExplorer - internal error");
+    }
+    myCurrent = myNodeStack.Value (myTop);
+  }
+  else if (myTop < 0)
+  {
+    if (!myRootIter.More())
+    {
+      return;
+    }
+
+    myCurrent.Label    = myRootIter.Value();
+    myCurrent.RefLabel = myCurrent.Label;
+    XCAFDoc_ShapeTool::GetReferredShape (myCurrent.Label, myCurrent.RefLabel);
+    myCurrent.LocalTrsf= XCAFDoc_ShapeTool::GetLocation (myCurrent.Label);
+    myCurrent.Location = myCurrent.LocalTrsf;
+    myCurrent.Style    = mergedStyle (myColorTool, myDefStyle, myCurrent.Label, myCurrent.RefLabel);
+    myCurrent.Id       = DefineChildId (myCurrent.Label, TCollection_AsciiString());
+  }
+  else
+  {
+    const XCAFPrs_DocumentNode& aTopNodeInStack = myNodeStack.Value (myTop);
+    myCurrent.Label    = aTopNodeInStack.ChildIter.Value();
+    myCurrent.RefLabel = myCurrent.Label;
+    XCAFDoc_ShapeTool::GetReferredShape (myCurrent.Label, myCurrent.RefLabel);
+    myCurrent.LocalTrsf= XCAFDoc_ShapeTool::GetLocation (myCurrent.Label);
+    myCurrent.Location = aTopNodeInStack.Location * myCurrent.LocalTrsf;
+    myCurrent.Style    = mergedStyle (myColorTool, aTopNodeInStack.Style, myCurrent.Label, myCurrent.RefLabel);
+    myCurrent.Id       = DefineChildId (myCurrent.Label, aTopNodeInStack.Id);
+  }
+}
+
+// =======================================================================
+// function : Next
+// purpose  :
+// =======================================================================
+void XCAFPrs_DocumentExplorer::Next()
+{
+  if (!myHasMore)
+  {
+    Standard_ProgramError::Raise ("CadDocumentExplorer::Next() - out of range");
+    return; // assert
+  }
+
+  if (myTop < 0)
+  {
+    const TDF_Label& aRootLab  = myRootIter.Value();
+    TDF_Label        aRefLabel = aRootLab;
+    XCAFDoc_ShapeTool::GetReferredShape (aRootLab, aRefLabel);
+    if (!XCAFDoc_ShapeTool::IsAssembly (aRefLabel))
+    {
+      // already visited once
+      myRootIter.Next();
+      initRoot();
+      return;
+    }
+
+    // push and try to find
+    myTop = 0;
+    XCAFPrs_DocumentNode aNodeInStack;
+    aNodeInStack.IsAssembly = Standard_True;
+    aNodeInStack.Label      = aRootLab;
+    aNodeInStack.RefLabel   = aRefLabel;
+    aNodeInStack.ChildIter  = TDF_ChildIterator (aNodeInStack.RefLabel);
+    aNodeInStack.LocalTrsf  = XCAFDoc_ShapeTool::GetLocation (aNodeInStack.Label);
+    aNodeInStack.Location   = aNodeInStack.LocalTrsf;
+    aNodeInStack.Style      = mergedStyle (myColorTool, myDefStyle, aNodeInStack.Label, aNodeInStack.RefLabel);
+    aNodeInStack.Id         = DefineChildId (aNodeInStack.Label, TCollection_AsciiString());
+    myNodeStack.SetValue (0, aNodeInStack);
+    if ((myFlags & XCAFPrs_DocumentExplorerFlags_OnlyLeafNodes) == 0)
+    {
+      initCurrent (Standard_True);
+      return;
+    }
+  }
+  else
+  {
+    if (!myCurrent.IsAssembly)
+    {
+      myNodeStack.ChangeValue (myTop).ChildIter.Next();
+    }
+  }
+
+  for (;;)
+  {
+    if (myNodeStack.Value (myTop).ChildIter.More())
+    {
+      const TDF_Label& aNodeTop = myNodeStack.Value (myTop).ChildIter.Value();
+      if (aNodeTop.IsNull()
+       || (!aNodeTop.HasChild() && !aNodeTop.HasAttribute()))
+      {
+        myNodeStack.ChangeValue (myTop).ChildIter.Next();
+        continue;
+      }
+
+      TDF_Label aRefLabel = aNodeTop;
+      XCAFDoc_ShapeTool::GetReferredShape (aNodeTop, aRefLabel);
+      if (!XCAFDoc_ShapeTool::IsAssembly (aRefLabel))
+      {
+        myHasMore = Standard_True;
+        initCurrent (Standard_False);
+        return;
+      }
+      else if (aRefLabel.HasAttribute()
+            || aRefLabel.HasChild())
+      {
+        const XCAFPrs_DocumentNode& aParent = myNodeStack.Value (myTop);
+        ++myTop;
+
+        XCAFPrs_DocumentNode aNodeInStack;
+        aNodeInStack.IsAssembly = Standard_True;
+        aNodeInStack.Label      = aNodeTop;
+        aNodeInStack.RefLabel   = aRefLabel;
+        aNodeInStack.LocalTrsf  = XCAFDoc_ShapeTool::GetLocation (aNodeInStack.Label);
+        aNodeInStack.Location   = aParent.Location * aNodeInStack.LocalTrsf;
+        aNodeInStack.Style      = mergedStyle (myColorTool, aParent.Style, aNodeInStack.Label, aNodeInStack.RefLabel);
+        aNodeInStack.Id         = DefineChildId (aNodeInStack.Label, aParent.Id);
+        aNodeInStack.ChildIter  = TDF_ChildIterator (aNodeInStack.RefLabel);
+        myNodeStack.SetValue (myTop, aNodeInStack);
+        if ((myFlags & XCAFPrs_DocumentExplorerFlags_OnlyLeafNodes) == 0)
+        {
+          initCurrent (Standard_True);
+          return;
+        }
+      }
+      else
+      {
+        myNodeStack.ChangeValue (myTop).ChildIter.Next();
+      }
+    }
+    else
+    {
+      myNodeStack.SetValue (myTop, XCAFPrs_DocumentNode());
+      --myTop;
+      if (myTop < 0)
+      {
+        myRootIter.Next();
+        initRoot();
+        return;
+      }
+
+      myNodeStack.ChangeValue (myTop).ChildIter.Next();
+    }
+  }
+}
diff --git a/src/XCAFPrs/XCAFPrs_DocumentExplorer.hxx b/src/XCAFPrs/XCAFPrs_DocumentExplorer.hxx
new file mode 100644 (file)
index 0000000..f617b90
--- /dev/null
@@ -0,0 +1,174 @@
+// Author: Kirill Gavrilov
+// Copyright (c) 2017-2019 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _XCAFPrs_DocumentExplorer_HeaderFile
+#define _XCAFPrs_DocumentExplorer_HeaderFile
+
+#include <NCollection_Vector.hxx>
+#include <NCollection_Sequence.hxx>
+#include <XCAFPrs_DocumentNode.hxx>
+#include <TDF_LabelSequence.hxx>
+#include <TopoDS_Shape.hxx>
+
+class TDocStd_Document;
+class XCAFDoc_ShapeTool;
+class XCAFDoc_ColorTool;
+
+typedef Standard_Integer XCAFPrs_DocumentExplorerFlags;
+
+//! Document explorer flags.
+enum
+{
+  XCAFPrs_DocumentExplorerFlags_None          = 0x00, //!< no flags
+  XCAFPrs_DocumentExplorerFlags_OnlyLeafNodes = 0x01, //!< explore only leaf nodes (skip assembly nodes)
+  XCAFPrs_DocumentExplorerFlags_NoStyle       = 0x02, //!< do not fetch styles
+};
+
+//! Document iterator through shape nodes.
+class XCAFPrs_DocumentExplorer
+{
+public: //! @name string identification tools
+
+  //! Construct a unique string identifier for the given label.
+  //! The identifier is a concatenation of label entries (TDF_Tool::Entry() with tailing '.') of hierarchy from parent to child
+  //! joined via '/' and looking like this:
+  //! @code
+  //!   0:1:1:1./0:1:1:1:9./0:1:1:5:7.
+  //! @endcode
+  //! This generation scheme also allows finding originating labels using TDF_Tool::Label().
+  //! The tailing dot simplifies parent equality check.
+  //! @param theLabel child label to define id
+  //! @param theParentId parent string identifier defined by this method
+  Standard_EXPORT static TCollection_AsciiString DefineChildId (const TDF_Label& theLabel,
+                                                                const TCollection_AsciiString& theParentId);
+
+  //! Find a shape entity based on a text identifier constructed from OCAF labels defining full path.
+  //! @sa DefineChildId()
+  Standard_EXPORT static TDF_Label FindLabelFromPathId (const Handle(TDocStd_Document)& theDocument,
+                                                        const TCollection_AsciiString& theId,
+                                                        TopLoc_Location& theParentLocation,
+                                                        TopLoc_Location& theLocation);
+
+  //! Find a shape entity based on a text identifier constructed from OCAF labels defining full path.
+  //! @sa DefineChildId()
+  static TDF_Label FindLabelFromPathId (const Handle(TDocStd_Document)& theDocument,
+                                        const TCollection_AsciiString& theId,
+                                        TopLoc_Location& theLocation)
+  {
+    TopLoc_Location aDummy;
+    return FindLabelFromPathId (theDocument, theId, aDummy, theLocation);
+  }
+
+  //! Find a shape entity based on a text identifier constructed from OCAF labels defining full path.
+  //! @sa DefineChildId()
+  Standard_EXPORT static TopoDS_Shape FindShapeFromPathId (const Handle(TDocStd_Document)& theDocument,
+                                                           const TCollection_AsciiString& theId);
+
+public:
+
+  //! Empty constructor.
+  Standard_EXPORT XCAFPrs_DocumentExplorer();
+
+  //! Constructor for exploring the whole document.
+  //! @param theDocument document to explore
+  //! @param theFlags    iteration flags
+  //! @param theDefStyle default style for nodes with undefined style
+  Standard_EXPORT XCAFPrs_DocumentExplorer (const Handle(TDocStd_Document)& theDocument,
+                                            const XCAFPrs_DocumentExplorerFlags theFlags,
+                                            const XCAFPrs_Style& theDefStyle = XCAFPrs_Style());
+
+  //! Constructor for exploring specified list of root shapes in the document.
+  //! @param theDocument  document to explore
+  //! @param theRoots     root labels to explore within specified document
+  //! @param theFlags     iteration flags
+  //! @param theDefStyle  default style for nodes with undefined style
+  Standard_EXPORT XCAFPrs_DocumentExplorer (const Handle(TDocStd_Document)& theDocument,
+                                            const TDF_LabelSequence& theRoots,
+                                            const XCAFPrs_DocumentExplorerFlags theFlags,
+                                            const XCAFPrs_Style& theDefStyle = XCAFPrs_Style());
+
+  //! Initialize the iterator from a single root shape in the document.
+  //! @param theDocument  document to explore
+  //! @param theRoot      single root label to explore within specified document
+  //! @param theFlags     iteration flags
+  //! @param theDefStyle  default style for nodes with undefined style
+  Standard_EXPORT void Init (const Handle(TDocStd_Document)& theDocument,
+                             const TDF_Label& theRoot,
+                             const XCAFPrs_DocumentExplorerFlags theFlags,
+                             const XCAFPrs_Style& theDefStyle = XCAFPrs_Style());
+
+  //! Initialize the iterator from the list of root shapes in the document.
+  //! @param theDocument  document to explore
+  //! @param theRoots     root labels to explore within specified document
+  //! @param theFlags     iteration flags
+  //! @param theDefStyle  default style for nodes with undefined style
+  Standard_EXPORT void Init (const Handle(TDocStd_Document)& theDocument,
+                             const TDF_LabelSequence& theRoots,
+                             const XCAFPrs_DocumentExplorerFlags theFlags,
+                             const XCAFPrs_Style& theDefStyle = XCAFPrs_Style());
+
+  //! Return TRUE if iterator points to the valid node.
+  Standard_Boolean More() const { return myHasMore; }
+
+  //! Return current position.
+  const XCAFPrs_DocumentNode& Current() const { return myCurrent; }
+
+  //! Return current position.
+  XCAFPrs_DocumentNode& ChangeCurrent() { return myCurrent; }
+
+  //! Return current position within specified assembly depth.
+  const XCAFPrs_DocumentNode& Current (Standard_Integer theDepth) const
+  {
+    const Standard_Integer aCurrDepth = CurrentDepth();
+    if (theDepth == aCurrDepth)
+    {
+      return myCurrent;
+    }
+
+    Standard_OutOfRange_Raise_if (theDepth < 0 || theDepth > myTop,
+                                  "XCAFPrs_DocumentExplorer::Current() out of range");
+    return myNodeStack.Value (theDepth);
+  }
+
+  //! Return depth of the current node in hierarchy, starting from 0.
+  //! Zero means Root label.
+  Standard_Integer CurrentDepth() const { return myCurrent.IsAssembly ? myTop : myTop + 1; }
+
+  //! Go to the next node.
+  Standard_EXPORT void Next();
+
+protected:
+
+  //! Initialize root label.
+  Standard_EXPORT void initRoot();
+
+  //! Initialize properties for a current label.
+  Standard_EXPORT void initCurrent (Standard_Boolean theIsAssmebly);
+
+protected:
+
+  Handle(XCAFDoc_ColorTool)      myColorTool; //!< color tool
+  TDF_LabelSequence              myRoots;     //!< sequence of root labels
+  TDF_LabelSequence::Iterator    myRootIter;  //!< current root label
+  NCollection_Vector<XCAFPrs_DocumentNode>
+                                 myNodeStack; //!< node stack
+  Standard_Integer               myTop;       //!< top position in the node stack
+  Standard_Boolean               myHasMore;   //!< global flag indicating that iterator points to the label
+  XCAFPrs_Style                  myDefStyle;  //!< default style
+  XCAFPrs_DocumentNode           myCurrent;   //!< current label info
+  XCAFPrs_DocumentExplorerFlags  myFlags;     //!< iteration flags
+
+};
+
+#endif // _XCAFPrs_DocumentExplorer_HeaderFile
diff --git a/src/XCAFPrs/XCAFPrs_DocumentIdIterator.hxx b/src/XCAFPrs/XCAFPrs_DocumentIdIterator.hxx
new file mode 100644 (file)
index 0000000..5d0c9dc
--- /dev/null
@@ -0,0 +1,90 @@
+// Author: Kirill Gavrilov
+// Copyright (c) 2017-2019 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _XCAFPrs_DocumentIdIterator_HeaderFile
+#define _XCAFPrs_DocumentIdIterator_HeaderFile
+
+#include <XCAFPrs_Style.hxx>
+
+#include <TDF_ChildIterator.hxx>
+#include <TDF_Label.hxx>
+#include <TopLoc_Location.hxx>
+
+//! Auxiliary tool for iterating through Path identification string.
+class XCAFPrs_DocumentIdIterator
+{
+public:
+  //! Main constructor.
+  XCAFPrs_DocumentIdIterator (const TCollection_AsciiString& thePath)
+  : myPath (thePath), myPosition (0)
+  {
+    Next();
+  }
+
+  //! Return TRUE if iterator points to a value.
+  bool More() const { return !mySubId.IsEmpty(); }
+
+  //! Return current value.
+  const TCollection_AsciiString& Value() const { return mySubId; }
+
+  //! Find the next value.
+  void Next();
+
+private:
+
+  // Disable assignment operator.
+  XCAFPrs_DocumentIdIterator& operator= (const XCAFPrs_DocumentIdIterator& );
+
+private:
+  const TCollection_AsciiString& myPath;     //!< full path
+  TCollection_AsciiString        mySubId;    //!< current value
+  Standard_Integer               myPosition; //!< last processed new-line symbol
+};
+
+// =======================================================================
+// function : Next
+// purpose  :
+// =======================================================================
+inline void XCAFPrs_DocumentIdIterator::Next()
+{
+  for (Standard_Integer aCharIndex = myPosition + 1; aCharIndex <= myPath.Length(); ++aCharIndex)
+  {
+    if (myPath.Value (aCharIndex) == '/')
+    {
+      // intermediate items have trailing dot and separator before the next item
+      const Standard_Integer aLen = aCharIndex - myPosition - 2;
+      if (aLen < 1)
+      {
+        return; // assert - should never happen for valid IDs!
+      }
+
+      mySubId = myPath.SubString (myPosition + 1, aCharIndex - 2);
+      myPosition = aCharIndex;
+      return;
+    }
+  }
+  if (myPosition < myPath.Length())
+  {
+    // last item has only trailing dot
+    mySubId = myPath.SubString (myPosition + 1, myPath.Length() - 1);
+    myPosition = myPath.Length();
+  }
+  else
+  {
+    mySubId.Clear();
+    myPosition = myPath.Length();
+  }
+}
+
+#endif // _XCAFPrs_DocumentIdIterator_HeaderFile
diff --git a/src/XCAFPrs/XCAFPrs_DocumentNode.hxx b/src/XCAFPrs/XCAFPrs_DocumentNode.hxx
new file mode 100644 (file)
index 0000000..9126a40
--- /dev/null
@@ -0,0 +1,39 @@
+// Author: Kirill Gavrilov
+// Copyright (c) 2017-2019 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _XCAFPrs_DocumentNode_HeaderFile
+#define _XCAFPrs_DocumentNode_HeaderFile
+
+#include <XCAFPrs_Style.hxx>
+
+#include <TDF_ChildIterator.hxx>
+#include <TDF_Label.hxx>
+#include <TopLoc_Location.hxx>
+
+//! Structure defining document node.
+struct XCAFPrs_DocumentNode
+{
+  TCollection_AsciiString Id;         //!< string identifier
+  TDF_Label               Label;      //!< label in the document
+  TDF_Label               RefLabel;   //!< reference label in the document
+  XCAFPrs_Style           Style;      //!< node style
+  TopLoc_Location         Location;   //!< node global transformation
+  TopLoc_Location         LocalTrsf;  //!< node transformation relative to parent
+  TDF_ChildIterator       ChildIter;  //!< child iterator
+  Standard_Boolean        IsAssembly; //!< flag indicating that this label is assembly
+
+  XCAFPrs_DocumentNode() : IsAssembly (Standard_False) {}
+};
+
+#endif // _XCAFPrs_DocumentNode_HeaderFile
diff --git a/tests/collections/n/osdpath b/tests/collections/n/osdpath
new file mode 100644 (file)
index 0000000..ff9c8f0
--- /dev/null
@@ -0,0 +1,48 @@
+puts "============="
+puts "OSD_Path - test file path parsing tools"
+puts "============="
+
+pload QAcommands
+
+if { [QAOsdPathType {c:\folder\file.png}]              != "absolute dos " }              { puts "Error: DOS path misdetection" }
+if { [QAOsdPathType {c:\file.png}]                     != "absolute dos " }              { puts "Error: DOS path misdetection" }
+if { [QAOsdPathType "D:\\"]                            != "absolute dos " }              { puts "Error: DOS root misdetection" }
+if { [QAOsdPathType {\\share\file.pdf}]                != "absolute unc " }              { puts "Error: UNC path misdetection" }
+if { [QAOsdPathType {\\?\C:\documents\file.docx}]      != "absolute ntextended " }       { puts "Error: NT Extended path misdetection" }
+if { [QAOsdPathType {\\?\UNC\server\share\file.zip}]   != "absolute unc ntextended uncextended " } { puts "Error: UNC extended path misdetection" }
+if { [QAOsdPathType {https://www.server.org/file.gif}] != "absolute protocol " }         { puts "Error: remote protocal path misdetection" }
+if { [QAOsdPathType {content://file.jpg}]              != "absolute protocol content " } { puts "Error: content protocal path misdetection" }
+if { [QAOsdPathType {/home/username/file.txt}]         != "absolute unix " }             { puts "Error: Unix path misdetection" }
+if { [QAOsdPathType {/boot.bin}]                       != "absolute unix " }             { puts "Error: Unix path misdetection" }
+if { [QAOsdPathType {/}]                               != "absolute unix " }             { puts "Error: Unix root misdetection" }
+if { [QAOsdPathType {./subfolder/../file.txt}]         != "relative " }                  { puts "Error: Realtive path misdetection" }
+if { [QAOsdPathType {../file.txt}]                     != "relative " }                  { puts "Error: Realtive path misdetection" }
+if { [QAOsdPathType {.}]                               != "relative " }                  { puts "Error: Realtive path misdetection" }
+if { [QAOsdPathType {..}]                              != "relative " }                  { puts "Error: Realtive path misdetection" }
+if { [QAOsdPathType {image.png}]                       != "relative " }                  { puts "Error: Realtive path misdetection" }
+
+if { [QAOsdPathPart {image.png} -folder]                   != "" }                  { puts "Error: Empty folder misdetected" }
+if { [QAOsdPathPart {image.png} -fileName]                 != "image.png" }         { puts "Error: File name misdetected" }
+if { [QAOsdPathPart {c:\folder\file.png} -folder]          != "c:\\folder\\" }      { puts "Error: DOS folder misdetected" }
+if { [QAOsdPathPart {c:\folder\file.png} -fileName]        != "file.png" }          { puts "Error: DOS file name misdetected" }
+if { [QAOsdPathPart {c:\file.png} -folder]                 != "c:\\" }              { puts "Error: DOS folder misdetected" }
+if { [QAOsdPathPart {c:\file.png} -fileName]               != "file.png" }          { puts "Error: DOS file name misdetected" }
+if { [QAOsdPathPart "D:\\" -folder]                        != "D:\\" }              { puts "Error: DOS root misdetected" }
+if { [QAOsdPathPart "D:\\" -fileName]                      != "" }                  { puts "Error: DOS root misdetected" }
+if { [QAOsdPathPart "/" -folder]                           != "/" }                 { puts "Error: Unit root misdetected" }
+if { [QAOsdPathPart "/" -fileName]                         != "" }                  { puts "Error: Unit root misdetected" }
+if { [QAOsdPathPart {subfolder/file.txt} -folder]          != "subfolder/" }        { puts "Error: Relative folder misdetected" }
+if { [QAOsdPathPart {subfolder/file.txt} -fileName]        != "file.txt" }          { puts "Error: Relative file name misdetected" }
+if { [QAOsdPathPart {./subfolder/../file.txt} -folder]     != "./subfolder/../" }   { puts "Error: Relative folder misdetected" }
+if { [QAOsdPathPart {./subfolder/../file.txt} -fileName]   != "file.txt" }          { puts "Error: Relative file name misdetected" }
+if { [QAOsdPathPart {../../file.txt} -folder]              != "../../" }            { puts "Error: Relative folder misdetected" }
+if { [QAOsdPathPart {../../file.txt} -fileName]            != "file.txt" }          { puts "Error: Relative file name misdetected" }
+if { [QAOsdPathPart {/home/username/file.txt} -folder]     != "/home/username/" }   { puts "Error: Unix folder misdetected" }
+if { [QAOsdPathPart {/home/username/file.txt} -fileName]   != "file.txt" }          { puts "Error: Unix file name misdetected" }
+
+if { [QAOsdPathPart {content://file.jpg} -folder]          != "content://" }        { puts "Error: Content folder misdetected" }
+if { [QAOsdPathPart {content://file.jpg} -fileName]        != "file.jpg" }          { puts "Error: Content file name misdetected" }
+if { [QAOsdPathPart {ftp://server.org/file.gif} -folder]   != "ftp://server.org/" } { puts "Error: Remote protocol folder misdetected" }
+if { [QAOsdPathPart {ftp://server.org/file.gif} -fileName] != "file.gif" }          { puts "Error: Remote protocol file name misdetected" }
+if { [QAOsdPathPart {\\?\UNC\server\file.zip} -folder]     != "\\\\?\\UNC\\server\\" } { puts "Error: UNC folder misdetected" }
+if { [QAOsdPathPart {\\?\UNC\server\file.zip} -fileName]   != "file.zip" }          { puts "Error: UNC filename misdetected" }