mirror of
https://github.com/mcneel/opennurbs.git
synced 2026-03-02 12:37:02 +08:00
5022 lines
147 KiB
C++
5022 lines
147 KiB
C++
/* $NoKeywords: $ */
|
|
/*
|
|
//
|
|
// Copyright (c) 1993-2012 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"
|
|
|
|
#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
|
|
|
|
|
|
#if defined(ON_COMPILER_MSC)
|
|
// Disable the MSC /W4 warning C4127: conditional expression is constant
|
|
//
|
|
// This file has a lot of for( i = 0; true; i < 123 )...
|
|
// loops where the "true" generates a 4127 warning.
|
|
// This source code has to work on many different
|
|
// compilers I do not trust all of them to correctly
|
|
// compile for( i = 0; /* empty condition*/; i < 123) ...
|
|
#pragma ON_PRAGMA_WARNING_PUSH
|
|
#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4127 )
|
|
#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;
|
|
};
|
|
|
|
ONX_Model::ONX_Model()
|
|
{
|
|
for (unsigned int i = 0; i < ONX_MCR_LIST_COUNT; i++)
|
|
{
|
|
if (i == static_cast<unsigned int>(ON_ModelComponent::Type::Unset) )
|
|
continue;
|
|
if (i == static_cast<unsigned int>(ON_ModelComponent::Type::Mixed) )
|
|
continue;
|
|
if (i > static_cast<unsigned int>(ON_ModelComponent::Type::HistoryRecord))
|
|
break;
|
|
m_mcr_lists[i].m_component_type = ON_ModelComponent::ComponentTypeFromUnsigned(i);
|
|
}
|
|
}
|
|
|
|
|
|
ONX_Model::~ONX_Model()
|
|
{
|
|
Reset();
|
|
}
|
|
|
|
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++)
|
|
{
|
|
ONX_Model_UserData* p = m_userdata_table[i];
|
|
if ( nullptr == p)
|
|
continue;
|
|
delete p;
|
|
}
|
|
m_userdata_table.Destroy();
|
|
|
|
for (unsigned int i = 0; i < ONX_MCR_LIST_COUNT; i++)
|
|
{
|
|
ONX_ModelComponentList& list = m_mcr_lists[i];
|
|
for (ONX_ModelComponentReferenceLink* mcr_link = list.m_first_mcr_link; nullptr != mcr_link; mcr_link = mcr_link->m_next)
|
|
{
|
|
mcr_link->m_mcr = ON_ModelComponentReference::Empty;
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
|
|
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, ... 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 unsigned int i = static_cast<unsigned int>(component_type);
|
|
return
|
|
(i < ONX_Model::ONX_MCR_LIST_COUNT)
|
|
? m_mcr_lists[i]
|
|
: m_mcr_lists[0];
|
|
}
|
|
|
|
const ONX_Model::ONX_ModelComponentList& ONX_Model::Internal_ComponentListConst(
|
|
ON_ModelComponent::Type component_type
|
|
) const
|
|
{
|
|
const unsigned int i = static_cast<unsigned int>(component_type);
|
|
return
|
|
(i < ONX_Model::ONX_MCR_LIST_COUNT)
|
|
? m_mcr_lists[i]
|
|
: 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_ModelComponentReference ONX_Model::RenderMaterialFromAttributes(
|
|
const ON_3dmObjectAttributes& attributes
|
|
) const
|
|
{
|
|
switch ( attributes.MaterialSource() )
|
|
{
|
|
case ON::material_from_layer:
|
|
return RenderMaterialFromLayerIndex( attributes.m_layer_index );
|
|
break;
|
|
|
|
case ON::material_from_object:
|
|
return RenderMaterialFromIndex( attributes.m_material_index );
|
|
break;
|
|
|
|
case ON::material_from_parent:
|
|
// TODO: If object is an idef, get material from iref attributes.
|
|
return RenderMaterialFromIndex( attributes.m_material_index );
|
|
break;
|
|
}
|
|
|
|
return m_default_render_material;
|
|
}
|
|
|
|
ON_ModelComponentReference ONX_Model::RenderMaterialFromLayerIndex(
|
|
int layer_index
|
|
) const
|
|
{
|
|
int render_material_index = ON_Layer::FromModelComponentRef(
|
|
LayerFromIndex(layer_index),
|
|
&ON_Layer::Default
|
|
)->RenderMaterialIndex();
|
|
return RenderMaterialFromIndex(render_material_index);
|
|
}
|
|
|
|
|
|
ON_ModelComponentReference ONX_Model::RenderMaterialFromIndex(
|
|
int render_material_index
|
|
) const
|
|
{
|
|
ON_ModelComponentReference cr = ComponentFromIndex(ON_ModelComponent::Type::RenderMaterial, render_material_index);
|
|
return cr.IsEmpty() ? m_default_render_material : cr;
|
|
}
|
|
|
|
ON_ModelComponentReference ONX_Model::RenderMaterialFromId(
|
|
ON_UUID render_material_id
|
|
) const
|
|
{
|
|
ON_ModelComponentReference cr = ComponentFromId(ON_ModelComponent::Type::RenderMaterial, render_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( RenderMaterialFromAttributes(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_DumpView( ON_TextLog& dump, const ON_3dmView& view )
|
|
{
|
|
view.Dump(dump);
|
|
}
|
|
|
|
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::RenderMaterial,
|
|
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::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
|
|
{
|
|
const bool bRemapIds = true;
|
|
ON_TextHash hash_log;
|
|
hash_log.SetIdRemap(bRemapIds);
|
|
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
|
|
|
|
for (;;)
|
|
{
|
|
if ( rc < 0 )
|
|
break;
|
|
|
|
if ( AddModelComponentForExperts(bitmap,bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty() )
|
|
{
|
|
delete bitmap;
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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
|
|
for (;;)
|
|
{
|
|
if ( rc < 0 )
|
|
break;
|
|
|
|
if (AddModelComponentForExperts(texture_mapping, bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty())
|
|
{
|
|
delete texture_mapping;
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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
|
|
for (;;)
|
|
{
|
|
if ( rc < 0 )
|
|
{
|
|
break;
|
|
}
|
|
|
|
bool bSetAsCurrent =
|
|
(bSetCurrentById && settings_current_id == material->Id())
|
|
|| (bSetCurrentByIndex && settings_current_index == material->Index());
|
|
|
|
if ( AddModelComponentForExperts(material,bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty() )
|
|
{
|
|
delete material;
|
|
continue;
|
|
}
|
|
|
|
if (bSetAsCurrent)
|
|
{
|
|
m_settings.SetCurrentMaterialId(material->Id());
|
|
bSetCurrentById = false;
|
|
bSetCurrentByIndex = false;
|
|
}
|
|
|
|
material = nullptr;
|
|
|
|
break;
|
|
}
|
|
if ( nullptr != material)
|
|
delete material;
|
|
}
|
|
}
|
|
|
|
// 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 )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
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;
|
|
continue;
|
|
}
|
|
|
|
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 )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
bool bSetAsCurrent =
|
|
(bSetCurrentById && settings_current_id == layer->Id())
|
|
|| (bSetCurrentByIndex && settings_current_index == layer->Index());
|
|
|
|
if (AddModelComponentForExperts(layer, bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty())
|
|
{
|
|
delete layer;
|
|
continue;
|
|
}
|
|
|
|
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
|
|
|
|
for (;;)
|
|
{
|
|
if ( rc < 0 )
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (AddModelComponentForExperts(group, bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty())
|
|
{
|
|
delete group;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
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;
|
|
continue;
|
|
}
|
|
|
|
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
|
|
}
|
|
for (;;)
|
|
{
|
|
if ( rc < 0 )
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (AddModelComponentForExperts(model_light, bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty())
|
|
{
|
|
delete model_light;
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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;
|
|
|
|
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;
|
|
continue;
|
|
}
|
|
|
|
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;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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 geoemtry table.");
|
|
return false;
|
|
}
|
|
|
|
if (false == archive.BeginRead3dmObjectTable())
|
|
{
|
|
ON_ERROR("Geoemtry table cannot be read from archive.");
|
|
return false;
|
|
}
|
|
active_table = archive.Active3dmTable();
|
|
if (active_table != ON_3dmArchiveTableType::object_table)
|
|
{
|
|
ON_ERROR("Catestrophic geoemtry 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("Catestrophic geoemtry 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("Geoemtry table has already been read from archive.");
|
|
return false;
|
|
}
|
|
default:
|
|
{
|
|
ON_ERROR("Geoemtry 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;
|
|
}
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
}
|
|
|
|
// 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
|
|
)
|
|
{
|
|
const bool bManageComponents = true;
|
|
const bool bManageGeometry = true;
|
|
const bool bManageAttributes = true;
|
|
for(;;)
|
|
{
|
|
IncrementalReadBegin( archive, bManageComponents, table_filter, error_log);
|
|
|
|
if ( 0 != archive.CriticalErrorCount() )
|
|
break;
|
|
|
|
// STEP 15: REQUIRED - Read object (geometry and annotation) table
|
|
if ( 0 == (static_cast<unsigned int>(ON_3dmArchiveTableType::object_table) & table_filter) )
|
|
{
|
|
for(;;)
|
|
{
|
|
ON_ModelComponentReference model_geometry_reference;
|
|
if ( false == IncrementalReadModelGeometry(archive, bManageComponents, bManageGeometry, bManageAttributes, model_object_type_filter, model_geometry_reference) )
|
|
break; // catestrophic error
|
|
if (model_geometry_reference.IsEmpty())
|
|
break; // no more geometry;
|
|
}
|
|
if ( 0 != archive.CriticalErrorCount() )
|
|
break;
|
|
}
|
|
|
|
IncrementalReadFinish(archive, bManageComponents, table_filter, error_log);
|
|
if ( 0 != archive.CriticalErrorCount() )
|
|
break;
|
|
|
|
break;
|
|
}
|
|
|
|
return ( 0 == archive.CriticalErrorCount() && 0 == archive.BadCRCCount() );
|
|
}
|
|
|
|
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 ( 0 != 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 ( 0 != 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
|
|
{
|
|
if ( 0 != version )
|
|
{
|
|
if ( version < 2
|
|
|| version > ON_BinaryArchive::CurrentArchiveVersion()
|
|
|| (version >= 50 && 0 != (version%10))
|
|
|| (version < 50 && version > ON_BinaryArchive::CurrentArchiveVersion()/10)
|
|
)
|
|
{
|
|
// version must be 0, 2, 3, 4, 5 or 50
|
|
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;
|
|
|
|
// RENDER 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;
|
|
}
|
|
|
|
// RENDER 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_model_content_version_number;
|
|
}
|
|
|
|
void ONX_Model::Internal_IncrementModelContentVersionNumber()
|
|
{
|
|
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 dreference.
|
|
// 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 dreference.
|
|
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;
|
|
}
|
|
|
|
|
|
static const ON_UnknownUserData* RDKObjectUserDataHelper(const ON_UserData* objectud)
|
|
{
|
|
// CRhRdkUserData object id: AFA82772-1525-43dd-A63C-C84AC5806911
|
|
// CRhRdkUserData::m_userdata_uuid = B63ED079-CF67-416c-800D-22023AE1BE21
|
|
|
|
// CRhRdkUserData object id
|
|
// {AFA82772-1525-43dd-A63C-C84AC5806911}
|
|
static const ON_UUID CRhRdkUserData_object_id =
|
|
{ 0xAFA82772, 0x1525, 0x43dd, { 0xA6, 0x3C, 0xC8, 0x4A, 0xC5, 0x80, 0x69, 0x11 } };
|
|
|
|
// CRhRdkUserData::m_userdata_uuid
|
|
// {B63ED079-CF67-416c-800D-22023AE1BE21}
|
|
static const ON_UUID CRhRdkUserData_userdata_uuid =
|
|
{ 0xB63ED079, 0xCF67, 0x416c, { 0x80, 0x0D, 0x22, 0x02, 0x3A, 0xE1, 0xBE, 0x21 } };
|
|
|
|
const ON_UnknownUserData* unknown_ud = ON_UnknownUserData::Cast(objectud);
|
|
|
|
bool rc = ( 0 != unknown_ud
|
|
&& unknown_ud->m_sizeof_buffer > 0
|
|
&& 0 != unknown_ud->m_buffer
|
|
&& 0 == ON_UuidCompare(CRhRdkUserData_object_id,unknown_ud->m_unknownclass_uuid)
|
|
&& 0 == ON_UuidCompare(CRhRdkUserData_userdata_uuid,unknown_ud->m_userdata_uuid)
|
|
);
|
|
return rc ? unknown_ud : 0;
|
|
}
|
|
|
|
bool ONX_Model::IsRDKObjectInformation(const ON_UserData& objectud)
|
|
{
|
|
return 0 != RDKObjectUserDataHelper(&objectud);
|
|
}
|
|
|
|
bool ONX_Model::GetRDKObjectInformation(const ON_Object& object,ON_wString& rdk_xml_object_data)
|
|
{
|
|
rdk_xml_object_data.SetLength(0);
|
|
const ON_UnknownUserData* unknown_ud = 0;
|
|
const ON_UserData* ud = ON_UserData::Cast(&object);
|
|
if ( 0 != ud )
|
|
{
|
|
unknown_ud = RDKObjectUserDataHelper(ud);
|
|
}
|
|
else
|
|
{
|
|
for ( ud = object.FirstUserData(); 0 != ud && 0 == unknown_ud; ud = ud->Next() )
|
|
{
|
|
unknown_ud = RDKObjectUserDataHelper(ud);
|
|
}
|
|
}
|
|
|
|
if ( 0 == unknown_ud )
|
|
return false;
|
|
|
|
ON_Read3dmBufferArchive a(unknown_ud->m_sizeof_buffer, unknown_ud->m_buffer, false, unknown_ud->m_3dm_version, unknown_ud->m_3dm_opennurbs_version_number);
|
|
int version = 0;
|
|
if (!a.ReadInt(&version) )
|
|
return false;
|
|
|
|
if ( 1 == version )
|
|
{
|
|
if ( !a.ReadString(rdk_xml_object_data) )
|
|
return false;
|
|
}
|
|
else if ( 2 == version )
|
|
{
|
|
// UTF8 string
|
|
ON_SimpleArray< char > s;
|
|
int slen = 0;
|
|
if ( !a.ReadInt(&slen) )
|
|
return false;
|
|
if ( slen <= 0 )
|
|
return false;
|
|
if ( slen + 4 > unknown_ud->m_sizeof_buffer )
|
|
return false;
|
|
s.Reserve(slen+1);
|
|
s.SetCount(slen+1);
|
|
s[slen] = 0;
|
|
if ( !a.ReadChar(slen,s.Array() ) )
|
|
return false;
|
|
const char* sArray = s.Array();
|
|
if ( 0 != sArray && 0 != sArray[0] )
|
|
{
|
|
unsigned int error_status = 0;
|
|
int wLen = ON_ConvertUTF8ToWideChar(false,sArray,-1,0,0,&error_status,0,0,0);
|
|
if ( wLen > 0 && 0 == error_status )
|
|
{
|
|
rdk_xml_object_data.SetLength(wLen+2);
|
|
wLen = ON_ConvertUTF8ToWideChar(false,sArray,-1,rdk_xml_object_data.Array(),wLen+1,&error_status,0,0,0);
|
|
if ( wLen > 0 && 0 == error_status )
|
|
rdk_xml_object_data.SetLength(wLen);
|
|
else
|
|
rdk_xml_object_data.SetLength(0);
|
|
}
|
|
if ( 0 != error_status )
|
|
{
|
|
ON_ERROR("RDK xml object information is not a valid UTF-8 string.");
|
|
}
|
|
}
|
|
}
|
|
|
|
return rdk_xml_object_data.Length() > 0;
|
|
}
|
|
|
|
bool ONX_Model::IsRDKDocumentInformation(const ONX_Model_UserData& docud)
|
|
{
|
|
// {16592D58-4A2F-401D-BF5E-3B87741C1B1B}
|
|
static const ON_UUID rdk_plugin_id =
|
|
{ 0x16592D58, 0x4A2F, 0x401D, { 0xBF, 0x5E, 0x3B, 0x87, 0x74, 0x1C, 0x1B, 0x1B } };
|
|
|
|
return ( 0 == ON_UuidCompare(rdk_plugin_id,docud.m_uuid) && docud.m_goo.m_value >= 4 && 0 != docud.m_goo.m_goo );
|
|
}
|
|
|
|
bool ONX_Model::GetRDKEmbeddedFiles(const ONX_Model_UserData& docud, ON_ClassArray<ON_wString>& paths, ON_SimpleArray<unsigned char*>& embedded_files_as_buffers)
|
|
{
|
|
if (!ONX_Model::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);
|
|
|
|
int version = 0;
|
|
if (!a.ReadInt(&version))
|
|
return false;
|
|
|
|
if (4 != version)
|
|
return false;
|
|
|
|
//Read out the document data, and throw it away.
|
|
{
|
|
int slen = 0;
|
|
if (!a.ReadInt(&slen))
|
|
return 0;
|
|
if (slen <= 0)
|
|
return 0;
|
|
if (slen + 4 > docud.m_goo.m_value)
|
|
return 0;
|
|
ON_String s;
|
|
s.SetLength(slen);
|
|
if (!a.ReadChar(slen, s.Array()))
|
|
return 0;
|
|
}
|
|
|
|
unsigned int iCount = 0;
|
|
if (!a.ReadInt(&iCount))
|
|
return false;
|
|
|
|
int unpacked = 0;
|
|
|
|
for (unsigned int i = 0; i < iCount; 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);
|
|
unpacked++;
|
|
}
|
|
else
|
|
{
|
|
delete[] buffer;
|
|
}
|
|
}
|
|
}
|
|
|
|
return unpacked > 0;
|
|
}
|
|
|
|
|
|
bool ONX_Model::GetRDKDocumentInformation(const ONX_Model_UserData& docud,ON_wString& rdk_xml_document_data)
|
|
{
|
|
if ( !ONX_Model::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);
|
|
|
|
int version = 0;
|
|
if (!a.ReadInt(&version) )
|
|
return false;
|
|
|
|
if ( 1 == version )
|
|
{
|
|
// UTF-16 string
|
|
if ( !a.ReadString(rdk_xml_document_data) )
|
|
return false;
|
|
}
|
|
else if ( 3 == version || 4 == version) //Version 4 files are exactly the same as version 3, but with an size_t (for the number of buffers)
|
|
//and the actual compressed buffers containing the embedded files
|
|
{
|
|
// UTF-8 string
|
|
int slen = 0;
|
|
if ( !a.ReadInt(&slen) )
|
|
return 0;
|
|
if ( slen <= 0 )
|
|
return 0;
|
|
if ( slen + 4 > docud.m_goo.m_value )
|
|
return 0;
|
|
ON_String s;
|
|
s.SetLength(slen);
|
|
if ( !a.ReadChar(slen,s.Array()) )
|
|
return 0;
|
|
const char* sArray = s.Array();
|
|
if ( 0 != sArray && 0 != sArray[0] )
|
|
{
|
|
unsigned int error_status = 0;
|
|
int wLen = ON_ConvertUTF8ToWideChar(false,sArray,-1,0,0,&error_status,0,0,0);
|
|
if ( wLen > 0 && 0 == error_status )
|
|
{
|
|
rdk_xml_document_data.SetLength(wLen+2);
|
|
wLen = ON_ConvertUTF8ToWideChar(false,sArray,-1,rdk_xml_document_data.Array(),wLen+1,&error_status,0,0,0);
|
|
if ( wLen > 0 && 0 == error_status )
|
|
rdk_xml_document_data.SetLength(wLen);
|
|
else
|
|
{
|
|
rdk_xml_document_data.SetLength(0);
|
|
}
|
|
}
|
|
if ( 0 != error_status )
|
|
{
|
|
ON_ERROR("RDK xml document settings is not a valid UTF-8 string.");
|
|
}
|
|
}
|
|
}
|
|
|
|
return rdk_xml_document_data.Length() > 0;
|
|
}
|
|
|
|
#if defined(ON_COMPILER_MSC)
|
|
#pragma ON_PRAGMA_WARNING_POP
|
|
#endif
|
|
|
|
|
|
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 < 50);
|
|
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.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.IsZeroDigentOrEmptyContentHash());
|
|
}
|
|
|
|
std::shared_ptr<ONX_Model> ONX_ModelTest::SourceModel() const
|
|
{
|
|
return m_model[0];
|
|
}
|
|
|
|
std::shared_ptr<ONX_Model> ONX_ModelTest::ReadWriteReadModel() const
|
|
{
|
|
const ONX_Model* source_model = SourceModel().get();
|
|
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
|
|
)
|
|
{
|
|
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 (bSourceModel)
|
|
text_file_path += L"original";
|
|
else
|
|
text_file_path += L"copy";
|
|
|
|
#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);
|
|
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 ONX_Model* model = SourceModel().get();
|
|
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);
|
|
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 ONX_Model* model = ReadWriteReadModel().get();
|
|
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);
|
|
}
|
|
|
|
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::SkipCompare(Source3dmFileVersion())
|
|
&& ONX_ModelTest::Type::ReadWriteReadCompare == test_type;
|
|
const unsigned int imax
|
|
= bSkipCompare
|
|
? static_cast<const unsigned char>(ONX_ModelTest::Type::ReadWriteRead)
|
|
: static_cast<const unsigned char>(test_type);
|
|
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)
|
|
{
|
|
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 files: ");
|
|
InternalDumpResultAndErrorCount(m_test_results[i], m_error_counts[i], text_log);
|
|
if (i >= imax)
|
|
break;
|
|
|
|
i++;
|
|
text_log.Print("Read temporary files: ");
|
|
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();
|
|
|
|
}
|