/* // // Copyright (c) 1993-2021 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 bool ON_IsHairlinePrintWidth(double width_mm) { if(width_mm > 0.0 && width_mm < 0.001) return true; return false; } double ON_HairlinePrintWidth() { return 0.0001; } ////////////////////////////////////////////////////////////////////// // class ON_LinetypeSegment bool ON_LinetypeSegment::operator==( const ON_LinetypeSegment& src) const { return ( m_length == src.m_length && m_seg_type == src.m_seg_type); } bool ON_LinetypeSegment::operator!=( const ON_LinetypeSegment& src) const { return ( m_length != src.m_length || m_seg_type != src.m_seg_type); } ON_LinetypeSegment::eSegType ON_LinetypeSegment::SegmentTypeFromUnsigned( unsigned int segment_type_as_unsigned ) { switch (segment_type_as_unsigned) { ON_ENUM_FROM_UNSIGNED_CASE(ON_LinetypeSegment::eSegType::Unset); ON_ENUM_FROM_UNSIGNED_CASE(ON_LinetypeSegment::eSegType::stLine); ON_ENUM_FROM_UNSIGNED_CASE(ON_LinetypeSegment::eSegType::stSpace); } ON_ERROR("Invalid segment_type_as_unsigned value."); return ON_LinetypeSegment::eSegType::stLine; } ON_LinetypeSegment::ON_LinetypeSegment( double segment_length, ON_LinetypeSegment::eSegType segment_type ) : m_length(segment_length) , m_seg_type(segment_type) {} void ON_LinetypeSegment::Dump( ON_TextLog& dump) const { switch( m_seg_type) { case ON_LinetypeSegment::eSegType::stLine: dump.Print( "Segment type = Line: %g\n", m_length); break; case ON_LinetypeSegment::eSegType::stSpace: dump.Print( "Segment type = Space: %g\n", m_length); break; } } ////////////////////////////////////////////////////////////////////// // class ON_Linetype ON_OBJECT_IMPLEMENT( ON_Linetype, ON_ModelComponent, "26F10A24-7D13-4f05-8FDA-8E364DAF8EA6" ); const ON_Linetype* ON_Linetype::FromModelComponentRef( const class ON_ModelComponentReference& model_component_reference, const ON_Linetype* none_return_value ) { const ON_Linetype* p = ON_Linetype::Cast(model_component_reference.ModelComponent()); return (nullptr != p) ? p : none_return_value; } ON_Linetype::ON_Linetype() ON_NOEXCEPT : ON_ModelComponent(ON_ModelComponent::Type::LinePattern) {} ON_Linetype::ON_Linetype( const ON_Linetype& src ) : ON_ModelComponent(ON_ModelComponent::Type::LinePattern,src) , m_segments(src.m_segments) , m_cap_style(src.m_cap_style) , m_join_style(src.m_join_style) { } bool ON_Linetype::IsValid( ON_TextLog* text_log ) const { int i, count = m_segments.Count(); if (false == ON_ModelComponent::IsValid(text_log)) return false; // An ON_Linetype with an empty name is valid. if ( count < 1 ) { if ( text_log ) text_log->Print("ON_Linetype m_segments.Count() = 0\n"); return false; } if ( 1 == count ) { if ( m_segments[0].m_length <= 0.0 ) { if ( text_log ) text_log->Print("ON_Linetype bogus single segment linetype - length <= 0.0 (it must be > 0)\n"); return false; } if ( ON_LinetypeSegment::eSegType::stLine != m_segments[0].m_seg_type ) { if ( text_log ) text_log->Print("ON_Linetype bogus single segment linetype - type != stLine\n"); return false; } } else { for (i = 0; i < count; i++ ) { if ( m_segments[i].m_length < 0.0 ) { if ( text_log ) text_log->Print("ON_Linetype segment has negative length.\n"); return false; } if ( ON_LinetypeSegment::eSegType::stLine != m_segments[i].m_seg_type && ON_LinetypeSegment::eSegType::stSpace != m_segments[i].m_seg_type ) { if ( text_log ) text_log->Print("ON_Linetype segment has invalid m_seg_type.\n"); return false; } if ( i ) { if ( m_segments[i].m_seg_type == m_segments[i-1].m_seg_type ) { if ( text_log ) text_log->Print("ON_Linetype consecutive segments have same type.\n"); return false; } if ( 0.0 == m_segments[i].m_length && 0.0 == m_segments[i-1].m_length ) { if ( text_log ) text_log->Print("ON_Linetype consecutive segments have length zero.\n"); return false; } } } } return true; } // 12 Aug 2021 S. Baer // When adding new fields written to 3dm files, always add information to this // Dump function. Dump is used by the opennurbs file testing framework to // perform comparisons and is useful for manual comparison in when tests fail. void ON_Linetype::Dump( ON_TextLog& dump ) const { ON_ModelComponent::Dump(dump); dump.Print( "Segment count = %d\n", m_segments.Count()); dump.Print( "Pattern length = %g\n", PatternLength()); dump.Print( "Pattern = (" ); for( int i = 0; i < m_segments.Count(); i++) { const ON_LinetypeSegment& seg = m_segments[i]; if ( i ) dump.Print(","); switch( seg.m_seg_type) { case ON_LinetypeSegment::eSegType::stLine: dump.Print( "line"); break; case ON_LinetypeSegment::eSegType::stSpace: dump.Print( "space"); break; default: dump.Print( "invalid"); break; } } dump.Print(")\n"); switch (m_cap_style) { case ON::LineCapStyle::Flat: dump.Print("Cap = Flat\n"); break; case ON::LineCapStyle::Square: dump.Print("Cap = Square\n"); break; case ON::LineCapStyle::Round: default: dump.Print("Cap = Round\n"); break; } switch (m_join_style) { case ON::LineJoinStyle::Bevel: dump.Print("Join = Bevel\n"); break; case ON::LineJoinStyle::Miter: dump.Print("Join = Miter\n"); break; case ON::LineJoinStyle::Round: default: dump.Print("Join = Round\n"); break; } } bool ON_Linetype::Write( ON_BinaryArchive& file) const { bool rc = false; if (file.Archive3dmVersion() < 60) { if (!file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK, 1, 1)) return false; for (;;) { // chunk version 1.0 fields if (!file.Write3dmReferencedComponentIndex(*this)) break; ON_wString linetype_name; GetName(linetype_name); if (!file.WriteString(linetype_name)) break; if (!file.WriteArray(m_segments)) break; // chunk version 1.1 fields if (!file.WriteUuid(Id())) break; rc = true; break; } } else { // 12 Aug 2021 S. Baer (RH-2285) // minor_version = 1: add cap and join styles to linetype const int minor_version = 1; if (!file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK, 2, minor_version)) return false; for (;;) { // chunk version 1.0 fields if (!file.WriteModelComponentAttributes(*this,ON_ModelComponent::Attributes::BinaryArchiveAttributes)) break; if (!file.WriteArray(m_segments)) break; // Only write non-default values in a similar fashion as ON_3dmObjectAttributes if (m_cap_style != ON::LineCapStyle::Round) { const unsigned char itemType = 1; if (!file.WriteChar(itemType)) break; if (!file.WriteChar((unsigned char)m_cap_style)) break; } if (m_join_style != ON::LineJoinStyle::Round) { const unsigned char itemType = 2; if (!file.WriteChar(itemType)) break; if (!file.WriteChar((unsigned char)m_join_style)) break; } // 0 indicates end of new linetype attributes const unsigned char attributes_end = 0; if (!file.WriteChar(attributes_end)) break; rc = true; break; } } if (!file.EndWrite3dmChunk()) rc = false; return rc; } bool ON_Linetype::Read( ON_BinaryArchive& file) { *this = ON_Linetype::Unset; int major_version=0; int minor_version=0; if (!file.BeginRead3dmChunk( TCODE_ANONYMOUS_CHUNK, &major_version, &minor_version )) return false; bool rc = false; if( 1 == major_version ) { for (;;) { // chunk version 1.0 fields int linetype_index = Index(); if (!file.ReadInt(&linetype_index)) break; SetIndex(linetype_index); ON_wString linetype_name; GetName(linetype_name); if (!file.ReadString(linetype_name)) break; SetName(linetype_name); if (!file.ReadArray(m_segments)) break; if (minor_version >= 1) { ON_UUID linetype_id = Id(); if (!file.ReadUuid(linetype_id)) break; SetId(linetype_id); } rc = true; break; } } else if ( 2 == major_version ) { for (;;) { // chunk version 2.0 fields unsigned int model_component_attributes_filter = 0; if (!file.ReadModelComponentAttributes(*this,&model_component_attributes_filter)) break; if (!file.ReadArray(m_segments)) break; // 12 Aug 2021 S. Baer (RH-2285) // Add cap and join style to linetype if (minor_version >= 1) { unsigned char item_id = 0; if (!file.ReadChar(&item_id)) break; if (1 == item_id) { unsigned char cap = 0; if (!file.ReadChar(&cap)) break; m_cap_style = ON::LineCapStyleFromUnsigned(cap); if (!file.ReadChar(&item_id)) break; } if (2 == item_id) { unsigned char join = 0; if (!file.ReadChar(&join)) break; m_join_style = ON::LineJoinStyleFromUnsigned(join); if (!file.ReadChar(&item_id)) break; } if (1 == minor_version && item_id != 0) { ON_ERROR("Bug in ON_Linetype::Read for chunk version 2.1"); } if (item_id > 2) { // we are reading file written with code newer // than this code (minor_version > 1) item_id = 0; } } rc = true; break; } } if ( !file.EndRead3dmChunk() ) rc = false; return rc; } bool ON_Linetype::PatternIsSet() const { return (0 != (m_is_set_bits & ON_Linetype::pattern_bit)); } bool ON_Linetype::ClearPattern() { if (false == PatternIsLocked()) { m_is_set_bits &= ~ON_Linetype::pattern_bit; m_segments.Destroy(); } return (false == PatternIsSet()); } bool ON_Linetype::PatternIsLocked() const { return (0 != (m_is_locked_bits & ON_Linetype::pattern_bit)); } void ON_Linetype::LockPattern() { m_is_locked_bits |= ON_Linetype::pattern_bit; } double ON_Linetype::PatternLength() const { double length = 0.0; int seg_count = m_segments.Count(); for( int i = 0; i < seg_count; i++) { length += m_segments[i].m_length; } return length; } ON_SimpleArray* ON_Linetype::ExpertSegments() { if (PatternIsLocked()) return nullptr; return &m_segments; } const ON_SimpleArray& ON_Linetype::Segments() const { return m_segments; } int ON_Linetype::SegmentCount() const { return m_segments.Count(); } int ON_Linetype::AppendSegment( const ON_LinetypeSegment& segment) { if (PatternIsLocked()) return -1; m_segments.Append( segment); return( m_segments.Count()-1); } bool ON_Linetype::RemoveSegment( int index ) { if (PatternIsLocked()) return false; bool rc = ( index >= 0 && index < m_segments.Count()); if (rc) m_segments.Remove(index); return rc; } bool ON_Linetype::SetSegment( int index, const ON_LinetypeSegment& segment) { if (PatternIsLocked()) return false; if( index >= 0 && index < m_segments.Count()) { m_segments[index] = segment; return true; } else return false; } bool ON_Linetype::SetSegment( int index, double length, ON_LinetypeSegment::eSegType type) { if (PatternIsLocked()) return false; if( index >= 0 && index < m_segments.Count()) { m_segments[index].m_length = length; m_segments[index].m_seg_type = type; return true; } else return false; } bool ON_Linetype::SetSegments(const ON_SimpleArray& segments) { if (PatternIsLocked()) return false; m_segments = segments; return true; } ON_LinetypeSegment ON_Linetype::Segment( int index) const { if( index >= 0 && index < m_segments.Count()) return m_segments[index]; else return ON_LinetypeSegment::OneMillimeterLine; } void ON_Linetype::SetLineCapStyle(ON::LineCapStyle style) { m_cap_style = style; } ON::LineCapStyle ON_Linetype::LineCapStyle() const { return m_cap_style; } void ON_Linetype::SetLineJoinStyle(ON::LineJoinStyle style) { m_join_style = style; } ON::LineJoinStyle ON_Linetype::LineJoinStyle() const { return m_join_style; }