// // Copyright (c) 1993-2022 Robert McNeel & Associates. All rights reserved. // OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert // McNeel & Associates. // // THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. // ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF // MERCHANTABILITY ARE HEREBY DISCLAIMED. // // For complete openNURBS copyright information see . // //////////////////////////////////////////////////////////////// #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 class ON_OBSOLETE_V5_HatchExtra : public ON_UserData { //March 23, 2008 - LW //Adding ON_OBSOLETE_V5_HatchExtra class to support movable base point for hatches //This should be combined with the ON_Hatch class next time that is possible // Don't put this extension class in a header file or export it. ON_OBJECT_DECLARE(ON_OBSOLETE_V5_HatchExtra); public: ON_OBSOLETE_V5_HatchExtra(); ~ON_OBSOLETE_V5_HatchExtra() = default; ON_OBSOLETE_V5_HatchExtra(const ON_OBSOLETE_V5_HatchExtra&) = default; ON_OBSOLETE_V5_HatchExtra& operator=(const ON_OBSOLETE_V5_HatchExtra&) = default; public: static ON_OBSOLETE_V5_HatchExtra* HatchExtension(const ON_Hatch* pHatch); // override virtual ON_Object::Dump function void Dump( ON_TextLog& text_log ) const override; // override virtual ON_Object::SizeOf function unsigned int SizeOf() const override; // override virtual ON_Object::Write function bool Write(ON_BinaryArchive& binary_archive) const override; // override virtual ON_Object::Read function bool Read(ON_BinaryArchive& binary_archive) override; // override virtual ON_UserData::GetDescription function bool GetDescription( ON_wString& description ) override; // override virtual ON_UserData::Archive function bool Archive() const override; bool WriteToArchive( const class ON_BinaryArchive& archive, const class ON_Object* parent_object ) const override; bool DeleteAfterWrite( const class ON_BinaryArchive& archive, const class ON_Object* parent_object ) const override; bool DeleteAfterRead( const class ON_BinaryArchive& archive, class ON_Object* parent_object ) const override; ON_2dPoint m_basepoint = ON_2dPoint::Origin; }; ON_OBJECT_IMPLEMENT(ON_OBSOLETE_V5_HatchExtra,ON_UserData,"3FF7007C-3D04-463f-84E3-132ACEB91062"); ON_OBSOLETE_V5_HatchExtra* ON_OBSOLETE_V5_HatchExtra::HatchExtension(const ON_Hatch* pHatch) { ON_OBSOLETE_V5_HatchExtra* pExtra = nullptr; if(pHatch) { pExtra = ON_OBSOLETE_V5_HatchExtra::Cast(pHatch->GetUserData(ON_CLASS_ID(ON_OBSOLETE_V5_HatchExtra))); if(pExtra == nullptr) { pExtra = new ON_OBSOLETE_V5_HatchExtra; if(pExtra) { if(!const_cast(pHatch)->AttachUserData(pExtra)) { delete pExtra; pExtra = nullptr; } } } } return pExtra; } ON_OBSOLETE_V5_HatchExtra::ON_OBSOLETE_V5_HatchExtra() { m_userdata_uuid = ON_CLASS_ID(ON_OBSOLETE_V5_HatchExtra); m_application_uuid = ON_opennurbs5_id; // opennurbs.dll reads/writes this userdata // The id must be the version 5 id because // V6 SaveAs V5 needs to work, but SaveAs // V4 should not write this userdata. m_userdata_copycount = 0; } void ON_OBSOLETE_V5_HatchExtra::Dump(ON_TextLog& text_log) const { } unsigned int ON_OBSOLETE_V5_HatchExtra::SizeOf() const { unsigned int sz = ON_UserData::SizeOf(); sz += sizeof(*this)-sizeof(ON_UserData); return sz; } bool ON_OBSOLETE_V5_HatchExtra::Write(ON_BinaryArchive& archive) const { bool rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0); if(rc) rc = archive.WriteUuid( ON_nil_uuid); if(rc) rc = archive.WritePoint(m_basepoint); if(!archive.EndWrite3dmChunk()) rc = false; return rc; } bool ON_OBSOLETE_V5_HatchExtra::Read(ON_BinaryArchive& archive) { int major_version = 0; int minor_version = 0; bool rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version); if(major_version != 1) rc = false; m_basepoint.Set(0.0,0.0); ON_UUID ignored_id = ON_nil_uuid; if(rc) rc = archive.ReadUuid(ignored_id); if(rc) rc = archive.ReadPoint(m_basepoint); if(!archive.EndRead3dmChunk()) rc = false; return rc; } bool ON_OBSOLETE_V5_HatchExtra::GetDescription( ON_wString& description) { description = L"OBSOLETE V5 ON_Hatch basepoint userdata."; return true; } bool ON_OBSOLETE_V5_HatchExtra::Archive() const { return true; } bool ON_OBSOLETE_V5_HatchExtra::WriteToArchive( const class ON_BinaryArchive& archive, const class ON_Object* parent_object ) const { return (50 == archive.Archive3dmVersion() && nullptr != ON_Hatch::Cast(parent_object)); } bool ON_OBSOLETE_V5_HatchExtra::DeleteAfterWrite( const class ON_BinaryArchive& archive, const class ON_Object* parent_object ) const { return true; } bool ON_OBSOLETE_V5_HatchExtra::DeleteAfterRead( const class ON_BinaryArchive& archive, class ON_Object* parent_object ) const { for (;;) { if (false == m_basepoint.IsValid()) break; ON_Hatch* hatch = ON_Hatch::Cast(parent_object); if (nullptr == hatch) break; ON_2dPoint p = hatch->BasePoint2d(); if (p.x != m_basepoint.x || p.y != m_basepoint.y) { hatch->SetBasePoint(m_basepoint); } break; } return true; } ///////////////////////////////////////////////////////////////// // class ON_HatchLine ///////////////////////////////////////////////////////////////// ON_HatchLine::ON_HatchLine( double angle_in_radians ) : m_angle_radians( angle_in_radians) {} ON_HatchLine::ON_HatchLine( double angle_in_radians, ON_2dPoint base, ON_2dVector offset, const ON_SimpleArray& dashes ) : m_angle_radians( angle_in_radians) , m_base( base) , m_offset( offset) , m_dashes( dashes) {} bool ON_HatchLine::operator==(const ON_HatchLine& src) const { return( m_angle_radians == src.m_angle_radians && m_base == src.m_base && m_offset == src.m_offset && m_dashes == src.m_dashes ); } bool ON_HatchLine::operator!=(const ON_HatchLine& src) const { return( m_angle_radians != src.m_angle_radians || m_base != src.m_base || m_offset != src.m_offset || m_dashes != src.m_dashes ); } bool ON_HatchLine::IsValid( ON_TextLog* text_log) const { bool rc = m_angle_radians >= 0.0; if( !rc) { if( text_log) text_log->Print( "Angle ( %lf) must be >= 0.0\n", m_angle_radians); return false; } rc = m_angle_radians < ON_PI * 2.0; if( !rc) { if( text_log) text_log->Print( "Angle ( %lf) must be < 2*Pi.\n", m_angle_radians); return false; } rc = m_base.IsValid(); if( !rc) { if( text_log) text_log->Print( "Base is not a valid point.\n"); return false; } rc = m_offset.IsValid(); if( !rc) { if( text_log) text_log->Print( "Offset is not a valid vector.\n"); return false; } return true; } void ON_HatchLine::Dump( ON_TextLog& dump) const { dump.Print( "ON_HatchLine: angle = %lf radians ( %lf degrees) ", AngleRadians(), AngleDegrees()); dump.Print( " base = "); dump.Print( m_base); dump.Print( " offset = "); dump.Print( m_offset); int count = m_dashes.Count(); dump.Print( "\nDash count = %d: ", count); for( int i = 0; i < count; i++) { dump.Print( "%lf", Dash( i)); if( i < count-1) dump.Print( ", "); } dump.Print( "\n"); } bool ON_HatchLine::Write(ON_BinaryArchive& archive) const { if (archive.Archive3dmVersion() < 60) return WriteV5(archive); const int major_version = 1; const int minor_version = 0; if (!archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK, major_version, minor_version)) return false; bool rc = false; for (;;) { if (!archive.WriteDouble(m_angle_radians)) break; if (!archive.WritePoint(m_base)) break; if (!archive.WriteVector(m_offset)) break; if (!archive.WriteArray(m_dashes)) break; rc = true; break; } if (!archive.EndWrite3dmChunk()) rc = false; return rc; } static bool Internal_UseHatchReadV5( ON_BinaryArchive& archive ) { if (archive.Archive3dmVersion() < 60) return true; if (archive.Archive3dmVersion() > 60) return false; // There are 4 possible Feb 26 version numbers depending on the build system // archive.Archive3dmVersion() % 4 = (0=developer,1=windows build system,2 = mac build system, 3=unused) const unsigned int Feb_26_2016_opennurbs_version_number0 = 2348833956; const unsigned int Feb_26_2016_opennurbs_version_number3 = 2348833956+3; const unsigned int archive_opennurbs_version_number = archive.ArchiveOpenNURBSVersion(); if (archive_opennurbs_version_number < Feb_26_2016_opennurbs_version_number0) return true; // "old" opennurbs hatch code if ( archive_opennurbs_version_number >= Feb_26_2016_opennurbs_version_number0 && archive_opennurbs_version_number <= Feb_26_2016_opennurbs_version_number3 ) { // new hatch IO code pushed to master branch on Feb 26, 2016. // THis clunky test has to be used on files written with the Feb 26, 2016 version of opennurbs source code. ON__UINT32 tcode = 0; ON__INT64 big_value = 0; archive.PeekAt3dmBigChunkType(&tcode, &big_value); if (TCODE_ANONYMOUS_CHUNK == tcode) return false; // use "new" IO return true; // use "old" IO } // 6.0 or later file created with executable using opennurbs code on Feb 27, 2016 or later return false; } bool ON_HatchLine::Read(ON_BinaryArchive& archive) { if ( Internal_UseHatchReadV5(archive) ) return ReadV5(archive); *this = ON_HatchLine::SolidHorizontal; int major_version = 0; int minor_version = 0; if (!archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK, &major_version, &minor_version)) return false; bool rc = false; for (;;) { if (1 != major_version) break; if (!archive.ReadDouble(&m_angle_radians)) break; if (!archive.ReadPoint(m_base)) break; if (!archive.ReadVector(m_offset)) break; if (!archive.ReadArray(m_dashes)) break; rc = true; break; } if (!archive.EndRead3dmChunk()) rc = false; return rc; } bool ON_HatchLine::WriteV5( ON_BinaryArchive& ar) const { // Incorrectly designed chunk IO - never change version numbers. bool rc = ar.Write3dmChunkVersion(1,1); if (rc) rc = ar.WriteDouble( m_angle_radians); if (rc) rc = ar.WritePoint( m_base); if (rc) rc = ar.WriteVector( m_offset); if (rc) rc = ar.WriteArray( m_dashes); return rc; } bool ON_HatchLine::ReadV5( ON_BinaryArchive& ar) { *this = ON_HatchLine::SolidHorizontal; // Incorrectly designed chunk IO - never change version numbers. int major_version = 0; int minor_version = 0; bool rc = ar.Read3dmChunkVersion( &major_version, &minor_version); if (rc) rc = (1 == major_version); if ( rc ) { if ( rc) rc = ar.ReadDouble( &m_angle_radians); if ( rc) rc = ar.ReadPoint( m_base); if ( rc) rc = ar.ReadVector( m_offset); if ( rc) rc = ar.ReadArray( m_dashes); } return rc; } int ON_HatchLine::Compare( const ON_HatchLine& a, const ON_HatchLine& b ) { int rc = ON_CompareDouble(a.m_angle_radians, b.m_angle_radians); if (rc) return rc; rc = ON_2dPoint::Compare(a.m_base, b.m_base); if (rc) return rc; rc = ON_2dVector::Compare(a.m_offset, b.m_offset); if (rc) return rc; size_t acount = (size_t)a.m_dashes.UnsignedCount(); size_t bcount = (size_t)b.m_dashes.UnsignedCount(); if (acount < bcount) return -1; if (acount > bcount) return 1; return ON_CompareDoubleArray(acount, a.m_dashes.Array(), b.m_dashes.Array()); } // ON_HatchLine Interface double ON_HatchLine::AngleRadians() const { return m_angle_radians; } double ON_HatchLine::AngleDegrees() const { if (0.0 <= m_angle_radians && m_angle_radians < 2.0*ON_PI) { double angle_degrees = m_angle_radians*(180.0/ON_PI); if (angle_degrees >= 360.0) angle_degrees = 0.0; return angle_degrees; } return m_angle_radians; // UNSET VALUE OR NaN } static double DealWithUnsetAngles( double a ) { if (((double)ON_UNSET_FLOAT) == a) return ON_UNSET_VALUE; if (((double)ON_UNSET_POSITIVE_FLOAT) == a) return ON_UNSET_POSITIVE_VALUE; if (a < ON_UNSET_VALUE) return ON_UNSET_VALUE; if (a > ON_UNSET_POSITIVE_VALUE) return ON_UNSET_POSITIVE_VALUE; return a; } void ON_HatchLine::SetAngleRadians( double angle_radians) { angle_radians = DealWithUnsetAngles(angle_radians); if (angle_radians > ON_UNSET_VALUE && angle_radians < ON_UNSET_POSITIVE_VALUE) { const double twopi = ON_PI * 2.0; // clamp between [0 2pi) while (angle_radians < 0.0) angle_radians += twopi; while (angle_radians > twopi) angle_radians -= twopi; const double zero_angle_tol = ON_ZERO_TOLERANCE*twopi; if ( fabs(angle_radians) <= zero_angle_tol || fabs(angle_radians - twopi) <= zero_angle_tol ) angle_radians = 0.0; } m_angle_radians = angle_radians; } void ON_HatchLine::SetAngleDegrees(double angle_degrees) { angle_degrees = DealWithUnsetAngles(angle_degrees); if (angle_degrees > ON_UNSET_VALUE && angle_degrees < ON_UNSET_POSITIVE_VALUE) SetAngleRadians(angle_degrees*(ON_PI/180.0)); else m_angle_radians = angle_degrees; } ON_2dPoint ON_HatchLine::Base() const { return m_base; } void ON_HatchLine::SetBase( const ON_2dPoint& base) { m_base = base; } ON_2dVector ON_HatchLine::Offset() const { return m_offset; } void ON_HatchLine::SetOffset( const ON_2dVector& offset) { m_offset = offset; } int ON_HatchLine::DashCount() const { return m_dashes.Count(); } double ON_HatchLine::Dash( int index) const { if( index >= 0 && index < m_dashes.Count()) return m_dashes[index]; return 0.0; } void ON_HatchLine::AppendDash( double dash) { // if( fabs( dash) > ON_SQRT_EPSILON) m_dashes.Append( dash); } void ON_HatchLine::SetDashes( const ON_SimpleArray& dashes) { m_dashes = dashes; } const ON_SimpleArray& ON_HatchLine::Dashes() const { return m_dashes; } void ON_HatchLine::GetLineData( double& angle, ON_2dPoint& base, ON_2dVector& offset, ON_SimpleArray& dashes) const { angle = m_angle_radians; base = m_base; offset = m_offset; dashes = m_dashes; } double ON_HatchLine::GetPatternLength() const { int i; double length = 0.0; for( i = 0; i < m_dashes.Count(); i++) length += fabs( m_dashes[i]); return length; } // class ON_HatchPattern ///////////////////////////////////////////////////////////////// ON_OBJECT_IMPLEMENT( ON_HatchPattern, ON_ModelComponent, "064E7C91-35F6-4734-A446-79FF7CD659E1" ); ON_HatchPattern::HatchFillType ON_HatchPattern::HatchFillTypeFromUnsigned( unsigned hatch_fill_type_as_unsigned ) { switch (hatch_fill_type_as_unsigned) { ON_ENUM_FROM_UNSIGNED_CASE(ON_HatchPattern::HatchFillType::Solid); ON_ENUM_FROM_UNSIGNED_CASE(ON_HatchPattern::HatchFillType::Lines); //ON_ENUM_FROM_UNSIGNED_CASE(ON_HatchPattern::HatchFillType::Gradient); } ON_ERROR("Invalid hatch_fill_type_as_unsigned value."); return ON_HatchPattern::HatchFillType::Solid; } int ON_HatchPattern::Compare( const ON_HatchPattern& a, const ON_HatchPattern& b ) { int rc = ON_ModelComponent::CompareNameAndId(a, b); if (0 == rc) rc = CompareAppearance(a, b); if (0 == rc) rc = ON_wString::CompareOrdinal(a.m_description, b.m_description, false); return rc; } int ON_HatchPattern::CompareAppearance( const ON_HatchPattern& a, const ON_HatchPattern& b ) { const unsigned int atype = static_cast(a.FillType()); const unsigned int btype = static_cast(b.FillType()); if (atype < btype) return -1; if (atype > btype) return 1; if (ON_HatchPattern::HatchFillType::Lines != a.FillType()) return 0; const unsigned acount = a.m_lines.UnsignedCount(); const unsigned bcount = b.m_lines.UnsignedCount(); if (acount < bcount) return -1; if (acount > bcount) return 1; for (unsigned int i = 0; i < acount; i++) { int rc = ON_HatchLine::Compare(a.m_lines[i], b.m_lines[i]); if (0 != rc) return rc; } return 0; } const ON_HatchPattern* ON_HatchPattern::FromModelComponentRef( const class ON_ModelComponentReference& model_component_reference, const ON_HatchPattern* none_return_value ) { const ON_HatchPattern* hatch_pattern = ON_HatchPattern::Cast(model_component_reference.ModelComponent()); return (nullptr != hatch_pattern) ? hatch_pattern : none_return_value; } ON_HatchPattern::ON_HatchPattern() ON_NOEXCEPT : ON_ModelComponent(ON_ModelComponent::Type::HatchPattern) {} ON_HatchPattern::ON_HatchPattern(const ON_HatchPattern& src) : ON_ModelComponent(ON_ModelComponent::Type::HatchPattern, src) , m_type(src.m_type) , m_description(src.m_description) , m_lines(src.m_lines) {} bool ON_HatchPattern::IsValid( ON_TextLog* text_log) const { ON_HatchPattern::HatchFillType type = FillType(); bool rc = true; if( type != ON_HatchPattern::HatchFillTypeFromUnsigned(static_cast(type))) { if( text_log) text_log->Print( "Type field not set correctly.\n"); rc = false; } if( type == ON_HatchPattern::HatchFillType::Lines) { int count = m_lines.Count(); if( count < 1) { if( text_log) text_log->Print( "Line type pattern with no lines.\n"); return false; } for( int i = 0; i < count; i++) { if( !m_lines[i].IsValid()) { if( text_log) text_log->Print( "Line[%d] is not valid.\n", i); return false; } } return true; } return rc; } void ON_HatchPattern::Dump( ON_TextLog& dump) const { ON_ModelComponent::Dump(dump); switch( m_type) { case ON_HatchPattern::HatchFillType::Solid: dump.Print( "fill type: Solid"); break; case ON_HatchPattern::HatchFillType::Lines: dump.Print( "fill type: Lines"); break; //case ON_HatchPattern::HatchFillType::Gradient: // dump.Print( "fill type: Gradient"); // break; } dump.Print( "\n"); const wchar_t* wsDescription = static_cast< const wchar_t* >(m_description); if ( 0 == wsDescription ) wsDescription = L""; dump.Print( "Description: %ls\n", wsDescription); if( m_type == ON_HatchPattern::HatchFillType::Lines) { int count = m_lines.Count(); dump.Print( "Line count = %d\n", count); for( int i = 0; i < count; i++) { m_lines[i].Dump( dump); } dump.Print( "\n"); } } bool ON_HatchPattern::Write(ON_BinaryArchive& archive ) const { if (archive.Archive3dmVersion() < 60) return WriteV5(archive); const int major_version = 1; const int minor_version = 0; if (!archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK, major_version, minor_version)) return false; bool rc = false; for (;;) { unsigned int attributes_filter = ON_ModelComponent::Attributes::IdAttribute | ON_ModelComponent::Attributes::IndexAttribute | ON_ModelComponent::Attributes::NameAttribute; if (!WriteModelComponentAttributes(archive, attributes_filter)) break; if (!archive.WriteInt(static_cast(m_type))) break; if (!archive.WriteString(m_description)) break; if (!archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK, 0)) break; { const unsigned int count = (m_type == ON_HatchPattern::HatchFillType::Lines ? m_lines.UnsignedCount() : 0); bool lines_rc = archive.WriteInt(count); for (unsigned int i = 0; i < count && lines_rc; i++) lines_rc = m_lines[i].Write(archive); if (!archive.EndWrite3dmChunk()) lines_rc = false; if (false == lines_rc) break; } rc = true; break; } if (!archive.EndWrite3dmChunk()) rc = false; return rc; } bool ON_HatchPattern::Read(ON_BinaryArchive& archive) { if ( Internal_UseHatchReadV5(archive) ) return ReadV5(archive); int major_version = 0; int minor_version = 0; if (!archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK, &major_version, &minor_version)) return false; bool rc = false; for (;;) { if (!ReadModelComponentAttributes(archive)) break; unsigned int type_as_unsigned = 0; if (!archive.ReadInt(&type_as_unsigned)) break; m_type = ON_HatchPattern::HatchFillTypeFromUnsigned(type_as_unsigned); if (!archive.ReadString(m_description)) break; unsigned int tcode = 0; ON__INT64 value = 0; if (!archive.BeginRead3dmBigChunk(&tcode, &value)) break; { bool lines_rc = (TCODE_ANONYMOUS_CHUNK == tcode && value > 0); unsigned int count = 0; lines_rc = archive.ReadInt(&count); m_lines.Reserve(count); for (unsigned int i = 0; i < count && lines_rc; i++) lines_rc = m_lines.AppendNew().Read(archive); if (!archive.EndRead3dmChunk()) lines_rc = false; if (false == lines_rc) break; } rc = true; break; } if (!archive.EndRead3dmChunk()) rc = false; return rc; } bool ON_HatchPattern::WriteV5( ON_BinaryArchive& ar) const { bool rc = ar.Write3dmChunkVersion(1,2); if (rc) rc = ar.Write3dmReferencedComponentIndex( *this ); if (rc) rc = ar.WriteInt( static_cast(m_type)); ON_wString name = Name(); if (name.IsEmpty() && ModelComponentStatus().IsDeleted()) name = DeletedName(); if (rc) rc = ar.WriteString(name ); if (rc) rc = ar.WriteString( m_description); if( rc) { if( m_type == ON_HatchPattern::HatchFillType::Lines) { int i, count = m_lines.Count(); if ( count < 0 ) count = 0; rc = ar.WriteInt( count ); for( i = 0; i < count && rc; i++) rc = m_lines[i].Write( ar); } } // version 1.2 field if (rc) rc = ar.WriteUuid(Id()); return rc; } bool ON_HatchPattern::ReadV5( ON_BinaryArchive& ar) { *this = ON_HatchPattern::Unset; int major_version = 0; int minor_version = 0; bool rc = ar.Read3dmChunkVersion( &major_version, &minor_version); if (rc && 1 != major_version) rc = false; if ( rc ) { int hatchpattern_index = Index(); if (rc) rc = ar.ReadInt(&hatchpattern_index); if (rc) SetIndex(hatchpattern_index); int fill_type_as_unsigned = 0; if( rc) rc = ar.ReadInt( &fill_type_as_unsigned); if( rc) m_type = ON_HatchPattern::HatchFillTypeFromUnsigned(fill_type_as_unsigned); ON_wString hatchpattern_name; if( rc) rc = ar.ReadString( hatchpattern_name); if (rc) SetName(hatchpattern_name); if( rc) rc = ar.ReadString( m_description); if( rc) { if( m_type == ON_HatchPattern::HatchFillType::Lines) { m_lines.Empty(); int count = 0; rc = ar.ReadInt( &count); if( rc && count > 0 ) { m_lines.SetCapacity( count); for(int li = 0; rc && li < count; li++) { ON_HatchLine& line = m_lines.AppendNew(); rc = line.Read( ar); } } } } if ( minor_version >= 2 ) { ON_UUID hatchpattern_id = ON_nil_uuid; rc = ar.ReadUuid(hatchpattern_id); if (rc) SetId(hatchpattern_id); } } if (rc && IdIsNil()) SetId(); // modern hatch patterns require a unique id. return rc; } ON_HatchPattern::ON_HatchPattern::HatchFillType ON_HatchPattern::FillType() const { return m_type; } void ON_HatchPattern::SetFillType( ON_HatchPattern::HatchFillType type) { if ( m_type != type && type == ON_HatchPattern::HatchFillTypeFromUnsigned(static_cast(type)) ) { if (ON_HatchPattern::HatchFillType::Lines != type) m_lines.Destroy(); m_type = type; IncrementContentVersionNumber(); } } void ON_HatchPattern::SetDescription( const wchar_t* pDescription) { ON_wString s(pDescription); s.TrimLeftAndRight(); if (s != m_description) { m_description = s; IncrementContentVersionNumber(); } } const ON_wString& ON_HatchPattern::Description() const { return m_description; } // Line HatchPattern functions int ON_HatchPattern::HatchLineCount() const { return m_lines.Count(); } int ON_HatchPattern::AddHatchLine( const ON_HatchLine& line) { m_lines.Append( line); IncrementContentVersionNumber(); return m_lines.Count()-1; } const ON_HatchLine* ON_HatchPattern::HatchLine( int index) const { if( index >= 0 && index < m_lines.Count()) return &m_lines[index]; else return nullptr; } bool ON_HatchPattern::RemoveHatchLine( int index) { if( index >= 0 && index < m_lines.Count()) { m_lines.Remove( index); IncrementContentVersionNumber(); return true; } return false; } void ON_HatchPattern::RemoveAllHatchLines() { if ( m_lines.UnsignedCount() > 0 ) IncrementContentVersionNumber(); m_lines.Empty(); } int ON_HatchPattern::SetHatchLines( const ON_ClassArray& lines) { return SetHatchLines( lines.UnsignedCount(), lines.Array() ); } int ON_HatchPattern::SetHatchLines( size_t count, const ON_HatchLine* lines ) { if (count > 0 && nullptr != lines ) { m_lines.SetCount(0); m_lines.Append((int)count, lines); IncrementContentVersionNumber(); SetFillType(ON_HatchPattern::HatchFillType::Lines); } else { if (0 != m_lines.UnsignedCount()) IncrementContentVersionNumber(); m_lines.Destroy(); if ( ON_HatchPattern::HatchFillType::Lines == FillType()) SetFillType(ON_HatchPattern::HatchFillType::Solid); } return m_lines.Count(); } const ON_ClassArray& ON_HatchPattern::HatchLines() const { return m_lines; } // class ON_HatchLoop ///////////////////////////////////////////////////////////////// #if defined(OPENNURBS_EXPORTS) // When the Microsoft CRT(s) is/are used, this is the best // way to prevent crashes that happen when a hatch loop is // allocated with new in one DLL and deallocated with // delete in another DLL. void* ON_HatchLoop::operator new(size_t sz) { // ON_HatchLoop new return onmalloc(sz); } void ON_HatchLoop::operator delete(void* p) { // ON_HatchLoop delete onfree(p); } void* ON_HatchLoop::operator new[] (size_t sz) { // ON_HatchLoop array new return onmalloc(sz); } void ON_HatchLoop::operator delete[] (void* p) { // ON_HatchLoop array delete onfree(p); } void* ON_HatchLoop::operator new(size_t, void* p) { // ON_HatchLoop placement new return p; } void ON_HatchLoop::operator delete(void*, void*) { // ON_HatchLoop placement delete return; } #endif ON_HatchLoop::ON_HatchLoop() : m_type( ON_HatchLoop::ltOuter), m_p2dCurve( nullptr) { } ON_HatchLoop::ON_HatchLoop( ON_Curve* pCurve2d, eLoopType type) : m_type( type), m_p2dCurve( pCurve2d) { } ON_HatchLoop::ON_HatchLoop( const ON_HatchLoop& src) : m_type( src.m_type), m_p2dCurve( nullptr) { if( src.m_p2dCurve) m_p2dCurve = src.m_p2dCurve->DuplicateCurve(); } ON_HatchLoop::~ON_HatchLoop() { delete m_p2dCurve; } ON_HatchLoop& ON_HatchLoop::operator=( const ON_HatchLoop& src) { if( this != &src) { if( m_p2dCurve) delete m_p2dCurve; m_p2dCurve = src.m_p2dCurve->DuplicateCurve(); m_type = src.m_type; } return *this; } bool ON_HatchLoop::IsValid( ON_TextLog* text_log) const { bool rc = m_p2dCurve != nullptr; if( !rc) { if( text_log) text_log->Print( "2d loop curve is nullptr\n"); } if( rc) { rc = m_p2dCurve->IsValid( text_log); if( !rc) { if( text_log) text_log->Print( "Loop curve is not valid\n"); } } if(rc) { } if( rc) { ON_BoundingBox box; m_p2dCurve->GetBoundingBox( box); rc = ( box.Max().z == box.Min().z && box.Max().z == 0.0); if( !rc) { if( text_log) text_log->Print( "2d loop curve has non-zero z coordinates\n"); } } if( rc && m_type != ltOuter && m_type != ltInner) { if( text_log) text_log->Print( "Loop type is invalid.\n"); rc = false; } return rc; } void ON_HatchLoop::Dump( ON_TextLog& dump) const { if( m_type == ltOuter) dump.Print( "Outer hatch loop\n"); if( m_type == ltInner) dump.Print( "Inner hatch loop\n"); if ( 0 == m_p2dCurve ) { dump.Print( "2d curve: null pointer\n"); } else { dump.Print( "2d curve:\n"); m_p2dCurve->Dump(dump); } } bool ON_HatchLoop::Write( ON_BinaryArchive& ar) const { bool rc = ar.Write3dmChunkVersion(1,1); if( rc) rc = ar.WriteInt( m_type); if( rc) rc = ar.WriteObject( m_p2dCurve); return rc; } bool ON_HatchLoop::Read( ON_BinaryArchive& ar) { m_type = ltOuter; delete m_p2dCurve; m_p2dCurve = nullptr; int major_version = 0; int minor_version = 0; bool rc = ar.Read3dmChunkVersion( &major_version, &minor_version); if ( major_version == 1 ) { int type = 0; if( rc) rc = ar.ReadInt( &type); if( rc) { switch( type) { case ltOuter: m_type = ltOuter; break; case ltInner: m_type = ltInner; break; default: rc = false; break; } } if( rc) { ON_Object* pObj = nullptr; rc = ar.ReadObject( &pObj); if( pObj) { m_p2dCurve = ON_Curve::Cast( pObj); if( !m_p2dCurve) // read something, but it wasn't right { rc = false; delete pObj; } } } } return rc; } const ON_Curve* ON_HatchLoop::Curve() const { return m_p2dCurve; } bool ON_HatchLoop::SetCurve( const ON_Curve& curve) { ON_Curve* pC = curve.DuplicateCurve(); if( pC) { if(pC->Dimension() == 3 && !pC->ChangeDimension(2)) return false; if( m_p2dCurve) delete m_p2dCurve; m_p2dCurve = pC; } return true; } ON_HatchLoop::eLoopType ON_HatchLoop::Type() const { return m_type; } void ON_HatchLoop::SetType( eLoopType type) { m_type = type; } // class ON_Hatch ///////////////////////////////////////////////////////////////// ON_OBJECT_IMPLEMENT( ON_Hatch, ON_Geometry, "0559733B-5332-49d1-A936-0532AC76ADE5"); ON_Hatch::ON_Hatch( const ON_Hatch& src) : ON_Geometry(src) { Internal_CopyFrom(src); } ON_Hatch& ON_Hatch::operator=( const ON_Hatch& src) { if( this != &src) { Internal_Destroy(); ON_Geometry::operator =(src); Internal_CopyFrom(src); } return *this; } ON_Hatch::~ON_Hatch() { Internal_Destroy(); } void ON_Hatch::Internal_Destroy() { const int count = m_loops.Count(); for ( int i = 0; i < count; i++ ) { ON_HatchLoop* pL = m_loops[i]; if ( pL ) { m_loops[i] = nullptr; delete pL; } } m_loops.Destroy(); } void ON_Hatch::Internal_CopyFrom(const ON_Hatch& src) { m_plane = src.m_plane; m_pattern_scale = src.m_pattern_scale; m_pattern_rotation = src.m_pattern_rotation; m_basepoint = src.m_basepoint; const int count = src.m_loops.Count(); m_loops.SetCount(0); m_loops.Reserve(count); for( int i = 0; i < count; i++) { ON_HatchLoop* pL = new ON_HatchLoop( *src.m_loops[i]); m_loops.Append( pL); } m_pattern_index = src.m_pattern_index; } ON_Hatch* ON_Hatch::DuplicateHatch() const { return Duplicate(); } ON_Hatch* ON_Hatch::HatchFromBrep( ON_Hatch* use_this_hatch, const ON_Brep* brep, int face_index, int pattern_index, double pattern_rotation_radians, double pattern_scale, ON_3dPoint basepoint) { ON_Hatch* newhatch = nullptr; if (nullptr != use_this_hatch) use_this_hatch->Internal_Destroy(); if (nullptr == brep) return nullptr; if (face_index < 0 || face_index >= brep->m_F.Count()) face_index = 0; const ON_BrepFace* face = brep->Face(face_index); if (nullptr == face) return nullptr; ON_Plane plane; if (!face->IsPlanar(&plane)) return nullptr; if (nullptr == use_this_hatch) newhatch = new ON_Hatch(); else { newhatch = use_this_hatch; newhatch->Internal_Destroy(); } if (0 > pattern_index) pattern_index = 0; if (pattern_scale < ON_SQRT_EPSILON) pattern_scale = 1.0; if (ON_3dPoint::UnsetPoint == basepoint) basepoint = ON_3dPoint::Origin; newhatch->SetPlane(plane); newhatch->SetPatternIndex(pattern_index); newhatch->SetPatternRotation(pattern_rotation_radians); newhatch->SetPatternScale(pattern_scale); newhatch->SetBasePoint(basepoint); bool rc = false; int loopcount = face->LoopCount(); for (int li = 0; li < loopcount; li++) { ON_Curve* loopcurve = nullptr; ON_SimpleArray< ON_Curve* > edgecurves; ON_BrepLoop* loop = face->Loop(li); if (nullptr != loop) { int trimcount = loop->TrimCount(); for (int ti = 0; ti < trimcount; ti++) { ON_BrepTrim* trim = loop->Trim(ti); if (nullptr != trim) { const ON_Curve* edgecurve = trim->EdgeCurveOf(); if (nullptr != edgecurve) { ON_Curve* ec = edgecurve->Duplicate(); if (trim->m_bRev3d) ec->Reverse(); edgecurves.Append(ec); } } } int edgecount = edgecurves.Count(); if (edgecount == 1) { loopcurve = edgecurves[0]; } else if (edgecount > 1) { ON_PolyCurve* pc = new ON_PolyCurve(edgecount); if (nullptr != pc) { for (int ei = 0; ei < edgecount; ei++) { ON_Curve* ec = edgecurves[ei]; if (nullptr != ec) pc->AppendAndMatch(ec); } loopcurve = pc; } } if (nullptr != loopcurve) { ON_Xform xf; xf.ChangeBasis(ON_xy_plane, plane); loopcurve->Transform(xf); ON_HatchLoop* hloop = new ON_HatchLoop(); hloop->SetCurve(*loopcurve); if(loop->m_type == ON_BrepLoop::TYPE::outer) { hloop->SetType(ON_HatchLoop::eLoopType::ltOuter); rc = true; } else hloop->SetType(ON_HatchLoop::eLoopType::ltInner); newhatch->AddLoop(hloop); } } } if (!rc) { if (nullptr != newhatch) { delete newhatch; newhatch = nullptr; } } return newhatch; } bool ON_Hatch::IsValid( ON_TextLog* text_log) const { bool rc = m_plane.IsValid(); if( !rc) { if( text_log) text_log->Print( "Plane is not valid\n"); return false; } // 18 June 2012 - Lowell - Added loop self-intersection and // intersecting other loops tests int count = m_loops.Count(); for(int i = 0; i < count; i++) { if(m_loops[i] == 0) { if( text_log) text_log->Print( "Loop[%d] is nullptr\n", i); return false; } if(rc) rc = m_loops[i]->IsValid( text_log); if( !rc) { if( text_log) text_log->Print( "Loop[%d] is not valid\n", i); return false; } } return true; } void ON_Hatch::Dump( ON_TextLog& dump) const { dump.Print("Hatch: Pattern index: %d\n", PatternIndex()); dump.Print("Pattern rotation: %g\n", PatternRotation()); dump.Print("Pattern scale: %g\n", PatternScale()); ON_3dPoint p = this->BasePoint(); dump.Print("Base point: %g, %g, %g\n", p.x, p.y, p.z); dump.Print("2d base point: %g, %g\n", m_basepoint.x, m_basepoint.y); dump.Print("Plane origin: %g, %g, %g\n", m_plane.origin.x, m_plane.origin.y, m_plane.origin.z); dump.Print("Plane x axis: %g, %g, %g\n", m_plane.xaxis.x, m_plane.xaxis.y, m_plane.xaxis.z); dump.Print("Plane y axis: %g, %g, %g\n", m_plane.yaxis.x, m_plane.yaxis.y, m_plane.yaxis.z); dump.Print("Plane z axis: %g, %g, %g\n", m_plane.zaxis.x, m_plane.zaxis.y, m_plane.zaxis.z); int count = m_loops.Count(); dump.Print("Loop count = %d\n", count); for( int i = 0; i < count; i++) m_loops[i]->Dump( dump); } bool ON_Hatch::Write( ON_BinaryArchive& ar) const { // Added basepoint to 1.2; const int minor_version = (ar.Archive3dmVersion() >= 60) ? 2 : 1; bool rc = ar.Write3dmChunkVersion(1,minor_version); if (rc) rc = ar.WritePlane( m_plane); if (rc) rc = ar.WriteDouble( m_pattern_scale); if (rc) rc = ar.WriteDouble( m_pattern_rotation); if (rc) rc = ar.Write3dmReferencedComponentIndex( ON_ModelComponent::Type::HatchPattern, m_pattern_index); if (rc) { int i, count = m_loops.Count(); if( count < 0 ) count = 0; rc = ar.WriteInt( count); for( i = 0; i < count && rc; i++) rc = m_loops[i]->Write( ar); } const ON_2dPoint basepoint = BasePoint2d(); if (minor_version >= 2) { // m_basepoint = a 2d (x,y) point. if (rc) rc = ar.WritePoint(basepoint); } else if ( basepoint.IsValid() && false == basepoint.IsZero() && 50 == ar.Archive3dmVersion() ) { // add temporary V5 user data cache ON_OBSOLETE_V5_HatchExtra* v5_ud = ON_OBSOLETE_V5_HatchExtra::HatchExtension(this); if( nullptr!= v5_ud) v5_ud->m_basepoint = basepoint; } return rc; } bool ON_Hatch::Read( ON_BinaryArchive& ar) { m_plane.CreateFromNormal( ON_3dPoint::Origin, ON_3dVector::ZAxis); m_pattern_scale = 1.0; m_pattern_rotation = 0.0; m_pattern_index = -1; m_loops.Empty(); int major_version = 0; int minor_version = 0; bool rc = ar.Read3dmChunkVersion( &major_version, &minor_version); if ( rc && major_version == 1 ) { if( rc) rc = ar.ReadPlane( m_plane); if( rc) rc = ar.ReadDouble( &m_pattern_scale); if( rc) rc = ar.ReadDouble( &m_pattern_rotation); if( rc) rc = ar.Read3dmReferencedComponentIndex(ON_ModelComponent::Type::HatchPattern,&m_pattern_index); if( rc) { m_loops.Empty(); int i, count = 0; rc = ar.ReadInt( &count); if( rc && count > 0) { m_loops.SetCapacity( count ); for( i = 0; rc && i < count; i++) { ON_HatchLoop*& pLoop = m_loops.AppendNew(); pLoop = new ON_HatchLoop; if( pLoop) rc = pLoop->Read( ar); else rc = false; } } } if (minor_version >= 2) { ON_2dPoint basepoint = BasePoint2d(); if (rc) rc = ar.ReadPoint(basepoint); if (rc) SetBasePoint(basepoint); } } return rc; } ON::object_type ON_Hatch::ObjectType() const { return ON::hatch_object; } int ON_Hatch::Dimension() const { return 3; } // Copy the 2d curve, make it 3d, and transform it // to the 3d plane position ON_Curve* ON_Hatch::LoopCurve3d( int index) const { int count = m_loops.Count(); ON_Curve* pC = nullptr; if( index >= 0 && index < count) { if( m_loops[index]->Curve()) { pC = m_loops[index]->Curve()->DuplicateCurve(); if( pC) { pC->ChangeDimension( 3); ON_Xform xf; xf.Rotation( ON_xy_plane, m_plane); pC->Transform( xf); } } } return pC; } int ON_Hatch::PatternIndex() const { return m_pattern_index; } void ON_Hatch::SetPatternIndex( int index) { m_pattern_index = index; } bool ON_Hatch::GetBBox( double* bmin, double* bmax, bool bGrowBox) const { int i; int count = m_loops.Count(); bool rc = true; ON_Curve* pC; for( i = 0; rc && i < count; i++) { pC = LoopCurve3d( i); if( pC) { rc = pC->GetBBox( bmin, bmax, i?true:bGrowBox); delete pC; } } return rc; } bool ON_Hatch::GetTightBoundingBox( ON_BoundingBox& tight_bbox, bool bGrowBox, const ON_Xform* xform) const { int i; int count = m_loops.Count(); ON_CurveArray curves(count); for( i = 0; i < count; i++) { curves.Append( LoopCurve3d(i) ); } return curves.GetTightBoundingBox(tight_bbox,bGrowBox,xform); } static double Angle3d(const ON_3dVector& axis, ON_3dVector& from, const ON_3dVector& to) { ON_3dVector x = from, a = to; x.Unitize(); a.Unitize(); ON_3dVector y = ON_CrossProduct(axis, from); y.Unitize(); double cosa = x * a; if(cosa > 1.0 - ON_SQRT_EPSILON) return 0.0; if(cosa < ON_SQRT_EPSILON - 1.0) return ON_PI; double sina = a * y; return atan2(sina, cosa); } #define ARBBOUND 0.015625 void arbaxis(const ON_3dVector& givenaxis, ON_3dVector& newaxis) { if(fabs(givenaxis[0]) < ARBBOUND && fabs(givenaxis[1]) < ARBBOUND) // near world z newaxis = ON_CrossProduct(ON_3dVector::YAxis, givenaxis); else newaxis = ON_CrossProduct(ON_3dVector::ZAxis, givenaxis); newaxis.Unitize(); } double arbaxisRotation(const ON_Plane& plane) { // get arbaxis frame and angle of rotation from it ON_3dVector arbXaxis; arbaxis(plane.zaxis, arbXaxis); return Angle3d(plane.zaxis, arbXaxis, plane.xaxis); } // 20 June 2012 - Lowell - rr44706, 68320 // This will find A, the arbaxis direction for the hatch plane // and rotate the hatch plane by -A and rotate the hatch boundaries // by A and add A to the hatch rotation. // The picture will be the same after that, but the way the // angle is represented will match the way AutoCAD does it // so hatches can be round-tripped with acad files. // In addition, after several hatches are rotated by different amounts // the hatch angles can be set to look the same by setting them all // to the same pattern rotation static void UnrotateHatch(ON_Hatch* hatch) { double a = arbaxisRotation(hatch->Plane()); ON_Plane& plane = *(ON_Plane*)(&hatch->Plane()); if(fabs(a) > ON_ZERO_TOLERANCE) { ON_2dPoint base2 = hatch->BasePoint2d(); plane.Rotate(-a, plane.zaxis); for(int i = 0; i < hatch->LoopCount(); i++) { ON_Curve* pC = (ON_Curve*)hatch->Loop(i)->Curve(); pC->Rotate(a, ON_3dVector::ZAxis, ON_3dPoint::Origin); } base2.Rotate(a, ON_2dPoint::Origin); hatch->SetBasePoint(base2); hatch->SetPatternRotation(hatch->PatternRotation()+a); } // Project world origin to hatch plane and set hatch plane origin to the result // Translate hatch 2d curves to get back to the right position ON_3dPoint P; plane.ClosestPointTo(ON_3dPoint::Origin, &P.x, &P.y); if(fabs(P.x) > ON_ZERO_TOLERANCE ||fabs(P.y) > ON_ZERO_TOLERANCE ||fabs(P.z) > ON_ZERO_TOLERANCE) { const ON_3dVector V(-P.x, -P.y, 0.0); for(int i = 0; i < hatch->LoopCount(); i++) { ON_Curve* pC = (ON_Curve*)hatch->Loop(i)->Curve(); pC->Translate(V); } ON_2dPoint base2 = hatch->BasePoint2d(); base2 = base2 + ON_2dVector(-P.x, -P.y); hatch->SetBasePoint(base2); P = plane.PointAt(P.x, P.y); plane.origin = P; } } bool ON_Hatch::Transform( const ON_Xform& xform) { if( fabs( fabs( xform.Determinant()) - 1.0) > 1.0e-4) { // xform has a scale component ON_Plane tmp( m_plane); tmp.Transform( xform); ON_Xform A, B, T; A.Rotation( ON_xy_plane, m_plane); B.Rotation( tmp, ON_xy_plane); T = B * xform * A; // kill translation and z-scaling T[0][2] = T[0][3] = 0.0; T[1][2] = T[1][3] = 0.0; T[2][0] = T[2][1] = 0.0; T[2][2] = 1.0; T[2][3] = 0.0; T[3][0] = T[3][1] = T[3][2] = 0.0; T[3][3] = 1.0; for( int i = 0; i < LoopCount(); i++) m_loops[i]->m_p2dCurve->Transform( T); } ON_3dPoint base = m_plane.PointAt(m_basepoint.x, m_basepoint.y); base.Transform(xform); int rc = m_plane.Transform(xform); SetBasePoint(base); //ON_3dVector x = m_plane.xaxis; //x.Transform(xform); //double scale = x.Length() * PatternScale(); //SetPatternScale(scale); UnrotateHatch(this); TransformUserData(xform); return rc; } bool ON_Hatch::ScalePattern(ON_Xform xform) { ON_3dVector v = m_plane.xaxis; v.Transform(xform); double l = v.Length(); if (v.Unitize()) { m_pattern_scale *= l; return true; } return false; } ON_Brep* ON_Hatch::BrepForm(ON_Brep* brep) const { if (brep) brep->Destroy(); ON_Brep* newbrep = brep ? brep : ON_Brep::New(); if (0 == newbrep) { ON_ERROR("Unable to get allocate brep."); return 0; } brep = newbrep; ON_Plane plane = Plane(); ON_PlaneSurface* srf = new ON_PlaneSurface(plane); int srf_i = -1, face_i = -1; int loopcount = LoopCount(); for (int i = 0; i < loopcount; i++) { const ON_HatchLoop* loop = Loop(i); const ON_Curve* loopcurve = loop->Curve(); ON_Curve* edgecurve = this->LoopCurve3d(i); ON_SimpleArray< ON_Curve* > bounds; bounds.Append(edgecurve); if (i == 0) { ON_BoundingBox bbox; loopcurve->GetBBox(&bbox.m_min.x, &bbox.m_max.x, false); srf->SetExtents(0, ON_Interval(bbox.m_min.x - 1.0, bbox.m_max.x + 1.0), true); srf->SetExtents(1, ON_Interval(bbox.m_min.y - 1.0, bbox.m_max.y + 1.0), true); srf_i = brep->AddSurface(srf); ON_BrepFace& face = brep->NewFace(srf_i); face_i = face.m_face_index; brep->NewPlanarFaceLoop(face_i, ON_BrepLoop::TYPE::outer, bounds, false); } else brep->NewPlanarFaceLoop(face_i, ON_BrepLoop::TYPE::inner, bounds, false); } brep->SetTolerancesBoxesAndFlags(); return brep; } bool ON_Hatch::Create( const ON_Plane& plane, const ON_SimpleArray loops, int pattern_index, double pattern_rotation, double pattern_scale) { if( loops.Count() < 1) return false; if( pattern_index < 0) return false; SetPlane( plane); for( int i = 0; i < loops.Count(); i++) { ON_HatchLoop* pLoop = new ON_HatchLoop; pLoop->SetCurve( *loops[i]); pLoop->SetType( i?ON_HatchLoop::ltInner:ON_HatchLoop::ltOuter); AddLoop( pLoop); } SetPatternIndex( pattern_index); SetPatternRotation( pattern_rotation); SetPatternScale( pattern_scale); return true; } const ON_Plane& ON_Hatch::Plane() const { return m_plane; } void ON_Hatch::SetPlane( const ON_Plane& plane) { m_plane = plane; } double ON_Hatch::PatternRotation() const { return m_pattern_rotation; } void ON_Hatch::SetPatternRotation( double rotation) { m_pattern_rotation = rotation; } double ON_Hatch::PatternScale() const { return m_pattern_scale; } void ON_Hatch::SetPatternScale( double scale) { if( scale > 0.001) // Changed May 13, 2009 - Lowell - rr39185 m_pattern_scale = scale; } int ON_Hatch::LoopCount() const { return m_loops.Count(); } void ON_Hatch::AddLoop( ON_HatchLoop* pLoop) { m_loops.Append( pLoop); } bool ON_Hatch::InsertLoop( int index, ON_HatchLoop* loop) { if( index >= 0 && index <= m_loops.Count()) // 26 June 2012 - Lowell - Changed ndex < to ndex <= { m_loops.Insert(index, loop); return true; } return false; } bool ON_Hatch::RemoveLoop( int index) { if( index >= 0 && index < m_loops.Count()) { delete m_loops[index]; m_loops.Remove(index); return true; } return false; } bool ON_Hatch::ReplaceLoops(ON_SimpleArray& loop_curves) { if(loop_curves.Count() < 1) return false; bool rc = true; ON_Xform xf; bool flat = false; ON_SimpleArray loops; for(int i = 0; i < loop_curves.Count(); i++) { if(loop_curves[i] == 0) { rc = false; break; } ON_Curve* p2d = loop_curves[i]->Duplicate(); if(p2d == 0) { rc = false; break; } if(p2d->Dimension() == 3) { if(!flat) { ON_Xform planexf, flatxf; ON_Plane hplane(ON_xy_plane); hplane.origin = m_plane.origin; planexf.Rotation(m_plane, hplane); flatxf.PlanarProjection(hplane); xf = flatxf * planexf; flat = true; } if(!p2d->Transform(xf) || !p2d->ChangeDimension(2)) { delete p2d; rc = false; break; } } ON_HatchLoop* loop = new ON_HatchLoop(p2d,loops.Count()?ON_HatchLoop::ltInner:ON_HatchLoop::ltOuter); if(loop) loops.Append(loop); else delete p2d; } if(!rc) { for(int i = 0; i < loops.Count(); i++) delete loops[i]; loops.Empty(); } if(loops.Count() < 1) return false; for(int i = 0; i < m_loops.Count(); i++) delete m_loops[i]; m_loops.Empty(); for(int i = 0; i < loops.Count(); i++) m_loops.Append(loops[i]); return true; } const ON_HatchLoop* ON_Hatch::Loop( int index) const { if( index >= 0 && index < m_loops.Count()) return m_loops[index]; return nullptr; } // Basepoint functions added March 23, 2008 -LW void ON_Hatch::SetBasePoint(ON_2dPoint basepoint) { m_basepoint = basepoint; } void ON_Hatch::SetBasePoint(ON_3dPoint point) { m_plane.ClosestPointTo(point, &m_basepoint.x, &m_basepoint.y); } ON_3dPoint ON_Hatch::BasePoint() const { return m_plane.PointAt(m_basepoint.x, m_basepoint.y); } ON_2dPoint ON_Hatch::BasePoint2d() const { return m_basepoint; } ON_CurveRegionBoundaryElement::ON_CurveRegionBoundaryElement() : m_curve_id(-1), m_bReversed(false) {} ON_CurveRegionBoundaryElement::ON_CurveRegionBoundaryElement(const ON_CurveRegionBoundaryElement& src) { *this = src; } ON_CurveRegionBoundaryElement::~ON_CurveRegionBoundaryElement() {} ON_CurveRegionBoundaryElement& ON_CurveRegionBoundaryElement::operator=(const ON_CurveRegionBoundaryElement& src) { if (this != &src){ m_curve_id = src.m_curve_id; m_subdomain = src.m_subdomain; m_bReversed = src.m_bReversed; } return *this; } class ON_CLASS ON_GradientColorData : public ON_UserData { ON_OBJECT_DECLARE(ON_GradientColorData); public: ON_GradientColorData(); ~ON_GradientColorData() = default; ON_GradientColorData(const ON_GradientColorData&) = default; ON_GradientColorData& operator=(const ON_GradientColorData&) = default; //ON_GradientColorData(const ON_GradientColorData&); //ON_GradientColorData& operator=(const ON_GradientColorData&); static ON_GradientColorData* FromObject( const ON_Object* ); static ON_GradientColorData* FromObject(ON_Object* obj, bool createAndAttachIfMissing); // override virtual ON_Object::Dump function void Dump(ON_TextLog& text_log) const override; // override virtual ON_Object::SizeOf function unsigned int SizeOf() const override; // override virtual ON_Object::DataCRC function ON__UINT32 DataCRC(ON__UINT32 current_remainder) const override; // override virtual ON_Object::Write function bool Write(ON_BinaryArchive& binary_archive) const override; // override virtual ON_Object::Read function bool Read(ON_BinaryArchive& binary_archive) override; // override virtual ON_UserData::GetDescription function bool GetDescription(ON_wString& description) override; // override virtual ON_UserData::Archive function bool WriteToArchive( const class ON_BinaryArchive& archive, const class ON_Object* parent_object ) const override; // override virtual ON_UserData::Transform function bool Transform(const ON_Xform&) override; ON_GradientType m_gradient_type = ON_GradientType::Linear; ON_3dPoint m_start = ON_3dPoint::UnsetPoint; ON_3dPoint m_end = ON_3dPoint::UnsetPoint; double m_repeat = 0; ON_SimpleArray m_colors; //private: //void Internal_CopyFrom(const ON_GradientColorData&); }; ON_OBJECT_IMPLEMENT(ON_GradientColorData, ON_UserData, "0C1AD613-4EFA-4F47-A147-4D79D77FCB0C"); ON_GradientColorData::ON_GradientColorData() { m_userdata_uuid = ON_CLASS_ID(ON_GradientColorData); m_application_uuid = ON_opennurbs6_id; m_userdata_copycount = 1; } //void ON_GradientColorData::Internal_CopyFrom(const ON_GradientColorData & src) //{ // m_gradient_type = src.m_gradient_type; // m_start = src.m_start; // m_end = src.m_end; // m_repeat = src.m_repeat; // m_colors = src.m_colors; //} // //ON_GradientColorData::ON_GradientColorData(const ON_GradientColorData &src) // : ON_UserData(src) //{ // Internal_CopyFrom(src); //} ON_GradientColorData* ON_GradientColorData::FromObject(const ON_Object* p) { return p ? ON_GradientColorData::Cast(p->GetUserData(ON_CLASS_ID(ON_GradientColorData))) : nullptr; } //ON_GradientColorData & ON_GradientColorData::operator=(const ON_GradientColorData & src) //{ // if (this != &src) // { // ON_UserData::operator=(src); // Internal_CopyFrom(src); // } // return *this; //} ON_GradientColorData* ON_GradientColorData::FromObject(ON_Object* obj, bool createAndAttachIfMissing) { if (nullptr == obj) return nullptr; ON_GradientColorData* rc = ON_GradientColorData::Cast(obj->GetUserData(ON_CLASS_ID(ON_GradientColorData))); if (nullptr == rc && createAndAttachIfMissing) { rc = new ON_GradientColorData(); if (!obj->AttachUserData(rc)) { delete rc; return nullptr; } } return rc; } bool ON_GradientColorData::GetDescription(ON_wString& description) { description = L"Color Gradient UserData"; return true; } bool ON_GradientColorData::WriteToArchive(const ON_BinaryArchive & archive, const ON_Object * parent_object) const { return (archive.Archive3dmVersion() >= 60); } unsigned int ON_GradientColorData::SizeOf() const { unsigned int sz = ON_UserData::SizeOf(); sz += sizeof(*this) - sizeof(ON_UserData); sz += m_colors.SizeOfArray(); return sz; } ON__UINT32 ON_GradientColorData::DataCRC(ON__UINT32 current_remainder) const { current_remainder = ON_CRC32(current_remainder, sizeof(m_gradient_type), &m_gradient_type); current_remainder = m_start.DataCRC(current_remainder); current_remainder = m_end.DataCRC(current_remainder); current_remainder = ON_CRC32(current_remainder, sizeof(m_repeat), &m_repeat); current_remainder = m_colors.DataCRC(current_remainder); return current_remainder; } void ON_GradientColorData::Dump(ON_TextLog& text_log) const { switch (m_gradient_type) { case ON_GradientType::None: text_log.Print("None gradient\n"); break; case ON_GradientType::Linear: text_log.Print("Linear gradient\n"); break; case ON_GradientType::Radial: text_log.Print("Radial gradient\n"); break; case ON_GradientType::LinearDisabled: text_log.Print("Linear(disabled) gradient\n"); break; case ON_GradientType::RadialDisabled: text_log.Print(L"Radial(disabled) gradient\n"); break; default: break; } text_log.PushIndent(); text_log.Print("points "); text_log.Print(m_start); text_log.Print("-"); text_log.Print(m_end); text_log.Print("\nrepeat %d\n", m_repeat); text_log.PopIndent(); } bool ON_GradientColorData::Write(ON_BinaryArchive& archive) const { bool rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK, 1, 0); if (!rc) return false; for (;;) { rc = archive.WriteInt((int)m_gradient_type); if (!rc) break; rc = archive.WritePoint(m_start); if (!rc) break; rc = archive.WritePoint(m_end); if (!rc) break; rc = archive.WriteDouble(m_repeat); if (!rc) break; int count = m_colors.Count(); rc = archive.WriteInt(count); if (!rc) break; for (int i = 0; i < count && rc; i++) { rc = m_colors[i].Write(archive); if (!rc) break; } if (!rc) break; break; } if (!archive.EndWrite3dmChunk()) rc = false; return rc; } bool ON_GradientColorData::Read(ON_BinaryArchive& archive) { m_colors.SetCount(0); int major_version = 0; int minor_version = 0; bool rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK, &major_version, &minor_version); if (!rc) return false; for (;;) { rc = (1 == major_version); if (!rc) break; int gt = (int)ON_GradientType::None; rc = archive.ReadInt(>); if (gt < (int)ON_GradientType::None || gt >(int)ON_GradientType::RadialDisabled) rc = false; if (!rc) break; m_gradient_type = (ON_GradientType)gt; rc = archive.ReadPoint(m_start); if (!rc) break; rc = archive.ReadPoint(m_end); if (!rc) break; rc = archive.ReadDouble(&m_repeat); if (!rc) break; int count = 0; rc = archive.ReadInt(&count); if (!rc) break; m_colors.Reserve(count); for (int i = 0; i < count && rc; i++) { ON_ColorStop cs; rc = cs.Read(archive); if (!rc) break; m_colors.Append(cs); } if (!rc) break; break; } if (!archive.EndRead3dmChunk()) rc = false; return rc; } bool ON_GradientColorData::Transform(const ON_Xform& xform) { m_start.Transform(xform); m_end.Transform(xform); return ON_UserData::Transform(xform); } ///////////////////////////////////////////////////////////////////////// ON_GradientType ON_Hatch::GetGradientType() const { ON_GradientColorData* data = ON_GradientColorData::FromObject(this); if (data) return data->m_gradient_type; return ON_GradientType::None; } void ON_Hatch::SetGradientType(ON_GradientType gt) { ON_GradientColorData* data = ON_GradientColorData::FromObject(this, true); if (data) data->m_gradient_type = gt; } void ON_Hatch::GetGradientColors(ON_SimpleArray& colors) const { colors.Empty(); ON_GradientColorData* data = ON_GradientColorData::FromObject(this); if (data) colors = data->m_colors; } bool ON_Hatch::SetGradientColors(const ON_SimpleArray& colors) { ON_GradientColorData* data = ON_GradientColorData::FromObject(this, true); if (nullptr == data) return false; data->m_colors = colors; return true; } double ON_Hatch::GetGradientRepeat() const { ON_GradientColorData* data = ON_GradientColorData::FromObject(this); if (data) return data->m_repeat; return 0; } bool ON_Hatch::SetGradientRepeat(double repeat) { ON_GradientColorData* data = ON_GradientColorData::FromObject(this); if (nullptr == data && 0 == repeat) return true; data = ON_GradientColorData::FromObject(this, true); if (nullptr == data) return false; data->m_repeat = repeat; return true; } void ON_Hatch::GetGradientEndPoints(ON_3dPoint& startPoint, ON_3dPoint& endPoint) const { ON_GradientColorData* data = ON_GradientColorData::FromObject(this); if (nullptr == data) { startPoint = ON_3dPoint::UnsetPoint; endPoint = ON_3dPoint::UnsetPoint; return; } startPoint = data->m_start; endPoint = data->m_end; } bool ON_Hatch::SetGradientEndPoints(ON_3dPoint startpoint, ON_3dPoint endpoint) { ON_GradientColorData* data = ON_GradientColorData::FromObject(this, true); if (nullptr == data) return false; data->m_start = startpoint; data->m_end = endpoint; return true; }