Files
opennurbs/opennurbs_hatch.cpp
2025-11-05 03:40:11 -08:00

2403 lines
58 KiB
C++

//
// Copyright (c) 1993-2022 Robert McNeel & Associates. All rights reserved.
// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert
// McNeel & Associates.
//
// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF
// MERCHANTABILITY ARE HEREBY DISCLAIMED.
//
// For complete openNURBS copyright information see <http://www.opennurbs.org>.
//
////////////////////////////////////////////////////////////////
#include "opennurbs.h"
#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<ON_Hatch*>(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<double>& 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
&& ON_SimpleArray_IsEqual(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
|| !ON_SimpleArray_IsEqual(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<double>& dashes)
{
m_dashes = dashes;
}
const ON_SimpleArray<double>& ON_HatchLine::Dashes() const
{
return m_dashes;
}
void ON_HatchLine::GetLineData( double& angle,
ON_2dPoint& base,
ON_2dVector& offset,
ON_SimpleArray<double>& 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<unsigned int>(a.FillType());
const unsigned int btype = static_cast<unsigned int>(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<unsigned int>(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<unsigned int>(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<unsigned int>(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<unsigned int>(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<ON_HatchLine>& 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_HatchLine>& 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<const ON_Curve*> 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<const ON_Curve*>& loop_curves)
{
if(loop_curves.Count() < 1)
return false;
bool rc = true;
ON_Xform xf;
bool flat = false;
ON_SimpleArray<ON_HatchLoop*> 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<ON_ColorStop> 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(&gt);
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<ON_ColorStop>& colors) const
{
colors.Empty();
ON_GradientColorData* data = ON_GradientColorData::FromObject(this);
if (data)
colors = data->m_colors;
}
bool ON_Hatch::SetGradientColors(const ON_SimpleArray<ON_ColorStop>& 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;
}