mirror of
https://github.com/mcneel/opennurbs.git
synced 2026-02-28 19:16:09 +08:00
6016 lines
180 KiB
C++
6016 lines
180 KiB
C++
//
|
|
// Copyright (c) 1993-2022 Robert McNeel & Associates. All rights reserved.
|
|
// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert
|
|
// McNeel & Associates.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
|
|
// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF
|
|
// MERCHANTABILITY ARE HEREBY DISCLAIMED.
|
|
//
|
|
// For complete openNURBS copyright information see <http://www.opennurbs.org>.
|
|
//
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
#include "opennurbs.h"
|
|
#include "opennurbs_internal_defines.h"
|
|
|
|
#if !defined(ON_COMPILING_OPENNURBS)
|
|
// This check is included in all opennurbs source .c and .cpp files to insure
|
|
// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled.
|
|
// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined
|
|
// and the opennurbs .h files alter what is declared and how it is declared.
|
|
#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs
|
|
#endif
|
|
|
|
const ONX_ErrorCounter ONX_ErrorCounter::operator+ (
|
|
const ONX_ErrorCounter& rhs
|
|
)
|
|
{
|
|
ONX_ErrorCounter sum;
|
|
sum.m_failure_count = m_failure_count + rhs.m_failure_count;
|
|
sum.m_error_count = m_error_count + rhs.m_error_count;
|
|
sum.m_warning_count = m_warning_count + rhs.m_warning_count;
|
|
|
|
|
|
sum.m_state_bit_field = m_state_bit_field | rhs.m_state_bit_field;
|
|
sum.m_opennurbs_library_error_count
|
|
= (m_opennurbs_library_error_count < rhs.m_opennurbs_library_error_count)
|
|
? rhs.m_opennurbs_library_error_count
|
|
: m_opennurbs_library_error_count;
|
|
sum.m_opennurbs_library_warning_count
|
|
= (m_opennurbs_library_warning_count < rhs.m_opennurbs_library_warning_count)
|
|
? rhs.m_opennurbs_library_warning_count
|
|
: m_opennurbs_library_warning_count;
|
|
|
|
return sum;
|
|
}
|
|
|
|
|
|
const ONX_ErrorCounter ONX_ErrorCounter::operator+= (const ONX_ErrorCounter& rhs)
|
|
{
|
|
m_failure_count += rhs.m_failure_count;
|
|
m_error_count += rhs.m_error_count;
|
|
m_warning_count += rhs.m_warning_count;
|
|
|
|
m_state_bit_field |= rhs.m_state_bit_field;
|
|
if (m_opennurbs_library_error_count < rhs.m_opennurbs_library_error_count)
|
|
m_opennurbs_library_error_count = rhs.m_opennurbs_library_error_count;
|
|
if (m_opennurbs_library_warning_count < rhs.m_opennurbs_library_warning_count)
|
|
m_opennurbs_library_warning_count = rhs.m_opennurbs_library_warning_count;
|
|
|
|
return *this;
|
|
}
|
|
|
|
|
|
unsigned int ONX_ErrorCounter::FailureCount() const
|
|
{
|
|
return this->m_failure_count;
|
|
}
|
|
|
|
unsigned int ONX_ErrorCounter::ErrorCount() const
|
|
{
|
|
return this->m_error_count;
|
|
}
|
|
|
|
unsigned int ONX_ErrorCounter::WarningCount() const
|
|
{
|
|
return this->m_warning_count;
|
|
}
|
|
|
|
unsigned int ONX_ErrorCounter::TotalCount() const
|
|
{
|
|
return FailureCount() + ErrorCount() + WarningCount();
|
|
}
|
|
|
|
unsigned int ONX_ErrorCounter::IncrementFailureCount()
|
|
{
|
|
return ++m_failure_count; // <- Good location for a debugger breakpoint.
|
|
}
|
|
|
|
unsigned int ONX_ErrorCounter::IncrementErrorCount()
|
|
{
|
|
return ++m_error_count; // <- Good location for a debugger breakpoint.
|
|
}
|
|
|
|
unsigned int ONX_ErrorCounter::IncrementWarningCount()
|
|
{
|
|
return ++m_warning_count; // <- Good location for a debugger breakpoint.
|
|
}
|
|
|
|
void ONX_ErrorCounter::ClearLibraryErrors()
|
|
{
|
|
m_opennurbs_library_error_count = ON_GetErrorCount();
|
|
m_state_bit_field |= 1;
|
|
}
|
|
|
|
unsigned int ONX_ErrorCounter::AddLibraryErrors()
|
|
{
|
|
const bool bActive = (0 != (1 & m_state_bit_field));
|
|
const unsigned int count0 = m_opennurbs_library_error_count;
|
|
ClearLibraryErrors();
|
|
const unsigned int count
|
|
= bActive
|
|
? (m_opennurbs_library_error_count - count0)
|
|
: 0U;
|
|
if (bActive && count > 0)
|
|
m_error_count += count; // <- Good location for a debugger breakpoint.
|
|
return count;
|
|
}
|
|
|
|
void ONX_ErrorCounter::ClearLibraryWarnings()
|
|
{
|
|
m_opennurbs_library_warning_count = ON_GetWarningCount();
|
|
m_state_bit_field |= 2;
|
|
}
|
|
|
|
unsigned int ONX_ErrorCounter::AddLibraryWarnings()
|
|
{
|
|
const bool bActive = (0 != (2 & m_state_bit_field));
|
|
const unsigned int count0 = m_opennurbs_library_warning_count;
|
|
ClearLibraryWarnings();
|
|
const unsigned int count
|
|
= bActive
|
|
? (m_opennurbs_library_warning_count - count0)
|
|
: 0U;
|
|
if (bActive && count>0)
|
|
m_warning_count += count; // <- Good location for a debugger breakpoint.
|
|
return count;
|
|
}
|
|
|
|
void ONX_ErrorCounter::ClearLibraryErrorsAndWarnings()
|
|
{
|
|
ClearLibraryErrors();
|
|
ClearLibraryWarnings();
|
|
}
|
|
|
|
unsigned int ONX_ErrorCounter::AddLibraryErrorsAndWarnings()
|
|
{
|
|
return AddLibraryErrors() + AddLibraryWarnings();
|
|
}
|
|
|
|
void ONX_ErrorCounter::Dump(ON_TextLog& text_log) const
|
|
{
|
|
text_log.Print(
|
|
"%u failures, %u errors, %u warnings",
|
|
m_failure_count,
|
|
m_error_count,
|
|
m_warning_count
|
|
);
|
|
}
|
|
|
|
#if defined(OPENNURBS_EXPORTS)
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// When openNURBS is used as a Microsoft Windows DLL, it is possible
|
|
// for new/delete to allocate memory in one executable and delete
|
|
// it in another. Because Microsoft Windows has incompatible memory
|
|
// managers in its plethora of C libraries and the choice of which
|
|
// C library actually gets used depends on the code generation
|
|
// options you choose, we get lots of support questions asking
|
|
// about hard to trace crashes.
|
|
//
|
|
// If you are using openNURBS as a Windows DLL, you are sure you know
|
|
// what you are doing, and you promise never to ask for support, then
|
|
// feel free to delete these overrides.
|
|
//
|
|
//
|
|
#if defined(ON_COMPILER_MSC)
|
|
#pragma message( " --- OpenNURBS overriding ONX_Model new and delete" )
|
|
#endif
|
|
|
|
// ONX_Model_UserData new/delete
|
|
|
|
void* ONX_Model_UserData::operator new(size_t sz)
|
|
{
|
|
// ONX_Model_UserData new
|
|
return onmalloc(sz);
|
|
}
|
|
|
|
void ONX_Model_UserData::operator delete(void* p)
|
|
{
|
|
// ONX_Model_UserData delete
|
|
onfree(p);
|
|
}
|
|
|
|
void* ONX_Model_UserData::operator new[] (size_t sz)
|
|
{
|
|
// ONX_Model_UserData array new
|
|
return onmalloc(sz);
|
|
}
|
|
|
|
void ONX_Model_UserData::operator delete[] (void* p)
|
|
{
|
|
// ONX_Model_UserData array delete
|
|
onfree(p);
|
|
}
|
|
|
|
void* ONX_Model_UserData::operator new(size_t, void* p)
|
|
{
|
|
// ONX_Model_UserData placement new
|
|
return p;
|
|
}
|
|
|
|
void ONX_Model_UserData::operator delete(void*, void*)
|
|
{
|
|
// ONX_Model_UserData placement delete
|
|
return;
|
|
}
|
|
|
|
// ONX_Model new/delete
|
|
|
|
void* ONX_Model::operator new(size_t sz)
|
|
{
|
|
// ONX_Model new
|
|
return onmalloc(sz);
|
|
}
|
|
|
|
void ONX_Model::operator delete(void* p)
|
|
{
|
|
// ONX_Model delete
|
|
onfree(p);
|
|
}
|
|
|
|
void* ONX_Model::operator new[] (size_t sz)
|
|
{
|
|
// ONX_Model array new
|
|
return onmalloc(sz);
|
|
}
|
|
|
|
void ONX_Model::operator delete[] (void* p)
|
|
{
|
|
// ONX_Model array delete
|
|
onfree(p);
|
|
}
|
|
|
|
void* ONX_Model::operator new(size_t, void* p)
|
|
{
|
|
// ONX_Model placement new
|
|
return p;
|
|
}
|
|
|
|
void ONX_Model::operator delete(void*, void*)
|
|
{
|
|
// ONX_Model placement delete
|
|
return;
|
|
}
|
|
|
|
#endif
|
|
|
|
//
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
class ONX_ModelComponentReferenceLink
|
|
{
|
|
public:
|
|
ONX_ModelComponentReferenceLink() = default;
|
|
~ONX_ModelComponentReferenceLink() = default;
|
|
ONX_ModelComponentReferenceLink(const ONX_ModelComponentReferenceLink&) = default;
|
|
ONX_ModelComponentReferenceLink& operator=(const ONX_ModelComponentReferenceLink&) = default;
|
|
|
|
public:
|
|
// m_mcr in this class is the "first" std::shared_ptr<ON_ModelComponent> that manages
|
|
// the referenced ON_ModelComponent and is what insures ON_ModelComponent alive inside
|
|
// and ONX_Model for the duration of the model's lifetime.
|
|
//
|
|
// m_sn = ON_ModelComponent.RuntimeSerialNumber() so sn lookup can be safely used to find runtime pointer.
|
|
// When m_mcr.ModelComponent() is not nullptr, then m_sn = m_mcr.ModelComponent()->RuntimeSerialNumber().
|
|
ON_ModelComponentReference m_mcr;
|
|
ON__UINT64 m_sn = 0;
|
|
ONX_ModelComponentReferenceLink* m_next = nullptr;
|
|
ONX_ModelComponentReferenceLink* m_prev = nullptr;
|
|
};
|
|
|
|
enum class RenderContentKinds { Material, Environment, Texture };
|
|
|
|
ON_3dmObjectAttributes* GetComponentAttributes(const ON_ModelComponent& component)
|
|
{
|
|
// To have attributes, the component must be an ON_ModelGeometryComponent.
|
|
const auto* mgc = ON_ModelGeometryComponent::Cast(&component);
|
|
if (nullptr == mgc)
|
|
return nullptr; // Wrong type of component.
|
|
|
|
return mgc->ExclusiveAttributes();
|
|
}
|
|
|
|
class ONX_ModelPrivate final
|
|
{
|
|
public:
|
|
ONX_ModelPrivate(ONX_Model& m);
|
|
~ONX_ModelPrivate();
|
|
|
|
using EmbeddedFileMap = std::unordered_map<std::wstring, std::wstring>;
|
|
|
|
bool GetRDKDocumentXML(ON_wString& xml, bool embedded_files, int archive_3dm_version) const;
|
|
ONX_Model_UserData* GetRDKDocumentUserData(int archive_3dm_version) const;
|
|
void PopulateDefaultRDKDocumentXML(ON_XMLRootNode& root) const;
|
|
bool PopulateRDKComponents(int archive_3dm_version);
|
|
bool UpdateRDKUserData(int archive_3dm_version);
|
|
bool CreateRenderContentFromXML(class ON_XMLNode& model_node, RenderContentKinds kind);
|
|
bool CreateXMLFromRenderContent(ON_XMLNode& model_node, RenderContentKinds kind) const;
|
|
bool SetRDKDocumentInformation(const wchar_t* xml, ONX_Model_UserData& docud, int archive_3dm_version) const;
|
|
ON_XMLNode* GetRenderContentSectionNode(ON_XMLNode& model_node, RenderContentKinds kind) const;
|
|
ON_XMLNode* GetPostEffectSectionNode(ON_XMLNode& model_node, ON_PostEffect::Types type) const;
|
|
static void RemoveAllEmbeddedFiles(ONX_Model& model);
|
|
static bool GetEntireRDKDocument(const ONX_Model_UserData& docud, ON_wString& xml, ONX_Model* model);
|
|
|
|
public:
|
|
ONX_Model& m_model;
|
|
ON__UINT64 m_model_content_version_number = 0;
|
|
ON_ClassArray<ONX_Model::ONX_ModelComponentList> m_mcr_lists;
|
|
};
|
|
|
|
ON_InternalXMLImpl::~ON_InternalXMLImpl()
|
|
{
|
|
if (nullptr != _local_node)
|
|
{
|
|
delete _local_node;
|
|
_local_node = nullptr;
|
|
}
|
|
}
|
|
|
|
ON_XMLNode& ON_InternalXMLImpl::Node(void) const
|
|
{
|
|
std::lock_guard<std::recursive_mutex> lg(_mutex);
|
|
|
|
// If the model node pointer is set, return that. This is a pointer to a node owned by the ON_3dmRenderSettings
|
|
// which contains the entire RDK document XML. This is used by objects (Ground Plane, etc.) that are owned by the
|
|
// ON_3dmRenderSettings. In the case of Ground Plane etc, it's a pointer into the ON_3dmRenderSettings XML.
|
|
// In the case of decals, it's a pointer into the decal collection's XML.
|
|
if (nullptr != _model_node)
|
|
return *_model_node;
|
|
|
|
// Since the model node is not set, we need a local node to hold the XML. If one has not been created yet,
|
|
// create it. The local node is owned by this object. This case occurs for free-floating copies of model
|
|
// objects and also for free-floating copies of decals and mesh modifiers. This node only contains the XML
|
|
// data that's relevant to the object it's for, not the entire XML, but the object's node is still a child
|
|
// node under the same node hierarchy as if it were the entire XML.
|
|
if (nullptr == _local_node)
|
|
_local_node = new ON_XMLNode(NameOfRootNode());
|
|
|
|
return *_local_node;
|
|
}
|
|
|
|
void ON_InternalXMLImpl::SetModelNode(ON_XMLNode& node)
|
|
{
|
|
ON_ASSERT(_model_node == nullptr);
|
|
|
|
std::lock_guard<std::recursive_mutex> lg(_mutex);
|
|
|
|
if (nullptr != _local_node)
|
|
{
|
|
delete _local_node;
|
|
_local_node = nullptr;
|
|
}
|
|
|
|
_model_node = &node;
|
|
}
|
|
|
|
ON_wString ON_InternalXMLImpl::NameOfRootNode(void) const
|
|
{
|
|
return ON_XMLRootNode().TagName();
|
|
}
|
|
|
|
ON_XMLVariant ON_InternalXMLImpl::GetParameter(const wchar_t* path_to_node, const wchar_t* param_name, const ON_XMLVariant& def) const
|
|
{
|
|
return InternalGetParameter(path_to_node, param_name, L"", def);
|
|
}
|
|
|
|
ON_XMLVariant ON_InternalXMLImpl::GetParameter_NoType(const wchar_t* path_to_node, const wchar_t* param_name, const wchar_t* default_type, const ON_XMLVariant& def) const
|
|
{
|
|
return InternalGetParameter(path_to_node, param_name, default_type, def);
|
|
}
|
|
|
|
ON_XMLVariant ON_InternalXMLImpl::InternalGetParameter(const wchar_t* path_to_node, const wchar_t* param_name, const wchar_t* default_type, const ON_XMLVariant& def) const
|
|
{
|
|
std::lock_guard<std::recursive_mutex> lg(_mutex);
|
|
|
|
const ON_XMLNode* node_read = Node().GetNodeAtPath(path_to_node);
|
|
if (nullptr == node_read)
|
|
return def;
|
|
|
|
ON_XMLVariant value;
|
|
ON_XMLParameters p(*node_read);
|
|
p.SetDefaultReadType(default_type);
|
|
if (!p.GetParam(param_name, value))
|
|
return def;
|
|
|
|
return value;
|
|
}
|
|
|
|
bool ON_InternalXMLImpl::SetParameter(const wchar_t* path_to_node, const wchar_t* param_name, const ON_XMLVariant& value)
|
|
{
|
|
return InternalSetParameter(path_to_node, param_name, true, value);
|
|
}
|
|
|
|
bool ON_InternalXMLImpl::SetParameter_NoType(const wchar_t* path_to_node, const wchar_t* param_name, const ON_XMLVariant& value)
|
|
{
|
|
return InternalSetParameter(path_to_node, param_name, false, value);
|
|
}
|
|
|
|
bool ON_InternalXMLImpl::InternalSetParameter(const wchar_t* path_to_node, const wchar_t* param_name, bool write_type, const ON_XMLVariant& value)
|
|
{
|
|
std::lock_guard<std::recursive_mutex> lg(_mutex);
|
|
|
|
bool success = false;
|
|
|
|
ON_XMLNode* node_write = Node().CreateNodeAtPath(path_to_node);
|
|
if (nullptr != node_write)
|
|
{
|
|
ON_XMLParameters p(*node_write);
|
|
p.SetWriteTypeProperty(write_type);
|
|
if (nullptr != p.SetParam(param_name, value))
|
|
success = true;
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
bool ON_InternalXMLImpl::RemoveParameter(const wchar_t* path_to_node, const wchar_t* param_name)
|
|
{
|
|
std::lock_guard<std::recursive_mutex> lg(_mutex);
|
|
|
|
bool success = false;
|
|
|
|
ON_XMLNode* node = Node().GetNodeAtPath(path_to_node);
|
|
if (nullptr != node)
|
|
{
|
|
ON_XMLNode* child = node->GetNamedChild(param_name);
|
|
success = node->RemoveChild(child);
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
// ONX_Model
|
|
|
|
ONX_Model::ONX_Model()
|
|
{
|
|
m_private = new ONX_ModelPrivate(*this);
|
|
|
|
// Tell the sun in our render settings to use the earth anchor point in our settings.
|
|
m_settings.m_RenderSettings.Sun().UseEarthAnchorPoint(m_settings.m_earth_anchor_point);
|
|
}
|
|
|
|
ONX_Model::~ONX_Model()
|
|
{
|
|
Reset();
|
|
|
|
delete m_private;
|
|
m_private = nullptr;
|
|
}
|
|
|
|
void ONX_Model::Reset()
|
|
{
|
|
m_3dm_file_version = 0;
|
|
m_3dm_opennurbs_version = 0;
|
|
m_sStartSectionComments = ON_String::EmptyString;
|
|
m_properties = ON_3dmProperties::Empty;
|
|
m_settings = ON_3dmSettings::Default;
|
|
|
|
for (unsigned int i = 0; i < m_userdata_table.UnsignedCount(); i++)
|
|
{
|
|
delete m_userdata_table[i];
|
|
}
|
|
m_userdata_table.Destroy();
|
|
|
|
for (int i = 0; i < m_private->m_mcr_lists.Count(); i++)
|
|
{
|
|
ONX_ModelComponentList& list = m_private->m_mcr_lists[i];
|
|
|
|
ONX_ModelComponentReferenceLink* mcr_link = list.m_first_mcr_link;
|
|
while (nullptr != mcr_link)
|
|
{
|
|
mcr_link->m_mcr = ON_ModelComponentReference::Empty;
|
|
mcr_link = mcr_link->m_next;
|
|
}
|
|
|
|
list.m_first_mcr_link = nullptr;
|
|
list.m_last_mcr_link = nullptr;
|
|
}
|
|
m_mcr_sn_map.EmptyList();
|
|
m_mcr_link_fsp.ReturnAll();
|
|
|
|
m_default_line_pattern = ON_ModelComponentReference::CreateConstantSystemComponentReference(ON_Linetype::Continuous);
|
|
m_default_layer = ON_ModelComponentReference::CreateConstantSystemComponentReference(ON_Layer::Default);
|
|
m_default_text_style = ON_ModelComponentReference::CreateConstantSystemComponentReference(ON_TextStyle::Default);
|
|
m_default_dimension_style = ON_ModelComponentReference::CreateConstantSystemComponentReference(ON_DimStyle::Default);
|
|
|
|
m_manifest.Reset();
|
|
|
|
m_original_to_manifest_map = ON_ManifestMap::Empty;
|
|
m_manifest_to_original_map = ON_ManifestMap::Empty;
|
|
|
|
m_model_geometry_bbox = ON_BoundingBox::UnsetBoundingBox;
|
|
m_render_light_bbox = ON_BoundingBox::UnsetBoundingBox;
|
|
|
|
if (nullptr != m_model_user_string_list)
|
|
{
|
|
delete m_model_user_string_list;
|
|
m_model_user_string_list = nullptr;
|
|
}
|
|
}
|
|
|
|
void ONX_Model::Internal_ComponentTypeBoundingBox(
|
|
const ON_ModelComponent::Type component_type,
|
|
ON_BoundingBox& bbox
|
|
) const
|
|
{
|
|
if (false == bbox.IsValid())
|
|
{
|
|
ON_BoundingBox local_bbox;
|
|
for (
|
|
const ONX_ModelComponentReferenceLink* link = Internal_ComponentListConst(component_type).m_first_mcr_link;
|
|
nullptr != link;
|
|
link = link->m_next
|
|
)
|
|
{
|
|
const ON_ModelComponent* model_component = link->m_mcr.ModelComponent();
|
|
if (nullptr == model_component)
|
|
continue;
|
|
if (component_type != model_component->ComponentType())
|
|
continue;
|
|
const ON_ModelGeometryComponent* model_geometry = ON_ModelGeometryComponent::Cast(model_component);
|
|
if (nullptr == model_geometry)
|
|
continue;
|
|
const ON_3dmObjectAttributes* attributes = model_geometry->Attributes(nullptr);
|
|
if (nullptr != attributes && attributes->IsInstanceDefinitionObject())
|
|
continue;
|
|
|
|
const ON_Geometry* geometry = model_geometry->Geometry(nullptr);
|
|
if (nullptr == geometry)
|
|
continue;
|
|
|
|
local_bbox.Union(geometry->BoundingBox());
|
|
}
|
|
bbox = local_bbox;
|
|
}
|
|
return;
|
|
}
|
|
|
|
ON_BoundingBox ONX_Model::ModelGeometryBoundingBox() const
|
|
{
|
|
Internal_ComponentTypeBoundingBox(ON_ModelComponent::Type::ModelGeometry,m_model_geometry_bbox);
|
|
return m_model_geometry_bbox;
|
|
}
|
|
|
|
ON_BoundingBox ONX_Model::RenderLightBoundingBox() const
|
|
{
|
|
Internal_ComponentTypeBoundingBox(ON_ModelComponent::Type::RenderLight,m_render_light_bbox);
|
|
return m_render_light_bbox;
|
|
}
|
|
|
|
const ON_ComponentManifest& ONX_Model::Manifest() const
|
|
{
|
|
return m_manifest;
|
|
}
|
|
|
|
const ON_ManifestMap& ONX_Model::OriginalToModelMap() const
|
|
{
|
|
return m_original_to_manifest_map;
|
|
}
|
|
|
|
const ON_ManifestMap& ONX_Model::ModelToOriginalMap() const
|
|
{
|
|
return m_manifest_to_original_map;
|
|
}
|
|
|
|
bool ONX_Model::ValdateComponentIdAndName(
|
|
ON_ModelComponent::Type component_type,
|
|
const ON_UUID& candidate_id,
|
|
const ON_UUID& component_parent_id,
|
|
const wchar_t* candidate_name,
|
|
bool bResolveIdConflict,
|
|
bool bResolveNameConflict,
|
|
ON_UUID& model_id,
|
|
ON_wString& model_name
|
|
) const
|
|
{
|
|
for (;;)
|
|
{
|
|
if (false == ON_ModelComponent::ComponentTypeIsValid(component_type))
|
|
{
|
|
ON_ERROR("Invalid component_type parameter.");
|
|
break;
|
|
}
|
|
|
|
const bool bIndexRequired = ON_ModelComponent::IndexRequired(component_type);
|
|
const unsigned int count = m_manifest.ComponentIndexLimit(component_type);
|
|
if (bIndexRequired && count >= 0x7FFFFFFFU)
|
|
{
|
|
ON_ERROR("Unable to create model component index.");
|
|
break;
|
|
}
|
|
|
|
const bool bIdAvailable = m_manifest.IdIsAvailable(candidate_id);
|
|
const bool bCreateId = ON_UuidIsNil(candidate_id) || (false == bIdAvailable && bResolveIdConflict);
|
|
if (false == bIdAvailable && false == bCreateId)
|
|
{
|
|
break;
|
|
}
|
|
|
|
ON_wString name(candidate_name);
|
|
name.TrimLeftAndRight();
|
|
const bool bUniqueNameReqired = ON_ModelComponent::UniqueNameRequired(component_type);
|
|
if ( bUniqueNameReqired )
|
|
{
|
|
const ON_UUID name_parent_id
|
|
= ON_ModelComponent::UniqueNameIncludesParent(component_type)
|
|
? component_parent_id
|
|
: ON_nil_uuid;
|
|
ON_NameHash name_hash = ON_NameHash::Create(name_parent_id, name);
|
|
if (name_hash.IsInvalidNameHash())
|
|
{
|
|
if (false == bResolveNameConflict)
|
|
{
|
|
ON_ERROR("Invalid candidate_name parameter.");
|
|
break;
|
|
}
|
|
name = ON_wString::EmptyString;
|
|
name_hash = ON_NameHash::Create(name_parent_id, name);
|
|
}
|
|
|
|
const bool bNameIsValid = name.IsNotEmpty() && m_manifest.NameIsAvailable(component_type, name_hash);
|
|
if (false == bNameIsValid )
|
|
{
|
|
// we need to create a unique and non-empty name
|
|
if (false == bResolveNameConflict)
|
|
{
|
|
// not allowed to modify name
|
|
break;
|
|
}
|
|
|
|
name = m_manifest.UnusedName(component_type, component_parent_id, nullptr, name, nullptr, ON_UNSET_UINT_INDEX, nullptr);
|
|
if (name.IsEmpty())
|
|
{
|
|
ON_ERROR("Unable to create component name.");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
model_id = bCreateId ? ON_CreateId() : candidate_id;
|
|
model_name = name;
|
|
return true;
|
|
}
|
|
|
|
model_id = ON_nil_uuid;
|
|
model_name = ON_wString::EmptyString;
|
|
return false;
|
|
}
|
|
|
|
unsigned int ONX_Model::ComponentIndexLimit(
|
|
ON_ModelComponent::Type component_type
|
|
) const
|
|
{
|
|
return m_manifest.ComponentIndexLimit(component_type);
|
|
}
|
|
|
|
unsigned int ONX_Model::ActiveAndDeletedComponentCount(
|
|
ON_ModelComponent::Type component_type
|
|
) const
|
|
{
|
|
return m_manifest.ActiveAndDeletedComponentCount(component_type);
|
|
}
|
|
|
|
unsigned int ONX_Model::ActiveComponentCount(
|
|
ON_ModelComponent::Type component_type
|
|
) const
|
|
{
|
|
return m_manifest.ActiveComponentCount(component_type);
|
|
}
|
|
|
|
unsigned int ONX_Model::DeletedComponentCount(
|
|
ON_ModelComponent::Type component_type
|
|
) const
|
|
{
|
|
return m_manifest.DeletedComponentCount(component_type);
|
|
}
|
|
|
|
ON_ModelComponentReference ONX_Model::ComponentFromIndex(
|
|
ON_ModelComponent::Type component_type,
|
|
int component_model_index
|
|
) const
|
|
{
|
|
if (component_model_index >= 0)
|
|
{
|
|
return ComponentFromUnsignedIndex(component_type,(unsigned int)component_model_index);
|
|
}
|
|
return ON_ModelComponentReference::Empty;
|
|
}
|
|
|
|
ON_ModelComponentReference ONX_Model::ComponentFromUnsignedIndex(
|
|
ON_ModelComponent::Type component_type,
|
|
unsigned int component_model_index
|
|
) const
|
|
{
|
|
ON__UINT64 sn = m_manifest.ItemFromIndex(component_type,component_model_index).ComponentRuntimeSerialNumber();
|
|
ONX_ModelComponentReferenceLink* link = Internal_ModelComponentLinkFromSerialNumber(sn);
|
|
return (nullptr != link)
|
|
? link->m_mcr
|
|
: ON_ModelComponentReference::Empty;
|
|
}
|
|
|
|
ON_ModelComponentReference ONX_Model::ComponentFromId(
|
|
ON_ModelComponent::Type component_type,
|
|
ON_UUID component_model_id
|
|
) const
|
|
{
|
|
ON__UINT64 sn = m_manifest.ItemFromId(component_type, component_model_id).ComponentRuntimeSerialNumber();
|
|
ONX_ModelComponentReferenceLink* link = Internal_ModelComponentLinkFromSerialNumber(sn);
|
|
return (nullptr != link)
|
|
? link->m_mcr
|
|
: ON_ModelComponentReference::Empty;
|
|
}
|
|
|
|
const ON_ModelComponentReference& ONX_Model::ComponentFromRuntimeSerialNumber(
|
|
ON__UINT64 runtime_serial_number
|
|
) const
|
|
{
|
|
ONX_ModelComponentReferenceLink* link = Internal_ModelComponentLinkFromSerialNumber(runtime_serial_number);
|
|
return (nullptr != link)
|
|
? link->m_mcr
|
|
: ON_ModelComponentReference::Empty;
|
|
}
|
|
|
|
ON_ModelComponentReference ONX_Model::ModelGeometryFromId(
|
|
ON_UUID model_object_id
|
|
) const
|
|
{
|
|
return ComponentFromId(ON_ModelComponent::Type::ModelGeometry, model_object_id);
|
|
}
|
|
|
|
const ON_ModelGeometryComponent& ONX_Model::ModelGeometryComponentFromId(
|
|
ON_UUID model_object_id
|
|
) const
|
|
{
|
|
const ON_ModelGeometryComponent* p = ON_ModelGeometryComponent::Cast(ModelGeometryFromId(model_object_id).ModelComponent());
|
|
return (nullptr != p) ? *p : ON_ModelGeometryComponent::Unset;
|
|
}
|
|
|
|
ON_ModelComponentReference ONX_Model::ComponentFromName(
|
|
ON_ModelComponent::Type component_type,
|
|
ON_UUID component_parent_id,
|
|
const wchar_t* component_model_name
|
|
) const
|
|
{
|
|
const ON_UUID name_parent_id
|
|
= ON_ModelComponent::UniqueNameIncludesParent(component_type)
|
|
? component_parent_id
|
|
: ON_nil_uuid;
|
|
const bool bIgnoreCase = ON_ModelComponent::UniqueNameIgnoresCase(component_type);
|
|
const ON_NameHash component_model_name_hash = ON_NameHash::Create(name_parent_id,component_model_name,bIgnoreCase);
|
|
return ComponentFromNameHash(component_type,component_model_name_hash);
|
|
}
|
|
|
|
ON_ModelComponentReference ONX_Model::ComponentFromNameHash(
|
|
ON_ModelComponent::Type component_type,
|
|
const ON_NameHash& component_model_name_hash
|
|
) const
|
|
{
|
|
ON__UINT64 sn = m_manifest.ItemFromNameHash(component_type, component_model_name_hash).ComponentRuntimeSerialNumber();
|
|
ONX_ModelComponentReferenceLink* link = Internal_ModelComponentLinkFromSerialNumber(sn);
|
|
return (nullptr != link)
|
|
? link->m_mcr
|
|
: ON_ModelComponentReference::Empty;
|
|
}
|
|
|
|
|
|
ON_ModelComponentReference ONX_Model::ImageFromIndex(
|
|
int image_model_index
|
|
) const
|
|
{
|
|
ON_ModelComponentReference cr = ComponentFromIndex(ON_ModelComponent::Type::Image, image_model_index);
|
|
return cr;
|
|
}
|
|
|
|
ON_ModelComponentReference ONX_Model::ImageFromId(
|
|
ON_UUID image_id
|
|
) const
|
|
{
|
|
ON_ModelComponentReference cr = ComponentFromId(ON_ModelComponent::Type::Image, image_id);
|
|
return cr;
|
|
}
|
|
|
|
ON_ModelComponentReference ONX_Model::ImageFromFileFullPath(
|
|
const wchar_t* image_file_full_path_name
|
|
) const
|
|
{
|
|
ON_FileReference file_reference;
|
|
file_reference.SetFullPath(image_file_full_path_name,false);
|
|
return ImageFromFileReference(file_reference);
|
|
}
|
|
|
|
ON_ModelComponentReference ONX_Model::ImageFromFileContent(
|
|
const ON_ContentHash& image_file_content_hash
|
|
) const
|
|
{
|
|
ON_FileReference file_reference;
|
|
file_reference.SetContentHash(image_file_content_hash);
|
|
return ImageFromFileReference(file_reference);
|
|
}
|
|
|
|
ON_ModelComponentReference ONX_Model::ImageFromFileReference(
|
|
const ON_FileReference& file_reference
|
|
) const
|
|
{
|
|
const ON_wString full_path_name(file_reference.FullPath());
|
|
const bool bCheckFullPath = full_path_name.IsNotEmpty();
|
|
|
|
const ON_ContentHash file_content_hash = file_reference.ContentHash();
|
|
const bool bCheckContentHash = file_content_hash.IsSet();
|
|
|
|
if (false == bCheckFullPath && false == bCheckContentHash)
|
|
return ON_ModelComponentReference::Empty;
|
|
|
|
ONX_ModelComponentIterator it(*this,ON_ModelComponent::Type::Image);
|
|
|
|
for ( ON_ModelComponentReference cr = it.FirstComponentReference(); false == cr.IsEmpty(); cr = it.NextComponentReference())
|
|
{
|
|
const ON_Bitmap* image = ON_Bitmap::Cast(cr.ModelComponent());
|
|
if (nullptr == image)
|
|
continue;
|
|
const ON_FileReference& image_file_reference = image->FileReference();
|
|
|
|
if (bCheckFullPath)
|
|
{
|
|
if (false == full_path_name.EqualPath(image_file_reference.FullPath()))
|
|
continue;
|
|
}
|
|
|
|
if (bCheckContentHash)
|
|
{
|
|
if (0 != ON_ContentHash::CompareContent(file_content_hash, image_file_reference.ContentHash()))
|
|
continue;
|
|
}
|
|
|
|
return cr;
|
|
}
|
|
|
|
return ON_ModelComponentReference::Empty;
|
|
}
|
|
|
|
|
|
ON_ModelComponentReference ONX_Model::LinePatternFromIndex(
|
|
int layer_model_index
|
|
) const
|
|
{
|
|
ON_ModelComponentReference cr = ComponentFromIndex(ON_ModelComponent::Type::LinePattern,layer_model_index);
|
|
return cr.IsEmpty() ? m_default_layer : cr;
|
|
}
|
|
|
|
ON_ModelComponentReference ONX_Model::LinePatternFromId(
|
|
ON_UUID layer_model_id
|
|
) const
|
|
{
|
|
ON_ModelComponentReference cr = ComponentFromId(ON_ModelComponent::Type::LinePattern,layer_model_id);
|
|
return cr.IsEmpty() ? m_default_layer : cr;
|
|
}
|
|
|
|
ON_ModelComponentReference ONX_Model::LinePatternFromName(
|
|
const wchar_t* line_pattern_name
|
|
) const
|
|
{
|
|
ON_ModelComponentReference cr = ComponentFromName(ON_ModelComponent::Type::LinePattern,ON_nil_uuid,line_pattern_name);
|
|
return cr.IsEmpty() ? m_default_layer : cr;
|
|
}
|
|
|
|
ON_ModelComponentReference ONX_Model::LinePatternFromNameHash(
|
|
ON_NameHash line_pattern_model_name_hash
|
|
) const
|
|
{
|
|
ON_ModelComponentReference cr = ComponentFromNameHash(ON_ModelComponent::Type::LinePattern,line_pattern_model_name_hash);
|
|
return cr.IsEmpty() ? m_default_layer : cr;
|
|
}
|
|
|
|
ON_ModelComponentReference ONX_Model::LinePatternFromAttributes(
|
|
const ON_3dmObjectAttributes& attributes
|
|
) const
|
|
{
|
|
int line_pattern_index = ON_Linetype::Continuous.Index();
|
|
|
|
switch ( attributes.LinetypeSource() )
|
|
{
|
|
case ON::linetype_from_layer:
|
|
if (attributes.m_layer_index >= 0)
|
|
{
|
|
const ON_Layer* layer = ON_Layer::Cast(LayerFromIndex(attributes.m_layer_index).ModelComponent());
|
|
if ( nullptr != layer )
|
|
line_pattern_index = layer->LinetypeIndex();
|
|
}
|
|
break;
|
|
case ON::linetype_from_object:
|
|
line_pattern_index = attributes.m_linetype_index;
|
|
break;
|
|
case ON::linetype_from_parent:
|
|
line_pattern_index = attributes.m_linetype_index;
|
|
// TODO: if object is an instance definition, get linetype
|
|
// from instance references.
|
|
break;
|
|
}
|
|
|
|
return LinePatternFromIndex(line_pattern_index);
|
|
}
|
|
|
|
ON_ModelComponentReference ONX_Model::LinePatternFromLayerIndex(
|
|
int layer_index
|
|
) const
|
|
{
|
|
ON_ModelComponentReference layer_component = LayerFromIndex(layer_index);
|
|
const ON_Layer* layer = ON_Layer::Cast(layer_component.ModelComponent());
|
|
if (nullptr == layer)
|
|
layer = &ON_Layer::Default;
|
|
return LinePatternFromIndex(layer->m_linetype_index);
|
|
}
|
|
|
|
ON_ModelComponentReference ONX_Model::LayerFromIndex(
|
|
int layer_model_index
|
|
) const
|
|
{
|
|
ON_ModelComponentReference cr = ComponentFromIndex(ON_ModelComponent::Type::Layer,layer_model_index);
|
|
return cr.IsEmpty() ? m_default_layer : cr;
|
|
}
|
|
|
|
ON_ModelComponentReference ONX_Model::LayerFromId(
|
|
ON_UUID layer_model_id
|
|
) const
|
|
{
|
|
ON_ModelComponentReference cr = ComponentFromId(ON_ModelComponent::Type::Layer,layer_model_id);
|
|
return cr.IsEmpty() ? m_default_layer : cr;
|
|
}
|
|
|
|
ON_ModelComponentReference ONX_Model::LayerFromName(
|
|
ON_UUID layer_parent_id,
|
|
const wchar_t* layer_model_name
|
|
) const
|
|
{
|
|
ON_ModelComponentReference cr = ComponentFromName(ON_ModelComponent::Type::Layer,layer_parent_id,layer_model_name);
|
|
return cr.IsEmpty() ? m_default_layer : cr;
|
|
}
|
|
|
|
ON_ModelComponentReference ONX_Model::LayerFromNameHash(
|
|
const ON_NameHash& layer_model_name_hash
|
|
) const
|
|
{
|
|
ON_ModelComponentReference cr = ComponentFromNameHash(ON_ModelComponent::Type::Layer,layer_model_name_hash);
|
|
return cr.IsEmpty() ? m_default_layer : cr;
|
|
}
|
|
|
|
ON_ModelComponentReference ONX_Model::LayerFromAttributes(
|
|
const ON_3dmObjectAttributes& attributes
|
|
) const
|
|
{
|
|
return LayerFromIndex(attributes.m_layer_index);
|
|
}
|
|
|
|
ON_ModelComponentReference ONX_Model::DimensionStyleFromIndex(
|
|
int dimension_style_model_index
|
|
) const
|
|
{
|
|
ON_ModelComponentReference cr = ComponentFromIndex(ON_ModelComponent::Type::DimStyle,dimension_style_model_index);
|
|
return cr.IsEmpty() ? DefaultDimensionStyle() : cr;
|
|
}
|
|
|
|
ON_ModelComponentReference ONX_Model::DimensionStyleFromId(
|
|
ON_UUID dimension_style_model_id
|
|
) const
|
|
{
|
|
ON_ModelComponentReference cr = ComponentFromId(ON_ModelComponent::Type::DimStyle,dimension_style_model_id);
|
|
return cr.IsEmpty() ? DefaultDimensionStyle() : cr;
|
|
}
|
|
|
|
ON_ModelComponentReference ONX_Model::DimensionStyleFromName(
|
|
const wchar_t* dimension_style_model_name
|
|
) const
|
|
{
|
|
ON_ModelComponentReference cr = ComponentFromName(ON_ModelComponent::Type::DimStyle,ON_nil_uuid,dimension_style_model_name);
|
|
return cr.IsEmpty() ? DefaultDimensionStyle() : cr;
|
|
}
|
|
|
|
ON_ModelComponentReference ONX_Model::DimensionStyleFromNameHash(
|
|
ON_NameHash dimension_style_model_name_hash
|
|
) const
|
|
{
|
|
ON_ModelComponentReference cr = ComponentFromNameHash(ON_ModelComponent::Type::DimStyle,dimension_style_model_name_hash);
|
|
return cr.IsEmpty() ? DefaultDimensionStyle() : cr;
|
|
}
|
|
|
|
ON_UUID ONX_Model::CurrentDimensionStyleId() const
|
|
{
|
|
const ON_UUID id = m_settings.CurrentDimensionStyleId();
|
|
if (ON_nil_uuid == id)
|
|
return id;
|
|
if (id == ON_DimStyle::SystemDimstyleFromId(id).Id())
|
|
return id;
|
|
const ON_ModelComponentReference cr = ComponentFromId(ON_ModelComponent::Type::DimStyle,id);
|
|
if (false == cr.IsEmpty())
|
|
return id;
|
|
return ON_nil_uuid;
|
|
}
|
|
|
|
ON_ModelComponentReference ONX_Model::CurrentDimensionStyle() const
|
|
{
|
|
return DimensionStyleFromId(CurrentDimensionStyleId());
|
|
}
|
|
|
|
bool ONX_Model::SetCurrentDimensionStyleId(
|
|
ON_UUID dimension_style_id
|
|
)
|
|
{
|
|
for (;;)
|
|
{
|
|
if (ON_nil_uuid == dimension_style_id)
|
|
break;
|
|
if (dimension_style_id == ON_DimStyle::SystemDimstyleFromId(dimension_style_id).Id())
|
|
break;
|
|
const ON_ModelComponentReference cr = ComponentFromId(ON_ModelComponent::Type::DimStyle,dimension_style_id);
|
|
if ( nullptr != ON_DimStyle::Cast(cr.ModelComponent()) )
|
|
break;
|
|
ON_ERROR("Invalid dimension_style_id parameter.");
|
|
return false;
|
|
}
|
|
m_settings.SetCurrentDimensionStyleId(dimension_style_id);
|
|
return true;
|
|
}
|
|
|
|
ON_ModelComponentReference ONX_Model::DefaultDimensionStyle() const
|
|
{
|
|
return m_default_dimension_style;
|
|
}
|
|
|
|
static bool Internal_DimStyleHasFont(
|
|
const ON_ModelComponentReference& mcr,
|
|
unsigned int managed_font_sn,
|
|
double model_space_text_scale,
|
|
bool bIgnoreSystemDimStyles
|
|
)
|
|
{
|
|
for (;;)
|
|
{
|
|
if (0 == managed_font_sn)
|
|
break;
|
|
const ON_DimStyle* dim_style_component = ON_DimStyle::Cast(mcr.ModelComponent());
|
|
if (nullptr == dim_style_component)
|
|
break;
|
|
if (managed_font_sn != dim_style_component->Font().ManagedFontSerialNumber())
|
|
break; // wrong font
|
|
if (bIgnoreSystemDimStyles && dim_style_component->IsSystemComponent())
|
|
break; // no system components
|
|
if (dim_style_component->ParentIdIsNotNil())
|
|
break; // no overrides
|
|
if (model_space_text_scale > 0.0 && !(model_space_text_scale == dim_style_component->DimScale()))
|
|
break;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
ON_ModelComponentReference ONX_Model::FirstDimensionStyleFromManagedFontSerialNumber(
|
|
unsigned int managed_font_serial_number,
|
|
double model_space_text_scale,
|
|
bool bIgnoreSystemDimStyles
|
|
) const
|
|
{
|
|
if (Internal_DimStyleHasFont(m_default_dimension_style, managed_font_serial_number, model_space_text_scale, bIgnoreSystemDimStyles))
|
|
return m_default_dimension_style;
|
|
|
|
for (
|
|
ONX_ModelComponentReferenceLink* link = Internal_ComponentListConst(ON_ModelComponent::Type::DimStyle).m_first_mcr_link;
|
|
nullptr != link;
|
|
link = link->m_next
|
|
)
|
|
{
|
|
if (Internal_DimStyleHasFont(link->m_mcr, managed_font_serial_number, model_space_text_scale, bIgnoreSystemDimStyles))
|
|
return link->m_mcr;
|
|
}
|
|
|
|
return ON_ModelComponentReference::Empty;
|
|
}
|
|
|
|
|
|
ON_ModelComponentReference ONX_Model::FirstDimensionStyleFromFont(
|
|
const ON_Font* font,
|
|
double model_space_text_scale,
|
|
bool bIgnoreSystemDimStyles
|
|
) const
|
|
{
|
|
const ON_Font* managed_font
|
|
= (nullptr != font)
|
|
? font->ManagedFont()
|
|
: nullptr;
|
|
|
|
const unsigned int managed_font_sn
|
|
= (nullptr != managed_font)
|
|
? managed_font->ManagedFontSerialNumber()
|
|
: 0;
|
|
|
|
if ( 0 == managed_font_sn )
|
|
{
|
|
ON_ERROR("Invalid font parameter");
|
|
return ON_ModelComponentReference::Empty;
|
|
}
|
|
|
|
return FirstDimensionStyleFromManagedFontSerialNumber(managed_font_sn, model_space_text_scale, bIgnoreSystemDimStyles);
|
|
}
|
|
|
|
ON_ModelComponentReference ONX_Model::DimensionStyleWithFontCharacteristics(
|
|
const ON_Font& font_characteristics,
|
|
double model_space_text_scale
|
|
)
|
|
{
|
|
// search for existing dimension style
|
|
const bool bIgnoreSystemComponents = true;
|
|
ON_ModelComponentReference existing_mcr = this->FirstDimensionStyleFromFont(&font_characteristics, model_space_text_scale, bIgnoreSystemComponents);
|
|
const ON_DimStyle* dim_style = ON_DimStyle::Cast(existing_mcr.ModelComponent());
|
|
if (nullptr != dim_style)
|
|
return existing_mcr;
|
|
|
|
// create new dimension style
|
|
const ON_DimStyle* default_dim_style = ON_DimStyle::Cast(this->DimensionStyleFromId(this->m_settings.CurrentDimensionStyleId()).ModelComponent());
|
|
ON_DimStyle* new_dim_style = ON_DimStyle::CreateFromFont(&font_characteristics, model_space_text_scale, default_dim_style, &this->Manifest(), nullptr);
|
|
if (nullptr != new_dim_style)
|
|
{
|
|
const bool bResolveIdAndNameConflicts = true;
|
|
return this->AddManagedModelComponent(new_dim_style, bResolveIdAndNameConflicts);
|
|
}
|
|
|
|
return ON_ModelComponentReference::Empty;
|
|
}
|
|
|
|
|
|
ON_ModelComponentReference ONX_Model::RemoveModelComponent(
|
|
ON_ModelComponent::Type component_type,
|
|
ON_UUID component_id
|
|
)
|
|
{
|
|
const ON_ComponentManifestItem item = m_manifest.ItemFromId(component_id);
|
|
if (item.IsUnset())
|
|
{
|
|
ON_ERROR("Invalid component_id parameter.");
|
|
return ON_ModelComponentReference::Empty;
|
|
}
|
|
if (ON_ModelComponent::Type::Unset != component_type && component_type != item.ComponentType())
|
|
{
|
|
ON_ERROR("Invalid model_component type.");
|
|
return ON_ModelComponentReference::Empty;
|
|
}
|
|
if (!m_manifest.RemoveComponent(item.Id()))
|
|
{
|
|
ON_ERROR("Unable to remove component from manifest.");
|
|
return ON_ModelComponentReference::Empty;
|
|
}
|
|
|
|
ONX_ModelComponentReferenceLink* mcr_link = Internal_ModelComponentLinkFromSerialNumber(item.ComponentRuntimeSerialNumber());
|
|
if (nullptr == mcr_link)
|
|
{
|
|
ON_ERROR("component not in model.");
|
|
return ON_ModelComponentReference::Empty;
|
|
}
|
|
ON_ModelComponentReference mcr = mcr_link->m_mcr;
|
|
Internal_RemoveModelComponentReferenceLink(mcr_link);
|
|
return mcr;
|
|
}
|
|
|
|
|
|
ON_ModelComponentReference ONX_Model::AddManagedModelComponent(
|
|
class ON_ModelComponent* managed_model_component
|
|
)
|
|
{
|
|
return AddManagedModelComponent(managed_model_component,true);
|
|
}
|
|
|
|
int ONX_Model::AddLayer(
|
|
const wchar_t* layer_name,
|
|
ON_Color layer_color
|
|
)
|
|
{
|
|
ON_Layer layer;
|
|
const ON_wString unused_name = m_manifest.UnusedName(layer.ComponentType(), layer.ParentId(), layer_name, nullptr, nullptr, 0, nullptr);
|
|
layer.SetName(unused_name);
|
|
if ( layer_color != ON_Color::UnsetColor)
|
|
layer.SetColor(layer_color);
|
|
const ON_ModelComponentReference mr = AddModelComponent(layer, true);
|
|
const ON_Layer* managed_layer = ON_Layer::FromModelComponentRef(mr, nullptr);
|
|
int layer_index = (nullptr != managed_layer) ? managed_layer->Index() : ON_UNSET_INT_INDEX;
|
|
if ( layer_index < 0 )
|
|
{
|
|
ON_ERROR("failed to add layer.");
|
|
}
|
|
return layer_index;
|
|
}
|
|
|
|
int ONX_Model::AddDefaultLayer(
|
|
const wchar_t* layer_name,
|
|
ON_Color layer_color
|
|
)
|
|
{
|
|
ON_UUID default_layer_id = m_settings.CurrentLayerId();
|
|
int default_layer_index = m_settings.CurrentLayerIndex();
|
|
for ( int pass = 0; pass < 2; pass++ )
|
|
{
|
|
if (0 == pass)
|
|
{
|
|
if (ON_nil_uuid == default_layer_id)
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
if (ON_UNSET_INT_INDEX == default_layer_index)
|
|
continue;
|
|
}
|
|
ON_ModelComponentReference mcr
|
|
= (0 == pass)
|
|
? LayerFromId(default_layer_id)
|
|
: LayerFromIndex(default_layer_index);
|
|
|
|
const ON_Layer* layer = ON_Layer::FromModelComponentRef(mcr, nullptr);
|
|
if (nullptr == layer)
|
|
continue;
|
|
if (false == layer->IsSystemComponent() && layer->Index() >= 0 && layer->ParentIdIsNil() && layer->IsVisible() && false == layer->IsLocked() )
|
|
{
|
|
m_settings.SetCurrentLayerId(layer->Id());
|
|
return layer->Index();
|
|
}
|
|
ON_Layer default_layer(*layer);
|
|
default_layer.ClearId();
|
|
default_layer.ClearIndex();
|
|
default_layer.ClearParentId();
|
|
default_layer.SetVisible(true);
|
|
default_layer.SetLocked(false);
|
|
|
|
if (nullptr == layer_name || 0 == layer_name[0])
|
|
layer_name = layer->NameAsPointer();
|
|
default_layer.SetName(m_manifest.UnusedName(default_layer.ComponentType(), ON_nil_uuid, layer_name, nullptr, nullptr, 0, nullptr) );
|
|
|
|
if (ON_Color::UnsetColor != layer_color)
|
|
default_layer.SetColor(layer_color);
|
|
|
|
const ON_ModelComponentReference mr = AddModelComponent(default_layer, true);
|
|
const ON_Layer* managed_layer = ON_Layer::FromModelComponentRef(mr, nullptr);
|
|
if (nullptr != managed_layer && managed_layer->Index() >= 0 && false == managed_layer->IsSystemComponent())
|
|
{
|
|
m_settings.SetCurrentLayerId(managed_layer->Id());
|
|
return managed_layer->Index();
|
|
}
|
|
}
|
|
|
|
int layer_index = AddLayer(layer_name, layer_color);
|
|
if (layer_index >= 0)
|
|
{
|
|
const ON_ModelComponentReference mr = LayerFromIndex(layer_index);
|
|
const ON_Layer* managed_layer = ON_Layer::FromModelComponentRef(mr, nullptr);
|
|
if (nullptr != managed_layer && managed_layer->Index() >= 0 && false == managed_layer->IsSystemComponent())
|
|
{
|
|
m_settings.SetCurrentLayerId(managed_layer->Id());
|
|
return managed_layer->Index();
|
|
}
|
|
}
|
|
|
|
ON_ERROR("Failed to add default layer.");
|
|
return ON_UNSET_INT_INDEX;
|
|
}
|
|
|
|
int ONX_Model::AddDefaultDimensionStyle(
|
|
const wchar_t* dimension_style_name,
|
|
ON::LengthUnitSystem length_unit_system,
|
|
double model_tolerance
|
|
)
|
|
{
|
|
const ON_DimStyle* source_dimstyle = nullptr;
|
|
|
|
ON_UUID dimstyle_id = m_settings.CurrentDimensionStyleId();
|
|
if (ON_nil_uuid != dimstyle_id)
|
|
{
|
|
const ON_DimStyle* dimstyle = ON_DimStyle::FromModelComponentRef(DimensionStyleFromId(dimstyle_id),nullptr);
|
|
if ( nullptr != dimstyle && dimstyle->ParentIdIsNil() )
|
|
{
|
|
if ( dimstyle->Index() < 0 || dimstyle->IsSystemComponent() )
|
|
source_dimstyle = dimstyle;
|
|
else
|
|
return dimstyle->Index();
|
|
}
|
|
else
|
|
{
|
|
source_dimstyle = &ON_DimStyle::SystemDimstyleFromId(dimstyle_id);
|
|
if (dimstyle_id != source_dimstyle->Id())
|
|
source_dimstyle = nullptr;
|
|
}
|
|
}
|
|
|
|
if (nullptr == source_dimstyle)
|
|
{
|
|
bool bIsMetricLengthUnit = false;
|
|
bool bIsUnitedStatesLengthUnit = false;
|
|
for (int pass = 0; pass < 3; pass++)
|
|
{
|
|
if (1 == pass)
|
|
length_unit_system = m_settings.m_ModelUnitsAndTolerances.m_unit_system.UnitSystem();
|
|
else if (pass > 1)
|
|
length_unit_system = ON_3dmSettings::Default.m_ModelUnitsAndTolerances.m_unit_system.UnitSystem();
|
|
bIsMetricLengthUnit = ON::IsMetricLengthUnit(length_unit_system);
|
|
bIsUnitedStatesLengthUnit = bIsMetricLengthUnit ? false : ON::IsUnitedStatesCustomaryLengthUnit(length_unit_system);
|
|
if (bIsMetricLengthUnit || bIsUnitedStatesLengthUnit)
|
|
break;
|
|
}
|
|
for (int pass = 0; pass < 2; pass++)
|
|
{
|
|
if ( model_tolerance > 0.0 )
|
|
break;
|
|
if (1 == pass)
|
|
model_tolerance = m_settings.m_ModelUnitsAndTolerances.m_absolute_tolerance;
|
|
else if (pass > 1)
|
|
model_tolerance = ON_3dmSettings::Default.m_ModelUnitsAndTolerances.m_absolute_tolerance;
|
|
}
|
|
|
|
const ON_DimStyle* system_dimstyle = nullptr;
|
|
if (bIsMetricLengthUnit)
|
|
{
|
|
double meters_per_unit = ON::UnitScale(length_unit_system, ON::LengthUnitSystem::Meters);
|
|
double tolerance_mm
|
|
= model_tolerance > 0.0
|
|
? model_tolerance*ON::UnitScale(length_unit_system, ON::LengthUnitSystem::Millimeters)
|
|
: 0.0;
|
|
system_dimstyle =
|
|
(tolerance_mm <= 1.0 || meters_per_unit < 1.0)
|
|
? &ON_DimStyle::DefaultMillimeterSmall
|
|
: &ON_DimStyle::DefaultMillimeterLarge;
|
|
}
|
|
else if (bIsUnitedStatesLengthUnit)
|
|
{
|
|
system_dimstyle =
|
|
(ON::LengthUnitSystem::Feet == length_unit_system)
|
|
? &ON_DimStyle::DefaultFootInchArchitecture
|
|
: &ON_DimStyle::DefaultInchDecimal;
|
|
}
|
|
else
|
|
{
|
|
system_dimstyle = &ON_DimStyle::Default;
|
|
}
|
|
|
|
source_dimstyle = system_dimstyle;
|
|
}
|
|
|
|
ON_DimStyle* default_dimstyle = new ON_DimStyle(*source_dimstyle);
|
|
default_dimstyle->ClearIndex();
|
|
default_dimstyle->ClearParentId();
|
|
default_dimstyle->SetId();
|
|
default_dimstyle->SetName(m_manifest.UnusedName(*default_dimstyle));
|
|
|
|
ON_ModelComponentReference mcr = AddManagedModelComponent(default_dimstyle, true);
|
|
const ON_DimStyle* model_dimstyle = ON_DimStyle::FromModelComponentRef(mcr,nullptr);
|
|
if (nullptr == model_dimstyle)
|
|
{
|
|
ON_ERROR("Failed to add default dimstyle.");
|
|
return ON_UNSET_INT_INDEX;
|
|
}
|
|
m_settings.SetCurrentDimensionStyleId(model_dimstyle->Id());
|
|
return model_dimstyle->Index();
|
|
}
|
|
|
|
ON_ModelComponentReference ONX_Model::AddManagedModelComponent(
|
|
class ON_ModelComponent* managed_model_component,
|
|
bool bResolveIdAndNameConflicts
|
|
)
|
|
{
|
|
const bool bManagedComponent = true;
|
|
const bool bUpdateComponentIdentification = true;
|
|
return AddModelComponentForExperts(managed_model_component, bManagedComponent, bResolveIdAndNameConflicts, bUpdateComponentIdentification);
|
|
}
|
|
|
|
|
|
ON_ModelComponentReference ONX_Model::AddModelComponent(
|
|
const class ON_ModelComponent& model_component
|
|
)
|
|
{
|
|
return AddModelComponent(model_component, true);
|
|
}
|
|
|
|
ON_ModelComponentReference ONX_Model::AddModelComponent(
|
|
const class ON_ModelComponent& model_component,
|
|
bool bResolveIdAndNameConflicts
|
|
)
|
|
{
|
|
const ON_ModelComponent::Type component_type = model_component.ComponentType();
|
|
if (!ON_ModelComponent::ComponentTypeIsValidAndNotMixed(component_type))
|
|
{
|
|
ON_ERROR("Invalid model_component parameter.");
|
|
return ON_ModelComponentReference::Empty;
|
|
}
|
|
|
|
ON_UUID id;
|
|
ON_wString name;
|
|
if (false == ValdateComponentIdAndName(component_type, model_component.Id(), model_component.ParentId(), model_component.Name(), bResolveIdAndNameConflicts, bResolveIdAndNameConflicts, id, name))
|
|
{
|
|
ON_ERROR("Invalid model_component id or name.");
|
|
return ON_ModelComponentReference::Empty;
|
|
}
|
|
|
|
ON_ModelComponent* candidate_model_component = nullptr;
|
|
if (ON_ModelComponent::Type::RenderLight == component_type ||
|
|
ON_ModelComponent::Type::ModelGeometry == component_type)
|
|
{
|
|
const ON_ModelGeometryComponent* model_geometry = ON_ModelGeometryComponent::Cast(&model_component);
|
|
if (nullptr != model_geometry)
|
|
{
|
|
const ON_Geometry* geometry = model_geometry->Geometry(nullptr);
|
|
candidate_model_component = ON_ModelGeometryComponent::Create(*geometry, model_geometry->Attributes(nullptr), nullptr);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Something simple like ON_Layer, ON_DimStyle, ON_RenderContent, etc
|
|
candidate_model_component = model_component.Duplicate();
|
|
}
|
|
|
|
bool bManagedComponent = true;
|
|
bool bUpdateComponentIdentification = true;
|
|
return Internal_AddModelComponent(candidate_model_component, id, model_component.ParentId(), name, bManagedComponent, bUpdateComponentIdentification);
|
|
}
|
|
|
|
ON_ModelComponentReference ONX_Model::AddModelComponentForExperts(
|
|
ON_ModelComponent* model_component,
|
|
bool bManagedComponent,
|
|
bool bResolveIdAndNameConflicts,
|
|
bool bUpdateComponentIdentification
|
|
)
|
|
{
|
|
if (nullptr == model_component)
|
|
{
|
|
ON_ERROR("model_component parameter is nullptr.");
|
|
return ON_ModelComponentReference::Empty;
|
|
}
|
|
|
|
const ON_ModelComponent::Type component_type = model_component->ComponentType();
|
|
if (!ON_ModelComponent::ComponentTypeIsValidAndNotMixed(component_type))
|
|
{
|
|
ON_ERROR("Invalid model_component->ComponentType() value.");
|
|
return ON_ModelComponentReference::Empty;
|
|
}
|
|
|
|
const ON_UUID parent_id = model_component->ParentId();
|
|
ON_UUID id = ON_nil_uuid;
|
|
ON_wString name;
|
|
if (false == ValdateComponentIdAndName(component_type, model_component->Id(), parent_id, model_component->Name(), bResolveIdAndNameConflicts, bResolveIdAndNameConflicts && bUpdateComponentIdentification, id, name))
|
|
{
|
|
ON_ERROR("Invalid model_component id or name.");
|
|
return ON_ModelComponentReference::Empty;
|
|
}
|
|
|
|
return Internal_AddModelComponent(model_component, id, parent_id, name, bManagedComponent, bUpdateComponentIdentification);
|
|
}
|
|
|
|
ON_ModelComponentReference ONX_Model::AddModelGeometryComponentForExperts(
|
|
bool bManageGeometry,
|
|
class ON_Object* geometry_object,
|
|
bool bManageAttributes,
|
|
class ON_3dmObjectAttributes* attributes,
|
|
bool bResolveIdAndNameConflicts
|
|
)
|
|
{
|
|
if ( nullptr == ON_Geometry::Cast(geometry_object) )
|
|
{
|
|
ON_ERROR("Invalid geometry_object parameter.");
|
|
return ON_ModelComponentReference::Empty;
|
|
}
|
|
|
|
if (nullptr == attributes)
|
|
{
|
|
bManageAttributes = true;
|
|
bResolveIdAndNameConflicts = true;
|
|
ON_Light* light = ON_Light::Cast(geometry_object);
|
|
if (nullptr != light
|
|
&& ON_nil_uuid != light->m_light_id
|
|
&& false == m_manifest.IdIsAvailable(light->m_light_id)
|
|
)
|
|
{
|
|
light->m_light_id = ON_nil_uuid;
|
|
}
|
|
const ON_Layer* default_layer
|
|
= (ON_nil_uuid == this->m_settings.CurrentLayerId())
|
|
? nullptr
|
|
: ON_Layer::FromModelComponentRef(LayerFromId(m_settings.CurrentLayerId()),nullptr);
|
|
if (nullptr == default_layer)
|
|
{
|
|
ONX_ModelComponentIterator layer_it(*this, ON_ModelComponent::Type::Layer);
|
|
for (const ON_Layer* layer = ON_Layer::Cast(layer_it.FirstComponent()); nullptr != layer; layer = ON_Layer::Cast(layer_it.NextComponent()) )
|
|
{
|
|
if (layer->IsVisible() && false == layer->IsLocked() && layer->ParentIdIsNil())
|
|
{
|
|
default_layer = layer;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
attributes = new ON_3dmObjectAttributes();
|
|
if (nullptr != light)
|
|
{
|
|
attributes->m_uuid = light->m_light_id;
|
|
attributes->m_name = light->m_light_name;
|
|
}
|
|
attributes->m_layer_index
|
|
= (nullptr == default_layer)
|
|
? ON_Layer::Default.Index()
|
|
: default_layer->Index();
|
|
}
|
|
else if ( ON_nil_uuid != attributes->m_uuid && false == m_manifest.IdIsAvailable(attributes->m_uuid))
|
|
{
|
|
if (bResolveIdAndNameConflicts)
|
|
attributes->m_uuid = ON_nil_uuid;
|
|
else
|
|
{
|
|
ON_ERROR("attributes->m_uuid is not valid or is in use.");
|
|
return ON_ModelComponentReference::Empty;
|
|
}
|
|
}
|
|
|
|
ON_ModelGeometryComponent* model_geometry_component = ON_ModelGeometryComponent::CreateForExperts(
|
|
bManageGeometry,
|
|
geometry_object,
|
|
bManageAttributes,
|
|
attributes,
|
|
nullptr
|
|
);
|
|
|
|
if ( nullptr == model_geometry_component )
|
|
return ON_ModelComponentReference::Empty;
|
|
|
|
ON_ModelComponentReference model_component_reference = AddManagedModelComponent(model_geometry_component, bResolveIdAndNameConflicts);
|
|
|
|
if (model_component_reference.IsEmpty())
|
|
delete model_geometry_component;
|
|
|
|
return model_component_reference;
|
|
}
|
|
|
|
|
|
ON_ModelComponentReference ONX_Model::AddModelGeometryComponent(
|
|
const class ON_Object* geometry_object,
|
|
const class ON_3dmObjectAttributes* attributes
|
|
)
|
|
{
|
|
return AddModelGeometryComponent(geometry_object, attributes, true);
|
|
}
|
|
|
|
ON_ModelComponentReference ONX_Model::AddModelGeometryComponent(
|
|
const class ON_Object* geometry_object,
|
|
const class ON_3dmObjectAttributes* attributes,
|
|
bool bResolveIdAndNameConflicts
|
|
)
|
|
{
|
|
if ( nullptr == ON_Geometry::Cast(geometry_object) )
|
|
{
|
|
ON_ERROR("Invalid geometry_object parameter.");
|
|
return ON_ModelComponentReference::Empty;
|
|
}
|
|
|
|
ON_UUID id = ON_nil_uuid;
|
|
if (nullptr != attributes && ON_nil_uuid != attributes->m_uuid )
|
|
{
|
|
if (m_manifest.IdIsAvailable(attributes->m_uuid))
|
|
id = attributes->m_uuid;
|
|
else if (false == bResolveIdAndNameConflicts)
|
|
{
|
|
ON_ERROR("attributes->m_uuid is invalid or in use in this model.");
|
|
return ON_ModelComponentReference::Empty;
|
|
}
|
|
}
|
|
|
|
ON_3dmObjectAttributes* managed_attributes = nullptr;
|
|
ON_Object* managed_geometry_object = geometry_object->Duplicate();
|
|
if (nullptr != attributes)
|
|
{
|
|
managed_attributes = new ON_3dmObjectAttributes(*attributes);
|
|
managed_attributes->m_uuid = id;
|
|
}
|
|
|
|
const bool bManageGeometry = true;
|
|
const bool bManageAttributes = true;
|
|
return AddModelGeometryComponentForExperts(
|
|
bManageGeometry,
|
|
managed_geometry_object,
|
|
bManageAttributes,
|
|
managed_attributes,
|
|
bResolveIdAndNameConflicts
|
|
);
|
|
}
|
|
|
|
|
|
ON_ModelComponentReference ONX_Model::AddManagedModelGeometryComponent(
|
|
class ON_Object* managed_geometry_object,
|
|
class ON_3dmObjectAttributes* managed_attributes
|
|
)
|
|
{
|
|
return AddManagedModelGeometryComponent(managed_geometry_object, managed_attributes, true);
|
|
}
|
|
|
|
ON_ModelComponentReference ONX_Model::AddManagedModelGeometryComponent(
|
|
class ON_Object* managed_geometry_object,
|
|
class ON_3dmObjectAttributes* managed_attributes,
|
|
bool bResolveIdAndNameConflicts
|
|
)
|
|
{
|
|
if ( nullptr == ON_Geometry::Cast(managed_geometry_object) )
|
|
{
|
|
ON_ERROR("Invalid managed_geometry_object parameter.");
|
|
return ON_ModelComponentReference::Empty;
|
|
}
|
|
const bool bManageGeometry = true;
|
|
const bool bManageAttributes = true;
|
|
return AddModelGeometryComponentForExperts(
|
|
bManageGeometry,
|
|
managed_geometry_object,
|
|
bManageAttributes,
|
|
managed_attributes,
|
|
bResolveIdAndNameConflicts
|
|
);
|
|
}
|
|
|
|
ONX_ModelComponentReferenceLink* ONX_Model::Internal_ModelComponentLinkFromSerialNumber(
|
|
ON__UINT64 model_component_runtime_serial_number
|
|
) const
|
|
{
|
|
const struct ON_SerialNumberMap::SN_ELEMENT* e = m_mcr_sn_map.FindSerialNumber(model_component_runtime_serial_number);
|
|
return (ONX_ModelComponentReferenceLink*)((nullptr != e) ? e->m_value.m_u.ptr : nullptr);
|
|
}
|
|
|
|
ONX_ModelComponentReferenceLink* ONX_Model::Internal_AddModelComponentReference(
|
|
ON_ModelComponentReference mcr
|
|
)
|
|
{
|
|
const ON_ModelComponent* model_component = mcr.ModelComponent();
|
|
if (nullptr == model_component)
|
|
{
|
|
ON_ERROR("Invalid mcr parameter - mcr.ModelComponent() is nullptr.");
|
|
return nullptr;
|
|
}
|
|
|
|
const ON_ModelComponent::Type component_type = model_component->ComponentType();
|
|
if (ON_ModelComponent::Type::Unset == component_type || ON_ModelComponent::Type::Mixed == component_type)
|
|
{
|
|
ON_ERROR("Invalid component type");
|
|
return nullptr;
|
|
}
|
|
|
|
ONX_ModelComponentReferenceLink* mcr_link = Internal_ModelComponentLinkFromSerialNumber(model_component->RuntimeSerialNumber());
|
|
if (nullptr != mcr_link)
|
|
{
|
|
// This component was already added.
|
|
return mcr_link;
|
|
}
|
|
|
|
struct ON_SerialNumberMap::SN_ELEMENT* e = m_mcr_sn_map.AddSerialNumber(model_component->RuntimeSerialNumber());
|
|
if (nullptr == e)
|
|
{
|
|
ON_ERROR("m_mcr_sn_map.AddSerialNumber(model_component->RuntimeSerialNumber()) failed.");
|
|
return nullptr;
|
|
}
|
|
|
|
if ( 0 == m_mcr_link_fsp.SizeofElement())
|
|
m_mcr_link_fsp.Create(sizeof(*mcr_link),0,0);
|
|
|
|
e->m_value.m_u.ptr = m_mcr_link_fsp.AllocateDirtyElement();
|
|
|
|
mcr_link = new(e->m_value.m_u.ptr) ONX_ModelComponentReferenceLink();
|
|
mcr_link->m_mcr = mcr;
|
|
|
|
ONX_Model::ONX_ModelComponentList& list = Internal_ComponentList( component_type );
|
|
if (component_type != list.m_component_type)
|
|
{
|
|
ON_ERROR("Internal_ComponentList(component_type) failed");
|
|
}
|
|
else
|
|
{
|
|
if (nullptr == list.m_first_mcr_link)
|
|
{
|
|
list.m_first_mcr_link = mcr_link;
|
|
mcr_link->m_prev = nullptr;
|
|
}
|
|
else
|
|
{
|
|
mcr_link->m_prev = list.m_last_mcr_link;
|
|
list.m_last_mcr_link->m_next = mcr_link;
|
|
}
|
|
mcr_link->m_next = nullptr;
|
|
list.m_last_mcr_link = mcr_link;
|
|
|
|
list.m_count++;
|
|
}
|
|
|
|
return mcr_link;
|
|
}
|
|
|
|
|
|
void ONX_Model::Internal_RemoveModelComponentReferenceLink(
|
|
class ONX_ModelComponentReferenceLink* mcr_link
|
|
)
|
|
{
|
|
if (nullptr == mcr_link)
|
|
return;
|
|
|
|
const ON_ModelComponent* model_component = mcr_link->m_mcr.ModelComponent();
|
|
|
|
if ( nullptr == model_component )
|
|
return;
|
|
|
|
m_mcr_sn_map.RemoveSerialNumberAndId(model_component->ReferenceModelSerialNumber());
|
|
|
|
mcr_link->m_mcr = ON_ModelComponentReference::Empty;
|
|
|
|
ONX_Model::ONX_ModelComponentList& list = Internal_ComponentList(model_component->ComponentType());
|
|
if (list.m_count > 0)
|
|
{
|
|
if (mcr_link->m_prev)
|
|
mcr_link->m_prev->m_next = mcr_link->m_next;
|
|
else
|
|
list.m_first_mcr_link = mcr_link->m_next;
|
|
if (mcr_link->m_next)
|
|
mcr_link->m_next->m_prev = mcr_link->m_prev;
|
|
else
|
|
list.m_last_mcr_link = mcr_link->m_prev;
|
|
list.m_count--;
|
|
}
|
|
|
|
mcr_link->m_prev = nullptr;
|
|
mcr_link->m_next = nullptr;
|
|
|
|
m_mcr_link_fsp.ReturnElement(mcr_link);
|
|
}
|
|
|
|
ONX_Model::ONX_ModelComponentList& ONX_Model::Internal_ComponentList(
|
|
ON_ModelComponent::Type component_type
|
|
)
|
|
{
|
|
const int i = static_cast<unsigned int>(component_type);
|
|
return
|
|
(i < ONX_Model::m_private->m_mcr_lists.Count())
|
|
? m_private->m_mcr_lists[i]
|
|
: m_private->m_mcr_lists[0];
|
|
}
|
|
|
|
const ONX_Model::ONX_ModelComponentList& ONX_Model::Internal_ComponentListConst(
|
|
ON_ModelComponent::Type component_type
|
|
) const
|
|
{
|
|
const int i = static_cast<unsigned int>(component_type);
|
|
return
|
|
(i < ONX_Model::m_private->m_mcr_lists.Count())
|
|
? m_private->m_mcr_lists[i]
|
|
: m_private->m_mcr_lists[0];
|
|
}
|
|
|
|
ON_ModelComponentReference ONX_Model::Internal_AddModelComponent(
|
|
ON_ModelComponent* model_component,
|
|
ON_UUID id,
|
|
ON_UUID name_parent_id,
|
|
const ON_wString& name,
|
|
bool bManagedComponent,
|
|
bool bUpdateComponentIdentification
|
|
)
|
|
{
|
|
for (;;)
|
|
{
|
|
if (nullptr == model_component)
|
|
{
|
|
ON_ERROR("Invalid model_component parameter.");
|
|
break;
|
|
}
|
|
|
|
if (model_component->IsSystemComponent())
|
|
{
|
|
ON_ERROR("Invalid model_component parameter.");
|
|
break;
|
|
}
|
|
|
|
const ON_ModelComponent::Type component_type = model_component->ComponentType();
|
|
if (!ON_ModelComponent::ComponentTypeIsValidAndNotMixed(component_type))
|
|
{
|
|
ON_ERROR("Invalid model_component parameter.");
|
|
break;
|
|
}
|
|
|
|
const int original_index = model_component->Index();
|
|
const ON_UUID original_id = model_component->Id();
|
|
const bool bIsEmbeddedFileReference = (ON_ModelComponent::Type::Image == component_type);
|
|
const ON_Bitmap* embedded_file
|
|
= bIsEmbeddedFileReference
|
|
? ON_Bitmap::Cast(model_component)
|
|
: nullptr;
|
|
|
|
const ON_NameHash original_name_hash
|
|
= bIsEmbeddedFileReference
|
|
? (nullptr == embedded_file ? ON_NameHash::UnsetNameHash : ON_NameHash::CreateFilePathHash(embedded_file->FileReference()) )
|
|
: model_component->NameHash();
|
|
|
|
const ON_NameHash name_hash
|
|
= bIsEmbeddedFileReference
|
|
? original_name_hash
|
|
: ON_NameHash::Create(name_parent_id,name,ON_ModelComponent::UniqueNameIgnoresCase(component_type));
|
|
|
|
int manifest_item_index = ON_UNSET_INT_INDEX;
|
|
const bool bIndexRequired = ON_ModelComponent::IndexRequired(component_type);
|
|
if (bIndexRequired)
|
|
{
|
|
manifest_item_index = m_manifest.ComponentIndexLimit(component_type);
|
|
if (manifest_item_index >= 0x7FFFFFFF && bUpdateComponentIdentification)
|
|
{
|
|
ON_ERROR("Unable to set model_component_index.");
|
|
break;
|
|
}
|
|
}
|
|
const bool bUpdateIndex = (bUpdateComponentIdentification && bIndexRequired && original_index != (int)manifest_item_index);
|
|
const bool bUpdateId = (bUpdateComponentIdentification && !(original_id == id));
|
|
const bool bUpdateName = (false == bIsEmbeddedFileReference && bUpdateComponentIdentification && original_name_hash != name_hash);
|
|
|
|
if (bUpdateIndex && model_component->IndexIsLocked())
|
|
{
|
|
ON_ERROR("Unable to set component index.");
|
|
break;
|
|
}
|
|
if (bUpdateId && model_component->IdIsLocked())
|
|
{
|
|
ON_ERROR("Unable to set component id.");
|
|
break;
|
|
}
|
|
if (bUpdateName && model_component->NameIsLocked())
|
|
{
|
|
ON_ERROR("Unable to set component name.");
|
|
break;
|
|
}
|
|
|
|
if (bUpdateIndex && false == model_component->SetIndex((int)manifest_item_index))
|
|
{
|
|
ON_ERROR("model_component->SetIndex(...) failed.");
|
|
break;
|
|
}
|
|
if(bUpdateId && false == model_component->SetId(id) )
|
|
{
|
|
ON_ERROR("model_component->SetId(...) failed.");
|
|
break;
|
|
}
|
|
if(bUpdateName && false == model_component->SetName(name) )
|
|
{
|
|
ON_ERROR("model_component->SetName(...) failed.");
|
|
break;
|
|
}
|
|
|
|
if (bUpdateComponentIdentification)
|
|
{
|
|
ON_ModelGeometryComponent* geometry_component = ON_ModelGeometryComponent::Cast(model_component);
|
|
if (nullptr != geometry_component)
|
|
{
|
|
const ON_Light* light = ON_Light::Cast(geometry_component->Geometry(nullptr));
|
|
if (nullptr != light)
|
|
{
|
|
if (id != light->m_light_id)
|
|
const_cast<ON_Light*>(light)->m_light_id = id;
|
|
if (name != light->m_light_name)
|
|
const_cast<ON_Light*>(light)->m_light_name = name;
|
|
}
|
|
const ON_3dmObjectAttributes* attributes = geometry_component->Attributes(nullptr);
|
|
if (nullptr != attributes)
|
|
{
|
|
if (id != attributes->m_uuid)
|
|
const_cast<ON_3dmObjectAttributes*>(attributes)->m_uuid = id;
|
|
if (name != attributes->m_name)
|
|
const_cast<ON_3dmObjectAttributes*>(attributes)->m_name = name;
|
|
}
|
|
}
|
|
}
|
|
|
|
Internal_IncrementModelContentVersionNumber();
|
|
|
|
const class ON_ComponentManifestItem& manifest_item = m_manifest.AddComponentToManifest(
|
|
model_component->ComponentType(),
|
|
model_component->RuntimeSerialNumber(),
|
|
id,
|
|
name_hash
|
|
);
|
|
|
|
if (manifest_item.IsUnset())
|
|
{
|
|
ON_ERROR("Unable to update model manifest.");
|
|
break;
|
|
}
|
|
|
|
ON_ManifestMapItem original_to_manifest;
|
|
if ( original_to_manifest.SetSourceIdentification(model_component) && original_to_manifest.SetDestinationIdentification(&manifest_item))
|
|
{
|
|
m_original_to_manifest_map.AddMapItem(original_to_manifest);
|
|
}
|
|
|
|
ON_ManifestMapItem manifest_to_original;
|
|
if ( manifest_to_original.SetSourceIdentification(&manifest_item) && manifest_to_original.SetDestinationIdentification(model_component) )
|
|
{
|
|
m_manifest_to_original_map.AddMapItem(manifest_to_original);
|
|
}
|
|
|
|
if (bIndexRequired)
|
|
{
|
|
if (model_component->Index() != manifest_item.Index())
|
|
{
|
|
ON_ERROR("Unexpected manifest_item_index value.");
|
|
if (bUpdateIndex && false == model_component->SetIndex((int)(manifest_item_index)))
|
|
{
|
|
ON_ERROR("model_component->SetIndex(...) failed.");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (ON_ComponentManifest::UnsetComponentIndex != model_component->Index() )
|
|
{
|
|
ON_ERROR("Unexpected model_component->Index() value.");
|
|
if (bUpdateIndex && false == model_component->SetIndex(ON_ComponentManifest::UnsetComponentIndex))
|
|
{
|
|
ON_ERROR("model_component->SetIndex(...) failed.");
|
|
break;
|
|
}
|
|
}
|
|
if ( ON_UNSET_INT_INDEX != manifest_item.Index() )
|
|
{
|
|
ON_ERROR("Unexpected manifest_item_index value.");
|
|
}
|
|
}
|
|
|
|
if (ON_ModelComponent::Type::ModelGeometry == model_component->ComponentType())
|
|
{
|
|
ON_ModelGeometryComponent* model_geometry = ON_ModelGeometryComponent::Cast(model_component);
|
|
if (nullptr != model_geometry)
|
|
{
|
|
// TEMPORARY - until the id and name can be removed from ON_3dmObjectAttributes,
|
|
// they need to be synchronized with the ones on ON_ModelGeometryComponent.
|
|
ON_3dmObjectAttributes* attributes = const_cast< ON_3dmObjectAttributes* >(model_geometry->Attributes(nullptr));
|
|
if (nullptr != attributes)
|
|
{
|
|
if ( bUpdateId )
|
|
attributes->m_uuid = model_geometry->Id();
|
|
if (bUpdateName)
|
|
attributes->m_name = model_geometry->Name();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bUpdateComponentIdentification)
|
|
{
|
|
// continue even when UpdateReferencedComponents() returns false
|
|
model_component->UpdateReferencedComponents(ON_ComponentManifest::Empty,m_manifest,m_original_to_manifest_map);
|
|
}
|
|
|
|
ON_ModelComponentReference model_component_reference = ON_ModelComponentReference::CreateForExperts(model_component,bManagedComponent);
|
|
Internal_AddModelComponentReference(model_component_reference);
|
|
return model_component_reference;
|
|
}
|
|
|
|
return ON_ModelComponentReference::Empty;
|
|
}
|
|
|
|
/* ON_DEPRECATED */ ON_ModelComponentReference ONX_Model::RenderMaterialFromAttributes(const ON_3dmObjectAttributes& attr) const
|
|
{
|
|
return MaterialFromAttributes(attr);
|
|
}
|
|
|
|
/* ON_DEPRECATED */ ON_ModelComponentReference ONX_Model::RenderMaterialFromLayerIndex(int index) const
|
|
{
|
|
return MaterialFromLayerIndex(index);
|
|
}
|
|
|
|
/* ON_DEPRECATED */ ON_ModelComponentReference ONX_Model::RenderMaterialFromIndex(int index) const
|
|
{
|
|
return MaterialFromIndex(index);
|
|
}
|
|
|
|
/* ON_DEPRECATED */ ON_ModelComponentReference ONX_Model::RenderMaterialFromId(ON_UUID id) const
|
|
{
|
|
return MaterialFromId(id);
|
|
}
|
|
|
|
ON_ModelComponentReference ONX_Model::MaterialFromAttributes(
|
|
const ON_3dmObjectAttributes& attributes
|
|
) const
|
|
{
|
|
switch ( attributes.MaterialSource() )
|
|
{
|
|
case ON::material_from_layer:
|
|
return MaterialFromLayerIndex( attributes.m_layer_index );
|
|
break;
|
|
|
|
case ON::material_from_object:
|
|
return MaterialFromIndex( attributes.m_material_index );
|
|
break;
|
|
|
|
case ON::material_from_parent:
|
|
// TODO: If object is an idef, get material from iref attributes.
|
|
return MaterialFromIndex( attributes.m_material_index );
|
|
break;
|
|
}
|
|
|
|
return m_default_render_material;
|
|
}
|
|
|
|
ON_ModelComponentReference ONX_Model::MaterialFromLayerIndex(
|
|
int layer_index
|
|
) const
|
|
{
|
|
int material_index = ON_Layer::FromModelComponentRef(
|
|
LayerFromIndex(layer_index),
|
|
&ON_Layer::Default
|
|
)->RenderMaterialIndex();
|
|
return MaterialFromIndex(material_index);
|
|
}
|
|
|
|
ON_ModelComponentReference ONX_Model::MaterialFromIndex(
|
|
int material_index
|
|
) const
|
|
{
|
|
ON_ModelComponentReference cr = ComponentFromIndex(ON_ModelComponent::Type::Material, material_index);
|
|
return cr.IsEmpty() ? m_default_render_material : cr;
|
|
}
|
|
|
|
ON_ModelComponentReference ONX_Model::MaterialFromId(
|
|
ON_UUID material_id
|
|
) const
|
|
{
|
|
ON_ModelComponentReference cr = ComponentFromId(ON_ModelComponent::Type::Material, material_id);
|
|
return cr.IsEmpty() ? m_default_render_material : cr;
|
|
}
|
|
|
|
ON_Color ONX_Model::WireframeColorFromAttributes(
|
|
const ON_3dmObjectAttributes& attributes
|
|
) const
|
|
{
|
|
ON_Color color = ON_UNSET_COLOR;
|
|
|
|
switch ( attributes.ColorSource() )
|
|
{
|
|
case ON::color_from_layer:
|
|
color = ON_Layer::FromModelComponentRef( LayerFromIndex(attributes.m_layer_index), &ON_Layer::Default )->Color();
|
|
break;
|
|
|
|
case ON::color_from_object:
|
|
color = attributes.m_color;
|
|
break;
|
|
|
|
case ON::color_from_material:
|
|
color = ON_Material::FromModelComponentRef( MaterialFromAttributes(attributes), &ON_Material::Default)->Diffuse();
|
|
break;
|
|
|
|
case ON::color_from_parent:
|
|
color = attributes.m_color;
|
|
// TODO: if object is an instance definition, get color
|
|
// from instance references.
|
|
break;
|
|
}
|
|
|
|
if ( color == ON_UNSET_COLOR )
|
|
color.SetRGB(128,128,128);
|
|
|
|
return color;
|
|
}
|
|
|
|
void ONX_Model::DumpSummary( ON_TextLog& dump ) const
|
|
{
|
|
dump.Print("File version: %u\n",m_3dm_file_version);
|
|
if (false == dump.IsTextHash())
|
|
{
|
|
dump.Print("File openNURBS version: %u\n", m_3dm_opennurbs_version);
|
|
if (m_3dm_file_byte_count > 0)
|
|
dump.Print("File length: %llu bytes\n", m_3dm_file_byte_count);
|
|
|
|
if (m_sStartSectionComments.Length() > 0)
|
|
{
|
|
dump.Print("Start section comments:\n");
|
|
dump.PushIndent();
|
|
dump.PrintWrappedText(static_cast<const char*>(m_sStartSectionComments));
|
|
dump.PopIndent();
|
|
dump.Print("\n");
|
|
}
|
|
|
|
m_properties.Dump(dump);
|
|
|
|
dump.Print("\n");
|
|
}
|
|
|
|
m_settings.Dump(dump);
|
|
|
|
dump.Print("\n");
|
|
|
|
dump.Print("Contents:\n");
|
|
dump.PushIndent();
|
|
dump.Print("%u embedded images\n",Internal_ComponentListConst(ON_ModelComponent::Type::Image).m_count);
|
|
dump.Print("%u materials\n",Internal_ComponentListConst(ON_ModelComponent::Type::RenderMaterial).m_count);
|
|
dump.Print("%u line patterns\n",Internal_ComponentListConst(ON_ModelComponent::Type::LinePattern).m_count);
|
|
dump.Print("%u text styles\n",Internal_ComponentListConst(ON_ModelComponent::Type::TextStyle).m_count);
|
|
dump.Print("%u annotation styles\n",Internal_ComponentListConst(ON_ModelComponent::Type::DimStyle).m_count);
|
|
dump.Print("%u hatch patterns\n",Internal_ComponentListConst(ON_ModelComponent::Type::HatchPattern).m_count);
|
|
dump.Print("%u layers\n",Internal_ComponentListConst(ON_ModelComponent::Type::Layer).m_count);
|
|
dump.Print("%u groups\n",Internal_ComponentListConst(ON_ModelComponent::Type::Group).m_count);
|
|
dump.Print("%u lights\n",Internal_ComponentListConst(ON_ModelComponent::Type::RenderLight).m_count);
|
|
dump.Print("%u model geometry objects\n",Internal_ComponentListConst(ON_ModelComponent::Type::ModelGeometry).m_count);
|
|
if (false == dump.IsTextHash())
|
|
{
|
|
dump.Print("%u user data objects\n", m_userdata_table.UnsignedCount());
|
|
}
|
|
dump.PopIndent();
|
|
}
|
|
|
|
void ONX_Model::DumpComponentList(
|
|
ON_ModelComponent::Type component_type,
|
|
ON_TextLog& text_log
|
|
) const
|
|
{
|
|
const ON_wString type_name_string = ON_ModelComponent::ComponentTypeToString(component_type);
|
|
const wchar_t* type_name = static_cast<const wchar_t*>(type_name_string);
|
|
unsigned int i = 0;
|
|
for (
|
|
const ONX_ModelComponentReferenceLink* link = Internal_ComponentListConst(component_type).m_first_mcr_link;
|
|
nullptr != link;
|
|
link = link->m_next
|
|
)
|
|
{
|
|
text_log.Print(L"%ls %d:\n",type_name,i);
|
|
const ON_ModelComponent* model_component = link->m_mcr.ModelComponent();
|
|
text_log.PushIndent();
|
|
if ( nullptr == model_component )
|
|
text_log.Print(L"nullptr\n");
|
|
else
|
|
model_component->Dump(text_log);
|
|
link->m_mcr.ModelComponent();
|
|
text_log.PopIndent();
|
|
i++;
|
|
}
|
|
}
|
|
|
|
void ONX_Model::DumpUserDataTable( ON_TextLog& dump) const
|
|
{
|
|
int i;
|
|
for ( i = 0; i < m_userdata_table.Count(); i++ )
|
|
{
|
|
const ONX_Model_UserData* ud = m_userdata_table[i];
|
|
if ( nullptr == ud)
|
|
continue;
|
|
dump.Print("User Data Table %d:\n",i);
|
|
dump.PushIndent();
|
|
dump.Print("uuid = "); dump.Print(ud->m_uuid); dump.Print("\n");
|
|
ud->m_goo.Dump(dump);
|
|
dump.PopIndent();
|
|
}
|
|
}
|
|
|
|
|
|
void ONX_Model::Dump(ON_TextLog& dump) const
|
|
{
|
|
dump.Print("Model summary:\n");
|
|
dump.PushIndent();
|
|
DumpSummary(dump);
|
|
dump.PopIndent();
|
|
|
|
dump.PrintNewLine();
|
|
DumpComponentLists(dump);
|
|
|
|
if ( false == dump.IsTextHash() )
|
|
{
|
|
dump.Print("User data table:\n");
|
|
dump.PushIndent();
|
|
DumpUserDataTable(dump);
|
|
dump.PopIndent();
|
|
dump.PrintNewLine();
|
|
}
|
|
}
|
|
|
|
void ONX_Model::DumpComponentLists( ON_TextLog& dump ) const
|
|
{
|
|
const ON_ModelComponent::Type table_types[] =
|
|
{
|
|
ON_ModelComponent::Type::Image,
|
|
ON_ModelComponent::Type::TextureMapping,
|
|
ON_ModelComponent::Type::Material,
|
|
ON_ModelComponent::Type::LinePattern,
|
|
ON_ModelComponent::Type::Layer,
|
|
ON_ModelComponent::Type::Group,
|
|
ON_ModelComponent::Type::TextStyle,
|
|
ON_ModelComponent::Type::DimStyle,
|
|
ON_ModelComponent::Type::RenderLight,
|
|
ON_ModelComponent::Type::HatchPattern,
|
|
ON_ModelComponent::Type::ModelGeometry,
|
|
ON_ModelComponent::Type::HistoryRecord,
|
|
ON_ModelComponent::Type::RenderContent,
|
|
ON_ModelComponent::Type::EmbeddedFile,
|
|
//ON_ModelComponent::Type::ObsoleteValue,
|
|
ON_ModelComponent::Type::SectionStyle,
|
|
ON_ModelComponent::Type::Unset // list terminator
|
|
};
|
|
|
|
for (unsigned i = 0; ON_ModelComponent::Type::Unset != table_types[i]; i++)
|
|
{
|
|
ON_wString type_name = ON_ModelComponent::ComponentTypeToString(table_types[i]);
|
|
dump.Print(L"%ls table:\n",static_cast<const wchar_t*>(type_name));
|
|
dump.PushIndent();
|
|
DumpComponentList(table_types[i],dump);
|
|
dump.PopIndent();
|
|
dump.Print("\n");
|
|
}
|
|
}
|
|
|
|
ON_SHA1_Hash ONX_Model::ContentHash(
|
|
) const
|
|
{
|
|
return ContentHash(ON_TextHash::Null);
|
|
}
|
|
|
|
ON_SHA1_Hash ONX_Model::ContentHash(
|
|
ON_TextLog& hashed_text
|
|
) const
|
|
{
|
|
const bool bRemapIds = true;
|
|
ON_TextHash hash_log;
|
|
hash_log.SetIdRemap(bRemapIds);
|
|
if (false == hashed_text.IsNull())
|
|
hash_log.SetOutputTextLog(&hashed_text);
|
|
Dump(hash_log);
|
|
return hash_log.Hash();
|
|
|
|
}
|
|
|
|
|
|
class ON__CIndexPair
|
|
{
|
|
public:
|
|
static int CompareOldIndex( const ON__CIndexPair* a, const ON__CIndexPair* b );
|
|
static int CompareOldAndNewIndex( const ON__CIndexPair* a, const ON__CIndexPair* b );
|
|
int m_old_index; // value in model.m_..._table[m_table_index].m_..._index; (Read from file)
|
|
int m_new_index; // index in model.m_..._table[] array
|
|
};
|
|
|
|
int ON__CIndexPair::CompareOldIndex( const ON__CIndexPair* a, const ON__CIndexPair* b )
|
|
{
|
|
return (a->m_old_index - b->m_old_index);
|
|
}
|
|
|
|
int ON__CIndexPair::CompareOldAndNewIndex( const ON__CIndexPair* a, const ON__CIndexPair* b )
|
|
{
|
|
int i;
|
|
if ( 0 == (i = a->m_old_index - b->m_old_index) )
|
|
i = a->m_new_index - b->m_new_index;
|
|
return i;
|
|
}
|
|
|
|
bool ONX_Model::Read(const char* filename, ON_TextLog* error_log)
|
|
{
|
|
bool rc = false;
|
|
|
|
if (nullptr != filename)
|
|
{
|
|
FILE* fp = ON::OpenFile(filename, "rb");
|
|
if ( 0 != fp )
|
|
{
|
|
ON_BinaryFile file(ON::archive_mode::read3dm,fp);
|
|
rc = Read(file, error_log);
|
|
ON::CloseFile(fp);
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
bool ONX_Model::Read(const wchar_t* filename, ON_TextLog* error_log)
|
|
{
|
|
bool rc = false;
|
|
|
|
if (nullptr != filename)
|
|
{
|
|
FILE* fp = ON::OpenFile(filename, L"rb");
|
|
if ( 0 != fp )
|
|
{
|
|
ON_BinaryFile file(ON::archive_mode::read3dm,fp);
|
|
rc = Read(file, error_log);
|
|
ON::CloseFile(fp);
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
bool ONX_Model::IncrementalReadBegin(
|
|
ON_BinaryArchive& archive,
|
|
bool bManageComponents,
|
|
unsigned int table_filter,
|
|
ON_TextLog* error_log
|
|
)
|
|
{
|
|
Reset();
|
|
|
|
const bool bResolveIdAndNameConflicts = true;
|
|
const bool bUpdateComponentIdentification = true;
|
|
|
|
int rc;
|
|
|
|
if ( 0 == table_filter )
|
|
table_filter = 0xFFFFFFFF; // read everything
|
|
|
|
// STEP 1: REQUIRED - Read start section
|
|
if ( !archive.Read3dmStartSection( &m_3dm_file_version, m_sStartSectionComments ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// STEP 2: REQUIRED - Read properties section
|
|
if ( !archive.Read3dmProperties( m_properties ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// version of opennurbs used to write the file.
|
|
m_3dm_opennurbs_version = archive.ArchiveOpenNURBSVersion();
|
|
|
|
// STEP 3: REQUIRED - Read setting section
|
|
if ( !archive.Read3dmSettings( m_settings ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// STEP 4: REQUIRED - Read embedded bitmap table
|
|
if ( archive.BeginRead3dmBitmapTable() )
|
|
{
|
|
if ( 0 != (static_cast<unsigned int>(ON_3dmArchiveTableType::bitmap_table) & table_filter) )
|
|
{
|
|
for(;;)
|
|
{
|
|
ON_Bitmap* bitmap = nullptr;
|
|
rc = archive.Read3dmBitmap(&bitmap);
|
|
if ( rc==0 )
|
|
break; // end of bitmap table
|
|
if (rc < 0)
|
|
break;
|
|
if ( AddModelComponentForExperts(bitmap,bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty() )
|
|
delete bitmap;
|
|
}
|
|
}
|
|
|
|
// If BeginRead3dmBitmapTable() returns true,
|
|
// then you MUST call EndRead3dmBitmapTable().
|
|
if ( !archive.EndRead3dmBitmapTable() )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// STEP 5: REQUIRED - Read texture mapping table
|
|
if ( archive.BeginRead3dmTextureMappingTable() )
|
|
{
|
|
if ( 0 != (static_cast<unsigned int>(ON_3dmArchiveTableType::texture_mapping_table) & table_filter) )
|
|
{
|
|
for(;;)
|
|
{
|
|
ON_TextureMapping* texture_mapping = nullptr;
|
|
rc = archive.Read3dmTextureMapping(&texture_mapping);
|
|
if ( rc==0 )
|
|
break; // end of texture_mapping table
|
|
if ( rc < 0 )
|
|
break;
|
|
if (AddModelComponentForExperts(texture_mapping, bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty())
|
|
delete texture_mapping;
|
|
}
|
|
}
|
|
|
|
// If BeginRead3dmTextureMappingTable() returns true,
|
|
// then you MUST call EndRead3dmTextureMappingTable().
|
|
if ( !archive.EndRead3dmTextureMappingTable() )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
// STEP 6: REQUIRED - Read render material table
|
|
if ( archive.BeginRead3dmMaterialTable() )
|
|
{
|
|
const ON_UUID settings_current_id = m_settings.CurrentMaterialId();
|
|
const int settings_current_index = m_settings.CurrentMaterialIndex();
|
|
bool bSetCurrentById = !(ON_nil_uuid == settings_current_id);
|
|
bool bSetCurrentByIndex = false == bSetCurrentById && settings_current_index >= 0;
|
|
m_settings.SetCurrentMaterialId(ON_nil_uuid);
|
|
|
|
if ( 0 != (static_cast<unsigned int>(ON_3dmArchiveTableType::material_table) & table_filter) )
|
|
{
|
|
for(;;)
|
|
{
|
|
ON_Material* material = nullptr;
|
|
rc = archive.Read3dmMaterial(&material);
|
|
if ( rc==0 )
|
|
break; // end of material table
|
|
if (rc < 0)
|
|
break;
|
|
// index or id might be modified by AddModelComponentForExperts()
|
|
const bool bSetAsCurrent =
|
|
(bSetCurrentById && settings_current_id == material->Id())
|
|
|| (bSetCurrentByIndex && settings_current_index == material->Index());
|
|
if ( AddModelComponentForExperts(material,bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty() )
|
|
{
|
|
delete material;
|
|
}
|
|
else if (bSetAsCurrent)
|
|
{
|
|
m_settings.SetCurrentMaterialId(material->Id());
|
|
bSetCurrentById = false;
|
|
bSetCurrentByIndex = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If BeginRead3dmMaterialTable() returns true,
|
|
// then you MUST call EndRead3dmMaterialTable().
|
|
if ( !archive.EndRead3dmMaterialTable() )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
// STEP 7: REQUIRED - Read line type table
|
|
if ( archive.BeginRead3dmLinetypeTable() )
|
|
{
|
|
const ON_UUID settings_current_id = m_settings.CurrentLinePatternId();
|
|
const int settings_current_index = m_settings.CurrentLinePatternIndex();
|
|
bool bSetCurrentById = !(ON_nil_uuid == settings_current_id);
|
|
bool bSetCurrentByIndex = false == bSetCurrentById && settings_current_index >= 0;
|
|
m_settings.SetCurrentLinePatternId(ON_nil_uuid);
|
|
|
|
if ( 0 != (static_cast<unsigned int>(ON_3dmArchiveTableType::linetype_table) & table_filter) )
|
|
{
|
|
for(;;)
|
|
{
|
|
ON_Linetype* line_pattern = nullptr;
|
|
rc = archive.Read3dmLinetype(&line_pattern);
|
|
if ( rc==0 )
|
|
break; // end of linetype table
|
|
if (rc < 0)
|
|
break;
|
|
// index or id might be modified by AddModelComponentForExperts()
|
|
const bool bSetAsCurrent =
|
|
(bSetCurrentById && settings_current_id == line_pattern->Id())
|
|
|| (bSetCurrentByIndex && settings_current_index == line_pattern->Index());
|
|
if ( AddModelComponentForExperts(line_pattern,bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty() )
|
|
{
|
|
delete line_pattern;
|
|
}
|
|
else if (bSetAsCurrent)
|
|
{
|
|
m_settings.SetCurrentLinePatternId(line_pattern->Id());
|
|
bSetCurrentById = false;
|
|
bSetCurrentByIndex = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If BeginRead3dmLinetypeTable() returns true,
|
|
// then you MUST call EndRead3dmLinetypeTable().
|
|
if ( !archive.EndRead3dmLinetypeTable() )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// STEP 8: REQUIRED - Read layer table
|
|
if ( archive.BeginRead3dmLayerTable() )
|
|
{
|
|
const ON_UUID settings_current_id = m_settings.CurrentLayerId();
|
|
const int settings_current_index = m_settings.CurrentLayerIndex();
|
|
bool bSetCurrentById = !(ON_nil_uuid == settings_current_id);
|
|
bool bSetCurrentByIndex = false == bSetCurrentById && settings_current_index >= 0;
|
|
m_settings.SetCurrentLayerId(ON_nil_uuid);
|
|
|
|
if ( 0 != (static_cast<unsigned int>(ON_3dmArchiveTableType::layer_table) & table_filter) )
|
|
{
|
|
for(;;)
|
|
{
|
|
ON_Layer* layer = nullptr;
|
|
rc = archive.Read3dmLayer(&layer);
|
|
if ( rc==0 )
|
|
break; // end of layer table
|
|
if (rc < 0)
|
|
break;
|
|
// index or id might be modified by AddModelComponentForExperts()
|
|
const bool bSetAsCurrent =
|
|
(bSetCurrentById && settings_current_id == layer->Id())
|
|
|| (bSetCurrentByIndex && settings_current_index == layer->Index());
|
|
if (AddModelComponentForExperts(layer, bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty())
|
|
{
|
|
delete layer;
|
|
}
|
|
else if (bSetAsCurrent)
|
|
{
|
|
m_settings.SetCurrentLayerId(layer->Id());
|
|
bSetCurrentById = false;
|
|
bSetCurrentByIndex = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If BeginRead3dmLayerTable() returns true,
|
|
// then you MUST call EndRead3dmLayerTable().
|
|
if ( !archive.EndRead3dmLayerTable() )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
ON_UUID current_layer_id = m_settings.CurrentLayerId();
|
|
const ON_Layer* current_layer
|
|
= (ON_nil_uuid == current_layer_id)
|
|
? nullptr
|
|
: ON_Layer::FromModelComponentRef(LayerFromId(current_layer_id), nullptr);
|
|
if (nullptr == current_layer)
|
|
{
|
|
current_layer_id = ON_nil_uuid;
|
|
int layer_count = 0;
|
|
ONX_ModelComponentIterator layer_it(*this, ON_ModelComponent::Type::Layer);
|
|
for (const ON_Layer* layer = ON_Layer::Cast(layer_it.FirstComponent()); nullptr != layer; layer = ON_Layer::Cast(layer_it.NextComponent()))
|
|
{
|
|
layer_count++;
|
|
if (layer->IsVisible() && false == layer->IsLocked())
|
|
{
|
|
current_layer = layer;
|
|
if ( layer->ParentIdIsNil() )
|
|
break;
|
|
}
|
|
}
|
|
if (nullptr == current_layer && layer_count > 0)
|
|
{
|
|
current_layer = ON_Layer::Cast(layer_it.FirstComponent());
|
|
}
|
|
if (nullptr != current_layer)
|
|
m_settings.SetCurrentLayerId(current_layer->Id());
|
|
else
|
|
AddDefaultLayer(nullptr, ON_Color::UnsetColor);
|
|
}
|
|
}
|
|
|
|
// STEP 9: REQUIRED - Read group table
|
|
if ( archive.BeginRead3dmGroupTable() )
|
|
{
|
|
if ( 0 != (static_cast<unsigned int>(ON_3dmArchiveTableType::group_table) & table_filter) )
|
|
{
|
|
for(;;)
|
|
{
|
|
ON_Group* group = nullptr;
|
|
rc = archive.Read3dmGroup(&group);
|
|
if ( rc==0 )
|
|
break; // end of group table
|
|
if (rc < 0)
|
|
break;
|
|
if (AddModelComponentForExperts(group, bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty())
|
|
delete group;
|
|
}
|
|
}
|
|
|
|
// If BeginRead3dmGroupTable() returns true,
|
|
// then you MUST call EndRead3dmGroupTable().
|
|
if ( !archive.EndRead3dmGroupTable() )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// STEP 11: REQUIRED - Read dimstyle table
|
|
if ( archive.BeginRead3dmDimStyleTable() )
|
|
{
|
|
const ON_UUID settings_current_id = m_settings.CurrentDimensionStyleId();
|
|
const int settings_current_index = m_settings.CurrentDimensionStyleIndex();
|
|
bool bSetCurrentById = !(ON_nil_uuid == settings_current_id);
|
|
bool bSetCurrentByIndex = false == bSetCurrentById && settings_current_index >= 0;
|
|
m_settings.SetCurrentDimensionStyleId(ON_nil_uuid);
|
|
|
|
if ( 0 != (static_cast<unsigned int>(ON_3dmArchiveTableType::dimension_style_table) & table_filter) )
|
|
{
|
|
for(;;)
|
|
{
|
|
ON_DimStyle* dimension_style = nullptr;
|
|
rc = archive.Read3dmDimStyle(&dimension_style);
|
|
if ( rc==0 )
|
|
break; // end of dimstyle table
|
|
if ( rc < 0 )
|
|
break;
|
|
// index or id might be modified by AddModelComponentForExperts()
|
|
const bool bSetAsCurrent =
|
|
(bSetCurrentById && settings_current_id == dimension_style->Id())
|
|
|| (bSetCurrentByIndex && settings_current_index == dimension_style->Index());
|
|
if (AddModelComponentForExperts(dimension_style, bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty())
|
|
{
|
|
delete dimension_style;
|
|
}
|
|
else if (bSetAsCurrent)
|
|
{
|
|
m_settings.SetCurrentDimensionStyleId(dimension_style->Id());
|
|
bSetCurrentById = false;
|
|
bSetCurrentByIndex = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If BeginRead3dmDimStyleTable() returns true,
|
|
// then you MUST call EndRead3dmDimStyleTable().
|
|
if ( !archive.EndRead3dmDimStyleTable() )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// STEP 12: REQUIRED - Read render lights table
|
|
if ( archive.BeginRead3dmLightTable() )
|
|
{
|
|
if ( 0 != (static_cast<unsigned int>(ON_3dmArchiveTableType::light_table) & table_filter) )
|
|
{
|
|
for(;;)
|
|
{
|
|
ON_ModelGeometryComponent* model_light = nullptr;
|
|
rc = archive.Read3dmModelLight(&model_light);
|
|
if (rc == 0)
|
|
break; // end of light table
|
|
if ( rc < 0 )
|
|
break;
|
|
if (AddModelComponentForExperts(model_light, bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty())
|
|
delete model_light;
|
|
}
|
|
}
|
|
|
|
// If BeginRead3dmLightTable() returns true,
|
|
// then you MUST call EndRead3dmLightTable().
|
|
if ( !archive.EndRead3dmLightTable() )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// STEP 13 - read hatch pattern table
|
|
if ( archive.BeginRead3dmHatchPatternTable() )
|
|
{
|
|
const ON_UUID settings_current_id = m_settings.CurrentHatchPatternId();
|
|
const int settings_current_index = ON_UNSET_INT_INDEX;
|
|
bool bSetCurrentById = !(ON_nil_uuid == settings_current_id);
|
|
bool bSetCurrentByIndex = false;
|
|
m_settings.SetCurrentHatchPatternId(ON_nil_uuid);
|
|
|
|
if ( 0 != (static_cast<unsigned int>(ON_3dmArchiveTableType::hatchpattern_table) & table_filter) )
|
|
{
|
|
for(;;)
|
|
{
|
|
ON_HatchPattern* hatch_pattern = nullptr;
|
|
rc = archive.Read3dmHatchPattern(&hatch_pattern);
|
|
if ( rc==0 )
|
|
break; // end of hatchpattern table
|
|
if ( rc < 0 )
|
|
break;
|
|
// index or id might be modified by AddModelComponentForExperts()
|
|
const bool bSetAsCurrent =
|
|
(bSetCurrentById && settings_current_id == hatch_pattern->Id())
|
|
|| (bSetCurrentByIndex && settings_current_index == hatch_pattern->Index());
|
|
if (AddModelComponentForExperts(hatch_pattern, bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty())
|
|
{
|
|
delete hatch_pattern;
|
|
}
|
|
else if (bSetAsCurrent)
|
|
{
|
|
m_settings.SetCurrentHatchPatternId(hatch_pattern->Id());
|
|
bSetCurrentById = false;
|
|
bSetCurrentByIndex = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If BeginRead3dmHatchPatternTable() returns true,
|
|
// then you MUST call EndRead3dmHatchPatternTable().
|
|
if ( !archive.EndRead3dmHatchPatternTable() )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// STEP 14: REQUIRED - Read instance definition table
|
|
if ( archive.BeginRead3dmInstanceDefinitionTable() )
|
|
{
|
|
if ( 0 != (static_cast<unsigned int>(ON_3dmArchiveTableType::instance_definition_table) & table_filter) )
|
|
{
|
|
for(;;)
|
|
{
|
|
ON_InstanceDefinition* instance_definition = nullptr;
|
|
rc = archive.Read3dmInstanceDefinition(&instance_definition);
|
|
if ( rc==0 )
|
|
break; // end of instance definition table
|
|
if ( rc < 0 )
|
|
break;
|
|
if (AddModelComponentForExperts(instance_definition, bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty())
|
|
delete instance_definition;
|
|
}
|
|
}
|
|
|
|
// If BeginRead3dmInstanceDefinitionTable() returns true,
|
|
// then you MUST call EndRead3dmInstanceDefinitionTable().
|
|
if ( !archive.EndRead3dmInstanceDefinitionTable() )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return (0 == archive.CriticalErrorCount());
|
|
}
|
|
|
|
bool ONX_Model::IncrementalReadModelGeometry(
|
|
ON_BinaryArchive& archive,
|
|
bool bManageModelGeometryComponent,
|
|
bool bManageGeometry,
|
|
bool bManageAttributes,
|
|
unsigned int object_filter,
|
|
ON_ModelComponentReference& model_component_reference
|
|
)
|
|
{
|
|
model_component_reference = ON_ModelComponentReference::Empty;
|
|
|
|
ON_3dmArchiveTableType active_table = archive.Active3dmTable();
|
|
if (ON_3dmArchiveTableType::Unset == active_table)
|
|
{
|
|
ON_3dmArchiveTableType previous_table = archive.Previous3dmTable();
|
|
if (ON_3dmArchiveTableType::Unset == previous_table)
|
|
{
|
|
// Yokel probably forgot to call IncrementalReadBegin()
|
|
ON_ERROR("IncrementalReadBegin() must be called before IncrementalReadModelGeometry().");
|
|
return false;
|
|
}
|
|
|
|
if (static_cast<unsigned int>(previous_table) >= static_cast<unsigned int>(ON_3dmArchiveTableType::object_table))
|
|
{
|
|
// Yokel either read or skipped reading the geometry table.
|
|
ON_ERROR("Too late to read the geometry table.");
|
|
return false;
|
|
}
|
|
|
|
if (false == archive.BeginRead3dmObjectTable())
|
|
{
|
|
ON_ERROR("Geometry table cannot be read from archive.");
|
|
return false;
|
|
}
|
|
active_table = archive.Active3dmTable();
|
|
if (active_table != ON_3dmArchiveTableType::object_table)
|
|
{
|
|
ON_ERROR("Catastrophic geometry table reading error.");
|
|
return false;
|
|
}
|
|
}
|
|
else if (active_table != ON_3dmArchiveTableType::object_table)
|
|
{
|
|
// Yokel is calling IncrementalReadModelGeometry() at the wrong time.
|
|
ON_ERROR("IncrementalReadModelGeometry() cannot be called while reading another part of the 3dm archive.");
|
|
return false;
|
|
}
|
|
|
|
ON_3dmArchiveTableStatus object_table_status = archive.Archive3dmTableStatus(ON_3dmArchiveTableType::object_table);
|
|
if (ON_3dmArchiveTableType::object_table != object_table_status.m_table_type)
|
|
{
|
|
ON_ERROR("Catastrophic geometry table reading error.");
|
|
return false;
|
|
}
|
|
|
|
switch(object_table_status.m_state)
|
|
{
|
|
case ON_3dmArchiveTableStatus::TableState::Started:
|
|
case ON_3dmArchiveTableStatus::TableState::InProgress:
|
|
break;
|
|
case ON_3dmArchiveTableStatus::TableState::Finished:
|
|
{
|
|
ON_ERROR("Geometry table has already been read from archive.");
|
|
return false;
|
|
}
|
|
default:
|
|
{
|
|
ON_ERROR("Geometry table reading error.");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
for(;;)
|
|
{
|
|
ON_ModelGeometryComponent* model_geometry = nullptr;
|
|
int rc = archive.Read3dmModelGeometryForExperts(bManageGeometry,bManageAttributes,&model_geometry,object_filter);
|
|
if ( rc <= 0 )
|
|
{
|
|
// end of object table or error reading
|
|
// If BeginRead3dmObjectTable() returns true,
|
|
// then you MUST call EndRead3dmObjectTable().
|
|
archive.EndRead3dmObjectTable();
|
|
return (0==rc);
|
|
}
|
|
|
|
if (2 == rc && 0 != object_filter)
|
|
{
|
|
if ( nullptr != model_geometry)
|
|
delete model_geometry;
|
|
continue; // item was intentionally skipped.
|
|
}
|
|
|
|
model_component_reference = AddModelComponentForExperts(model_geometry,bManageModelGeometryComponent,true,true);
|
|
|
|
if (model_component_reference.IsEmpty())
|
|
continue;
|
|
|
|
// return the read object.
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ONX_Model::IncrementalReadFinish(
|
|
ON_BinaryArchive& archive,
|
|
bool bManageComponents,
|
|
unsigned int table_filter,
|
|
ON_TextLog* error_log
|
|
)
|
|
{
|
|
int rc;
|
|
|
|
const bool bResolveIdAndNameConflicts = true;
|
|
const bool bUpdateComponentIdentification = true;
|
|
|
|
if ( 0 == table_filter )
|
|
table_filter = 0xFFFFFFFF; // read everything
|
|
|
|
// STEP 16: Read history table
|
|
if ( archive.BeginRead3dmHistoryRecordTable() )
|
|
{
|
|
if ( 0 != (static_cast<unsigned int>(ON_3dmArchiveTableType::historyrecord_table) & table_filter) )
|
|
{
|
|
for(;;)
|
|
{
|
|
ON_HistoryRecord* pHistoryRecord = nullptr;
|
|
rc = archive.Read3dmHistoryRecord(pHistoryRecord);
|
|
if ( rc == 0 )
|
|
break; // end of history record table
|
|
if ( rc < 0 )
|
|
break;
|
|
if ( AddModelComponentForExperts(pHistoryRecord,bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty() )
|
|
{
|
|
delete pHistoryRecord;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If BeginRead3dmHistoryRecordTable() returns true,
|
|
// then you MUST call EndRead3dmHistoryRecordTable().
|
|
if ( !archive.EndRead3dmHistoryRecordTable() )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (0 != archive.CriticalErrorCount())
|
|
return false;
|
|
|
|
// STEP 17: OPTIONAL - Read user tables as anonymous goo
|
|
// If you develop a plug-ins or application that uses OpenNURBS files,
|
|
// you can store anything you want in a user table.
|
|
for(;;)
|
|
{
|
|
if ( archive.Archive3dmVersion() <= 1 )
|
|
{
|
|
// no user tables in version 1 archives.
|
|
break;
|
|
}
|
|
|
|
{
|
|
ON__UINT32 tcode = 0;
|
|
ON__INT64 big_value = 0;
|
|
if ( !archive.PeekAt3dmBigChunkType(&tcode,&big_value) )
|
|
break;
|
|
if ( TCODE_USER_TABLE != tcode )
|
|
break;
|
|
}
|
|
ON_UUID plugin_id = ON_nil_uuid;
|
|
bool bGoo = false;
|
|
int usertable_3dm_version = 0;
|
|
unsigned int usertable_opennurbs_version = 0;
|
|
if ( !archive.BeginRead3dmUserTable( plugin_id, &bGoo, &usertable_3dm_version, &usertable_opennurbs_version ) )
|
|
{
|
|
// attempt to skip bogus user table
|
|
const ON__UINT64 pos0 = archive.CurrentPosition();
|
|
ON__UINT32 tcode = 0;
|
|
ON__INT64 big_value = 0;
|
|
if ( !archive.BeginRead3dmBigChunk(&tcode,&big_value) )
|
|
break;
|
|
if ( !archive.EndRead3dmChunk() )
|
|
break;
|
|
const ON__UINT64 pos1 = archive.CurrentPosition();
|
|
if (pos1 <= pos0)
|
|
break;
|
|
if ( TCODE_USER_TABLE != tcode )
|
|
break;
|
|
|
|
continue; // skip this bogus user table
|
|
}
|
|
|
|
if (
|
|
nullptr == m_model_user_string_list
|
|
&& plugin_id == ON_CLASS_ID(ON_DocumentUserStringList)
|
|
)
|
|
{
|
|
// Read the document user strings (key-value pairs) as
|
|
// a user table with plug-in id = ON_CLASS_ID(ON_DocumentUserStringList)
|
|
ON_Object* p = 0;
|
|
archive.ReadObject(&p);
|
|
m_model_user_string_list = ON_DocumentUserStringList::Cast(p);
|
|
if ( 0 == m_model_user_string_list )
|
|
{
|
|
ON_ERROR("The document user string information in the file is damaged.");
|
|
if ( 0 != p )
|
|
delete p;
|
|
p = 0;
|
|
}
|
|
}
|
|
else if ( 0 != (static_cast<unsigned int>(ON_3dmArchiveTableType::user_table) & table_filter) )
|
|
{
|
|
// read user data tables as anonymous goo
|
|
ONX_Model_UserData* model_ud = new ONX_Model_UserData();
|
|
model_ud->m_uuid = plugin_id;
|
|
model_ud->m_usertable_3dm_version = usertable_3dm_version;
|
|
model_ud->m_usertable_opennurbs_version = usertable_opennurbs_version;
|
|
if ( !archive.Read3dmAnonymousUserTable( usertable_3dm_version, usertable_opennurbs_version, model_ud->m_goo ) )
|
|
{
|
|
delete model_ud;
|
|
break;
|
|
}
|
|
m_userdata_table.Append(model_ud);
|
|
}
|
|
|
|
// If BeginRead3dmObjectTable() returns true,
|
|
// then you MUST call EndRead3dmUserTable().
|
|
if ( !archive.EndRead3dmUserTable() )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (0 != archive.CriticalErrorCount())
|
|
return false;
|
|
|
|
// STEP 18: OPTIONAL - check for end mark
|
|
size_t file_length = 0;
|
|
if (!archive.Read3dmEndMark(&file_length))
|
|
{
|
|
if (archive.Archive3dmVersion() != 1)
|
|
{
|
|
// some v1 files are missing end-of-archive markers
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_3dm_file_byte_count = file_length;
|
|
}
|
|
|
|
return (0 == archive.CriticalErrorCount());
|
|
}
|
|
|
|
|
|
bool ONX_Model::Read(
|
|
const char* filename,
|
|
unsigned int table_filter,
|
|
unsigned int model_object_type_filter,
|
|
ON_TextLog* error_log
|
|
)
|
|
{
|
|
const ON_wString wfilename_buffer(filename);
|
|
const wchar_t* wfilename = static_cast< const wchar_t* >(wfilename_buffer);
|
|
return Read(wfilename,table_filter,model_object_type_filter,error_log);
|
|
}
|
|
|
|
bool ONX_Model::Read(
|
|
const wchar_t* filename,
|
|
unsigned int table_filter,
|
|
unsigned int model_object_type_filter,
|
|
ON_TextLog* error_log
|
|
)
|
|
{
|
|
bool bCallReset = true;
|
|
bool rc = false;
|
|
|
|
if ( 0 != filename )
|
|
{
|
|
FILE* fp = ON::OpenFile(filename,L"rb");
|
|
if ( 0 != fp )
|
|
{
|
|
bCallReset = false;
|
|
ON_BinaryFile file(ON::archive_mode::read3dm,fp);
|
|
rc = Read(file, table_filter, model_object_type_filter, error_log);
|
|
ON::CloseFile(fp);
|
|
}
|
|
}
|
|
|
|
if ( bCallReset )
|
|
Reset();
|
|
|
|
return rc;
|
|
}
|
|
|
|
bool ONX_Model::Read(ON_BinaryArchive& archive, unsigned int table_filter,
|
|
unsigned int model_object_type_filter, ON_TextLog* error_log)
|
|
{
|
|
// STEPS 1 to 14: REQUIRED.
|
|
const bool bManageComponents = true;
|
|
IncrementalReadBegin(archive, bManageComponents, table_filter, error_log);
|
|
if (0 != archive.CriticalErrorCount())
|
|
return false;
|
|
|
|
// STEP 15: REQUIRED - Read object (geometry and annotation) table.
|
|
if (0 == (static_cast<unsigned int>(ON_3dmArchiveTableType::object_table) & table_filter))
|
|
{
|
|
const bool bManageGeometry = true;
|
|
const bool bManageAttributes = true;
|
|
for (;;)
|
|
{
|
|
ON_ModelComponentReference model_geometry_reference;
|
|
|
|
if (!IncrementalReadModelGeometry(archive, bManageComponents, bManageGeometry, bManageAttributes,
|
|
model_object_type_filter, model_geometry_reference))
|
|
{
|
|
// Catastrophic error.
|
|
break;
|
|
}
|
|
|
|
if (model_geometry_reference.IsEmpty())
|
|
break; // No more geometry.
|
|
}
|
|
|
|
if (0 != archive.CriticalErrorCount())
|
|
return false;
|
|
}
|
|
|
|
IncrementalReadFinish(archive, bManageComponents, table_filter, error_log);
|
|
if (0 != archive.CriticalErrorCount())
|
|
return false;
|
|
|
|
if (0 != archive.BadCRCCount())
|
|
return false;
|
|
|
|
// Having read the model data, populate the RDK components.
|
|
const int archive_3dm_version = archive.Archive3dmVersion();
|
|
m_private->PopulateRDKComponents(archive_3dm_version);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ONX_Model::Read(ON_BinaryArchive& archive, ON_TextLog* error_log)
|
|
{
|
|
unsigned int table_filter = 0; // read every table
|
|
unsigned int model_object_type_filter = 0; // read every type of object
|
|
return Read(archive, table_filter, model_object_type_filter, error_log);
|
|
}
|
|
|
|
bool ONX_Model::Write(const char* filename, int version, ON_TextLog* error_log) const
|
|
{
|
|
bool rc = false;
|
|
|
|
if (nullptr != filename && 0 != filename[0])
|
|
{
|
|
FILE* fp = ON::OpenFile(filename, "wb");
|
|
if (nullptr != fp)
|
|
{
|
|
ON_BinaryFile file(ON::archive_mode::write3dm, fp);
|
|
const ON_wString wFileName(filename);
|
|
file.SetArchiveFullPath(static_cast<const wchar_t*>(wFileName));
|
|
rc = Write(file, version, error_log);
|
|
ON::CloseFile(fp);
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
bool ONX_Model::Write(const wchar_t* filename, int version, ON_TextLog* error_log) const
|
|
{
|
|
bool rc = false;
|
|
|
|
if (nullptr != filename && 0 != filename[0])
|
|
{
|
|
FILE* fp = ON::OpenFile(filename, L"wb");
|
|
if (nullptr != fp)
|
|
{
|
|
ON_BinaryFile file(ON::archive_mode::write3dm, fp);
|
|
file.SetArchiveFullPath(filename);
|
|
rc = Write(file, version, error_log);
|
|
ON::CloseFile(fp);
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
bool ONX_Model::Write(ON_BinaryArchive& archive, int version, ON_TextLog* error_log) const
|
|
{
|
|
m_private->UpdateRDKUserData(version);
|
|
|
|
if ( 0 != version )
|
|
{
|
|
if ( version < 2
|
|
|| version > ON_BinaryArchive::CurrentArchiveVersion()
|
|
|| (version < 50 && version > ON_BinaryArchive::CurrentArchiveVersion()/10)
|
|
|| (version >= 50 && 0 != (version % 10))
|
|
)
|
|
{
|
|
version = 0;
|
|
if ( error_log) error_log->Print("ONX_Model::Write version parameter = %d; it must be 0, or >= 2 and <= %d, or a multiple of 10 >= 50 and <= %d.\n",
|
|
version,ON_BinaryArchive::CurrentArchiveVersion()/10,ON_BinaryArchive::CurrentArchiveVersion());
|
|
}
|
|
}
|
|
|
|
if ( !archive.WriteMode() )
|
|
{
|
|
// You passed in a bogus archive. You must pass ON::archive_mode::write3dm to the archive constructor.
|
|
if ( error_log) error_log->Print("ONX_Model::Write archive.Mode() is not ON::archive_mode::write3dm.\n"
|
|
"See ONX_Model::Write example in the header file.\n");
|
|
return false;
|
|
}
|
|
|
|
bool ok;
|
|
|
|
// START SECTION
|
|
ok = archive.Write3dmStartSection( version, static_cast< const char* >(m_sStartSectionComments) );
|
|
if ( !ok )
|
|
{
|
|
// make sure your archive was created with ON::archive_mode::write3dm mode.
|
|
if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmStartSection() failed.\n"
|
|
"Your archive is not properly initialized\n"
|
|
"(make sure you passed ON::archive_mode::write3dm to the constructor),\n"
|
|
"a file is locked, a disk is locked, or something along those lines.\n");
|
|
return false;
|
|
}
|
|
|
|
// PROPERTIES SECTION
|
|
if ( m_properties.m_RevisionHistory.m_revision_count == 0 )
|
|
const_cast<ONX_Model*>(this)->m_properties.m_RevisionHistory.NewRevision();
|
|
|
|
ok = archive.Write3dmProperties( m_properties );
|
|
if ( !ok )
|
|
{
|
|
// make sure m_properties is valid
|
|
if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmProperties() failed.\n"
|
|
"Your m_properties information is not valid or basic file writing failed.\n"
|
|
);
|
|
return false;
|
|
}
|
|
|
|
// SETTINGS SECTION
|
|
ok = archive.Write3dmSettings( m_settings );
|
|
if ( !ok )
|
|
{
|
|
// make sure m_settings is valid
|
|
if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmSettings() failed.\n"
|
|
"Your m_settings information is not valid or basic file writing failed.\n");
|
|
return false;
|
|
}
|
|
|
|
// BITMAP TABLE
|
|
ok = archive.BeginWrite3dmBitmapTable();
|
|
if ( !ok )
|
|
{
|
|
if ( error_log) error_log->Print("ONX_Model::Write archive.BeginWrite3dmBitmapTable() failed.\n");
|
|
return false;
|
|
}
|
|
|
|
for(
|
|
class ONX_ModelComponentReferenceLink* link = Internal_ComponentListConst(ON_ModelComponent::Type::Image).m_first_mcr_link;
|
|
nullptr != link && ok;
|
|
link = link->m_next
|
|
)
|
|
{
|
|
ok = archive.Write3dmImageComponent(link->m_mcr);
|
|
if ( !ok )
|
|
{
|
|
if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmImageComponent() failed.\n");
|
|
}
|
|
}
|
|
|
|
if ( !archive.EndWrite3dmBitmapTable() )
|
|
{
|
|
if ( error_log) error_log->Print("ONX_Model::Write archive.EndWrite3dmBitmapTable() failed.\n");
|
|
return false;
|
|
}
|
|
if (!ok)
|
|
return false;
|
|
|
|
|
|
// TEXTURE MAPPING TABLE
|
|
if ( archive.Archive3dmVersion() >= 4 )
|
|
{
|
|
ok = archive.BeginWrite3dmTextureMappingTable();
|
|
if ( !ok )
|
|
{
|
|
if ( error_log) error_log->Print("ONX_Model::Write archive.BeginWrite3dmTextureMappingTable() failed.\n");
|
|
return false;
|
|
}
|
|
|
|
for(
|
|
class ONX_ModelComponentReferenceLink* link = Internal_ComponentListConst(ON_ModelComponent::Type::TextureMapping).m_first_mcr_link;
|
|
nullptr != link && ok;
|
|
link = link->m_next
|
|
)
|
|
{
|
|
ok = archive.Write3dmTextureMappingComponent(link->m_mcr);
|
|
if ( !ok )
|
|
{
|
|
if ( error_log) error_log->Print("ONX_Model::Write archive.TextureMapping() failed.\n");
|
|
}
|
|
}
|
|
|
|
if ( !archive.EndWrite3dmTextureMappingTable() )
|
|
{
|
|
if ( error_log) error_log->Print("ONX_Model::Write archive.EndWrite3dmTextureMappingTable() failed.\n");
|
|
return false;
|
|
}
|
|
if (!ok)
|
|
return false;
|
|
}
|
|
|
|
// MATERIAL TABLE
|
|
ok = archive.BeginWrite3dmMaterialTable();
|
|
if ( !ok )
|
|
{
|
|
if ( error_log) error_log->Print("ONX_Model::Write archive.BeginWrite3dmMaterialTable() failed.\n");
|
|
return false;
|
|
}
|
|
|
|
for(
|
|
class ONX_ModelComponentReferenceLink* link = Internal_ComponentListConst(ON_ModelComponent::Type::RenderMaterial).m_first_mcr_link;
|
|
nullptr != link && ok;
|
|
link = link->m_next
|
|
)
|
|
{
|
|
ok = archive.Write3dmMaterialComponent(link->m_mcr);
|
|
if ( !ok )
|
|
{
|
|
if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmMaterialComponent() failed.\n");
|
|
}
|
|
}
|
|
|
|
if ( !archive.EndWrite3dmMaterialTable() )
|
|
{
|
|
if ( error_log) error_log->Print("ONX_Model::Write archive.EndWrite3dmMaterialTable() failed.\n");
|
|
return false;
|
|
}
|
|
if (!ok)
|
|
return false;
|
|
|
|
// LINETYPE TABLE
|
|
if ( archive.Archive3dmVersion() >= 4 )
|
|
{
|
|
ok = archive.BeginWrite3dmLinetypeTable();
|
|
if ( !ok )
|
|
{
|
|
if ( error_log) error_log->Print("ONX_Model::Write archive.BeginWrite3dmLinetypeTable() failed.\n");
|
|
return false;
|
|
}
|
|
for(
|
|
class ONX_ModelComponentReferenceLink* link = Internal_ComponentListConst(ON_ModelComponent::Type::LinePattern).m_first_mcr_link;
|
|
nullptr != link && ok;
|
|
link = link->m_next
|
|
)
|
|
{
|
|
ok = archive.Write3dmLinePatternComponent(link->m_mcr);
|
|
if ( !ok )
|
|
{
|
|
if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmLinePatternComponent() failed.\n");
|
|
}
|
|
}
|
|
if ( !archive.EndWrite3dmLinetypeTable() )
|
|
{
|
|
if ( error_log) error_log->Print("ONX_Model::Write archive.EndWrite3dmLinetypeTable() failed.\n");
|
|
return false;
|
|
}
|
|
if (!ok)
|
|
return false;
|
|
}
|
|
|
|
// LAYER TABLE
|
|
ok = archive.BeginWrite3dmLayerTable();
|
|
if ( !ok )
|
|
{
|
|
// make sure m_settings is valid
|
|
if ( error_log) error_log->Print("ONX_Model::Write archive.BeginWrite3dmLayerTable() failed.\n");
|
|
return false;
|
|
}
|
|
unsigned int layer_count = 0;
|
|
for(
|
|
class ONX_ModelComponentReferenceLink* link = Internal_ComponentListConst(ON_ModelComponent::Type::Layer).m_first_mcr_link;
|
|
nullptr != link && ok;
|
|
link = link->m_next
|
|
)
|
|
{
|
|
ok = archive.Write3dmLayerComponent(link->m_mcr);
|
|
if (!ok)
|
|
{
|
|
if (error_log) error_log->Print("ONX_Model::Write archive.Write3dmLayerComponent() failed.\n");
|
|
}
|
|
else
|
|
layer_count++;
|
|
}
|
|
if (0 == layer_count && ok)
|
|
{
|
|
ON_Layer layer(ON_Layer::Default);
|
|
layer.SetId();
|
|
layer.SetIndex(0);
|
|
ok = archive.Write3dmLayer(layer);
|
|
if (!ok)
|
|
{
|
|
if (error_log) error_log->Print("ONX_Model::Write archive.Write3dmLayer() failed.\n");
|
|
}
|
|
}
|
|
if ( !archive.EndWrite3dmLayerTable() )
|
|
{
|
|
if ( error_log) error_log->Print("ONX_Model::Write archive.EndWrite3dmLayerTable() failed.\n");
|
|
return false;
|
|
}
|
|
if (!ok)
|
|
return false;
|
|
|
|
// GROUP TABLE
|
|
ok = archive.BeginWrite3dmGroupTable();
|
|
if ( !ok )
|
|
{
|
|
// make sure m_settings is valid
|
|
if ( error_log) error_log->Print("ONX_Model::Write archive.BeginWrite3dmGroupTable() failed.\n");
|
|
return false;
|
|
}
|
|
for(
|
|
class ONX_ModelComponentReferenceLink* link = Internal_ComponentListConst(ON_ModelComponent::Type::Group).m_first_mcr_link;
|
|
nullptr != link && ok;
|
|
link = link->m_next
|
|
)
|
|
{
|
|
ok = archive.Write3dmGroupComponent(link->m_mcr);
|
|
if ( !ok )
|
|
{
|
|
if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmGroupComponent() failed.\n");
|
|
}
|
|
}
|
|
if ( !archive.EndWrite3dmGroupTable() )
|
|
{
|
|
if ( error_log) error_log->Print("ONX_Model::Write archive.EndWrite3dmGroupTable() failed.\n");
|
|
return false;
|
|
}
|
|
if (!ok)
|
|
return false;
|
|
|
|
// DIMSTYLE TABLE
|
|
if ( archive.Archive3dmVersion() >= 3 )
|
|
{
|
|
ok = archive.BeginWrite3dmDimStyleTable();
|
|
if ( !ok )
|
|
{
|
|
// make sure m_settings is valid
|
|
if ( error_log) error_log->Print("ONX_Model::Write archive.BeginWrite3dmDimStyleTable() failed.\n");
|
|
return false;
|
|
}
|
|
for(
|
|
class ONX_ModelComponentReferenceLink* link = Internal_ComponentListConst(ON_ModelComponent::Type::DimStyle).m_first_mcr_link;
|
|
nullptr != link;
|
|
link = link->m_next
|
|
)
|
|
{
|
|
ok = archive.Write3dmDimStyleComponent(link->m_mcr);
|
|
if (!ok)
|
|
{
|
|
if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmDimStyleComponent() failed.\n");
|
|
}
|
|
}
|
|
if ( !archive.EndWrite3dmDimStyleTable() )
|
|
{
|
|
if ( error_log) error_log->Print("ONX_Model::Write archive.EndWrite3dmDimStyleTable() failed.\n");
|
|
return false;
|
|
}
|
|
if (!ok)
|
|
return false;
|
|
}
|
|
|
|
// LIGHT TABLE
|
|
ok = archive.BeginWrite3dmLightTable();
|
|
if ( !ok )
|
|
{
|
|
// make sure m_settings is valid
|
|
if ( error_log) error_log->Print("ONX_Model::Write archive.BeginWrite3dmLightTable() failed.\n");
|
|
return false;
|
|
}
|
|
for(
|
|
class ONX_ModelComponentReferenceLink* link = Internal_ComponentListConst(ON_ModelComponent::Type::RenderLight).m_first_mcr_link;
|
|
nullptr != link;
|
|
link = link->m_next
|
|
)
|
|
{
|
|
ok = archive.Write3dmModelLightComponent(link->m_mcr);
|
|
if ( !ok )
|
|
{
|
|
if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmModelLightComponent() failed.\n");
|
|
}
|
|
}
|
|
if ( !archive.EndWrite3dmLightTable() )
|
|
{
|
|
if ( error_log) error_log->Print("ONX_Model::Write archive.EndWrite3dmLightTable() failed.\n");
|
|
return false;
|
|
}
|
|
if (!ok)
|
|
return false;
|
|
|
|
// HATCH PATTERN TABLE
|
|
if ( archive.Archive3dmVersion() >= 4 )
|
|
{
|
|
ok = archive.BeginWrite3dmHatchPatternTable();
|
|
if ( !ok )
|
|
{
|
|
if ( error_log) error_log->Print("ONX_Model::Write archive.BeginWrite3dmHatchPatternTable() failed.\n");
|
|
return false;
|
|
}
|
|
|
|
for(
|
|
class ONX_ModelComponentReferenceLink* link = Internal_ComponentListConst(ON_ModelComponent::Type::HatchPattern).m_first_mcr_link;
|
|
nullptr != link;
|
|
link = link->m_next
|
|
)
|
|
{
|
|
ok = archive.Write3dmHatchPatternComponent(link->m_mcr);
|
|
if ( !ok )
|
|
{
|
|
if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmHatchPatternComponent() failed.\n");
|
|
}
|
|
}
|
|
|
|
if ( !archive.EndWrite3dmHatchPatternTable() )
|
|
{
|
|
if ( error_log) error_log->Print("ONX_Model::Write archive.EndWrite3dmHatchPatternTable() failed.\n");
|
|
return false;
|
|
}
|
|
if (!ok)
|
|
return false;
|
|
}
|
|
|
|
// INSTANCE DEFINITION TABLE
|
|
if ( archive.Archive3dmVersion() >= 3 )
|
|
{
|
|
ok = archive.BeginWrite3dmInstanceDefinitionTable();
|
|
if ( !ok )
|
|
{
|
|
// make sure m_settings is valid
|
|
if ( error_log) error_log->Print("ONX_Model::Write archive.BeginWrite3dmInstanceDefinitionTable() failed.\n");
|
|
return false;
|
|
}
|
|
for(
|
|
class ONX_ModelComponentReferenceLink* link = Internal_ComponentListConst(ON_ModelComponent::Type::InstanceDefinition).m_first_mcr_link;
|
|
nullptr != link;
|
|
link = link->m_next
|
|
)
|
|
{
|
|
ok = archive.Write3dmInstanceDefinitionComponent(link->m_mcr);
|
|
if ( !ok )
|
|
{
|
|
if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmInstanceDefinitionComponent() failed.\n");
|
|
}
|
|
}
|
|
if ( !archive.EndWrite3dmInstanceDefinitionTable() )
|
|
{
|
|
if ( error_log) error_log->Print("ONX_Model::Write archive.EndWrite3dmInstanceDefinitionTable() failed.\n");
|
|
return false;
|
|
}
|
|
if (!ok)
|
|
return false;
|
|
}
|
|
|
|
// OBJECT TABLE
|
|
ok = archive.BeginWrite3dmObjectTable();
|
|
if ( !ok )
|
|
{
|
|
if ( error_log) error_log->Print("ONX_Model::Write archive.BeginWrite3dmObjectTable() failed.\n");
|
|
return false;
|
|
}
|
|
for(
|
|
class ONX_ModelComponentReferenceLink* link = Internal_ComponentListConst(ON_ModelComponent::Type::ModelGeometry).m_first_mcr_link;
|
|
nullptr != link;
|
|
link = link->m_next
|
|
)
|
|
{
|
|
ok = archive.Write3dmModelGeometryComponent(link->m_mcr);
|
|
if ( !ok )
|
|
{
|
|
if ( error_log)
|
|
error_log->Print("ONX_Model::Write archive.Write3dmModelGeometryComponent() failed.\n");
|
|
}
|
|
}
|
|
if ( !archive.EndWrite3dmObjectTable() )
|
|
{
|
|
if ( error_log)
|
|
error_log->Print("ONX_Model::Write archive.EndWrite3dmObjectTable() failed.\n");
|
|
return false;
|
|
}
|
|
if (!ok)
|
|
return false;
|
|
|
|
// HISTORY RECORD TABLE
|
|
if ( archive.Archive3dmVersion() >= 4 )
|
|
{
|
|
ok = archive.BeginWrite3dmHistoryRecordTable();
|
|
if ( !ok )
|
|
{
|
|
if ( error_log) error_log->Print("ONX_Model::Write archive.BeginWrite3dmHistoryRecordTable() failed.\n");
|
|
return false;
|
|
}
|
|
|
|
for(
|
|
class ONX_ModelComponentReferenceLink* link = Internal_ComponentListConst(ON_ModelComponent::Type::HistoryRecord).m_first_mcr_link;
|
|
nullptr != link;
|
|
link = link->m_next
|
|
)
|
|
{
|
|
ok = archive.Write3dmHistoryRecordComponent(link->m_mcr);
|
|
if ( !ok )
|
|
{
|
|
if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmistoryRecordComponent() failed.\n");
|
|
}
|
|
}
|
|
|
|
|
|
if( !archive.EndWrite3dmHistoryRecordTable() )
|
|
{
|
|
if ( error_log) error_log->Print("ONX_Model::Write archive.EndWrite3dmHistoryTable() failed.\n");
|
|
return false;
|
|
}
|
|
if (!ok)
|
|
return false;
|
|
}
|
|
|
|
// STEP 17: - write user tables (plug-in info, etc.)
|
|
if (archive.ArchiveContains3dmTable(ON_3dmArchiveTableType::user_table))
|
|
{
|
|
if (nullptr != m_model_user_string_list && m_model_user_string_list->UserStringCount() > 0)
|
|
{
|
|
// Write the document user strings (key-value pairs) as
|
|
// a user table with plug-in id =
|
|
ON_UUID model_user_string_plugin_id = ON_CLASS_ID(ON_DocumentUserStringList);
|
|
if (archive.BeginWrite3dmUserTable(model_user_string_plugin_id, false, 0, 0))
|
|
{
|
|
archive.WriteObject(m_model_user_string_list);
|
|
archive.EndWrite3dmUserTable();
|
|
}
|
|
}
|
|
|
|
// USER DATA TABLE
|
|
for (int i = 0; ok && i < m_userdata_table.Count(); i++)
|
|
{
|
|
const ONX_Model_UserData* model_ud = m_userdata_table[i];
|
|
if (nullptr == model_ud)
|
|
continue;
|
|
if (ON_UuidIsNotNil(model_ud->m_uuid))
|
|
{
|
|
if (!archive.Write3dmAnonymousUserTableRecord(
|
|
model_ud->m_uuid,
|
|
model_ud->m_usertable_3dm_version,
|
|
model_ud->m_usertable_opennurbs_version,
|
|
model_ud->m_goo)
|
|
)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( !archive.Write3dmEndMark() )
|
|
{
|
|
ok = false;
|
|
if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmEndMark() failed.\n");
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
int ONX_Model::UsesIDef(
|
|
const ON_InstanceRef& iref,
|
|
ON_UUID idef_uuid
|
|
) const
|
|
{
|
|
// get id of idef we are looking for
|
|
if ( ON_UuidIsNil(idef_uuid) )
|
|
return 0;
|
|
|
|
// id of idef that defines iref
|
|
ON_UUID iref_idef_uuid = iref.m_instance_definition_uuid;
|
|
if ( 0 == ON_UuidCompare( idef_uuid, iref_idef_uuid ) )
|
|
return 1;
|
|
|
|
ON_ModelComponentReference idef_reference = ComponentFromId(ON_ModelComponent::Type::InstanceDefinition,iref_idef_uuid);
|
|
const ON_InstanceDefinition* iref_idef = ON_InstanceDefinition::FromModelComponentRef(idef_reference,nullptr);
|
|
|
|
if ( nullptr == iref_idef )
|
|
return -1; // invalid id.
|
|
|
|
|
|
// set iref_list[] = list of all nested instance references in iref_idef.
|
|
ON_SimpleArray<const ON_InstanceRef*> iref_list(256);
|
|
const ON_SimpleArray<ON_UUID>& iref_idef_object_uuid = iref_idef->InstanceGeometryIdList();
|
|
for ( unsigned j = 0; j < iref_idef_object_uuid.UnsignedCount(); j++ )
|
|
{
|
|
ON_ModelComponentReference component_reference = ComponentFromId(ON_ModelComponent::Type::InstanceDefinition,iref_idef_object_uuid[j]);
|
|
const ON_ModelGeometryComponent* model_geometry = ON_ModelGeometryComponent::Cast(component_reference.ModelComponent());
|
|
if ( nullptr == model_geometry )
|
|
continue;
|
|
const ON_Geometry* geometry = model_geometry->Geometry(nullptr);
|
|
if ( nullptr == geometry )
|
|
continue;
|
|
if ( geometry->ObjectType() != ON::instance_reference )
|
|
continue;
|
|
const ON_InstanceRef* pNestedIRef = ON_InstanceRef::Cast(geometry);
|
|
if ( nullptr == pNestedIRef )
|
|
continue;
|
|
if ( 0 == ON_UuidCompare( idef_uuid, pNestedIRef->m_instance_definition_uuid ) )
|
|
return 2;
|
|
iref_list.Append(pNestedIRef);
|
|
}
|
|
|
|
// test the nested instance references to see if they use idef_index.
|
|
unsigned int i1 = 0;
|
|
int depth = 3;
|
|
for ( depth=3; i1 < iref_list.UnsignedCount(); depth++ )
|
|
{
|
|
const unsigned int i0 = i1;
|
|
i1 = iref_list.UnsignedCount();
|
|
for ( unsigned int i = i0; i < i1; i++ )
|
|
{
|
|
const ON_InstanceRef* pNestedIRef = iref_list[i];
|
|
if ( nullptr == pNestedIRef )
|
|
continue;
|
|
ON_ModelComponentReference nested_idef_reference = ComponentFromId(ON_ModelComponent::Type::InstanceDefinition,pNestedIRef->m_instance_definition_uuid);
|
|
const ON_InstanceDefinition* nested_idef = ON_InstanceDefinition::FromModelComponentRef(nested_idef_reference,nullptr);
|
|
if ( nullptr == nested_idef )
|
|
continue;
|
|
const ON_SimpleArray<ON_UUID>& nested_idef_object_uuid = nested_idef->InstanceGeometryIdList();
|
|
for ( unsigned int j = 0; j < nested_idef_object_uuid.UnsignedCount(); j++ )
|
|
{
|
|
ON_ModelComponentReference component_reference = ComponentFromId(ON_ModelComponent::Type::InstanceDefinition,nested_idef_object_uuid[j]);
|
|
const ON_ModelGeometryComponent* model_geometry = ON_ModelGeometryComponent::Cast(component_reference.ModelComponent());
|
|
if ( nullptr == model_geometry )
|
|
continue;
|
|
const ON_Geometry* geometry = model_geometry->Geometry(nullptr);
|
|
if ( nullptr == geometry )
|
|
continue;
|
|
if ( geometry->ObjectType() != ON::instance_reference )
|
|
continue;
|
|
const ON_InstanceRef* pNestedIRef_local = ON_InstanceRef::Cast(geometry);
|
|
if ( nullptr == pNestedIRef_local )
|
|
continue;
|
|
if ( 0 == ON_UuidCompare( idef_uuid, pNestedIRef_local->m_instance_definition_uuid ) )
|
|
return depth;
|
|
iref_list.Append(pNestedIRef_local);
|
|
}
|
|
}
|
|
if ( i1 > 10000 && i1 < iref_list.UnsignedCount() )
|
|
return -2; // probably have a circular reference
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
ON__UINT64 ONX_Model::ModelContentVersionNumber() const
|
|
{
|
|
return m_private->m_model_content_version_number;
|
|
}
|
|
|
|
void ONX_Model::Internal_IncrementModelContentVersionNumber()
|
|
{
|
|
m_private->m_model_content_version_number++;
|
|
}
|
|
|
|
bool ONX_Model::SetDocumentUserString( const wchar_t* key, const wchar_t* string_value )
|
|
{
|
|
if (nullptr == key || 0 == key[0] )
|
|
return false;
|
|
if ( nullptr == m_model_user_string_list )
|
|
m_model_user_string_list = new ON_DocumentUserStringList();
|
|
return m_model_user_string_list->SetUserString(key,string_value);
|
|
}
|
|
|
|
bool ONX_Model::GetDocumentUserString(
|
|
const wchar_t* key,
|
|
ON_wString& string_value
|
|
) const
|
|
{
|
|
if ( nullptr != m_model_user_string_list )
|
|
return m_model_user_string_list->GetUserString(key,string_value);
|
|
string_value = ON_wString::EmptyString;
|
|
return false;
|
|
}
|
|
|
|
int ONX_Model::GetDocumentUserStrings(
|
|
ON_ClassArray<ON_UserString>& user_strings
|
|
) const
|
|
{
|
|
if ( nullptr != m_model_user_string_list )
|
|
return m_model_user_string_list->GetUserStrings(user_strings);
|
|
return 0;
|
|
}
|
|
|
|
ONX_ModelComponentIterator::ONX_ModelComponentIterator(
|
|
const ONX_Model& model,
|
|
ON_ModelComponent::Type component_type
|
|
)
|
|
: m_component_type(component_type)
|
|
, m_model(&model)
|
|
{}
|
|
|
|
const ONX_Model* ONX_ModelComponentIterator::Model() const
|
|
{
|
|
return m_model;
|
|
}
|
|
|
|
const class ONX_Model::ONX_ModelComponentList* ONX_ModelComponentIterator::Internal_List() const
|
|
{
|
|
if (nullptr != m_list)
|
|
return m_list;
|
|
|
|
if ( nullptr == m_model )
|
|
return nullptr;
|
|
|
|
m_list = &m_model->Internal_ComponentListConst(m_component_type);
|
|
|
|
return m_list;
|
|
}
|
|
|
|
void ONX_ModelComponentIterator::Internal_SetLink(
|
|
const class ONX_ModelComponentReferenceLink* link
|
|
) const
|
|
{
|
|
// m_model is never nullptr when this function is called
|
|
m_model_content_version = m_model->ModelContentVersionNumber();
|
|
m_link = link;
|
|
if (nullptr == m_link)
|
|
{
|
|
m_current_component_sn = 0;
|
|
m_next_component_sn = 0;
|
|
m_prev_component_sn = 0;
|
|
m_current_component_weak_ref = ON_ModelComponentWeakReference::Empty;
|
|
m_model_content_version = 0;
|
|
}
|
|
else
|
|
{
|
|
m_current_component_sn = link->m_sn;
|
|
m_next_component_sn = (nullptr != link->m_next) ? link->m_next->m_sn : 0;
|
|
m_prev_component_sn = (nullptr != link->m_prev) ? link->m_prev->m_sn : 0;
|
|
m_current_component_weak_ref = link->m_mcr;
|
|
}
|
|
}
|
|
|
|
void ONX_ModelComponentIterator::Internal_SetLink(
|
|
ON__UINT64 model_component_sn
|
|
) const
|
|
{
|
|
}
|
|
|
|
|
|
ON_ModelComponentReference ONX_ModelComponentIterator::FirstComponentReference()
|
|
{
|
|
return ON_ModelComponentReference(FirstComponentWeakReference());
|
|
}
|
|
|
|
ON_ModelComponentReference ONX_ModelComponentIterator::LastComponentReference()
|
|
{
|
|
return ON_ModelComponentReference(LastComponentWeakReference());
|
|
}
|
|
|
|
ON_ModelComponentReference ONX_ModelComponentIterator::CurrentComponentReference() const
|
|
{
|
|
return ON_ModelComponentReference(CurrentComponentWeakReference());
|
|
}
|
|
|
|
ON_ModelComponentReference ONX_ModelComponentIterator::NextComponentReference()
|
|
{
|
|
return ON_ModelComponentReference(NextComponentWeakReference());
|
|
}
|
|
|
|
ON_ModelComponentReference ONX_ModelComponentIterator::PreviousComponentReference()
|
|
{
|
|
return ON_ModelComponentReference(PreviousComponentWeakReference());
|
|
}
|
|
|
|
|
|
|
|
ON_ModelComponentWeakReference ONX_ModelComponentIterator::FirstComponentWeakReference()
|
|
{
|
|
const ONX_Model::ONX_ModelComponentList* list = Internal_List();
|
|
Internal_SetLink((nullptr != list) ? list->m_first_mcr_link : nullptr);
|
|
return m_current_component_weak_ref;
|
|
}
|
|
|
|
ON_ModelComponentWeakReference ONX_ModelComponentIterator::LastComponentWeakReference()
|
|
{
|
|
const ONX_Model::ONX_ModelComponentList* list = Internal_List();
|
|
Internal_SetLink((nullptr != list) ? list->m_last_mcr_link : nullptr);
|
|
return m_current_component_weak_ref;
|
|
}
|
|
|
|
ON_ModelComponentWeakReference ONX_ModelComponentIterator::CurrentComponentWeakReference() const
|
|
{
|
|
// unchanged ModelContentVersionNumber() means that m_link is safe to dereference.
|
|
// Otherwise use sn for safe reset.
|
|
if (m_model_content_version != m_model->ModelContentVersionNumber() )
|
|
Internal_SetLink(m_model->Internal_ModelComponentLinkFromSerialNumber(m_current_component_sn));
|
|
return m_current_component_weak_ref;
|
|
}
|
|
|
|
ON_ModelComponentWeakReference ONX_ModelComponentIterator::NextComponentWeakReference()
|
|
{
|
|
if ( nullptr == m_list )
|
|
return FirstComponentReference();
|
|
|
|
if ( nullptr == m_link )
|
|
return ON_ModelComponentReference::Empty;
|
|
|
|
if (m_model_content_version == m_model->ModelContentVersionNumber() && nullptr != m_link )
|
|
{
|
|
// unchanged ModelContentVersionNumber() means that m_link is safe to dereference.
|
|
m_link = m_link->m_next;
|
|
if (nullptr == m_link)
|
|
{
|
|
m_prev_component_sn = m_current_component_sn;
|
|
m_current_component_sn = 0;
|
|
m_next_component_sn = 0;
|
|
m_current_component_weak_ref = ON_ModelComponentWeakReference::Empty;
|
|
}
|
|
else
|
|
{
|
|
m_current_component_sn = m_link->m_sn;
|
|
m_next_component_sn = (nullptr != m_link->m_next) ? m_link->m_next->m_sn : 0;
|
|
m_prev_component_sn = (nullptr != m_link->m_prev) ? m_link->m_prev->m_sn : 0;
|
|
m_current_component_weak_ref = m_link->m_mcr;
|
|
}
|
|
}
|
|
else if ( 0 != m_next_component_sn )
|
|
{
|
|
// Otherwise m_link is not safe to dereference.
|
|
// Use slower serial number lookup.
|
|
Internal_SetLink(m_model->Internal_ModelComponentLinkFromSerialNumber(m_next_component_sn));
|
|
}
|
|
else
|
|
{
|
|
m_link = nullptr;
|
|
m_current_component_sn = 0;
|
|
m_current_component_weak_ref = ON_ModelComponentWeakReference::Empty;
|
|
}
|
|
|
|
return m_current_component_weak_ref;
|
|
}
|
|
|
|
ON_ModelComponentWeakReference ONX_ModelComponentIterator::PreviousComponentWeakReference()
|
|
{
|
|
if ( nullptr == m_list )
|
|
return LastComponentReference();
|
|
|
|
if ( nullptr == m_link )
|
|
return ON_ModelComponentReference::Empty;
|
|
|
|
if (m_model_content_version == m_model->ModelContentVersionNumber() && nullptr != m_link )
|
|
{
|
|
m_link = m_link->m_prev;
|
|
if (nullptr == m_link)
|
|
{
|
|
m_next_component_sn = m_current_component_sn;
|
|
m_current_component_sn = 0;
|
|
m_prev_component_sn = 0;
|
|
m_current_component_weak_ref = ON_ModelComponentWeakReference::Empty;
|
|
}
|
|
else
|
|
{
|
|
m_current_component_sn = m_link->m_sn;
|
|
m_next_component_sn = (nullptr != m_link->m_next) ? m_link->m_next->m_sn : 0;
|
|
m_prev_component_sn = (nullptr != m_link->m_prev) ? m_link->m_prev->m_sn : 0;
|
|
m_current_component_weak_ref = m_link->m_mcr;
|
|
}
|
|
}
|
|
else if ( 0 != m_prev_component_sn )
|
|
{
|
|
Internal_SetLink(m_model->Internal_ModelComponentLinkFromSerialNumber(m_prev_component_sn));
|
|
}
|
|
else
|
|
{
|
|
m_link = nullptr;
|
|
m_current_component_sn = 0;
|
|
m_current_component_weak_ref = ON_ModelComponentReference::Empty;
|
|
}
|
|
|
|
return m_current_component_weak_ref;
|
|
}
|
|
|
|
const ON_ModelComponent* ONX_ModelComponentIterator::FirstComponent()
|
|
{
|
|
return FirstComponentReference().ModelComponent();
|
|
}
|
|
|
|
const ON_ModelComponent* ONX_ModelComponentIterator::LastComponent()
|
|
{
|
|
return LastComponentReference().ModelComponent();
|
|
}
|
|
|
|
const ON_ModelComponent* ONX_ModelComponentIterator::CurrentComponent() const
|
|
{
|
|
return CurrentComponentReference().ModelComponent();
|
|
}
|
|
|
|
const ON_ModelComponent* ONX_ModelComponentIterator::NextComponent()
|
|
{
|
|
return NextComponentReference().ModelComponent();
|
|
}
|
|
|
|
const ON_ModelComponent* ONX_ModelComponentIterator::PreviousComponent()
|
|
{
|
|
return PreviousComponentReference().ModelComponent();
|
|
}
|
|
|
|
unsigned int ONX_ModelComponentIterator::ActiveComponentCount() const
|
|
{
|
|
return (nullptr != m_list) ? m_list->m_count : 0;
|
|
}
|
|
|
|
class ON_TextLogNull : public ON_TextLog
|
|
{
|
|
public:
|
|
ON_TextLogNull() = default;
|
|
~ON_TextLogNull() = default;
|
|
|
|
void AppendText(const char*) override {}
|
|
void AppendText(const wchar_t*) override {}
|
|
};
|
|
|
|
const char* ONX_ModelTest::TestTypeToString(ONX_ModelTest::Type test_type)
|
|
{
|
|
switch (test_type)
|
|
{
|
|
case ONX_ModelTest::Type::Unset:
|
|
return "Unset";
|
|
break;
|
|
case ONX_ModelTest::Type::Read:
|
|
return "Read";
|
|
break;
|
|
case ONX_ModelTest::Type::ReadWrite:
|
|
return "ReadWrite";
|
|
break;
|
|
case ONX_ModelTest::Type::ReadWriteRead:
|
|
return "ReadWriteRead";
|
|
break;
|
|
case ONX_ModelTest::Type::ReadWriteReadCompare:
|
|
return "ReadWriteReadCompare";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
ON_ERROR("Invalid test_type parameter.");
|
|
return "Invalid test_type parameter";
|
|
}
|
|
|
|
|
|
const wchar_t* ONX_ModelTest::TestTypeToWideString(ONX_ModelTest::Type test_type)
|
|
{
|
|
switch (test_type)
|
|
{
|
|
case ONX_ModelTest::Type::Unset:
|
|
return L"Unset";
|
|
break;
|
|
case ONX_ModelTest::Type::Read:
|
|
return L"Read";
|
|
break;
|
|
case ONX_ModelTest::Type::ReadWrite:
|
|
return L"ReadWrite";
|
|
break;
|
|
case ONX_ModelTest::Type::ReadWriteRead:
|
|
return L"ReadWriteRead";
|
|
break;
|
|
case ONX_ModelTest::Type::ReadWriteReadCompare:
|
|
return L"ReadWriteReadCompare";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
ON_ERROR("Invalid test_type parameter.");
|
|
return L"Invalid test_type parameter";
|
|
}
|
|
|
|
const char* ONX_ModelTest::ResultToString(ONX_ModelTest::Result result)
|
|
{
|
|
switch (result)
|
|
{
|
|
case ONX_ModelTest::Result::Unset:
|
|
return "Unset";
|
|
break;
|
|
case ONX_ModelTest::Result::Fail:
|
|
return "Fail";
|
|
break;
|
|
case ONX_ModelTest::Result::Errors:
|
|
return "Errors";
|
|
break;
|
|
case ONX_ModelTest::Result::Warnings:
|
|
return "Warnings";
|
|
break;
|
|
case ONX_ModelTest::Result::Pass:
|
|
return "Pass";
|
|
break;
|
|
case ONX_ModelTest::Result::Skip:
|
|
return "Skip";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
ON_ERROR("Invalid result parameter.");
|
|
return "Invalid result parameter";
|
|
}
|
|
|
|
const wchar_t* ONX_ModelTest::ResultToWideString(ONX_ModelTest::Result result)
|
|
{
|
|
switch (result)
|
|
{
|
|
case ONX_ModelTest::Result::Unset:
|
|
return L"Unset";
|
|
break;
|
|
case ONX_ModelTest::Result::Fail:
|
|
return L"Fail";
|
|
break;
|
|
case ONX_ModelTest::Result::Errors:
|
|
return L"Errors";
|
|
break;
|
|
case ONX_ModelTest::Result::Warnings:
|
|
return L"Warnings";
|
|
break;
|
|
case ONX_ModelTest::Result::Pass:
|
|
return L"Pass";
|
|
break;
|
|
case ONX_ModelTest::Result::Skip:
|
|
return L"Skip";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
ON_ERROR("Invalid result parameter.");
|
|
return L"Invalid result parameter";
|
|
|
|
}
|
|
|
|
|
|
ONX_ModelTest::Result ONX_ModelTest::WorstResult(
|
|
ONX_ModelTest::Result a,
|
|
ONX_ModelTest::Result b
|
|
)
|
|
{
|
|
if (ONX_ModelTest::Result::Unset == a && ONX_ModelTest::Result::Unset != b)
|
|
return b;
|
|
if (ONX_ModelTest::Result::Unset != a && ONX_ModelTest::Result::Unset == b)
|
|
return a;
|
|
return
|
|
(static_cast<unsigned char>(a) < static_cast<unsigned char>(b))
|
|
? a : b;
|
|
}
|
|
|
|
ONX_ModelTest::Result ONX_ModelTest::ResultFromErrorCounter(
|
|
ONX_ErrorCounter error_count,
|
|
ONX_ModelTest::Result no_errors_result
|
|
)
|
|
{
|
|
if (error_count.FailureCount() > 0)
|
|
return ONX_ModelTest::Result::Fail;
|
|
if (error_count.ErrorCount() > 0)
|
|
return ONX_ModelTest::Result::Errors;
|
|
if (error_count.WarningCount() > 0)
|
|
return ONX_ModelTest::Result::Warnings;
|
|
return no_errors_result;
|
|
}
|
|
|
|
|
|
void ONX_ModelTest::Internal_BeginTest()
|
|
{
|
|
*this = ONX_ModelTest::Unset;
|
|
|
|
m_test_result = ONX_ModelTest::Result::Unset;
|
|
const size_t count = sizeof(m_test_results) / sizeof(m_test_results[0]);
|
|
for (size_t i = 0; i < count; i++)
|
|
{
|
|
m_test_results[i] = ONX_ModelTest::Result::Unset;
|
|
}
|
|
|
|
m_error_count = ONX_ErrorCounter::Zero;
|
|
for (size_t i = 0; i < count; i++)
|
|
{
|
|
m_error_counts[i] = ONX_ErrorCounter::Zero;
|
|
}
|
|
m_error_counts[0].ClearLibraryErrorsAndWarnings();
|
|
m_error_count.ClearLibraryErrorsAndWarnings();
|
|
|
|
m_current_test_index = 0;
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
m_model_3dm_file_version[i] = 0;
|
|
m_model_hash[i] = ON_SHA1_Hash::ZeroDigest;
|
|
}
|
|
}
|
|
|
|
|
|
void ONX_ModelTest::Internal_EndCurrentTest()
|
|
{
|
|
if (m_current_test_index > 0 && ONX_ModelTest::Result::Unset == m_test_results[m_current_test_index])
|
|
{
|
|
m_error_counts[m_current_test_index].AddLibraryErrorsAndWarnings();
|
|
m_test_results[m_current_test_index] = ONX_ModelTest::WorstResult(m_test_results[m_current_test_index], ONX_ModelTest::ResultFromErrorCounter(m_error_counts[m_current_test_index], ONX_ModelTest::Result::Pass));
|
|
m_error_counts[0].ClearLibraryErrorsAndWarnings();
|
|
}
|
|
}
|
|
|
|
void ONX_ModelTest::Internal_BeginNextTest(
|
|
ONX_ModelTest::Type test_type
|
|
)
|
|
{
|
|
m_error_counts[0].AddLibraryErrorsAndWarnings();
|
|
|
|
//const size_t count = sizeof(m_test_results) / sizeof(m_test_results[0]);
|
|
const unsigned int test_index = static_cast<unsigned char>(test_type);
|
|
|
|
if ( test_index > m_current_test_index )
|
|
{
|
|
Internal_EndCurrentTest();
|
|
m_current_test_index = test_index;
|
|
m_test_results[m_current_test_index] = ONX_ModelTest::Result::Unset;
|
|
m_error_counts[m_current_test_index] = ONX_ErrorCounter::Zero;
|
|
m_error_counts[m_current_test_index].ClearLibraryErrorsAndWarnings();
|
|
}
|
|
}
|
|
|
|
bool ONX_ModelTest::Internal_TallyTestResults()
|
|
{
|
|
const size_t count = sizeof(m_test_results) / sizeof(m_test_results[0]);
|
|
m_test_results[0] = ONX_ModelTest::WorstResult(m_test_results[0], ONX_ModelTest::Result::Pass);
|
|
for (size_t i = 0; i < count; i++)
|
|
{
|
|
m_test_results[i] = ONX_ModelTest::WorstResult(m_test_results[i], ONX_ModelTest::ResultFromErrorCounter(m_error_counts[i], ONX_ModelTest::Result::Unset));
|
|
m_test_result = ONX_ModelTest::WorstResult(m_test_result, m_test_results[i]);
|
|
m_error_count += m_error_counts[i];
|
|
}
|
|
|
|
m_error_count.ClearLibraryErrorsAndWarnings();
|
|
|
|
m_test_result = ONX_ModelTest::WorstResult(m_test_result, ONX_ModelTest::ResultFromErrorCounter(m_error_count, ONX_ModelTest::Result::Unset));
|
|
|
|
return (ONX_ModelTest::Result::Pass == m_test_result);
|
|
}
|
|
|
|
ONX_ModelTest::Type ONX_ModelTest::TestType() const
|
|
{
|
|
return m_test_type;
|
|
}
|
|
|
|
const ON_wString ONX_ModelTest::Source3dmFilePath() const
|
|
{
|
|
return m_source_3dm_file_path;
|
|
}
|
|
|
|
const ON_wString ONX_ModelTest::TextLogSource3dmFilePath() const
|
|
{
|
|
return
|
|
m_text_log_3dm_file_path.IsNotEmpty()
|
|
? m_text_log_3dm_file_path
|
|
: Source3dmFilePath();
|
|
}
|
|
|
|
|
|
unsigned int ONX_ModelTest::Source3dmFileVersion() const
|
|
{
|
|
return m_model_3dm_file_version[0];
|
|
}
|
|
|
|
bool ONX_ModelTest::SkipCompare(unsigned int source_3dm_file_version)
|
|
{
|
|
const bool bSkipCompare
|
|
= (source_3dm_file_version >= 1 && source_3dm_file_version < 70);
|
|
return bSkipCompare;
|
|
}
|
|
|
|
ONX_ModelTest::Result ONX_ModelTest::TestResult() const
|
|
{
|
|
return m_test_result;
|
|
}
|
|
|
|
ONX_ModelTest::Result ONX_ModelTest::TestResult(
|
|
ONX_ModelTest::Type test_type
|
|
)
|
|
{
|
|
const unsigned int i = static_cast<unsigned char>(m_test_type);
|
|
return m_test_results[i];
|
|
}
|
|
|
|
|
|
ONX_ErrorCounter ONX_ModelTest::ErrorCounter() const
|
|
{
|
|
return m_error_count;
|
|
}
|
|
|
|
|
|
ONX_ErrorCounter ONX_ModelTest::ErrorCounter(
|
|
ONX_ModelTest::Type test_type
|
|
) const
|
|
{
|
|
const unsigned int i = static_cast<unsigned char>(m_test_type);
|
|
return m_error_counts[i];
|
|
}
|
|
|
|
bool ONX_ModelTest::ReadTest(
|
|
const char* file_path,
|
|
ONX_ModelTest::Type test_type,
|
|
bool bKeepModels,
|
|
const char* text_log_file_path,
|
|
ON_TextLog* text_log
|
|
)
|
|
{
|
|
Internal_BeginTest();
|
|
|
|
m_test_type = test_type;
|
|
|
|
ON_TextLogNull devnull;
|
|
if (nullptr == text_log)
|
|
text_log = &ON_TextLog::Null;
|
|
|
|
FILE* fp = nullptr;
|
|
for (;;)
|
|
{
|
|
if (nullptr == file_path || 0 == file_path[0])
|
|
{
|
|
m_error_counts[0].IncrementFailureCount();
|
|
text_log->Print("file_path was the empty string.");
|
|
break;
|
|
}
|
|
|
|
fp = ON_FileStream::Open3dmToRead(file_path);
|
|
if (nullptr == fp)
|
|
{
|
|
m_error_counts[0].IncrementFailureCount();
|
|
text_log->Print("ON_FileStream::Open(%s, \"rb\") failed.",file_path);
|
|
break;
|
|
}
|
|
|
|
ON_BinaryFile archive(ON::archive_mode::read3dm, fp);
|
|
archive.SetArchiveFullPath(ON_wString(file_path));
|
|
|
|
ON_wString wide_text_log_file_path(text_log_file_path);
|
|
Internal_ReadTest(archive, test_type, bKeepModels, wide_text_log_file_path, text_log);
|
|
break;
|
|
}
|
|
|
|
if (nullptr != fp)
|
|
{
|
|
if (0 != ON_FileStream::Close(fp))
|
|
{
|
|
text_log->Print("ON_FileStream::Close(%s) failed.", file_path);
|
|
m_error_counts[0].IncrementErrorCount();
|
|
}
|
|
}
|
|
|
|
return Internal_TallyTestResults();
|
|
}
|
|
|
|
|
|
bool ONX_ModelTest::ReadTest(
|
|
const wchar_t* file_path,
|
|
ONX_ModelTest::Type test_type,
|
|
bool bKeepModels,
|
|
const wchar_t* text_log_file_path,
|
|
ON_TextLog* text_log
|
|
)
|
|
{
|
|
Internal_BeginTest();
|
|
|
|
ON_TextLogNull devnull;
|
|
if (nullptr == text_log)
|
|
text_log = &ON_TextLog::Null;
|
|
|
|
FILE* fp = nullptr;
|
|
for (;;)
|
|
{
|
|
if (nullptr == file_path || 0 == file_path[0])
|
|
{
|
|
m_error_counts[0].IncrementFailureCount();
|
|
text_log->Print("file_path was the empty string.");
|
|
break;
|
|
}
|
|
|
|
fp = ON_FileStream::Open3dmToRead(file_path);
|
|
if (nullptr == fp)
|
|
{
|
|
m_error_counts[0].IncrementFailureCount();
|
|
text_log->Print(L"ON_FileStream::Open(%ls, L\"rb\") failed.",file_path);
|
|
break;
|
|
}
|
|
|
|
ON_BinaryFile archive(ON::archive_mode::read3dm, fp);
|
|
archive.SetArchiveFullPath(file_path);
|
|
Internal_ReadTest(archive, test_type, bKeepModels, text_log_file_path, text_log);
|
|
break;
|
|
}
|
|
|
|
if (nullptr != fp)
|
|
{
|
|
if (0 != ON_FileStream::Close(fp))
|
|
{
|
|
text_log->Print(L"ON_FileStream::Close(%ls) failed.",file_path);
|
|
m_error_counts[0].IncrementErrorCount();
|
|
}
|
|
}
|
|
|
|
return Internal_TallyTestResults();
|
|
}
|
|
|
|
bool ONX_ModelTest::ReadTest(
|
|
FILE* fp,
|
|
ONX_ModelTest::Type test_type,
|
|
bool bKeepModels,
|
|
const wchar_t* text_log_file_path,
|
|
ON_TextLog* text_log
|
|
)
|
|
{
|
|
Internal_BeginTest();
|
|
|
|
ON_TextLogNull devnull;
|
|
if (nullptr == text_log)
|
|
text_log = &ON_TextLog::Null;
|
|
|
|
for (;;)
|
|
{
|
|
if (nullptr == fp)
|
|
{
|
|
m_error_counts[0].IncrementFailureCount();
|
|
text_log->Print("fp is nullptr.");
|
|
break;
|
|
}
|
|
|
|
ON_BinaryFile archive(ON::archive_mode::read3dm, fp);
|
|
|
|
Internal_ReadTest(archive, test_type, bKeepModels, text_log_file_path, text_log);
|
|
break;
|
|
}
|
|
|
|
return Internal_TallyTestResults();
|
|
}
|
|
|
|
static bool InternalCleanPass(
|
|
ONX_ModelTest::Result result,
|
|
ONX_ErrorCounter error_counter
|
|
)
|
|
{
|
|
return (ONX_ModelTest::Result::Pass == result && 0 == error_counter.TotalCount());
|
|
}
|
|
|
|
static void InternalDumpResultAndErrorCount(
|
|
ONX_ModelTest::Result result,
|
|
ONX_ErrorCounter error_counter,
|
|
ON_TextLog& text_log
|
|
)
|
|
{
|
|
text_log.Print("%s", ONX_ModelTest::ResultToString(result));
|
|
if (false == InternalCleanPass(result,error_counter))
|
|
{
|
|
text_log.Print(" (");
|
|
error_counter.Dump(text_log);
|
|
text_log.Print(")");
|
|
}
|
|
text_log.PrintNewLine();
|
|
}
|
|
|
|
bool ONX_ModelTest::DumpModel(const ONX_Model* model, ON_TextLog& text_log)
|
|
{
|
|
if (nullptr == model || model->Manifest().ActiveComponentCount(ON_ModelComponent::Type::Unset) <= 0)
|
|
return false;
|
|
|
|
ON_TextHash hash_log;
|
|
hash_log.SetIdRemap(true);
|
|
hash_log.SetOutputTextLog(&text_log);
|
|
model->Dump(hash_log);
|
|
const ON_SHA1_Hash dump_hash = hash_log.Hash();
|
|
text_log.PrintNewLine();
|
|
text_log.Print("Model Hash: ");
|
|
dump_hash.Dump(text_log);
|
|
text_log.PrintNewLine();
|
|
|
|
return (false == dump_hash.IsZeroDigestOrEmptyContentHash());
|
|
}
|
|
|
|
std::shared_ptr<ONX_Model> ONX_ModelTest::SourceModel() const
|
|
{
|
|
return m_model[0];
|
|
}
|
|
|
|
std::shared_ptr<ONX_Model> ONX_ModelTest::ReadWriteReadModel() const
|
|
{
|
|
const auto source_model = SourceModel();
|
|
if (nullptr != source_model)
|
|
{
|
|
for (int i = 1; i < 3; i++)
|
|
{
|
|
const ONX_Model* copy_model = m_model[i].get();
|
|
if (nullptr == copy_model)
|
|
continue;
|
|
if (copy_model->m_3dm_file_version != source_model->m_3dm_file_version)
|
|
continue;
|
|
return m_model[i];
|
|
}
|
|
}
|
|
std::shared_ptr<ONX_Model> nullsp;
|
|
return nullsp;
|
|
}
|
|
|
|
static const ON_wString Internal_DumpModelfileName(
|
|
const ON_wString source_3dm_file_path,
|
|
bool bSourceModel,
|
|
bool bIsHashLog
|
|
)
|
|
{
|
|
ON_wString file_name_stem = ON_FileSystemPath::FileNameFromPath(source_3dm_file_path,false);
|
|
if (file_name_stem.IsEmpty())
|
|
return ON_wString::EmptyString;
|
|
ON_wString text_file_path = ON_FileSystemPath::VolumeAndDirectoryFromPath(source_3dm_file_path);
|
|
text_file_path += file_name_stem;
|
|
text_file_path += L"_ONX_ModelTest_";
|
|
if (bIsHashLog)
|
|
text_file_path += L"HASH_LOG_";
|
|
if (bSourceModel)
|
|
text_file_path += L"original";
|
|
else
|
|
text_file_path += L"copy";
|
|
|
|
if (false == bIsHashLog)
|
|
{
|
|
// The C-runtime loat/double formatting
|
|
// can change with change with OS and relase/debug.
|
|
// This information is appended so the person reading
|
|
// the text file knows the context where it was created.
|
|
#if defined(ON_RUNTIME_WIN)
|
|
#if defined(ON_64BIT_RUNTIME)
|
|
text_file_path += L"_Win64";
|
|
#elif defined(ON_32BIT_RUNTIME)
|
|
text_file_path += L"_Win32";
|
|
#else
|
|
text_file_path += L"_Win";
|
|
#endif
|
|
#elif defined(ON_RUNTIME_APPLE_MACOS)
|
|
text_file_path += L"_MacOS";
|
|
#elif defined(ON_RUNTIME_APPLE_IOS)
|
|
text_file_path += L"_iOS";
|
|
#elif defined(ON_RUNTIME_APPLE)
|
|
text_file_path += L"_AppleOS";
|
|
#elif defined(ON_RUNTIME_ANDROID)
|
|
text_file_path += L"_AndroidOS";
|
|
#endif
|
|
|
|
#if defined(ON_DEBUG)
|
|
text_file_path += L"Debug";
|
|
#else
|
|
text_file_path += L"Release";
|
|
#endif
|
|
}
|
|
|
|
text_file_path += L".txt";
|
|
return text_file_path;
|
|
}
|
|
|
|
bool ONX_ModelTest::DumpSourceModel() const
|
|
{
|
|
const ON_wString text_file_path = Internal_DumpModelfileName(m_source_3dm_file_path, true, false);
|
|
return DumpSourceModel(text_file_path);
|
|
}
|
|
|
|
bool ONX_ModelTest::DumpSourceModel(const wchar_t* text_file_full_path) const
|
|
{
|
|
bool rc = false;
|
|
FILE* fp = nullptr;
|
|
for (;;)
|
|
{
|
|
if (nullptr == text_file_full_path || 0 == text_file_full_path[0])
|
|
break;
|
|
fp = ON_FileStream::Open(text_file_full_path, L"w");
|
|
if (nullptr == fp)
|
|
break;
|
|
const auto model = SourceModel();
|
|
if (nullptr == model)
|
|
break;
|
|
if (model->Manifest().ActiveComponentCount(ON_ModelComponent::Type::Unset) <= 0)
|
|
break;
|
|
ON_TextLog text_log(fp);
|
|
rc = DumpSourceModel(text_log);
|
|
break;
|
|
}
|
|
if (nullptr != fp)
|
|
ON_FileStream::Close(fp);
|
|
return rc;
|
|
}
|
|
|
|
bool ONX_ModelTest::DumpSourceModel(ON_TextLog& text_log) const
|
|
{
|
|
return ONX_ModelTest::DumpModel(SourceModel().get(), text_log);
|
|
}
|
|
|
|
bool ONX_ModelTest::DumpReadWriteReadModel() const
|
|
{
|
|
const ON_wString text_file_path = Internal_DumpModelfileName(m_source_3dm_file_path,false, false);
|
|
return DumpReadWriteReadModel(text_file_path);
|
|
}
|
|
|
|
bool ONX_ModelTest::DumpReadWriteReadModel(const wchar_t* text_file_full_path) const
|
|
{
|
|
bool rc = false;
|
|
FILE* fp = nullptr;
|
|
for (;;)
|
|
{
|
|
if (nullptr == text_file_full_path || 0 == text_file_full_path[0])
|
|
break;
|
|
fp = ON_FileStream::Open(text_file_full_path, L"w");
|
|
if (nullptr == fp)
|
|
break;
|
|
const auto model = ReadWriteReadModel();
|
|
if (nullptr == model)
|
|
break;
|
|
if (model->Manifest().ActiveComponentCount(ON_ModelComponent::Type::Unset) <= 0)
|
|
break;
|
|
ON_TextLog text_log(fp);
|
|
rc = DumpReadWriteReadModel(text_log);
|
|
break;
|
|
}
|
|
|
|
if (nullptr != fp)
|
|
ON_FileStream::Close(fp);
|
|
|
|
return rc;
|
|
}
|
|
|
|
bool ONX_ModelTest::DumpReadWriteReadModel(ON_TextLog& text_log) const
|
|
{
|
|
return ONX_ModelTest::DumpModel(ReadWriteReadModel().get(), text_log);
|
|
}
|
|
|
|
bool ONX_ModelTest::DumpHashLogs(
|
|
ON_wString& source_model_hash_log_filename,
|
|
ON_SHA1_Hash& source_model_hash,
|
|
ON_wString& copy_model_hash_log_filename,
|
|
ON_SHA1_Hash& copy_model_hash
|
|
) const
|
|
{
|
|
source_model_hash_log_filename = ON_wString::EmptyString;
|
|
source_model_hash = ON_SHA1_Hash::EmptyContentHash;
|
|
copy_model_hash_log_filename = ON_wString::EmptyString;
|
|
copy_model_hash = ON_SHA1_Hash::EmptyContentHash;
|
|
|
|
for (;;)
|
|
{
|
|
const auto source_model_sp = SourceModel();
|
|
const ONX_Model* source_model = source_model_sp.get();
|
|
if (nullptr == source_model)
|
|
break;
|
|
if (source_model->Manifest().ActiveComponentCount(ON_ModelComponent::Type::Unset) <= 0)
|
|
break;
|
|
|
|
const auto copy_model_sp = ReadWriteReadModel();
|
|
const ONX_Model* copy_model = copy_model_sp.get();
|
|
if (nullptr == copy_model)
|
|
break;
|
|
if (copy_model->Manifest().ActiveComponentCount(ON_ModelComponent::Type::Unset) <= 0)
|
|
break;
|
|
|
|
source_model_hash_log_filename = Internal_DumpModelfileName(m_source_3dm_file_path, true, true);
|
|
if (source_model_hash_log_filename.IsEmpty())
|
|
break;
|
|
FILE* fp = ON_FileStream::Open(static_cast<const wchar_t*>(source_model_hash_log_filename), L"w");
|
|
if (nullptr == fp)
|
|
break;
|
|
ON_TextLog source_hash_log(fp);
|
|
source_model_hash = source_model->ContentHash(source_hash_log);
|
|
ON_FileStream::Close(fp);
|
|
|
|
copy_model_hash_log_filename = Internal_DumpModelfileName(m_source_3dm_file_path, false, true);
|
|
if (copy_model_hash_log_filename.IsEmpty())
|
|
break;
|
|
fp = ON_FileStream::Open(static_cast<const wchar_t*>(copy_model_hash_log_filename), L"w");
|
|
if (nullptr == fp)
|
|
break;
|
|
ON_TextLog copy_hash_log(fp);
|
|
source_model_hash = copy_model->ContentHash(copy_hash_log);
|
|
ON_FileStream::Close(fp);
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
void ONX_ModelTest::Dump(ON_TextLog& text_log) const
|
|
{
|
|
const ONX_ModelTest::Type test_type = TestType();
|
|
|
|
text_log.Print("Test type: %s\n", ONX_ModelTest::TestTypeToString(test_type));
|
|
|
|
//const ON_wString source_archive = Source3dmFilePath();
|
|
const ON_wString test_log_source_archive = TextLogSource3dmFilePath();
|
|
text_log.Print(L"Source 3dm file path: %ls\n", static_cast<const wchar_t*>(test_log_source_archive));
|
|
text_log.Print(L"Source 3dm file version: %u\n", Source3dmFileVersion());
|
|
|
|
text_log.Print("Result: ");
|
|
InternalDumpResultAndErrorCount(m_test_result, m_error_count, text_log);
|
|
|
|
//const int i_rwrcompare = static_cast<const unsigned char>(ONX_ModelTest::Type::ReadWriteReadCompare);
|
|
const bool bSkipCompare
|
|
= ONX_ModelTest::Type::ReadWriteReadCompare == test_type
|
|
&& InternalCleanPass(m_test_results[0], m_error_counts[0]) // init passed cleanly
|
|
&& InternalCleanPass(m_test_results[1], m_error_counts[1]) // read source passed cleanly
|
|
&& InternalCleanPass(m_test_results[2], m_error_counts[2]) // write temporary passed cleanly
|
|
&& InternalCleanPass(m_test_results[2], m_error_counts[3]) // read temporary passed cleanly
|
|
&& (ONX_ModelTest::SkipCompare(Source3dmFileVersion()) || ONX_ModelTest::Result::Skip == m_test_results[4]);
|
|
;
|
|
const unsigned int imax
|
|
= bSkipCompare
|
|
? static_cast<const unsigned char>(ONX_ModelTest::Type::ReadWriteRead)
|
|
: static_cast<const unsigned char>(test_type);
|
|
|
|
// bSkipDetails = true if all tests passed and there are no errors, warnings, failures of any sort.
|
|
bool bSkipDetails = InternalCleanPass(m_test_result, m_error_count);
|
|
for (unsigned int i = 0; i <= imax && bSkipDetails; i++)
|
|
{
|
|
bSkipDetails = InternalCleanPass(m_test_results[i], m_error_counts[i]);
|
|
}
|
|
|
|
if (bSkipDetails)
|
|
{
|
|
if (bSkipCompare)
|
|
{
|
|
// Let user know we skipped the compare
|
|
text_log.PushIndent();
|
|
text_log.Print("Compare test skipped because source file version is too old.\n");
|
|
text_log.PopIndent();
|
|
}
|
|
return;
|
|
}
|
|
|
|
text_log.PushIndent();
|
|
for(;;)
|
|
{
|
|
unsigned int i = 0;
|
|
text_log.Print("Initialization: ");
|
|
InternalDumpResultAndErrorCount(m_test_results[i], m_error_counts[i], text_log);
|
|
if (i >= imax)
|
|
break;
|
|
|
|
i++;
|
|
text_log.Print("Read source file: ");
|
|
InternalDumpResultAndErrorCount(m_test_results[i], m_error_counts[i], text_log);
|
|
if (i >= imax)
|
|
break;
|
|
|
|
i++;
|
|
text_log.Print("Write temporary file: ");
|
|
InternalDumpResultAndErrorCount(m_test_results[i], m_error_counts[i], text_log);
|
|
if (i >= imax)
|
|
break;
|
|
|
|
i++;
|
|
text_log.Print("Read temporary file: ");
|
|
InternalDumpResultAndErrorCount(m_test_results[i], m_error_counts[i], text_log);
|
|
if (i >= imax)
|
|
break;
|
|
|
|
i++;
|
|
text_log.Print("Compare models from source and temporary file: ");
|
|
InternalDumpResultAndErrorCount(m_test_results[i], m_error_counts[i], text_log);
|
|
if (i >= imax)
|
|
break;
|
|
|
|
break;
|
|
}
|
|
text_log.PopIndent();
|
|
}
|
|
|
|
bool ONX_ModelTest::ReadTest(
|
|
ON_BinaryArchive& archive,
|
|
ONX_ModelTest::Type test_type,
|
|
bool bKeepModels,
|
|
const wchar_t* text_log_file_path,
|
|
ON_TextLog* text_log
|
|
)
|
|
{
|
|
Internal_BeginTest();
|
|
Internal_ReadTest(archive, test_type, bKeepModels, text_log_file_path, text_log);
|
|
return Internal_TallyTestResults();
|
|
}
|
|
|
|
void ONX_ModelTest::Internal_ReadTest(
|
|
ON_BinaryArchive& archive,
|
|
ONX_ModelTest::Type test_type,
|
|
bool bKeepModels,
|
|
const wchar_t* text_log_file_path,
|
|
ON_TextLog* text_log
|
|
)
|
|
{
|
|
m_test_type = test_type;
|
|
m_source_3dm_file_path = archive.ArchiveFullPath();
|
|
m_text_log_3dm_file_path = text_log_file_path;
|
|
|
|
const unsigned int current_3dm_file_version = (unsigned int)ON_BinaryArchive::CurrentArchiveVersion();
|
|
|
|
ON_TextLogNull devnull;
|
|
if (nullptr == text_log)
|
|
text_log = &ON_TextLog::Null;
|
|
|
|
for (;;)
|
|
{
|
|
Internal_BeginNextTest(ONX_ModelTest::Type::Read);
|
|
|
|
if ( ON::archive_mode::read3dm != archive.Mode() )
|
|
{
|
|
m_error_counts[0].IncrementFailureCount();
|
|
text_log->Print("archive.Mode() must be ON::archive_mode::read3dm.");
|
|
break;
|
|
}
|
|
|
|
ONX_Model* model0 = new ONX_Model();
|
|
std::shared_ptr<ONX_Model> model0_sp = std::shared_ptr<ONX_Model>(model0);
|
|
if (bKeepModels)
|
|
this->m_model[0] = model0_sp;
|
|
|
|
ON_String text_log_3dm_archive_name = TextLogSource3dmFilePath();
|
|
if (text_log_3dm_archive_name.IsEmpty())
|
|
{
|
|
text_log_3dm_archive_name = "archive";
|
|
}
|
|
|
|
const ON_String read0_description
|
|
= ON_String::FormatToString("ONX_Model.Read(%s,...)", static_cast<const char*>(text_log_3dm_archive_name));
|
|
|
|
// read the original file
|
|
text_log->Print("Calling %s ...\n", static_cast<const char*>(read0_description));
|
|
text_log->PushIndent();
|
|
ONX_ErrorCounter read0_error_counter;
|
|
read0_error_counter.ClearLibraryErrorsAndWarnings();
|
|
const bool bRead0 = model0->Read(archive, text_log);
|
|
read0_error_counter.AddLibraryErrorsAndWarnings();
|
|
m_model_3dm_file_version[0] = model0->m_3dm_file_version;
|
|
|
|
text_log->PopIndent();
|
|
|
|
if (false == bRead0)
|
|
{
|
|
m_error_counts[m_current_test_index].IncrementFailureCount();
|
|
text_log->Print("%s failed.\n", static_cast<const char*>(read0_description));
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
text_log->Print("... %s ", static_cast<const char*>(read0_description));
|
|
if ( 0 == read0_error_counter.TotalCount() )
|
|
text_log->Print("succeeded.");
|
|
else
|
|
{
|
|
text_log->Print("finished. ");
|
|
read0_error_counter.Dump(*text_log);
|
|
}
|
|
text_log->PrintNewLine();
|
|
|
|
}
|
|
text_log->PushIndent();
|
|
text_log->Print("Source model 3dm file version: %d", model0->m_3dm_file_version);
|
|
text_log->PrintNewLine();
|
|
m_model_hash[0] = model0->ContentHash();
|
|
text_log->Print("Source model hash: ");
|
|
m_model_hash[0].Dump(*text_log);
|
|
text_log->PrintNewLine();
|
|
text_log->PopIndent();
|
|
|
|
if ( ONX_ModelTest::Type::Read == test_type)
|
|
break;
|
|
Internal_EndCurrentTest();
|
|
Internal_BeginNextTest(ONX_ModelTest::Type::ReadWrite);
|
|
|
|
//const unsigned int original_model_3dm_file_version = (unsigned int)(model0->m_3dm_file_version);
|
|
|
|
// Write original_model to a termporary archive using "buffer" for storage.
|
|
ON_Buffer temporary_buffer[2];
|
|
const unsigned int temporary_buffer_3dm_version[2] = { current_3dm_file_version - 10, current_3dm_file_version };
|
|
|
|
for (int buffer_index = 0; buffer_index < 2; buffer_index++)
|
|
{
|
|
ON_BinaryArchiveBuffer temporary_archive(ON::archive_mode::write3dm, &temporary_buffer[buffer_index]);
|
|
|
|
const ON_String write1_description
|
|
= ON_String::FormatToString(
|
|
"ONX_Model.Write( temporary_archive version %d, ...)",
|
|
temporary_buffer_3dm_version[buffer_index]);
|
|
|
|
text_log->Print("Calling %s ...\n", static_cast<const char*>(write1_description));
|
|
text_log->PushIndent();
|
|
ONX_ErrorCounter write1_error_counter;
|
|
write1_error_counter.ClearLibraryErrorsAndWarnings();
|
|
bool bWrite1 = model0->Write(temporary_archive, temporary_buffer_3dm_version[buffer_index], text_log);
|
|
write1_error_counter.AddLibraryErrorsAndWarnings();
|
|
text_log->PopIndent();
|
|
|
|
if (false == bWrite1)
|
|
{
|
|
m_error_counts[m_current_test_index].IncrementFailureCount();
|
|
text_log->Print("%s failed.\n", static_cast<const char*>(write1_description));
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
text_log->Print("... %s ", static_cast<const char*>(write1_description));
|
|
if ( 0 == write1_error_counter.TotalCount() )
|
|
text_log->Print("succeeded.");
|
|
else
|
|
{
|
|
text_log->Print("finished. ");
|
|
write1_error_counter.Dump(*text_log);
|
|
}
|
|
text_log->PrintNewLine();
|
|
}
|
|
}
|
|
|
|
// no longer need model0
|
|
model0 = nullptr;
|
|
|
|
if ( ONX_ModelTest::Type::ReadWrite == test_type)
|
|
break;
|
|
Internal_EndCurrentTest();
|
|
Internal_BeginNextTest(ONX_ModelTest::Type::ReadWriteRead);
|
|
|
|
// read models from the temporary archives
|
|
for (int buffer_index = 0; buffer_index < 2; buffer_index++)
|
|
{
|
|
ON_BinaryArchiveBuffer temporary_archive(ON::archive_mode::read3dm, &temporary_buffer[buffer_index]);
|
|
const ON_String read1_description
|
|
= ON_String::FormatToString(
|
|
"ONX_Model.Read( temporary_archive version %d, ...)",
|
|
temporary_buffer_3dm_version[buffer_index]);
|
|
|
|
text_log->Print("Calling %s ...\n", static_cast<const char*>(read1_description));
|
|
text_log->PushIndent();
|
|
|
|
ONX_Model* model1 = new ONX_Model();
|
|
std::shared_ptr<ONX_Model> model1_sp = std::shared_ptr<ONX_Model>(model1);
|
|
if (bKeepModels)
|
|
this->m_model[buffer_index+1] = model1_sp;
|
|
|
|
ONX_ErrorCounter read1_error_counter;
|
|
read1_error_counter.ClearLibraryErrorsAndWarnings();
|
|
const bool bRead1 = model1->Read(temporary_archive, text_log);
|
|
read1_error_counter.AddLibraryErrorsAndWarnings();
|
|
m_model_3dm_file_version[buffer_index + 1] = model1->m_3dm_file_version;
|
|
|
|
text_log->PopIndent();
|
|
|
|
if (false == bRead1)
|
|
{
|
|
m_error_counts[m_current_test_index].IncrementFailureCount();
|
|
text_log->Print("%s failed.\n", static_cast<const char*>(read1_description));
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
text_log->Print("... %s ", static_cast<const char*>(read1_description));
|
|
if ( 0 == read1_error_counter.TotalCount())
|
|
text_log->Print("succeeded.");
|
|
else
|
|
{
|
|
text_log->Print("finished. ");
|
|
read1_error_counter.Dump(*text_log);
|
|
}
|
|
text_log->PrintNewLine();
|
|
}
|
|
text_log->PushIndent();
|
|
text_log->Print("Temporary model %d 3dm file version: %d",buffer_index+1,model1->m_3dm_file_version);
|
|
text_log->PrintNewLine();
|
|
m_model_hash[buffer_index+1] = model1->ContentHash();
|
|
text_log->Print("Temporary model %d hash: ",buffer_index+1);
|
|
m_model_hash[buffer_index+1].Dump(*text_log);
|
|
text_log->PrintNewLine();
|
|
text_log->PopIndent();
|
|
}
|
|
|
|
if ( ONX_ModelTest::Type::ReadWrite == test_type)
|
|
break;
|
|
Internal_EndCurrentTest();
|
|
Internal_BeginNextTest(ONX_ModelTest::Type::ReadWriteReadCompare);
|
|
|
|
bool bSkippedCompare = true;
|
|
for (int buffer_index = 0; buffer_index < 2; buffer_index++)
|
|
{
|
|
if (m_model_3dm_file_version[0] != m_model_3dm_file_version[buffer_index+1])
|
|
continue;
|
|
if (m_model_3dm_file_version[0] != temporary_buffer_3dm_version[buffer_index])
|
|
continue;
|
|
|
|
bSkippedCompare = false;
|
|
if (m_model_hash[0] != m_model_hash[buffer_index+1])
|
|
{
|
|
m_error_counts[m_current_test_index].IncrementFailureCount();
|
|
text_log->Print("The source model and temporary model %d are different.\n",buffer_index+1);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
text_log->Print("The source model and temporary model %d are identical.\n",buffer_index+1);
|
|
}
|
|
}
|
|
|
|
unsigned int compare_test_index = m_current_test_index;
|
|
if (bSkippedCompare)
|
|
this->m_test_results[compare_test_index] = ONX_ModelTest::Result::Skip;
|
|
|
|
break;
|
|
}
|
|
|
|
Internal_EndCurrentTest();
|
|
}
|
|
|
|
/* +----------------------------------------------------------------------------+
|
|
| |
|
|
| RDK version 4 document archive format |
|
|
| |
|
|
+================+==========================+================================+
|
|
| Type | Usage | ON_BinaryArchive function |
|
|
+================+==========================+================================+
|
|
| ON__INT32 | RDK document version | ReadInt() / WriteInt() |
|
|
+----------------+--------------------------+--------------------------------+
|
|
| ON__INT32 | Length of UTF8 XML block | ReadInt() / WriteInt() |
|
|
+----------------+--------------------------+--------------------------------+
|
|
| UTF8 chars | Entire RDK document XML | ReadChar() / WriteChar() |
|
|
+----------------+--------------------------+--------------------------------+
|
|
| ON__INT32 | Number of embedded files | ReadInt() / WriteInt() |
|
|
+----------------+--------------------------+--------------------------------+
|
|
|
|
If the number of embedded files is not zero, then for each embedded file:
|
|
|
|
+----------------+--------------------------+--------------------------------+
|
|
| String | Full path to file | ReadString() / WriteString() |
|
|
+----------------+--------------------------+--------------------------------+
|
|
| Bytes | Compressed embedded file | Read / WriteCompressedBuffer() |
|
|
+----------------+--------------------------+--------------------------------+
|
|
*/
|
|
|
|
static const wchar_t* RenderContentKindString(RenderContentKinds kind)
|
|
{
|
|
switch (kind)
|
|
{
|
|
case RenderContentKinds::Material: return ON_KIND_MATERIAL;
|
|
case RenderContentKinds::Environment: return ON_KIND_ENVIRONMENT;
|
|
case RenderContentKinds::Texture: return ON_KIND_TEXTURE;
|
|
default: ON_ASSERT(false); return L"";
|
|
}
|
|
}
|
|
|
|
extern int ON_ComponentManifestImpl_TableCount(void);
|
|
|
|
ON_XMLNode& ON_GetRdkDocNode(const ON_3dmRenderSettings& rs);
|
|
|
|
ONX_ModelPrivate::ONX_ModelPrivate(ONX_Model& m)
|
|
:
|
|
m_model(m)
|
|
{
|
|
// The TableCount enum in opennurbs_archive_manifest.cpp should always be
|
|
// equal to ON_ModelComponent::Type::NumOf.
|
|
ON_ASSERT(int(ON_ModelComponent::Type::NumOf) == ON_ComponentManifestImpl_TableCount());
|
|
|
|
for (unsigned int i = 0; i < int(ON_ModelComponent::Type::NumOf); i++)
|
|
{
|
|
ONX_Model::ONX_ModelComponentList& list = m_mcr_lists.AppendNew();
|
|
list.m_component_type = ON_ModelComponent::ComponentTypeFromUnsigned(i);
|
|
}
|
|
}
|
|
|
|
ONX_ModelPrivate::~ONX_ModelPrivate()
|
|
{
|
|
}
|
|
|
|
ONX_Model_UserData* ONX_ModelPrivate::GetRDKDocumentUserData(int archive_3dm_version) const
|
|
{
|
|
// Try to find existing RDK document user data.
|
|
for (int i = 0; i < m_model.m_userdata_table.Count(); i++)
|
|
{
|
|
auto* pUserData = m_model.m_userdata_table[i];
|
|
if (nullptr != pUserData)
|
|
{
|
|
if (::IsRDKDocumentInformation(*pUserData))
|
|
return pUserData; // Found it.
|
|
}
|
|
}
|
|
|
|
// Not found, so create it.
|
|
auto* ud = new ONX_Model_UserData;
|
|
ud->m_goo.m_typecode = TCODE_USER_RECORD;
|
|
ud->m_uuid = RdkPlugInId();
|
|
ud->m_usertable_3dm_version = archive_3dm_version;
|
|
ud->m_usertable_opennurbs_version = ON::Version();
|
|
|
|
ON_XMLRootNode root;
|
|
PopulateDefaultRDKDocumentXML(root);
|
|
SetRDKDocumentInformation(root.String(), *ud, archive_3dm_version);
|
|
|
|
m_model.m_userdata_table.Append(ud);
|
|
|
|
return ud;
|
|
}
|
|
|
|
void ONX_ModelPrivate::PopulateDefaultRDKDocumentXML(ON_XMLRootNode& root) const
|
|
{
|
|
// Populate default render content kinds.
|
|
GetRenderContentSectionNode(root, RenderContentKinds::Material);
|
|
GetRenderContentSectionNode(root, RenderContentKinds::Environment);
|
|
GetRenderContentSectionNode(root, RenderContentKinds::Texture);
|
|
}
|
|
|
|
bool ONX_ModelPrivate::GetRDKDocumentXML(ON_wString& xml, bool embedded_files, int archive_3dm_version) const
|
|
{
|
|
// Gets the entire RDK document XML as a string in 'xml'. If 'embedded_files' is true,
|
|
// ON_EmbeddedFile objects are created for each embedded file.
|
|
|
|
const ONX_Model_UserData* pUserData = GetRDKDocumentUserData(archive_3dm_version);
|
|
if (nullptr != pUserData)
|
|
{
|
|
ONX_Model* model = embedded_files ? &m_model : nullptr;
|
|
if (GetEntireRDKDocument(*pUserData, xml, model))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool ContentIsKind(const ON_RenderContent* pContent, RenderContentKinds kind)
|
|
{
|
|
switch (kind)
|
|
{
|
|
case RenderContentKinds::Material: return nullptr != ON_RenderMaterial ::Cast(pContent);
|
|
case RenderContentKinds::Environment: return nullptr != ON_RenderEnvironment::Cast(pContent);
|
|
case RenderContentKinds::Texture: return nullptr != ON_RenderTexture ::Cast(pContent);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
ON_XMLNode* ONX_ModelPrivate::GetPostEffectSectionNode(ON_XMLNode& docNode, ON_PostEffect::Types type) const
|
|
{
|
|
ON_wString s = ON_RDK_DOCUMENT ON_XML_SLASH ON_RDK_SETTINGS ON_XML_SLASH ON_RDK_POST_EFFECTS ON_XML_SLASH;
|
|
s += ON_PostEffectTypeString(type);
|
|
|
|
return docNode.CreateNodeAtPath(s);
|
|
}
|
|
|
|
ON_XMLNode* ONX_ModelPrivate::GetRenderContentSectionNode(ON_XMLNode& docNode, RenderContentKinds kind) const
|
|
{
|
|
ON_wString s = ON_RDK_DOCUMENT ON_XML_SLASH;
|
|
s += RenderContentKindString(kind);
|
|
s += ON_RDK_POSTFIX_SECTION;
|
|
|
|
return docNode.CreateNodeAtPath(s);
|
|
}
|
|
|
|
bool ONX_ModelPrivate::CreateRenderContentFromXML(ON_XMLNode& model_node, RenderContentKinds kind)
|
|
{
|
|
const ON_XMLNode* rc_section_node = GetRenderContentSectionNode(model_node, kind);
|
|
if (nullptr == rc_section_node)
|
|
return false;
|
|
|
|
auto it = rc_section_node->GetChildIterator();
|
|
|
|
const ON_XMLNode* rc_node = it.GetNextChild();
|
|
while (nullptr != rc_node)
|
|
{
|
|
ON_RenderContent* rc = NewRenderContentFromNode(*rc_node);
|
|
if (nullptr != rc)
|
|
{
|
|
// The name currently in the render content came from XML. It's been corrected if it was invalid,
|
|
// but it still might be a duplicate of another one in the model, so we have to turn it into an
|
|
// unused name if necessary.
|
|
const ON_wString name = m_model.m_manifest.UnusedName(rc->ComponentType(), ON_nil_uuid, rc->Name(),
|
|
nullptr, nullptr, 0, nullptr);
|
|
rc->SetName(name);
|
|
|
|
const auto ref = m_model.AddModelComponent(*rc);
|
|
auto* model_rc = ON_RenderContent::Cast(ref.ModelComponent());
|
|
if (nullptr != model_rc)
|
|
{
|
|
SetModel(*model_rc, m_model);
|
|
}
|
|
|
|
delete rc;
|
|
}
|
|
|
|
rc_node = it.GetNextChild();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ONX_ModelPrivate::CreateXMLFromRenderContent(ON_XMLNode& model_node, RenderContentKinds kind) const
|
|
{
|
|
ON_XMLNode* rc_section_node = GetRenderContentSectionNode(model_node, kind);
|
|
if (nullptr == rc_section_node)
|
|
return false;
|
|
|
|
rc_section_node->RemoveAllChildren();
|
|
|
|
ONX_ModelComponentIterator it(m_model, ON_ModelComponent::Type::RenderContent);
|
|
const ON_ModelComponent* component = it.FirstComponent();
|
|
while (nullptr != component)
|
|
{
|
|
const auto* rc = ON_RenderContent::Cast(component);
|
|
if (nullptr != rc)
|
|
{
|
|
if (ContentIsKind(rc, kind))
|
|
{
|
|
SetRenderContentNodeRecursive(*rc, *rc_section_node);
|
|
}
|
|
}
|
|
|
|
component = it.NextComponent();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ONX_ModelPrivate::PopulateRDKComponents(int archive_3dm_version)
|
|
{
|
|
// Get the entire RDK document XML. This includes not only render contents
|
|
// but also Sun, GroundPlane and other RDK document data. Ignore embedded files.
|
|
ON_wString xml;
|
|
if (!GetRDKDocumentXML(xml, true, archive_3dm_version))
|
|
return false;
|
|
|
|
// Read the entire XML into the document node.
|
|
ON_XMLNode& doc_node = ON_GetRdkDocNode(m_model.m_settings.m_RenderSettings);
|
|
const auto read = doc_node.ReadFromStream(xml, false, true);
|
|
if (ON_XMLNode::ReadError == read)
|
|
return false;
|
|
|
|
// Create the render contents from the relevant nodes.
|
|
CreateRenderContentFromXML(doc_node, RenderContentKinds::Material);
|
|
CreateRenderContentFromXML(doc_node, RenderContentKinds::Environment);
|
|
CreateRenderContentFromXML(doc_node, RenderContentKinds::Texture);
|
|
|
|
// Create the mesh modifiers.
|
|
CreateMeshModifiersFromXML(m_model, archive_3dm_version);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ONX_ModelPrivate::UpdateRDKUserData(int archive_3dm_version)
|
|
{
|
|
if (0 == archive_3dm_version)
|
|
archive_3dm_version = ON_BinaryArchive::CurrentArchiveVersion();
|
|
|
|
ON_XMLNode& doc_node = ON_GetRdkDocNode(m_model.m_settings.m_RenderSettings);
|
|
|
|
// For each kind, convert the render content hierarchy to fresh XML.
|
|
CreateXMLFromRenderContent(doc_node, RenderContentKinds::Material);
|
|
CreateXMLFromRenderContent(doc_node, RenderContentKinds::Environment);
|
|
CreateXMLFromRenderContent(doc_node, RenderContentKinds::Texture);
|
|
|
|
// Convert the mesh modifier collection to fresh XML.
|
|
CreateXMLFromMeshModifiers(m_model, archive_3dm_version);
|
|
|
|
// Get the RDK document user data.
|
|
ONX_Model_UserData* pUserData = GetRDKDocumentUserData(archive_3dm_version);
|
|
if (nullptr == pUserData)
|
|
return false; // Shouldn't happen because we were able to get the XML earlier.
|
|
|
|
// Get the entire document XML as a string and set it to the user data.
|
|
ON_wString xml = doc_node.String();
|
|
pUserData->m_usertable_3dm_version = archive_3dm_version;
|
|
SetRDKDocumentInformation(xml, *pUserData, archive_3dm_version);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool IsRDKDocumentInformation(const ONX_Model_UserData& docud)
|
|
{
|
|
return (0 == ON_UuidCompare(RdkPlugInId(), docud.m_uuid)) && (docud.m_goo.m_value >= 4) && (nullptr != docud.m_goo.m_goo);
|
|
}
|
|
|
|
bool ONX_Model::IsRDKDocumentInformation(const ONX_Model_UserData& docud)
|
|
{
|
|
return ::IsRDKDocumentInformation(docud);
|
|
}
|
|
|
|
static size_t ArchiveLengthUpToEmbeddedFiles(size_t utf8_length)
|
|
{
|
|
// version utf8_length utf8_buf
|
|
const size_t length = sizeof(ON__INT32) + sizeof(ON__INT32) + utf8_length;
|
|
ON_ASSERT(length <= INT_MAX);
|
|
|
|
return length;
|
|
}
|
|
|
|
bool ONX_Model::GetRDKEmbeddedFiles(const ONX_Model_UserData& docud, ON_ClassArray<ON_wString>& paths, ON_SimpleArray<unsigned char*>& embedded_files_as_buffers)
|
|
{
|
|
ON_SimpleArray<size_t> dummy;
|
|
return ::GetRDKEmbeddedFiles(docud, paths, embedded_files_as_buffers, dummy);
|
|
}
|
|
|
|
bool ONX_Model::GetRDKEmbeddedFiles(const ONX_Model_UserData& docud, ON_ClassArray<ON_wString>& paths, ON_SimpleArray<unsigned char*>& embedded_files_as_buffers, ON_SimpleArray<size_t>& buffer_sizes) // Static.
|
|
{
|
|
return ::GetRDKEmbeddedFiles(docud, paths, embedded_files_as_buffers, buffer_sizes);
|
|
}
|
|
|
|
bool ONX_Model::GetRDKDocumentInformation(const ONX_Model_UserData& docud, ON_wString& xml) // Static.
|
|
{
|
|
return ONX_ModelPrivate::GetEntireRDKDocument(docud, xml, nullptr);
|
|
}
|
|
|
|
static int SeekArchiveToEmbeddedFiles(ON_Read3dmBufferArchive& archive, int goo_length)
|
|
{
|
|
// Skips over the bulk of the archive to arrive at the embedded files.
|
|
// Then reads the number of embedded files and returns it. The archive
|
|
// is then ready to read the first embedded file.
|
|
|
|
if (!archive.ReadMode())
|
|
return 0;
|
|
|
|
// Read the version number. Must be 4.
|
|
int version = 0;
|
|
if (!archive.ReadInt(&version) || (4 != version))
|
|
return 0;
|
|
|
|
// Read the UTF8 data length.
|
|
int utf8_length = 0;
|
|
if (!archive.ReadInt(&utf8_length))
|
|
return 0;
|
|
|
|
// Validate the length.
|
|
if (utf8_length <= 0)
|
|
return 0;
|
|
|
|
const auto length_so_far = ArchiveLengthUpToEmbeddedFiles(utf8_length);
|
|
if (length_so_far > size_t(goo_length))
|
|
return 0; // Sanity check.
|
|
|
|
// Seek past the UTF8 data.
|
|
if (!archive.SeekForward(utf8_length))
|
|
return 0;
|
|
|
|
// Read the number of embedded files.
|
|
int num_embedded_files = 0;
|
|
if (!archive.ReadInt(&num_embedded_files))
|
|
return 0;
|
|
|
|
return num_embedded_files;
|
|
}
|
|
|
|
static bool SeekArchivePastCompressedBuffer(ON_BinaryArchive& archive)
|
|
{
|
|
// WARNING: This function has intimate knowledge of how ON_BinaryArchive::WriteCompressedBuffer() works.
|
|
// It is my opinion that this function should really be a method on ON_BinaryArchive since only that
|
|
// class should know how its own implementation works. JohnC, 2022AD.
|
|
|
|
if (!archive.ReadMode())
|
|
return false;
|
|
|
|
bool rc = false;
|
|
unsigned int buffer_crc0 = 0;
|
|
char method = 0;
|
|
|
|
size_t sizeof__outbuffer;
|
|
if (!archive.ReadCompressedBufferSize(&sizeof__outbuffer))
|
|
return false;
|
|
|
|
if (0 == sizeof__outbuffer)
|
|
return true;
|
|
|
|
if (!archive.ReadInt(&buffer_crc0)) // 32 bit crc of uncompressed buffer
|
|
return false;
|
|
|
|
if (!archive.ReadChar(&method))
|
|
return false;
|
|
|
|
if (method != 0 && method != 1)
|
|
return false;
|
|
|
|
switch (method)
|
|
{
|
|
case 0: // uncompressed
|
|
rc = archive.SeekForward(sizeof__outbuffer);
|
|
break;
|
|
|
|
case 1: // compressed
|
|
{
|
|
ON__UINT32 tcode = 0;
|
|
ON__INT64 big_value = 0;
|
|
rc = archive.BeginRead3dmBigChunk(&tcode, &big_value);
|
|
if (rc)
|
|
rc = archive.EndRead3dmChunk();
|
|
}
|
|
break;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
static bool ReadEmbeddedFilePathsFromArchive(ON_Read3dmBufferArchive& archive, int count, ON_ClassArray<ON_wString>& paths)
|
|
{
|
|
// Reads the embedded file paths and skips over the data for 'count' embedded files. Must be called with
|
|
// the archive position at the start of the first embedded file, right after the count of embedded files.
|
|
if (0 == count)
|
|
return false;
|
|
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
ON_wString sPath;
|
|
if (!archive.ReadString(sPath))
|
|
return false;
|
|
|
|
paths.Append(sPath);
|
|
|
|
SeekArchivePastCompressedBuffer(archive);
|
|
}
|
|
|
|
return paths.Count() > 0;
|
|
}
|
|
|
|
bool ONX_Model::GetRDKEmbeddedFilePaths(const ONX_Model_UserData& docud, ON_ClassArray<ON_wString>& paths)
|
|
{
|
|
if (!::IsRDKDocumentInformation(docud))
|
|
return false;
|
|
|
|
ON_Read3dmBufferArchive archive(docud.m_goo.m_value, docud.m_goo.m_goo, false, docud.m_usertable_3dm_version, docud.m_usertable_opennurbs_version);
|
|
|
|
const int count = SeekArchiveToEmbeddedFiles(archive, docud.m_goo.m_value);
|
|
if (!ReadEmbeddedFilePathsFromArchive(archive, count, paths))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool GetRDKEmbeddedFiles(const ONX_Model_UserData& docud, ON_ClassArray<ON_wString>& paths, ON_SimpleArray<unsigned char*>& embedded_files_as_buffers, ON_SimpleArray<size_t>& buffer_sizes)
|
|
{
|
|
if (!::IsRDKDocumentInformation(docud))
|
|
return false;
|
|
|
|
ON_Read3dmBufferArchive a(docud.m_goo.m_value, docud.m_goo.m_goo, false, docud.m_usertable_3dm_version, docud.m_usertable_opennurbs_version);
|
|
|
|
const int count = SeekArchiveToEmbeddedFiles(a, docud.m_goo.m_value);
|
|
if (0 == count)
|
|
return false;
|
|
|
|
int unpacked = 0;
|
|
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
ON_wString sPath;
|
|
if (!a.ReadString(sPath))
|
|
return false;
|
|
|
|
size_t size;
|
|
if (!a.ReadCompressedBufferSize(&size))
|
|
return false;
|
|
|
|
auto* buffer = new unsigned char[size];
|
|
bool bFailedCRC = false;
|
|
if (a.ReadCompressedBuffer(size, buffer, &bFailedCRC))
|
|
{
|
|
if (!bFailedCRC)
|
|
{
|
|
embedded_files_as_buffers.Append(buffer);
|
|
paths.Append(sPath);
|
|
buffer_sizes.Append(size);
|
|
unpacked++;
|
|
}
|
|
else
|
|
{
|
|
delete[] buffer;
|
|
}
|
|
}
|
|
}
|
|
|
|
return unpacked > 0;
|
|
}
|
|
|
|
bool ONX_Model::GetRDKEmbeddedFile(const ONX_Model_UserData& docud, const wchar_t* path, ON_SimpleArray<unsigned char>& bytes)
|
|
{
|
|
if (!::IsRDKDocumentInformation(docud))
|
|
return false;
|
|
|
|
ON_Read3dmBufferArchive archive(docud.m_goo.m_value, docud.m_goo.m_goo, false, docud.m_usertable_3dm_version, docud.m_usertable_opennurbs_version);
|
|
|
|
const auto count = SeekArchiveToEmbeddedFiles(archive, docud.m_goo.m_value);
|
|
if (0 == count)
|
|
return false;
|
|
|
|
bool found = false;
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
ON_wString sPath;
|
|
if (!archive.ReadString(sPath))
|
|
break;
|
|
|
|
if (0 == sPath.ComparePath(path))
|
|
{
|
|
size_t size;
|
|
if (!archive.ReadCompressedBufferSize(&size))
|
|
break;
|
|
|
|
bytes.Destroy();
|
|
bytes.Reserve(size);
|
|
|
|
bool bFailedCRC = false;
|
|
bool bRet = archive.ReadCompressedBuffer(size, bytes.Array(), &bFailedCRC);
|
|
|
|
if (!bRet || bFailedCRC)
|
|
break;
|
|
|
|
bytes.SetCount((int)size);
|
|
|
|
found = true;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
SeekArchivePastCompressedBuffer(archive);
|
|
}
|
|
}
|
|
|
|
return found;
|
|
}
|
|
|
|
void ONX_ModelPrivate::RemoveAllEmbeddedFiles(ONX_Model& model)
|
|
{
|
|
ON_SimpleArray<ON_UUID> a;
|
|
const auto type = ON_ModelComponent::Type::EmbeddedFile;
|
|
|
|
ONX_ModelComponentIterator it(model, type);
|
|
const ON_ModelComponent* pComponent = it.FirstComponent();
|
|
while (nullptr != pComponent)
|
|
{
|
|
a.Append(pComponent->Id());
|
|
|
|
pComponent = it.NextComponent();
|
|
}
|
|
|
|
for (int i = 0; i < a.Count(); i++)
|
|
{
|
|
model.RemoveModelComponent(type, a[i]);
|
|
}
|
|
}
|
|
|
|
bool ONX_ModelPrivate::GetEntireRDKDocument(const ONX_Model_UserData& docud, ON_wString& xml, ONX_Model* model) // Static.
|
|
{
|
|
if (!::IsRDKDocumentInformation(docud))
|
|
return false;
|
|
|
|
ON_Read3dmBufferArchive archive(docud.m_goo.m_value, docud.m_goo.m_goo, false, docud.m_usertable_3dm_version, docud.m_usertable_opennurbs_version);
|
|
|
|
int version = 0;
|
|
if (!archive.ReadInt(&version))
|
|
return false;
|
|
|
|
if (1 == version)
|
|
{
|
|
// Version 1 was a UTF-16 string.
|
|
if (!archive.ReadString(xml))
|
|
return false;
|
|
}
|
|
else
|
|
if ((3 == version) || (4 == version))
|
|
{
|
|
// Version 4 files are exactly the same as version 3, but with the addition of embedded files.
|
|
|
|
// Version 3 and 4 is a UTF-8 string.
|
|
int utf8_length = 0;
|
|
if (!archive.ReadInt(&utf8_length))
|
|
return false;
|
|
|
|
if (utf8_length <= 0)
|
|
return false;
|
|
|
|
const size_t length_so_far = ArchiveLengthUpToEmbeddedFiles(utf8_length);
|
|
if (length_so_far > size_t(docud.m_goo.m_value))
|
|
return false; // Sanity check.
|
|
|
|
ON_String s;
|
|
s.SetLength(utf8_length);
|
|
if (!archive.ReadChar(utf8_length, s.Array()))
|
|
return false;
|
|
|
|
if (s.IsNotEmpty())
|
|
{
|
|
const char* sArray = s.Array();
|
|
unsigned int error_status = 0;
|
|
auto wideLength = ON_ConvertUTF8ToWideChar(false, sArray, -1, 0, 0, &error_status, 0, 0, 0);
|
|
if ((wideLength > 0) && (0 == error_status))
|
|
{
|
|
xml.SetLength(wideLength);
|
|
ON_ConvertUTF8ToWideChar(false, sArray, -1, xml.Array(), wideLength+1, &error_status, 0, 0, 0);
|
|
}
|
|
|
|
if (0 != error_status)
|
|
{
|
|
ON_ERROR("RDK xml document settings is not a valid UTF-8 string.");
|
|
}
|
|
}
|
|
|
|
if (nullptr != model)
|
|
{
|
|
RemoveAllEmbeddedFiles(*model);
|
|
|
|
if (4 == version)
|
|
{
|
|
// Read the number of embedded files.
|
|
int num_embedded_files = 0;
|
|
if (!archive.ReadInt(&num_embedded_files))
|
|
return false;
|
|
|
|
// Create an ON_EmbeddedFile object for each embedded file.
|
|
for (int i = 0; i < num_embedded_files; i++)
|
|
{
|
|
// We keep the embedded file object even if it fails to load; then it will have an error flag set.
|
|
// See ON_EmbeddedFile::Error().
|
|
ON_EmbeddedFile ef;
|
|
ef.Read(archive);
|
|
model->AddModelComponent(ef);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return xml.Length() > 0;
|
|
}
|
|
|
|
bool ONX_ModelPrivate::SetRDKDocumentInformation(const wchar_t* xml, ONX_Model_UserData& docud, int archive_3dm_version) const
|
|
{
|
|
ON_Write3dmBufferArchive archive(0, 0, docud.m_usertable_3dm_version, docud.m_usertable_opennurbs_version);
|
|
|
|
// Write the version.
|
|
int version = 4; // Where do I get this from?
|
|
if (!archive.WriteInt(version))
|
|
return false;
|
|
|
|
// Convert the XML to UTF8 and write it to the archive.
|
|
unsigned int error_status = 0;
|
|
const int utf8_length = ON_ConvertWideCharToUTF8(false, xml, -1, 0, 0, &error_status, 0, 0, 0);
|
|
|
|
{ // BEGIN UTF8
|
|
auto utf8 = std::unique_ptr<char[]>(new char[utf8_length]);
|
|
char* utf8_buf = utf8.get();
|
|
|
|
ON_ConvertWideCharToUTF8(false, xml, -1, utf8_buf, utf8_length, &error_status, 0, 0, 0);
|
|
|
|
// Write the length of the UTF8 data.
|
|
if (!archive.WriteInt(utf8_length))
|
|
return false;
|
|
|
|
// Write the UTF8 data.
|
|
if (!archive.WriteChar(size_t(utf8_length), utf8_buf))
|
|
return false;
|
|
} // END UTF8
|
|
|
|
const auto length_so_far = ArchiveLengthUpToEmbeddedFiles(utf8_length);
|
|
ON_ASSERT(archive.SizeOfArchive() == length_so_far); // Sanity check.
|
|
|
|
// Write the number of embedded files.
|
|
const auto num_embedded_files = int(m_model.ActiveComponentCount(ON_ModelComponent::Type::EmbeddedFile));
|
|
if (!archive.WriteInt(num_embedded_files))
|
|
return false;
|
|
|
|
// Write the embedded files to the archive.
|
|
ONX_ModelComponentIterator it(m_model, ON_ModelComponent::Type::EmbeddedFile);
|
|
const ON_ModelComponent* pComponent = it.FirstComponent();
|
|
while (nullptr != pComponent)
|
|
{
|
|
const auto* embedded_file = ON_EmbeddedFile::Cast(pComponent);
|
|
if (nullptr != embedded_file)
|
|
{
|
|
embedded_file->Write(archive);
|
|
}
|
|
|
|
pComponent = it.NextComponent();
|
|
}
|
|
|
|
// Delete the old goo.
|
|
if (nullptr != docud.m_goo.m_goo)
|
|
onfree(docud.m_goo.m_goo);
|
|
|
|
// Allocate the new goo and copy the archive to it.
|
|
const auto length_goo = archive.SizeOfArchive();
|
|
docud.m_goo.m_goo = (unsigned char*)onmalloc(length_goo);
|
|
docud.m_goo.m_value = int(length_goo);
|
|
memcpy(docud.m_goo.m_goo, archive.Buffer(), length_goo);
|
|
|
|
return true;
|
|
}
|
|
|
|
// Object user data
|
|
|
|
static ON_UserData* RDKObjectUserDataHelper(const ON_UserData* objectud)
|
|
{
|
|
if (nullptr == objectud)
|
|
return nullptr;
|
|
|
|
if (0 == ON_UuidCompare(objectud->m_application_uuid, RdkPlugInId()))
|
|
{
|
|
if (0 != ON_UuidCompare(objectud->m_userdata_uuid, ON_RdkUserData::Uuid()))
|
|
return nullptr; // Not RDK user data.
|
|
|
|
return const_cast<ON_UserData*>(objectud); // RDK user data -- used for decals.
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
static bool IsMeshModifierObjectUserData(ON_UserData& objectud)
|
|
{
|
|
if (0 == ON_UuidCompare(objectud.m_application_uuid, ON_MeshModifier::PlugInId()))
|
|
return true; // User data from Displacement plug-in.
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ONX_Model::IsRDKObjectInformation(const ON_UserData& objectud) // Static.
|
|
{
|
|
return nullptr != RDKObjectUserDataHelper(&objectud);
|
|
}
|
|
|
|
static bool CreateArchiveBufferFromXML(const ON_wString& xml, ON_Buffer& buf, int archive_3dm_version)
|
|
{
|
|
const auto archive_opennurbs_version_number = ON::Version(); // I don't know if this is correct.
|
|
|
|
ON_Write3dmBufferArchive archive(0, 0, archive_3dm_version, archive_opennurbs_version_number);
|
|
|
|
int version = 2; // Not sure if this is correct.
|
|
if (!archive.WriteInt(version))
|
|
return false;
|
|
|
|
const wchar_t* wsz = static_cast<const wchar_t*>(xml);
|
|
|
|
unsigned int error_status = 0;
|
|
const int num_chars = ON_ConvertWideCharToUTF8(false, wsz, -1, nullptr, 0, &error_status, 0, 0, nullptr);
|
|
|
|
auto p = std::unique_ptr<char[]>(new char[size_t(num_chars) + 1]);
|
|
char* pBuffer = p.get();
|
|
ON_ConvertWideCharToUTF8(false, wsz, -1, pBuffer, num_chars + 1, &error_status, 0, 0, nullptr);
|
|
|
|
if (0 != error_status)
|
|
{
|
|
ON_ERROR("XML is not a valid UTF-8 string.");
|
|
return false;
|
|
}
|
|
|
|
int len = num_chars * sizeof(char);
|
|
if (!archive.WriteInt(len))
|
|
return false;
|
|
|
|
if (!archive.WriteChar(len, pBuffer))
|
|
return false;
|
|
|
|
buf.Write(archive.SizeOfArchive(), archive.Buffer());
|
|
buf.SeekFromStart(0);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SetXMLToUserData(const ON_wString& xml, ON_UserData& ud, int archive_3dm_version)
|
|
{
|
|
ON_Buffer buf;
|
|
if (!CreateArchiveBufferFromXML(xml, buf, archive_3dm_version))
|
|
return false;
|
|
|
|
ON_BinaryArchiveBuffer arc(ON::archive_mode::read, &buf);
|
|
ud.Read(arc);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SetRDKObjectInformation(ON_Object& object, const ON_wString& xml, int archive_3dm_version)
|
|
{
|
|
// Create a buffer from the XML.
|
|
ON_Buffer buf;
|
|
if (!CreateArchiveBufferFromXML(xml, buf, archive_3dm_version))
|
|
return false;
|
|
|
|
const auto archive_opennurbs_version = ON::Version(); // I don't know if this is correct.
|
|
|
|
// Create an archive from the buffer.
|
|
ON_BinaryArchiveBuffer archive(ON::archive_mode::read, &buf);
|
|
archive.SetArchive3dmVersion(archive_3dm_version);
|
|
ON_SetBinaryArchiveOpenNURBSVersion(archive, archive_opennurbs_version);
|
|
|
|
// Try to find existing user data.
|
|
ON_UserData* rdk_ud = nullptr;
|
|
|
|
for (ON_UserData* ud = object.FirstUserData(); (nullptr != ud) && (nullptr == rdk_ud); ud = ud->Next())
|
|
{
|
|
rdk_ud = RDKObjectUserDataHelper(ud);
|
|
}
|
|
|
|
if (nullptr != rdk_ud)
|
|
{
|
|
// Found it, so read the archive into it.
|
|
rdk_ud->Read(archive);
|
|
}
|
|
else
|
|
{
|
|
// No user data found; create a new one and read the archive into it.
|
|
auto* ud = new ON_RdkUserData;
|
|
ud->Read(archive);
|
|
|
|
if (!object.AttachUserData(ud))
|
|
{
|
|
delete ud;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool GetRDKObjectInformation(const ON_Object& object, ON_wString& xml, int archive_3dm_version)
|
|
{
|
|
if (0 == archive_3dm_version)
|
|
archive_3dm_version = ON_BinaryArchive::CurrentArchiveVersion();
|
|
|
|
xml.SetLength(0);
|
|
|
|
const ON_UserData* rdk_ud = nullptr;
|
|
const auto* ud = ON_UserData::Cast(&object);
|
|
if (nullptr != ud)
|
|
{
|
|
rdk_ud = RDKObjectUserDataHelper(ud);
|
|
}
|
|
else
|
|
{
|
|
for (ud = object.FirstUserData(); (nullptr != ud) && (nullptr == rdk_ud); ud = ud->Next())
|
|
{
|
|
rdk_ud = RDKObjectUserDataHelper(ud);
|
|
}
|
|
}
|
|
|
|
if (nullptr == rdk_ud)
|
|
return false;
|
|
|
|
ON_Buffer buf;
|
|
ON_BinaryArchiveBuffer arc(ON::archive_mode::write, &buf);
|
|
rdk_ud->Write(arc);
|
|
|
|
const auto sizeof_buffer = buf.Size();
|
|
|
|
auto p = std::unique_ptr<ON__UINT8[]>(new ON__UINT8[size_t(sizeof_buffer)]);
|
|
ON__UINT8* buffer = p.get();
|
|
buf.SeekFromStart(0);
|
|
buf.Read(sizeof_buffer, buffer);
|
|
|
|
const unsigned int archive_opennurbs_version_number = ON::Version();
|
|
ON_Read3dmBufferArchive archive(size_t(sizeof_buffer), buffer, false, archive_3dm_version, archive_opennurbs_version_number);
|
|
|
|
int version = 0;
|
|
if (!archive.ReadInt(&version))
|
|
return false;
|
|
|
|
if (1 == version)
|
|
{
|
|
// Version 1 was a UTF-16 string.
|
|
if (!archive.ReadString(xml))
|
|
return false;
|
|
}
|
|
else
|
|
if (2 == version)
|
|
{
|
|
// Version 2 is a UTF-8 string.
|
|
int len = 0;
|
|
if (!archive.ReadInt(&len))
|
|
return false;
|
|
|
|
if (len <= 0)
|
|
return false;
|
|
|
|
if (size_t(len + 4) > sizeof_buffer) // JohnC asks: What does the 4 signify?
|
|
return false;
|
|
|
|
ON_SimpleArray<char> s;
|
|
s.Reserve(size_t(len) + 1);
|
|
s.SetCount(len + 1);
|
|
s[len] = 0;
|
|
|
|
char* sArray = s.Array();
|
|
if (nullptr == sArray)
|
|
return false;
|
|
|
|
if (!archive.ReadChar(len, sArray))
|
|
return false;
|
|
|
|
if (0 == sArray[0])
|
|
return false;
|
|
|
|
unsigned int error_status = 0;
|
|
const int num_chars = ON_ConvertUTF8ToWideChar(false, sArray, -1, 0, 0, &error_status, 0, 0, 0);
|
|
if ((num_chars > 0) && (0 == error_status))
|
|
{
|
|
xml.SetLength(size_t(num_chars) + 2);
|
|
ON_ConvertUTF8ToWideChar(false, sArray, -1, xml.Array(), num_chars + 1, &error_status, 0, 0, 0);
|
|
xml.SetLength(num_chars);
|
|
}
|
|
else
|
|
{
|
|
xml.SetLength(0);
|
|
ON_ERROR("RDK xml object information is not a valid UTF-8 string.");
|
|
}
|
|
}
|
|
|
|
return xml.Length() > 0;
|
|
}
|
|
|
|
bool GetEntireDecalXML(const ON_3dmObjectAttributes& attr, ON_XMLRootNode& xmlOut)
|
|
{
|
|
// Get the entire XML off of the attributes user data. At the moment (V8) this can only contain decals.
|
|
ON_wString xml;
|
|
if (!GetRDKObjectInformation(attr, xml, 0))
|
|
return false; // No XML on attributes.
|
|
|
|
// Read the XML into a root node.
|
|
if (ON_XMLNode::ReadError == xmlOut.ReadFromStream(xml))
|
|
return false; // Failed to read XML.
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool GetMeshModifierUserDataXML(ON_UserData& ud, ON_wString& xml, int archive_3dm_version)
|
|
{
|
|
ON_Buffer buf;
|
|
ON_BinaryArchiveBuffer arc(ON::archive_mode::write, &buf);
|
|
ud.Write(arc);
|
|
|
|
const ON__UINT64 sizeof_buffer = buf.Size();
|
|
|
|
auto p = std::unique_ptr<ON__UINT8[]>(new ON__UINT8[size_t(sizeof_buffer)]);
|
|
ON__UINT8* buffer = p.get();
|
|
buf.SeekFromStart(0);
|
|
buf.Read(sizeof_buffer, buffer);
|
|
|
|
const auto archive_opennurbs_version_number = ON::Version();
|
|
ON_Read3dmBufferArchive archive(size_t(sizeof_buffer), buffer, false, archive_3dm_version, archive_opennurbs_version_number);
|
|
|
|
int version = 0;
|
|
if (!archive.ReadInt(&version))
|
|
return false;
|
|
|
|
if (1 == version)
|
|
{
|
|
// Version 1 was a UTF-16 string.
|
|
if (!archive.ReadString(xml))
|
|
return false;
|
|
}
|
|
else
|
|
if (2 == version)
|
|
{
|
|
// Version 2 is a UTF-8 string.
|
|
int len = 0;
|
|
if (!archive.ReadInt(&len))
|
|
return false;
|
|
|
|
if (len <= 0)
|
|
return false;
|
|
|
|
if (size_t(len + 4) > sizeof_buffer) // JohnC asks: What does the 4 signify?
|
|
return false;
|
|
|
|
ON_SimpleArray<char> s;
|
|
s.Reserve(size_t(len) + 1);
|
|
s.SetCount(len + 1);
|
|
s[len] = 0;
|
|
|
|
char* sArray = s.Array();
|
|
if (nullptr == sArray)
|
|
return false;
|
|
|
|
if (!archive.ReadChar(len, sArray))
|
|
return false;
|
|
|
|
if (0 == sArray[0])
|
|
return false;
|
|
|
|
unsigned int error_status = 0;
|
|
const int num_chars = ON_ConvertUTF8ToWideChar(false, sArray, -1, 0, 0, &error_status, 0, 0, 0);
|
|
if ((num_chars > 0) && (0 == error_status))
|
|
{
|
|
xml.SetLength(size_t(num_chars) + 2);
|
|
ON_ConvertUTF8ToWideChar(false, sArray, -1, xml.Array(), num_chars + 1, &error_status, 0, 0, 0);
|
|
xml.SetLength(num_chars);
|
|
}
|
|
else
|
|
{
|
|
xml.SetLength(0);
|
|
ON_ERROR("Mesh modifier xml object information is not a valid UTF-8 string.");
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ONX_Model::GetRDKObjectInformation(const ON_Object& object, ON_wString& xml) // Static.
|
|
{
|
|
// Deprecated; only for backward compatibility.
|
|
return ::GetRDKObjectInformation(object, xml, ON_BinaryArchive::CurrentArchiveVersion());
|
|
}
|
|
|
|
bool GetMeshModifierObjectInformation(const ON_Object& object, ON_wString& xml, int archive_3dm_version)
|
|
{
|
|
xml = L"";
|
|
|
|
// The mesh modifiers are stored in separate user data items. We must get each one's
|
|
// XML and combine it into a single XML node containing the entire information for all
|
|
// mesh modifiers on this object.
|
|
|
|
ON_XMLRootNode entire;
|
|
|
|
ON_UserData* ud = object.FirstUserData();
|
|
while (nullptr != ud)
|
|
{
|
|
if (IsMeshModifierObjectUserData(*ud))
|
|
{
|
|
ON_wString ud_xml;
|
|
GetMeshModifierUserDataXML(*ud, ud_xml, archive_3dm_version);
|
|
|
|
ON_XMLRootNode root;
|
|
root.ReadFromStream(ud_xml);
|
|
ON_XMLNode* mm_node = root.FirstChild();
|
|
if (nullptr != mm_node)
|
|
{
|
|
root.DetachChild(*mm_node);
|
|
entire.AttachChildNode(mm_node);
|
|
}
|
|
}
|
|
|
|
ud = ud->Next();
|
|
}
|
|
|
|
if (entire.FirstChild() == nullptr)
|
|
return false;
|
|
|
|
// Return the entire XML for all mesh modifiers on this object.
|
|
xml = entire.String();
|
|
|
|
return true;
|
|
}
|
|
|
|
static ON_UserData* GetMeshModifierUserData(ON_Object& object, const ON_UUID& uuid_mm)
|
|
{
|
|
ON_UserData* existing_ud = object.FirstUserData();
|
|
while (nullptr != existing_ud)
|
|
{
|
|
if (existing_ud->m_userdata_uuid == uuid_mm)
|
|
return existing_ud;
|
|
|
|
existing_ud = existing_ud->Next();
|
|
}
|
|
|
|
// Not found so create it.
|
|
ON_XMLUserData* new_ud = nullptr;
|
|
|
|
if (uuid_mm == ON_DisplacementUserData::Uuid()) new_ud = new ON_DisplacementUserData;
|
|
else if (uuid_mm == ON_EdgeSofteningUserData::Uuid()) new_ud = new ON_EdgeSofteningUserData;
|
|
else if (uuid_mm == ON_ThickeningUserData::Uuid()) new_ud = new ON_ThickeningUserData;
|
|
else if (uuid_mm == ON_CurvePipingUserData::Uuid()) new_ud = new ON_CurvePipingUserData;
|
|
else if (uuid_mm == ON_ShutLiningUserData::Uuid()) new_ud = new ON_ShutLiningUserData;
|
|
else ON_ASSERT(false);
|
|
|
|
if (nullptr != new_ud)
|
|
{
|
|
new_ud->SetToDefaults(); // This doesn't work because the XML gets overwritten by the cached XML.
|
|
// In fact, having cached XML in this and decals is the reason why the systems are wrong.
|
|
// NO! LIGHT BULB MOMENT says that's not why it's wrong. Something else is wrong here.
|
|
// I'm about to fix the decals to not have cached XML and directly use the user data. NO! After
|
|
// that I'll do the same for mesh modifiers. NO! Then, this should work because there is no longer
|
|
// an XML cache to overwrite the new defaults in the user data's XML. NO!
|
|
|
|
if (!object.AttachUserData(new_ud))
|
|
{
|
|
delete new_ud;
|
|
new_ud = nullptr;
|
|
}
|
|
}
|
|
|
|
return new_ud;
|
|
}
|
|
|
|
void SetMeshModifierObjectInformation(ON_Object& object, const ON_MeshModifier* mm, int archive_3dm_version)
|
|
{
|
|
if (nullptr == mm)
|
|
return; // Can't create user data for non-existent mesh modifiers.
|
|
|
|
ON_XMLRootNode root;
|
|
mm->AddChildXML(root);
|
|
|
|
ON_UserData* ud = GetMeshModifierUserData(object, mm->Uuid());
|
|
if (nullptr != ud)
|
|
{
|
|
SetXMLToUserData(root.String(), *ud, archive_3dm_version);
|
|
}
|
|
}
|