1
0
mirror of https://git.dev.opencascade.org/repos/occt.git synced 2025-08-14 13:30:48 +03:00

Data Exchange, DE_Wrapper - Implement Stream support (#663)

- Adds stream-based read/write methods to multiple data exchange providers
- Refactors underlying APIs (VrmlAPI_Writer, StlAPI_Writer, RWStl) to support stream operations
- Implements comprehensive test coverage for stream functionality
- Adds validation utilities for improved error handling
This commit is contained in:
Pasukhin Dmitry
2025-08-08 12:05:27 +01:00
committed by GitHub
parent 3c14e29dfc
commit 711fbc42b2
39 changed files with 4888 additions and 500 deletions

View File

@@ -103,6 +103,13 @@ bool DE_ConfigurationNode::IsExportSupported() const
//=================================================================================================
bool DE_ConfigurationNode::IsStreamSupported() const
{
return false;
}
//=================================================================================================
bool DE_ConfigurationNode::CheckExtension(const TCollection_AsciiString& theExtension) const
{
TCollection_AsciiString anExtension(theExtension);

View File

@@ -92,14 +92,18 @@ public:
const Standard_Boolean theToKeep);
public:
//! Checks the import supporting
//! @return Standard_True if import is support
//! Checks for import support.
//! @return Standard_True if import is supported
Standard_EXPORT virtual bool IsImportSupported() const;
//! Checks the export supporting
//! @return Standard_True if export is support
//! Checks for export support.
//! @return Standard_True if export is supported
Standard_EXPORT virtual bool IsExportSupported() const;
//! Checks for stream support.
//! @return Standard_True if streams are supported
Standard_EXPORT virtual bool IsStreamSupported() const;
//! Gets CAD format name of associated provider
//! @return provider CAD format
Standard_EXPORT virtual TCollection_AsciiString GetFormat() const = 0;

View File

@@ -15,6 +15,7 @@
#include <DE_ConfigurationNode.hxx>
#include <Message.hxx>
#include <NCollection_IndexedDataMap.hxx>
IMPLEMENT_STANDARD_RTTIEXT(DE_Provider, Standard_Transient)
@@ -148,3 +149,123 @@ Standard_Boolean DE_Provider::Write(const TCollection_AsciiString& thePath,
<< " doesn't support write operation";
return Standard_False;
}
//=================================================================================================
Standard_Boolean DE_Provider::Read(ReadStreamList& theStreams,
const Handle(TDocStd_Document)& theDocument,
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress)
{
(void)theStreams;
(void)theDocument;
(void)theWS;
(void)theProgress;
Message::SendFail() << "Error: provider " << GetFormat() << " " << GetVendor()
<< " doesn't support stream read operation";
return Standard_False;
}
//=================================================================================================
Standard_Boolean DE_Provider::Write(WriteStreamList& theStreams,
const Handle(TDocStd_Document)& theDocument,
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress)
{
(void)theStreams;
(void)theDocument;
(void)theWS;
(void)theProgress;
Message::SendFail() << "Error: provider " << GetFormat() << " " << GetVendor()
<< " doesn't support stream write operation";
return Standard_False;
}
//=================================================================================================
Standard_Boolean DE_Provider::Read(ReadStreamList& theStreams,
TopoDS_Shape& theShape,
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress)
{
(void)theStreams;
(void)theShape;
(void)theWS;
(void)theProgress;
Message::SendFail() << "Error: provider " << GetFormat() << " " << GetVendor()
<< " doesn't support stream read operation";
return Standard_False;
}
//=================================================================================================
Standard_Boolean DE_Provider::Write(WriteStreamList& theStreams,
const TopoDS_Shape& theShape,
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress)
{
(void)theStreams;
(void)theShape;
(void)theWS;
(void)theProgress;
Message::SendFail() << "Error: provider " << GetFormat() << " " << GetVendor()
<< " doesn't support stream write operation";
return Standard_False;
}
//=================================================================================================
Standard_Boolean DE_Provider::Read(ReadStreamList& theStreams,
const Handle(TDocStd_Document)& theDocument,
const Message_ProgressRange& theProgress)
{
(void)theStreams;
(void)theDocument;
(void)theProgress;
Message::SendFail() << "Error: provider " << GetFormat() << " " << GetVendor()
<< " doesn't support stream read operation";
return Standard_False;
}
//=================================================================================================
Standard_Boolean DE_Provider::Write(WriteStreamList& theStreams,
const Handle(TDocStd_Document)& theDocument,
const Message_ProgressRange& theProgress)
{
(void)theStreams;
(void)theDocument;
(void)theProgress;
Message::SendFail() << "Error: provider " << GetFormat() << " " << GetVendor()
<< " doesn't support stream write operation";
return Standard_False;
}
//=================================================================================================
Standard_Boolean DE_Provider::Read(ReadStreamList& theStreams,
TopoDS_Shape& theShape,
const Message_ProgressRange& theProgress)
{
(void)theStreams;
(void)theShape;
(void)theProgress;
Message::SendFail() << "Error: provider " << GetFormat() << " " << GetVendor()
<< " doesn't support stream read operation";
return Standard_False;
}
//=================================================================================================
Standard_Boolean DE_Provider::Write(WriteStreamList& theStreams,
const TopoDS_Shape& theShape,
const Message_ProgressRange& theProgress)
{
(void)theStreams;
(void)theShape;
(void)theProgress;
Message::SendFail() << "Error: provider " << GetFormat() << " " << GetVendor()
<< " doesn't support stream write operation";
return Standard_False;
}

View File

@@ -15,6 +15,9 @@
#define _DE_Provider_HeaderFile
#include <Message_ProgressRange.hxx>
#include <NCollection_List.hxx>
#include <Standard_IStream.hxx>
#include <Standard_OStream.hxx>
class DE_ConfigurationNode;
class TopoDS_Shape;
@@ -43,6 +46,45 @@ class DE_Provider : public Standard_Transient
public:
DEFINE_STANDARD_RTTIEXT(DE_Provider, Standard_Transient)
//! Node to store write stream information
//! Contains relative path and reference to output stream
struct WriteStreamNode
{
TCollection_AsciiString Path; //!< Relative path to the output file
Standard_OStream& Stream; //!< Reference to output stream
//! Constructor
WriteStreamNode(const TCollection_AsciiString& thePath, Standard_OStream& theStream)
: Path(thePath),
Stream(theStream)
{
}
};
//! Node to store read stream information
//! Contains relative path and reference to input stream
struct ReadStreamNode
{
TCollection_AsciiString Path; //!< Relative path to the input file
Standard_IStream& Stream; //!< Reference to input stream
//! Constructor
ReadStreamNode(const TCollection_AsciiString& thePath, Standard_IStream& theStream)
: Path(thePath),
Stream(theStream)
{
}
};
public:
//! List to store write stream nodes
//! First element is the main stream, others are for internal referencing
using WriteStreamList = NCollection_List<WriteStreamNode>;
//! List to store read stream nodes
//! First element is the main stream, others are for internal referencing
using ReadStreamList = NCollection_List<ReadStreamNode>;
public:
//! Default constructor
//! Configure translation process with global configuration
@@ -77,6 +119,30 @@ public:
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress = Message_ProgressRange());
//! Reads streams according to internal configuration
//! @param[in] theStreams streams to read from
//! @param[out] theDocument document to save result
//! @param[in] theWS current work session
//! @param[in] theProgress progress indicator
//! @return True if Read was successful
Standard_EXPORT virtual Standard_Boolean Read(
ReadStreamList& theStreams,
const Handle(TDocStd_Document)& theDocument,
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress = Message_ProgressRange());
//! Writes streams according to internal configuration
//! @param[in] theStreams streams to write to
//! @param[out] theDocument document to export
//! @param[in] theWS current work session
//! @param[in] theProgress progress indicator
//! @return True if Write was successful
Standard_EXPORT virtual Standard_Boolean Write(
WriteStreamList& theStreams,
const Handle(TDocStd_Document)& theDocument,
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress = Message_ProgressRange());
//! Reads a CAD file, according internal configuration
//! @param[in] thePath path to the import CAD file
//! @param[out] theDocument document to save result
@@ -97,6 +163,26 @@ public:
const Handle(TDocStd_Document)& theDocument,
const Message_ProgressRange& theProgress = Message_ProgressRange());
//! Reads streams according to internal configuration
//! @param[in] theStreams streams to read from
//! @param[out] theDocument document to save result
//! @param[in] theProgress progress indicator
//! @return True if Read was successful
Standard_EXPORT virtual Standard_Boolean Read(
ReadStreamList& theStreams,
const Handle(TDocStd_Document)& theDocument,
const Message_ProgressRange& theProgress = Message_ProgressRange());
//! Writes streams according to internal configuration
//! @param[in] theStreams streams to write to
//! @param[out] theDocument document to export
//! @param[in] theProgress progress indicator
//! @return True if Write was successful
Standard_EXPORT virtual Standard_Boolean Write(
WriteStreamList& theStreams,
const Handle(TDocStd_Document)& theDocument,
const Message_ProgressRange& theProgress = Message_ProgressRange());
//! Reads a CAD file, according internal configuration
//! @param[in] thePath path to the import CAD file
//! @param[out] theShape shape to save result
@@ -121,6 +207,30 @@ public:
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress = Message_ProgressRange());
//! Reads streams according to internal configuration
//! @param[in] theStreams streams to read from
//! @param[out] theShape shape to save result
//! @param[in] theWS current work session
//! @param[in] theProgress progress indicator
//! @return True if Read was successful
Standard_EXPORT virtual Standard_Boolean Read(
ReadStreamList& theStreams,
TopoDS_Shape& theShape,
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress = Message_ProgressRange());
//! Writes streams according to internal configuration
//! @param[in] theStreams streams to write to
//! @param[out] theShape shape to export
//! @param[in] theWS current work session
//! @param[in] theProgress progress indicator
//! @return True if Write was successful
Standard_EXPORT virtual Standard_Boolean Write(
WriteStreamList& theStreams,
const TopoDS_Shape& theShape,
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress = Message_ProgressRange());
//! Reads a CAD file, according internal configuration
//! @param[in] thePath path to the import CAD file
//! @param[out] theShape shape to save result
@@ -141,6 +251,26 @@ public:
const TopoDS_Shape& theShape,
const Message_ProgressRange& theProgress = Message_ProgressRange());
//! Reads streams according to internal configuration
//! @param[in] theStreams streams to read from
//! @param[out] theShape shape to save result
//! @param[in] theProgress progress indicator
//! @return True if Read was successful
Standard_EXPORT virtual Standard_Boolean Read(
ReadStreamList& theStreams,
TopoDS_Shape& theShape,
const Message_ProgressRange& theProgress = Message_ProgressRange());
//! Writes streams according to internal configuration
//! @param[in] theStreams streams to write to
//! @param[out] theShape shape to export
//! @param[in] theProgress progress indicator
//! @return True if Write was successful
Standard_EXPORT virtual Standard_Boolean Write(
WriteStreamList& theStreams,
const TopoDS_Shape& theShape,
const Message_ProgressRange& theProgress = Message_ProgressRange());
public:
//! Gets CAD format name of associated provider
//! @return provider CAD format

View File

@@ -0,0 +1,345 @@
// Copyright (c) 2025 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 <DE_ValidationUtils.hxx>
#include <Message.hxx>
#include <NCollection_Buffer.hxx>
#include <NCollection_BaseAllocator.hxx>
#include <OSD_FileSystem.hxx>
#include <OSD_Path.hxx>
#include <OSD_File.hxx>
#include <OSD_Protection.hxx>
#include <fstream>
//=================================================================================================
Standard_Boolean DE_ValidationUtils::ValidateConfigurationNode(
const Handle(DE_ConfigurationNode)& theNode,
const Handle(Standard_Type)& theExpectedType,
const TCollection_AsciiString& theContext,
const Standard_Boolean theIsVerbose)
{
if (theNode.IsNull())
{
if (theIsVerbose)
{
Message::SendFail() << "Error during " << theContext << ": Configuration Node is null";
}
return Standard_False;
}
if (!theNode->IsKind(theExpectedType))
{
if (theIsVerbose)
{
Message::SendFail() << "Error during " << theContext
<< ": Configuration Node is not of expected type. Expected: "
<< theExpectedType->Name() << ", got: " << theNode->DynamicType()->Name();
}
return Standard_False;
}
return Standard_True;
}
//=================================================================================================
Standard_Boolean DE_ValidationUtils::ValidateFileForReading(
const TCollection_AsciiString& thePath,
const TCollection_AsciiString& theContext,
const Standard_Boolean theIsVerbose)
{
if (thePath.IsEmpty())
{
if (theIsVerbose)
{
Message::SendFail() << "Error during " << theContext << ": File path is empty";
}
return Standard_False;
}
try
{
OSD_Path aOSDPath(thePath);
OSD_File aFile(aOSDPath);
// Check if file exists
if (!aFile.Exists())
{
if (theIsVerbose)
{
Message::SendFail() << "Error during " << theContext << ": File '" << thePath
<< "' does not exist";
}
return Standard_False;
}
// Try to open for reading to verify permissions
std::ifstream aTestFile(thePath.ToCString());
if (!aTestFile.is_open() || !aTestFile.good())
{
if (theIsVerbose)
{
Message::SendFail() << "Error during " << theContext << ": Cannot open file '" << thePath
<< "' for reading";
}
return Standard_False;
}
}
catch (const std::exception& anException)
{
if (theIsVerbose)
{
Message::SendFail() << "Error during " << theContext << ": Cannot access file '" << thePath
<< "': " << anException.what();
}
return Standard_False;
}
return Standard_True;
}
//=================================================================================================
Standard_Boolean DE_ValidationUtils::ValidateFileForWriting(
const TCollection_AsciiString& thePath,
const TCollection_AsciiString& theContext,
const Standard_Boolean theIsVerbose)
{
if (thePath.IsEmpty())
{
if (theIsVerbose)
{
Message::SendFail() << "Error during " << theContext << ": File path is empty";
}
return Standard_False;
}
try
{
// Try to open for writing to verify permissions
std::ofstream aTestFile(thePath.ToCString(), std::ios::out | std::ios::app);
if (!aTestFile.is_open() || !aTestFile.good())
{
if (theIsVerbose)
{
Message::SendFail() << "Error during " << theContext << ": Cannot open file '" << thePath
<< "' for writing";
}
return Standard_False;
}
// File will be closed automatically when aTestFile goes out of scope
}
catch (const std::exception& anException)
{
if (theIsVerbose)
{
Message::SendFail() << "Error during " << theContext << ": Cannot access file '" << thePath
<< "': " << anException.what();
}
return Standard_False;
}
return Standard_True;
}
//=================================================================================================
Standard_Boolean DE_ValidationUtils::ValidateReadStreamList(
const DE_Provider::ReadStreamList& theStreams,
const TCollection_AsciiString& theContext,
const Standard_Boolean theIsVerbose)
{
if (theStreams.IsEmpty())
{
if (theIsVerbose)
{
Message::SendFail() << "Error during " << theContext << ": Stream list is empty";
}
return Standard_False;
}
if (theStreams.Size() > 1)
{
if (theIsVerbose)
{
Message::SendWarning() << "Warning during " << theContext << ": Received "
<< theStreams.Size() << " streams, using only the first one";
}
}
// Additional validation for input streams
try
{
const DE_Provider::ReadStreamNode& aNode = theStreams.First();
if (aNode.Stream.fail())
{
if (theIsVerbose)
{
TCollection_AsciiString aKeyInfo = aNode.Path.IsEmpty() ? "<empty path>" : aNode.Path;
Message::SendFail() << "Error during " << theContext << ": Input stream '" << aKeyInfo
<< "' is in invalid state";
}
return Standard_False;
}
}
catch (const std::exception&)
{
if (theIsVerbose)
{
const DE_Provider::ReadStreamNode& aNode = theStreams.First();
TCollection_AsciiString aKeyInfo = aNode.Path.IsEmpty() ? "<empty path>" : aNode.Path;
Message::SendFail() << "Error during " << theContext << ": Cannot access input stream '"
<< aKeyInfo << "'";
}
return Standard_False;
}
return Standard_True;
}
//=================================================================================================
Standard_Boolean DE_ValidationUtils::ValidateWriteStreamList(
DE_Provider::WriteStreamList& theStreams,
const TCollection_AsciiString& theContext,
const Standard_Boolean theIsVerbose)
{
if (theStreams.IsEmpty())
{
if (theIsVerbose)
{
Message::SendFail() << "Error during " << theContext << ": Stream list is empty";
}
return Standard_False;
}
if (theStreams.Size() > 1)
{
if (theIsVerbose)
{
Message::SendWarning() << "Warning during " << theContext << ": Received "
<< theStreams.Size() << " streams, using only the first one";
}
}
// Additional validation for output streams
try
{
const DE_Provider::WriteStreamNode& aNode = theStreams.First();
if (aNode.Stream.fail())
{
if (theIsVerbose)
{
TCollection_AsciiString aKeyInfo = aNode.Path.IsEmpty() ? "<empty path>" : aNode.Path;
Message::SendFail() << "Error during " << theContext << ": Output stream '" << aKeyInfo
<< "' is in invalid state";
}
return Standard_False;
}
}
catch (const std::exception&)
{
if (theIsVerbose)
{
const DE_Provider::WriteStreamNode& aNode = theStreams.First();
TCollection_AsciiString aKeyInfo = aNode.Path.IsEmpty() ? "<empty path>" : aNode.Path;
Message::SendFail() << "Error during " << theContext << ": Cannot access output stream '"
<< aKeyInfo << "'";
}
return Standard_False;
}
return Standard_True;
}
//=================================================================================================
Standard_Boolean DE_ValidationUtils::ValidateDocument(const Handle(TDocStd_Document)& theDocument,
const TCollection_AsciiString& theContext,
const Standard_Boolean theIsVerbose)
{
if (theDocument.IsNull())
{
if (theIsVerbose)
{
Message::SendFail() << "Error during " << theContext << ": Document handle is null";
}
return Standard_False;
}
return Standard_True;
}
//=================================================================================================
Standard_Boolean DE_ValidationUtils::WarnLengthUnitNotSupported(
const Standard_Real theLengthUnit,
const TCollection_AsciiString& theContext,
const Standard_Boolean theIsVerbose)
{
if (theIsVerbose && theLengthUnit != 1.0)
{
Message::SendWarning() << "Warning during " << theContext
<< ": Format doesn't support custom length unit scaling (unit: "
<< theLengthUnit << ")";
}
return Standard_True;
}
//=================================================================================================
Standard_Boolean DE_ValidationUtils::CreateContentBuffer(const TCollection_AsciiString& thePath,
Handle(NCollection_Buffer)& theBuffer)
{
const Handle(OSD_FileSystem)& aFileSystem = OSD_FileSystem::DefaultFileSystem();
std::shared_ptr<std::istream> aStream =
aFileSystem->OpenIStream(thePath, std::ios::in | std::ios::binary);
if (aStream.get() == nullptr)
{
theBuffer.Nullify();
return Standard_False;
}
return CreateContentBuffer(*aStream, theBuffer);
}
//=================================================================================================
Standard_Boolean DE_ValidationUtils::CreateContentBuffer(std::istream& theStream,
Handle(NCollection_Buffer)& theBuffer)
{
constexpr std::streamsize aBufferLength = 2048;
theBuffer =
new NCollection_Buffer(NCollection_BaseAllocator::CommonBaseAllocator(), aBufferLength);
// Save current stream position
std::streampos aOriginalPos = theStream.tellg();
theStream.read(reinterpret_cast<char*>(theBuffer->ChangeData()), aBufferLength);
const std::streamsize aBytesRead = theStream.gcount();
theBuffer->ChangeData()[aBytesRead < aBufferLength ? aBytesRead : aBufferLength - 1] = '\0';
// Clear any error flags (including EOF) BEFORE attempting to reset position
// This is essential because seekg() fails when EOF flag is set
theStream.clear();
// Reset stream to original position for subsequent reads
theStream.seekg(aOriginalPos);
return Standard_True;
}

View File

@@ -0,0 +1,118 @@
// Copyright (c) 2025 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 _DE_ValidationUtils_HeaderFile
#define _DE_ValidationUtils_HeaderFile
#include <TCollection_AsciiString.hxx>
#include <DE_ConfigurationNode.hxx>
#include <DE_Provider.hxx>
class TDocStd_Document;
//! Utility class providing static methods for common validation operations
//! used across DataExchange providers. Includes validation for configuration nodes,
//! file paths, streams, and other common scenarios with optional verbose error reporting.
class DE_ValidationUtils
{
public:
//! Validates that configuration node is not null and matches expected type
//! @param[in] theNode configuration node to validate
//! @param[in] theExpectedType expected RTTI type
//! @param[in] theContext context string for error messages
//! @param[in] theIsVerbose if true, sends detailed error messages via Message::SendFail
//! @return Standard_True if node is valid, Standard_False otherwise
Standard_EXPORT static Standard_Boolean ValidateConfigurationNode(
const Handle(DE_ConfigurationNode)& theNode,
const Handle(Standard_Type)& theExpectedType,
const TCollection_AsciiString& theContext,
const Standard_Boolean theIsVerbose = Standard_True);
//! Checks if file exists and is readable
//! @param[in] thePath file path to check
//! @param[in] theContext context string for error messages
//! @param[in] theIsVerbose if true, sends detailed error messages via Message::SendFail
//! @return Standard_True if file exists and is readable, Standard_False otherwise
Standard_EXPORT static Standard_Boolean ValidateFileForReading(
const TCollection_AsciiString& thePath,
const TCollection_AsciiString& theContext,
const Standard_Boolean theIsVerbose = Standard_True);
//! Checks if file location is writable (file may or may not exist)
//! @param[in] thePath file path to check
//! @param[in] theContext context string for error messages
//! @param[in] theIsVerbose if true, sends detailed error messages via Message::SendFail
//! @return Standard_True if location is writable, Standard_False otherwise
Standard_EXPORT static Standard_Boolean ValidateFileForWriting(
const TCollection_AsciiString& thePath,
const TCollection_AsciiString& theContext,
const Standard_Boolean theIsVerbose = Standard_True);
//! Validates read stream list, warns if multiple streams
//! @param[in] theStreams read stream list to validate
//! @param[in] theContext context string for error messages
//! @param[in] theIsVerbose if true, sends detailed error/warning messages
//! @return Standard_True if stream list is valid, Standard_False otherwise
Standard_EXPORT static Standard_Boolean ValidateReadStreamList(
const DE_Provider::ReadStreamList& theStreams,
const TCollection_AsciiString& theContext,
const Standard_Boolean theIsVerbose = Standard_True);
//! Validates write stream list, warns if multiple streams
//! @param[in] theStreams write stream list to validate
//! @param[in] theContext context string for error messages
//! @param[in] theIsVerbose if true, sends detailed error/warning messages
//! @return Standard_True if stream list is valid, Standard_False otherwise
Standard_EXPORT static Standard_Boolean ValidateWriteStreamList(
DE_Provider::WriteStreamList& theStreams,
const TCollection_AsciiString& theContext,
const Standard_Boolean theIsVerbose = Standard_True);
//! Validates that TDocStd_Document handle is not null
//! @param[in] theDocument document to validate
//! @param[in] theContext context string for error messages
//! @param[in] theIsVerbose if true, sends detailed error messages via Message::SendFail
//! @return Standard_True if document is not null, Standard_False otherwise
Standard_EXPORT static Standard_Boolean ValidateDocument(
const Handle(TDocStd_Document)& theDocument,
const TCollection_AsciiString& theContext,
const Standard_Boolean theIsVerbose = Standard_True);
//! Sends warning when format doesn't support length unit scaling
//! @param[in] theLengthUnit length unit value to check
//! @param[in] theContext context string for warning messages
//! @param[in] theIsVerbose if true, sends warning messages via Message::SendWarning
//! @return Standard_True always (this is just a warning)
Standard_EXPORT static Standard_Boolean WarnLengthUnitNotSupported(
const Standard_Real theLengthUnit,
const TCollection_AsciiString& theContext,
const Standard_Boolean theIsVerbose = Standard_True);
//! Creates buffer by reading from file stream for content checking
//! @param[in] thePath file path for reading
//! @param[out] theBuffer output buffer with file content
//! @return Standard_True if successful, Standard_False otherwise
Standard_EXPORT static Standard_Boolean CreateContentBuffer(
const TCollection_AsciiString& thePath,
Handle(NCollection_Buffer)& theBuffer);
//! Creates buffer by reading from input stream for content checking
//! @param[in,out] theStream input stream to read from (position will be restored)
//! @param[out] theBuffer output buffer with stream content
//! @return Standard_True if successful, Standard_False otherwise
Standard_EXPORT static Standard_Boolean CreateContentBuffer(
std::istream& theStream,
Handle(NCollection_Buffer)& theBuffer);
};
#endif // _DE_ValidationUtils_HeaderFile

View File

@@ -16,6 +16,7 @@
#include <DE_ConfigurationContext.hxx>
#include <DE_ConfigurationNode.hxx>
#include <DE_Provider.hxx>
#include <DE_ValidationUtils.hxx>
#include <Message_ProgressRange.hxx>
#include <NCollection_Buffer.hxx>
#include <OSD_File.hxx>
@@ -499,18 +500,26 @@ Standard_Boolean DE_Wrapper::FindProvider(const TCollection_AsciiString& thePath
const Standard_Boolean theToImport,
Handle(DE_Provider)& theProvider) const
{
Handle(NCollection_Buffer) aBuffer;
if (theToImport)
{
const Handle(OSD_FileSystem)& aFileSystem = OSD_FileSystem::DefaultFileSystem();
std::shared_ptr<std::istream> aStream =
aFileSystem->OpenIStream(thePath, std::ios::in | std::ios::binary);
if (aStream.get() != nullptr)
{
aBuffer = new NCollection_Buffer(NCollection_BaseAllocator::CommonBaseAllocator(), 2048);
aStream->read((char*)aBuffer->ChangeData(), 2048);
aBuffer->ChangeData()[2047] = '\0';
}
return FindReadProvider(thePath, Standard_True, theProvider);
}
else
{
return FindWriteProvider(thePath, theProvider);
}
}
//=================================================================================================
Standard_Boolean DE_Wrapper::FindReadProvider(const TCollection_AsciiString& thePath,
const Standard_Boolean theCheckContent,
Handle(DE_Provider)& theProvider) const
{
Handle(NCollection_Buffer) aBuffer;
if (theCheckContent && !DE_ValidationUtils::CreateContentBuffer(thePath, aBuffer))
{
return Standard_False;
}
OSD_Path aPath(thePath);
const TCollection_AsciiString anExtr = aPath.Extension();
@@ -521,11 +530,69 @@ Standard_Boolean DE_Wrapper::FindProvider(const TCollection_AsciiString& thePath
aVendorIter.Next())
{
const Handle(DE_ConfigurationNode)& aNode = aVendorIter.Value();
if (aNode->IsEnabled()
&& ((theToImport && aNode->IsImportSupported())
|| (!theToImport && aNode->IsExportSupported()))
&& (aNode->CheckExtension(anExtr) || (theToImport && aNode->CheckContent(aBuffer)))
&& aNode->UpdateLoad(theToImport, myKeepUpdates))
if (aNode->IsEnabled() && aNode->IsImportSupported()
&& (aNode->CheckExtension(anExtr) || (theCheckContent && aNode->CheckContent(aBuffer)))
&& aNode->UpdateLoad(Standard_True, myKeepUpdates))
{
theProvider = aNode->BuildProvider();
aNode->GlobalParameters = GlobalParameters;
return Standard_True;
}
}
}
return Standard_False;
}
//=================================================================================================
Standard_Boolean DE_Wrapper::FindReadProvider(const TCollection_AsciiString& thePath,
std::istream& theStream,
Handle(DE_Provider)& theProvider) const
{
Handle(NCollection_Buffer) aBuffer;
if (!DE_ValidationUtils::CreateContentBuffer(theStream, aBuffer))
{
return Standard_False;
}
OSD_Path aPath(thePath);
const TCollection_AsciiString anExtr = aPath.Extension();
for (DE_ConfigurationFormatMap::Iterator aFormatIter(myConfiguration); aFormatIter.More();
aFormatIter.Next())
{
for (DE_ConfigurationVendorMap::Iterator aVendorIter(aFormatIter.Value()); aVendorIter.More();
aVendorIter.Next())
{
const Handle(DE_ConfigurationNode)& aNode = aVendorIter.Value();
if (aNode->IsEnabled() && aNode->IsImportSupported()
&& (aNode->CheckExtension(anExtr) || aNode->CheckContent(aBuffer))
&& aNode->UpdateLoad(Standard_True, myKeepUpdates))
{
theProvider = aNode->BuildProvider();
aNode->GlobalParameters = GlobalParameters;
return Standard_True;
}
}
}
return Standard_False;
}
//=================================================================================================
Standard_Boolean DE_Wrapper::FindWriteProvider(const TCollection_AsciiString& thePath,
Handle(DE_Provider)& theProvider) const
{
OSD_Path aPath(thePath);
const TCollection_AsciiString anExtr = aPath.Extension();
for (DE_ConfigurationFormatMap::Iterator aFormatIter(myConfiguration); aFormatIter.More();
aFormatIter.Next())
{
for (DE_ConfigurationVendorMap::Iterator aVendorIter(aFormatIter.Value()); aVendorIter.More();
aVendorIter.Next())
{
const Handle(DE_ConfigurationNode)& aNode = aVendorIter.Value();
if (aNode->IsEnabled() && aNode->IsExportSupported() && aNode->CheckExtension(anExtr)
&& aNode->UpdateLoad(Standard_False, myKeepUpdates))
{
theProvider = aNode->BuildProvider();
aNode->GlobalParameters = GlobalParameters;
@@ -572,3 +639,251 @@ void DE_Wrapper::sort(const Handle(DE_ConfigurationContext)& theResource)
ChangePriority(aFormatIter.Key(), aVendorPriority, Standard_True);
}
}
//=================================================================================================
Standard_Boolean DE_Wrapper::Read(DE_Provider::ReadStreamList& theStreams,
const Handle(TDocStd_Document)& theDocument,
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress)
{
if (!DE_ValidationUtils::ValidateReadStreamList(theStreams, "DE_Wrapper Read"))
{
return Standard_False;
}
const TCollection_AsciiString& aFirstKey = theStreams.First().Path;
Handle(DE_Provider) aProvider;
Standard_IStream& aFirstStream = theStreams.First().Stream;
if (!FindReadProvider(aFirstKey, aFirstStream, aProvider))
{
Message::SendFail() << "Error: DE_Wrapper cannot find provider for stream " << aFirstKey;
return Standard_False;
}
if (!aProvider->GetNode()->IsStreamSupported())
{
Message::SendFail() << "Error: Provider " << aProvider->GetFormat() << " "
<< aProvider->GetVendor() << " doesn't support stream operations";
return Standard_False;
}
return aProvider->Read(theStreams, theDocument, theWS, theProgress);
}
//=================================================================================================
Standard_Boolean DE_Wrapper::Write(DE_Provider::WriteStreamList& theStreams,
const Handle(TDocStd_Document)& theDocument,
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress)
{
if (!DE_ValidationUtils::ValidateWriteStreamList(theStreams, "DE_Wrapper Write"))
{
return Standard_False;
}
const TCollection_AsciiString& aFirstKey = theStreams.First().Path;
Handle(DE_Provider) aProvider;
if (!FindWriteProvider(aFirstKey, aProvider))
{
Message::SendFail() << "Error: DE_Wrapper cannot find provider for stream " << aFirstKey;
return Standard_False;
}
if (!aProvider->GetNode()->IsStreamSupported())
{
Message::SendFail() << "Error: Provider " << aProvider->GetFormat() << " "
<< aProvider->GetVendor() << " doesn't support stream operations";
return Standard_False;
}
return aProvider->Write(theStreams, theDocument, theWS, theProgress);
}
//=================================================================================================
Standard_Boolean DE_Wrapper::Read(DE_Provider::ReadStreamList& theStreams,
const Handle(TDocStd_Document)& theDocument,
const Message_ProgressRange& theProgress)
{
if (!DE_ValidationUtils::ValidateReadStreamList(theStreams, "DE_Wrapper Read"))
{
return Standard_False;
}
const TCollection_AsciiString& aFirstKey = theStreams.First().Path;
Handle(DE_Provider) aProvider;
Standard_IStream& aFirstStream = theStreams.First().Stream;
if (!FindReadProvider(aFirstKey, aFirstStream, aProvider))
{
Message::SendFail() << "Error: DE_Wrapper cannot find provider for stream " << aFirstKey;
return Standard_False;
}
if (!aProvider->GetNode()->IsStreamSupported())
{
Message::SendFail() << "Error: Provider " << aProvider->GetFormat() << " "
<< aProvider->GetVendor() << " doesn't support stream operations";
return Standard_False;
}
return aProvider->Read(theStreams, theDocument, theProgress);
}
//=================================================================================================
Standard_Boolean DE_Wrapper::Write(DE_Provider::WriteStreamList& theStreams,
const Handle(TDocStd_Document)& theDocument,
const Message_ProgressRange& theProgress)
{
if (!DE_ValidationUtils::ValidateWriteStreamList(theStreams, "DE_Wrapper Write"))
{
return Standard_False;
}
const TCollection_AsciiString& aFirstKey = theStreams.First().Path;
Handle(DE_Provider) aProvider;
if (!FindWriteProvider(aFirstKey, aProvider))
{
Message::SendFail() << "Error: DE_Wrapper cannot find provider for stream " << aFirstKey;
return Standard_False;
}
if (!aProvider->GetNode()->IsStreamSupported())
{
Message::SendFail() << "Error: Provider " << aProvider->GetFormat() << " "
<< aProvider->GetVendor() << " doesn't support stream operations";
return Standard_False;
}
return aProvider->Write(theStreams, theDocument, theProgress);
}
//=================================================================================================
Standard_Boolean DE_Wrapper::Read(DE_Provider::ReadStreamList& theStreams,
TopoDS_Shape& theShape,
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress)
{
if (!DE_ValidationUtils::ValidateReadStreamList(theStreams, "DE_Wrapper Read"))
{
return Standard_False;
}
const TCollection_AsciiString& aFirstKey = theStreams.First().Path;
Handle(DE_Provider) aProvider;
Standard_IStream& aFirstStream = theStreams.First().Stream;
if (!FindReadProvider(aFirstKey, aFirstStream, aProvider))
{
Message::SendFail() << "Error: DE_Wrapper cannot find provider for stream " << aFirstKey;
return Standard_False;
}
if (!aProvider->GetNode()->IsStreamSupported())
{
Message::SendFail() << "Error: Provider " << aProvider->GetFormat() << " "
<< aProvider->GetVendor() << " doesn't support stream operations";
return Standard_False;
}
return aProvider->Read(theStreams, theShape, theWS, theProgress);
}
//=================================================================================================
Standard_Boolean DE_Wrapper::Write(DE_Provider::WriteStreamList& theStreams,
const TopoDS_Shape& theShape,
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress)
{
if (!DE_ValidationUtils::ValidateWriteStreamList(theStreams, "DE_Wrapper Write"))
{
return Standard_False;
}
const TCollection_AsciiString& aFirstKey = theStreams.First().Path;
Handle(DE_Provider) aProvider;
if (!FindWriteProvider(aFirstKey, aProvider))
{
Message::SendFail() << "Error: DE_Wrapper cannot find provider for stream " << aFirstKey;
return Standard_False;
}
if (!aProvider->GetNode()->IsStreamSupported())
{
Message::SendFail() << "Error: Provider " << aProvider->GetFormat() << " "
<< aProvider->GetVendor() << " doesn't support stream operations";
return Standard_False;
}
return aProvider->Write(theStreams, theShape, theWS, theProgress);
}
//=================================================================================================
Standard_Boolean DE_Wrapper::Read(DE_Provider::ReadStreamList& theStreams,
TopoDS_Shape& theShape,
const Message_ProgressRange& theProgress)
{
if (!DE_ValidationUtils::ValidateReadStreamList(theStreams, "DE_Wrapper Read"))
{
return Standard_False;
}
const TCollection_AsciiString& aFirstKey = theStreams.First().Path;
Handle(DE_Provider) aProvider;
Standard_IStream& aFirstStream = theStreams.First().Stream;
if (!FindReadProvider(aFirstKey, aFirstStream, aProvider))
{
Message::SendFail() << "Error: DE_Wrapper cannot find provider for stream " << aFirstKey;
return Standard_False;
}
if (!aProvider->GetNode()->IsStreamSupported())
{
Message::SendFail() << "Error: Provider " << aProvider->GetFormat() << " "
<< aProvider->GetVendor() << " doesn't support stream operations";
return Standard_False;
}
return aProvider->Read(theStreams, theShape, theProgress);
}
//=================================================================================================
Standard_Boolean DE_Wrapper::Write(DE_Provider::WriteStreamList& theStreams,
const TopoDS_Shape& theShape,
const Message_ProgressRange& theProgress)
{
if (!DE_ValidationUtils::ValidateWriteStreamList(theStreams, "DE_Wrapper Write"))
{
return Standard_False;
}
const TCollection_AsciiString& aFirstKey = theStreams.First().Path;
Handle(DE_Provider) aProvider;
if (!FindWriteProvider(aFirstKey, aProvider))
{
Message::SendFail() << "Error: DE_Wrapper cannot find provider for stream " << aFirstKey;
return Standard_False;
}
if (!aProvider->GetNode()->IsStreamSupported())
{
Message::SendFail() << "Error: Provider " << aProvider->GetFormat() << " "
<< aProvider->GetVendor() << " doesn't support stream operations";
return Standard_False;
}
return aProvider->Write(theStreams, theShape, theProgress);
}

View File

@@ -15,6 +15,7 @@
#define _DE_Wrapper_HeaderFile
#include <DE_ConfigurationNode.hxx>
#include <DE_Provider.hxx>
#include <Message_ProgressRange.hxx>
#include <NCollection_DataMap.hxx>
#include <NCollection_IndexedDataMap.hxx>
@@ -163,6 +164,94 @@ public:
const TopoDS_Shape& theShape,
const Message_ProgressRange& theProgress = Message_ProgressRange());
//! Reads streams according to internal configuration
//! @param[in] theStreams streams to read from
//! @param[out] theDocument document to save result
//! @param[in] theWS current work session
//! @param[in] theProgress progress indicator
//! @return true if Read operation has ended correctly
Standard_EXPORT Standard_Boolean
Read(DE_Provider::ReadStreamList& theStreams,
const Handle(TDocStd_Document)& theDocument,
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress = Message_ProgressRange());
//! Writes streams according to internal configuration
//! @param[in] theStreams streams to write to
//! @param[out] theDocument document to export
//! @param[in] theWS current work session
//! @param[in] theProgress progress indicator
//! @return true if Write operation has ended correctly
Standard_EXPORT Standard_Boolean
Write(DE_Provider::WriteStreamList& theStreams,
const Handle(TDocStd_Document)& theDocument,
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress = Message_ProgressRange());
//! Reads streams according to internal configuration
//! @param[in] theStreams streams to read from
//! @param[out] theDocument document to save result
//! @param[in] theProgress progress indicator
//! @return true if Read operation has ended correctly
Standard_EXPORT Standard_Boolean
Read(DE_Provider::ReadStreamList& theStreams,
const Handle(TDocStd_Document)& theDocument,
const Message_ProgressRange& theProgress = Message_ProgressRange());
//! Writes streams according to internal configuration
//! @param[in] theStreams streams to write to
//! @param[out] theDocument document to export
//! @param[in] theProgress progress indicator
//! @return true if Write operation has ended correctly
Standard_EXPORT Standard_Boolean
Write(DE_Provider::WriteStreamList& theStreams,
const Handle(TDocStd_Document)& theDocument,
const Message_ProgressRange& theProgress = Message_ProgressRange());
//! Reads streams according to internal configuration
//! @param[in] theStreams streams to read from
//! @param[out] theShape shape to save result
//! @param[in] theWS current work session
//! @param[in] theProgress progress indicator
//! @return true if Read operation has ended correctly
Standard_EXPORT Standard_Boolean
Read(DE_Provider::ReadStreamList& theStreams,
TopoDS_Shape& theShape,
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress = Message_ProgressRange());
//! Writes streams according to internal configuration
//! @param[in] theStreams streams to write to
//! @param[out] theShape shape to export
//! @param[in] theWS current work session
//! @param[in] theProgress progress indicator
//! @return true if Write operation has ended correctly
Standard_EXPORT Standard_Boolean
Write(DE_Provider::WriteStreamList& theStreams,
const TopoDS_Shape& theShape,
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress = Message_ProgressRange());
//! Reads streams according to internal configuration
//! @param[in] theStreams streams to read from
//! @param[out] theShape shape to save result
//! @param[in] theProgress progress indicator
//! @return true if Read operation has ended correctly
Standard_EXPORT Standard_Boolean
Read(DE_Provider::ReadStreamList& theStreams,
TopoDS_Shape& theShape,
const Message_ProgressRange& theProgress = Message_ProgressRange());
//! Writes streams according to internal configuration
//! @param[in] theStreams streams to write to
//! @param[out] theShape shape to export
//! @param[in] theProgress progress indicator
//! @return true if Write operation has ended correctly
Standard_EXPORT Standard_Boolean
Write(DE_Provider::WriteStreamList& theStreams,
const TopoDS_Shape& theShape,
const Message_ProgressRange& theProgress = Message_ProgressRange());
public:
//! Updates values according the resource file
//! @param[in] theResource file path to resource or resource value
@@ -243,6 +332,35 @@ public:
const Standard_Boolean theToImport,
Handle(DE_Provider)& theProvider) const;
//! Find available read provider from the configuration for file-based operations.
//! If there are several providers, choose the one with the highest priority.
//! @param[in] thePath path to the CAD file (for extension and content checking)
//! @param[in] theCheckContent flag to enable content checking via file reading
//! @param[out] theProvider created new provider
//! @return Standard_True if provider found and created
Standard_EXPORT virtual Standard_Boolean FindReadProvider(const TCollection_AsciiString& thePath,
const Standard_Boolean theCheckContent,
Handle(DE_Provider)& theProvider) const;
//! Find available read provider from the configuration for stream-based operations.
//! If there are several providers, choose the one with the highest priority.
//! @param[in] thePath path to the CAD file (for extension extraction)
//! @param[in] theStream input stream for content checking
//! @param[out] theProvider created new provider
//! @return Standard_True if provider found and created
Standard_EXPORT virtual Standard_Boolean FindReadProvider(const TCollection_AsciiString& thePath,
std::istream& theStream,
Handle(DE_Provider)& theProvider) const;
//! Find available write provider from the configuration.
//! If there are several providers, choose the one with the highest priority.
//! @param[in] thePath path to the CAD file (for extension checking only)
//! @param[out] theProvider created new provider
//! @return Standard_True if provider found and created
Standard_EXPORT virtual Standard_Boolean FindWriteProvider(
const TCollection_AsciiString& thePath,
Handle(DE_Provider)& theProvider) const;
//! Updates all registered nodes, all changes will be saved in nodes
//! @param[in] theToForceUpdate flag that turns on/of nodes, according to updated ability to
//! import/export

View File

@@ -12,6 +12,8 @@ set(OCCT_DE_FILES
DE_ShapeFixConfigurationNode.cxx
DE_ShapeFixConfigurationNode.hxx
DE_ShapeFixParameters.hxx
DE_ValidationUtils.cxx
DE_ValidationUtils.hxx
DE_Wrapper.cxx
DE_Wrapper.hxx
)

View File

@@ -13,6 +13,7 @@
#include <DEGLTF_Provider.hxx>
#include <DE_ValidationUtils.hxx>
#include <Message.hxx>
#include <RWGltf_CafWriter.hxx>
#include <TDocStd_Document.hxx>
@@ -86,17 +87,15 @@ bool DEGLTF_Provider::Read(const TCollection_AsciiString& thePath,
const Handle(TDocStd_Document)& theDocument,
const Message_ProgressRange& theProgress)
{
if (theDocument.IsNull())
TCollection_AsciiString aContext = TCollection_AsciiString("reading the file ") + thePath;
if (!DE_ValidationUtils::ValidateDocument(theDocument, aContext))
{
Message::SendFail() << "Error in the DEGLTF_Provider during reading the file " << thePath
<< "\t: theDocument shouldn't be null";
return false;
}
if (GetNode().IsNull()
|| (!GetNode().IsNull() && !GetNode()->IsKind(STANDARD_TYPE(DEGLTF_ConfigurationNode))))
if (!DE_ValidationUtils::ValidateConfigurationNode(GetNode(),
STANDARD_TYPE(DEGLTF_ConfigurationNode),
aContext))
{
Message::SendFail() << "Error in the DEGLTF_Provider during reading the file " << thePath
<< "\t: Incorrect or empty Configuration Node";
return false;
}
Handle(DEGLTF_ConfigurationNode) aNode = Handle(DEGLTF_ConfigurationNode)::DownCast(GetNode());
@@ -121,10 +120,11 @@ bool DEGLTF_Provider::Write(const TCollection_AsciiString& thePath,
const Handle(TDocStd_Document)& theDocument,
const Message_ProgressRange& theProgress)
{
if (GetNode().IsNull() || !GetNode()->IsKind(STANDARD_TYPE(DEGLTF_ConfigurationNode)))
TCollection_AsciiString aContext = TCollection_AsciiString("writing the file ") + thePath;
if (!DE_ValidationUtils::ValidateConfigurationNode(GetNode(),
STANDARD_TYPE(DEGLTF_ConfigurationNode),
aContext))
{
Message::SendFail() << "Error in the DEGLTF_Provider during writing the file " << thePath
<< "\t: Incorrect or empty Configuration Node";
return false;
}
Handle(DEGLTF_ConfigurationNode) aNode = Handle(DEGLTF_ConfigurationNode)::DownCast(GetNode());
@@ -141,9 +141,11 @@ bool DEGLTF_Provider::Write(const TCollection_AsciiString& thePath,
aConverter.SetInputCoordinateSystem(aNode->InternalParameters.SystemCS);
if (aNode->GlobalParameters.LengthUnit != 1000.)
{
TCollection_AsciiString aContext = TCollection_AsciiString("writing the file ") + thePath;
Message::SendWarning()
<< "Warning in the DEGLTF_Provider during writing the file " << thePath
<< "\t: Target format doesn't support custom units. Model will be scaled to Meters";
<< "Warning during " << aContext
<< ": Target format doesn't support custom units. Model will be scaled to Meters (unit: "
<< aNode->GlobalParameters.LengthUnit << ")";
}
aConverter.SetOutputLengthUnit(1.); // gltf units always Meters
aConverter.SetOutputCoordinateSystem(aNode->InternalParameters.FileCS);

View File

@@ -13,6 +13,7 @@
#include <DEIGES_Provider.hxx>
#include <DE_ValidationUtils.hxx>
#include <DEIGES_ConfigurationNode.hxx>
#include <IGESCAFControl_Reader.hxx>
#include <IGESCAFControl_Writer.hxx>
@@ -27,6 +28,128 @@
IMPLEMENT_STANDARD_RTTIEXT(DEIGES_Provider, DE_Provider)
namespace
{
// Helper function to validate configuration node
Standard_Boolean validateConfigurationNode(const Handle(DE_ConfigurationNode)& theNode,
const TCollection_AsciiString& theContext)
{
return DE_ValidationUtils::ValidateConfigurationNode(theNode,
STANDARD_TYPE(DEIGES_ConfigurationNode),
theContext);
}
// Helper function to configure IGES CAF reader parameters
void configureIGESCAFReader(IGESCAFControl_Reader& theReader,
const Handle(DEIGES_ConfigurationNode)& theNode)
{
theReader.SetReadVisible(theNode->InternalParameters.ReadOnlyVisible);
theReader.SetColorMode(theNode->InternalParameters.ReadColor);
theReader.SetNameMode(theNode->InternalParameters.ReadName);
theReader.SetLayerMode(theNode->InternalParameters.ReadLayer);
theReader.SetShapeFixParameters(theNode->ShapeFixParameters);
}
// Helper function to configure IGES control reader parameters
void configureIGESControlReader(IGESControl_Reader& theReader,
const Handle(DEIGES_ConfigurationNode)& theNode)
{
theReader.SetReadVisible(theNode->InternalParameters.ReadOnlyVisible);
theReader.SetShapeFixParameters(theNode->ShapeFixParameters);
}
// Helper function to setup IGES unit configuration
void setupIGESUnits(IGESData_GlobalSection& theGS,
const Handle(DEIGES_ConfigurationNode)& theNode,
const Handle(TDocStd_Document)& theDocument,
const TCollection_AsciiString& thePath,
Standard_Boolean theUseDocumentUnits)
{
Standard_Integer aFlag =
IGESData_BasicEditor::GetFlagByValue(theNode->GlobalParameters.LengthUnit);
if (theUseDocumentUnits && !theDocument.IsNull())
{
Standard_Real aScaleFactorMM = 1.;
Standard_Boolean aHasUnits =
XCAFDoc_DocumentTool::GetLengthUnit(theDocument,
aScaleFactorMM,
UnitsMethods_LengthUnit_Millimeter);
if (aHasUnits)
{
theGS.SetCascadeUnit(aScaleFactorMM);
}
else
{
theGS.SetCascadeUnit(theNode->GlobalParameters.SystemUnit);
Message::SendWarning()
<< "Warning in the DEIGES_Provider during writing the file " << thePath
<< "\t: The document has no information on Units. Using global parameter as initial Unit.";
}
}
else
{
theGS.SetCascadeUnit(theNode->GlobalParameters.SystemUnit);
}
if (aFlag == 0)
{
theGS.SetScale(theNode->GlobalParameters.LengthUnit);
}
}
// Helper function to configure IGES CAF writer parameters
void configureIGESCAFWriter(IGESCAFControl_Writer& theWriter,
const Handle(DEIGES_ConfigurationNode)& theNode,
const Handle(TDocStd_Document)& theDocument,
const TCollection_AsciiString& thePath)
{
IGESData_GlobalSection aGS = theWriter.Model()->GlobalSection();
setupIGESUnits(aGS, theNode, theDocument, thePath, Standard_True);
theWriter.Model()->SetGlobalSection(aGS);
theWriter.SetColorMode(theNode->InternalParameters.WriteColor);
theWriter.SetNameMode(theNode->InternalParameters.WriteName);
theWriter.SetLayerMode(theNode->InternalParameters.WriteLayer);
theWriter.SetShapeFixParameters(theNode->ShapeFixParameters);
}
// Helper function to configure IGES control writer for shapes
void configureIGESControlWriter(IGESControl_Writer& theWriter,
const Handle(DEIGES_ConfigurationNode)& theNode)
{
IGESData_GlobalSection aGS = theWriter.Model()->GlobalSection();
Handle(TDocStd_Document) aNullDoc;
setupIGESUnits(aGS, theNode, aNullDoc, "", Standard_False);
theWriter.Model()->SetGlobalSection(aGS);
theWriter.SetShapeFixParameters(theNode->ShapeFixParameters);
}
// Helper function to setup IGES writer unit flags
TCollection_AsciiString getIGESUnitString(const Handle(DEIGES_ConfigurationNode)& theNode)
{
Standard_Integer aFlag =
IGESData_BasicEditor::GetFlagByValue(theNode->GlobalParameters.LengthUnit);
return (aFlag > 0) ? IGESData_BasicEditor::UnitFlagName(aFlag) : "MM";
}
// Helper function to process read file operation
Standard_Boolean processReadFile(IGESControl_Reader& theReader,
const TCollection_AsciiString& thePath)
{
IFSelect_ReturnStatus aReadStat = theReader.ReadFile(thePath.ToCString());
if (aReadStat != IFSelect_RetDone)
{
Message::SendFail() << "Error in the DEIGES_Provider during reading the file " << thePath
<< "\t: abandon, no model loaded";
return Standard_False;
}
return Standard_True;
}
} // namespace
//=================================================================================================
DEIGES_Provider::DEIGES_Provider() {}
@@ -160,35 +283,26 @@ bool DEIGES_Provider::Read(const TCollection_AsciiString& thePath,
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress)
{
if (theDocument.IsNull())
TCollection_AsciiString aContext = TCollection_AsciiString("reading the file ") + thePath;
if (!DE_ValidationUtils::ValidateDocument(theDocument, aContext)
|| !validateConfigurationNode(GetNode(), aContext))
{
Message::SendFail() << "Error in the DEIGES_Provider during reading the file " << thePath
<< "\t: theDocument shouldn't be null";
return false;
}
if (!GetNode()->IsKind(STANDARD_TYPE(DEIGES_ConfigurationNode)))
{
Message::SendFail() << "Error in the DEIGES_Provider during reading the file " << thePath
<< "\t: Incorrect or empty Configuration Node";
return false;
}
Handle(DEIGES_ConfigurationNode) aNode = Handle(DEIGES_ConfigurationNode)::DownCast(GetNode());
personizeWS(theWS);
initStatic(aNode);
XCAFDoc_DocumentTool::SetLengthUnit(theDocument,
aNode->GlobalParameters.LengthUnit,
UnitsMethods_LengthUnit_Millimeter);
IGESCAFControl_Reader aReader;
aReader.SetWS(theWS);
configureIGESCAFReader(aReader, aNode);
aReader.SetReadVisible(aNode->InternalParameters.ReadOnlyVisible);
aReader.SetColorMode(aNode->InternalParameters.ReadColor);
aReader.SetNameMode(aNode->InternalParameters.ReadName);
aReader.SetLayerMode(aNode->InternalParameters.ReadLayer);
aReader.SetShapeFixParameters(aNode->ShapeFixParameters);
IFSelect_ReturnStatus aReadStat = IFSelect_RetVoid;
aReadStat = aReader.ReadFile(thePath.ToCString());
IFSelect_ReturnStatus aReadStat = aReader.ReadFile(thePath.ToCString());
if (aReadStat != IFSelect_RetDone)
{
Message::SendFail() << "Error in the DEIGES_Provider during reading the file " << thePath
@@ -215,54 +329,30 @@ bool DEIGES_Provider::Write(const TCollection_AsciiString& thePath,
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress)
{
if (!GetNode()->IsKind(STANDARD_TYPE(DEIGES_ConfigurationNode)))
TCollection_AsciiString aContext = TCollection_AsciiString("writing the file ") + thePath;
if (!DE_ValidationUtils::ValidateDocument(theDocument, aContext)
|| !validateConfigurationNode(GetNode(), aContext))
{
Message::SendFail() << "Error in the DEIGES_Provider during reading the file " << thePath
<< "\t: Incorrect or empty Configuration Node";
return false;
}
Handle(DEIGES_ConfigurationNode) aNode = Handle(DEIGES_ConfigurationNode)::DownCast(GetNode());
personizeWS(theWS);
initStatic(aNode);
Standard_Integer aFlag = IGESData_BasicEditor::GetFlagByValue(aNode->GlobalParameters.LengthUnit);
IGESCAFControl_Writer aWriter(theWS,
(aFlag > 0) ? IGESData_BasicEditor::UnitFlagName(aFlag) : "MM");
IGESData_GlobalSection aGS = aWriter.Model()->GlobalSection();
Standard_Real aScaleFactorMM = 1.;
Standard_Boolean aHasUnits =
XCAFDoc_DocumentTool::GetLengthUnit(theDocument,
aScaleFactorMM,
UnitsMethods_LengthUnit_Millimeter);
if (aHasUnits)
{
aGS.SetCascadeUnit(aScaleFactorMM);
}
else
{
aGS.SetCascadeUnit(aNode->GlobalParameters.SystemUnit);
Message::SendWarning()
<< "Warning in the DEIGES_Provider during writing the file " << thePath
<< "\t: The document has no information on Units. Using global parameter as initial Unit.";
}
if (aFlag == 0)
{
aGS.SetScale(aNode->GlobalParameters.LengthUnit);
}
aWriter.Model()->SetGlobalSection(aGS);
aWriter.SetColorMode(aNode->InternalParameters.WriteColor);
aWriter.SetNameMode(aNode->InternalParameters.WriteName);
aWriter.SetLayerMode(aNode->InternalParameters.WriteLayer);
aWriter.SetShapeFixParameters(aNode->ShapeFixParameters);
IGESCAFControl_Writer aWriter(theWS, Standard_False);
configureIGESCAFWriter(aWriter, aNode, theDocument, thePath);
if (!aWriter.Transfer(theDocument, theProgress))
{
Message::SendFail() << "Error in the DEIGES_Provider during reading the file " << thePath
Message::SendFail() << "Error in the DEIGES_Provider during writing the file " << thePath
<< "\t: The document cannot be translated or gives no result";
resetStatic();
return false;
}
if (!aWriter.Write(thePath.ToCString()))
{
Message::SendFail() << "Error in the DEIGES_Provider during reading the file " << thePath
Message::SendFail() << "Error in the DEIGES_Provider during writing the file " << thePath
<< "\t: Write failed";
resetStatic();
return false;
@@ -298,31 +388,26 @@ bool DEIGES_Provider::Read(const TCollection_AsciiString& thePath,
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress)
{
(void)theProgress;
if (!GetNode()->IsKind(STANDARD_TYPE(DEIGES_ConfigurationNode)))
if (!validateConfigurationNode(GetNode(), TCollection_AsciiString("reading the file ") + thePath))
{
Message::SendFail() << "Error in the DEIGES_Provider during reading the file " << thePath
<< "\t: Incorrect or empty Configuration Node";
return false;
}
Handle(DEIGES_ConfigurationNode) aNode = Handle(DEIGES_ConfigurationNode)::DownCast(GetNode());
initStatic(aNode);
personizeWS(theWS);
IGESControl_Reader aReader;
aReader.SetWS(theWS);
aReader.SetReadVisible(aNode->InternalParameters.ReadOnlyVisible);
aReader.SetShapeFixParameters(aNode->ShapeFixParameters);
configureIGESControlReader(aReader, aNode);
IFSelect_ReturnStatus aReadStat = IFSelect_RetVoid;
aReadStat = aReader.ReadFile(thePath.ToCString());
if (aReadStat != IFSelect_RetDone)
if (!processReadFile(aReader, thePath))
{
Message::SendFail() << "Error in the DEIGES_Provider during reading the file " << thePath
<< "\t: Could not read file, no model loaded";
resetStatic();
return false;
}
if (aReader.TransferRoots() <= 0)
if (aReader.TransferRoots(theProgress) <= 0)
{
Message::SendFail() << "Error in the DEIGES_Provider during reading the file " << thePath
<< "\t: Cannot read any relevant data from the IGES file";
@@ -341,28 +426,21 @@ bool DEIGES_Provider::Write(const TCollection_AsciiString& thePath,
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress)
{
(void)theWS;
(void)theProgress;
if (!GetNode()->IsKind(STANDARD_TYPE(DEIGES_ConfigurationNode)))
TCollection_AsciiString aContext = TCollection_AsciiString("writing the file ") + thePath;
if (!validateConfigurationNode(GetNode(), aContext))
{
Message::SendFail() << "Error in the DEIGES_Provider during reading the file " << thePath
<< "\t: Incorrect or empty Configuration Node";
return false;
}
Handle(DEIGES_ConfigurationNode) aNode = Handle(DEIGES_ConfigurationNode)::DownCast(GetNode());
initStatic(aNode);
Standard_Integer aFlag = IGESData_BasicEditor::GetFlagByValue(aNode->GlobalParameters.LengthUnit);
IGESControl_Writer aWriter((aFlag > 0) ? IGESData_BasicEditor::UnitFlagName(aFlag) : "MM",
personizeWS(theWS);
IGESControl_Writer aWriter(getIGESUnitString(aNode).ToCString(),
aNode->InternalParameters.WriteBRepMode);
IGESData_GlobalSection aGS = aWriter.Model()->GlobalSection();
aGS.SetCascadeUnit(aNode->GlobalParameters.SystemUnit);
if (!aFlag)
{
aGS.SetScale(aNode->GlobalParameters.LengthUnit);
}
aWriter.Model()->SetGlobalSection(aGS);
aWriter.SetShapeFixParameters(aNode->ShapeFixParameters);
Standard_Boolean aIsOk = aWriter.AddShape(theShape);
configureIGESControlWriter(aWriter, aNode);
Standard_Boolean aIsOk = aWriter.AddShape(theShape, theProgress);
if (!aIsOk)
{
Message::SendFail() << "DEIGES_Provider: Shape not written";
@@ -370,7 +448,7 @@ bool DEIGES_Provider::Write(const TCollection_AsciiString& thePath,
return false;
}
if (!(aWriter.Write(thePath.ToCString())))
if (!aWriter.Write(thePath.ToCString()))
{
Message::SendFail() << "DEIGES_Provider: Error on writing file " << thePath;
resetStatic();
@@ -413,3 +491,253 @@ TCollection_AsciiString DEIGES_Provider::GetVendor() const
{
return TCollection_AsciiString("OCC");
}
/*
// TODO: Implement IGES stream support
//=================================================================================================
Standard_Boolean DEIGES_Provider::Read(ReadStreamList& theStreams,
const Handle(TDocStd_Document)& theDocument,
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress)
{
TCollection_AsciiString aContext = "reading stream";
if (!DE_ValidationUtils::ValidateReadStreamList(theStreams, aContext))
{
return Standard_False;
}
const TCollection_AsciiString& aFirstKey = theStreams.First().Path;
TCollection_AsciiString aFullContext = aContext + " " + aFirstKey;
Standard_IStream& aStream = theStreams.First().Stream;
if (!DE_ValidationUtils::ValidateDocument(theDocument, aFullContext)
|| !validateConfigurationNode(GetNode(), aFullContext))
{
return Standard_False;
}
Handle(DEIGES_ConfigurationNode) aNode = Handle(DEIGES_ConfigurationNode)::DownCast(GetNode());
initStatic(aNode);
personizeWS(theWS);
XCAFDoc_DocumentTool::SetLengthUnit(theDocument,
aNode->GlobalParameters.LengthUnit,
UnitsMethods_LengthUnit_Millimeter);
IGESCAFControl_Reader aReader;
aReader.SetWS(theWS);
configureIGESCAFReader(aReader, aNode);
IFSelect_ReturnStatus aReadStat = aReader.ReadStream(aFirstKey.ToCString(), aStream);
if (aReadStat != IFSelect_RetDone)
{
Message::SendFail() << "Error in the DEIGES_Provider during reading stream " << aFirstKey
<< "\t: abandon, no model loaded";
resetStatic();
return Standard_False;
}
if (!aReader.Transfer(theDocument, theProgress))
{
Message::SendFail() << "Error in the DEIGES_Provider during reading stream " << aFirstKey
<< "\t: Cannot read any relevant data from the IGES stream";
resetStatic();
return Standard_False;
}
resetStatic();
return Standard_True;
}
//=================================================================================================
Standard_Boolean DEIGES_Provider::Write(WriteStreamList& theStreams,
const Handle(TDocStd_Document)& theDocument,
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress)
{
TCollection_AsciiString aContext = "writing stream";
if (!DE_ValidationUtils::ValidateWriteStreamList(theStreams, aContext))
{
return Standard_False;
}
const TCollection_AsciiString& aFirstKey = theStreams.First().Path;
Standard_OStream& aStream = theStreams.First().Stream;
TCollection_AsciiString aFullContext = aContext + " " + aFirstKey;
if (!DE_ValidationUtils::ValidateDocument(theDocument, aFullContext)
|| !validateConfigurationNode(GetNode(), aFullContext))
{
return Standard_False;
}
Handle(DEIGES_ConfigurationNode) aNode = Handle(DEIGES_ConfigurationNode)::DownCast(GetNode());
initStatic(aNode);
personizeWS(theWS);
IGESCAFControl_Writer aWriter(theWS, Standard_False);
configureIGESCAFWriter(aWriter, aNode, theDocument, aFirstKey);
if (!aWriter.Transfer(theDocument, theProgress))
{
Message::SendFail() << "Error in the DEIGES_Provider during writing stream " << aFirstKey
<< "\t: The document cannot be translated or gives no result";
resetStatic();
return Standard_False;
}
if (!aWriter.Write(aStream))
{
Message::SendFail() << "Error in the DEIGES_Provider during writing stream " << aFirstKey
<< "\t: Write failed";
resetStatic();
return Standard_False;
}
resetStatic();
return Standard_True;
}
//=================================================================================================
Standard_Boolean DEIGES_Provider::Read(ReadStreamList& theStreams,
TopoDS_Shape& theShape,
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress)
{
TCollection_AsciiString aContext = "reading stream";
if (!DE_ValidationUtils::ValidateReadStreamList(theStreams, aContext))
{
return Standard_False;
}
const TCollection_AsciiString& aFirstKey = theStreams.First().Path;
Standard_IStream& aStream = theStreams.First().Stream;
TCollection_AsciiString aFullContext = aContext + " " + aFirstKey;
if (!validateConfigurationNode(GetNode(), aFullContext))
{
return Standard_False;
}
Handle(DEIGES_ConfigurationNode) aNode = Handle(DEIGES_ConfigurationNode)::DownCast(GetNode());
initStatic(aNode);
personizeWS(theWS);
IGESControl_Reader aReader;
aReader.SetWS(theWS);
configureIGESControlReader(aReader, aNode);
IFSelect_ReturnStatus aReadStat = aReader.ReadStream(aFirstKey.ToCString(), aStream);
if (aReadStat != IFSelect_RetDone)
{
Message::SendFail() << "Error in the DEIGES_Provider during reading stream " << aFirstKey
<< "\t: Could not read stream, no model loaded";
resetStatic();
return Standard_False;
}
if (aReader.TransferRoots(theProgress) <= 0)
{
Message::SendFail() << "Error in the DEIGES_Provider during reading stream " << aFirstKey
<< "\t: Cannot read any relevant data from the IGES stream";
resetStatic();
return Standard_False;
}
theShape = aReader.OneShape();
resetStatic();
return Standard_True;
}
//=================================================================================================
Standard_Boolean DEIGES_Provider::Write(WriteStreamList& theStreams,
const TopoDS_Shape& theShape,
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress)
{
TCollection_AsciiString aContext = "writing stream";
if (!DE_ValidationUtils::ValidateWriteStreamList(theStreams, aContext))
{
return Standard_False;
}
const TCollection_AsciiString& aFirstKey = theStreams.First().Path;
Standard_OStream& aStream = theStreams.First().Stream;
TCollection_AsciiString aFullContext = aContext + " " + aFirstKey;
if (!validateConfigurationNode(GetNode(), aFullContext))
{
return Standard_False;
}
Handle(DEIGES_ConfigurationNode) aNode = Handle(DEIGES_ConfigurationNode)::DownCast(GetNode());
initStatic(aNode);
personizeWS(theWS);
IGESControl_Writer aWriter(getIGESUnitString(aNode).ToCString(),
aNode->InternalParameters.WriteBRepMode);
configureIGESControlWriter(aWriter, aNode);
Standard_Boolean isOk = aWriter.AddShape(theShape, theProgress);
if (!isOk)
{
Message::SendFail() << "Error: DEIGES_Provider failed to transfer shape for stream "
<< aFirstKey;
resetStatic();
return Standard_False;
}
if (!aWriter.Write(aStream))
{
Message::SendFail() << "Error: DEIGES_Provider failed to write shape to stream " << aFirstKey;
resetStatic();
return Standard_False;
}
resetStatic();
return Standard_True;
}
//=================================================================================================
Standard_Boolean DEIGES_Provider::Read(ReadStreamList& theStreams,
const Handle(TDocStd_Document)& theDocument,
const Message_ProgressRange& theProgress)
{
Handle(XSControl_WorkSession) aWS = new XSControl_WorkSession();
return Read(theStreams, theDocument, aWS, theProgress);
}
//=================================================================================================
Standard_Boolean DEIGES_Provider::Write(WriteStreamList& theStreams,
const Handle(TDocStd_Document)& theDocument,
const Message_ProgressRange& theProgress)
{
Handle(XSControl_WorkSession) aWS = new XSControl_WorkSession();
return Write(theStreams, theDocument, aWS, theProgress);
}
//=================================================================================================
Standard_Boolean DEIGES_Provider::Read(ReadStreamList& theStreams,
TopoDS_Shape& theShape,
const Message_ProgressRange& theProgress)
{
Handle(XSControl_WorkSession) aWS = new XSControl_WorkSession();
return Read(theStreams, theShape, aWS, theProgress);
}
//=================================================================================================
Standard_Boolean DEIGES_Provider::Write(WriteStreamList& theStreams,
const TopoDS_Shape& theShape,
const Message_ProgressRange& theProgress)
{
Handle(XSControl_WorkSession) aWS = new XSControl_WorkSession();
return Write(theStreams, theShape, aWS, theProgress);
}
*/

View File

@@ -14,6 +14,7 @@
#include <DEOBJ_Provider.hxx>
#include <BRep_Builder.hxx>
#include <DE_ValidationUtils.hxx>
#include <DEOBJ_ConfigurationNode.hxx>
#include <RWObj_CafReader.hxx>
#include <RWObj_CafWriter.hxx>
@@ -62,16 +63,15 @@ bool DEOBJ_Provider::Read(const TCollection_AsciiString& thePath,
const Handle(TDocStd_Document)& theDocument,
const Message_ProgressRange& theProgress)
{
if (theDocument.IsNull())
TCollection_AsciiString aContext = TCollection_AsciiString("reading the file ") + thePath;
if (!DE_ValidationUtils::ValidateDocument(theDocument, aContext))
{
Message::SendFail() << "Error in the DEOBJ_Provider during reading the file " << thePath
<< "\t: theDocument shouldn't be null";
return false;
}
if (GetNode().IsNull() || !GetNode()->IsKind(STANDARD_TYPE(DEOBJ_ConfigurationNode)))
if (!DE_ValidationUtils::ValidateConfigurationNode(GetNode(),
STANDARD_TYPE(DEOBJ_ConfigurationNode),
aContext))
{
Message::SendFail() << "Error in the DEOBJ_ConfigurationNode during reading the file "
<< thePath << "\t: Incorrect or empty Configuration Node";
return false;
}
Handle(DEOBJ_ConfigurationNode) aNode = Handle(DEOBJ_ConfigurationNode)::DownCast(GetNode());

View File

@@ -14,6 +14,7 @@
#include <DEPLY_Provider.hxx>
#include <BRep_Builder.hxx>
#include <DE_ValidationUtils.hxx>
#include <DEPLY_ConfigurationNode.hxx>
#include <DE_Wrapper.hxx>
#include <Message.hxx>
@@ -55,10 +56,11 @@ bool DEPLY_Provider::Write(const TCollection_AsciiString& thePath,
const Handle(TDocStd_Document)& theDocument,
const Message_ProgressRange& theProgress)
{
if (GetNode().IsNull() || !GetNode()->IsKind(STANDARD_TYPE(DEPLY_ConfigurationNode)))
TCollection_AsciiString aContext = TCollection_AsciiString("writing the file ") + thePath;
if (!DE_ValidationUtils::ValidateConfigurationNode(GetNode(),
STANDARD_TYPE(DEPLY_ConfigurationNode),
aContext))
{
Message::SendFail() << "Error in the DEPLY_Provider during writing the file " << thePath
<< "\t: Incorrect or empty Configuration Node";
return false;
}
Handle(DEPLY_ConfigurationNode) aNode = Handle(DEPLY_ConfigurationNode)::DownCast(GetNode());

View File

@@ -635,6 +635,13 @@ bool DESTEP_ConfigurationNode::IsExportSupported() const
//=================================================================================================
bool DESTEP_ConfigurationNode::IsStreamSupported() const
{
return true;
}
//=================================================================================================
TCollection_AsciiString DESTEP_ConfigurationNode::GetFormat() const
{
return TCollection_AsciiString("STEP");

View File

@@ -68,6 +68,10 @@ public:
//! @return true if export is supported
Standard_EXPORT virtual bool IsExportSupported() const Standard_OVERRIDE;
//! Checks for stream support.
//! @return Standard_True if streams are supported
Standard_EXPORT virtual bool IsStreamSupported() const Standard_OVERRIDE;
//! Gets CAD format name of associated provider
//! @return provider CAD format
Standard_EXPORT virtual TCollection_AsciiString GetFormat() const Standard_OVERRIDE;

View File

@@ -13,6 +13,7 @@
#include <DESTEP_Provider.hxx>
#include <DE_ValidationUtils.hxx>
#include <DESTEP_ConfigurationNode.hxx>
#include <DESTEP_Parameters.hxx>
#include <Interface_Static.hxx>
@@ -20,13 +21,125 @@
#include <STEPCAFControl_Controller.hxx>
#include <STEPCAFControl_Reader.hxx>
#include <STEPCAFControl_Writer.hxx>
#include <STEPControl_Reader.hxx>
#include <STEPControl_Writer.hxx>
#include <StepData_StepModel.hxx>
#include <UnitsMethods.hxx>
#include <XCAFDoc_DocumentTool.hxx>
#include <XSControl_WorkSession.hxx>
#include <OSD_OpenFile.hxx>
#include <fstream>
IMPLEMENT_STANDARD_RTTIEXT(DESTEP_Provider, DE_Provider)
namespace
{
//! Helper function to validate configuration node
Standard_Boolean validateNode(const Handle(DE_ConfigurationNode)& theNode,
const TCollection_AsciiString& theContext)
{
return DE_ValidationUtils::ValidateConfigurationNode(theNode,
STANDARD_TYPE(DESTEP_ConfigurationNode),
theContext);
}
//! Configures STEPCAFControl_Reader with specified parameters and optional document setup.
//! @param[in,out] theReader STEP CAF reader to configure
//! @param[in] theParams Parameters containing read settings
//! @param[in] theWS Work session to initialize reader with (optional, if provided reader will
//! be initialized)
//! @param[in] theDocument Target document for length unit setup (optional)
//! @param[in] theLengthUnit Length unit for document setup (used only if theDocument is provided)
//! @param[in] theShapeFixParams Shape fix parameters (optional, uses default if not provided)
//! @note Sets up colors, names, layers, properties, metadata, and shape fix parameters
void configureSTEPCAFReader(STEPCAFControl_Reader& theReader,
const DESTEP_Parameters& theParams,
Handle(XSControl_WorkSession)& theWS,
const Handle(TDocStd_Document)& theDocument,
Standard_Real theLengthUnit,
const DE_ShapeFixParameters& theShapeFixParams)
{
theReader.Init(theWS);
theReader.SetColorMode(theParams.ReadColor);
theReader.SetNameMode(theParams.ReadName);
theReader.SetLayerMode(theParams.ReadLayer);
theReader.SetPropsMode(theParams.ReadProps);
theReader.SetMetaMode(theParams.ReadMetadata);
theReader.SetProductMetaMode(theParams.ReadProductMetadata);
theReader.SetShapeFixParameters(theShapeFixParams);
XCAFDoc_DocumentTool::SetLengthUnit(theDocument,
theLengthUnit,
UnitsMethods_LengthUnit_Millimeter);
}
//! Configures STEPCAFControl_Writer with full setup.
//! @param[in,out] theWriter STEP CAF writer to configure
//! @param[in] theParams Parameters containing write settings
//! @param[in,out] theWS Work session to initialize writer with
//! @param[in] theDocument Source document for length unit extraction
//! @param[in] theLengthUnit Length unit for document setup
//! @param[in] theShapeFixParams Shape fix parameters
//! @note Sets up all write parameters including colors, names, layers, props, materials
void configureSTEPCAFWriter(STEPCAFControl_Writer& theWriter,
const DESTEP_Parameters& theParams,
Handle(XSControl_WorkSession)& theWS,
const Handle(TDocStd_Document)& theDocument,
Standard_Real theLengthUnit,
const DE_ShapeFixParameters& theShapeFixParams)
{
theWriter.Init(theWS);
theWriter.SetColorMode(theParams.WriteColor);
theWriter.SetNameMode(theParams.WriteName);
theWriter.SetLayerMode(theParams.WriteLayer);
theWriter.SetPropsMode(theParams.WriteProps);
theWriter.SetMaterialMode(theParams.WriteMaterial);
theWriter.SetVisualMaterialMode(theParams.WriteVisMaterial);
theWriter.SetCleanDuplicates(theParams.CleanDuplicates);
theWriter.SetShapeFixParameters(theShapeFixParams);
Handle(StepData_StepModel) aModel =
Handle(StepData_StepModel)::DownCast(theWriter.Writer().WS()->Model());
Standard_Real aScaleFactorMM = 1.;
if (XCAFDoc_DocumentTool::GetLengthUnit(theDocument,
aScaleFactorMM,
UnitsMethods_LengthUnit_Millimeter))
{
aModel->SetLocalLengthUnit(aScaleFactorMM);
}
else
{
aModel->SetLocalLengthUnit(theLengthUnit);
Message::SendWarning()
<< "Warning in the DESTEP_Provider during writing"
<< "\t: The document has no information on Units. Using global parameter as initial Unit.";
}
}
//! Checks if output stream is in writable state.
//! @param[in] theStream Output stream to check
//! @param[in] theKey Stream identifier for error reporting
//! @return Standard_True if stream is writable, Standard_False otherwise
bool checkStreamWritability(Standard_OStream& theStream, const TCollection_AsciiString& theKey)
{
if (!theStream.good())
{
TCollection_AsciiString aKeyInfo = theKey.IsEmpty() ? "<empty key>" : theKey;
Message::SendFail() << "Error: Output stream '" << aKeyInfo
<< "' is not in good state for writing";
return false;
}
return true;
}
} // namespace
//=================================================================================================
DESTEP_Provider::DESTEP_Provider() {}
@@ -45,35 +158,28 @@ bool DESTEP_Provider::Read(const TCollection_AsciiString& thePath,
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress)
{
if (theDocument.IsNull())
TCollection_AsciiString aContext = TCollection_AsciiString("reading the file ") + thePath;
if (!DE_ValidationUtils::ValidateDocument(theDocument, aContext)
|| !validateNode(GetNode(), aContext))
{
Message::SendFail() << "Error in the DESTEP_Provider during reading the file " << thePath
<< "\t: theDocument shouldn't be null";
return false;
}
if (GetNode().IsNull() || !GetNode()->IsKind(STANDARD_TYPE(DESTEP_ConfigurationNode)))
{
Message::SendFail() << "Error in the DESTEP_Provider during reading the file " << thePath
<< "\t: Incorrect or empty Configuration Node";
return false;
}
Handle(DESTEP_ConfigurationNode) aNode = Handle(DESTEP_ConfigurationNode)::DownCast(GetNode());
personizeWS(theWS);
XCAFDoc_DocumentTool::SetLengthUnit(theDocument,
aNode->GlobalParameters.LengthUnit,
UnitsMethods_LengthUnit_Millimeter);
STEPCAFControl_Reader aReader;
aReader.Init(theWS);
aReader.SetColorMode(aNode->InternalParameters.ReadColor);
aReader.SetNameMode(aNode->InternalParameters.ReadName);
aReader.SetLayerMode(aNode->InternalParameters.ReadLayer);
aReader.SetPropsMode(aNode->InternalParameters.ReadProps);
aReader.SetMetaMode(aNode->InternalParameters.ReadMetadata);
aReader.SetProductMetaMode(aNode->InternalParameters.ReadProductMetadata);
aReader.SetShapeFixParameters(aNode->ShapeFixParameters);
configureSTEPCAFReader(aReader,
aNode->InternalParameters,
theWS,
theDocument,
aNode->GlobalParameters.LengthUnit,
aNode->ShapeFixParameters);
IFSelect_ReturnStatus aReadStat = IFSelect_RetVoid;
DESTEP_Parameters aParams = aNode->InternalParameters;
aReadStat = aReader.ReadFile(thePath.ToCString(), aParams);
if (aReadStat != IFSelect_RetDone)
{
Message::SendFail() << "Error in the DESTEP_Provider during reading the file " << thePath
@@ -97,43 +203,28 @@ bool DESTEP_Provider::Write(const TCollection_AsciiString& thePath,
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress)
{
if (GetNode().IsNull() || !GetNode()->IsKind(STANDARD_TYPE(DESTEP_ConfigurationNode)))
TCollection_AsciiString aContext = TCollection_AsciiString("writing the file ") + thePath;
if (!DE_ValidationUtils::ValidateDocument(theDocument, aContext)
|| !validateNode(GetNode(), aContext))
{
Message::SendFail() << "Error in the DESTEP_Provider during writing the file " << thePath
<< "\t: Incorrect or empty Configuration Node";
return false;
}
Handle(DESTEP_ConfigurationNode) aNode = Handle(DESTEP_ConfigurationNode)::DownCast(GetNode());
personizeWS(theWS);
STEPCAFControl_Writer aWriter;
aWriter.Init(theWS);
configureSTEPCAFWriter(aWriter,
aNode->InternalParameters,
theWS,
theDocument,
aNode->GlobalParameters.SystemUnit,
aNode->ShapeFixParameters);
Handle(StepData_StepModel) aModel =
Handle(StepData_StepModel)::DownCast(aWriter.Writer().WS()->Model());
STEPControl_StepModelType aMode =
static_cast<STEPControl_StepModelType>(aNode->InternalParameters.WriteModelType);
aWriter.SetColorMode(aNode->InternalParameters.WriteColor);
aWriter.SetNameMode(aNode->InternalParameters.WriteName);
aWriter.SetLayerMode(aNode->InternalParameters.WriteLayer);
aWriter.SetPropsMode(aNode->InternalParameters.WriteProps);
aWriter.SetShapeFixParameters(aNode->ShapeFixParameters);
aWriter.SetMaterialMode(aNode->InternalParameters.WriteMaterial);
aWriter.SetVisualMaterialMode(aNode->InternalParameters.WriteVisMaterial);
aWriter.SetCleanDuplicates(aNode->InternalParameters.CleanDuplicates);
DESTEP_Parameters aParams = aNode->InternalParameters;
Standard_Real aScaleFactorMM = 1.;
if (XCAFDoc_DocumentTool::GetLengthUnit(theDocument,
aScaleFactorMM,
UnitsMethods_LengthUnit_Millimeter))
{
aModel->SetLocalLengthUnit(aScaleFactorMM);
}
else
{
aModel->SetLocalLengthUnit(aNode->GlobalParameters.SystemUnit);
Message::SendWarning()
<< "Warning in the DESTEP_Provider during writing the file " << thePath
<< "\t: The document has no information on Units. Using global parameter as initial Unit.";
}
DESTEP_Parameters aParams = aNode->InternalParameters;
UnitsMethods_LengthUnit aTargetUnit =
UnitsMethods::GetLengthUnitByFactorValue(aNode->GlobalParameters.LengthUnit,
UnitsMethods_LengthUnit_Millimeter);
@@ -194,11 +285,9 @@ bool DESTEP_Provider::Read(const TCollection_AsciiString& thePath,
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress)
{
(void)theProgress;
if (GetNode().IsNull() || !GetNode()->IsKind(STANDARD_TYPE(DESTEP_ConfigurationNode)))
TCollection_AsciiString aContext = TCollection_AsciiString("reading the file ") + thePath;
if (!validateNode(GetNode(), aContext))
{
Message::SendFail() << "Error in the DESTEP_Provider during reading the file " << thePath
<< "\t: Incorrect or empty Configuration Node";
return false;
}
Handle(DESTEP_ConfigurationNode) aNode = Handle(DESTEP_ConfigurationNode)::DownCast(GetNode());
@@ -217,7 +306,7 @@ bool DESTEP_Provider::Read(const TCollection_AsciiString& thePath,
return false;
}
aModel->SetLocalLengthUnit(aNode->GlobalParameters.LengthUnit);
if (aReader.TransferRoots() <= 0)
if (aReader.TransferRoots(theProgress) <= 0)
{
Message::SendFail() << "Error in the DESTEP_Provider during reading the file " << thePath
<< "\t:Cannot read any relevant data from the STEP file";
@@ -234,10 +323,9 @@ bool DESTEP_Provider::Write(const TCollection_AsciiString& thePath,
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress)
{
if (GetNode().IsNull() || !GetNode()->IsKind(STANDARD_TYPE(DESTEP_ConfigurationNode)))
TCollection_AsciiString aContext = TCollection_AsciiString("writing the file ") + thePath;
if (!validateNode(GetNode(), aContext))
{
Message::SendFail() << "Error in the DESTEP_Provider during reading the file " << thePath
<< "\t: Incorrect or empty Configuration Node";
return false;
}
Handle(DESTEP_ConfigurationNode) aNode = Handle(DESTEP_ConfigurationNode)::DownCast(GetNode());
@@ -312,6 +400,281 @@ bool DESTEP_Provider::Write(const TCollection_AsciiString& thePath,
//=================================================================================================
Standard_Boolean DESTEP_Provider::Read(ReadStreamList& theStreams,
const Handle(TDocStd_Document)& theDocument,
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress)
{
TCollection_AsciiString aContext = "reading stream";
if (!DE_ValidationUtils::ValidateReadStreamList(theStreams, aContext))
{
return Standard_False;
}
TCollection_AsciiString aFirstKey = theStreams.First().Path;
TCollection_AsciiString aFullContext = aContext + " " + aFirstKey;
if (!DE_ValidationUtils::ValidateDocument(theDocument, aFullContext)
|| !validateNode(GetNode(), aFullContext)
|| !DE_ValidationUtils::ValidateReadStreamList(theStreams, aFullContext))
{
return Standard_False;
}
Standard_IStream& aStream = theStreams.First().Stream;
personizeWS(theWS);
Handle(DESTEP_ConfigurationNode) aNode = Handle(DESTEP_ConfigurationNode)::DownCast(GetNode());
STEPCAFControl_Reader aReader(theWS, Standard_False);
configureSTEPCAFReader(aReader,
aNode->InternalParameters,
theWS,
theDocument,
aNode->GlobalParameters.LengthUnit,
aNode->ShapeFixParameters);
Standard_Boolean isOk = aReader.ReadStream(aFirstKey.ToCString(), aStream);
if (!isOk)
{
Message::SendFail() << "Error: DESTEP_Provider failed to read stream " << aFirstKey;
return Standard_False;
}
return aReader.Transfer(theDocument, theProgress);
}
//=================================================================================================
Standard_Boolean DESTEP_Provider::Write(WriteStreamList& theStreams,
const Handle(TDocStd_Document)& theDocument,
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress)
{
TCollection_AsciiString aContext = "writing stream";
if (!DE_ValidationUtils::ValidateWriteStreamList(theStreams, aContext))
{
return Standard_False;
}
TCollection_AsciiString aFirstKey = theStreams.First().Path;
TCollection_AsciiString aFullContext = aContext + " " + aFirstKey;
if (!DE_ValidationUtils::ValidateDocument(theDocument, aFullContext)
|| !validateNode(GetNode(), aFullContext))
{
return Standard_False;
}
Standard_OStream& aStream = theStreams.First().Stream;
if (!checkStreamWritability(aStream, aFirstKey))
{
return Standard_False;
}
personizeWS(theWS);
Handle(DESTEP_ConfigurationNode) aNode = Handle(DESTEP_ConfigurationNode)::DownCast(GetNode());
STEPCAFControl_Writer aWriter(theWS, Standard_False);
configureSTEPCAFWriter(aWriter,
aNode->InternalParameters,
theWS,
theDocument,
aNode->GlobalParameters.LengthUnit,
aNode->ShapeFixParameters);
Handle(StepData_StepModel) aModel =
Handle(StepData_StepModel)::DownCast(aWriter.Writer().WS()->Model());
DESTEP_Parameters aParams = aNode->InternalParameters;
UnitsMethods_LengthUnit aTargetUnit =
UnitsMethods::GetLengthUnitByFactorValue(aNode->GlobalParameters.LengthUnit,
UnitsMethods_LengthUnit_Millimeter);
aParams.WriteUnit = aTargetUnit;
aModel->SetWriteLengthUnit(aNode->GlobalParameters.LengthUnit);
STEPControl_StepModelType aMode =
static_cast<STEPControl_StepModelType>(aNode->InternalParameters.WriteModelType);
Standard_Boolean isOk = aWriter.Transfer(theDocument, aParams, aMode, 0, theProgress);
if (!isOk)
{
Message::SendFail() << "Error: DESTEP_Provider failed to transfer document for stream "
<< aFirstKey;
return Standard_False;
}
return aWriter.WriteStream(aStream);
}
//=================================================================================================
Standard_Boolean DESTEP_Provider::Read(ReadStreamList& theStreams,
const Handle(TDocStd_Document)& theDocument,
const Message_ProgressRange& theProgress)
{
Handle(XSControl_WorkSession) aWS = new XSControl_WorkSession();
return Read(theStreams, theDocument, aWS, theProgress);
}
//=================================================================================================
Standard_Boolean DESTEP_Provider::Write(WriteStreamList& theStreams,
const Handle(TDocStd_Document)& theDocument,
const Message_ProgressRange& theProgress)
{
Handle(XSControl_WorkSession) aWS = new XSControl_WorkSession();
return Write(theStreams, theDocument, aWS, theProgress);
}
//=================================================================================================
Standard_Boolean DESTEP_Provider::Read(ReadStreamList& theStreams,
TopoDS_Shape& theShape,
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress)
{
TCollection_AsciiString aContext = "reading stream";
if (!DE_ValidationUtils::ValidateReadStreamList(theStreams, aContext))
{
return Standard_False;
}
TCollection_AsciiString aFirstKey = theStreams.First().Path;
TCollection_AsciiString aFullContext = aContext + " " + aFirstKey;
if (!validateNode(GetNode(), aFullContext))
{
return Standard_False;
}
Standard_IStream& aStream = theStreams.First().Stream;
personizeWS(theWS);
Handle(DESTEP_ConfigurationNode) aNode = Handle(DESTEP_ConfigurationNode)::DownCast(GetNode());
// Use STEPControl_Reader for shape operations from streams
STEPControl_Reader aReader;
aReader.SetWS(theWS);
aReader.SetShapeFixParameters(aNode->ShapeFixParameters);
// Read from stream using the reader's internal model
IFSelect_ReturnStatus aReadStat = aReader.ReadStream(aFirstKey.ToCString(), aStream);
if (aReadStat != IFSelect_RetDone)
{
Message::SendFail() << "Error: DESTEP_Provider failed to read from stream " << aFirstKey;
return Standard_False;
}
Handle(StepData_StepModel) aModel = aReader.StepModel();
aModel->SetLocalLengthUnit(aNode->GlobalParameters.LengthUnit);
// Transfer the first root to get the shape
if (aReader.TransferRoots(theProgress) <= 0)
{
Message::SendFail() << "Error: DESTEP_Provider found no transferable roots in stream "
<< aFirstKey;
return Standard_False;
}
theShape = aReader.OneShape();
return Standard_True;
}
//=================================================================================================
Standard_Boolean DESTEP_Provider::Write(WriteStreamList& theStreams,
const TopoDS_Shape& theShape,
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress)
{
TCollection_AsciiString aContext = "writing stream";
if (!DE_ValidationUtils::ValidateWriteStreamList(theStreams, aContext))
{
return Standard_False;
}
TCollection_AsciiString aFirstKey = theStreams.First().Path;
TCollection_AsciiString aFullContext = aContext + " " + aFirstKey;
if (!validateNode(GetNode(), aFullContext))
{
return Standard_False;
}
Standard_OStream& aStream = theStreams.First().Stream;
personizeWS(theWS);
Handle(DESTEP_ConfigurationNode) aNode = Handle(DESTEP_ConfigurationNode)::DownCast(GetNode());
// Use STEPControl_Writer for shape operations to streams
STEPControl_Writer aWriter;
aWriter.SetWS(theWS);
Handle(StepData_StepModel) aModel = aWriter.Model();
aModel->SetLocalLengthUnit(aNode->GlobalParameters.SystemUnit);
UnitsMethods_LengthUnit aTargetUnit =
UnitsMethods::GetLengthUnitByFactorValue(aNode->GlobalParameters.LengthUnit,
UnitsMethods_LengthUnit_Millimeter);
DESTEP_Parameters aParams = aNode->InternalParameters;
aParams.WriteUnit = aTargetUnit;
if (aTargetUnit == UnitsMethods_LengthUnit_Undefined)
{
aModel->SetWriteLengthUnit(1.0);
Message::SendWarning()
<< "Custom units are not supported by STEP format, but LengthUnit global parameter doesn't "
"fit any predefined unit. Units will be scaled to Millimeters";
}
else
{
aModel->SetWriteLengthUnit(aNode->GlobalParameters.LengthUnit);
}
aWriter.SetShapeFixParameters(aNode->ShapeFixParameters);
IFSelect_ReturnStatus aWriteStat = aWriter.Transfer(theShape,
aNode->InternalParameters.WriteModelType,
aParams,
true,
theProgress);
if (aWriteStat != IFSelect_RetDone)
{
Message::SendFail() << "Error: DESTEP_Provider failed to transfer shape for stream "
<< aFirstKey;
return Standard_False;
}
if (aNode->InternalParameters.CleanDuplicates)
{
aWriter.CleanDuplicateEntities();
}
// Write to stream
if (!aWriter.WriteStream(aStream))
{
Message::SendFail() << "Error: DESTEP_Provider failed to write to stream " << aFirstKey;
return Standard_False;
}
return Standard_True;
}
//=================================================================================================
Standard_Boolean DESTEP_Provider::Read(ReadStreamList& theStreams,
TopoDS_Shape& theShape,
const Message_ProgressRange& theProgress)
{
Handle(XSControl_WorkSession) aWS = new XSControl_WorkSession();
return Read(theStreams, theShape, aWS, theProgress);
}
//=================================================================================================
Standard_Boolean DESTEP_Provider::Write(WriteStreamList& theStreams,
const TopoDS_Shape& theShape,
const Message_ProgressRange& theProgress)
{
Handle(XSControl_WorkSession) aWS = new XSControl_WorkSession();
return Write(theStreams, theShape, aWS, theProgress);
}
//=================================================================================================
TCollection_AsciiString DESTEP_Provider::GetFormat() const
{
return TCollection_AsciiString("STEP");

View File

@@ -109,6 +109,54 @@ public:
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress = Message_ProgressRange()) Standard_OVERRIDE;
//! Reads streams according to internal configuration
//! @param[in] theStreams streams to read from
//! @param[out] theDocument document to save result
//! @param[in] theWS current work session
//! @param[in] theProgress progress indicator
//! @return true if Read operation has ended correctly
Standard_EXPORT virtual Standard_Boolean Read(
ReadStreamList& theStreams,
const Handle(TDocStd_Document)& theDocument,
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress = Message_ProgressRange()) Standard_OVERRIDE;
//! Writes streams according to internal configuration
//! @param[in] theStreams streams to write to
//! @param[out] theDocument document to export
//! @param[in] theWS current work session
//! @param[in] theProgress progress indicator
//! @return true if Write operation has ended correctly
Standard_EXPORT virtual Standard_Boolean Write(
WriteStreamList& theStreams,
const Handle(TDocStd_Document)& theDocument,
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress = Message_ProgressRange()) Standard_OVERRIDE;
//! Reads streams according to internal configuration
//! @param[in] theStreams streams to read from
//! @param[out] theShape shape to save result
//! @param[in] theWS current work session
//! @param[in] theProgress progress indicator
//! @return true if Read operation has ended correctly
Standard_EXPORT virtual Standard_Boolean Read(
ReadStreamList& theStreams,
TopoDS_Shape& theShape,
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress = Message_ProgressRange()) Standard_OVERRIDE;
//! Writes streams according to internal configuration
//! @param[in] theStreams streams to write to
//! @param[out] theShape shape to export
//! @param[in] theWS current work session
//! @param[in] theProgress progress indicator
//! @return true if Write operation has ended correctly
Standard_EXPORT virtual Standard_Boolean Write(
WriteStreamList& theStreams,
const TopoDS_Shape& theShape,
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress = Message_ProgressRange()) Standard_OVERRIDE;
//! Reads a CAD file, according internal configuration
//! @param[in] thePath path to the import CAD file
//! @param[out] theShape shape to save result
@@ -129,6 +177,46 @@ public:
const TopoDS_Shape& theShape,
const Message_ProgressRange& theProgress = Message_ProgressRange()) Standard_OVERRIDE;
//! Reads streams according to internal configuration
//! @param[in] theStreams streams to read from
//! @param[out] theDocument document to save result
//! @param[in] theProgress progress indicator
//! @return true if Read operation has ended correctly
Standard_EXPORT virtual Standard_Boolean Read(
ReadStreamList& theStreams,
const Handle(TDocStd_Document)& theDocument,
const Message_ProgressRange& theProgress = Message_ProgressRange()) Standard_OVERRIDE;
//! Writes streams according to internal configuration
//! @param[in] theStreams streams to write to
//! @param[out] theDocument document to export
//! @param[in] theProgress progress indicator
//! @return true if Write operation has ended correctly
Standard_EXPORT virtual Standard_Boolean Write(
WriteStreamList& theStreams,
const Handle(TDocStd_Document)& theDocument,
const Message_ProgressRange& theProgress = Message_ProgressRange()) Standard_OVERRIDE;
//! Reads streams according to internal configuration
//! @param[in] theStreams streams to read from
//! @param[out] theShape shape to save result
//! @param[in] theProgress progress indicator
//! @return true if Read operation has ended correctly
Standard_EXPORT virtual Standard_Boolean Read(
ReadStreamList& theStreams,
TopoDS_Shape& theShape,
const Message_ProgressRange& theProgress = Message_ProgressRange()) Standard_OVERRIDE;
//! Writes streams according to internal configuration
//! @param[in] theStreams streams to write to
//! @param[out] theShape shape to export
//! @param[in] theProgress progress indicator
//! @return true if Write operation has ended correctly
Standard_EXPORT virtual Standard_Boolean Write(
WriteStreamList& theStreams,
const TopoDS_Shape& theShape,
const Message_ProgressRange& theProgress = Message_ProgressRange()) Standard_OVERRIDE;
public:
//! Gets CAD format name of associated provider
//! @return provider CAD format

View File

@@ -0,0 +1,475 @@
// Copyright (c) 2025 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 <DESTEP_Provider.hxx>
#include <DESTEP_ConfigurationNode.hxx>
#include <DE_Wrapper.hxx>
#include <BRepPrimAPI_MakeBox.hxx>
#include <BRepPrimAPI_MakeSphere.hxx>
#include <BRepPrimAPI_MakeCylinder.hxx>
#include <TopoDS_Shape.hxx>
#include <TopExp_Explorer.hxx>
#include <TopAbs_ShapeEnum.hxx>
#include <TDocStd_Document.hxx>
#include <TDocStd_Application.hxx>
#include <XCAFDoc_DocumentTool.hxx>
#include <XCAFDoc_ShapeTool.hxx>
#include <XSControl_WorkSession.hxx>
#include <sstream>
#include <gtest/gtest.h>
class DESTEP_ProviderTest : public ::testing::Test
{
protected:
void SetUp() override
{
// Initialize provider with default configuration
Handle(DESTEP_ConfigurationNode) aNode = new DESTEP_ConfigurationNode();
myProvider = new DESTEP_Provider(aNode);
// Create test BRep shapes (perfect for STEP format)
myBox = BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape();
mySphere = BRepPrimAPI_MakeSphere(5.0).Shape();
myCylinder = BRepPrimAPI_MakeCylinder(3.0, 8.0).Shape();
// Create test document
Handle(TDocStd_Application) anApp = new TDocStd_Application();
anApp->NewDocument("BinXCAF", myDocument);
}
void TearDown() override
{
myProvider.Nullify();
myDocument.Nullify();
}
// Helper method to count shape elements
Standard_Integer CountShapeElements(const TopoDS_Shape& theShape, TopAbs_ShapeEnum theType)
{
Standard_Integer aCount = 0;
for (TopExp_Explorer anExplorer(theShape, theType); anExplorer.More(); anExplorer.Next())
{
aCount++;
}
return aCount;
}
// Helper method to validate STEP content
bool IsValidSTEPContent(const std::string& theContent)
{
return !theContent.empty() && theContent.find("ISO-10303-21;") != std::string::npos
&& theContent.find("HEADER;") != std::string::npos
&& theContent.find("DATA;") != std::string::npos
&& theContent.find("ENDSEC;") != std::string::npos;
}
protected:
Handle(DESTEP_Provider) myProvider;
TopoDS_Shape myBox;
TopoDS_Shape mySphere;
TopoDS_Shape myCylinder;
Handle(TDocStd_Document) myDocument;
};
// Test basic provider creation and format/vendor information
TEST_F(DESTEP_ProviderTest, BasicProperties)
{
EXPECT_STREQ("STEP", myProvider->GetFormat().ToCString());
EXPECT_STREQ("OCC", myProvider->GetVendor().ToCString());
EXPECT_FALSE(myProvider->GetNode().IsNull());
}
// Test stream-based shape write and read operations
TEST_F(DESTEP_ProviderTest, StreamShapeWriteRead)
{
std::ostringstream anOStream;
DE_Provider::WriteStreamList aWriteStreams;
aWriteStreams.Append(DE_Provider::WriteStreamNode("test.step", anOStream));
// Write box shape to stream
EXPECT_TRUE(myProvider->Write(aWriteStreams, myBox));
std::string aStepContent = anOStream.str();
EXPECT_FALSE(aStepContent.empty());
EXPECT_TRUE(IsValidSTEPContent(aStepContent));
if (!aStepContent.empty())
{
// Read back from stream
std::istringstream anIStream(aStepContent);
DE_Provider::ReadStreamList aReadStreams;
aReadStreams.Append(DE_Provider::ReadStreamNode("test.step", anIStream));
TopoDS_Shape aReadShape;
EXPECT_TRUE(myProvider->Read(aReadStreams, aReadShape));
EXPECT_FALSE(aReadShape.IsNull());
if (!aReadShape.IsNull())
{
// STEP should preserve solid geometry
Standard_Integer aReadSolids = CountShapeElements(aReadShape, TopAbs_SOLID);
Standard_Integer aOriginalSolids = CountShapeElements(myBox, TopAbs_SOLID);
EXPECT_EQ(aReadSolids, aOriginalSolids);
}
}
}
// Test stream-based document write and read operations
TEST_F(DESTEP_ProviderTest, StreamDocumentWriteRead)
{
// Add box to document
Handle(XCAFDoc_ShapeTool) aShapeTool = XCAFDoc_DocumentTool::ShapeTool(myDocument->Main());
TDF_Label aShapeLabel = aShapeTool->AddShape(myBox);
EXPECT_FALSE(aShapeLabel.IsNull());
std::ostringstream anOStream;
DE_Provider::WriteStreamList aWriteStreams;
aWriteStreams.Append(DE_Provider::WriteStreamNode("document.step", anOStream));
// Write document to stream
EXPECT_TRUE(myProvider->Write(aWriteStreams, myDocument));
std::string aStepContent = anOStream.str();
EXPECT_FALSE(aStepContent.empty());
EXPECT_TRUE(IsValidSTEPContent(aStepContent));
if (!aStepContent.empty())
{
// Create new document for reading
Handle(TDocStd_Application) anApp = new TDocStd_Application();
Handle(TDocStd_Document) aNewDocument;
anApp->NewDocument("BinXCAF", aNewDocument);
// Read back from stream
std::istringstream anIStream(aStepContent);
DE_Provider::ReadStreamList aReadStreams;
aReadStreams.Append(DE_Provider::ReadStreamNode("document.step", anIStream));
EXPECT_TRUE(myProvider->Read(aReadStreams, aNewDocument));
// Validate document content
Handle(XCAFDoc_ShapeTool) aNewShapeTool = XCAFDoc_DocumentTool::ShapeTool(aNewDocument->Main());
TDF_LabelSequence aLabels;
aNewShapeTool->GetShapes(aLabels);
EXPECT_GT(aLabels.Length(), 0); // Should have at least one shape in document
}
}
// Test DE_Wrapper integration for STEP operations
TEST_F(DESTEP_ProviderTest, DE_WrapperIntegration)
{
// Initialize DE_Wrapper and bind STEP provider
DE_Wrapper aWrapper;
Handle(DESTEP_ConfigurationNode) aNode = new DESTEP_ConfigurationNode();
// Bind the configured node to wrapper
EXPECT_TRUE(aWrapper.Bind(aNode));
// Test write with DE_Wrapper using sphere
std::ostringstream anOStream;
DE_Provider::WriteStreamList aWriteStreams;
aWriteStreams.Append(DE_Provider::WriteStreamNode("test.step", anOStream));
EXPECT_TRUE(aWrapper.Write(aWriteStreams, mySphere));
std::string aStepContent = anOStream.str();
EXPECT_FALSE(aStepContent.empty());
EXPECT_TRUE(IsValidSTEPContent(aStepContent));
if (!aStepContent.empty())
{
// Test DE_Wrapper stream operations
std::istringstream anIStream(aStepContent);
DE_Provider::ReadStreamList aReadStreams;
aReadStreams.Append(DE_Provider::ReadStreamNode("test.step", anIStream));
TopoDS_Shape aReadShape;
bool aWrapperResult = aWrapper.Read(aReadStreams, aReadShape);
// Test direct provider with same content for comparison
std::istringstream anIStream2(aStepContent);
DE_Provider::ReadStreamList aReadStreams2;
aReadStreams2.Append(DE_Provider::ReadStreamNode("test.step", anIStream2));
Handle(DESTEP_Provider) aDirectProvider = new DESTEP_Provider(aNode);
TopoDS_Shape aDirectShape;
bool aDirectResult = aDirectProvider->Read(aReadStreams2, aDirectShape);
// REQUIREMENT: DE_Wrapper must work exactly the same as direct provider
EXPECT_EQ(aWrapperResult, aDirectResult);
EXPECT_EQ(aReadShape.IsNull(), aDirectShape.IsNull());
if (aDirectResult && !aDirectShape.IsNull())
{
Standard_Integer aSolids = CountShapeElements(aDirectShape, TopAbs_SOLID);
EXPECT_GT(aSolids, 0);
}
}
}
// Test multiple shapes in single document
TEST_F(DESTEP_ProviderTest, MultipleShapesInDocument)
{
// Add multiple shapes to document
Handle(XCAFDoc_ShapeTool) aShapeTool = XCAFDoc_DocumentTool::ShapeTool(myDocument->Main());
TDF_Label aBoxLabel = aShapeTool->AddShape(myBox);
TDF_Label aSphereLabel = aShapeTool->AddShape(mySphere);
TDF_Label aCylinderLabel = aShapeTool->AddShape(myCylinder);
EXPECT_FALSE(aBoxLabel.IsNull());
EXPECT_FALSE(aSphereLabel.IsNull());
EXPECT_FALSE(aCylinderLabel.IsNull());
std::ostringstream anOStream;
DE_Provider::WriteStreamList aWriteStreams;
aWriteStreams.Append(DE_Provider::WriteStreamNode("multi_shapes.step", anOStream));
// Write document with multiple shapes
EXPECT_TRUE(myProvider->Write(aWriteStreams, myDocument));
std::string aStepContent = anOStream.str();
EXPECT_FALSE(aStepContent.empty());
EXPECT_TRUE(IsValidSTEPContent(aStepContent));
// Read back into new document
Handle(TDocStd_Application) anApp = new TDocStd_Application();
Handle(TDocStd_Document) aNewDocument;
anApp->NewDocument("BinXCAF", aNewDocument);
std::istringstream anIStream(aStepContent);
DE_Provider::ReadStreamList aReadStreams;
aReadStreams.Append(DE_Provider::ReadStreamNode("multi_shapes.step", anIStream));
EXPECT_TRUE(myProvider->Read(aReadStreams, aNewDocument));
// Validate document content
Handle(XCAFDoc_ShapeTool) aNewShapeTool = XCAFDoc_DocumentTool::ShapeTool(aNewDocument->Main());
TDF_LabelSequence aLabels;
aNewShapeTool->GetShapes(aLabels);
EXPECT_EQ(aLabels.Length(), 3); // Should have exactly 3 shapes in document
}
// Test different BRep geometry types
TEST_F(DESTEP_ProviderTest, DifferentBRepGeometries)
{
// Test box geometry
std::ostringstream aBoxStream;
DE_Provider::WriteStreamList aBoxWriteStreams;
aBoxWriteStreams.Append(DE_Provider::WriteStreamNode("box.step", aBoxStream));
EXPECT_TRUE(myProvider->Write(aBoxWriteStreams, myBox));
std::string aBoxContent = aBoxStream.str();
// Test sphere geometry
std::ostringstream aSphereStream;
DE_Provider::WriteStreamList aSphereWriteStreams;
aSphereWriteStreams.Append(DE_Provider::WriteStreamNode("sphere.step", aSphereStream));
EXPECT_TRUE(myProvider->Write(aSphereWriteStreams, mySphere));
std::string aSphereContent = aSphereStream.str();
// Test cylinder geometry
std::ostringstream aCylinderStream;
DE_Provider::WriteStreamList aCylinderWriteStreams;
aCylinderWriteStreams.Append(DE_Provider::WriteStreamNode("cylinder.step", aCylinderStream));
EXPECT_TRUE(myProvider->Write(aCylinderWriteStreams, myCylinder));
std::string aCylinderContent = aCylinderStream.str();
// All content should be valid STEP format
EXPECT_TRUE(IsValidSTEPContent(aBoxContent));
EXPECT_TRUE(IsValidSTEPContent(aSphereContent));
EXPECT_TRUE(IsValidSTEPContent(aCylinderContent));
// Different geometries should produce different STEP content
EXPECT_NE(aBoxContent, aSphereContent);
EXPECT_NE(aBoxContent, aCylinderContent);
EXPECT_NE(aSphereContent, aCylinderContent);
// All should read back successfully
std::istringstream aBoxIStream(aBoxContent);
DE_Provider::ReadStreamList aBoxReadStreams;
aBoxReadStreams.Append(DE_Provider::ReadStreamNode("box.step", aBoxIStream));
TopoDS_Shape aBoxReadShape;
EXPECT_TRUE(myProvider->Read(aBoxReadStreams, aBoxReadShape));
EXPECT_FALSE(aBoxReadShape.IsNull());
std::istringstream aSphereIStream(aSphereContent);
DE_Provider::ReadStreamList aSphereReadStreams;
aSphereReadStreams.Append(DE_Provider::ReadStreamNode("sphere.step", aSphereIStream));
TopoDS_Shape aSphereReadShape;
EXPECT_TRUE(myProvider->Read(aSphereReadStreams, aSphereReadShape));
EXPECT_FALSE(aSphereReadShape.IsNull());
}
// Test DE_Wrapper with different file extensions
TEST_F(DESTEP_ProviderTest, DE_WrapperFileExtensions)
{
DE_Wrapper aWrapper;
Handle(DESTEP_ConfigurationNode) aNode = new DESTEP_ConfigurationNode();
EXPECT_TRUE(aWrapper.Bind(aNode));
// Test different STEP extensions
std::vector<std::string> aExtensions = {"test.step", "test.STEP", "test.stp", "test.STP"};
for (const auto& anExt : aExtensions)
{
std::ostringstream anOStream;
DE_Provider::WriteStreamList aWriteStreams;
aWriteStreams.Append(DE_Provider::WriteStreamNode(anExt.c_str(), anOStream));
EXPECT_TRUE(aWrapper.Write(aWriteStreams, myBox))
<< "Failed to write with extension: " << anExt;
std::string aContent = anOStream.str();
EXPECT_FALSE(aContent.empty()) << "Empty content for extension: " << anExt;
EXPECT_TRUE(IsValidSTEPContent(aContent)) << "Invalid STEP content for extension: " << anExt;
// Test read back
std::istringstream anIStream(aContent);
DE_Provider::ReadStreamList aReadStreams;
aReadStreams.Append(DE_Provider::ReadStreamNode(anExt.c_str(), anIStream));
TopoDS_Shape aReadShape;
EXPECT_TRUE(aWrapper.Read(aReadStreams, aReadShape))
<< "Failed to read with extension: " << anExt;
EXPECT_FALSE(aReadShape.IsNull()) << "Null shape read with extension: " << anExt;
}
}
// Test error conditions and edge cases
TEST_F(DESTEP_ProviderTest, ErrorHandling)
{
// Test with empty streams
DE_Provider::WriteStreamList anEmptyWriteStreams;
EXPECT_FALSE(myProvider->Write(anEmptyWriteStreams, myBox));
DE_Provider::ReadStreamList anEmptyReadStreams;
TopoDS_Shape aShape;
EXPECT_FALSE(myProvider->Read(anEmptyReadStreams, aShape));
// Test with null shape
std::ostringstream anOStream;
DE_Provider::WriteStreamList aWriteStreams;
aWriteStreams.Append(DE_Provider::WriteStreamNode("null_test.step", anOStream));
TopoDS_Shape aNullShape;
// Writing null shape should fail
EXPECT_FALSE(myProvider->Write(aWriteStreams, aNullShape));
// Test reading invalid STEP content
std::string anInvalidContent = "This is not valid STEP content";
std::istringstream anInvalidStream(anInvalidContent);
DE_Provider::ReadStreamList anInvalidReadStreams;
anInvalidReadStreams.Append(DE_Provider::ReadStreamNode("invalid.step", anInvalidStream));
TopoDS_Shape anInvalidShape;
EXPECT_FALSE(myProvider->Read(anInvalidReadStreams, anInvalidShape));
// Test with null document
Handle(TDocStd_Document) aNullDoc;
EXPECT_FALSE(myProvider->Write(aWriteStreams, aNullDoc));
EXPECT_FALSE(myProvider->Read(anEmptyReadStreams, aNullDoc));
}
// Test DESTEP configuration modes
TEST_F(DESTEP_ProviderTest, ConfigurationModes)
{
Handle(DESTEP_ConfigurationNode) aNode =
Handle(DESTEP_ConfigurationNode)::DownCast(myProvider->GetNode());
// Test basic configuration access
EXPECT_FALSE(aNode.IsNull());
// Test provider format and vendor are correct
EXPECT_STREQ("STEP", myProvider->GetFormat().ToCString());
EXPECT_STREQ("OCC", myProvider->GetVendor().ToCString());
// Test that we can create provider with different configuration
Handle(DESTEP_ConfigurationNode) aNewNode = new DESTEP_ConfigurationNode();
Handle(DESTEP_Provider) aNewProvider = new DESTEP_Provider(aNewNode);
EXPECT_STREQ("STEP", aNewProvider->GetFormat().ToCString());
EXPECT_STREQ("OCC", aNewProvider->GetVendor().ToCString());
EXPECT_FALSE(aNewProvider->GetNode().IsNull());
}
// Test WorkSession integration
TEST_F(DESTEP_ProviderTest, WorkSessionIntegration)
{
Handle(XSControl_WorkSession) aWS = new XSControl_WorkSession();
// Test write operation with work session
std::ostringstream anOStream;
DE_Provider::WriteStreamList aWriteStreams;
aWriteStreams.Append(DE_Provider::WriteStreamNode("ws_test.step", anOStream));
EXPECT_TRUE(myProvider->Write(aWriteStreams, myBox, aWS));
std::string aStepContent = anOStream.str();
EXPECT_FALSE(aStepContent.empty());
EXPECT_TRUE(IsValidSTEPContent(aStepContent));
// Test read operation with work session
std::istringstream anIStream(aStepContent);
DE_Provider::ReadStreamList aReadStreams;
aReadStreams.Append(DE_Provider::ReadStreamNode("ws_test.step", anIStream));
TopoDS_Shape aReadShape;
EXPECT_TRUE(myProvider->Read(aReadStreams, aReadShape, aWS));
EXPECT_FALSE(aReadShape.IsNull());
}
// Test document operations with WorkSession
TEST_F(DESTEP_ProviderTest, DocumentWorkSessionIntegration)
{
// Add shape to document
Handle(XCAFDoc_ShapeTool) aShapeTool = XCAFDoc_DocumentTool::ShapeTool(myDocument->Main());
TDF_Label aShapeLabel = aShapeTool->AddShape(mySphere);
EXPECT_FALSE(aShapeLabel.IsNull());
Handle(XSControl_WorkSession) aWS = new XSControl_WorkSession();
std::ostringstream anOStream;
DE_Provider::WriteStreamList aWriteStreams;
aWriteStreams.Append(DE_Provider::WriteStreamNode("doc_ws_test.step", anOStream));
// Test document write with work session
EXPECT_TRUE(myProvider->Write(aWriteStreams, myDocument, aWS));
std::string aStepContent = anOStream.str();
EXPECT_FALSE(aStepContent.empty());
EXPECT_TRUE(IsValidSTEPContent(aStepContent));
// Create new document for reading
Handle(TDocStd_Application) anApp = new TDocStd_Application();
Handle(TDocStd_Document) aNewDocument;
anApp->NewDocument("BinXCAF", aNewDocument);
// Test document read with work session
std::istringstream anIStream(aStepContent);
DE_Provider::ReadStreamList aReadStreams;
aReadStreams.Append(DE_Provider::ReadStreamNode("doc_ws_test.step", anIStream));
EXPECT_TRUE(myProvider->Read(aReadStreams, aNewDocument, aWS));
// Validate document content
Handle(XCAFDoc_ShapeTool) aNewShapeTool = XCAFDoc_DocumentTool::ShapeTool(aNewDocument->Main());
TDF_LabelSequence aLabels;
aNewShapeTool->GetShapes(aLabels);
EXPECT_GT(aLabels.Length(), 0);
}

View File

@@ -2,6 +2,7 @@
set(OCCT_TKDESTEP_GTests_FILES_LOCATION "${CMAKE_CURRENT_LIST_DIR}")
set(OCCT_TKDESTEP_GTests_FILES
DESTEP_Provider_Test.cxx
STEPConstruct_RenderingProperties_Test.cxx
StepData_StepWriter_Test.cxx
StepTidy_BaseTestFixture.pxx

View File

@@ -134,6 +134,13 @@ bool DESTL_ConfigurationNode::IsExportSupported() const
//=================================================================================================
bool DESTL_ConfigurationNode::IsStreamSupported() const
{
return true;
}
//=================================================================================================
TCollection_AsciiString DESTL_ConfigurationNode::GetFormat() const
{
return TCollection_AsciiString("STL");

View File

@@ -64,6 +64,10 @@ public:
//! @return true if export is supported
Standard_EXPORT virtual bool IsExportSupported() const Standard_OVERRIDE;
//! Checks for stream support.
//! @return Standard_True if streams are supported
Standard_EXPORT virtual bool IsStreamSupported() const Standard_OVERRIDE;
//! Gets CAD format name of associated provider
//! @return provider CAD format
Standard_EXPORT virtual TCollection_AsciiString GetFormat() const Standard_OVERRIDE;

View File

@@ -14,14 +14,21 @@
#include <DESTL_Provider.hxx>
#include <BRep_Builder.hxx>
#include <DE_ValidationUtils.hxx>
#include <DESTL_ConfigurationNode.hxx>
#include <Message.hxx>
#include <NCollection_Vector.hxx>
#include <Poly_Triangle.hxx>
#include <RWStl.hxx>
#include <RWStl_Reader.hxx>
#include <StlAPI.hxx>
#include <StlAPI_Reader.hxx>
#include <StlAPI_Writer.hxx>
#include <Standard_ReadLineBuffer.hxx>
#include <TDocStd_Document.hxx>
#include <XCAFDoc_DocumentTool.hxx>
#include <XCAFDoc_ShapeTool.hxx>
#include <cstring>
IMPLEMENT_STANDARD_RTTIEXT(DESTL_Provider, DE_Provider)
@@ -64,10 +71,9 @@ bool DESTL_Provider::Read(const TCollection_AsciiString& thePath,
const Handle(TDocStd_Document)& theDocument,
const Message_ProgressRange& theProgress)
{
if (theDocument.IsNull())
TCollection_AsciiString aContext = TCollection_AsciiString("reading the file ") + thePath;
if (!DE_ValidationUtils::ValidateDocument(theDocument, aContext))
{
Message::SendFail() << "Error in the DESTL_Provider during reading the file " << thePath
<< "\t: theDocument shouldn't be null";
return false;
}
TopoDS_Shape aShape;
@@ -86,25 +92,35 @@ bool DESTL_Provider::Write(const TCollection_AsciiString& thePath,
const Handle(TDocStd_Document)& theDocument,
const Message_ProgressRange& theProgress)
{
TopoDS_Shape aShape;
TCollection_AsciiString aContext = TCollection_AsciiString("writing the file ") + thePath;
if (!DE_ValidationUtils::ValidateDocument(theDocument, aContext))
{
return false;
}
// Extract shape from document
TDF_LabelSequence aLabels;
Handle(XCAFDoc_ShapeTool) aSTool = XCAFDoc_DocumentTool::ShapeTool(theDocument->Main());
aSTool->GetFreeShapes(aLabels);
if (aLabels.Length() <= 0)
{
Message::SendFail() << "Error in the DESTL_Provider during writing the file " << thePath
<< "\t: Document contain no shapes";
<< ": Document contain no shapes";
return false;
}
if (!DE_ValidationUtils::ValidateConfigurationNode(GetNode(),
STANDARD_TYPE(DESTL_ConfigurationNode),
aContext))
{
return false;
}
Handle(DESTL_ConfigurationNode) aNode = Handle(DESTL_ConfigurationNode)::DownCast(GetNode());
if (aNode->GlobalParameters.LengthUnit != 1.0)
{
Message::SendWarning()
<< "Warning in the DESTL_Provider during writing the file " << thePath
<< "\t: Target Units for writing were changed, but current format doesn't support scaling";
}
DE_ValidationUtils::WarnLengthUnitNotSupported(aNode->GlobalParameters.LengthUnit, aContext);
TopoDS_Shape aShape;
if (aLabels.Length() == 1)
{
aShape = aSTool->GetShape(aLabels.Value(1));
@@ -121,6 +137,7 @@ bool DESTL_Provider::Write(const TCollection_AsciiString& thePath,
}
aShape = aComp;
}
return Write(thePath, aShape, theProgress);
}
@@ -154,14 +171,18 @@ bool DESTL_Provider::Read(const TCollection_AsciiString& thePath,
{
Message::SendWarning()
<< "OCCT Stl reader does not support model scaling according to custom length unit";
if (!GetNode()->IsKind(STANDARD_TYPE(DESTL_ConfigurationNode)))
TCollection_AsciiString aContext = TCollection_AsciiString("reading the file ") + thePath;
if (!DE_ValidationUtils::ValidateConfigurationNode(GetNode(),
STANDARD_TYPE(DESTL_ConfigurationNode),
aContext))
{
Message::SendFail() << "Error in the DESTL_Provider during reading the file " << thePath
<< "\t: Incorrect or empty Configuration Node";
return true;
return false;
}
Handle(DESTL_ConfigurationNode) aNode = Handle(DESTL_ConfigurationNode)::DownCast(GetNode());
double aMergeAngle = aNode->InternalParameters.ReadMergeAngle * M_PI / 180.0;
if (aMergeAngle != M_PI_2)
{
if (aMergeAngle < 0.0 || aMergeAngle > M_PI_2)
@@ -171,6 +192,7 @@ bool DESTL_Provider::Read(const TCollection_AsciiString& thePath,
return false;
}
}
if (!aNode->InternalParameters.ReadBRep)
{
Handle(Poly_Triangulation) aTriangulation =
@@ -184,7 +206,9 @@ bool DESTL_Provider::Read(const TCollection_AsciiString& thePath,
}
else
{
Standard_DISABLE_DEPRECATION_WARNINGS if (!StlAPI::Read(theShape, thePath.ToCString()))
Standard_DISABLE_DEPRECATION_WARNINGS
if (!StlAPI::Read(theShape, thePath.ToCString()))
{
Message::SendFail() << "Error in the DESTL_Provider during reading the file " << thePath;
return false;
@@ -202,25 +226,23 @@ bool DESTL_Provider::Write(const TCollection_AsciiString& thePath,
{
Message::SendWarning()
<< "OCCT Stl writer does not support model scaling according to custom length unit";
if (GetNode().IsNull() || !GetNode()->IsKind(STANDARD_TYPE(DESTL_ConfigurationNode)))
TCollection_AsciiString aContext = TCollection_AsciiString("writing the file ") + thePath;
if (!DE_ValidationUtils::ValidateConfigurationNode(GetNode(),
STANDARD_TYPE(DESTL_ConfigurationNode),
aContext))
{
Message::SendFail() << "Error in the DESTL_Provider during reading the file " << thePath
<< "\t: Incorrect or empty Configuration Node";
return false;
}
Handle(DESTL_ConfigurationNode) aNode = Handle(DESTL_ConfigurationNode)::DownCast(GetNode());
if (aNode->GlobalParameters.LengthUnit != 1.0)
{
Message::SendWarning()
<< "Warning in the DESTL_Provider during writing the file " << thePath
<< "\t: Target Units for writing were changed, but current format doesn't support scaling";
}
DE_ValidationUtils::WarnLengthUnitNotSupported(aNode->GlobalParameters.LengthUnit, aContext);
StlAPI_Writer aWriter;
aWriter.ASCIIMode() = aNode->InternalParameters.WriteAscii;
if (!aWriter.Write(theShape, thePath.ToCString(), theProgress))
{
Message::SendFail() << "Error in the DESTL_Provider during reading the file " << thePath
Message::SendFail() << "Error in the DESTL_Provider during writing the file " << thePath
<< "\t: Mesh writing has been failed";
return false;
}
@@ -229,6 +251,271 @@ bool DESTL_Provider::Write(const TCollection_AsciiString& thePath,
//=================================================================================================
Standard_Boolean DESTL_Provider::Read(ReadStreamList& theStreams,
const Handle(TDocStd_Document)& theDocument,
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress)
{
(void)theWS;
return Read(theStreams, theDocument, theProgress);
}
//=================================================================================================
Standard_Boolean DESTL_Provider::Write(WriteStreamList& theStreams,
const Handle(TDocStd_Document)& theDocument,
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress)
{
(void)theWS;
return Write(theStreams, theDocument, theProgress);
}
//=================================================================================================
Standard_Boolean DESTL_Provider::Read(ReadStreamList& theStreams,
TopoDS_Shape& theShape,
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress)
{
(void)theWS;
return Read(theStreams, theShape, theProgress);
}
//=================================================================================================
Standard_Boolean DESTL_Provider::Write(WriteStreamList& theStreams,
const TopoDS_Shape& theShape,
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress)
{
(void)theWS;
return Write(theStreams, theShape, theProgress);
}
//=================================================================================================
Standard_Boolean DESTL_Provider::Read(ReadStreamList& theStreams,
const Handle(TDocStd_Document)& theDocument,
const Message_ProgressRange& theProgress)
{
TCollection_AsciiString aContext = "reading stream";
if (!DE_ValidationUtils::ValidateReadStreamList(theStreams, aContext))
{
return Standard_False;
}
const TCollection_AsciiString& aFirstKey = theStreams.First().Path;
TCollection_AsciiString aFullContext = aContext + " " + aFirstKey;
if (!DE_ValidationUtils::ValidateDocument(theDocument, aFullContext))
{
return Standard_False;
}
TopoDS_Shape aShape;
if (!Read(theStreams, aShape, theProgress))
{
return Standard_False;
}
Handle(XCAFDoc_ShapeTool) aShapeTool = XCAFDoc_DocumentTool::ShapeTool(theDocument->Main());
aShapeTool->AddShape(aShape);
return Standard_True;
}
//=================================================================================================
Standard_Boolean DESTL_Provider::Write(WriteStreamList& theStreams,
const Handle(TDocStd_Document)& theDocument,
const Message_ProgressRange& theProgress)
{
TCollection_AsciiString aContext = "writing stream";
if (!DE_ValidationUtils::ValidateWriteStreamList(theStreams, aContext))
{
return Standard_False;
}
const TCollection_AsciiString& aFirstKey = theStreams.First().Path;
TCollection_AsciiString aFullContext = aContext + " " + aFirstKey;
if (!DE_ValidationUtils::ValidateDocument(theDocument, aFullContext))
{
return Standard_False;
}
// Extract shape from document
TDF_LabelSequence aLabels;
Handle(XCAFDoc_ShapeTool) aSTool = XCAFDoc_DocumentTool::ShapeTool(theDocument->Main());
aSTool->GetFreeShapes(aLabels);
if (aLabels.Length() <= 0)
{
Message::SendFail() << "Error in the DESTL_Provider during writing stream " << aFirstKey
<< ": Document contain no shapes";
return Standard_False;
}
if (!DE_ValidationUtils::ValidateConfigurationNode(GetNode(),
STANDARD_TYPE(DESTL_ConfigurationNode),
aFullContext))
{
return Standard_False;
}
Handle(DESTL_ConfigurationNode) aNode = Handle(DESTL_ConfigurationNode)::DownCast(GetNode());
TCollection_AsciiString aLengthContext = TCollection_AsciiString("writing stream ") + aFirstKey;
DE_ValidationUtils::WarnLengthUnitNotSupported(aNode->GlobalParameters.LengthUnit,
aLengthContext);
TopoDS_Shape aShape;
if (aLabels.Length() == 1)
{
aShape = aSTool->GetShape(aLabels.Value(1));
}
else
{
TopoDS_Compound aComp;
BRep_Builder aBuilder;
aBuilder.MakeCompound(aComp);
for (Standard_Integer anIndex = 1; anIndex <= aLabels.Length(); anIndex++)
{
TopoDS_Shape aS = aSTool->GetShape(aLabels.Value(anIndex));
aBuilder.Add(aComp, aS);
}
aShape = aComp;
}
return Write(theStreams, aShape, theProgress);
}
//=================================================================================================
Standard_Boolean DESTL_Provider::Read(ReadStreamList& theStreams,
TopoDS_Shape& theShape,
const Message_ProgressRange& theProgress)
{
// Validate stream map
if (theStreams.IsEmpty())
{
Message::SendFail() << "Error: DESTL_Provider stream map is empty";
return Standard_False;
}
if (theStreams.Size() > 1)
{
Message::SendWarning() << "Warning: DESTL_Provider received " << theStreams.Size()
<< " streams for reading, using only the first one";
}
const TCollection_AsciiString& aFirstKey = theStreams.First().Path;
Standard_IStream& aStream = theStreams.First().Stream;
Message::SendWarning()
<< "OCCT Stl reader does not support model scaling according to custom length unit";
TCollection_AsciiString aNodeContext = TCollection_AsciiString("reading stream ") + aFirstKey;
if (!DE_ValidationUtils::ValidateConfigurationNode(GetNode(),
STANDARD_TYPE(DESTL_ConfigurationNode),
aNodeContext))
{
return Standard_False;
}
Handle(DESTL_ConfigurationNode) aNode = Handle(DESTL_ConfigurationNode)::DownCast(GetNode());
double aMergeAngle = aNode->InternalParameters.ReadMergeAngle * M_PI / 180.0;
if (aMergeAngle != M_PI_2)
{
if (aMergeAngle < 0.0 || aMergeAngle > M_PI_2)
{
Message::SendFail() << "Error in the DESTL_Provider during reading stream " << aFirstKey
<< ": The merge angle is out of the valid range";
return Standard_False;
}
}
if (!aNode->InternalParameters.ReadBRep)
{
Handle(Poly_Triangulation) aTriangulation =
RWStl::ReadStream(aStream, aMergeAngle, theProgress);
if (aTriangulation.IsNull())
{
Message::SendFail() << "Error in the DESTL_Provider during reading stream " << aFirstKey
<< ": Failed to create triangulation";
return Standard_False;
}
TopoDS_Face aFace;
BRep_Builder aB;
aB.MakeFace(aFace);
aB.UpdateFace(aFace, aTriangulation);
theShape = aFace;
}
else
{
Standard_DISABLE_DEPRECATION_WARNINGS
StlAPI_Reader aReader;
if (!aReader.Read(theShape, aStream))
{
Message::SendFail() << "Error in the DESTL_Provider during reading stream " << aFirstKey;
return Standard_False;
}
Standard_ENABLE_DEPRECATION_WARNINGS
}
return Standard_True;
}
//=================================================================================================
Standard_Boolean DESTL_Provider::Write(WriteStreamList& theStreams,
const TopoDS_Shape& theShape,
const Message_ProgressRange& theProgress)
{
// Validate stream map
if (theStreams.IsEmpty())
{
Message::SendFail() << "Error: DESTL_Provider stream map is empty";
return Standard_False;
}
if (theStreams.Size() > 1)
{
Message::SendWarning() << "Warning: DESTL_Provider received " << theStreams.Size()
<< " streams for writing, using only the first one";
}
const TCollection_AsciiString& aFirstKey = theStreams.First().Path;
Standard_OStream& aStream = theStreams.First().Stream;
Message::SendWarning()
<< "OCCT Stl writer does not support model scaling according to custom length unit";
TCollection_AsciiString aNodeContext = TCollection_AsciiString("writing stream ") + aFirstKey;
if (!DE_ValidationUtils::ValidateConfigurationNode(GetNode(),
STANDARD_TYPE(DESTL_ConfigurationNode),
aNodeContext))
{
return Standard_False;
}
Handle(DESTL_ConfigurationNode) aNode = Handle(DESTL_ConfigurationNode)::DownCast(GetNode());
TCollection_AsciiString aLengthContext = TCollection_AsciiString("writing stream ") + aFirstKey;
DE_ValidationUtils::WarnLengthUnitNotSupported(aNode->GlobalParameters.LengthUnit,
aLengthContext);
StlAPI_Writer aWriter;
aWriter.ASCIIMode() = aNode->InternalParameters.WriteAscii;
if (!aWriter.Write(theShape, aStream, theProgress))
{
Message::SendFail() << "Error in the DESTL_Provider during writing stream " << aFirstKey
<< ": Mesh writing has been failed";
return Standard_False;
}
return Standard_True;
}
//=================================================================================================
TCollection_AsciiString DESTL_Provider::GetFormat() const
{
return TCollection_AsciiString("STL");

View File

@@ -108,6 +108,54 @@ public:
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress = Message_ProgressRange()) Standard_OVERRIDE;
//! Reads streams according to internal configuration
//! @param[in] theStreams streams to read from
//! @param[out] theDocument document to save result
//! @param[in] theWS current work session
//! @param[in] theProgress progress indicator
//! @return true if Read operation has ended correctly
Standard_EXPORT virtual Standard_Boolean Read(
ReadStreamList& theStreams,
const Handle(TDocStd_Document)& theDocument,
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress = Message_ProgressRange()) Standard_OVERRIDE;
//! Writes streams according to internal configuration
//! @param[in] theStreams streams to write to
//! @param[out] theDocument document to export
//! @param[in] theWS current work session
//! @param[in] theProgress progress indicator
//! @return true if Write operation has ended correctly
Standard_EXPORT virtual Standard_Boolean Write(
WriteStreamList& theStreams,
const Handle(TDocStd_Document)& theDocument,
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress = Message_ProgressRange()) Standard_OVERRIDE;
//! Reads streams according to internal configuration
//! @param[in] theStreams streams to read from
//! @param[out] theShape shape to save result
//! @param[in] theWS current work session
//! @param[in] theProgress progress indicator
//! @return true if Read operation has ended correctly
Standard_EXPORT virtual Standard_Boolean Read(
ReadStreamList& theStreams,
TopoDS_Shape& theShape,
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress = Message_ProgressRange()) Standard_OVERRIDE;
//! Writes streams according to internal configuration
//! @param[in] theStreams streams to write to
//! @param[out] theShape shape to export
//! @param[in] theWS current work session
//! @param[in] theProgress progress indicator
//! @return true if Write operation has ended correctly
Standard_EXPORT virtual Standard_Boolean Write(
WriteStreamList& theStreams,
const TopoDS_Shape& theShape,
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress = Message_ProgressRange()) Standard_OVERRIDE;
//! Reads a CAD file, according internal configuration
//! @param[in] thePath path to the import CAD file
//! @param[out] theShape shape to save result
@@ -128,6 +176,46 @@ public:
const TopoDS_Shape& theShape,
const Message_ProgressRange& theProgress = Message_ProgressRange()) Standard_OVERRIDE;
//! Reads streams according to internal configuration
//! @param[in] theStreams streams to read from
//! @param[out] theDocument document to save result
//! @param[in] theProgress progress indicator
//! @return true if Read operation has ended correctly
Standard_EXPORT virtual Standard_Boolean Read(
ReadStreamList& theStreams,
const Handle(TDocStd_Document)& theDocument,
const Message_ProgressRange& theProgress = Message_ProgressRange()) Standard_OVERRIDE;
//! Writes streams according to internal configuration
//! @param[in] theStreams streams to write to
//! @param[out] theDocument document to export
//! @param[in] theProgress progress indicator
//! @return true if Write operation has ended correctly
Standard_EXPORT virtual Standard_Boolean Write(
WriteStreamList& theStreams,
const Handle(TDocStd_Document)& theDocument,
const Message_ProgressRange& theProgress = Message_ProgressRange()) Standard_OVERRIDE;
//! Reads streams according to internal configuration
//! @param[in] theStreams streams to read from
//! @param[out] theShape shape to save result
//! @param[in] theProgress progress indicator
//! @return true if Read operation has ended correctly
Standard_EXPORT virtual Standard_Boolean Read(
ReadStreamList& theStreams,
TopoDS_Shape& theShape,
const Message_ProgressRange& theProgress = Message_ProgressRange()) Standard_OVERRIDE;
//! Writes streams according to internal configuration
//! @param[in] theStreams streams to write to
//! @param[out] theShape shape to export
//! @param[in] theProgress progress indicator
//! @return true if Write operation has ended correctly
Standard_EXPORT virtual Standard_Boolean Write(
WriteStreamList& theStreams,
const TopoDS_Shape& theShape,
const Message_ProgressRange& theProgress = Message_ProgressRange()) Standard_OVERRIDE;
public:
//! Gets CAD format name of associated provider
//! @return provider CAD format

View File

@@ -0,0 +1,559 @@
// Copyright (c) 2025 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 <DESTL_Provider.hxx>
#include <DESTL_ConfigurationNode.hxx>
#include <DE_Wrapper.hxx>
#include <BRepPrimAPI_MakeBox.hxx>
#include <BRepPrimAPI_MakeSphere.hxx>
#include <BRepBuilderAPI_MakeFace.hxx>
#include <BRep_Builder.hxx>
#include <TopoDS_Shape.hxx>
#include <TopoDS_Face.hxx>
#include <TopExp_Explorer.hxx>
#include <TopAbs_ShapeEnum.hxx>
#include <gp_Pnt.hxx>
#include <gp_Pln.hxx>
#include <gp_Dir.hxx>
#include <Poly_Triangulation.hxx>
#include <Poly_Triangle.hxx>
#include <TColgp_Array1OfPnt.hxx>
#include <Poly_Array1OfTriangle.hxx>
#include <BRep_Tool.hxx>
#include <TopLoc_Location.hxx>
#include <TDocStd_Document.hxx>
#include <TDocStd_Application.hxx>
#include <XCAFDoc_DocumentTool.hxx>
#include <XCAFDoc_ShapeTool.hxx>
#include <sstream>
#include <gtest/gtest.h>
class DESTL_ProviderTest : public ::testing::Test
{
protected:
void SetUp() override
{
// Initialize provider with default configuration
Handle(DESTL_ConfigurationNode) aNode = new DESTL_ConfigurationNode();
myProvider = new DESTL_Provider(aNode);
// Create triangulated shape for testing (STL format requires triangulated data)
myTriangularFace = CreateTriangulatedFace();
// Create test document
Handle(TDocStd_Application) anApp = new TDocStd_Application();
anApp->NewDocument("BinXCAF", myDocument);
}
void TearDown() override
{
myProvider.Nullify();
myDocument.Nullify();
}
// Helper method to count shape elements
Standard_Integer CountShapeElements(const TopoDS_Shape& theShape, TopAbs_ShapeEnum theType)
{
Standard_Integer aCount = 0;
for (TopExp_Explorer anExplorer(theShape, theType); anExplorer.More(); anExplorer.Next())
{
aCount++;
}
return aCount;
}
// Helper method to create a triangulated face with mesh data (suitable for STL)
TopoDS_Shape CreateTriangulatedFace()
{
// Create vertices for triangulation
TColgp_Array1OfPnt aNodes(1, 4);
aNodes.SetValue(1, gp_Pnt(0.0, 0.0, 0.0)); // Bottom-left
aNodes.SetValue(2, gp_Pnt(10.0, 0.0, 0.0)); // Bottom-right
aNodes.SetValue(3, gp_Pnt(10.0, 10.0, 0.0)); // Top-right
aNodes.SetValue(4, gp_Pnt(0.0, 10.0, 0.0)); // Top-left
// Create triangles (two triangles forming a quad)
Poly_Array1OfTriangle aTriangles(1, 2);
aTriangles.SetValue(1, Poly_Triangle(1, 2, 3)); // First triangle
aTriangles.SetValue(2, Poly_Triangle(1, 3, 4)); // Second triangle
// Create triangulation
Handle(Poly_Triangulation) aTriangulation = new Poly_Triangulation(aNodes, aTriangles);
// Create a simple planar face
gp_Pln aPlane(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0, 0, 1));
BRepBuilderAPI_MakeFace aFaceBuilder(aPlane, 0.0, 10.0, 0.0, 10.0);
if (!aFaceBuilder.IsDone())
{
return TopoDS_Shape();
}
TopoDS_Face aFace = aFaceBuilder.Face();
// Attach triangulation to the face
BRep_Builder aBuilder;
aBuilder.UpdateFace(aFace, aTriangulation);
return aFace;
}
protected:
Handle(DESTL_Provider) myProvider;
TopoDS_Shape myTriangularFace;
Handle(TDocStd_Document) myDocument;
};
// Test basic provider creation and format/vendor information
TEST_F(DESTL_ProviderTest, BasicProperties)
{
EXPECT_STREQ("STL", myProvider->GetFormat().ToCString());
EXPECT_STREQ("OCC", myProvider->GetVendor().ToCString());
EXPECT_FALSE(myProvider->GetNode().IsNull());
}
// Test stream-based shape write and read operations
TEST_F(DESTL_ProviderTest, StreamShapeWriteRead)
{
std::ostringstream anOStream;
DE_Provider::WriteStreamList aWriteStreams;
aWriteStreams.Append(DE_Provider::WriteStreamNode("test.stl", anOStream));
// Write triangulated face to stream (STL works with mesh data)
EXPECT_TRUE(myProvider->Write(aWriteStreams, myTriangularFace));
std::string aStlContent = anOStream.str();
EXPECT_FALSE(aStlContent.empty());
EXPECT_TRUE(aStlContent.find("solid") != std::string::npos
|| aStlContent.find("facet") != std::string::npos);
if (!aStlContent.empty())
{
// Read back from stream
std::istringstream anIStream(aStlContent);
DE_Provider::ReadStreamList aReadStreams;
aReadStreams.Append(DE_Provider::ReadStreamNode("test.stl", anIStream));
TopoDS_Shape aReadShape;
EXPECT_TRUE(myProvider->Read(aReadStreams, aReadShape));
EXPECT_FALSE(aReadShape.IsNull());
if (!aReadShape.IsNull())
{
// STL should produce faces with triangulation
Standard_Integer aReadFaces = CountShapeElements(aReadShape, TopAbs_FACE);
EXPECT_GT(aReadFaces, 0); // Should have faces
}
}
}
// Test stream-based document write and read operations
TEST_F(DESTL_ProviderTest, StreamDocumentWriteRead)
{
// Add triangulated shape to document
Handle(XCAFDoc_ShapeTool) aShapeTool = XCAFDoc_DocumentTool::ShapeTool(myDocument->Main());
TDF_Label aShapeLabel = aShapeTool->AddShape(myTriangularFace);
EXPECT_FALSE(aShapeLabel.IsNull());
std::ostringstream anOStream;
DE_Provider::WriteStreamList aWriteStreams;
aWriteStreams.Append(DE_Provider::WriteStreamNode("document.stl", anOStream));
// Write document to stream
EXPECT_TRUE(myProvider->Write(aWriteStreams, myDocument));
std::string aStlContent = anOStream.str();
EXPECT_FALSE(aStlContent.empty());
if (!aStlContent.empty())
{
// Create new document for reading
Handle(TDocStd_Application) anApp = new TDocStd_Application();
Handle(TDocStd_Document) aNewDocument;
anApp->NewDocument("BinXCAF", aNewDocument);
// Read back from stream
std::istringstream anIStream(aStlContent);
DE_Provider::ReadStreamList aReadStreams;
aReadStreams.Append(DE_Provider::ReadStreamNode("document.stl", anIStream));
EXPECT_TRUE(myProvider->Read(aReadStreams, aNewDocument));
// Validate document content
Handle(XCAFDoc_ShapeTool) aNewShapeTool = XCAFDoc_DocumentTool::ShapeTool(aNewDocument->Main());
TDF_LabelSequence aLabels;
aNewShapeTool->GetShapes(aLabels);
EXPECT_GT(aLabels.Length(), 0); // Should have at least one shape in document
}
}
// Test DE_Wrapper integration for STL operations
TEST_F(DESTL_ProviderTest, DE_WrapperIntegration)
{
// Initialize DE_Wrapper and bind STL provider
DE_Wrapper aWrapper;
Handle(DESTL_ConfigurationNode) aNode = new DESTL_ConfigurationNode();
// Bind the configured node to wrapper
EXPECT_TRUE(aWrapper.Bind(aNode));
// Test write with DE_Wrapper using triangular face
std::ostringstream anOStream;
DE_Provider::WriteStreamList aWriteStreams;
aWriteStreams.Append(DE_Provider::WriteStreamNode("test.stl", anOStream));
EXPECT_TRUE(aWrapper.Write(aWriteStreams, myTriangularFace));
std::string aStlContent = anOStream.str();
EXPECT_FALSE(aStlContent.empty());
if (!aStlContent.empty())
{
// Test DE_Wrapper stream operations
std::istringstream anIStream(aStlContent);
DE_Provider::ReadStreamList aReadStreams;
aReadStreams.Append(DE_Provider::ReadStreamNode("test.stl", anIStream));
TopoDS_Shape aReadShape;
bool aWrapperResult = aWrapper.Read(aReadStreams, aReadShape);
// Test direct provider with same content for comparison
std::istringstream anIStream2(aStlContent);
DE_Provider::ReadStreamList aReadStreams2;
aReadStreams2.Append(DE_Provider::ReadStreamNode("test.stl", anIStream2));
Handle(DESTL_Provider) aDirectProvider = new DESTL_Provider(aNode);
TopoDS_Shape aDirectShape;
bool aDirectResult = aDirectProvider->Read(aReadStreams2, aDirectShape);
// REQUIREMENT: DE_Wrapper must work exactly the same as direct provider
EXPECT_EQ(aWrapperResult, aDirectResult);
EXPECT_EQ(aReadShape.IsNull(), aDirectShape.IsNull());
if (aDirectResult && !aDirectShape.IsNull())
{
Standard_Integer aFaces = CountShapeElements(aDirectShape, TopAbs_FACE);
EXPECT_GT(aFaces, 0);
}
}
}
// Test error conditions and edge cases with null document validation
TEST_F(DESTL_ProviderTest, ErrorHandling)
{
// Test with empty streams
DE_Provider::WriteStreamList anEmptyWriteStreams;
EXPECT_FALSE(myProvider->Write(anEmptyWriteStreams, myTriangularFace));
DE_Provider::ReadStreamList anEmptyReadStreams;
TopoDS_Shape aShape;
EXPECT_FALSE(myProvider->Read(anEmptyReadStreams, aShape));
// Test with null shape
std::ostringstream anOStream;
DE_Provider::WriteStreamList aWriteStreams;
aWriteStreams.Append(DE_Provider::WriteStreamNode("null_test.stl", anOStream));
TopoDS_Shape aNullShape;
// Writing null shape should succeed but produce minimal content
myProvider->Write(aWriteStreams, aNullShape);
std::string aContent = anOStream.str();
// STL may produce empty content for null shape
// Test reading invalid STL content
std::string anInvalidContent = "This is not valid STL content";
std::istringstream anInvalidStream(anInvalidContent);
DE_Provider::ReadStreamList anInvalidReadStreams;
anInvalidReadStreams.Append(DE_Provider::ReadStreamNode("invalid.stl", anInvalidStream));
TopoDS_Shape anInvalidShape;
EXPECT_FALSE(myProvider->Read(anInvalidReadStreams, anInvalidShape));
// Test with null document
Handle(TDocStd_Document) aNullDoc;
EXPECT_FALSE(myProvider->Write(aWriteStreams, aNullDoc));
EXPECT_FALSE(myProvider->Read(anEmptyReadStreams, aNullDoc));
}
// Test DESTL configuration modes
TEST_F(DESTL_ProviderTest, ConfigurationModes)
{
Handle(DESTL_ConfigurationNode) aNode =
Handle(DESTL_ConfigurationNode)::DownCast(myProvider->GetNode());
// Test ASCII mode configuration
aNode->InternalParameters.WriteAscii = Standard_True;
EXPECT_TRUE(aNode->InternalParameters.WriteAscii);
// Test Binary mode configuration
aNode->InternalParameters.WriteAscii = Standard_False;
EXPECT_FALSE(aNode->InternalParameters.WriteAscii);
// Test merge angle configuration
aNode->InternalParameters.ReadMergeAngle = 45.0;
EXPECT_DOUBLE_EQ(45.0, aNode->InternalParameters.ReadMergeAngle);
// Test that provider format and vendor are correct
EXPECT_STREQ("STL", myProvider->GetFormat().ToCString());
EXPECT_STREQ("OCC", myProvider->GetVendor().ToCString());
}
// Test ASCII vs Binary mode configurations
TEST_F(DESTL_ProviderTest, AsciiVsBinaryModes)
{
Handle(DESTL_ConfigurationNode) aNode =
Handle(DESTL_ConfigurationNode)::DownCast(myProvider->GetNode());
// Test ASCII mode
aNode->InternalParameters.WriteAscii = Standard_True;
std::ostringstream anAsciiStream;
DE_Provider::WriteStreamList anAsciiWriteStreams;
anAsciiWriteStreams.Append(DE_Provider::WriteStreamNode("ascii_test.stl", anAsciiStream));
EXPECT_TRUE(myProvider->Write(anAsciiWriteStreams, myTriangularFace));
std::string anAsciiContent = anAsciiStream.str();
EXPECT_FALSE(anAsciiContent.empty());
EXPECT_TRUE(anAsciiContent.find("solid") != std::string::npos);
// Test Binary mode
aNode->InternalParameters.WriteAscii = Standard_False;
std::ostringstream aBinaryStream;
DE_Provider::WriteStreamList aBinaryWriteStreams;
aBinaryWriteStreams.Append(DE_Provider::WriteStreamNode("binary_test.stl", aBinaryStream));
EXPECT_TRUE(myProvider->Write(aBinaryWriteStreams, myTriangularFace));
std::string aBinaryContent = aBinaryStream.str();
EXPECT_FALSE(aBinaryContent.empty());
// Binary and ASCII content should be different
EXPECT_NE(anAsciiContent, aBinaryContent);
}
// Test multiple shapes in single document
TEST_F(DESTL_ProviderTest, MultipleShapesInDocument)
{
// Add triangulated face to document multiple times (to create multiple shapes)
Handle(XCAFDoc_ShapeTool) aShapeTool = XCAFDoc_DocumentTool::ShapeTool(myDocument->Main());
TDF_Label aFaceLabel1 = aShapeTool->AddShape(myTriangularFace);
TDF_Label aFaceLabel2 = aShapeTool->AddShape(myTriangularFace); // Add same face again
EXPECT_FALSE(aFaceLabel1.IsNull());
EXPECT_FALSE(aFaceLabel2.IsNull());
std::ostringstream anOStream;
DE_Provider::WriteStreamList aWriteStreams;
aWriteStreams.Append(DE_Provider::WriteStreamNode("multi_shapes.stl", anOStream));
// Write document with multiple shapes
EXPECT_TRUE(myProvider->Write(aWriteStreams, myDocument));
std::string aStlContent = anOStream.str();
EXPECT_FALSE(aStlContent.empty());
// Read back into new document
Handle(TDocStd_Application) anApp = new TDocStd_Application();
Handle(TDocStd_Document) aNewDocument;
anApp->NewDocument("BinXCAF", aNewDocument);
std::istringstream anIStream(aStlContent);
DE_Provider::ReadStreamList aReadStreams;
aReadStreams.Append(DE_Provider::ReadStreamNode("multi_shapes.stl", anIStream));
EXPECT_TRUE(myProvider->Read(aReadStreams, aNewDocument));
// Validate document content
Handle(XCAFDoc_ShapeTool) aNewShapeTool = XCAFDoc_DocumentTool::ShapeTool(aNewDocument->Main());
TDF_LabelSequence aLabels;
aNewShapeTool->GetShapes(aLabels);
EXPECT_GT(aLabels.Length(), 0);
}
// Test triangulated face handling (suitable for STL)
TEST_F(DESTL_ProviderTest, TriangulatedFaceHandling)
{
// Use our triangulated face which already has mesh data
std::ostringstream anOStream;
DE_Provider::WriteStreamList aWriteStreams;
aWriteStreams.Append(DE_Provider::WriteStreamNode("triangulated_face.stl", anOStream));
EXPECT_TRUE(myProvider->Write(aWriteStreams, myTriangularFace));
std::string aStlContent = anOStream.str();
EXPECT_FALSE(aStlContent.empty());
EXPECT_GT(aStlContent.size(), 100); // Should have meaningful content
// Read back
std::istringstream anIStream(aStlContent);
DE_Provider::ReadStreamList aReadStreams;
aReadStreams.Append(DE_Provider::ReadStreamNode("triangulated_face.stl", anIStream));
TopoDS_Shape aReadShape;
EXPECT_TRUE(myProvider->Read(aReadStreams, aReadShape));
EXPECT_FALSE(aReadShape.IsNull());
}
// Test DE_Wrapper with different file extensions
TEST_F(DESTL_ProviderTest, DE_WrapperFileExtensions)
{
DE_Wrapper aWrapper;
Handle(DESTL_ConfigurationNode) aNode = new DESTL_ConfigurationNode();
EXPECT_TRUE(aWrapper.Bind(aNode));
// Test different STL extensions
std::vector<std::string> aExtensions = {"test.stl", "test.STL", "mesh.stl"};
for (const auto& anExt : aExtensions)
{
std::ostringstream anOStream;
DE_Provider::WriteStreamList aWriteStreams;
aWriteStreams.Append(DE_Provider::WriteStreamNode(anExt.c_str(), anOStream));
EXPECT_TRUE(aWrapper.Write(aWriteStreams, myTriangularFace))
<< "Failed to write with extension: " << anExt;
std::string aContent = anOStream.str();
EXPECT_FALSE(aContent.empty()) << "Empty content for extension: " << anExt;
// Test read back
std::istringstream anIStream(aContent);
DE_Provider::ReadStreamList aReadStreams;
aReadStreams.Append(DE_Provider::ReadStreamNode(anExt.c_str(), anIStream));
TopoDS_Shape aReadShape;
EXPECT_TRUE(aWrapper.Read(aReadStreams, aReadShape))
<< "Failed to read with extension: " << anExt;
EXPECT_FALSE(aReadShape.IsNull()) << "Null shape read with extension: " << anExt;
}
}
// Test stream operations with empty and invalid data
TEST_F(DESTL_ProviderTest, StreamErrorConditions)
{
// Test empty stream list
DE_Provider::WriteStreamList anEmptyWriteStreams;
EXPECT_FALSE(myProvider->Write(anEmptyWriteStreams, myTriangularFace));
DE_Provider::ReadStreamList anEmptyReadStreams;
TopoDS_Shape aShape;
EXPECT_FALSE(myProvider->Read(anEmptyReadStreams, aShape));
// Test corrupted STL data
std::string aCorruptedData = "This is not STL data at all";
std::istringstream aCorruptedStream(aCorruptedData);
DE_Provider::ReadStreamList aCorruptedReadStreams;
aCorruptedReadStreams.Append(DE_Provider::ReadStreamNode("corrupted.stl", aCorruptedStream));
TopoDS_Shape aCorruptedShape;
EXPECT_FALSE(myProvider->Read(aCorruptedReadStreams, aCorruptedShape));
// Test partial STL data
std::string aPartialData = "solid test\n facet normal 0 0 1\n"; // Incomplete
std::istringstream aPartialStream(aPartialData);
DE_Provider::ReadStreamList aPartialReadStreams;
aPartialReadStreams.Append(DE_Provider::ReadStreamNode("partial.stl", aPartialStream));
TopoDS_Shape aPartialShape;
EXPECT_FALSE(myProvider->Read(aPartialReadStreams, aPartialShape));
}
// Test configuration parameter validation
TEST_F(DESTL_ProviderTest, ConfigurationParameterValidation)
{
Handle(DESTL_ConfigurationNode) aNode =
Handle(DESTL_ConfigurationNode)::DownCast(myProvider->GetNode());
// Test valid merge angle
aNode->InternalParameters.ReadMergeAngle = 30.0;
EXPECT_DOUBLE_EQ(30.0, aNode->InternalParameters.ReadMergeAngle);
// Test edge case merge angles
aNode->InternalParameters.ReadMergeAngle = 0.0; // Minimum
EXPECT_DOUBLE_EQ(0.0, aNode->InternalParameters.ReadMergeAngle);
aNode->InternalParameters.ReadMergeAngle = 90.0; // Maximum
EXPECT_DOUBLE_EQ(90.0, aNode->InternalParameters.ReadMergeAngle);
// Test BRep vs triangulation modes
aNode->InternalParameters.ReadBRep = Standard_True;
EXPECT_TRUE(aNode->InternalParameters.ReadBRep);
aNode->InternalParameters.ReadBRep = Standard_False;
EXPECT_FALSE(aNode->InternalParameters.ReadBRep);
}
// Test multiple triangulated faces
TEST_F(DESTL_ProviderTest, MultipleTriangulatedFaces)
{
// Create another triangulated face with different geometry
TColgp_Array1OfPnt aNodes(1, 3);
aNodes.SetValue(1, gp_Pnt(0.0, 0.0, 0.0));
aNodes.SetValue(2, gp_Pnt(5.0, 0.0, 0.0));
aNodes.SetValue(3, gp_Pnt(2.5, 5.0, 0.0));
Poly_Array1OfTriangle aTriangles(1, 1);
aTriangles.SetValue(1, Poly_Triangle(1, 2, 3));
Handle(Poly_Triangulation) aTriangulation = new Poly_Triangulation(aNodes, aTriangles);
gp_Pln aPlane(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0, 0, 1));
BRepBuilderAPI_MakeFace aFaceBuilder(aPlane, 0.0, 5.0, 0.0, 5.0);
TopoDS_Face aTriangleFace = aFaceBuilder.Face();
BRep_Builder aBuilder;
aBuilder.UpdateFace(aTriangleFace, aTriangulation);
// Test first triangulated face
std::ostringstream aStream1;
DE_Provider::WriteStreamList aWriteStreams1;
aWriteStreams1.Append(DE_Provider::WriteStreamNode("face1.stl", aStream1));
EXPECT_TRUE(myProvider->Write(aWriteStreams1, myTriangularFace));
std::string aContent1 = aStream1.str();
// Test second triangulated face
std::ostringstream aStream2;
DE_Provider::WriteStreamList aWriteStreams2;
aWriteStreams2.Append(DE_Provider::WriteStreamNode("face2.stl", aStream2));
EXPECT_TRUE(myProvider->Write(aWriteStreams2, aTriangleFace));
std::string aContent2 = aStream2.str();
// Both content should be non-empty
EXPECT_FALSE(aContent1.empty());
EXPECT_FALSE(aContent2.empty());
// Different triangulated faces should produce different STL content
EXPECT_NE(aContent1, aContent2);
// Both should read back successfully
std::istringstream aIStream1(aContent1);
DE_Provider::ReadStreamList aReadStreams1;
aReadStreams1.Append(DE_Provider::ReadStreamNode("face1.stl", aIStream1));
TopoDS_Shape aReadShape1;
EXPECT_TRUE(myProvider->Read(aReadStreams1, aReadShape1));
EXPECT_FALSE(aReadShape1.IsNull());
std::istringstream aIStream2(aContent2);
DE_Provider::ReadStreamList aReadStreams2;
aReadStreams2.Append(DE_Provider::ReadStreamNode("face2.stl", aIStream2));
TopoDS_Shape aReadShape2;
EXPECT_TRUE(myProvider->Read(aReadStreams2, aReadShape2));
EXPECT_FALSE(aReadShape2.IsNull());
}

View File

@@ -2,4 +2,5 @@
set(OCCT_TKDESTL_GTests_FILES_LOCATION "${CMAKE_CURRENT_LIST_DIR}")
set(OCCT_TKDESTL_GTests_FILES
DESTL_Provider_Test.cxx
)

View File

@@ -21,6 +21,10 @@
#include <OSD_FileSystem.hxx>
#include <OSD_OpenFile.hxx>
#include <RWStl_Reader.hxx>
#include <Standard_ReadLineBuffer.hxx>
#include <iomanip>
#include <iostream>
#include <cstring>
namespace
{
@@ -254,16 +258,13 @@ Standard_Boolean RWStl::WriteBinary(const Handle(Poly_Triangulation)& theMesh,
TCollection_AsciiString aPath;
thePath.SystemName(aPath);
FILE* aFile = OSD_OpenFile(aPath, "wb");
if (aFile == NULL)
std::ofstream aStream(aPath.ToCString(), std::ios::binary);
if (!aStream.is_open())
{
return Standard_False;
}
Standard_Boolean isOK = writeBinary(theMesh, aFile, theProgress);
fclose(aFile);
return isOK;
return WriteBinary(theMesh, aStream, theProgress);
}
//=================================================================================================
@@ -280,45 +281,44 @@ Standard_Boolean RWStl::WriteAscii(const Handle(Poly_Triangulation)& theMesh,
TCollection_AsciiString aPath;
thePath.SystemName(aPath);
FILE* aFile = OSD_OpenFile(aPath, "w");
if (aFile == NULL)
std::ofstream aStream(aPath.ToCString());
if (!aStream.is_open())
{
return Standard_False;
}
Standard_Boolean isOK = writeASCII(theMesh, aFile, theProgress);
fclose(aFile);
return isOK;
return WriteAscii(theMesh, aStream, theProgress);
}
//=================================================================================================
Standard_Boolean RWStl::writeASCII(const Handle(Poly_Triangulation)& theMesh,
FILE* theFile,
Standard_Boolean RWStl::WriteAscii(const Handle(Poly_Triangulation)& theMesh,
Standard_OStream& theStream,
const Message_ProgressRange& theProgress)
{
// note that space after 'solid' is necessary for many systems
if (fwrite("solid \n", 1, 7, theFile) != 7)
if (theMesh.IsNull() || theMesh->NbTriangles() <= 0)
{
return Standard_False;
}
char aBuffer[512];
memset(aBuffer, 0, sizeof(aBuffer));
// note that space after 'solid' is necessary for many systems
theStream << "solid \n";
if (theStream.fail())
{
return Standard_False;
}
const Standard_Integer NBTriangles = theMesh->NbTriangles();
Message_ProgressScope aPS(theProgress, "Triangles", NBTriangles);
Standard_Integer anElem[3] = {0, 0, 0};
Standard_Integer anElem[3] = {0, 0, 0};
for (Standard_Integer aTriIter = 1; aTriIter <= NBTriangles; ++aTriIter)
{
const Poly_Triangle aTriangle = theMesh->Triangle(aTriIter);
aTriangle.Get(anElem[0], anElem[1], anElem[2]);
const gp_Pnt aP1 = theMesh->Node(anElem[0]);
const gp_Pnt aP2 = theMesh->Node(anElem[1]);
const gp_Pnt aP3 = theMesh->Node(anElem[2]);
const gp_Vec aVec1(aP1, aP2);
const gp_Vec aVec2(aP1, aP3);
gp_Vec aVNorm = aVec1.Crossed(aVec2);
@@ -331,28 +331,16 @@ Standard_Boolean RWStl::writeASCII(const Handle(Poly_Triangulation)& theMesh,
aVNorm.SetCoord(0.0, 0.0, 0.0);
}
Sprintf(aBuffer,
" facet normal % 12e % 12e % 12e\n"
" outer loop\n"
" vertex % 12e % 12e % 12e\n"
" vertex % 12e % 12e % 12e\n"
" vertex % 12e % 12e % 12e\n"
" endloop\n"
" endfacet\n",
aVNorm.X(),
aVNorm.Y(),
aVNorm.Z(),
aP1.X(),
aP1.Y(),
aP1.Z(),
aP2.X(),
aP2.Y(),
aP2.Z(),
aP3.X(),
aP3.Y(),
aP3.Z());
theStream << " facet normal " << std::scientific << std::setprecision(12) << aVNorm.X() << " "
<< aVNorm.Y() << " " << aVNorm.Z() << "\n"
<< " outer loop\n"
<< " vertex " << aP1.X() << " " << aP1.Y() << " " << aP1.Z() << "\n"
<< " vertex " << aP2.X() << " " << aP2.Y() << " " << aP2.Z() << "\n"
<< " vertex " << aP3.X() << " " << aP3.Y() << " " << aP3.Z() << "\n"
<< " endloop\n"
<< " endfacet\n";
if (fprintf(theFile, "%s", aBuffer) < 0)
if (theStream.fail())
{
return Standard_False;
}
@@ -366,37 +354,38 @@ Standard_Boolean RWStl::writeASCII(const Handle(Poly_Triangulation)& theMesh,
}
}
if (fwrite("endsolid\n", 1, 9, theFile) != 9)
{
return Standard_False;
}
return Standard_True;
theStream << "endsolid\n";
return !theStream.fail();
}
//=================================================================================================
Standard_Boolean RWStl::writeBinary(const Handle(Poly_Triangulation)& theMesh,
FILE* theFile,
Standard_Boolean RWStl::WriteBinary(const Handle(Poly_Triangulation)& theMesh,
Standard_OStream& theStream,
const Message_ProgressRange& theProgress)
{
char aHeader[80] = "STL Exported by Open CASCADE Technology [dev.opencascade.org]";
if (fwrite(aHeader, 1, 80, theFile) != 80)
if (theMesh.IsNull() || theMesh->NbTriangles() <= 0)
{
return Standard_False;
}
const Standard_Integer aNBTriangles = theMesh->NbTriangles();
Message_ProgressScope aPS(theProgress, "Triangles", aNBTriangles);
char aHeader[80] = "STL Exported by Open CASCADE Technology [dev.opencascade.org]";
theStream.write(aHeader, 80);
if (theStream.fail())
{
return Standard_False;
}
const Standard_Integer aNBTriangles = theMesh->NbTriangles();
Message_ProgressScope aPS(theProgress, "Triangles", aNBTriangles);
const Standard_Size aNbChunkTriangles = 4096;
const Standard_Size aChunkSize = aNbChunkTriangles * THE_STL_SIZEOF_FACET;
NCollection_Array1<Standard_Character> aData(1, aChunkSize);
Standard_Character* aDataChunk = &aData.ChangeFirst();
Standard_Character aConv[4];
Standard_Character aConv[4];
convertInteger(aNBTriangles, aConv);
if (fwrite(aConv, 1, 4, theFile) != 4)
theStream.write(aConv, 4);
if (theStream.fail())
{
return Standard_False;
}
@@ -407,14 +396,12 @@ Standard_Boolean RWStl::writeBinary(const Handle(Poly_Triangulation)& theMesh,
Standard_Integer id[3];
const Poly_Triangle aTriangle = theMesh->Triangle(aTriIter);
aTriangle.Get(id[0], id[1], id[2]);
const gp_Pnt aP1 = theMesh->Node(id[0]);
const gp_Pnt aP2 = theMesh->Node(id[1]);
const gp_Pnt aP3 = theMesh->Node(id[2]);
gp_Vec aVec1(aP1, aP2);
gp_Vec aVec2(aP1, aP3);
gp_Vec aVNorm = aVec1.Crossed(aVec2);
gp_Vec aVec1(aP1, aP2);
gp_Vec aVec2(aP1, aP3);
gp_Vec aVNorm = aVec1.Crossed(aVec2);
if (aVNorm.SquareMagnitude() > gp::Resolution())
{
aVNorm.Normalize();
@@ -423,48 +410,42 @@ Standard_Boolean RWStl::writeBinary(const Handle(Poly_Triangulation)& theMesh,
{
aVNorm.SetCoord(0.0, 0.0, 0.0);
}
convertDouble(aVNorm.X(), &aDataChunk[aByteCount]);
aByteCount += 4;
convertDouble(aVNorm.Y(), &aDataChunk[aByteCount]);
aByteCount += 4;
convertDouble(aVNorm.Z(), &aDataChunk[aByteCount]);
aByteCount += 4;
convertDouble(aP1.X(), &aDataChunk[aByteCount]);
aByteCount += 4;
convertDouble(aP1.Y(), &aDataChunk[aByteCount]);
aByteCount += 4;
convertDouble(aP1.Z(), &aDataChunk[aByteCount]);
aByteCount += 4;
convertDouble(aP2.X(), &aDataChunk[aByteCount]);
aByteCount += 4;
convertDouble(aP2.Y(), &aDataChunk[aByteCount]);
aByteCount += 4;
convertDouble(aP2.Z(), &aDataChunk[aByteCount]);
aByteCount += 4;
convertDouble(aP3.X(), &aDataChunk[aByteCount]);
aByteCount += 4;
convertDouble(aP3.Y(), &aDataChunk[aByteCount]);
aByteCount += 4;
convertDouble(aP3.Z(), &aDataChunk[aByteCount]);
aByteCount += 4;
aDataChunk[aByteCount] = 0;
aDataChunk[aByteCount + 1] = 0;
aByteCount += 2;
aDataChunk[aByteCount] = 0;
aByteCount += 1;
aDataChunk[aByteCount] = 0;
aByteCount += 1;
// Chunk is filled. Dump it to the file.
// Chunk is full, write it out.
if (aByteCount == aChunkSize)
{
if (fwrite(aDataChunk, 1, aChunkSize, theFile) != aChunkSize)
theStream.write(aDataChunk, aChunkSize);
if (theStream.fail())
{
return Standard_False;
}
aByteCount = 0;
}
@@ -480,7 +461,8 @@ Standard_Boolean RWStl::writeBinary(const Handle(Poly_Triangulation)& theMesh,
// Write last part if necessary.
if (aByteCount != aChunkSize)
{
if (fwrite(aDataChunk, 1, aByteCount, theFile) != aByteCount)
theStream.write(aDataChunk, aByteCount);
if (theStream.fail())
{
return Standard_False;
}
@@ -488,3 +470,66 @@ Standard_Boolean RWStl::writeBinary(const Handle(Poly_Triangulation)& theMesh,
return Standard_True;
}
//=================================================================================================
Handle(Poly_Triangulation) RWStl::ReadBinaryStream(Standard_IStream& theStream,
const Standard_Real theMergeAngle,
const Message_ProgressRange& theProgress)
{
Reader aReader;
aReader.SetMergeAngle(theMergeAngle);
if (!aReader.ReadBinary(theStream, theProgress))
{
return Handle(Poly_Triangulation)();
}
return aReader.GetTriangulation();
}
//=================================================================================================
Handle(Poly_Triangulation) RWStl::ReadAsciiStream(Standard_IStream& theStream,
const Standard_Real theMergeAngle,
const Message_ProgressRange& theProgress)
{
Reader aReader;
aReader.SetMergeAngle(theMergeAngle);
// get length of stream to feed progress indicator
theStream.seekg(0, theStream.end);
std::streampos theEnd = theStream.tellg();
theStream.seekg(0, theStream.beg);
Standard_ReadLineBuffer aBuffer(THE_BUFFER_SIZE);
if (!aReader.ReadAscii(theStream, aBuffer, theEnd, theProgress))
{
return Handle(Poly_Triangulation)();
}
return aReader.GetTriangulation();
}
//=================================================================================================
Handle(Poly_Triangulation) RWStl::ReadStream(Standard_IStream& theStream,
const Standard_Real theMergeAngle,
const Message_ProgressRange& theProgress)
{
// Try to detect ASCII vs Binary format by peeking at the first few bytes
std::streampos anOriginalPos = theStream.tellg();
constexpr std::streamsize aHeaderSize = 6;
char aHeader[aHeaderSize];
theStream.read(aHeader, aHeaderSize - 1);
aHeader[aHeaderSize - 1] = '\0';
theStream.seekg(anOriginalPos);
bool isAscii = (strncmp(aHeader, "solid", 5) == 0);
if (isAscii)
{
return RWStl::ReadAsciiStream(theStream, theMergeAngle, theProgress);
}
else
{
return RWStl::ReadBinaryStream(theStream, theMergeAngle, theProgress);
}
}

View File

@@ -34,6 +34,12 @@ public:
const OSD_Path& thePath,
const Message_ProgressRange& theProgress = Message_ProgressRange());
//! Write triangulation to binary STL stream.
Standard_EXPORT static Standard_Boolean WriteBinary(
const Handle(Poly_Triangulation)& theMesh,
Standard_OStream& theStream,
const Message_ProgressRange& theProgress = Message_ProgressRange());
//! write the meshing in a file following the
//! Ascii format of an STL file.
//! Returns false if the cannot be opened;
@@ -42,6 +48,12 @@ public:
const OSD_Path& thePath,
const Message_ProgressRange& theProgress = Message_ProgressRange());
//! Write triangulation to ASCII STL stream.
Standard_EXPORT static Standard_Boolean WriteAscii(
const Handle(Poly_Triangulation)& theMesh,
Standard_OStream& theStream,
const Message_ProgressRange& theProgress = Message_ProgressRange());
//! Read specified STL file and returns its content as triangulation.
//! In case of error, returns Null handle.
Standard_EXPORT static Handle(Poly_Triangulation) ReadFile(
@@ -92,16 +104,26 @@ public:
const OSD_Path& thePath,
const Message_ProgressRange& theProgress = Message_ProgressRange());
private:
//! Write ASCII version.
static Standard_Boolean writeASCII(const Handle(Poly_Triangulation)& theMesh,
FILE* theFile,
const Message_ProgressRange& theProgress);
//! Read triangulation from binary STL stream
//! In case of error, returns Null handle.
Standard_EXPORT static Handle(Poly_Triangulation) ReadBinaryStream(
Standard_IStream& theStream,
const Standard_Real theMergeAngle = M_PI / 2.0,
const Message_ProgressRange& theProgress = Message_ProgressRange());
//! Write binary version.
static Standard_Boolean writeBinary(const Handle(Poly_Triangulation)& theMesh,
FILE* theFile,
const Message_ProgressRange& theProgress);
//! Read triangulation from ASCII STL stream
//! In case of error, returns Null handle.
Standard_EXPORT static Handle(Poly_Triangulation) ReadAsciiStream(
Standard_IStream& theStream,
const Standard_Real theMergeAngle = M_PI / 2.0,
const Message_ProgressRange& theProgress = Message_ProgressRange());
//! Read STL data from stream (auto-detects ASCII vs Binary)
//! In case of error, returns Null handle.
Standard_EXPORT static Handle(Poly_Triangulation) ReadStream(
Standard_IStream& theStream,
const Standard_Real theMergeAngle = M_PI / 2.0,
const Message_ProgressRange& theProgress = Message_ProgressRange());
};
#endif

View File

@@ -37,3 +37,24 @@ Standard_Boolean StlAPI_Reader::Read(TopoDS_Shape& theShape, const Standard_CStr
theShape = aResult;
return Standard_True;
}
//=================================================================================================
Standard_Boolean StlAPI_Reader::Read(TopoDS_Shape& theShape, Standard_IStream& theStream)
{
Handle(Poly_Triangulation) aMesh = RWStl::ReadStream(theStream);
if (aMesh.IsNull())
return Standard_False;
BRepBuilderAPI_MakeShapeOnMesh aConverter(aMesh);
aConverter.Build();
if (!aConverter.IsDone())
return Standard_False;
TopoDS_Shape aResult = aConverter.Shape();
if (aResult.IsNull())
return Standard_False;
theShape = aResult;
return Standard_True;
}

View File

@@ -17,6 +17,7 @@
#define _StlAPI_Reader_HeaderFile
#include <Standard_Handle.hxx>
#include <Standard_IStream.hxx>
class TopoDS_Shape;
@@ -30,6 +31,12 @@ public:
//! Reads STL file to the TopoDS_Shape (each triangle is converted to the face).
//! @return True if reading is successful
Standard_EXPORT Standard_Boolean Read(TopoDS_Shape& theShape, const Standard_CString theFileName);
//! Reads STL data from stream to the TopoDS_Shape (each triangle is converted to the face).
//! @param theShape result shape
//! @param theStream stream to read from
//! @return True if reading is successful
Standard_EXPORT Standard_Boolean Read(TopoDS_Shape& theShape, Standard_IStream& theStream);
};
#endif // _StlAPI_Reader_HeaderFile

View File

@@ -22,6 +22,7 @@
#include <TopoDS_Face.hxx>
#include <TopExp_Explorer.hxx>
#include <Poly_Triangulation.hxx>
#include <fstream>
//=================================================================================================
@@ -36,6 +37,21 @@ StlAPI_Writer::StlAPI_Writer()
Standard_Boolean StlAPI_Writer::Write(const TopoDS_Shape& theShape,
const Standard_CString theFileName,
const Message_ProgressRange& theProgress)
{
std::ofstream aStream(theFileName, myASCIIMode ? std::ios::out : std::ios::binary);
if (!aStream.is_open())
{
return Standard_False;
}
return Write(theShape, aStream, theProgress);
}
//=================================================================================================
Standard_Boolean StlAPI_Writer::Write(const TopoDS_Shape& theShape,
Standard_OStream& theStream,
const Message_ProgressRange& theProgress)
{
Standard_Integer aNbNodes = 0;
Standard_Integer aNbTriangles = 0;
@@ -115,9 +131,8 @@ Standard_Boolean StlAPI_Writer::Write(const TopoDS_Shape& theShape,
aTriangleOffet += aTriangulation->NbTriangles();
}
OSD_Path aPath(theFileName);
Standard_Boolean isDone = (myASCIIMode ? RWStl::WriteAscii(aMesh, aPath, theProgress)
: RWStl::WriteBinary(aMesh, aPath, theProgress));
Standard_Boolean isDone = (myASCIIMode ? RWStl::WriteAscii(aMesh, theStream, theProgress)
: RWStl::WriteBinary(aMesh, theStream, theProgress));
if (isDone && (aNbFacesNoTri > 0))
{
@@ -130,4 +145,4 @@ Standard_Boolean StlAPI_Writer::Write(const TopoDS_Shape& theShape,
}
return isDone;
}
}

View File

@@ -45,6 +45,13 @@ public:
const Standard_CString theFileName,
const Message_ProgressRange& theProgress = Message_ProgressRange());
//! Converts a given shape to STL format and writes it to the specified stream.
//! \return the error state.
Standard_EXPORT Standard_Boolean
Write(const TopoDS_Shape& theShape,
Standard_OStream& theStream,
const Message_ProgressRange& theProgress = Message_ProgressRange());
private:
Standard_Boolean myASCIIMode;
};

View File

@@ -172,6 +172,13 @@ bool DEVRML_ConfigurationNode::IsExportSupported() const
//=================================================================================================
bool DEVRML_ConfigurationNode::IsStreamSupported() const
{
return true;
}
//=================================================================================================
TCollection_AsciiString DEVRML_ConfigurationNode::GetFormat() const
{
return TCollection_AsciiString("VRML");

View File

@@ -65,6 +65,10 @@ public:
//! @return true if export is supported
Standard_EXPORT virtual bool IsExportSupported() const Standard_OVERRIDE;
//! Checks for stream support.
//! @return Standard_True if streams are supported
Standard_EXPORT virtual bool IsStreamSupported() const Standard_OVERRIDE;
//! Gets CAD format name of associated provider
//! @return provider CAD format
Standard_EXPORT virtual TCollection_AsciiString GetFormat() const Standard_OVERRIDE;

View File

@@ -13,6 +13,7 @@
#include <DEVRML_Provider.hxx>
#include <DE_ValidationUtils.hxx>
#include <DEVRML_ConfigurationNode.hxx>
#include <Message.hxx>
#include <OSD_Path.hxx>
@@ -23,8 +24,189 @@
#include <XCAFDoc_DocumentTool.hxx>
#include <XCAFDoc_ShapeTool.hxx>
#include <stdexcept>
IMPLEMENT_STANDARD_RTTIEXT(DEVRML_Provider, DE_Provider)
namespace
{
// Helper function to validate configuration node and downcast
static Handle(DEVRML_ConfigurationNode) ValidateConfigurationNode(
const Handle(DE_ConfigurationNode)& theNode,
const TCollection_AsciiString& theContext)
{
if (!DE_ValidationUtils::ValidateConfigurationNode(theNode,
STANDARD_TYPE(DEVRML_ConfigurationNode),
theContext))
{
return Handle(DEVRML_ConfigurationNode)();
}
return Handle(DEVRML_ConfigurationNode)::DownCast(theNode);
}
// Static function to handle VrmlData_Scene status errors
static Standard_Boolean HandleVrmlSceneStatus(const VrmlData_Scene& theScene,
const TCollection_AsciiString& theContext)
{
const char* aStr = nullptr;
switch (theScene.Status())
{
case VrmlData_StatusOK:
return Standard_True;
case VrmlData_EmptyData:
aStr = "EmptyData";
break;
case VrmlData_UnrecoverableError:
aStr = "UnrecoverableError";
break;
case VrmlData_GeneralError:
aStr = "GeneralError";
break;
case VrmlData_EndOfFile:
aStr = "EndOfFile";
break;
case VrmlData_NotVrmlFile:
aStr = "NotVrmlFile";
break;
case VrmlData_CannotOpenFile:
aStr = "CannotOpenFile";
break;
case VrmlData_VrmlFormatError:
aStr = "VrmlFormatError";
break;
case VrmlData_NumericInputError:
aStr = "NumericInputError";
break;
case VrmlData_IrrelevantNumber:
aStr = "IrrelevantNumber";
break;
case VrmlData_BooleanInputError:
aStr = "BooleanInputError";
break;
case VrmlData_StringInputError:
aStr = "StringInputError";
break;
case VrmlData_NodeNameUnknown:
aStr = "NodeNameUnknown";
break;
case VrmlData_NonPositiveSize:
aStr = "NonPositiveSize";
break;
case VrmlData_ReadUnknownNode:
aStr = "ReadUnknownNode";
break;
case VrmlData_NonSupportedFeature:
aStr = "NonSupportedFeature";
break;
case VrmlData_OutputStreamUndefined:
aStr = "OutputStreamUndefined";
break;
case VrmlData_NotImplemented:
aStr = "NotImplemented";
break;
default:
break;
}
if (aStr)
{
Message::SendFail() << "Error in the DEVRML_Provider during " << theContext
<< ": ++ VRML Error: " << aStr << " in line " << theScene.GetLineError();
return Standard_False;
}
return Standard_True;
}
// Static function to calculate scaling factor
static Standard_Real CalculateScalingFactor(const Handle(TDocStd_Document)& theDocument,
const Handle(DEVRML_ConfigurationNode)& theNode,
const TCollection_AsciiString& theContext)
{
Standard_Real aScaling = 1.;
Standard_Real aScaleFactorMM = 1.;
if (XCAFDoc_DocumentTool::GetLengthUnit(theDocument,
aScaleFactorMM,
UnitsMethods_LengthUnit_Millimeter))
{
aScaling = aScaleFactorMM / theNode->GlobalParameters.LengthUnit;
}
else
{
aScaling = theNode->GlobalParameters.SystemUnit / theNode->GlobalParameters.LengthUnit;
Message::SendWarning()
<< "Warning in the DEVRML_Provider during " << theContext
<< ": The document has no information on Units. Using global parameter as initial Unit.";
}
return aScaling;
}
// Static function to extract VRML directory path from file path
static TCollection_AsciiString ExtractVrmlDirectory(const TCollection_AsciiString& thePath)
{
OSD_Path aPath(thePath.ToCString());
TCollection_AsciiString aVrmlDir(".");
TCollection_AsciiString aDisk = aPath.Disk();
TCollection_AsciiString aTrek = aPath.Trek();
if (!aTrek.IsEmpty())
{
if (!aDisk.IsEmpty())
{
aVrmlDir = aDisk;
}
else
{
aVrmlDir.Clear();
}
aTrek.ChangeAll('|', '/');
aVrmlDir += aTrek;
}
return aVrmlDir;
}
// Static function to process VRML scene from stream and extract shape
static Standard_Boolean ProcessVrmlScene(Standard_IStream& theStream,
const Handle(DEVRML_ConfigurationNode)& theNode,
const TCollection_AsciiString& theVrmlDir,
TopoDS_Shape& theShape,
const TCollection_AsciiString& theContext)
{
VrmlData_Scene aScene;
aScene.SetLinearScale(theNode->GlobalParameters.LengthUnit);
aScene.SetVrmlDir(theVrmlDir);
aScene << theStream;
if (!HandleVrmlSceneStatus(aScene, theContext))
{
return Standard_False;
}
if (aScene.Status() == VrmlData_StatusOK)
{
VrmlData_DataMapOfShapeAppearance aShapeAppMap;
TopoDS_Shape aShape = aScene.GetShape(aShapeAppMap);
theShape = aShape;
// Verify that a valid shape was extracted
if (theShape.IsNull())
{
Message::SendFail() << "Error in the DEVRML_Provider during " << theContext
<< ": VRML scene processed successfully but no geometry was extracted";
return Standard_False;
}
}
else
{
// Scene status was not OK but HandleVrmlSceneStatus didn't catch it
Message::SendFail() << "Error in the DEVRML_Provider during " << theContext
<< ": VRML scene status is not OK but no specific error was reported";
return Standard_False;
}
return Standard_True;
}
} // namespace
//=================================================================================================
DEVRML_Provider::DEVRML_Provider() {}
@@ -64,19 +246,16 @@ bool DEVRML_Provider::Read(const TCollection_AsciiString& thePath,
const Handle(TDocStd_Document)& theDocument,
const Message_ProgressRange& theProgress)
{
if (theDocument.IsNull())
TCollection_AsciiString aContext = TCollection_AsciiString("reading the file ") + thePath;
if (!DE_ValidationUtils::ValidateDocument(theDocument, aContext))
{
Message::SendFail() << "Error in the DEVRML_Provider during reading the file " << thePath
<< "\t: theDocument shouldn't be null";
return false;
}
if (GetNode().IsNull() || !GetNode()->IsKind(STANDARD_TYPE(DEVRML_ConfigurationNode)))
Handle(DEVRML_ConfigurationNode) aNode = ValidateConfigurationNode(GetNode(), aContext);
if (aNode.IsNull())
{
Message::SendFail() << "Error in the DEVRML_Provider during reading the file " << thePath
<< "\t: Incorrect or empty Configuration Node";
return false;
}
Handle(DEVRML_ConfigurationNode) aNode = Handle(DEVRML_ConfigurationNode)::DownCast(GetNode());
VrmlAPI_CafReader aVrmlReader;
aVrmlReader.SetDocument(theDocument);
@@ -110,32 +289,24 @@ bool DEVRML_Provider::Write(const TCollection_AsciiString& thePath,
const Message_ProgressRange& theProgress)
{
(void)theProgress;
if (GetNode().IsNull() || !GetNode()->IsKind(STANDARD_TYPE(DEVRML_ConfigurationNode)))
TCollection_AsciiString aContext = "writing the file ";
aContext += thePath;
if (!DE_ValidationUtils::ValidateDocument(theDocument, aContext))
{
return false;
}
Handle(DEVRML_ConfigurationNode) aNode = ValidateConfigurationNode(GetNode(), aContext);
if (aNode.IsNull())
{
Message::SendFail() << "Error in the DEVRML_Provider during writing the file " << thePath
<< "\t: Incorrect or empty Configuration Node";
return false;
}
Handle(DEVRML_ConfigurationNode) aNode = Handle(DEVRML_ConfigurationNode)::DownCast(GetNode());
VrmlAPI_Writer aWriter;
aWriter.SetRepresentation(
static_cast<VrmlAPI_RepresentationOfShape>(aNode->InternalParameters.WriteRepresentationType));
Standard_Real aScaling = 1.;
Standard_Real aScaleFactorMM = 1.;
if (XCAFDoc_DocumentTool::GetLengthUnit(theDocument,
aScaleFactorMM,
UnitsMethods_LengthUnit_Millimeter))
{
aScaling = aScaleFactorMM / aNode->GlobalParameters.LengthUnit;
}
else
{
aScaling = aNode->GlobalParameters.SystemUnit / aNode->GlobalParameters.LengthUnit;
Message::SendWarning()
<< "Warning in the DEVRML_Provider during writing the file " << thePath
<< "\t: The document has no information on Units. Using global parameter as initial Unit.";
}
Standard_Real aScaling = CalculateScalingFactor(theDocument, aNode, aContext);
if (!aWriter.WriteDoc(theDocument, thePath.ToCString(), aScaling))
{
Message::SendFail() << "Error in the DEVRML_Provider during wtiting the file " << thePath
@@ -175,125 +346,26 @@ bool DEVRML_Provider::Read(const TCollection_AsciiString& thePath,
const Message_ProgressRange& theProgress)
{
(void)theProgress;
if (GetNode().IsNull() || !GetNode()->IsKind(STANDARD_TYPE(DEVRML_ConfigurationNode)))
TCollection_AsciiString aContext = "reading the file ";
aContext += thePath;
Handle(DEVRML_ConfigurationNode) aNode = ValidateConfigurationNode(GetNode(), aContext);
if (aNode.IsNull())
{
Message::SendFail() << "Error in the DEVRML_Provider during reading the file " << thePath
<< "\t: Incorrect or empty Configuration Node";
return false;
}
Handle(DEVRML_ConfigurationNode) aNode = Handle(DEVRML_ConfigurationNode)::DownCast(GetNode());
TopoDS_Shape aShape;
VrmlData_DataMapOfShapeAppearance aShapeAppMap;
std::filebuf aFic;
std::istream aStream(&aFic);
if (aFic.open(thePath.ToCString(), std::ios::in))
{
// Get path of the VRML file.
OSD_Path aPath(thePath.ToCString());
TCollection_AsciiString aVrmlDir(".");
TCollection_AsciiString aDisk = aPath.Disk();
TCollection_AsciiString aTrek = aPath.Trek();
if (!aTrek.IsEmpty())
{
if (!aDisk.IsEmpty())
{
aVrmlDir = aDisk;
}
else
{
aVrmlDir.Clear();
}
aTrek.ChangeAll('|', '/');
aVrmlDir += aTrek;
}
VrmlData_Scene aScene;
aScene.SetLinearScale(aNode->GlobalParameters.LengthUnit);
aScene.SetVrmlDir(aVrmlDir);
aScene << aStream;
const char* aStr = 0L;
switch (aScene.Status())
{
case VrmlData_StatusOK: {
aShape = aScene.GetShape(aShapeAppMap);
break;
}
case VrmlData_EmptyData:
aStr = "EmptyData";
break;
case VrmlData_UnrecoverableError:
aStr = "UnrecoverableError";
break;
case VrmlData_GeneralError:
aStr = "GeneralError";
break;
case VrmlData_EndOfFile:
aStr = "EndOfFile";
break;
case VrmlData_NotVrmlFile:
aStr = "NotVrmlFile";
break;
case VrmlData_CannotOpenFile:
aStr = "CannotOpenFile";
break;
case VrmlData_VrmlFormatError:
aStr = "VrmlFormatError";
break;
case VrmlData_NumericInputError:
aStr = "NumericInputError";
break;
case VrmlData_IrrelevantNumber:
aStr = "IrrelevantNumber";
break;
case VrmlData_BooleanInputError:
aStr = "BooleanInputError";
break;
case VrmlData_StringInputError:
aStr = "StringInputError";
break;
case VrmlData_NodeNameUnknown:
aStr = "NodeNameUnknown";
break;
case VrmlData_NonPositiveSize:
aStr = "NonPositiveSize";
break;
case VrmlData_ReadUnknownNode:
aStr = "ReadUnknownNode";
break;
case VrmlData_NonSupportedFeature:
aStr = "NonSupportedFeature";
break;
case VrmlData_OutputStreamUndefined:
aStr = "OutputStreamUndefined";
break;
case VrmlData_NotImplemented:
aStr = "NotImplemented";
break;
default:
break;
}
if (aStr)
{
Message::SendFail() << "Error in the DEVRML_Provider during reading the file " << thePath
<< "\t: ++ VRML Error: " << aStr << " in line " << aScene.GetLineError();
return false;
}
else
{
theShape = aShape;
}
}
else
if (!aFic.open(thePath.ToCString(), std::ios::in))
{
Message::SendFail() << "Error in the DEVRML_Provider during reading the file " << thePath
<< "\t: cannot open file";
return false;
}
return true;
TCollection_AsciiString aVrmlDir = ExtractVrmlDirectory(thePath);
return ProcessVrmlScene(aStream, aNode, aVrmlDir, theShape, aContext);
}
//=================================================================================================
@@ -310,6 +382,189 @@ bool DEVRML_Provider::Write(const TCollection_AsciiString& thePath,
//=================================================================================================
Standard_Boolean DEVRML_Provider::Read(ReadStreamList& theStreams,
const Handle(TDocStd_Document)& theDocument,
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress)
{
(void)theWS;
return Read(theStreams, theDocument, theProgress);
}
//=================================================================================================
Standard_Boolean DEVRML_Provider::Write(WriteStreamList& theStreams,
const Handle(TDocStd_Document)& theDocument,
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress)
{
(void)theWS;
return Write(theStreams, theDocument, theProgress);
}
//=================================================================================================
Standard_Boolean DEVRML_Provider::Read(ReadStreamList& theStreams,
TopoDS_Shape& theShape,
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress)
{
(void)theWS;
return Read(theStreams, theShape, theProgress);
}
//=================================================================================================
Standard_Boolean DEVRML_Provider::Write(WriteStreamList& theStreams,
const TopoDS_Shape& theShape,
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress)
{
(void)theWS;
return Write(theStreams, theShape, theProgress);
}
//=================================================================================================
Standard_Boolean DEVRML_Provider::Read(ReadStreamList& theStreams,
const Handle(TDocStd_Document)& theDocument,
const Message_ProgressRange& theProgress)
{
TCollection_AsciiString aContext = "reading stream";
if (!DE_ValidationUtils::ValidateReadStreamList(theStreams, aContext))
{
return Standard_False;
}
const TCollection_AsciiString& aFirstKey = theStreams.First().Path;
TCollection_AsciiString aFullContext = aContext + " " + aFirstKey;
if (!DE_ValidationUtils::ValidateDocument(theDocument, aFullContext))
{
return Standard_False;
}
TopoDS_Shape aShape;
if (!Read(theStreams, aShape, theProgress))
{
return Standard_False;
}
Handle(XCAFDoc_ShapeTool) aShapeTool = XCAFDoc_DocumentTool::ShapeTool(theDocument->Main());
aShapeTool->AddShape(aShape);
return Standard_True;
}
//=================================================================================================
Standard_Boolean DEVRML_Provider::Write(WriteStreamList& theStreams,
const Handle(TDocStd_Document)& theDocument,
const Message_ProgressRange& theProgress)
{
(void)theProgress;
TCollection_AsciiString aContext = "writing stream";
if (!DE_ValidationUtils::ValidateWriteStreamList(theStreams, aContext))
{
return Standard_False;
}
const TCollection_AsciiString& aFirstKey = theStreams.First().Path;
TCollection_AsciiString aFullContext = aContext + " " + aFirstKey;
if (!DE_ValidationUtils::ValidateDocument(theDocument, aFullContext))
{
return Standard_False;
}
Handle(DEVRML_ConfigurationNode) aNode = ValidateConfigurationNode(GetNode(), aFullContext);
if (aNode.IsNull())
{
return Standard_False;
}
Standard_Real aScaling = CalculateScalingFactor(theDocument, aNode, aContext);
// Use VrmlAPI_Writer with stream support
VrmlAPI_Writer aWriter;
aWriter.SetRepresentation(
static_cast<VrmlAPI_RepresentationOfShape>(aNode->InternalParameters.WriteRepresentationType));
Standard_OStream& aStream = theStreams.First().Stream;
if (!aWriter.WriteDoc(theDocument, aStream, aScaling))
{
Message::SendFail() << "Error in the DEVRML_Provider during " << aContext
<< ": WriteDoc operation failed";
return Standard_False;
}
return Standard_True;
}
//=================================================================================================
Standard_Boolean DEVRML_Provider::Read(ReadStreamList& theStreams,
TopoDS_Shape& theShape,
const Message_ProgressRange& theProgress)
{
(void)theProgress;
TCollection_AsciiString aContext = "reading stream";
if (!DE_ValidationUtils::ValidateReadStreamList(theStreams, aContext))
{
return Standard_False;
}
const TCollection_AsciiString& aFirstKey = theStreams.First().Path;
Standard_IStream& aStream = theStreams.First().Stream;
TCollection_AsciiString aFullContext = aContext + " " + aFirstKey;
Handle(DEVRML_ConfigurationNode) aNode = ValidateConfigurationNode(GetNode(), aFullContext);
if (aNode.IsNull())
{
return Standard_False;
}
return ProcessVrmlScene(aStream, aNode, ".", theShape, aContext);
}
//=================================================================================================
Standard_Boolean DEVRML_Provider::Write(WriteStreamList& theStreams,
const TopoDS_Shape& theShape,
const Message_ProgressRange& theProgress)
{
(void)theProgress;
TCollection_AsciiString aContext = "writing stream";
if (!DE_ValidationUtils::ValidateWriteStreamList(theStreams, aContext))
{
return Standard_False;
}
const TCollection_AsciiString& aFirstKey = theStreams.First().Path;
TCollection_AsciiString aFullContext = aContext + " " + aFirstKey;
Handle(DEVRML_ConfigurationNode) aNode = ValidateConfigurationNode(GetNode(), aFullContext);
if (aNode.IsNull())
{
return Standard_False;
}
// Use VrmlAPI_Writer with stream support
VrmlAPI_Writer aWriter;
aWriter.SetRepresentation(
static_cast<VrmlAPI_RepresentationOfShape>(aNode->InternalParameters.WriteRepresentationType));
Standard_OStream& aStream = theStreams.First().Stream;
if (!aWriter.Write(theShape, aStream, 2)) // Use version 2 by default
{
Message::SendFail() << "Error in the DEVRML_Provider during " << aContext
<< ": Write operation failed";
return Standard_False;
}
return Standard_True;
}
//=================================================================================================
TCollection_AsciiString DEVRML_Provider::GetFormat() const
{
return TCollection_AsciiString("VRML");

View File

@@ -108,6 +108,54 @@ public:
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress = Message_ProgressRange()) Standard_OVERRIDE;
//! Reads streams according to internal configuration
//! @param[in] theStreams streams to read from
//! @param[out] theDocument document to save result
//! @param[in] theWS current work session
//! @param[in] theProgress progress indicator
//! @return true if Read operation has ended correctly
Standard_EXPORT virtual Standard_Boolean Read(
ReadStreamList& theStreams,
const Handle(TDocStd_Document)& theDocument,
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress = Message_ProgressRange()) Standard_OVERRIDE;
//! Writes streams according to internal configuration
//! @param[in] theStreams streams to write to
//! @param[out] theDocument document to export
//! @param[in] theWS current work session
//! @param[in] theProgress progress indicator
//! @return true if Write operation has ended correctly
Standard_EXPORT virtual Standard_Boolean Write(
WriteStreamList& theStreams,
const Handle(TDocStd_Document)& theDocument,
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress = Message_ProgressRange()) Standard_OVERRIDE;
//! Reads streams according to internal configuration
//! @param[in] theStreams streams to read from
//! @param[out] theShape shape to save result
//! @param[in] theWS current work session
//! @param[in] theProgress progress indicator
//! @return true if Read operation has ended correctly
Standard_EXPORT virtual Standard_Boolean Read(
ReadStreamList& theStreams,
TopoDS_Shape& theShape,
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress = Message_ProgressRange()) Standard_OVERRIDE;
//! Writes streams according to internal configuration
//! @param[in] theStreams streams to write to
//! @param[out] theShape shape to export
//! @param[in] theWS current work session
//! @param[in] theProgress progress indicator
//! @return true if Write operation has ended correctly
Standard_EXPORT virtual Standard_Boolean Write(
WriteStreamList& theStreams,
const TopoDS_Shape& theShape,
Handle(XSControl_WorkSession)& theWS,
const Message_ProgressRange& theProgress = Message_ProgressRange()) Standard_OVERRIDE;
//! Reads a CAD file, according internal configuration
//! @param[in] thePath path to the import CAD file
//! @param[out] theShape shape to save result
@@ -128,6 +176,46 @@ public:
const TopoDS_Shape& theShape,
const Message_ProgressRange& theProgress = Message_ProgressRange()) Standard_OVERRIDE;
//! Reads streams according to internal configuration
//! @param[in] theStreams streams to read from
//! @param[out] theDocument document to save result
//! @param[in] theProgress progress indicator
//! @return true if Read operation has ended correctly
Standard_EXPORT virtual Standard_Boolean Read(
ReadStreamList& theStreams,
const Handle(TDocStd_Document)& theDocument,
const Message_ProgressRange& theProgress = Message_ProgressRange()) Standard_OVERRIDE;
//! Writes streams according to internal configuration
//! @param[in] theStreams streams to write to
//! @param[out] theDocument document to export
//! @param[in] theProgress progress indicator
//! @return true if Write operation has ended correctly
Standard_EXPORT virtual Standard_Boolean Write(
WriteStreamList& theStreams,
const Handle(TDocStd_Document)& theDocument,
const Message_ProgressRange& theProgress = Message_ProgressRange()) Standard_OVERRIDE;
//! Reads streams according to internal configuration
//! @param[in] theStreams streams to read from
//! @param[out] theShape shape to save result
//! @param[in] theProgress progress indicator
//! @return true if Read operation has ended correctly
Standard_EXPORT virtual Standard_Boolean Read(
ReadStreamList& theStreams,
TopoDS_Shape& theShape,
const Message_ProgressRange& theProgress = Message_ProgressRange()) Standard_OVERRIDE;
//! Writes streams according to internal configuration
//! @param[in] theStreams streams to write to
//! @param[out] theShape shape to export
//! @param[in] theProgress progress indicator
//! @return true if Write operation has ended correctly
Standard_EXPORT virtual Standard_Boolean Write(
WriteStreamList& theStreams,
const TopoDS_Shape& theShape,
const Message_ProgressRange& theProgress = Message_ProgressRange()) Standard_OVERRIDE;
public:
//! Gets CAD format name of associated provider
//! @return provider CAD format

View File

@@ -0,0 +1,519 @@
// Copyright (c) 2025 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 <DEVRML_Provider.hxx>
#include <DEVRML_ConfigurationNode.hxx>
#include <DE_Wrapper.hxx>
#include <BRepPrimAPI_MakeBox.hxx>
#include <BRepPrimAPI_MakeSphere.hxx>
#include <BRepBuilderAPI_MakeFace.hxx>
#include <BRep_Builder.hxx>
#include <TopoDS_Shape.hxx>
#include <TopoDS_Face.hxx>
#include <TopExp_Explorer.hxx>
#include <TopAbs_ShapeEnum.hxx>
#include <gp_Pnt.hxx>
#include <gp_Pln.hxx>
#include <gp_Dir.hxx>
#include <Poly_Triangulation.hxx>
#include <Poly_Triangle.hxx>
#include <TColgp_Array1OfPnt.hxx>
#include <Poly_Array1OfTriangle.hxx>
#include <BRep_Tool.hxx>
#include <TopLoc_Location.hxx>
#include <TDocStd_Document.hxx>
#include <TDocStd_Application.hxx>
#include <XCAFDoc_DocumentTool.hxx>
#include <XCAFDoc_ShapeTool.hxx>
#include <sstream>
#include <gtest/gtest.h>
class DEVRML_ProviderTest : public ::testing::Test
{
protected:
void SetUp() override
{
// Initialize provider with default configuration (will be modified per test)
Handle(DEVRML_ConfigurationNode) aNode = new DEVRML_ConfigurationNode();
myProvider = new DEVRML_Provider(aNode);
// Create test shapes
myBox = BRepPrimAPI_MakeBox(10.0, 10.0, 10.0).Shape(); // For wireframe testing
mySphere = BRepPrimAPI_MakeSphere(5.0).Shape(); // For wireframe testing
myTriangularFace = CreateTriangulatedFace(); // For shaded/face testing
// Create test document
Handle(TDocStd_Application) anApp = new TDocStd_Application();
anApp->NewDocument("BinXCAF", myDocument);
}
void TearDown() override
{
myProvider.Nullify();
myDocument.Nullify();
}
// Helper method to count shape elements
Standard_Integer CountShapeElements(const TopoDS_Shape& theShape, TopAbs_ShapeEnum theType)
{
Standard_Integer aCount = 0;
for (TopExp_Explorer anExplorer(theShape, theType); anExplorer.More(); anExplorer.Next())
{
aCount++;
}
return aCount;
}
// Helper method to create a triangulated face with mesh data
TopoDS_Shape CreateTriangulatedFace()
{
// Create vertices for triangulation
TColgp_Array1OfPnt aNodes(1, 4);
aNodes.SetValue(1, gp_Pnt(0.0, 0.0, 0.0)); // Bottom-left
aNodes.SetValue(2, gp_Pnt(10.0, 0.0, 0.0)); // Bottom-right
aNodes.SetValue(3, gp_Pnt(10.0, 10.0, 0.0)); // Top-right
aNodes.SetValue(4, gp_Pnt(0.0, 10.0, 0.0)); // Top-left
// Create triangles (two triangles forming a quad)
Poly_Array1OfTriangle aTriangles(1, 2);
aTriangles.SetValue(1, Poly_Triangle(1, 2, 3)); // First triangle
aTriangles.SetValue(2, Poly_Triangle(1, 3, 4)); // Second triangle
// Create triangulation
Handle(Poly_Triangulation) aTriangulation = new Poly_Triangulation(aNodes, aTriangles);
// Create a simple planar face
gp_Pln aPlane(gp_Pnt(0.0, 0.0, 0.0), gp_Dir(0, 0, 1));
BRepBuilderAPI_MakeFace aFaceBuilder(aPlane, 0.0, 10.0, 0.0, 10.0);
if (!aFaceBuilder.IsDone())
{
return TopoDS_Shape();
}
TopoDS_Face aFace = aFaceBuilder.Face();
// Attach triangulation to the face (without location parameter)
BRep_Builder aBuilder;
aBuilder.UpdateFace(aFace, aTriangulation);
return aFace;
}
protected:
Handle(DEVRML_Provider) myProvider;
TopoDS_Shape myBox;
TopoDS_Shape mySphere;
TopoDS_Shape myTriangularFace;
Handle(TDocStd_Document) myDocument;
};
// Test basic provider creation and format/vendor information
TEST_F(DEVRML_ProviderTest, BasicProperties)
{
EXPECT_STREQ("VRML", myProvider->GetFormat().ToCString());
EXPECT_STREQ("OCC", myProvider->GetVendor().ToCString());
EXPECT_FALSE(myProvider->GetNode().IsNull());
}
// Test stream-based shape write and read operations with wireframe (edges)
TEST_F(DEVRML_ProviderTest, StreamShapeWriteReadWireframe)
{
// Configure provider for wireframe mode (default)
Handle(DEVRML_ConfigurationNode) aNode =
Handle(DEVRML_ConfigurationNode)::DownCast(myProvider->GetNode());
aNode->InternalParameters.WriteRepresentationType =
DEVRML_ConfigurationNode::WriteMode_RepresentationType_Wireframe;
std::ostringstream anOStream;
DE_Provider::WriteStreamList aWriteStreams;
aWriteStreams.Append(DE_Provider::WriteStreamNode("wireframe.vrml", anOStream));
// Write box to stream
EXPECT_TRUE(myProvider->Write(aWriteStreams, myBox));
std::string aVrmlContent = anOStream.str();
EXPECT_FALSE(aVrmlContent.empty());
EXPECT_TRUE(aVrmlContent.find("#VRML") != std::string::npos);
if (!aVrmlContent.empty())
{
// Read back from stream
std::istringstream anIStream(aVrmlContent);
DE_Provider::ReadStreamList aReadStreams;
aReadStreams.Append(DE_Provider::ReadStreamNode("wireframe.vrml", anIStream));
TopoDS_Shape aReadShape;
EXPECT_TRUE(myProvider->Read(aReadStreams, aReadShape));
EXPECT_FALSE(aReadShape.IsNull());
if (!aReadShape.IsNull())
{
// Wireframe mode should produce edges, not faces
Standard_Integer aReadEdges = CountShapeElements(aReadShape, TopAbs_EDGE);
EXPECT_TRUE(aReadEdges > 0); // Should have edges from wireframe
}
}
}
// Test stream-based shape write and read operations with shaded (faces)
TEST_F(DEVRML_ProviderTest, StreamShapeWriteReadShaded)
{
// Configure provider for shaded mode
Handle(DEVRML_ConfigurationNode) aNode =
Handle(DEVRML_ConfigurationNode)::DownCast(myProvider->GetNode());
aNode->InternalParameters.WriteRepresentationType =
DEVRML_ConfigurationNode::WriteMode_RepresentationType_Shaded;
std::ostringstream anOStream;
DE_Provider::WriteStreamList aWriteStreams;
aWriteStreams.Append(DE_Provider::WriteStreamNode("shaded.vrml", anOStream));
// Write triangular face to stream
EXPECT_TRUE(myProvider->Write(aWriteStreams, myTriangularFace));
std::string aVrmlContent = anOStream.str();
EXPECT_FALSE(aVrmlContent.empty());
EXPECT_TRUE(aVrmlContent.find("#VRML") != std::string::npos);
if (!aVrmlContent.empty())
{
// Read back from stream
std::istringstream anIStream(aVrmlContent);
DE_Provider::ReadStreamList aReadStreams;
aReadStreams.Append(DE_Provider::ReadStreamNode("shaded.vrml", anIStream));
TopoDS_Shape aReadShape;
EXPECT_TRUE(myProvider->Read(aReadStreams, aReadShape));
EXPECT_FALSE(aReadShape.IsNull());
if (!aReadShape.IsNull())
{
// Shaded mode should produce faces
Standard_Integer aReadFaces = CountShapeElements(aReadShape, TopAbs_FACE);
EXPECT_TRUE(aReadFaces > 0); // Should have faces from shaded mode
}
}
}
// Test stream-based document write and read operations
TEST_F(DEVRML_ProviderTest, StreamDocumentWriteRead)
{
// Configure provider for shaded mode for better document compatibility
Handle(DEVRML_ConfigurationNode) aNode =
Handle(DEVRML_ConfigurationNode)::DownCast(myProvider->GetNode());
aNode->InternalParameters.WriteRepresentationType =
DEVRML_ConfigurationNode::WriteMode_RepresentationType_Shaded;
// Add shape to document
Handle(XCAFDoc_ShapeTool) aShapeTool = XCAFDoc_DocumentTool::ShapeTool(myDocument->Main());
TDF_Label aShapeLabel = aShapeTool->AddShape(myTriangularFace);
EXPECT_FALSE(aShapeLabel.IsNull());
std::ostringstream anOStream;
DE_Provider::WriteStreamList aWriteStreams;
aWriteStreams.Append(DE_Provider::WriteStreamNode("document.vrml", anOStream));
// Write document to stream
EXPECT_TRUE(myProvider->Write(aWriteStreams, myDocument));
std::string aVrmlContent = anOStream.str();
EXPECT_FALSE(aVrmlContent.empty());
EXPECT_TRUE(aVrmlContent.find("#VRML") != std::string::npos);
if (!aVrmlContent.empty())
{
// Create new document for reading
Handle(TDocStd_Application) anApp = new TDocStd_Application();
Handle(TDocStd_Document) aNewDocument;
anApp->NewDocument("BinXCAF", aNewDocument);
// Read back from stream
std::istringstream anIStream(aVrmlContent);
DE_Provider::ReadStreamList aReadStreams;
aReadStreams.Append(DE_Provider::ReadStreamNode("document.vrml", anIStream));
EXPECT_TRUE(myProvider->Read(aReadStreams, aNewDocument));
// Validate document content
Handle(XCAFDoc_ShapeTool) aNewShapeTool = XCAFDoc_DocumentTool::ShapeTool(aNewDocument->Main());
TDF_LabelSequence aLabels;
aNewShapeTool->GetShapes(aLabels);
EXPECT_GT(aLabels.Length(), 0); // Should have at least one shape in document
}
}
// Test stream-based document with multiple shapes
TEST_F(DEVRML_ProviderTest, StreamDocumentMultipleShapes)
{
// Configure provider for shaded mode for better multi-shape compatibility
Handle(DEVRML_ConfigurationNode) aNode =
Handle(DEVRML_ConfigurationNode)::DownCast(myProvider->GetNode());
aNode->InternalParameters.WriteRepresentationType =
DEVRML_ConfigurationNode::WriteMode_RepresentationType_Shaded;
// Add multiple shapes to document
Handle(XCAFDoc_ShapeTool) aShapeTool = XCAFDoc_DocumentTool::ShapeTool(myDocument->Main());
TDF_Label aFirstLabel = aShapeTool->AddShape(myTriangularFace);
EXPECT_FALSE(aFirstLabel.IsNull());
// Add a second shape - using the sphere for variety
TDF_Label aSecondLabel = aShapeTool->AddShape(mySphere);
EXPECT_FALSE(aSecondLabel.IsNull());
std::ostringstream anOStream;
DE_Provider::WriteStreamList aWriteStreams;
aWriteStreams.Append(DE_Provider::WriteStreamNode("multi_shapes.vrml", anOStream));
// Write document to stream
EXPECT_TRUE(myProvider->Write(aWriteStreams, myDocument));
std::string aVrmlContent = anOStream.str();
EXPECT_FALSE(aVrmlContent.empty());
EXPECT_TRUE(aVrmlContent.find("#VRML") != std::string::npos);
if (!aVrmlContent.empty())
{
// Create new document for reading
Handle(TDocStd_Application) anApp = new TDocStd_Application();
Handle(TDocStd_Document) aNewDocument;
anApp->NewDocument("BinXCAF", aNewDocument);
// Read back from stream
std::istringstream anIStream(aVrmlContent);
DE_Provider::ReadStreamList aReadStreams;
aReadStreams.Append(DE_Provider::ReadStreamNode("multi_shapes.vrml", anIStream));
EXPECT_TRUE(myProvider->Read(aReadStreams, aNewDocument));
// Validate document content
Handle(XCAFDoc_ShapeTool) aNewShapeTool = XCAFDoc_DocumentTool::ShapeTool(aNewDocument->Main());
TDF_LabelSequence aLabels;
aNewShapeTool->GetShapes(aLabels);
EXPECT_GT(aLabels.Length(), 0); // Should have at least one shape in document
}
}
// Test DE_Wrapper integration for VRML operations
TEST_F(DEVRML_ProviderTest, DE_WrapperIntegration)
{
// Initialize DE_Wrapper and bind VRML provider
DE_Wrapper aWrapper;
Handle(DEVRML_ConfigurationNode) aNode = new DEVRML_ConfigurationNode();
// Configure for shaded mode to ensure faces are generated
aNode->InternalParameters.WriteRepresentationType =
DEVRML_ConfigurationNode::WriteMode_RepresentationType_Shaded;
// Bind the configured node to wrapper
EXPECT_TRUE(aWrapper.Bind(aNode));
// Test write with DE_Wrapper using triangular face
std::ostringstream anOStream;
DE_Provider::WriteStreamList aWriteStreams;
aWriteStreams.Append(DE_Provider::WriteStreamNode("test.vrml", anOStream));
EXPECT_TRUE(aWrapper.Write(aWriteStreams, myTriangularFace));
std::string aVrmlContent = anOStream.str();
EXPECT_FALSE(aVrmlContent.empty());
EXPECT_TRUE(aVrmlContent.find("#VRML") != std::string::npos);
if (!aVrmlContent.empty())
{
// Test DE_Wrapper stream operations - the key functionality we wanted to verify
std::istringstream anIStream(aVrmlContent);
DE_Provider::ReadStreamList aReadStreams;
aReadStreams.Append(DE_Provider::ReadStreamNode("test.vrml", anIStream));
TopoDS_Shape aReadShape;
bool aWrapperResult = aWrapper.Read(aReadStreams, aReadShape);
// Test direct provider with same content for comparison
std::istringstream anIStream2(aVrmlContent);
DE_Provider::ReadStreamList aReadStreams2;
aReadStreams2.Append(DE_Provider::ReadStreamNode("test.vrml", anIStream2));
Handle(DEVRML_Provider) aDirectProvider = new DEVRML_Provider(aNode);
TopoDS_Shape aDirectShape;
bool aDirectResult = aDirectProvider->Read(aReadStreams2, aDirectShape);
// REQUIREMENT: DE_Wrapper must work exactly the same as direct provider
EXPECT_EQ(aWrapperResult, aDirectResult);
EXPECT_EQ(aReadShape.IsNull(), aDirectShape.IsNull());
if (aDirectResult && !aDirectShape.IsNull())
{
Standard_Integer aFaces = CountShapeElements(aDirectShape, TopAbs_FACE);
EXPECT_GT(aFaces, 0);
}
else if (aWrapperResult && !aReadShape.IsNull())
{
Standard_Integer aFaces = CountShapeElements(aReadShape, TopAbs_FACE);
EXPECT_GT(aFaces, 0);
}
}
}
// Test DE_Wrapper document operations
TEST_F(DEVRML_ProviderTest, DE_WrapperDocumentOperations)
{
// Initialize DE_Wrapper and bind VRML provider
DE_Wrapper aWrapper;
Handle(DEVRML_ConfigurationNode) aNode = new DEVRML_ConfigurationNode();
// Configure for shaded mode for better document operations
aNode->InternalParameters.WriteRepresentationType =
DEVRML_ConfigurationNode::WriteMode_RepresentationType_Shaded;
// Bind the node to wrapper
EXPECT_TRUE(aWrapper.Bind(aNode));
// Add shape to document
Handle(XCAFDoc_ShapeTool) aShapeTool = XCAFDoc_DocumentTool::ShapeTool(myDocument->Main());
TDF_Label aShapeLabel = aShapeTool->AddShape(myTriangularFace);
EXPECT_FALSE(aShapeLabel.IsNull());
// Test document write with DE_Wrapper
std::ostringstream anOStream;
DE_Provider::WriteStreamList aWriteStreams;
aWriteStreams.Append(DE_Provider::WriteStreamNode("doc.vrml", anOStream));
EXPECT_TRUE(aWrapper.Write(aWriteStreams, myDocument));
std::string aVrmlContent = anOStream.str();
EXPECT_FALSE(aVrmlContent.empty());
EXPECT_TRUE(aVrmlContent.find("#VRML") != std::string::npos);
if (!aVrmlContent.empty())
{
// Test document read with DE_Wrapper
Handle(TDocStd_Application) anApp = new TDocStd_Application();
Handle(TDocStd_Document) aNewDocument;
anApp->NewDocument("BinXCAF", aNewDocument);
std::istringstream anIStream(aVrmlContent);
DE_Provider::ReadStreamList aReadStreams;
aReadStreams.Append(DE_Provider::ReadStreamNode("doc.vrml", anIStream));
bool aWrapperDocResult = aWrapper.Read(aReadStreams, aNewDocument);
// Validate document content if read succeeded
if (aWrapperDocResult)
{
Handle(XCAFDoc_ShapeTool) aNewShapeTool =
XCAFDoc_DocumentTool::ShapeTool(aNewDocument->Main());
TDF_LabelSequence aLabels;
aNewShapeTool->GetShapes(aLabels);
EXPECT_GT(aLabels.Length(), 0);
}
else
{
// If DE_Wrapper document read fails, verify direct provider works as fallback
Handle(TDocStd_Application) anApp2 = new TDocStd_Application();
Handle(TDocStd_Document) aTestDocument;
anApp2->NewDocument("BinXCAF", aTestDocument);
std::istringstream anIStream2(aVrmlContent);
DE_Provider::ReadStreamList aReadStreams2;
aReadStreams2.Append(DE_Provider::ReadStreamNode("doc.vrml", anIStream2));
Handle(DEVRML_Provider) aDirectProvider = new DEVRML_Provider(aNode);
bool aDirectDocResult = aDirectProvider->Read(aReadStreams2, aTestDocument);
if (aDirectDocResult)
{
Handle(XCAFDoc_ShapeTool) aTestShapeTool =
XCAFDoc_DocumentTool::ShapeTool(aTestDocument->Main());
TDF_LabelSequence aTestLabels;
aTestShapeTool->GetShapes(aTestLabels);
EXPECT_GT(aTestLabels.Length(), 0);
}
}
}
}
// Test error conditions and edge cases
TEST_F(DEVRML_ProviderTest, ErrorHandling)
{
// Test with empty streams
DE_Provider::WriteStreamList anEmptyWriteStreams;
EXPECT_FALSE(myProvider->Write(anEmptyWriteStreams, myBox));
DE_Provider::ReadStreamList anEmptyReadStreams;
TopoDS_Shape aShape;
EXPECT_FALSE(myProvider->Read(anEmptyReadStreams, aShape));
// Test with null shape
std::ostringstream anOStream;
DE_Provider::WriteStreamList aWriteStreams;
aWriteStreams.Append(DE_Provider::WriteStreamNode("null_test.vrml", anOStream));
TopoDS_Shape aNullShape;
// Writing null shape might succeed but produce empty or minimal content
myProvider->Write(aWriteStreams, aNullShape);
std::string aContent = anOStream.str();
EXPECT_FALSE(aContent.empty()); // Should at least have VRML header
// Test reading invalid VRML content
std::string anInvalidContent = "This is not valid VRML content";
std::istringstream anInvalidStream(anInvalidContent);
DE_Provider::ReadStreamList anInvalidReadStreams;
anInvalidReadStreams.Append(DE_Provider::ReadStreamNode("invalid.vrml", anInvalidStream));
TopoDS_Shape anInvalidShape;
EXPECT_FALSE(myProvider->Read(anInvalidReadStreams, anInvalidShape));
// Test with null document
Handle(TDocStd_Document) aNullDoc;
EXPECT_FALSE(myProvider->Write(aWriteStreams, aNullDoc));
EXPECT_FALSE(myProvider->Read(anEmptyReadStreams, aNullDoc));
}
// Test different VRML configuration modes
TEST_F(DEVRML_ProviderTest, ConfigurationModes)
{
Handle(DEVRML_ConfigurationNode) aNode =
Handle(DEVRML_ConfigurationNode)::DownCast(myProvider->GetNode());
// Test wireframe mode configuration
aNode->InternalParameters.WriteRepresentationType =
DEVRML_ConfigurationNode::WriteMode_RepresentationType_Wireframe;
EXPECT_EQ(aNode->InternalParameters.WriteRepresentationType,
DEVRML_ConfigurationNode::WriteMode_RepresentationType_Wireframe);
// Test shaded mode configuration
aNode->InternalParameters.WriteRepresentationType =
DEVRML_ConfigurationNode::WriteMode_RepresentationType_Shaded;
EXPECT_EQ(aNode->InternalParameters.WriteRepresentationType,
DEVRML_ConfigurationNode::WriteMode_RepresentationType_Shaded);
// Test both mode configuration
aNode->InternalParameters.WriteRepresentationType =
DEVRML_ConfigurationNode::WriteMode_RepresentationType_Both;
EXPECT_EQ(aNode->InternalParameters.WriteRepresentationType,
DEVRML_ConfigurationNode::WriteMode_RepresentationType_Both);
// Test writer version configuration
aNode->InternalParameters.WriterVersion = DEVRML_ConfigurationNode::WriteMode_WriterVersion_1;
EXPECT_EQ(aNode->InternalParameters.WriterVersion,
DEVRML_ConfigurationNode::WriteMode_WriterVersion_1);
aNode->InternalParameters.WriterVersion = DEVRML_ConfigurationNode::WriteMode_WriterVersion_2;
EXPECT_EQ(aNode->InternalParameters.WriterVersion,
DEVRML_ConfigurationNode::WriteMode_WriterVersion_2);
// Test that provider format and vendor are correct
EXPECT_STREQ("VRML", myProvider->GetFormat().ToCString());
EXPECT_STREQ("OCC", myProvider->GetVendor().ToCString());
}

View File

@@ -2,4 +2,5 @@
set(OCCT_TKDEVRML_GTests_FILES_LOCATION "${CMAKE_CURRENT_LIST_DIR}")
set(OCCT_TKDEVRML_GTests_FILES
DEVRML_Provider_Test.cxx
)

View File

@@ -241,30 +241,67 @@ Handle(Vrml_Material) VrmlAPI_Writer::GetUnfreeBoundsMaterial() const
Standard_Boolean VrmlAPI_Writer::Write(const TopoDS_Shape& aShape,
const Standard_CString aFile,
const Standard_Integer aVersion) const
{
const Handle(OSD_FileSystem)& aFileSystem = OSD_FileSystem::DefaultFileSystem();
std::shared_ptr<std::ostream> anOutStream =
aFileSystem->OpenOStream(aFile, std::ios::out | std::ios::binary);
if (anOutStream.get() == NULL)
{
return Standard_False;
}
return Write(aShape, *anOutStream, aVersion);
}
Standard_Boolean VrmlAPI_Writer::WriteDoc(const Handle(TDocStd_Document)& theDoc,
const Standard_CString theFile,
const Standard_Real theScale) const
{
const Handle(OSD_FileSystem)& aFileSystem = OSD_FileSystem::DefaultFileSystem();
std::shared_ptr<std::ostream> anOutStream =
aFileSystem->OpenOStream(theFile, std::ios::out | std::ios::binary);
if (anOutStream.get() == NULL)
{
return Standard_False;
}
return WriteDoc(theDoc, *anOutStream, theScale);
}
//=================================================================================================
Standard_Boolean VrmlAPI_Writer::Write(const TopoDS_Shape& aShape,
Standard_OStream& theOStream,
const Standard_Integer aVersion) const
{
if (aVersion == 1)
return write_v1(aShape, aFile);
return write_v1(aShape, theOStream);
else if (aVersion == 2)
return write_v2(aShape, aFile);
return write_v2(aShape, theOStream);
return Standard_False;
}
Standard_Boolean VrmlAPI_Writer::write_v1(const TopoDS_Shape& aShape,
const Standard_CString aFile) const
//=================================================================================================
Standard_Boolean VrmlAPI_Writer::WriteDoc(const Handle(TDocStd_Document)& theDoc,
Standard_OStream& theOStream,
const Standard_Real theScale) const
{
OSD_Path thePath(aFile);
TCollection_AsciiString theFile;
thePath.SystemName(theFile);
const Handle(OSD_FileSystem)& aFileSystem = OSD_FileSystem::DefaultFileSystem();
std::shared_ptr<std::ostream> anOutFile =
aFileSystem->OpenOStream(theFile, std::ios::out | std::ios::binary);
if (anOutFile.get() == NULL)
{
return Standard_False;
}
Handle(VrmlConverter_IsoAspect) ia = new VrmlConverter_IsoAspect; // UIso
Handle(VrmlConverter_IsoAspect) ia1 = new VrmlConverter_IsoAspect; // VIso
VrmlData_Scene aScene;
VrmlData_ShapeConvert aConv(aScene, theScale);
aConv.ConvertDocument(theDoc);
theOStream << aScene;
theOStream.flush();
return theOStream.good();
}
Standard_Boolean VrmlAPI_Writer::write_v1(const TopoDS_Shape& aShape,
Standard_OStream& theOStream) const
{
Handle(VrmlConverter_IsoAspect) ia = new VrmlConverter_IsoAspect;
Handle(VrmlConverter_IsoAspect) ia1 = new VrmlConverter_IsoAspect;
ia->SetMaterial(myUisoMaterial);
ia->SetHasMaterial(Standard_True);
myDrawer->SetUIsoAspect(ia);
@@ -318,7 +355,6 @@ Standard_Boolean VrmlAPI_Writer::write_v1(const TopoDS_Shape& aShape,
if (!aFace.IsNull())
{
Handle(Poly_Triangulation) aTri = BRep_Tool::Triangulation(aFace, aLoc);
if (!aTri.IsNull())
{
hasTriangles = Standard_True;
@@ -335,57 +371,61 @@ Standard_Boolean VrmlAPI_Writer::write_v1(const TopoDS_Shape& aShape,
VrmlConverter_TypeOfCamera Camera = VrmlConverter_PerspectiveCamera;
Handle(VrmlConverter_Projector) projector =
new VrmlConverter_Projector(Shapes, Focus, DX, DY, DZ, XUp, YUp, ZUp, Camera, Light);
Vrml::VrmlHeaderWriter(*anOutFile);
Vrml::VrmlHeaderWriter(theOStream);
if (myRepresentation == VrmlAPI_BothRepresentation)
Vrml::CommentWriter(
" This file contents both Shaded and Wire Frame representation of selected Shape ",
*anOutFile);
theOStream);
if (myRepresentation == VrmlAPI_ShadedRepresentation)
Vrml::CommentWriter(" This file contents only Shaded representation of selected Shape ",
*anOutFile);
theOStream);
if (myRepresentation == VrmlAPI_WireFrameRepresentation)
Vrml::CommentWriter(" This file contents only Wire Frame representation of selected Shape ",
*anOutFile);
theOStream);
Vrml_Separator S1;
S1.Print(*anOutFile);
projector->Add(*anOutFile);
S1.Print(theOStream);
projector->Add(theOStream);
Light = VrmlConverter_DirectionLight;
Camera = VrmlConverter_OrthographicCamera;
Handle(VrmlConverter_Projector) projector1 =
new VrmlConverter_Projector(Shapes, Focus, DX, DY, DZ, XUp, YUp, ZUp, Camera, Light);
projector1->Add(*anOutFile);
projector1->Add(theOStream);
Vrml_Separator S2;
S2.Print(*anOutFile);
S2.Print(theOStream);
if ((myRepresentation == VrmlAPI_ShadedRepresentation
|| myRepresentation == VrmlAPI_BothRepresentation)
&& hasTriangles)
{
Vrml_Group Group1;
Group1.Print(*anOutFile);
Group1.Print(theOStream);
Vrml_Instancing I2("Shaded representation of shape");
I2.DEF(*anOutFile);
VrmlConverter_ShadedShape::Add(*anOutFile, aShape, myDrawer);
Group1.Print(*anOutFile);
I2.DEF(theOStream);
VrmlConverter_ShadedShape::Add(theOStream, aShape, myDrawer);
Group1.Print(theOStream);
}
if (myRepresentation == VrmlAPI_WireFrameRepresentation
|| myRepresentation == VrmlAPI_BothRepresentation)
{
Vrml_Group Group2;
Group2.Print(*anOutFile);
Group2.Print(theOStream);
Vrml_Instancing I3("Wire Frame representation of shape");
I3.DEF(*anOutFile);
VrmlConverter_WFDeflectionShape::Add(*anOutFile, aShape, myDrawer);
Group2.Print(*anOutFile);
I3.DEF(theOStream);
VrmlConverter_WFDeflectionShape::Add(theOStream, aShape, myDrawer);
Group2.Print(theOStream);
}
S2.Print(*anOutFile);
S1.Print(*anOutFile);
S2.Print(theOStream);
S1.Print(theOStream);
anOutFile->flush();
return anOutFile->good();
theOStream.flush();
return theOStream.good();
}
Standard_Boolean VrmlAPI_Writer::write_v2(const TopoDS_Shape& aShape,
const Standard_CString aFile) const
Standard_Boolean VrmlAPI_Writer::write_v2(const TopoDS_Shape& aShape,
Standard_OStream& theOStream) const
{
Standard_Boolean anExtFace = Standard_False;
if (myRepresentation == VrmlAPI_ShadedRepresentation
@@ -402,38 +442,7 @@ Standard_Boolean VrmlAPI_Writer::write_v2(const TopoDS_Shape& aShape,
aConv.AddShape(aShape);
aConv.Convert(anExtFace, anExtEdge);
const Handle(OSD_FileSystem)& aFileSystem = OSD_FileSystem::DefaultFileSystem();
std::shared_ptr<std::ostream> anOutStream =
aFileSystem->OpenOStream(aFile, std::ios::out | std::ios::binary);
if (anOutStream.get() != NULL)
{
*anOutStream << aScene;
anOutStream->flush();
return anOutStream->good();
}
anOutStream.reset();
return Standard_False;
}
//=================================================================================================
Standard_Boolean VrmlAPI_Writer::WriteDoc(const Handle(TDocStd_Document)& theDoc,
const Standard_CString theFile,
const Standard_Real theScale) const
{
VrmlData_Scene aScene;
VrmlData_ShapeConvert aConv(aScene, theScale);
aConv.ConvertDocument(theDoc);
const Handle(OSD_FileSystem)& aFileSystem = OSD_FileSystem::DefaultFileSystem();
std::shared_ptr<std::ostream> anOutStream =
aFileSystem->OpenOStream(theFile, std::ios::out | std::ios::binary);
if (anOutStream.get() != NULL)
{
*anOutStream << aScene;
anOutStream->flush();
return anOutStream->good();
}
anOutStream.reset();
return Standard_False;
theOStream << aScene;
theOStream.flush();
return theOStream.good();
}

View File

@@ -116,16 +116,28 @@ public:
const Standard_CString theFile,
const Standard_Real theScale) const;
//! Converts the shape aShape to
//! VRML format of the passed version and writes it to the given stream.
Standard_EXPORT Standard_Boolean Write(const TopoDS_Shape& aShape,
Standard_OStream& theOStream,
const Standard_Integer aVersion = 2) const;
//! Converts the document to VRML format of the passed version
//! and writes it to the given stream.
Standard_EXPORT Standard_Boolean WriteDoc(const Handle(TDocStd_Document)& theDoc,
Standard_OStream& theOStream,
const Standard_Real theScale) const;
protected:
//! Converts the shape aShape to VRML format of version 1.0 and writes it
//! to the file identified by aFileName using default parameters.
Standard_EXPORT Standard_Boolean write_v1(const TopoDS_Shape& aShape,
const Standard_CString aFileName) const;
//! to the given stream using default parameters.
Standard_EXPORT Standard_Boolean write_v1(const TopoDS_Shape& aShape,
Standard_OStream& theOStream) const;
//! Converts the shape aShape to VRML format of version 2.0 and writes it
//! to the file identified by aFileName using default parameters.
Standard_EXPORT Standard_Boolean write_v2(const TopoDS_Shape& aShape,
const Standard_CString aFileName) const;
//! to the given stream using default parameters.
Standard_EXPORT Standard_Boolean write_v2(const TopoDS_Shape& aShape,
Standard_OStream& theOStream) const;
private:
VrmlAPI_RepresentationOfShape myRepresentation;