/* $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 . // //////////////////////////////////////////////////////////////// */ #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 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(ON_ModelComponent::Type::Unset) ) continue; if (i == static_cast(ON_ModelComponent::Type::Mixed) ) continue; if (i > static_cast(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(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(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(light)->m_light_id = id; if (name != light->m_light_name) const_cast(light)->m_light_name = name; } const ON_3dmObjectAttributes* attributes = geometry_component->Attributes(nullptr); if (nullptr != attributes) { if (id != attributes->m_uuid) const_cast(attributes)->m_uuid = id; if (name != attributes->m_name) const_cast(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(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(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(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(ON_3dmArchiveTableType::bitmap_table) & table_filter) ) { for(;;) { ON_Bitmap* bitmap = nullptr; rc = archive.Read3dmBitmap(&bitmap); if ( rc==0 ) break; // end of bitmap table if (rc < 0) break; if ( AddModelComponentForExperts(bitmap,bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty() ) delete bitmap; } } // If BeginRead3dmBitmapTable() returns true, // then you MUST call EndRead3dmBitmapTable(). if ( !archive.EndRead3dmBitmapTable() ) { return false; } } // STEP 5: REQUIRED - Read texture mapping table if ( archive.BeginRead3dmTextureMappingTable() ) { if ( 0 != (static_cast(ON_3dmArchiveTableType::texture_mapping_table) & table_filter) ) { for(;;) { ON_TextureMapping* texture_mapping = nullptr; rc = archive.Read3dmTextureMapping(&texture_mapping); if ( rc==0 ) break; // end of texture_mapping table if ( rc < 0 ) break; if (AddModelComponentForExperts(texture_mapping, bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty()) delete texture_mapping; } } // If BeginRead3dmTextureMappingTable() returns true, // then you MUST call EndRead3dmTextureMappingTable(). if ( !archive.EndRead3dmTextureMappingTable() ) { return false; } } // STEP 6: REQUIRED - Read render material table if ( archive.BeginRead3dmMaterialTable() ) { const ON_UUID settings_current_id = m_settings.CurrentMaterialId(); const int settings_current_index = m_settings.CurrentMaterialIndex(); bool bSetCurrentById = !(ON_nil_uuid == settings_current_id); bool bSetCurrentByIndex = false == bSetCurrentById && settings_current_index >= 0; m_settings.SetCurrentMaterialId(ON_nil_uuid); if ( 0 != (static_cast(ON_3dmArchiveTableType::material_table) & table_filter) ) { for(;;) { ON_Material* material = nullptr; rc = archive.Read3dmMaterial(&material); if ( rc==0 ) break; // end of material table if (rc < 0) break; // index or id might be modified by AddModelComponentForExperts() const bool bSetAsCurrent = (bSetCurrentById && settings_current_id == material->Id()) || (bSetCurrentByIndex && settings_current_index == material->Index()); if ( AddModelComponentForExperts(material,bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty() ) { delete material; } else if (bSetAsCurrent) { m_settings.SetCurrentMaterialId(material->Id()); bSetCurrentById = false; bSetCurrentByIndex = false; } } } // If BeginRead3dmMaterialTable() returns true, // then you MUST call EndRead3dmMaterialTable(). if ( !archive.EndRead3dmMaterialTable() ) { return false; } } // STEP 7: REQUIRED - Read line type table if ( archive.BeginRead3dmLinetypeTable() ) { const ON_UUID settings_current_id = m_settings.CurrentLinePatternId(); const int settings_current_index = m_settings.CurrentLinePatternIndex(); bool bSetCurrentById = !(ON_nil_uuid == settings_current_id); bool bSetCurrentByIndex = false == bSetCurrentById && settings_current_index >= 0; m_settings.SetCurrentLinePatternId(ON_nil_uuid); if ( 0 != (static_cast(ON_3dmArchiveTableType::linetype_table) & table_filter) ) { for(;;) { ON_Linetype* line_pattern = nullptr; rc = archive.Read3dmLinetype(&line_pattern); if ( rc==0 ) break; // end of linetype table if (rc < 0) break; // index or id might be modified by AddModelComponentForExperts() const bool bSetAsCurrent = (bSetCurrentById && settings_current_id == line_pattern->Id()) || (bSetCurrentByIndex && settings_current_index == line_pattern->Index()); if ( AddModelComponentForExperts(line_pattern,bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty() ) { delete line_pattern; } else if (bSetAsCurrent) { m_settings.SetCurrentLinePatternId(line_pattern->Id()); bSetCurrentById = false; bSetCurrentByIndex = false; } } } // If BeginRead3dmLinetypeTable() returns true, // then you MUST call EndRead3dmLinetypeTable(). if ( !archive.EndRead3dmLinetypeTable() ) { return false; } } // STEP 8: REQUIRED - Read layer table if ( archive.BeginRead3dmLayerTable() ) { const ON_UUID settings_current_id = m_settings.CurrentLayerId(); const int settings_current_index = m_settings.CurrentLayerIndex(); bool bSetCurrentById = !(ON_nil_uuid == settings_current_id); bool bSetCurrentByIndex = false == bSetCurrentById && settings_current_index >= 0; m_settings.SetCurrentLayerId(ON_nil_uuid); if ( 0 != (static_cast(ON_3dmArchiveTableType::layer_table) & table_filter) ) { for(;;) { ON_Layer* layer = nullptr; rc = archive.Read3dmLayer(&layer); if ( rc==0 ) break; // end of layer table if (rc < 0) break; // index or id might be modified by AddModelComponentForExperts() const bool bSetAsCurrent = (bSetCurrentById && settings_current_id == layer->Id()) || (bSetCurrentByIndex && settings_current_index == layer->Index()); if (AddModelComponentForExperts(layer, bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty()) { delete layer; } else if (bSetAsCurrent) { m_settings.SetCurrentLayerId(layer->Id()); bSetCurrentById = false; bSetCurrentByIndex = false; } } } // If BeginRead3dmLayerTable() returns true, // then you MUST call EndRead3dmLayerTable(). if ( !archive.EndRead3dmLayerTable() ) { return false; } ON_UUID current_layer_id = m_settings.CurrentLayerId(); const ON_Layer* current_layer = (ON_nil_uuid == current_layer_id) ? nullptr : ON_Layer::FromModelComponentRef(LayerFromId(current_layer_id), nullptr); if (nullptr == current_layer) { current_layer_id = ON_nil_uuid; int layer_count = 0; ONX_ModelComponentIterator layer_it(*this, ON_ModelComponent::Type::Layer); for (const ON_Layer* layer = ON_Layer::Cast(layer_it.FirstComponent()); nullptr != layer; layer = ON_Layer::Cast(layer_it.NextComponent())) { layer_count++; if (layer->IsVisible() && false == layer->IsLocked()) { current_layer = layer; if ( layer->ParentIdIsNil() ) break; } } if (nullptr == current_layer && layer_count > 0) { current_layer = ON_Layer::Cast(layer_it.FirstComponent()); } if (nullptr != current_layer) m_settings.SetCurrentLayerId(current_layer->Id()); else AddDefaultLayer(nullptr, ON_Color::UnsetColor); } } // STEP 9: REQUIRED - Read group table if ( archive.BeginRead3dmGroupTable() ) { if ( 0 != (static_cast(ON_3dmArchiveTableType::group_table) & table_filter) ) { for(;;) { ON_Group* group = nullptr; rc = archive.Read3dmGroup(&group); if ( rc==0 ) break; // end of group table if (rc < 0) break; if (AddModelComponentForExperts(group, bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty()) delete group; } } // If BeginRead3dmGroupTable() returns true, // then you MUST call EndRead3dmGroupTable(). if ( !archive.EndRead3dmGroupTable() ) { return false; } } // STEP 11: REQUIRED - Read dimstyle table if ( archive.BeginRead3dmDimStyleTable() ) { const ON_UUID settings_current_id = m_settings.CurrentDimensionStyleId(); const int settings_current_index = m_settings.CurrentDimensionStyleIndex(); bool bSetCurrentById = !(ON_nil_uuid == settings_current_id); bool bSetCurrentByIndex = false == bSetCurrentById && settings_current_index >= 0; m_settings.SetCurrentDimensionStyleId(ON_nil_uuid); if ( 0 != (static_cast(ON_3dmArchiveTableType::dimension_style_table) & table_filter) ) { for(;;) { ON_DimStyle* dimension_style = nullptr; rc = archive.Read3dmDimStyle(&dimension_style); if ( rc==0 ) break; // end of dimstyle table if ( rc < 0 ) break; // index or id might be modified by AddModelComponentForExperts() const bool bSetAsCurrent = (bSetCurrentById && settings_current_id == dimension_style->Id()) || (bSetCurrentByIndex && settings_current_index == dimension_style->Index()); if (AddModelComponentForExperts(dimension_style, bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty()) { delete dimension_style; } else if (bSetAsCurrent) { m_settings.SetCurrentDimensionStyleId(dimension_style->Id()); bSetCurrentById = false; bSetCurrentByIndex = false; } } } // If BeginRead3dmDimStyleTable() returns true, // then you MUST call EndRead3dmDimStyleTable(). if ( !archive.EndRead3dmDimStyleTable() ) { return false; } } // STEP 12: REQUIRED - Read render lights table if ( archive.BeginRead3dmLightTable() ) { if ( 0 != (static_cast(ON_3dmArchiveTableType::light_table) & table_filter) ) { for(;;) { ON_ModelGeometryComponent* model_light = nullptr; rc = archive.Read3dmModelLight(&model_light); if (rc == 0) break; // end of light table if ( rc < 0 ) break; if (AddModelComponentForExperts(model_light, bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty()) delete model_light; } } // If BeginRead3dmLightTable() returns true, // then you MUST call EndRead3dmLightTable(). if ( !archive.EndRead3dmLightTable() ) { return false; } } // STEP 13 - read hatch pattern table if ( archive.BeginRead3dmHatchPatternTable() ) { const ON_UUID settings_current_id = m_settings.CurrentHatchPatternId(); const int settings_current_index = ON_UNSET_INT_INDEX; bool bSetCurrentById = !(ON_nil_uuid == settings_current_id); bool bSetCurrentByIndex = false; m_settings.SetCurrentHatchPatternId(ON_nil_uuid); if ( 0 != (static_cast(ON_3dmArchiveTableType::hatchpattern_table) & table_filter) ) { for(;;) { ON_HatchPattern* hatch_pattern = nullptr; rc = archive.Read3dmHatchPattern(&hatch_pattern); if ( rc==0 ) break; // end of hatchpattern table if ( rc < 0 ) break; // index or id might be modified by AddModelComponentForExperts() const bool bSetAsCurrent = (bSetCurrentById && settings_current_id == hatch_pattern->Id()) || (bSetCurrentByIndex && settings_current_index == hatch_pattern->Index()); if (AddModelComponentForExperts(hatch_pattern, bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty()) { delete hatch_pattern; } else if (bSetAsCurrent) { m_settings.SetCurrentHatchPatternId(hatch_pattern->Id()); bSetCurrentById = false; bSetCurrentByIndex = false; } } } // If BeginRead3dmHatchPatternTable() returns true, // then you MUST call EndRead3dmHatchPatternTable(). if ( !archive.EndRead3dmHatchPatternTable() ) { return false; } } // STEP 14: REQUIRED - Read instance definition table if ( archive.BeginRead3dmInstanceDefinitionTable() ) { if ( 0 != (static_cast(ON_3dmArchiveTableType::instance_definition_table) & table_filter) ) { for(;;) { ON_InstanceDefinition* instance_definition = nullptr; rc = archive.Read3dmInstanceDefinition(&instance_definition); if ( rc==0 ) break; // end of instance definition table if ( rc < 0 ) break; if (AddModelComponentForExperts(instance_definition, bManageComponents,bResolveIdAndNameConflicts,bUpdateComponentIdentification).IsEmpty()) delete instance_definition; } } // If BeginRead3dmInstanceDefinitionTable() returns true, // then you MUST call EndRead3dmInstanceDefinitionTable(). if ( !archive.EndRead3dmInstanceDefinitionTable() ) { return false; } } return (0 == archive.CriticalErrorCount()); } bool ONX_Model::IncrementalReadModelGeometry( ON_BinaryArchive& archive, bool bManageModelGeometryComponent, bool bManageGeometry, bool bManageAttributes, unsigned int object_filter, ON_ModelComponentReference& model_component_reference ) { model_component_reference = ON_ModelComponentReference::Empty; ON_3dmArchiveTableType active_table = archive.Active3dmTable(); if (ON_3dmArchiveTableType::Unset == active_table) { ON_3dmArchiveTableType previous_table = archive.Previous3dmTable(); if (ON_3dmArchiveTableType::Unset == previous_table) { // Yokel probably forgot to call IncrementalReadBegin() ON_ERROR("IncrementalReadBegin() must be called before IncrementalReadModelGeometry()."); return false; } if (static_cast(previous_table) >= static_cast(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(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(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(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(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 && version > ON_BinaryArchive::CurrentArchiveVersion()/10) || (version >= 50 && 0 != (version % 10)) ) { version = 0; if ( error_log) error_log->Print("ONX_Model::Write version parameter = %d; it must be 0, or >= 2 and <= %d, or a multiple of 10 >= 50 and <= %d.\n", version,ON_BinaryArchive::CurrentArchiveVersion()/10,ON_BinaryArchive::CurrentArchiveVersion()); } } if ( !archive.WriteMode() ) { // You passed in a bogus archive. You must pass ON::archive_mode::write3dm to the // archive constructor. if ( error_log) error_log->Print("ONX_Model::Write archive.Mode() is not ON::archive_mode::write3dm.\n" "See ONX_Model::Write example in the header file.\n"); return false; } bool ok; // START SECTION ok = archive.Write3dmStartSection( version, static_cast< const char* >(m_sStartSectionComments) ); if ( !ok ) { // make sure your archive was created with ON::archive_mode::write3dm mode. if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmStartSection() failed.\n" "Your archive is not properly initialized\n" "(make sure you passed ON::archive_mode::write3dm to the constructor),\n" "a file is locked, a disk is locked, or something along those lines.\n"); return false; } // PROPERTIES SECTION if ( m_properties.m_RevisionHistory.m_revision_count == 0 ) const_cast(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 iref_list(256); const ON_SimpleArray& 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& 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& 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& paths, ON_SimpleArray& embedded_files_as_buffers) { ON_SimpleArray dummy; return GetRDKEmbeddedFiles(docud, paths, embedded_files_as_buffers, dummy); } //Returns the number of embedded files static unsigned int SkipArchiveToFiles(ON_Read3dmBufferArchive& archive, int iGooLen) { if (!archive.ReadMode()) return 0; int version = 0; if (!archive.ReadInt(&version)) return 0; if (4 != version) return 0; //Read out the document data, and throw it away. { int slen = 0; if (!archive.ReadInt(&slen)) return 0; if (slen <= 0) return 0; if (slen + 4 > iGooLen) return 0; ON_String s; s.SetLength(slen); if (!archive.ReadChar(slen, s.Array())) return 0; } unsigned int iCount = 0; if (!archive.ReadInt(&iCount)) return 0; return iCount; } static bool SeekPastCompressedBuffer(ON_BinaryArchive& archive) { if (!archive.ReadMode()) return false; bool rc = false; unsigned int buffer_crc0 = 0; char method = 0; size_t sizeof__outbuffer; if (!archive.ReadCompressedBufferSize(&sizeof__outbuffer)) return false; if (0 == sizeof__outbuffer) return true; if (!archive.ReadInt(&buffer_crc0)) // 32 bit crc of uncompressed buffer return false; if (!archive.ReadChar(&method)) return false; if (method != 0 && method != 1) return false; switch (method) { case 0: // uncompressed rc = archive.SeekForward(sizeof__outbuffer); break; case 1: // compressed { ON__UINT32 tcode = 0; ON__INT64 big_value = 0; rc = archive.BeginRead3dmBigChunk(&tcode, &big_value); if (rc) rc = archive.EndRead3dmChunk(); } break; } return rc; } bool ONX_Model::GetRDKEmbeddedFilePaths(const ONX_Model_UserData& docud, ON_ClassArray& paths) { 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); const unsigned int iCount = SkipArchiveToFiles(a, docud.m_goo.m_value); if (0 == iCount) return false; for (unsigned int i = 0; i < iCount; i++) { ON_wString sPath; if (!a.ReadString(sPath)) return false; paths.Append(sPath); SeekPastCompressedBuffer(a); } return paths.Count() > 0; } bool ONX_Model::GetRDKEmbeddedFiles(const ONX_Model_UserData& docud, ON_ClassArray& paths, ON_SimpleArray& embedded_files_as_buffers, ON_SimpleArray& buffer_sizes) { 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); const unsigned int iCount = SkipArchiveToFiles(a, docud.m_goo.m_value); if (0 == 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); buffer_sizes.Append(size); unpacked++; } else { delete[] buffer; } } } return unpacked > 0; } bool ONX_Model::GetRDKEmbeddedFile(const ONX_Model_UserData& docud, const wchar_t* path, ON_SimpleArray& bytes) { 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); const unsigned int iCount = SkipArchiveToFiles(a, docud.m_goo.m_value); if (0 == iCount) return false; for (unsigned int i = 0; i < iCount; i++) { ON_wString sPath; if (!a.ReadString(sPath)) return false; if (0 == sPath.ComparePath(path)) { size_t size; if (!a.ReadCompressedBufferSize(&size)) return false; bytes.Destroy(); bytes.Reserve(size); bool bFailedCRC = false; bool bRet = a.ReadCompressedBuffer(size, bytes.Array(), &bFailedCRC); if (!bRet || bFailedCRC) { return false; } bytes.SetCount((int)size); return true; } else { SeekPastCompressedBuffer(a); } } return false; } 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(a) < static_cast(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(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(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(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_ModelTest::SourceModel() const { return m_model[0]; } std::shared_ptr 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 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(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(ONX_ModelTest::Type::ReadWriteReadCompare); const bool bSkipCompare = ONX_ModelTest::SkipCompare(Source3dmFileVersion()) && ONX_ModelTest::Type::ReadWriteReadCompare == test_type; const unsigned int imax = bSkipCompare ? static_cast(ONX_ModelTest::Type::ReadWriteRead) : static_cast(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 model0_sp = std::shared_ptr(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(text_log_3dm_archive_name)); // read the original file text_log->Print("Calling %s ...\n", static_cast(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(read0_description)); break; } else { text_log->Print("... %s ", static_cast(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(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(write1_description)); break; } else { text_log->Print("... %s ", static_cast(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(read1_description)); text_log->PushIndent(); ONX_Model* model1 = new ONX_Model(); std::shared_ptr model1_sp = std::shared_ptr(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(read1_description)); break; } else { text_log->Print("... %s ", static_cast(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(); }