Files
opennurbs/opennurbs_brep.cpp
Bozo The Builder e7c29061e3 Sync changes from upstream repository
Co-authored-by: Alain <alain@mcneel.com>
Co-authored-by: Andrew Le Bihan <andy@mcneel.com>
Co-authored-by: chuck <chuck@mcneel.com>
Co-authored-by: croudyj <croudyj@gmail.com>
Co-authored-by: Dale Fugier <dale@mcneel.com>
Co-authored-by: Giulio Piacentino <giulio@mcneel.com>
Co-authored-by: Greg Arden <greg@mcneel.com>
Co-authored-by: Jussi Aaltonen <jussi@mcneel.com>
Co-authored-by: kike-garbo <kike@mcneel.com>
Co-authored-by: Steve Baer <steve@mcneel.com>
2022-11-21 14:18:57 -08:00

13304 lines
356 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"
#include <memory>
#include <array>
#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_BrepVertex
////////////////////////////////////////////////////////////////
ON_OBJECT_IMPLEMENT_NO_COPYCTOR(ON_BrepVertex,ON_Point,"60B5DBC0-E660-11d3-BFE4-0010830122F0");
static bool ON_BrepIsNotValid()
{
return ON_IsNotValid(); // <-- good place for a breakpoint
}
//bool ON_Brep::GetLock()
//{
// return m_sleep_lock.GetLock();
//}
//
//bool ON_Brep::GetLockOrReturnFalse()
//{
// return m_sleep_lock.GetLockOrReturnFalse();
//}
//
//bool ON_Brep::ReturnLock()
//{
// return m_sleep_lock.ReturnLock();
//}
ON_SleepLockGuard::ON_SleepLockGuard(const class ON_Brep& brep)
: m_sleep_lock(brep.m_sleep_lock)
{
m_bIsManagingLock = m_sleep_lock.GetLock();
}
ON_BrepVertex::ON_BrepVertex()
{
memset(&m_vertex_user,0,sizeof(m_vertex_user));
}
ON_BrepVertex::ON_BrepVertex( int vertex_index )
: m_vertex_index(vertex_index)
{
memset(&m_vertex_user,0,sizeof(m_vertex_user));
}
unsigned int ON_BrepVertex::SizeOf() const
{
unsigned int sz = ON_Geometry::SizeOf();
sz += (sizeof(*this) - sizeof(ON_Geometry));
sz += m_ei.SizeOfArray();
return sz;
}
ON_BrepVertex& ON_BrepVertex::operator=(const ON_BrepVertex& src)
{
if ( &src != this ) {
ON_Point::operator=(src);
m_vertex_user = src.m_vertex_user;
m_status = src.m_status;
m_vertex_index = src.m_vertex_index;
m_ei = src.m_ei;
m_tolerance = src.m_tolerance;
}
return *this;
}
bool ON_BrepVertex::IsValid( ON_TextLog* text_log ) const
{
if (m_vertex_index < 0)
{
if ( text_log )
text_log->Print("ON_BrepVertex m_vertex_index = %d. Should be >= 0\n",m_vertex_index);
return ON_BrepIsNotValid();
}
const int ve_count = EdgeCount();
int vei, ei;
for ( vei = 0; vei < ve_count; vei++ ) {
ei = m_ei[vei];
if ( ei < 0 )
{
if ( text_log )
text_log->Print("ON_BrepVertex m_ei[%d] = %d. m_ei[] values should be >= 0\n",vei,ei);
return ON_BrepIsNotValid();
}
}
return ON_Point::IsValid(text_log);
}
void
ON_BrepVertex::Dump( ON_TextLog& dump ) const
{
dump.Print("ON_BrepVertex[%d]: ",m_vertex_index);
dump.Print( point );
dump.Print("\n");
}
bool
ON_BrepVertex::SetPoint( const ON_3dPoint& p )
{
point = p;
m_tolerance = ON_UNSET_VALUE;
return true;
}
ON_3dPoint
ON_BrepVertex::Point() const
{
return point;
}
double
ON_BrepVertex::Tolerance() const
{
return m_tolerance;
}
int
ON_BrepVertex::EdgeCount() const
{
return m_ei.Count();
}
////////////////////////////////////////////////////////////////
// Class ON_BrepEdge
////////////////////////////////////////////////////////////////
ON_OBJECT_IMPLEMENT_NO_COPYCTOR(ON_BrepEdge,ON_CurveProxy,"60B5DBC1-E660-11d3-BFE4-0010830122F0");
ON_BrepEdge::ON_BrepEdge()
: ON_CurveProxy(0)
{
memset(&m_edge_user,0,sizeof(m_edge_user));
m_vi[0] = m_vi[1] = -1;
}
ON_BrepEdge::ON_BrepEdge(int edge_index )
: ON_CurveProxy(0),
m_edge_index(edge_index)
{
memset(&m_edge_user,0,sizeof(m_edge_user));
m_vi[0] = m_vi[1] = -1;
}
ON::object_type ON_BrepEdge::ObjectType() const
{
// This MUST return ON::curve_object.
// NEVER change this to ON::edge_object.
return ON::curve_object;
}
unsigned int ON_BrepEdge::SizeOf() const
{
unsigned int sz = ON_CurveProxy::SizeOf();
sz = (sizeof(*this) - sizeof(ON_CurveProxy));
sz += m_ti.SizeOfArray();
return sz;
}
ON_BrepEdge& ON_BrepEdge::operator=(const ON_BrepEdge& src)
{
if ( &src != this )
{
// do not copy m_brep pointer
ON_CurveProxy::operator=(src);
m_edge_user = src.m_edge_user;
m_status = src.m_status;
m_edge_index = src.m_edge_index;
m_c3i = src.m_c3i;
m_vi[0] = src.m_vi[0];
m_vi[1] = src.m_vi[1];
m_ti = src.m_ti;
m_tolerance = src.m_tolerance;
}
return *this;
}
bool ON_BrepEdge::IsValid( ON_TextLog* text_log ) const
{
bool rc = ON_CurveProxy::IsValid(text_log) ? true : false;
if ( !rc )
{
if ( text_log )
{
text_log->Print("ON_BrepEdge is not a valid curve proxy\n");
}
}
else if (m_edge_index < 0)
{
if ( text_log )
{
text_log->Print("ON_BrepEdge.m_edge_index = %d (should be >= 0 )\n",m_edge_index);
}
rc = false;
}
else if ( m_c3i < 0 )
{
if ( text_log )
{
text_log->Print("ON_BrepEdge.m_c3i = %d (should be >= 0 )\n",m_c3i);
}
rc = false;
}
else if ( m_vi[0] < 0 )
{
if ( text_log )
{
text_log->Print("ON_BrepEdge.m_vi[0] = %d (should be >= 0 )\n",m_vi[0]);
}
rc = false;
}
else if ( m_vi[1] < 0 )
{
if ( text_log )
{
text_log->Print("ON_BrepEdge.m_vi[1] = %d (should be >= 0 )\n",m_vi[1]);
}
rc = false;
}
else if ( !m_brep )
{
if ( text_log )
{
text_log->Print("ON_BrepEdge.m_brep = nullptr (should point to parent ON_Brep)\n");
}
rc = false;
}
return rc;
}
bool ON_BrepEdge::IsClosed() const
{
// This function must return true if ON_CurveProxy::IsClosed() is true.
bool rc = ON_CurveProxy::IsClosed();
if ( 0 == rc
&& m_vi[0] >= 0
&& m_vi[0] == m_vi[1]
&& 0 != ProxyCurve()
&& ProxyCurveDomain() == ProxyCurve()->Domain()
&& 0 != m_brep
&& m_vi[0] < m_brep->m_V.Count()
)
{
// When ON_CurveProxy::IsClosed() is false and the topology
// indicates the edge is closed, we need to verify that its
// geometry is within tolerance of being closed.
const ON_BrepVertex& v = m_brep->m_V[m_vi[0]];
ON_3dPoint P = PointAtStart();
ON_3dPoint Q = PointAtEnd();
ON_3dPoint V = v.point;
double vtx_tol = v.m_tolerance;
if ( P.DistanceTo(Q) <= m_tolerance
&& V.DistanceTo(P) <= vtx_tol
&& V.DistanceTo(Q) <= vtx_tol )
rc = true;
}
return rc;
}
ON_Brep* ON_BrepEdge::Brep() const
{
return m_brep;
}
ON_BrepTrim* ON_BrepEdge::Trim( int eti ) const
{
return (m_brep && eti >= 0 && eti < m_ti.Count()) ? m_brep->Trim(m_ti[eti]) : 0;
}
int ON_BrepEdge::TrimCount() const
{
return m_ti.Count();
}
void ON_BrepEdge::Dump( ON_TextLog& dump ) const
{
dump.Print("ON_BrepEdge[%d]: ",m_edge_index);
}
// virtual ON_Curve::Reverse override
bool ON_BrepEdge::Reverse()
{
bool rc = false;
if ( m_brep )
{
ON_Interval edge_domain = Domain();
if ( m_brep->StandardizeEdgeCurve( m_edge_index, false ) )
{
ON_Curve* c3 = const_cast<ON_Curve*>(EdgeCurveOf());
if ( c3 )
{
rc = c3->Reverse();
edge_domain.Reverse();
c3->SetDomain(edge_domain);
SetProxyCurve(c3);
}
}
}
if ( !rc )
rc = ON_CurveProxy::Reverse();
if (rc)
{
int i = m_vi[0];
m_vi[0] = m_vi[1];
m_vi[1] = i;
if ( m_brep )
{
const int tcount = m_brep->m_T.Count();
int ti, eti;
for ( eti = m_ti.Count()-1; eti >= 0; eti-- ) {
ti = m_ti[eti];
if ( ti >= 0 && ti < tcount )
{
ON_BrepTrim& trim = m_brep->m_T[ti];
trim.m_bRev3d = trim.m_bRev3d ? false : true;
trim.UnsetPlineEdgeParameters();
}
}
}
}
return rc;
}
////////////////////////////////////////////////////////////////
// Class ON_BrepTrim
////////////////////////////////////////////////////////////////
ON_OBJECT_IMPLEMENT_NO_COPYCTOR(ON_BrepTrim,ON_CurveProxy,"60B5DBC2-E660-11d3-BFE4-0010830122F0");
ON_BrepTrim::ON_BrepTrim()
{
memset(&m_trim_user,0,sizeof(m_trim_user));
m_vi[0] = m_vi[1] = -1;
m_tolerance[0] = m_tolerance[1] = ON_UNSET_VALUE;
m_pline.Reserve(4); // This is a stopgap fix to insures the memory
// pool used for pline segments is the same as
// the memory pool used for the rest of this brep.
//m_P[0] = ON_3dPoint::UnsetPoint;
//m_P[1] = ON_3dPoint::UnsetPoint;
}
ON_BrepTrim::ON_BrepTrim(int trim_index)
: m_trim_index(trim_index)
{
memset(&m_trim_user,0,sizeof(m_trim_user));
m_vi[0] = m_vi[1] = -1;
m_tolerance[0] = m_tolerance[1] = ON_UNSET_VALUE;
m_pline.Reserve(4); // This is a stopgap fix to insures the memory
// pool used for pline segments is the same as
// the memory pool used for the rest of this brep.
//m_P[0] = ON_3dPoint::UnsetPoint;
//m_P[1] = ON_3dPoint::UnsetPoint;
}
unsigned int ON_BrepTrim::SizeOf() const
{
unsigned int sz = ON_CurveProxy::SizeOf();
sz = (sizeof(*this) - sizeof(ON_CurveProxy));
// runtime m_pline is not counted
return sz;
}
ON_BrepTrim& ON_BrepTrim::operator=(const ON_BrepTrim& src)
{
if ( &src != this )
{
// do not copy m_brep pointer
ON_CurveProxy::operator=(src);
m_trim_user = src.m_trim_user;
m_status = src.m_status;
m_trim_index = src.m_trim_index;
m_c2i = src.m_c2i;
//m_t = src.m_t;
m_ei = src.m_ei;
m_vi[0] = src.m_vi[0];
m_vi[1] = src.m_vi[1];
m_bRev3d = src.m_bRev3d;
m_type = src.m_type;
m_iso = src.m_iso;
m_li = src.m_li;
m_tolerance[0] = src.m_tolerance[0];
m_tolerance[1] = src.m_tolerance[1];
//m_P[0] = src.m_P[0];
//m_P[1] = src.m_P[1];
m__legacy_2d_tol = src.m__legacy_2d_tol;
m__legacy_3d_tol = src.m__legacy_3d_tol;
m__legacy_flags = src.m__legacy_flags;
m_pline = src.m_pline;
m_pbox = src.m_pbox;
}
return *this;
}
ON_Brep* ON_BrepTrim::Brep() const
{
return m_brep;
}
ON_BrepLoop* ON_BrepTrim::Loop() const
{
ON_BrepLoop* loop = 0;
if ( m_brep && m_li >= 0 && m_li < m_brep->m_L.Count() )
loop = &m_brep->m_L[m_li];
return loop;
}
ON_BrepFace* ON_BrepTrim::Face() const
{
ON_BrepFace* face = 0;
if ( m_brep && m_li >= 0 && m_li < m_brep->m_L.Count() )
{
int fi = m_brep->m_L[m_li].m_fi;
if ( fi >= 0 && fi < m_brep->m_F.Count() )
face = &m_brep->m_F[fi];
}
return face;
}
ON_BrepEdge* ON_BrepTrim::Edge() const
{
ON_BrepEdge* edge = 0;
if ( m_brep && m_ei >= 0 && m_ei < m_brep->m_E.Count() )
edge = &m_brep->m_E[m_ei];
return edge;
}
ON_BrepVertex* ON_BrepTrim::Vertex(int tvi) const
{
ON_BrepVertex* vertex = 0;
if ( 0 != m_brep && 0 <= tvi && tvi <= 1 )
{
int vi = m_vi[tvi];
if ( 0 <= vi && vi < m_brep->m_V.Count() )
{
vertex = &m_brep->m_V[vi];
}
}
return vertex;
}
ON_BrepVertex* ON_BrepEdge::Vertex(int evi) const
{
ON_BrepVertex* vertex = 0;
if ( 0 != m_brep && 0 <= evi && evi <= 1 )
{
int vi = m_vi[evi];
if ( 0 <= vi && vi < m_brep->m_V.Count() )
{
vertex = &m_brep->m_V[vi];
}
}
return vertex;
}
bool ON_BrepTrim::IsValid( ON_TextLog* text_log ) const
{
if ( m_trim_index < 0 )
{
if ( text_log )
{
text_log->Print("trim.m_trim_index < 0.\n");
}
return ON_BrepIsNotValid();
}
if ( m_c2i < 0 )
{
if ( text_log )
{
text_log->Print("trim.m_c2i = %d is not valid\n",m_c2i);
}
return ON_BrepIsNotValid();
}
if ( !ON_CurveProxy::IsValid(text_log) )
{
if ( text_log )
{
text_log->Print("trim curve proxy settings are not valid.\n");
}
return ON_BrepIsNotValid();
}
if ( m_ei < 0 )
{
if ( m_type != singular )
{
if ( text_log )
{
text_log->Print("trim.m_ei = %d but trim.mtype != singular\n",m_ei);
}
return ON_BrepIsNotValid();
}
}
if ( m_vi[0] < 0 )
{
if ( text_log )
{
text_log->Print("trim.m_v[0] = %d is not valid\n",m_vi[0]);
}
return ON_BrepIsNotValid();
}
if ( m_vi[1] < 0 )
{
if ( text_log )
{
text_log->Print("trim.m_v[1] = %d is not valid\n",m_vi[1]);
}
return ON_BrepIsNotValid();
}
unsigned int i = m_type;
if ( i >= trim_type_count )
{
if ( text_log )
{
text_log->Print("trim.m_type = %d is not valid\n",i);
}
return ON_BrepIsNotValid();
}
if ( i == ON_BrepTrim::slit )
{
if ( text_log )
{
text_log->Print("trim.m_type = ON_BrepTrim::slit is not valid. REserved for future use.\n",i);
}
return ON_BrepIsNotValid();
}
i = m_iso;
if ( i >= ON_Surface::iso_count )
{
if ( text_log )
{
text_log->Print("trim.m_iso = %d is not valid\n",i);
}
return ON_BrepIsNotValid();
}
if ( m_li < 0 )
{
if ( text_log )
{
text_log->Print("trim.m_li = %d is not valid\n",m_li);
}
return ON_BrepIsNotValid();
}
if ( !m_brep )
{
if ( text_log )
{
text_log->Print("trim.m_brep is null.\n");
}
return ON_BrepIsNotValid();
}
return true;
}
void ON_BrepTrim::Dump( ON_TextLog& dump ) const
{
dump.Print("ON_BrepTrim[%d]:\n",m_trim_index);
}
bool ON_BrepTrim::Reverse()
{
m_pline.Destroy();
DestroyCurveTree();
bool rc = false;
if ( m_brep )
{
ON_Interval trim_domain = Domain();
if ( m_brep->StandardizeTrimCurve( m_trim_index ) )
{
ON_Curve* c2 = const_cast<ON_Curve*>(TrimCurveOf());
if ( c2 )
{
rc = c2->Reverse();
trim_domain.Reverse();
c2->SetDomain(trim_domain);
SetProxyCurve(c2);
}
}
}
if ( !rc )
rc = ON_CurveProxy::Reverse();
if (rc)
{
int i = m_vi[0];
m_vi[0] = m_vi[1];
m_vi[1] = i;
if ( m_ei >= 0 )
m_bRev3d = m_bRev3d ? false : true;
}
return rc;
}
////////////////////////////////////////////////////////////////
// Class ON_BrepLoop
////////////////////////////////////////////////////////////////
ON_OBJECT_IMPLEMENT_NO_COPYCTOR(ON_BrepLoop,ON_Geometry,"60B5DBC3-E660-11d3-BFE4-0010830122F0");
ON_BrepLoop::ON_BrepLoop()
{
memset(&m_loop_user,0,sizeof(m_loop_user));
}
ON_BrepLoop::ON_BrepLoop(int loop_index)
: m_loop_index(loop_index)
{
memset(&m_loop_user,0,sizeof(m_loop_user));
}
ON_Brep* ON_BrepLoop::Brep() const
{
return m_brep;
}
ON_BrepFace* ON_BrepLoop::Face() const
{
return m_brep ? m_brep->Face(m_fi) : 0;
}
unsigned int ON_BrepLoop::SizeOf() const
{
unsigned int sz = ON_Object::SizeOf();
sz += (sizeof(ON_BrepLoop) - sizeof(ON_Object));
sz += m_ti.SizeOfArray();
return sz;
}
ON_BrepTrim* ON_BrepLoop::Trim( int lti ) const
{
ON_BrepTrim* trim = ( m_brep && lti >= 0 && lti < m_ti.Count() )
? m_brep->Trim(m_ti[lti])
: 0;
return trim;
}
int ON_BrepLoop::TrimCount() const
{
return m_ti.Count();
}
ON_BrepLoop& ON_BrepLoop::operator=(const ON_BrepLoop& src)
{
if ( &src != this )
{
// do not copy m_brep pointer
ON_Object::operator=(src);
m_loop_user = src.m_loop_user;
m_status = src.m_status;
m_loop_index = src.m_loop_index;
m_ti = src.m_ti;
m_type = src.m_type;
m_fi = src.m_fi;
m_pbox = src.m_pbox;
}
return *this;
}
static void BadLoopMessage( int loop_index, ON_TextLog* text_log )
{
if ( text_log )
{
text_log->Print("brep.m_L[%d] loop is not valid.\n",loop_index);
}
}
bool ON_BrepLoop::IsValid( ON_TextLog* text_log ) const
{
if ( m_loop_index < 0 )
{
BadLoopMessage(m_loop_index,text_log);
if ( text_log )
text_log->Print("loop.m_loop_index < 0.\n");
return ON_BrepIsNotValid();
}
if ( m_ti.Count() < 1 )
{
BadLoopMessage(m_loop_index,text_log);
if ( text_log )
text_log->Print("loop.m_ti[] is empty.\n");
return ON_BrepIsNotValid();
}
int i = m_type;
if ( i < 0 || i > type_count )
{
BadLoopMessage(m_loop_index,text_log);
if ( text_log )
text_log->Print("loop.m_type = %d is not a valid value.\n",i);
return ON_BrepIsNotValid();
}
if ( m_fi < 0 )
{
BadLoopMessage(m_loop_index,text_log);
if ( text_log )
text_log->Print("loop.m_fi = %d (should be >= 0 ).\n",m_fi);
return ON_BrepIsNotValid();
}
if ( !m_brep )
{
BadLoopMessage(m_loop_index,text_log);
if ( text_log )
text_log->Print("loop.m_brep is nullptr.\n");
return ON_BrepIsNotValid();
}
return true;
}
void ON_BrepLoop::Dump( ON_TextLog& dump ) const
{
dump.Print("ON_BrepLoop[%d]: m_fi = %d, m_type = %d m_ti.Count() = %d\n",
m_loop_index,m_fi,m_type,m_ti.Count()
);
}
int ON_BrepLoop::IndexOfTrim( const ON_BrepTrim& trim ) const
{
const int count = m_ti.Count();
int lti;
for ( lti = 0; lti < count; lti++ )
{
if ( m_ti[lti] == trim.m_trim_index )
return lti;
}
return -1;
}
////////////////////////////////////////////////////////////////
// Class ON_BrepFace
////////////////////////////////////////////////////////////////
ON_OBJECT_IMPLEMENT_NO_COPYCTOR(ON_BrepFace,ON_SurfaceProxy,"60B5DBC4-E660-11d3-BFE4-0010830122F0");
class ON_BrepFace::Impl
{
public:
std::shared_ptr<const ON_Mesh> m_render_mesh;
std::shared_ptr<const ON_Mesh> m_analysis_mesh;
std::shared_ptr<const ON_Mesh> m_preview_mesh;
};
ON_BrepFace::ON_BrepFace()
: ON_SurfaceProxy(0),
m_pImpl(new Impl)
{
memset(&m_face_user,0,sizeof(m_face_user));
}
ON_BrepFace::ON_BrepFace(int face_index)
: ON_SurfaceProxy(0),
m_face_index(face_index),
m_pImpl(new Impl)
{
memset(&m_face_user,0,sizeof(m_face_user));
}
unsigned int ON_BrepFace::SizeOf() const
{
unsigned int sz = ON_SurfaceProxy::SizeOf();
sz += (sizeof(*this) - sizeof(ON_SurfaceProxy));
sz += m_li.SizeOfArray();
if ( m_pImpl->m_render_mesh )
sz += m_pImpl->m_render_mesh->SizeOf();
if (m_pImpl->m_analysis_mesh )
sz += m_pImpl->m_analysis_mesh->SizeOf();
if (m_pImpl->m_preview_mesh )
sz += m_pImpl->m_preview_mesh->SizeOf();
return sz;
}
ON_BrepFace& ON_BrepFace::operator=(const ON_BrepFace& src)
{
if ( &src != this )
{
// do not copy m_brep pointer
ON_SurfaceProxy::operator=(src);
m_face_user = src.m_face_user;
m_status = src.m_status;
m_face_index = src.m_face_index;
m_li = src.m_li;
m_si = src.m_si;
m_bRev = src.m_bRev;
m_face_material_channel = src.m_face_material_channel;
m_face_uuid = src.m_face_uuid;
m_per_face_color = src.m_per_face_color;
m_pImpl->m_render_mesh = src.m_pImpl->m_render_mesh ? src.m_pImpl->m_render_mesh : nullptr;
m_pImpl->m_analysis_mesh = src.m_pImpl->m_analysis_mesh ? src.m_pImpl->m_analysis_mesh : nullptr;
m_pImpl->m_preview_mesh = src.m_pImpl->m_preview_mesh ? src.m_pImpl->m_preview_mesh : nullptr;
}
return *this;
}
ON_BrepFace::~ON_BrepFace()
{
DestroyMesh(ON::any_mesh);
m_li.Destroy();
delete m_pImpl;
}
ON_Brep* ON_BrepFace::Brep() const
{
return m_brep;
}
ON_BrepLoop* ON_BrepFace::Loop( int lti ) const
{
return (m_brep && lti >= 0 && lti < m_li.Count()) ? m_brep->Loop( m_li[lti]) : 0;
}
int ON_BrepFace::LoopCount() const
{
return m_li.Count();
}
ON_BrepLoop* ON_BrepFace::OuterLoop() const
{
int li, lti;
for ( lti = 0; lti < m_li.Count(); lti++ )
{
li = m_li[lti];
if ( li >= 0 && li < m_brep->m_L.Count() )
{
if ( ON_BrepLoop::outer == m_brep->m_L[li].m_type )
{
return &m_brep->m_L[li];
}
}
}
return 0;
}
unsigned int ON_BrepFace::PackId() const
{
return 0x10000U * ((unsigned int)m_pack_id_high) + ((unsigned int)m_pack_id_low);
}
void ON_BrepFace::ClearPackId()
{
m_pack_id_low = 0;
m_pack_id_high = 0;
}
void ON_BrepFace::SetPackIdForExperts(
unsigned int pack_id
)
{
if (0 == pack_id)
ClearPackId();
else
{
m_pack_id_low = (ON__UINT16)(pack_id % 0x10000U);
m_pack_id_high = (ON__UINT16)(pack_id / 0x10000U);
}
}
bool ON_BrepFace::IsValid( ON_TextLog* text_log ) const
{
if ( m_face_index < 0 )
{
if ( 0 != text_log )
text_log->Print("ON_BrepFace m_face_index = %d. Should be >= 0.\n",m_face_index);
return false;
}
if ( m_li.Count() < 1 )
{
if ( 0 != text_log )
text_log->Print("ON_BrepFace m_li.Count() = 0 Should be > 0.\n");
return false;
}
if ( m_si < 0 )
{
if ( 0 != text_log )
text_log->Print("ON_BrepFace m_si = %d. Should be >= 0.\n",m_si);
return false;
}
if ( 0 == m_brep )
{
if ( 0 != text_log )
text_log->Print("ON_BrepFace m_brep = 0. Should point to parent brep.\n");
return false;
}
return true;
}
void ON_BrepFace::Dump( ON_TextLog& dump ) const
{
dump.Print("ON_BrepFace[%d]:",m_face_index);
bool bNeedComma = false;
if ( ON_UuidCompare(m_face_uuid,ON_nil_uuid) )
{
if (bNeedComma)
dump.Print(",");
dump.Print(" FaceId=");
dump.Print(m_face_uuid);
bNeedComma = true;
}
if (ON_Color::UnsetColor != m_per_face_color)
{
if (bNeedComma)
dump.Print(",");
dump.Print(" PerFaceColor=(");
dump.PrintColor(m_per_face_color);
dump.Print(")");
}
if (0 != m_face_material_channel)
{
if (bNeedComma)
dump.Print(",");
dump.Print(" PerFaceMaterialChannel=");
dump.Print("%d", m_face_material_channel);
}
dump.Print("\n");
}
void ON_BrepFace::SetMaterialChannelIndex(int material_channel_index) const
{
if (material_channel_index > 0 && material_channel_index <= ON_Material::MaximumMaterialChannelIndex)
{
const_cast<ON_BrepFace*>(this)->m_face_material_channel = material_channel_index;
}
else
{
if (0 != material_channel_index)
{
ON_ERROR("Invalid material_channel_index value.");
}
ClearMaterialChannelIndex(); // makes it easier to detect when the per face setting is cleared.
}
}
void ON_BrepFace::ClearMaterialChannelIndex() const
{
const_cast<ON_BrepFace*>(this)->m_face_material_channel = 0;
}
int ON_BrepFace::MaterialChannelIndex() const
{
return m_face_material_channel;
}
void ON_BrepFace::SetPerFaceColor(
ON_Color color
) const
{
if (ON_Color::UnsetColor == color)
ClearPerFaceColor(); // makes it easier to detect when the per face setting is cleared.
else
m_per_face_color = color;
}
void ON_BrepFace::ClearPerFaceColor() const
{
m_per_face_color = ON_Color::UnsetColor;
}
const ON_Color ON_BrepFace::PerFaceColor() const
{
return m_per_face_color;
}
unsigned int ON_Brep::ClearPerFaceMaterialChannelIndices()
{
unsigned change_count = 0;
const unsigned face_count = m_F.UnsignedCount();
const ON_BrepFace* f = m_F.Array();
for (unsigned fi = 0; fi < face_count; ++fi)
{
if (0 != f[fi].m_face_material_channel)
{
f[fi].ClearMaterialChannelIndex();
++change_count;
}
}
return change_count;
}
bool ON_Brep::HasPerFaceMaterialChannelIndices() const
{
const unsigned face_count = m_F.UnsignedCount();
const ON_BrepFace* f = m_F.Array();
for (unsigned fi = 0; fi < face_count; ++fi)
{
if (0 != f[fi].m_face_material_channel)
return true;
}
return false;
}
unsigned int ON_Brep::ClearPerFaceColors() const
{
unsigned change_count = 0;
const unsigned face_count = m_F.UnsignedCount();
const ON_BrepFace* f = m_F.Array();
for (unsigned fi = 0; fi < face_count; ++fi)
{
if (ON_Color::UnsetColor != f[fi].m_per_face_color)
{
f[fi].ClearPerFaceColor();
++change_count;
}
}
return change_count;
}
bool ON_Brep::HasPerFaceColors() const
{
const unsigned face_count = m_F.UnsignedCount();
const ON_BrepFace* f = m_F.Array();
for (unsigned fi = 0; fi < face_count; ++fi)
{
if (ON_Color::UnsetColor != f[fi].m_per_face_color)
return true;
}
return false;
}
const std::shared_ptr<const ON_Mesh>& ON_BrepFace::UniqueMesh(ON::mesh_type mesh_type)
{
const auto& sp = SharedMesh(mesh_type);
if (sp && sp.use_count() > 1)
{
SetMesh(mesh_type, std::make_shared<const ON_Mesh>(*sp));
return SharedMesh(mesh_type);
}
return sp;
}
const std::shared_ptr<const ON_Mesh>& ON_BrepFace::SharedMesh(ON::mesh_type mesh_type) const
{
std::shared_ptr<const ON_Mesh>* pMesh = nullptr;
switch (mesh_type)
{
case ON::render_mesh:
pMesh = &m_pImpl->m_render_mesh;
break;
case ON::analysis_mesh:
pMesh = &m_pImpl->m_analysis_mesh;
break;
case ON::preview_mesh:
pMesh = &m_pImpl->m_preview_mesh;
break;
default:
pMesh = m_pImpl->m_render_mesh ? &m_pImpl->m_render_mesh : &m_pImpl->m_analysis_mesh;
if (pMesh->get() == nullptr)
{
pMesh = &m_pImpl->m_preview_mesh;
}
break;
}
if (pMesh && pMesh->get())
{
const_cast<ON_Mesh*>(pMesh->get())->m_parent = this;
}
return *pMesh;
}
const ON_Mesh* ON_BrepFace::Mesh( ON::mesh_type mt ) const
{
const auto& sp = SharedMesh(mt);
return sp ? sp.get() : nullptr;
}
bool ON_BrepFace::SetMesh(ON::mesh_type mt, const std::shared_ptr<const ON_Mesh>& mesh)
{
bool rc = true;
switch (mt)
{
case ON::render_mesh:
m_pImpl->m_render_mesh = mesh;
break;
case ON::analysis_mesh:
m_pImpl->m_analysis_mesh = mesh;
break;
case ON::preview_mesh:
m_pImpl->m_preview_mesh = mesh;
break;
default:
rc = false;
}
return rc;
}
bool ON_BrepFace::SetMesh(ON::mesh_type mt, ON_Mesh* mesh)
{
bool rc = true;
switch (mt)
{
case ON::render_mesh:
m_pImpl->m_render_mesh.reset(mesh);
break;
case ON::analysis_mesh:
m_pImpl->m_analysis_mesh.reset(mesh);
break;
case ON::preview_mesh:
m_pImpl->m_preview_mesh.reset(mesh);
break;
default:
rc = false;
}
return rc;
}
void ON_BrepFace::DestroyMesh( ON::mesh_type mt, bool bDeleteMesh )
{
return DestroyMesh(mt);
}
void ON_BrepFace::DestroyMesh(ON::mesh_type mt)
{
switch(mt) {
case ON::render_mesh:
m_pImpl->m_render_mesh.reset();
break;
case ON::analysis_mesh:
m_pImpl->m_analysis_mesh.reset();
break;
case ON::preview_mesh:
m_pImpl->m_preview_mesh.reset();
break;
default:
m_pImpl->m_render_mesh.reset();
m_pImpl->m_analysis_mesh.reset();
m_pImpl->m_preview_mesh.reset();
break;
}
}
unsigned int ON_BrepVertexArray::SizeOf() const
{
unsigned int sz = 0;
int i, count = Count();
for ( i = 0; i < count; i++ )
{
sz += m_a[i].SizeOf();
}
sz += (m_capacity - m_count)*sizeof(m_a[0]);
return sz;
}
unsigned int ON_BrepEdgeArray::SizeOf() const
{
unsigned int sz = 0;
int i, count = Count();
for ( i = 0; i < count; i++ )
{
sz += m_a[i].SizeOf();
}
sz += (m_capacity - m_count)*sizeof(m_a[0]);
return sz;
}
unsigned int ON_BrepTrimArray::SizeOf() const
{
unsigned int sz = 0;
int i, count = Count();
for ( i = 0; i < count; i++ )
{
sz += m_a[i].SizeOf();
}
sz += (m_capacity - m_count)*sizeof(m_a[0]);
return sz;
}
unsigned int ON_BrepLoopArray::SizeOf() const
{
unsigned int sz = 0;
int i, count = Count();
for ( i = 0; i < count; i++ )
{
sz += m_a[i].SizeOf();
}
sz += (m_capacity - m_count)*sizeof(m_a[0]);
return sz;
}
unsigned int ON_BrepFaceArray::SizeOf() const
{
unsigned int sz = 0;
int i, count = Count();
for ( i = 0; i < count; i++ )
{
sz += m_a[i].SizeOf();
}
sz += (m_capacity - m_count)*sizeof(m_a[0]);
return sz;
}
////////////////////////////////////////////////////////////////
// Class ON_Brep
////////////////////////////////////////////////////////////////
ON_BrepVertexArray::ON_BrepVertexArray()
{}
ON_BrepVertexArray::~ON_BrepVertexArray()
{}
ON_BrepEdgeArray::ON_BrepEdgeArray()
{}
ON_BrepEdgeArray::~ON_BrepEdgeArray()
{}
ON_BrepTrimArray::ON_BrepTrimArray()
{}
ON_BrepTrimArray::~ON_BrepTrimArray()
{}
ON_BrepLoopArray::ON_BrepLoopArray()
{}
ON_BrepLoopArray::~ON_BrepLoopArray()
{}
ON_BrepFaceArray::ON_BrepFaceArray()
{}
ON_BrepFaceArray::~ON_BrepFaceArray()
{}
ON_OBJECT_IMPLEMENT(ON_Brep,ON_Geometry,"60B5DBC5-E660-11d3-BFE4-0010830122F0");
void ON_Brep::Initialize()
{
memset(&m_brep_user,0,sizeof(m_brep_user));
m_is_solid = 0;
m_bbox.Destroy();
m_region_topology = nullptr;
}
ON_Brep* ON_Brep::New()
{
// use instead of new ON_Brep()
// (When openNURBS is used as a Windows DLL,
// this forces the call to new to happen in the openNURBS DLL.)
return new ON_Brep();
}
ON_Brep* ON_Brep::New(const ON_Brep& src)
{
// use instead of new ON_Brep(const ON_Brep&)
// (When openNURBS is used as a Windows DLL,
// this forces the call to new to happen in the openNURBS DLL.)
return new ON_Brep(src);
}
ON_Brep::ON_Brep()
{
ON__SET__THIS__PTR(m_s_ON_Brep_ptr);
Initialize();
/*
const size_t sz = sizeof(*this);
const char* p0 = (char*)(this);
const char* p1 = (char*)(&m_bbox);
const char* p2 = (char*)(&m_region_topology);
const char* p3 = (char*)(&m_aggregate_status);
const char* p4 = (char*)(&m_is_solid);
const char* p5 = (char*)(&m_sleep_lock);
const char* p100 = (char*)(this+1);
const size_t offset1 = (p1 - p0);
const size_t offset2 = (p2 - p0);
const size_t offset3 = (p3 - p0);
const size_t offset4 = (p4 - p0);
const size_t offset5 = (p5 - p0);
const size_t offset100 = (p100 - p0);
ON_TextLog::Null.Print(L"", sz, offset1, offset2, offset3, offset4, offset5, offset100);
//BEFORE:
// offset1 216
// offset2 264
// offset3 272
// offset4 304
// offset5 308
// offset100 312
// sz 312
// AFTER
// ...
*/
}
ON_Brep::ON_Brep(const ON_Brep& src) : ON_Geometry(src)
{
ON__SET__THIS__PTR(m_s_ON_Brep_ptr);
Initialize();
*this = src;
}
ON_Brep::~ON_Brep()
{
DestroyMesh(ON::any_mesh);
// everything is in array classes that destroy themselves.
delete m_region_topology;
m_region_topology = 0;
}
unsigned int ON_Brep::SizeOf() const
{
int i, count;
unsigned int sz = ON_Geometry::SizeOf();
sz += (sizeof(*this) - sizeof(ON_Geometry));
sz += m_C2.SizeOfArray();
sz += m_C3.SizeOfArray();
sz += m_S.SizeOfArray();
count = m_C2.Count();
for ( i = 0; i < count; i++ )
{
const ON_Curve* c2 = m_C2[i];
if ( c2 )
sz += c2->SizeOf();
}
count = m_C3.Count();
for ( i = 0; i < count; i++ )
{
const ON_Curve* c3 = m_C3[i];
if ( c3 )
sz += c3->SizeOf();
}
count = m_S.Count();
for ( i = 0; i < count; i++ )
{
const ON_Surface* s = m_S[i];
if ( s )
sz += s->SizeOf();
}
sz += m_V.SizeOf();
sz += m_E.SizeOf();
sz += m_T.SizeOf();
sz += m_L.SizeOf();
sz += m_F.SizeOf();
return sz;
}
ON__UINT32 ON_BrepVertex::DataCRC(ON__UINT32 current_remainder) const
{
current_remainder = ON_CRC32(current_remainder,sizeof(m_vertex_index),&m_vertex_index);
current_remainder = ON_CRC32(current_remainder,sizeof(m_tolerance),&m_tolerance);
current_remainder = m_ei.DataCRC(current_remainder);
return current_remainder;
}
ON__UINT32 ON_BrepEdge::DataCRC(ON__UINT32 current_remainder) const
{
current_remainder = ON_CurveProxy::DataCRC(current_remainder);
current_remainder = ON_CRC32(current_remainder,sizeof(m_edge_index),&m_edge_index);
current_remainder = ON_CRC32(current_remainder,sizeof(m_c3i),&m_c3i);
current_remainder = ON_CRC32(current_remainder,2*sizeof(m_vi[0]),&m_vi[0]);
current_remainder = m_ti.DataCRC(current_remainder);
current_remainder = ON_CRC32(current_remainder,sizeof(m_tolerance),&m_tolerance);
return current_remainder;
}
ON__UINT32 ON_BrepFace::DataCRC(ON__UINT32 current_remainder) const
{
current_remainder = ON_SurfaceProxy::DataCRC(current_remainder);
current_remainder = ON_CRC32(current_remainder,sizeof(m_face_index),&m_face_index);
current_remainder = ON_CRC32(current_remainder,sizeof(m_bRev),&m_bRev);
current_remainder = m_li.DataCRC(current_remainder);
return current_remainder;
}
ON__UINT32 ON_Brep::DataCRC(ON__UINT32 current_remainder) const
{
current_remainder = m_V.DataCRC(current_remainder);
current_remainder = m_E.DataCRC(current_remainder);
current_remainder = m_F.DataCRC(current_remainder);
return current_remainder;
}
void ON_Brep::DestroyMesh( ON::mesh_type mt, bool bDeleteMesh )
{
DestroyMesh(mt);
}
void ON_Brep::DestroyMesh(ON::mesh_type mt)
{
const int fcnt = m_F.Count();
int fi;
for (fi = 0; fi < fcnt; fi++) {
m_F[fi].DestroyMesh(mt);
}
}
int ON_Brep::GetMesh( ON::mesh_type mt, ON_SimpleArray<const ON_Mesh*>& meshes ) const
{
int fcnt = m_F.Count();
int fi;
int null_count = 0;
meshes.Reserve( meshes.Count() + fcnt );
for ( fi = 0; fi < fcnt; fi++ )
{
const ON_Mesh* mesh = m_F[fi].Mesh(mt);
meshes.Append( mesh );
if ( !mesh )
{
// If some meshes are missing, we have to put
// a null in the return array so the face-to-mesh
// correspondence is preserved.
null_count++;
}
}
if ( null_count == fcnt )
{
// If ALL the meshes are missing, return 0.
meshes.SetCount(meshes.Count()-fcnt);
fcnt = 0;
}
return fcnt;
}
int ON_Brep::Dimension() const
{
return (m_V.Count() > 0) ? 3 : 0;
}
static void ON_BrepTransformSwapSrfHelper( ON_Brep& brep, ON_NurbsSurface* nurbs_srf, int si )
{
// Replace plane surface which could not be properly transformed
// with nurbs_surface.
ON_Surface* old_srf = brep.m_S[si];
ON_UserDataHolder udholder;
udholder.MoveUserDataFrom(*old_srf);
udholder.MoveUserDataTo(*nurbs_srf,false);
brep.m_S[si] = nurbs_srf;
// Update faces to use new surface.
const int fcount = brep.m_F.Count();
ON_BrepFace* f = brep.m_F.Array();
for ( int fi = 0; fi < fcount; fi++ )
{
if (f[fi].m_si == si || f[fi].ProxySurface() == old_srf )
{
const bool bIsTransposed = f[fi].ProxySurfaceIsTransposed();
f[fi].SetProxySurface(nurbs_srf);
if (bIsTransposed)
f[fi].ON_SurfaceProxy::Transpose();
}
}
delete old_srf;
}
bool ON_Brep::Transform( const ON_Xform& xform )
{
int i, count;
bool rc = true;
DestroyRuntimeCache();
int is_similarity = xform.IsSimilarity();
const double det = xform.Determinant();
bool bUniformScale = (0 != is_similarity && det != 0.0 && ON_IsValid(det) && fabs(fabs(det) - 1.0) > 1.0e-6);
const double uniform_scale = bUniformScale ? pow(fabs(det), 1.0 / 3.0) : 1.0;
if ( 1 != is_similarity )
{
// this will cause the solid flag to be
// recaclulated the next time it is needed.
m_is_solid = 0;
}
// 13 Feb 2003 Dale Lear:
// Transforming the bbox makes it grow too large under repeated
// rotations. So, we will destroy it here and reset it below.
//m_bbox.Transform(xform);
m_bbox.Destroy();
count = m_C3.Count();
for ( i = 0; i < count; i++ )
{
if ( m_C3[i] )
{
if ( !m_C3[i]->Transform(xform) )
rc = false;
}
}
count = m_S.Count();
for ( i = 0; i < count; i++ ) {
if ( m_S[i] )
{
ON_NurbsSurface* nurbs_srf = 0;
if ( !is_similarity )
{
if ( 1 == m_S[i]->Degree(0) // degree tests reduce calls to
&& 1 == m_S[i]->Degree(1) // slow ON_PlaneSurface::Cast()
&& 0 != ON_PlaneSurface::Cast(m_S[i]) )
{
nurbs_srf = ON_NurbsSurface::New();
if ( !m_S[i]->GetNurbForm(*nurbs_srf) )
{
delete nurbs_srf;
nurbs_srf = 0;
}
else if ( !nurbs_srf->Transform(xform) )
{
delete nurbs_srf;
nurbs_srf = 0;
}
}
}
if ( !m_S[i]->Transform(xform) )
{
if ( nurbs_srf )
{
ON_BrepTransformSwapSrfHelper(*this,nurbs_srf,i);
nurbs_srf = 0;
}
else
{
rc = false;
}
}
else if ( nurbs_srf )
{
// make sure transformation was good
ON_Interval u = nurbs_srf->Domain(0);
ON_Interval v = nurbs_srf->Domain(1);
for ( int ui = 0; ui < 2 && nurbs_srf; ui++ ) for (int vi = 0; vi < 2 && nurbs_srf; vi++)
{
ON_3dPoint P = nurbs_srf->PointAt(u[ui],v[vi]);
ON_3dPoint Q = m_S[i]->PointAt(u[ui],v[vi]);
if ( P.DistanceTo(Q) > ON_ZERO_TOLERANCE )
{
ON_BrepTransformSwapSrfHelper(*this,nurbs_srf,i);
nurbs_srf = 0;
break;
}
}
if ( nurbs_srf )
{
delete nurbs_srf;
nurbs_srf = 0;
}
}
}
}
count = m_V.Count();
for ( i = 0; i < count; i++ ) {
if ( !m_V[i].Transform(xform) )
rc = false;
}
count = m_E.Count();
for ( i = 0; i < count; i++ ) {
ON_BrepEdge& edge = m_E[i];
edge.TransformUserData(xform);
// 2014-05-13 Dale Lear RH-26852
// Based on a conversation with Chuck, we will
// test scaling edge tolerances. The bug
// bailed down to a unit system change from a
// "long" unit to mm made a valid brep invalid
// because the edge tolerances were too small.
// This fix does ot handle non-uniform scaling.
// In the case of a non-uniform scale, the edge
// tolerance should be recalculated.
if (edge.m_tolerance > 0.0 && uniform_scale > 0.0 && uniform_scale != 1.0)
{
edge.m_tolerance *= uniform_scale;
}
}
count = m_F.Count();
for ( i = 0; i < count; i++ )
{
ON_BrepFace& face = m_F[i];
face.TransformUserData(xform);
// 13 Feb 2003 Dale Lear:
// Transforming the bbox makes it grow too large under repeated
// rotations. So, we need to reset it.
face.m_bbox.Destroy();
//GBA 20 May 2020. Brep box now computed from face boxes, instead of surface boxes.
face.m_bbox = face.BoundingBox();
if ( face.m_face_index != -1 )
m_bbox.Union( face.m_bbox );
// 12 May 2003 Dale Lear - RR 10528
// Use surface evaluation to update rendermesh when
// calling ON_Mesh::Transform() will map mesh normals
// to some thing different that the "true" surface
// normal.
bool bEvMesh = ( fabs(det) <= ON_SQRT_EPSILON
|| xform[3][0] != 0.0
|| xform[3][1] != 0.0
|| xform[3][2] != 0.0
|| xform[3][3] != 1.0
);
const ON_Surface* srf = face.SurfaceOf();
if ( 0 == srf )
bEvMesh = false;
//Render meshes
{
auto spMesh = face.UniqueMesh(ON::render_mesh);
if (spMesh)
{
auto pMesh = const_cast<ON_Mesh*>(spMesh.get());
if ( bEvMesh && pMesh->EvaluateMeshGeometry(*srf) )
{
if ( face.m_bRev )
{
// 29 September 2003 Dale Lear
// Normals on render meshes (and face orientations)
// take face.m_bRev into account so that two sided
// materials work as expected. EvaluateMeshGeometry()
// does not take face.m_bRev into account, so we need
// to reverse the face normals here.
int ni, ncnt = pMesh->m_N.Count();
for ( ni = 0; ni < ncnt; ni++ )
{
pMesh->m_N[ni] = -(pMesh->m_N[ni]);
}
}
}
else
{
pMesh->Transform(xform);
}
}
}
//Analysis meshes
{
auto spMesh = face.UniqueMesh(ON::analysis_mesh);
if (spMesh)
{
auto pMesh = const_cast<ON_Mesh*>(spMesh.get());
// Dale Lear 30 March 2009 - bug 46766
// Evaluate analysis meshes when the transform involves scaling
// so curvature values are properly updated.
bool bEvAnalysisMesh = bEvMesh;
if ( !bEvAnalysisMesh )
{
ON_Xform tmp(xform);
tmp.m_xform[0][3] = 0.0;
tmp.m_xform[1][3] = 0.0;
tmp.m_xform[2][3] = 0.0;
if ( 1 != tmp.IsSimilarity() )
bEvAnalysisMesh = true;
}
// 1 Sept 2021, Mikko, RH-65468:
// Added null check to prevent a crash.
if ( bEvAnalysisMesh && nullptr != srf && pMesh->EvaluateMeshGeometry(*srf) )
{
// 28 Sept 2012, Mikko:
// Apply the "29 September 2003 Dale Lear" fix above also to analysis meshes.
if ( face.m_bRev )
{
int ni, ncnt = pMesh->m_N.Count();
for ( ni = 0; ni < ncnt; ni++ )
{
pMesh->m_N[ni] = -(pMesh->m_N[ni]);
}
}
}
else
{
pMesh->Transform(xform);
}
}
}
//Preview meshes
{
auto spMesh = face.UniqueMesh(ON::preview_mesh);
if (spMesh)
{
auto pMesh = const_cast<ON_Mesh*>(spMesh.get());
if ( bEvMesh && pMesh->EvaluateMeshGeometry(*srf) )
{
if ( face.m_bRev )
{
// 10 June 2021, Mikko, RH-64582:
// Fixed typo that caused a crash, changed "m_analysis_mesh" to "m_preview_mesh".
int ni, ncnt = pMesh->m_N.Count();
for ( ni = 0; ni < ncnt; ni++ )
{
pMesh->m_N[ni] = -(pMesh->m_N[ni]);
}
}
}
else
{
pMesh->Transform(xform);
}
}
}
}
// The call to transform user data needs to be last
// so that the rest of the brep is in position.
// In particular, ON_BrepRegionTopologyUserData::Transform
// assumes the face bounding boxes are up to date.
TransformUserData(xform);
return rc;
}
/////////////////////////////////////////////////////////////////
// ON_Brep Creation Interface
int
ON_Brep::AddTrimCurve( ON_Curve* pC )
{
int c2i = -1;
if ( 0 != pC )
{
// 7 April 2003 Dale Lear:
// There are too many cases where bugs are caused by
// people attempting to use 3d curves for trims. In
// all the cases encountered so far, the intent was
// to pass in a 2d curve, so...
int dim = pC->Dimension();
if ( dim != 2 )
{
ON_ERROR("ON_Brep::AddTrimCurve() go a non-2d curve - changing dim to 2.");
pC->ChangeDimension(2);
dim = pC->Dimension();
}
if ( 2 == dim )
{
c2i = m_C2.Count();
m_C2.Append(pC);
}
}
return c2i;
}
int
ON_Brep::AddEdgeCurve( ON_Curve* pC )
{
int c3i = -1;
if ( 0 != pC )
{
int dim = pC->Dimension();
if ( dim != 3 )
{
// 7 April 2003 Dale Lear: (See comment in ON_Brep::AddTrimCurve().)
ON_ERROR("ON_Brep::AddEdgeCurve() got a non-3d curve - changing dim to 3.");
pC->ChangeDimension(3);
dim = pC->Dimension();
}
if ( 3 == dim )
{
c3i = m_C3.Count();
m_C3.Append(pC);
}
}
return c3i;
}
int
ON_Brep::AddSurface( ON_Surface* pS )
{
int si = -1;
if ( pS && pS->Dimension() == 3 )
{
si = m_S.Count();
m_S.Append(pS);
}
m_bbox.Destroy();
m_is_solid = 0;
return si;
}
ON_BrepVertex&
ON_Brep::NewVertex()
{
int vi = m_V.Count();
m_V.Reserve(vi+1);
m_V.SetCount(vi+1);
ON_BrepVertex& vertex = m_V.Array()[vi];
vertex.m_vertex_index = vi;
vertex.point = ON_3dPoint::UnsetPoint;
vertex.m_tolerance = ON_UNSET_VALUE;
return vertex;
}
ON_BrepVertex&
ON_Brep::NewVertex( ON_3dPoint vertex_point, double vertex_tolerance )
{
ON_BrepVertex& vertex = NewVertex();
vertex.point = vertex_point;
vertex.m_tolerance = vertex_tolerance;
return vertex;
}
ON_BrepEdge&
ON_Brep::NewEdge( int c3i )
{
int ei = m_E.Count();
ON_BrepEdge& edge = m_E.AppendNew();
edge.m_tolerance = ON_UNSET_VALUE;
edge.m_edge_index = ei;
edge.m_c3i = c3i;
if ( edge.m_c3i >= 0 && edge.m_c3i < m_C3.Count() )
{
edge.SetProxyCurve(m_C3[edge.m_c3i]);
}
edge.m_brep = this;
return edge;
}
ON_BrepEdge&
ON_Brep::NewEdge( ON_BrepVertex& v0, ON_BrepVertex& v1,
int c3i, const ON_Interval* edomain,
double edge_tolerance )
{
ON_BrepEdge& edge = NewEdge(c3i);
edge.m_vi[0] = v0.m_vertex_index;
edge.m_vi[1] = v1.m_vertex_index;
v0.m_ei.Append(edge.m_edge_index);
v1.m_ei.Append(edge.m_edge_index);
if ( edomain && edomain->IsIncreasing() ) {
ON_Interval edom;
edom.Intersection( edge.ProxyCurveDomain(), *edomain );
if ( edom.IsIncreasing() )
edge.SetProxyCurveDomain(edom);
}
edge.m_tolerance = edge_tolerance;
return edge;
}
bool ON_Brep::SetEdgeCurve(
ON_BrepEdge& edge,
int c3_index,
const ON_Interval* sub_domain
)
{
bool rc = false;
if ( c3_index == - 1 && !sub_domain )
{
edge.m_c3i = -1;
edge.SetProxyCurve(0);
rc = true;
}
else if ( c3_index >= 0 && c3_index <= m_C3.Count() && m_C3[c3_index] )
{
ON_Interval curve_domain = m_C3[c3_index]->Domain();
if ( !sub_domain || (sub_domain->IsIncreasing() && curve_domain.Includes(*sub_domain)) )
{
edge.m_c3i = c3_index;
edge.SetProxyCurve( m_C3[c3_index],
(sub_domain) ? *sub_domain : curve_domain
);
rc = true;
}
}
return rc;
}
bool ON_Brep::SetTrimCurve(
ON_BrepTrim& trim,
int c2_index,
const ON_Interval* sub_domain
)
{
bool rc = false;
if ( c2_index == - 1 && !sub_domain )
{
trim.m_c2i = -1;
trim.SetProxyCurve(0);
//June 18 2013 - Chuck - nuke the bounding box and pline since it came from the old 2d curve.
trim.DestroyPspaceInformation();
rc = true;
}
else if ( c2_index >= 0 && c2_index <= m_C2.Count() && m_C2[c2_index] )
{
ON_Interval curve_domain = m_C2[c2_index]->Domain();
if ( !sub_domain || (sub_domain->IsIncreasing() && curve_domain.Includes(*sub_domain)) )
{
trim.m_c2i = c2_index;
trim.SetProxyCurve( m_C2[trim.m_c2i], (sub_domain) ? *sub_domain : curve_domain );
trim.m_pbox = m_C2[trim.m_c2i]->BoundingBox();
trim.m_pbox.m_min.z = 0.0;
trim.m_pbox.m_max.z = 0.0;
rc = true;
}
}
return rc;
}
ON_BrepTrim&
ON_Brep::NewTrim( int c2i )
{
m_is_solid = 0;
int ti = m_T.Count();
ON_BrepTrim& trim = m_T.AppendNew();
trim.m_brep = this;
trim.m_trim_index = ti;
trim.m_ei = -1;
trim.m_type = ON_BrepTrim::unknown;
trim.m_bRev3d = false;
trim.m_c2i = c2i;
trim.m_iso = ON_Surface::not_iso;
trim.m_li = -1;
trim.m_tolerance[0] = ON_UNSET_VALUE;
trim.m_tolerance[1] = ON_UNSET_VALUE;
trim.m__legacy_2d_tol = ON_UNSET_VALUE;
trim.m__legacy_3d_tol = ON_UNSET_VALUE;
trim.m__legacy_flags = 0;
const ON_Curve* c2 = (c2i >= 0 && c2i < m_C2.Count())
? m_C2[c2i]
: 0;
if ( c2 )
{
trim.SetProxyCurve( c2 );
trim.m_pbox = c2->BoundingBox();
trim.m_pbox.m_min.z = 0.0;
trim.m_pbox.m_max.z = 0.0;
}
return trim;
}
ON_BrepTrim&
ON_Brep::NewTrim( ON_BrepEdge& edge, bool bRev3d, int c2i )
{
m_is_solid = 0;
ON_BrepTrim& trim = NewTrim( c2i );
trim.m_ei = edge.m_edge_index;
edge.m_ti.Append(trim.m_trim_index);
trim.m_vi[0] = edge.m_vi[bRev3d?1:0];
trim.m_vi[1] = edge.m_vi[bRev3d?0:1];
trim.m_bRev3d = bRev3d?true:false;
return trim;
}
ON_BrepTrim&
ON_Brep::NewTrim( ON_BrepEdge& edge, bool bRev3d, ON_BrepLoop& loop, int c2i )
{
m_is_solid = 0;
const int edge_trim_count0 = edge.m_ti.Count();
ON_BrepTrim& trim = NewTrim( edge, bRev3d, c2i );
trim.m_li = loop.m_loop_index;
loop.m_ti.Append(trim.m_trim_index);
if ( c2i >= 0 && c2i < m_C2.Count() )
{
ON_Curve* c2 = m_C2[c2i];
if ( c2 )
{
ON_BoundingBox c2_bbox;
if ( c2->GetBoundingBox(c2_bbox) )
{
c2_bbox.m_min.z = 0.0;
c2_bbox.m_max.z = 0.0;
if ( loop.m_ti.Count() == 1 )
loop.m_pbox = c2_bbox;
else
loop.m_pbox.Union(c2_bbox);
}
}
}
if ( edge_trim_count0 == 0 )
{
// This is the only trim using this edge.
//
// At the moment it's a boundary trim. The type
// will be changed to seam or mated when
// another trim is added that uses this edge.
trim.m_type = ON_BrepTrim::boundary;
}
else if ( edge_trim_count0 == 1 )
{
// there are now two trims using this edge
ON_BrepTrim::TYPE trim_type = ON_BrepTrim::mated;
ON_BrepTrim& other_trim = m_T[edge.m_ti[0]];
/*
GBA RH-60512 April-21
New seam requirements.
Mate is on on the same face
Loop is an outer loop
This and Mate are paired side isos ( e.g. W and East)
matching non iso dir parameters in opposite directions
Underlying surface is closed in the correct direction
Remarks
If constructing a brep with mated trims on the same face that
don't meet these requirements. Then split the face so all
trims are validly mated
*/
if ( trim.Face() && trim.Face() == other_trim.Face() )
{
if( other_trim.Loop()==trim.Loop())
trim_type = ON_BrepTrim::seam;
else
{
// trim_type is left as unknown this will cause IsValid to fail
trim_type = ON_BrepTrim::unknown;
}
}
else
trim_type = ON_BrepTrim::mated;
trim.m_type = trim_type;
other_trim.m_type = trim_type;
}
else
{
// edge_trim_count0 >1
// non-manifold edge - need to check for mated or seam
bool matchedseam = false;
ON_BrepTrim::TYPE trim_type = ON_BrepTrim::mated;
for ( int eti = 0; eti < edge_trim_count0; eti++ )
{
ON_BrepTrim& other_trim = m_T[edge.m_ti[eti]];
if (trim.Face() && trim.Face() == other_trim.Face())
{
if (other_trim.Loop() == trim.Loop() && !matchedseam &&
( other_trim.m_type == ON_BrepTrim::mated || other_trim.m_type == ON_BrepTrim::seam))
{
trim_type = ON_BrepTrim::seam;
other_trim.m_type = ON_BrepTrim::seam;
matchedseam = true;
}
else
{
// Trims from from same edge on same face but different loops is invalid
trim_type = ON_BrepTrim::unknown;
other_trim.m_type = ON_BrepTrim::unknown;
break;
}
}
}
trim.m_type = trim_type;
}
return trim;
}
ON_BrepTrim&
ON_Brep::NewTrim( bool bRev3d, ON_BrepLoop& loop, int c2i )
{
m_is_solid = 0;
ON_BrepTrim& trim = NewTrim( c2i );
trim.m_bRev3d = bRev3d ? true : false;
trim.m_li = loop.m_loop_index;
loop.m_ti.Append(trim.m_trim_index);
if ( c2i >= 0 && c2i < m_C2.Count() )
{
const ON_Curve* c2 = m_C2[c2i];
if ( c2 )
{
ON_BoundingBox c2_bbox;
if ( c2->GetBoundingBox(c2_bbox) )
{
c2_bbox.m_min.z = 0.0;
c2_bbox.m_max.z = 0.0;
if ( loop.m_ti.Count() == 1 )
loop.m_pbox = c2_bbox;
else
loop.m_pbox.Union( c2_bbox );
}
}
}
return trim;
}
ON_BrepTrim&
ON_Brep::NewSingularTrim(const ON_BrepVertex& vertex,ON_BrepLoop& loop, ON_Surface::ISO iso, int c2i)
{
ON_BrepTrim& trim = NewTrim(false,loop,c2i);
trim.m_vi[0] = vertex.m_vertex_index;
trim.m_vi[1] = trim.m_vi[0];
trim.m_type = ON_BrepTrim::singular;
trim.m_iso = iso;
trim.m_tolerance[0] = 0.0;
trim.m_tolerance[1] = 0.0;
trim.m__legacy_2d_tol = 0.0;
trim.m__legacy_3d_tol = 0.0;
trim.m__legacy_flags_Set(-1,1);
return trim;
}
void ON_Brep::Append( const ON_Brep& b )
{
int i, j, jcnt;
const int vcount0 = m_V.Count();
const int ecount0 = m_E.Count();
const int fcount0 = m_F.Count();
const int tcount0 = m_T.Count();
const int lcount0 = m_L.Count();
const int c2count0 = m_C2.Count();
const int c3count0 = m_C3.Count();
const int scount0 = m_S.Count();
const int vcount1 = b.m_V.Count();
const int ecount1 = b.m_E.Count();
const int fcount1 = b.m_F.Count();
const int tcount1 = b.m_T.Count();
const int lcount1 = b.m_L.Count();
const int c2count1 = b.m_C2.Count();
const int c3count1 = b.m_C3.Count();
const int scount1 = b.m_S.Count();
// need to duplicate geometry
ON_Object* obj;
ON_Curve* c;
ON_Surface* s;
for ( i = 0; i < scount1; i++ ) {
s = b.m_S[i];
if ( s ) {
obj = s->Duplicate();
s = ON_Surface::Cast(obj);
if ( !s )
delete obj;
}
m_S.Append(s);
}
for ( i = 0; i < c2count1; i++ ) {
c = b.m_C2[i];
if ( c ) {
obj = c->Duplicate();
c = ON_Curve::Cast(obj);
if ( !c )
delete obj;
}
m_C2.Append(c);
}
for ( i = 0; i < c3count1; i++ ) {
c = b.m_C3[i];
if ( c ) {
obj = c->Duplicate();
c = ON_Curve::Cast(obj);
if ( !c )
delete obj;
}
m_C3.Append(c);
}
// copy topology info
m_V.Append( b.m_V.Count(), b.m_V.Array() );
m_E.Append( b.m_E.Count(), b.m_E.Array() );
m_F.Append( b.m_F.Count(), b.m_F.Array() );
m_T.Append( b.m_T.Count(), b.m_T.Array() );
m_L.Append( b.m_L.Count(), b.m_L.Array() );
// update indices
for ( i = 0; i < vcount1; i++ ) {
ON_BrepVertex& vertex = m_V[vcount0+i];
if ( vertex.m_vertex_index >= 0 )
vertex.m_vertex_index += vcount0;
else
vertex.m_vertex_index = -1;
jcnt = vertex.m_ei.Count();
for ( j = 0; j < jcnt; j++ ) {
if ( vertex.m_ei[j] >=0 )
vertex.m_ei[j] += ecount0;
}
}
for ( i = 0; i < ecount1; i++ )
{
ON_BrepEdge& edge = m_E[ecount0+i];
if ( edge.m_edge_index >= 0 )
edge.m_edge_index += ecount0;
else
edge.m_edge_index = -1;
if ( edge.m_c3i >= 0 )
edge.m_c3i += c3count0;
if ( edge.m_vi[0] >= 0 )
edge.m_vi[0] += vcount0;
if ( edge.m_vi[1] >= 0 )
edge.m_vi[1] += vcount0;
jcnt = edge.m_ti.Count();
for ( j = 0; j < jcnt; j++ ) {
if ( edge.m_ti[j] >= 0 )
edge.m_ti[j] += tcount0;
}
edge.m_brep = this;
if (edge.m_c3i >= 0)
edge.SetProxyCurve( m_C3[edge.m_c3i], b.m_E[i].ProxyCurveDomain() );
else
edge.SetProxyCurve( 0, b.m_E[i].ProxyCurveDomain() );
if ( b.m_E[i].ProxyCurveIsReversed() != edge.ProxyCurveIsReversed() )
edge.ON_CurveProxy::Reverse();
edge.SetDomain( b.m_E[i].Domain() );
}
for ( i = 0; i < tcount1; i++ ) {
ON_BrepTrim& trim = m_T[tcount0+i];
trim.m_brep = this;
if ( trim.m_trim_index == i )
trim.m_trim_index = tcount0+i;
else
trim.m_trim_index = -1;
if ( trim.m_c2i >= 0 )
trim.m_c2i += c2count0;
if ( trim.m_ei >= 0 )
trim.m_ei += ecount0;
if ( trim.m_vi[0] >= 0 )
trim.m_vi[0] += vcount0;
if ( trim.m_vi[1] >= 0 )
trim.m_vi[1] += vcount0;
if ( trim.m_li >= 0 )
trim.m_li += lcount0;
if (trim.m_c2i >= 0)
trim.SetProxyCurve( m_C2[trim.m_c2i], b.m_T[i].ProxyCurveDomain() );
else
trim.SetProxyCurve( 0, b.m_T[i].ProxyCurveDomain() );
if ( b.m_T[i].ProxyCurveIsReversed() != trim.ProxyCurveIsReversed() )
trim.ON_CurveProxy::Reverse();
trim.SetDomain( b.m_T[i].Domain() );
}
for ( i = 0; i < lcount1; i++ )
{
ON_BrepLoop& loop = m_L[lcount0+i];
if ( loop.m_loop_index >= 0 )
loop.m_loop_index += lcount0;
else
loop.m_loop_index = -1;
jcnt = loop.m_ti.Count();
for ( j = 0; j < jcnt; j++ ) {
if ( loop.m_ti[j] >= 0)
loop.m_ti[j] += tcount0;
}
if ( loop.m_fi >= 0 )
loop.m_fi += fcount0;
loop.m_brep = this;
}
for ( i = 0; i < fcount1; i++ ) {
ON_BrepFace& face = m_F[fcount0+i];
if ( face.m_face_index >= 0 )
face.m_face_index += fcount0;
else
face.m_face_index = -1;
jcnt = face.m_li.Count();
for ( j = 0; j < jcnt; j++ ) {
if ( face.m_li[j] >= 0 )
face.m_li[j] += lcount0;
}
if ( face.m_si >= 0 )
{
face.m_si += scount0;
face.SetProxySurface(m_S[face.m_si]);
}
else
{
face.SetProxySurface( 0 );
}
face.m_brep = this;
}
//grow bounding box if possible. otherwise invalidate it.
if (m_bbox.IsValid() && b.BoundingBox().IsValid())
m_bbox.Union(b.BoundingBox());
else m_bbox.Destroy();
m_is_solid = 0;
DestroyMesh(ON::any_mesh);
return;
}
ON_BrepLoop&
ON_Brep::NewLoop( ON_BrepLoop::TYPE looptype )
{
// 2 Sept 2020 S. Baer (RH-59952)
// Destroy cached bounding box on breps when messing around with loops
m_bbox.Destroy();
m_is_solid = 0;
int li = m_L.Count();
m_L.Reserve(li+1);
m_L.SetCount(li+1);
ON_BrepLoop& loop = m_L.Array()[li];
loop.m_loop_index = li;
loop.m_type = looptype;
loop.m_brep = this;
return loop;
}
ON_BrepLoop&
ON_Brep::NewLoop( ON_BrepLoop::TYPE looptype, ON_BrepFace& face )
{
// 2 Sept 2020 S. Baer (RH-59952)
// Destroy cached bounding box on breps when messing around with loops
m_bbox.Destroy();
m_is_solid = 0;
ON_BrepLoop& loop = NewLoop( looptype );
loop.m_fi = face.m_face_index;
if ( ON_BrepLoop::outer == looptype )
{
// the index of the outer loop is always
// in face.m_li[0]
face.m_li.Insert(0,loop.m_loop_index);
}
else
{
face.m_li.Append(loop.m_loop_index);
}
loop.m_brep = this;
return loop;
}
ON_BrepLoop* ON_Brep::NewOuterLoop( int face_index )
{
m_is_solid = 0;
int vid[4] = {-1,-1,-1,-1};
int eid[4] = {-1,-1,-1,-1};
bool bRev3d[4] = {false, false, false, false};
return NewOuterLoop( face_index,vid,eid,bRev3d);
}
ON_BrepFace& ON_Brep::NewFace( int si )
{
m_bbox.Destroy();
// GBA 28-MAy-2020 RH-58462. m_bbox is now left unset after this function
// This works since ON_BrepFace::BoundingBox() supports lazy evaluation
m_is_solid = 0;
int fi = m_F.Count();
m_F.Reserve(fi+1);
m_F.SetCount(fi+1);
ON_BrepFace& face = m_F.Array()[fi];
face.m_face_index = fi;
face.m_si = si;
face.m_brep = this;
if ( si >= 0 && si < m_S.Count() )
face.SetProxySurface(m_S[si]);
return face;
}
ON_BrepFace* ON_Brep::NewFace( const ON_Surface& surface )
{
m_bbox.Destroy();
m_is_solid = 0;
ON_BrepFace* face = nullptr;
ON_Surface* pSurface = surface.DuplicateSurface();
if ( pSurface )
{
int vid[4] = {-1,-1,-1,-1};
int eid[4] = {-1,-1,-1,-1};
bool bRev3d[4] = {false,false,false,false};
face = NewFace(pSurface,vid,eid,bRev3d);
}
return face;
}
bool
ON_Brep::SetTrimIsoFlags()
{
bool rc = true;
int fi;
const int fcnt = m_F.Count();
for ( fi = 0; fi < fcnt; fi++ ) {
if ( !SetTrimIsoFlags( m_F[fi] ) )
rc = false;
}
return rc;
}
bool
ON_Brep::SetTrimIsoFlags( ON_BrepFace& face )
{
bool rc = true;
int fli;
const int face_loop_count = face.m_li.Count();
for ( fli = 0; fli < face_loop_count; fli++ ) {
if ( !SetTrimIsoFlags( m_L[face.m_li[fli]] ) )
rc = false;
}
return rc;
}
bool
ON_Brep::SetTrimIsoFlags( ON_BrepLoop& loop )
{
bool rc = true;
int lti;
const int loop_trim_count = loop.m_ti.Count();
for ( lti = 0; lti < loop_trim_count; lti++ ) {
if ( !SetTrimIsoFlags( m_T[loop.m_ti[lti]] ) )
rc = false;
}
return rc;
}
bool
ON_Brep::SetTrimIsoFlags( ON_BrepTrim& trim )
{
bool rc = false;
if ( trim.m_li >= 0 && trim.m_li < m_L.Count() )
{
const int fi = m_L[trim.m_li].m_fi;
if ( fi >= 0 && fi < m_F.Count() )
{
const ON_Surface* pS = m_F[fi].SurfaceOf();
if ( pS )
{
const ON_Curve* pC = (trim.m_c2i >= 0 && trim.m_c2i < m_C2.Count())
? m_C2[trim.m_c2i]
: 0;
if ( pC )
{
ON_Interval PD = trim.ProxyCurveDomain();
trim.m_iso = pS->IsIsoparametric( *pC, &PD);
rc = true;
}
}
}
}
return rc;
}
bool
ON_Brep::SetTrimTypeFlags( bool bLazy )
{
bool rc = true;
int fi;
const int fcnt = m_F.Count();
for ( fi = 0; fi < fcnt; fi++ ) {
if ( !SetTrimTypeFlags( m_F[fi], bLazy ) )
rc = false;
}
return rc;
}
bool
ON_Brep::SetTrimTypeFlags( ON_BrepFace& face, bool bLazy )
{
bool rc = true;
int fli;
const int face_loop_count = face.m_li.Count();
for ( fli = 0; fli < face_loop_count; fli++ ) {
if ( !SetTrimTypeFlags( m_L[face.m_li[fli]], bLazy ) )
rc = false;
}
return rc;
}
bool
ON_Brep::SetTrimTypeFlags( ON_BrepLoop& loop, bool bLazy )
{
bool rc = true;
int lti;
const int loop_trim_count = loop.m_ti.Count();
for ( lti = 0; lti < loop_trim_count; lti++ ) {
if ( !SetTrimTypeFlags( m_T[loop.m_ti[lti]], bLazy ) )
rc = false;
}
return rc;
}
ON_BrepTrim::TYPE ON_Brep::TrimType( const ON_BrepTrim& trim, bool bLazy ) const
{
ON_BrepTrim::TYPE trim_type = bLazy ? trim.m_type : ON_BrepTrim::unknown;
int eti, other_ti;
if ( trim_type == ON_BrepTrim::unknown && trim.m_li >= 0 && trim.m_li < m_L.Count() )
{
const ON_BrepLoop& loop = m_L[trim.m_li];
if ( loop.m_type == ON_BrepLoop::ptonsrf )
trim_type = ON_BrepTrim::ptonsrf;
else if (loop.m_type == ON_BrepLoop::crvonsrf )
trim_type = ON_BrepTrim::crvonsrf;
else if ( trim.m_ei == -1 )
{
trim_type = ON_BrepTrim::singular;
}
else if ( trim.m_ei >= 0 && trim.m_ei < m_E.Count() )
{
const ON_BrepEdge& edge = m_E[trim.m_ei];
if ( edge.m_ti.Count() == 1 && edge.m_ti[0] == trim.m_trim_index )
{
trim_type = ON_BrepTrim::boundary;
}
else if ( edge.m_ti.Count() > 1 )
{
trim_type = ON_BrepTrim::mated;
// check for seam
for ( eti = 0; eti < edge.m_ti.Count(); eti++ )
{
other_ti = edge.m_ti[eti];
if ( other_ti != trim.m_trim_index && other_ti >= 0 && other_ti < m_T.Count() )
{
if ( m_T[other_ti].m_li == trim.m_li )
{
trim_type = ON_BrepTrim::seam;
break;
}
}
}
}
}
}
return trim_type;
}
bool
ON_Brep::SetTrimTypeFlags( ON_BrepTrim& trim, bool bLazy )
{
if ( !bLazy || trim.m_type == ON_BrepTrim::unknown)
trim.m_type = TrimType(trim,false);
return ((trim.m_type != ON_BrepTrim::unknown)?true:false);
}
bool
ON_Brep::GetTrim2dStart(int trim_index,
ON_2dPoint& P
) const
{
if (trim_index < 0 || trim_index >= m_T.Count())
return false;
const ON_BrepTrim& trim = m_T[trim_index];
ON_3dPoint pp;
if (!trim.EvPoint(trim.Domain()[0], pp))
return false;
P = pp;
return true;
}
bool
ON_Brep::GetTrim2dEnd(int trim_index,
ON_2dPoint& P
) const
{
if (trim_index < 0 || trim_index >= m_T.Count())
return false;
const ON_BrepTrim& trim = m_T[trim_index];
ON_3dPoint pp;
if (!trim.EvPoint(trim.Domain()[1], pp))
return false;
P = pp;
return true;
}
bool
ON_Brep::GetTrim3dStart(int trim_index,
ON_3dPoint& P
) const
{
const ON_Surface* srf = nullptr;
ON_3dPoint uv(ON_3dPoint::NanPoint);
if ( trim_index >= 0 && trim_index < m_T.Count() )
{
const ON_BrepTrim& trim = m_T[trim_index];
if ( trim.m_li >= 0 && trim.m_li < m_L.Count() )
{
const int fi = m_L[trim.m_li].m_fi;
if ( fi >= 0 && fi < m_F.Count() )
{
if ( trim.Evaluate(trim.Domain()[0],0,3,&uv.x) )
{
srf = m_F[fi].SurfaceOf();
}
}
}
}
return (nullptr != srf && srf->EvPoint(uv.x, uv.y, P) ? true : false);
}
bool
ON_Brep::GetTrim3dEnd(int trim_index,
ON_3dPoint& P
) const
{
const ON_Surface* srf = nullptr;
ON_3dPoint uv(ON_3dPoint::NanPoint);
if ( trim_index >= 0 && trim_index < m_T.Count() )
{
const ON_BrepTrim& trim = m_T[trim_index];
if ( trim.m_li >= 0 && trim.m_li < m_L.Count() )
{
const int fi = m_L[trim.m_li].m_fi;
if ( fi >= 0 && fi < m_F.Count() )
{
if ( trim.Evaluate(trim.Domain()[1],0,3,&uv.x) )
{
srf = m_F[fi].SurfaceOf();
}
}
}
}
return (nullptr != srf && srf->EvPoint(uv.x, uv.y, P) ? true : false);
}
ON_BrepLoop::TYPE
ON_Brep::ComputeLoopType( const ON_BrepLoop& loop ) const
{
// This function must always compute the type from the
// 2d trim geometry. NEVER modify this function to
// return the input value of loop.m_type.
ON_BrepLoop::TYPE loop_type = ON_BrepLoop::unknown;
int loop_dir = LoopDirection( loop );
if ( 1 == loop_dir )
loop_type = ON_BrepLoop::outer;
else if ( -1 == loop_dir )
loop_type = ON_BrepLoop::inner;
// TODO check for gaps, slits, etc.
/*
int ugap_count = 0;
int vgap_count = 0;
double d, utol0, vtol0, loop_start_utol, loop_start_vtol;
ON_3dPoint p0, p1, loop_start;
ON_Interval srf_domain[2];
if ( loop.m_fi >= 0 && loop.m_fi < m_F.Count() ) {
const ON_BrepFace& face = m_F[loop.m_fi];
if ( face.m_si >= 0 && face.m_si < m_S.Count() ) {
ON_Surface* srf = m_S[face.m_si];
srf_domain[0] = srf->Domain(0);
srf_domain[1] = srf->Domain(1);
}
}
const ON_2dPoint basePt( srf_domain[0].ParameterAt(0.5), srf_domain[1].ParameterAt(0.5) );
const int trim_count = loop.m_ti.Count();
for ( lti = 0; lti < trim_count; lti++ )
{
ti = loop.m_ti[lti];
const ON_BrepTrim& trim = m_T[ti];
p0 = trim.PointAtStart();
u_tol0 = trim.m_tolerance[0];
v_tol0 = trim.m_tolerance[1];
if ( !lti )
{
loop_start = p0;
loop_start_utol = trim.m_tolerance[0];
loop_start_vtol = trim.m_tolerance[1];
}
else
{
d = fabs(p0.x-p1.x);
if ( d > utol0 + trim.m_tolerance[0] )
ugap_count++;
d = fabs(p0.y-p1.y);
if ( d > vtol0 + trim.m_tolerance[1] )
vgap_count++;
}
p1 = c2->PointAtEnd();
}
*/
return loop_type;
}
bool
ON_Brep::IsValidTrim( int trim_index, ON_TextLog* text_log ) const
{
if ( trim_index < 0 || trim_index >= m_T.Count() )
{
if ( text_log )
{
text_log->Print("brep trim_index = %d (should be >=0 and <%d=brep.m_T.Count()).\n",
trim_index, m_T.Count());
}
return ON_BrepIsNotValid();
}
const ON_BrepTrim& trim = m_T[trim_index];
if ( trim.m_trim_index != trim_index )
{
if ( text_log )
{
text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index);
text_log->PushIndent();
text_log->Print("trim.m_trim_index = %d (should be %d).\n",
trim.m_trim_index, trim_index );
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
if ( !trim.IsValid(text_log) )
{
if ( text_log )
text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index);
return ON_BrepIsNotValid();
}
if ( trim.m_c2i < 0 || trim.m_c2i >= m_C2.Count() )
{
if ( text_log )
{
text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index);
text_log->PushIndent();
text_log->Print( "trim.m_c2i = %d (should be >=0 and <%d).\n", trim.m_c2i, 0, m_C2.Count() );
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
const ON_Curve* pC = m_C2[trim.m_c2i];
if ( !pC )
{
if ( text_log )
{
text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index);
text_log->PushIndent();
text_log->Print("trim.m_c2i = %d and ON_Brep.m_C2[%d] is nullptr\n", trim.m_c2i, trim.m_c2i );
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
int c2_dim = pC->Dimension();
if ( c2_dim != 2 )
{
if ( text_log )
{
text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index);
text_log->PushIndent();
text_log->Print("trim.m_c2i = %d and ON_Brep.m_C2[%d]->Dimension() = %d (should be 2).\n", trim.m_c2i, trim.m_c2i, c2_dim );
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
if ( pC != trim.ProxyCurve() )
{
if ( text_log )
{
text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index);
text_log->PushIndent();
text_log->Print("trim.ProxyCurve() != m_C2[trim.m_c2i].\n");
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
//if ( trim.ProxyCurveIsReversed() )
//{
// if ( text_log )
// {
// text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index);
// text_log->PushIndent();
// text_log->Print("trim.ProxyCurveIsReversed() is true\n");
// text_log->PopIndent();
// }
// return ON_BrepIsNotValid();
//}
ON_Interval trim_domain = trim.Domain();
ON_Interval c2_domain = pC->Domain();
if ( !trim_domain.IsIncreasing() )
{
if ( text_log )
{
text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index);
text_log->PushIndent();
text_log->Print("trim.Domain() = (%g,%g) (should be an increasing interval).\n", trim_domain[0], trim_domain[1] );
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
if ( !c2_domain.Includes(trim_domain) )
{
if ( text_log )
{
text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index);
text_log->PushIndent();
text_log->Print("trim.Domain() = (%g,%g) is not included in brep.m_C2[trim.m_c2i=%d]->Domain() = (%g,%g)\n",
trim_domain[0], trim_domain[1], trim.m_c2i, c2_domain[0], c2_domain[1] );
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
int vi0 = trim.m_vi[0];
int vi1 = trim.m_vi[1];
if ( vi0 < 0 || vi0 >= m_V.Count() )
{
if ( text_log )
{
text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index);
text_log->PushIndent();
text_log->Print("trim.m_vi[0] = %d (should be >= 0 and < %d=brep.m_V.Count()).\n",
trim_index, vi0, m_V.Count() );
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
if ( vi1 < 0 || vi1 >= m_V.Count() )
{
if ( text_log )
{
text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index);
text_log->PushIndent();
text_log->Print("trim.m_vi[1] = %d (should be >= 0 and < %d=brep.m_V.Count()).\n",
trim_index, vi1, m_V.Count() );
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
const int ei = trim.m_ei;
int trim_eti = -1;
if ( trim.m_type == ON_BrepTrim::singular )
{
// singular trim - no edge and 3d v0 = 3d v1
if ( ei != -1 )
{
if ( text_log )
{
text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index);
text_log->PushIndent();
text_log->Print("trim.m_type = singular but trim.m_ei = %d (should be -1)\n",ei);
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
if ( vi0 != vi1 )
{
if ( text_log )
{
text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index);
text_log->PushIndent();
text_log->Print("trim.m_type = singular but trim.m_vi[] = [%d,%d] (the m_vi[] values should be equal).\n",
vi0,vi1);
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
if ( pC->IsClosed() )
{
if ( text_log )
{
text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index);
text_log->PushIndent();
text_log->Print("trim.m_type = singular but brep.m_C2[trim.m_c2i=%d]->IsClosed() is true.\n",
trim.m_c2i,trim.m_c2i);
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
}
else if ( trim.m_type != ON_BrepTrim::ptonsrf )
{
// non-singular non-ptonsrf trim must have valid edge
if ( ei < 0 || ei >= m_E.Count() )
{
if ( text_log )
{
text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index);
text_log->PushIndent();
text_log->Print("trim.m_type != singular and trim.m_ei = %d (m_ei should be >=0 and <brep.m_E.Count()=%d\n",
trim.m_ei,m_E.Count());
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
const ON_BrepEdge& edge = m_E[ei];
if ( edge.m_vi[trim.m_bRev3d?1:0] != vi0 )
{
if ( text_log )
{
text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index);
text_log->PushIndent();
text_log->Print("trim.m_vi[0] != brep.m_E[trim.m_ei=%d].m_vi[trim.m_bRev3d?1:0]\n",ei);
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
if ( edge.m_vi[trim.m_bRev3d?0:1] != vi1 )
{
if ( text_log )
{
text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index);
text_log->PushIndent();
text_log->Print("trim.m_vi[1] != brep.m_E[trim.m_ei=%d].m_vi[trim.m_bRev3d?0:1]\n",ei);
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
if ( trim_domain == c2_domain && pC->IsClosed() )
{
// (open 2d trims can still have vi0 = vi1 on closed surfaces)
if ( vi0 != vi1 )
{
if ( text_log )
{
text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index);
text_log->PushIndent();
text_log->Print("trim.m_vi[] = [%d,%d] but brep.m_C2[trim.m_c2i=%d]->IsClosed()=true\n",
vi0, vi1, trim.m_c2i );
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
}
else if ( vi0 == vi1 )
{
// TODO: check that trim start/end is a closed surface seam point.
}
else
{
// vi0 != vi1
// TODO: check that trim start/end is not a closed surface seam point.
}
int i;
for ( i = 0; i < edge.m_ti.Count(); i++ )
{
if ( edge.m_ti[i] == trim_index )
{
trim_eti = i;
break;
}
}
if ( trim_eti < 0 )
{
if ( text_log )
{
text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index);
text_log->PushIndent();
text_log->Print("trim index %d is not in brep.m_E[trim.m_ei=%d].m_ti[]\n",
trim_index, trim.m_ei );
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
if ( edge.m_ti.Count() == 2 )
{
int other_ti = edge.m_ti[ (edge.m_ti[0]==trim_index)?1:0 ];
if ( other_ti >= 0 && other_ti < m_T.Count() && other_ti != trim_index )
{
const ON_BrepTrim& other_trim = m_T[other_ti];
if ( other_trim.m_li == trim.m_li )
{
if ( trim.m_type != ON_BrepTrim::seam )
{
if ( text_log )
{
text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index);
text_log->PushIndent();
text_log->Print("trim.m_type!=seam but brep.m_E[trim.m_ei=%d] references two trims in loop trim.m_li=%d.\n",
trim.m_ei,trim.m_li);
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
}
}
}
}
if ( trim.m_li < 0 || trim.m_li >= m_L.Count() )
{
if ( text_log )
{
text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index);
text_log->PushIndent();
text_log->Print("trim.m_li = %d (should be >= 0 and <brep.m_L.Count()=%d\n", trim.m_li,m_L.Count() );
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
if ( trim.m_ei >= 0 && trim_eti < 0 )
{
if ( text_log )
{
text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index);
text_log->PushIndent();
text_log->Print("brep.m_E[trim.m_ei=%d].m_ti[] does not reference the trim.\n",trim.m_ei);
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
switch ( trim.m_type )
{
case ON_BrepTrim::unknown:
{
if ( text_log )
{
text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index);
text_log->PushIndent();
text_log->Print("trim.m_type = unknown (should be set to the correct ON_BrepTrim::TYPE value)\n");
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
break;
case ON_BrepTrim::boundary:
{
const ON_BrepLoop& loop = m_L[trim.m_li];
const ON_BrepEdge& edge = m_E[trim.m_ei];
if ( edge.m_ti.Count() > 1 )
{
if ( text_log )
{
text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index);
text_log->PushIndent();
text_log->Print("trim.m_type = boundary but brep.m_E[trim.m_ei=%d] has 2 or more trims.\n",trim.m_ei);
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
if ( loop.m_type != ON_BrepLoop::outer && loop.m_type != ON_BrepLoop::inner )
{
if ( text_log )
{
text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index);
text_log->PushIndent();
text_log->Print("trim.m_type = boundary but brep.m_L[trim.m_li=%d].m_type is not inner or outer.\n",trim.m_li);
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
}
break;
case ON_BrepTrim::mated:
{
const ON_BrepLoop& loop = m_L[trim.m_li];
const ON_BrepEdge& edge = m_E[trim.m_ei];
if ( edge.m_ti.Count() < 2 )
{
if ( text_log )
{
text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index);
text_log->PushIndent();
text_log->Print("trim.m_type = mated but brep.m_E[trim.m_ei=%d] only references this trim.\n",trim.m_ei);
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
if ( loop.m_type != ON_BrepLoop::outer && loop.m_type != ON_BrepLoop::inner )
{
if ( text_log )
{
text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index);
text_log->PushIndent();
text_log->Print("trim.m_type = mated but brep.m_L[trim.m_li=%d].m_type is not inner or outer.\n",trim.m_li);
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
}
break;
case ON_BrepTrim::seam:
{
const ON_BrepLoop& loop = m_L[trim.m_li];
const ON_BrepEdge& edge = m_E[trim.m_ei];
if ( edge.m_ti.Count() < 2 )
{
if ( text_log )
{
text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index);
text_log->PushIndent();
text_log->Print("trim.m_type = seam but brep.m_E[trim.m_ei=%d] < 2.\n",trim.m_ei);
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
int other_ti = -1;
for ( int eti = 0; eti < edge.m_ti.Count(); eti++ )
{
if ( trim_eti == eti )
continue;
int i = edge.m_ti[eti];
if ( i == trim_index )
{
if ( text_log )
{
text_log->Print("brep.m_E[%d] trim is not valid.\n",trim.m_ei);
text_log->PushIndent();
text_log->Print("edge.m_ti[%d] = m_ti[%d] = %d.\n",trim_eti,eti,trim_index);
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
if ( i < 0 || i >= m_T.Count() )
{
if ( text_log )
{
text_log->Print("brep.m_E[%d] trim is not valid.\n",trim.m_ei);
text_log->PushIndent();
text_log->Print("edge.m_ti[%d]=%d is not a valid m_T[] index.\n",eti,i);
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
const ON_BrepTrim& other_trim = m_T[i];
if ( other_trim.m_type == ON_BrepTrim::seam && other_trim.m_li == trim.m_li )
{
if ( other_ti < 0 )
other_ti = i;
else
{
if ( text_log )
{
text_log->Print("brep.m_T[%d,%d, or %d] trim is not valid.\n",trim_index,other_ti,i);
text_log->PushIndent();
text_log->Print("All three trims have m_type = seam m_ei=%d and m_li = %d.\n",trim.m_ei,trim.m_li);
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
}
}
if ( other_ti < 0 )
{
if ( text_log )
{
text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index);
text_log->PushIndent();
text_log->Print("trim.m_type = seam but its other trim is not in the loop.\n");
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
if ( loop.m_type != ON_BrepLoop::outer && edge.m_ti.Count() <= 2 )
{
if ( text_log )
{
text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index);
text_log->PushIndent();
text_log->Print("trim.m_type = seam, the edge is manifold, but brep.m_L[trim.m_li=%d].m_type is not outer.\n",trim.m_li);
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
// 31 Jan 2002 - The definition of a seam trim is a trim that is connected to
// an edge, is part of loop, and exactly one other trim in the
// same loop is connected to the same edge. This can happen
// on the interior of a surface (like an annulus in a plane)
// and on non-manifold edges.
//if ( trim.m_iso != ON_Surface::W_iso && trim.m_iso != ON_Surface::N_iso &&
// trim.m_iso != ON_Surface::E_iso && trim.m_iso != ON_Surface::S_iso )
//{
// if ( text_log )
// {
// text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index);
// text_log->PushIndent();
// text_log->Print("trim.m_type = seam but trim.m_iso != N/S/E/W_iso\n");
// text_log->PopIndent();
// }
// return ON_BrepIsNotValid();
//}
}
break;
case ON_BrepTrim::singular:
// most requirements are checked above
if ( trim.m_iso != ON_Surface::W_iso && trim.m_iso != ON_Surface::N_iso &&
trim.m_iso != ON_Surface::E_iso && trim.m_iso != ON_Surface::S_iso )
{
if ( text_log )
{
text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index);
text_log->PushIndent();
text_log->Print("trim.m_type = singular but trim.m_iso != N/S/E/W_iso\n");
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
break;
case ON_BrepTrim::crvonsrf:
{
const ON_BrepLoop& loop = m_L[trim.m_li];
if ( loop.m_type != ON_BrepLoop::crvonsrf )
{
return ON_BrepIsNotValid();
}
if ( trim.m_c2i < 0 || trim.m_c2i >= m_C2.Count() )
{
return ON_BrepIsNotValid();
}
if ( trim.m_ei < 0 || trim.m_ei >= m_E.Count() )
{
return ON_BrepIsNotValid();
}
}
break;
case ON_BrepTrim::ptonsrf:
{
const ON_BrepLoop& loop = m_L[trim.m_li];
if ( loop.m_type != ON_BrepLoop::ptonsrf )
{
return ON_BrepIsNotValid();
}
if ( trim.m_ei != -1 )
{
return ON_BrepIsNotValid();
}
if ( trim.m_c2i != -1 )
{
return ON_BrepIsNotValid();
}
if ( trim.m_pbox.m_min.x != trim.m_pbox.m_max.x || trim.m_pbox.m_min.y != trim.m_pbox.m_max.y || trim.m_pbox.m_min.z != trim.m_pbox.m_max.z )
{
// m_pbox must be a single point that defines surface parameters of the point.
return ON_BrepIsNotValid();
}
if ( trim.m_pbox.m_min.x == ON_UNSET_VALUE || trim.m_pbox.m_min.y == ON_UNSET_VALUE || trim.m_pbox.m_min.z != 0.0 )
{
// m_pbox must be a single point that defines surface parameters of the point.
return ON_BrepIsNotValid();
}
}
break;
case ON_BrepTrim::slit:
{
if ( text_log )
{
text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index);
text_log->PushIndent();
text_log->Print("trim.m_type = ON_BrepTrim::slit (should be set to the correct ON_BrepTrim::TYPE value)\n");
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
break;
case ON_BrepTrim::trim_type_count:
{
if ( text_log )
{
text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index);
text_log->PushIndent();
text_log->Print("trim.m_type = type_count (should be set to the correct ON_BrepTrim::TYPE value)\n");
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
break;
default:
{
if ( text_log )
{
text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index);
text_log->PushIndent();
text_log->Print("trim.m_type = garbage (should be set to the correct ON_BrepTrim::TYPE value)\n");
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
break;
}
if ( trim.m_tolerance[0] < 0.0 )
{
if ( text_log )
{
text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index);
text_log->PushIndent();
text_log->Print("trim.m_tolerance[0] = %g (should be >= 0.0)\n",trim.m_tolerance[0]);
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
if ( trim.m_tolerance[1] < 0.0 )
{
if ( text_log )
{
text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index);
text_log->PushIndent();
text_log->Print("trim.m_tolerance[1] = %g (should be >= 0.0)\n",trim.m_tolerance[1]);
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
if ( !trim.m_pbox.IsValid() )
{
if ( text_log )
{
text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index);
text_log->PushIndent();
text_log->Print("trim.m_pbox is not valid.\n");
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
if ( trim.m_brep != this )
{
if ( text_log )
{
text_log->Print("brep.m_T[%d] trim is not valid.\n",trim_index);
text_log->PushIndent();
text_log->Print("trim.m_brep does not point to parent brep.\n");
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
return true;
}
bool
ON_Brep::IsValidLoop( int loop_index, ON_TextLog* text_log ) const
{
if ( loop_index < 0 || loop_index >= m_L.Count() )
{
if ( text_log )
{
text_log->Print("brep loop_index = %d (should be >=0 and <%d=brep.m_L.Count()).\n",
loop_index, m_L.Count());
}
return ON_BrepIsNotValid();
}
const ON_BrepLoop& loop = m_L[loop_index];
if ( loop.m_loop_index != loop_index )
{
if ( text_log )
{
text_log->Print("brep.m_L[%d] loop is not valid.\n",loop_index);
text_log->PushIndent();
text_log->Print("loop.m_loop_index = %d (should be %d).\n",
loop.m_loop_index, loop_index );
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
if ( loop.m_fi < 0 || loop.m_fi >= m_F.Count() )
{
if ( text_log )
{
text_log->Print("brep.m_L[%d] loop is not valid.\n",loop_index);
text_log->PushIndent();
text_log->Print("loop.m_fi = %d (should be >= 0 and <brep.m_F.Count()=%d\n", loop.m_fi, m_F.Count() );
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
const int loop_trim_count = loop.m_ti.Count();
if ( loop_trim_count <= 0 )
{
if ( text_log )
{
text_log->Print("brep.m_L[%d] loop is not valid.\n",loop_index);
text_log->PushIndent();
text_log->Print("loop.m_ti.Count() is <= 0 (should be > 0)\n");
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
if ( loop.m_type != ON_BrepLoop::outer
&& loop.m_type != ON_BrepLoop::inner
&& loop.m_type != ON_BrepLoop::slit
)
{
if ( text_log )
{
text_log->Print("brep.m_L[%d] loop is not valid.\n",loop_index);
text_log->PushIndent();
text_log->Print("loop.m_type = %d (must be %d=outer, %d=inner, or %d=slit)\n",
loop.m_type,ON_BrepLoop::outer,ON_BrepLoop::inner,ON_BrepLoop::slit);
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
if ( loop.m_brep != this )
{
if ( text_log )
{
text_log->Print("loop.m_L[%d] loop is not valid.\n",loop_index);
text_log->PushIndent();
text_log->Print("loop.m_brep does not point to parent brep.\n");
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
// make sure trims are valid
int i, lti, ti;
for ( lti = 0; lti < loop_trim_count; lti++ ) {
ti = loop.m_ti[lti];
for ( i = 0; i < lti; i++ ) {
if ( loop.m_ti[i] == ti )
{
if ( text_log )
{
text_log->Print("brep.m_L[%d] loop is not valid.\n",loop_index);
text_log->PushIndent();
text_log->Print("loop.m_ti[%d] = loop.m_ti[%d] = %d (trim index can only appear once)\n",
lti, i, ti);
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
}
if ( !IsValidTrim( ti, text_log ) )
{
if ( text_log )
{
text_log->Print("brep.m_L[%d] loop is not valid.\n",loop_index);
text_log->PushIndent();
text_log->Print("brep.m_T[loop.m_ti[%d]=%d] is not valid.\n",
lti, ti);
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
if ( m_T[ti].m_li != loop_index )
{
if ( text_log )
{
text_log->Print("brep.m_L[%d] loop is not valid.\n",loop_index);
text_log->PushIndent();
text_log->Print("brep.m_T[loop.m_ti[%d]=%d].m_li=%d (m_li should be %d).\n",
lti, ti, m_T[ti].m_li, loop_index );
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
}
if ( ON_BrepLoop::slit == loop.m_type )
{
if ( loop.m_ti.Count() < 2 || 0 != (loop.m_ti.Count() % 2) )
{
if ( text_log )
{
text_log->Print("brep.m_L[%d] loop is not valid.\n",loop_index);
text_log->PushIndent();
text_log->Print("loop.m_type = slit but loop has %d trims\n",loop.m_ti.Count());
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
for ( int lti_for_loop = 0; lti_for_loop < loop.m_ti.Count(); lti_for_loop++ )
{
int ti_for_loop = loop.m_ti[lti_for_loop];
const ON_BrepTrim& trim = m_T[ti_for_loop];
if ( trim.m_type != ON_BrepTrim::seam )
{
if ( text_log )
{
text_log->Print("brep.m_L[%d] slit loop is not valid.\n",loop_index);
text_log->PushIndent();
text_log->Print("brep.m_T[loop.m_ti[%d]=%d].m_type = %d (should be %d = seam)\n",
lti_for_loop,ti_for_loop,trim.m_type,ON_BrepTrim::seam);
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
switch( trim.m_iso )
{
case ON_Surface::W_iso:
case ON_Surface::E_iso:
case ON_Surface::S_iso:
case ON_Surface::N_iso:
{
if ( text_log )
{
text_log->Print("brep.m_L[%d] slit loop is not valid.\n",loop_index);
text_log->PushIndent();
text_log->Print("brep.m_T[loop.m_ti[%d]=%d].m_iso = E/W/N/S_iso (should be interior)\n",
lti_for_loop,ti_for_loop);
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
break;
case ON_Surface::not_iso:
case ON_Surface::x_iso:
case ON_Surface::y_iso:
case ON_Surface::iso_count:
break;
}
}
}
// make sure ends of trims jibe
int ci0, ci1, next_lti;
ON_3dPoint P0, P1;
const ON_Curve *pC0, *pC1;
for ( lti = 0; lti < loop_trim_count; lti++ )
{
//double x_tol = ON_ZERO_TOLERANCE;
//double y_tol = ON_ZERO_TOLERANCE;
const ON_BrepTrim& trim0 = m_T[loop.m_ti[lti]];
next_lti = (lti+1)%loop_trim_count;
const ON_BrepTrim& trim1 = m_T[loop.m_ti[next_lti]];
ON_Interval trim0_domain = trim0.Domain();
ON_Interval trim1_domain = trim1.Domain();
ci0 = trim0.m_c2i;
ci1 = trim1.m_c2i;
pC0 = m_C2[ci0];
pC1 = m_C2[ci1];
P0 = pC0->PointAt( trim0_domain[1] ); // end of this 2d trim
P1 = pC1->PointAt( trim1_domain[0] ); // start of next 2d trim
if ( !(P0-P1).IsTiny() )
{
// 16 September 2003 Dale Lear - RR 11319
// Added relative tol check so cases with huge
// coordinate values that agreed to 10 places
// didn't get flagged as bad.
double xtol = (fabs(P0.x) + fabs(P1.x))*1.0e-10;
double ytol = (fabs(P0.y) + fabs(P1.y))*1.0e-10;
if ( xtol < ON_ZERO_TOLERANCE )
xtol = ON_ZERO_TOLERANCE;
if ( ytol < ON_ZERO_TOLERANCE )
ytol = ON_ZERO_TOLERANCE;
double dx = fabs(P0.x-P1.x);
double dy = fabs(P0.y-P1.y);
if ( dx > xtol || dy > ytol )
{
if ( text_log )
{
text_log->Print("brep.m_L[%d] loop is not valid.\n",loop_index);
text_log->PushIndent();
text_log->Print("end of brep.m_T[loop.m_ti[%d]=%d]=(%g,%g) and start \n", lti, loop.m_ti[lti],P0.x,P0.y);
text_log->Print("of brep.m_T[loop.m_ti[%d]=%d]=(%g,%g) do not match.\n",next_lti, loop.m_ti[next_lti],P1.x,P1.y);
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
}
}
if ( !loop.m_pbox.IsValid() )
{
if ( text_log )
{
text_log->Print("brep.m_L[%d] loop is not valid.\n",loop_index);
text_log->PushIndent();
text_log->Print("loop.m_pbox is not valid\n");
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
return true;
}
bool
ON_Brep::IsValidFace( int face_index, ON_TextLog* text_log ) const
{
if ( face_index < 0 || face_index >= m_F.Count() )
{
if ( text_log )
{
text_log->Print("brep face_index = %d (should be >=0 and <%d=brep.m_F.Count()).\n",
face_index, m_F.Count());
}
return ON_BrepIsNotValid();
}
const ON_BrepFace& face = m_F[face_index];
if ( face.m_face_index != face_index )
{
if ( text_log )
{
text_log->Print("brep.m_F[%d] face is not valid.\n",face_index);
text_log->PushIndent();
text_log->Print("face.m_face_index = %d (should be %d).\n",
face.m_face_index, face_index );
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
if ( face.m_brep != this )
{
if ( text_log )
{
text_log->Print("brep.m_F[%d] face is not valid.\n",face_index);
text_log->PushIndent();
text_log->Print("face.m_brep does not point to parent brep.\n");
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
const int face_loop_count = face.m_li.Count();
if ( face_loop_count <= 0 )
{
if ( text_log )
{
text_log->Print("brep.m_F[%d] face is not valid.\n",face_index);
text_log->PushIndent();
text_log->Print("face.m_li.Count() <= 0 (should be >= 1)\n");
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
int i, fli, li;
for ( fli = 0; fli < face_loop_count; fli++ )
{
li = face.m_li[fli];
for ( i = 0; i < fli; i++ )
{
if ( face.m_li[i] == li )
{
if ( text_log )
{
text_log->Print("brep.m_F[%d] face is not valid.\n",face_index);
text_log->PushIndent();
text_log->Print("face.m_li[%d]=face.m_li[%d]=%d (a loop index should appear once in face.m_li[])\n",
fli,i,li);
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
}
if ( !IsValidLoop( li, text_log ) )
{
if ( text_log )
{
text_log->Print("brep.m_F[%d] face is not valid.\n",face_index);
text_log->PushIndent();
text_log->Print("brep.m_L[face.m_li[%d]=%d] is not valid.\n",fli,li);
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
const ON_BrepLoop& loop = m_L[li];
if ( loop.m_loop_index != li )
{
if ( text_log )
{
text_log->Print("brep.m_F[%d] face is not valid.\n",face_index);
text_log->PushIndent();
text_log->Print("face.m_li[%d]=%d is a deleted loop\n",
fli,li);
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
if ( loop.m_fi != face_index )
{
if ( text_log )
{
text_log->Print("brep.m_F[%d] face is not valid.\n",face_index);
text_log->PushIndent();
text_log->Print("face.m_li[%d]=%d but brep.m_L[%d].m_fi=%d (m_fi should be %d)\n",
fli,li,li,loop.m_fi,face_index);
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
if ( fli == 0 )
{
if ( loop.m_type != ON_BrepLoop::outer )
{
if ( text_log )
{
text_log->Print("brep.m_F[%d] face is not valid.\n",face_index);
text_log->PushIndent();
text_log->Print("brep.m_L[face.m_li[0]=%d].m_type is not outer.\n",li);
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
}
else
{
if ( loop.m_type != ON_BrepLoop::slit
&& loop.m_type != ON_BrepLoop::inner
)
{
if ( text_log )
{
text_log->Print("brep.m_F[%d] face is not valid.\n",face_index);
text_log->PushIndent();
text_log->Print("brep.m_L[face.m_li[%d]=%d].m_type is not inner or slit.\n",fli,li);
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
}
}
const int si = face.m_si;
if ( si < 0 || si >= m_S.Count() )
{
if ( text_log )
{
text_log->Print("brep.m_F[%d] face is not valid.\n",face_index);
text_log->PushIndent();
text_log->Print("face.m_si=%d (should be >=0 and <%d=m_S.Count())\n",
face.m_si,m_S.Count());
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
if ( !m_S[si] )
{
if ( text_log )
{
text_log->Print("brep.m_F[%d] face is not valid.\n",face_index);
text_log->PushIndent();
text_log->Print("brep.m_S[face.m_si=%d] is nullptr\n",face.m_si);
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
if ( m_S[si] != face.ProxySurface() )
{
if ( text_log )
{
text_log->Print("brep.m_F[%d] face is not valid.\n",face_index);
text_log->PushIndent();
text_log->Print("brep.m_S[face.m_si=%d] != face.ProxySurface().\n",si);
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
if ( face.ProxySurfaceIsTransposed() )
{
if ( text_log )
{
text_log->Print("brep.m_F[%d] face is not valid.\n",face_index);
text_log->PushIndent();
text_log->Print("face.ProxySurfaceIsTransposed() is true.\n");
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
return true;
}
bool
ON_Brep::IsValidEdge( int edge_index, ON_TextLog* text_log ) const
{
if ( edge_index < 0 || edge_index >= m_E.Count() )
{
if ( text_log )
text_log->Print("brep edge_index = %d (should be >=0 and <%d=brep.m_E.Count() ).\n",
edge_index, m_E.Count());
return ON_BrepIsNotValid();
}
const ON_BrepEdge& edge = m_E[edge_index];
if ( edge.m_brep != this )
{
if ( text_log )
{
text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index);
text_log->PushIndent();
text_log->Print("edge.m_brep does not point to parent brep\n");
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
if ( edge.m_edge_index != edge_index )
{
if ( text_log )
{
text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index);
text_log->PushIndent();
text_log->Print("edge.m_edge_index = %d (should be %d).\n",
edge.m_edge_index, edge_index );
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
if ( !edge.IsValid(text_log) )
{
if ( text_log )
{
text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index);
text_log->PushIndent();
text_log->Print("edge is not a valid.\n");
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
const int ci = edge.m_c3i;
if ( ci < 0 || ci >= m_C3.Count() )
{
if ( text_log )
{
text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index);
text_log->PushIndent();
text_log->Print("edge.m_c3i = %d (should be >=0 and <%d=m_C3.Count()\n",
edge.m_c3i,m_C3.Count() );
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
if ( m_C3[ci] != edge.ProxyCurve() || 0 == m_C3[ci] )
{
if ( text_log )
{
text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index);
text_log->PushIndent();
text_log->Print("edge.m_curve != brep.m_C3[edge.m_c3i=%d]\n", edge.m_c3i );
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
//if ( edge.ProxyCurveIsReversed() )
//{
// if ( text_log )
// {
// text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index);
// text_log->PushIndent();
// text_log->Print("edge.ProxyCurveIsReversed() is true.\n" );
// text_log->PopIndent();
// }
// return ON_BrepIsNotValid();
//}
double t0, t1;
if ( !edge.GetDomain( &t0, &t1 ) )
{
if ( text_log )
{
ON_Interval edom = edge.ProxyCurveDomain();
text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index);
text_log->PushIndent();
text_log->Print( "edge.m_domain=(%g,%g) is not valid\n", edom[0], edom[1]);
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
const int vi0 = edge.m_vi[0];
const int vi1 = edge.m_vi[1];
if ( vi0 < 0 || vi0 >= m_V.Count() )
{
if ( text_log )
{
text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index);
text_log->PushIndent();
text_log->Print("edge.m_vi[0]=%d (should be >=0 and <%d=m_V.Count()\n",
vi0, m_V.Count() );
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
if ( vi1 < 0 || vi1 >= m_V.Count() )
{
if ( text_log )
{
text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index);
text_log->PushIndent();
text_log->Print("edge.m_vi[1]=%d (should be >=0 and <%d=m_V.Count()\n",
vi1, m_V.Count() );
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
int evi;
for ( evi = 0; evi < 2; evi++ )
{
const ON_BrepVertex& vertex = m_V[edge.m_vi[evi]];
if ( edge.m_vi[evi] != vertex.m_vertex_index )
{
if ( text_log )
{
text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index);
text_log->PushIndent();
text_log->Print("edge.m_vi[%d]=%d is a deleted vertex\n",
evi,edge.m_vi[evi] );
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
const int vertex_edge_count = vertex.m_ei.Count();
bool bFoundIt = false;
int vei;
for ( vei = 0; vei < vertex_edge_count && !bFoundIt; vei++ ) {
bFoundIt = (vertex.m_ei[vei] == edge_index);
}
if ( !bFoundIt )
{
if ( text_log )
{
text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index);
text_log->PushIndent();
text_log->Print("edge.m_vi[%d]=%d but edge is not referenced in m_V[%d].m_ei[]\n",
evi,edge.m_vi[evi],edge.m_vi[evi] );
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
}
if ( edge.IsClosed() ) {
if ( vi0 != vi1 )
{
if ( text_log )
{
text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index);
text_log->PushIndent();
text_log->Print("edge.m_vi[]=(%d,%d) but edge.IsClosed() is true\n",
vi0,vi1);
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
}
else {
if ( vi0 == vi1 )
{
if ( text_log )
{
text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index);
text_log->PushIndent();
text_log->Print("edge.m_vi[0]=edge.m_vi[1]=%d but edge.IsClosed() is false.\n",
vi0);
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
}
const int edge_trim_count = edge.m_ti.Count();
if ( edge_trim_count < 0 )
{
if ( text_log )
{
text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index);
text_log->PushIndent();
text_log->Print("edge.m_ti.Count() < 0\n");
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
int i, eti, ti;
for (eti = 0; eti < edge_trim_count; eti++ )
{
ti = edge.m_ti[eti];
if ( ti < 0 || ti >= m_T.Count() )
{
if ( text_log )
{
text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index);
text_log->PushIndent();
text_log->Print("edge.m_ti[%d]=%d (should be >=0 and <%d=m_T.Count())\n",eti,ti);
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
if ( m_T[ti].m_trim_index != ti )
{
if ( text_log )
{
text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index);
text_log->PushIndent();
text_log->Print("edge.m_ti[%d]=%d is a deleted trim\n",eti,ti);
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
for ( i = 0; i < eti; i++ )
{
if ( edge.m_ti[i] == ti )
{
if ( text_log )
{
text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index);
text_log->PushIndent();
text_log->Print("edge.m_ti[%d]=edge.m_ti[%d]=%d (a trim should be referenced once).\n",i,eti,ti);
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
}
const ON_BrepTrim& trim = m_T[ti];
if ( trim.m_ei != edge_index )
{
if ( text_log )
{
text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index);
text_log->PushIndent();
text_log->Print("edge.m_ti[%d]=%d but brep.m_T[%d].m_ei=%d\n",eti,ti,ti,trim.m_ei);
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
}
if ( edge.m_tolerance < 0.0 )
{
if ( text_log )
{
text_log->Print("brep.m_E[%d] edge is not valid.\n",edge_index);
text_log->PushIndent();
text_log->Print("edge.m_tolerance=%g (should be >= 0.0)\n",edge.m_tolerance);
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
return true;
}
bool
ON_Brep::IsValidVertex( int vertex_index, ON_TextLog* text_log ) const
{
if ( vertex_index < 0 || vertex_index >= m_V.Count() )
{
if ( text_log )
text_log->Print("brep vertex_index = %d (should be >=0 and <%d=brep.m_V.Count() ).\n",
vertex_index, m_V.Count());
return ON_BrepIsNotValid();
}
const ON_BrepVertex& vertex = m_V[vertex_index];
if ( vertex.m_vertex_index != vertex_index )
{
if ( text_log )
{
text_log->Print("brep.m_V[%d] vertex is not valid.\n",vertex_index);
text_log->PushIndent();
text_log->Print("vertex.m_vertex_index = %d (should be %d).\n",
vertex.m_vertex_index, vertex_index );
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
const int vertex_edge_count = vertex.m_ei.Count();
int i, j, vei, ei;
for ( vei = 0; vei < vertex_edge_count; vei++ ) {
ei = vertex.m_ei[vei];
if ( ei < 0 || ei >= m_E.Count() )
{
if ( text_log )
{
text_log->Print("brep.m_V[%d] vertex is not valid.\n",vertex_index);
text_log->PushIndent();
text_log->Print("vertex.m_ei[%d] = %d (should be >=0 and <%d).\n", vei, ei, m_E.Count());
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
const ON_BrepEdge& edge = m_E[ei];
if ( ei != edge.m_edge_index )
{
if ( text_log )
{
text_log->Print("brep.m_V[%d] vertex is not valid.\n",vertex_index);
text_log->PushIndent();
text_log->Print("vertex.m_ei[%d] = %d is a deleted edge.\n", vei, ei);
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
for ( i = 0; i < vei; i++ )
{
if ( vertex.m_ei[i] == ei )
{
// edge should be closed
if ( edge.m_vi[0] != vertex_index || edge.m_vi[1] != vertex_index )
{
if ( text_log )
{
text_log->Print("brep.m_V[%d] vertex is not valid.\n",vertex_index);
text_log->PushIndent();
text_log->Print("vertex.m_ei[%d] and vertex.m_ei[%d] = %d but brep.m_E[%d].m_vi[0] = %d",
i,vei,ei,ei,edge.m_vi[0]);
text_log->Print("and ON_Brep.m_E[%d].m_vi[1] = %d (both m_vi[] values should be %d).\n",
ei,edge.m_vi[1],vertex_index);
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
for (j = i+1; j < vei; j++ )
{
if ( vertex.m_ei[j] == ei )
{
if ( text_log )
{
text_log->Print("brep.m_V[%d] vertex is not valid.\n",vertex_index);
text_log->PushIndent();
text_log->Print("vertex.m_ei[%d,%d,%d] = %d. An open edge index should appear once\n",i,vei,j,ei);
text_log->Print("in vertex.m_ei[] and a closed edge index should appear twice.\n");
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
}
break;
}
}
if ( edge.m_vi[0] != vertex_index && edge.m_vi[1] != vertex_index )
{
if ( text_log )
{
text_log->Print("brep.m_V[%d] vertex is not valid.\n",vertex_index);
text_log->PushIndent();
text_log->Print("vertex.m_ei[%d] = %d but ON_Brep.m_E[%d].m_vi[] = [%d,%d]. "
"At least one edge m_vi[] value should be %d.\n",
vei,ei,ei,edge.m_vi[0],edge.m_vi[1],vertex_index);
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
}
if ( vertex.m_tolerance < 0.0 )
{
if ( text_log )
{
text_log->Print("brep.m_V[%d] vertex is not valid.\n",vertex_index);
text_log->PushIndent();
text_log->Print("vertex.m_tolerace = %g (should be >= 0.0)\n",vertex.m_tolerance);
text_log->PopIndent();
}
return ON_BrepIsNotValid();
}
return true;
}
static
bool TestTrimPBox( const ON_BrepTrim& trim, ON_TextLog* text_log )
{
ON_3dPoint pt;
double d;
ON_BoundingBox pbox = trim.m_pbox;
d = ON_SQRT_EPSILON*(fabs(pbox.m_min.x)+fabs(pbox.m_max.x));
if ( d < ON_ZERO_TOLERANCE )
d = ON_ZERO_TOLERANCE;
pbox.m_min.x -= d;
pbox.m_max.x += d;
d = ON_SQRT_EPSILON*(fabs(pbox.m_min.y)+fabs(pbox.m_max.y));
if ( d < ON_ZERO_TOLERANCE )
d = ON_ZERO_TOLERANCE;
pbox.m_min.y -= d;
pbox.m_max.y += d;
pt = trim.PointAtStart();
if ( !pbox.IsPointIn(pt) )
{
if ( text_log )
text_log->Print("ON_Brep.m_T[%d].m_pbox does not contain start of trim.\n",trim.m_trim_index);
return false;
}
pt = trim.PointAtEnd();
if ( !pbox.IsPointIn(pt) )
{
if ( text_log )
text_log->Print("ON_Brep.m_T[%d].m_pbox does not contain end of trim.\n",trim.m_trim_index);
return false;
}
pt = trim.PointAt(trim.Domain().ParameterAt(0.5));
if ( !pbox.IsPointIn(pt) )
{
if ( text_log )
text_log->Print("ON_Brep.m_T[%d].m_pbox does not contain middle of trim.\n",trim.m_trim_index);
return false;
}
return true;
}
static
bool CheckTrimOnSrfHelper( const ON_Interval& srf_domain0,
const ON_Interval& srf_domain1,
const ON_BrepTrim& trim,
ON_TextLog* text_log
)
{
// this check only works if the pline exists.
const ON_BrepTrimPoint* tp = trim.m_pline.Array();
int i, count = trim.m_pline.Count();
/*
June 08 2012 - Chuck - Changing a and b to -ON_ZERO_TOLERANCE and 1+ON_ZERO_TOLERANCE. Done so RR 106304 is considered a bad object. Split, Trim, etc expect uv curves to
be on the surface. If you get a Check error here and feel the need to change this,
discuss first with Chuck or Dale L and document with RR item numbers.
const double a = -0.01;
const double b = 1.01;
26 June 2012 Chuck and Dale Lear
Concerning bugs
http://dev.mcneel.com/bugtrack/?q=106304
http://dev.mcneel.com/bugtrack/?q=107842
We are moving the relative fuzz back to 1%
because making it smaller is generating
more tech support than it is worth at this
point.
We did learn that 2d trim curves leak off the
surface domain a bit fairly often and that
forcing the 2d pline to be in the surface domain
but leaving the 2d trim curve as is does not
fix the bugs cited above.
*/
const double a = -0.01;
const double b = 1.01;
double s,t;
for ( i = 0; i < count; i++ )
{
s = srf_domain0.NormalizedParameterAt(tp[i].p.x);
t = srf_domain1.NormalizedParameterAt(tp[i].p.y);
if ( s < a || s > b || t < a || t > b )
{
if ( text_log )
{
text_log->Print("ON_Brep.m_T[%d] 2d curve is not inside surface domain.\n",trim.m_trim_index);
}
return false;
}
}
return true;
}
static
bool CheckLoopOnSrfHelper( const ON_Brep& brep,
const ON_Interval& srf_domain0,
const ON_Interval& srf_domain1,
const ON_BrepLoop& loop,
ON_TextLog* text_log
)
{
for ( int lti = 0; lti < loop.m_ti.Count(); lti++ )
{
int ti = loop.m_ti[lti];
if ( ti < 0 || ti >= brep.m_T.Count() )
continue;
if ( ! CheckTrimOnSrfHelper( srf_domain0, srf_domain1, brep.m_T[ti], text_log ) )
return false;
}
return true;
}
static void Internal_ValidateBrepIndex(
ON__UINT_PTR text_log_ptr_plus,
const wchar_t* corruption_descirption,
bool& bCorrupt,
const int max_idx,
const int& idx
)
{
if (idx < max_idx)
return;
const bool bSilentError = (0 != (text_log_ptr_plus & 1));
const bool bRepair = (0 != (text_log_ptr_plus & 2));
ON_TextLog* text_log = (ON_TextLog*)(text_log_ptr_plus & (~((ON__UINT_PTR)3)) );
if (false == bCorrupt && false == bSilentError)
{
ON_ERROR("ON_Brep has corrupt indices that will cause crashes.");
}
bCorrupt = true;
if (nullptr != text_log)
text_log->PrintString(corruption_descirption);
if (bRepair)
const_cast<int&>(idx) = -1; // prevents crashes because code checks for -1 as an unset index value
}
static void Internal_ValidateBrepIndexArray(
ON__UINT_PTR text_log_ptr_plus,
const wchar_t* corruption_descirption,
bool& bCorrupt,
const int max_idx,
const int idex_count,
const int* idex_a
)
{
for (int j = 0; j < idex_count; j++)
{
Internal_ValidateBrepIndex(text_log_ptr_plus, corruption_descirption, bCorrupt, max_idx, idex_a[j]);
}
}
static void Internal_ValidateBrepIndexSimpleArray(
ON__UINT_PTR text_log_ptr_plus,
const wchar_t* corruption_descirption,
bool& bCorrupt,
const int max_idx,
const ON_SimpleArray<int>& idx_array
)
{
const int idex_count = idx_array.Count();
const int* idex_a = idx_array.Array();
Internal_ValidateBrepIndexArray(text_log_ptr_plus, corruption_descirption, bCorrupt, max_idx, idex_count, idex_a);
}
static void Internal_ValidateBrepComponentBackPtr(
ON__UINT_PTR text_log_ptr_plus,
const wchar_t* corruption_descirption,
bool& bCorrupt,
const ON_Brep* this_brep,
ON_Brep*const* brep_back_ptr,
const int idx,
const int& component_back_idx
)
{
if (this_brep != *brep_back_ptr)
{
const bool bSilentError = (0 != (text_log_ptr_plus & 1));
const bool bRepair = (0 != (text_log_ptr_plus & 2));
ON_TextLog* text_log = (ON_TextLog*)(text_log_ptr_plus & (~((ON__UINT_PTR)3)) );
if (false == bCorrupt && false == bSilentError)
{
ON_ERROR("ON_Brep has corrupt indices that will cause crashes.");
}
bCorrupt = true;
if (nullptr != text_log)
text_log->Print(corruption_descirption);
if (bRepair)
*const_cast<ON_Brep**>(brep_back_ptr) = const_cast<ON_Brep*>(this_brep);
}
if (idx != component_back_idx)
{
const bool bSilentError = (0 != (text_log_ptr_plus & 1));
const bool bRepair = (0 != (text_log_ptr_plus & 2));
ON_TextLog* text_log = (ON_TextLog*)(text_log_ptr_plus & (~((ON__UINT_PTR)3)));
if (false == bCorrupt && false == bSilentError)
{
ON_ERROR("ON_Brep has corrupt indices that will cause crashes.");
}
bCorrupt = true;
if (nullptr != text_log)
text_log->Print(corruption_descirption);
if (bRepair)
const_cast<int&>(component_back_idx) = idx;
}
}
bool ON_Brep::IsCorrupt(
bool bRepair,
bool bSilentError,
class ON_TextLog* text_log
) const
{
bool bCorrupt = false;
const int C2_count = m_C2.Count();
const int C3_count = m_C3.Count();
const int S_count = m_S.Count();
const int V_count = m_V.Count();
const int E_count = m_E.Count();
const int T_count = m_T.Count();
const int L_count = m_L.Count();
const int F_count = m_F.Count();
ON__UINT_PTR text_log_ptr = (ON__UINT_PTR)text_log;
if (bSilentError)
text_log_ptr |= 1;
if (bRepair)
text_log_ptr |= 2;
ON_Brep* ignored_this_ptr = const_cast<ON_Brep*>(this);
for (int vi = 0; vi < V_count; vi++)
{
const ON_BrepVertex& v = m_V[vi];
Internal_ValidateBrepComponentBackPtr(
text_log_ptr,
L"Corrupt ON_BrepVertex.m_vertex_index back pointer.\n",
bCorrupt,
this, &ignored_this_ptr,
vi, v.m_vertex_index
);
Internal_ValidateBrepIndexSimpleArray(
text_log_ptr,
L"Corrupt ON_BrepVertex.m_ei[] index.\n",
bCorrupt,
E_count,
v.m_ei
);
}
for (int ei = 0; ei < E_count; ei++)
{
const ON_BrepEdge& e = m_E[ei];
Internal_ValidateBrepComponentBackPtr(
text_log_ptr,
L"Corrupt ON_BrepEdge m_brep or m_edge_index back pointers.\n",
bCorrupt,
this, &e.m_brep,
ei, e.m_edge_index
);
Internal_ValidateBrepIndexArray(
text_log_ptr,
L"Corrupt ON_BrepEdge.m_vi[] index.\n",
bCorrupt,
V_count,
2,
e.m_vi
);
Internal_ValidateBrepIndex(
text_log_ptr,
L"Corrupt ON_BrepEdge.m_c3i index.\n",
bCorrupt,
C3_count,
e.m_c3i
);
Internal_ValidateBrepIndexSimpleArray(
text_log_ptr,
L"Corrupt ON_BrepEdge.m_ti[] index.\n",
bCorrupt,
T_count,
e.m_ti
);
}
for (int ti = 0; ti < T_count; ti++)
{
const ON_BrepTrim& t = m_T[ti];
Internal_ValidateBrepComponentBackPtr(
text_log_ptr,
L"Corrupt ON_BrepTrim m_brep or m_trim_index back pointers.\n",
bCorrupt,
this, &t.m_brep,
ti, t.m_trim_index
);
Internal_ValidateBrepIndex(
text_log_ptr,
L"Corrupt ON_BrepTrim.m_c2i index.\n",
bCorrupt,
C2_count,
t.m_c2i
);
Internal_ValidateBrepIndex(
text_log_ptr,
L"Corrupt ON_BrepTrim.m_ei index.\n",
bCorrupt,
E_count,
t.m_ei
);
Internal_ValidateBrepIndex(
text_log_ptr,
L"Corrupt ON_BrepTrim.m_li index.\n",
bCorrupt,
L_count,
t.m_li
);
Internal_ValidateBrepIndexArray(
text_log_ptr,
L"Corrupt ON_BrepTrim.m_vi[] index.\n",
bCorrupt,
V_count,
2,
t.m_vi
);
}
for (int li = 0; li < L_count; li++)
{
const ON_BrepLoop& l = m_L[li];
Internal_ValidateBrepComponentBackPtr(
text_log_ptr,
L"Corrupt ON_BrepLoop m_brep or m_loop_index back pointers.\n",
bCorrupt,
this, &l.m_brep,
li, l.m_loop_index
);
Internal_ValidateBrepIndexSimpleArray(
text_log_ptr,
L"Corrupt ON_BrepLoop.m_ti[] index.\n",
bCorrupt,
T_count,
l.m_ti
);
Internal_ValidateBrepIndex(
text_log_ptr,
L"Corrupt ON_BrepLoop.m_fi index.\n",
bCorrupt,
F_count,
l.m_fi
);
}
for (int fi = 0; fi < F_count; fi++)
{
const ON_BrepFace& f = m_F[fi];
Internal_ValidateBrepComponentBackPtr(
text_log_ptr,
L"Corrupt ON_BrepFace m_brep or m_face_index back pointers.\n",
bCorrupt,
this, &f.m_brep,
fi, f.m_face_index
);
Internal_ValidateBrepIndexSimpleArray(
text_log_ptr,
L"Corrupt ON_BrepFace.m_li[] index.\n",
bCorrupt,
L_count,
f.m_li
);
Internal_ValidateBrepIndex(
text_log_ptr,
L"Corrupt ON_BrepFace.m_si index.\n",
bCorrupt,
S_count,
f.m_si
);
}
return bCorrupt;
}
bool ON_Brep::IsValid( ON_TextLog* text_log ) const
{
if (IsCorrupt(false, true, text_log))
return false;
const int curve2d_count = m_C2.Count();
const int curve3d_count = m_C3.Count();
const int surface_count = m_S.Count();
const int vertex_count = m_V.Count();
const int edge_count = m_E.Count();
const int trim_count = m_T.Count();
const int loop_count = m_L.Count();
const int face_count = m_F.Count();
int c2i, c3i, si, vi, ei, fi, ti, li;
if ( 0 == face_count && 0 == edge_count && 0 == vertex_count )
{
if ( text_log )
text_log->Print( "ON_Brep has no faces, edges, or vertices\n");
return ON_BrepIsNotValid();
}
if ( 0 != face_count )
{
if ( 0 == edge_count )
{
if ( text_log )
text_log->Print( "ON_Brep has no edges.\n");
return ON_BrepIsNotValid();
}
if ( 0 == loop_count )
{
if ( text_log )
text_log->Print( "ON_Brep has no loops.\n");
return ON_BrepIsNotValid();
}
if ( 0 == surface_count )
{
if ( text_log )
text_log->Print( "ON_Brep has no surfaces.\n");
return ON_BrepIsNotValid();
}
if ( 0 == trim_count )
{
if ( text_log )
text_log->Print( "ON_Brep has no trims.\n");
return ON_BrepIsNotValid();
}
if ( 0 == curve2d_count )
{
if ( text_log )
text_log->Print( "ON_Brep has no 2d curves.\n");
return ON_BrepIsNotValid();
}
}
if ( 0 != edge_count )
{
if ( 0 == curve3d_count )
{
if ( text_log )
text_log->Print( "ON_Brep has no 3d curves.\n");
return ON_BrepIsNotValid();
}
if ( 0 == vertex_count )
{
if ( text_log )
text_log->Print( "ON_Brep has no vertices.\n");
return ON_BrepIsNotValid();
}
}
// check element indices match array positions
for ( vi = 0; vi < vertex_count; vi++ )
{
if ( m_V[vi].m_vertex_index == -1 )
{
const ON_BrepVertex& vertex = m_V[vi];
if ( vertex.m_ei.Count() > 0 )
{
if ( text_log )
text_log->Print( "ON_Brep.m_V[%d] is deleted (m_vertex_index = -1) but vertex.m_ei.Count() = %d.\n",
vi, vertex.m_ei.Count() );
return ON_BrepIsNotValid();
}
}
else if ( m_V[vi].m_vertex_index != vi )
{
if ( text_log )
text_log->Print( "ON_Brep.m_V[%d].m_vertex_index = %d (should be %d)\n",
vi, m_V[vi].m_vertex_index, vi );
return ON_BrepIsNotValid();
}
}
for ( ei = 0; ei < edge_count; ei++ )
{
if ( m_E[ei].m_edge_index == -1 )
{
const ON_BrepEdge& edge = m_E[ei];
if ( edge.m_ti.Count() > 0 )
{
if ( text_log )
text_log->Print( "ON_Brep.m_E[%d] is deleted (m_edge_index = -1) but edge.m_ti.Count() = %d.\n",
ei, edge.m_ti.Count() );
return ON_BrepIsNotValid();
}
if ( edge.m_c3i != -1 )
{
if ( text_log )
text_log->Print( "ON_Brep.m_E[%d] is deleted (m_edge_index = -1) but edge.m_c3i=%d (should be -1).\n",
ei, edge.m_c3i );
return ON_BrepIsNotValid();
}
if ( edge.ProxyCurve() )
{
if ( text_log )
text_log->Print( "ON_Brep.m_E[%d] is deleted (m_edge_index = -1) but edge.m_curve is not nullptr.\n",
ei );
return ON_BrepIsNotValid();
}
if ( edge.m_vi[0] != -1 )
{
if ( text_log )
text_log->Print( "ON_Brep.m_E[%d] is deleted (m_edge_index = -1) but edge.m_vi[0]=%d (should be -1).\n",
ei, edge.m_vi[0] );
return ON_BrepIsNotValid();
}
if ( edge.m_vi[1] != -1 )
{
if ( text_log )
text_log->Print( "ON_Brep.m_E[%d] is deleted (m_edge_index = -1) but edge.m_vi[1]=%d (should be -1).\n",
ei, edge.m_vi[1] );
return ON_BrepIsNotValid();
}
}
else if ( m_E[ei].m_edge_index != ei )
{
if ( text_log )
text_log->Print( "ON_Brep.m_E[%d].m_edge_index = %d (should be %d)\n",
ei, m_E[ei].m_edge_index, ei );
return ON_BrepIsNotValid();
}
}
for ( ti = 0; ti < trim_count; ti++ )
{
if ( m_T[ti].m_trim_index == -1 )
{
const ON_BrepTrim& trim = m_T[ti];
if ( trim.m_ei != -1 )
{
if ( text_log )
text_log->Print( "ON_Brep.m_T[%d] is deleted (m_trim_index = -1) but trim.m_ei=%d (should be -1).\n",
ti, trim.m_ei );
return ON_BrepIsNotValid();
}
if ( trim.m_li != -1 )
{
if ( text_log )
text_log->Print( "ON_Brep.m_T[%d] is deleted (m_trim_index = -1) but trim.m_li=%d (should be -1).\n",
ti, trim.m_li );
return ON_BrepIsNotValid();
}
if ( trim.m_c2i != -1 )
{
if ( text_log )
text_log->Print( "ON_Brep.m_T[%d] is deleted (m_trim_index = -1) but trim.m_c2i=%d (should be -1).\n",
ti, trim.m_c2i );
return ON_BrepIsNotValid();
}
if ( trim.m_vi[0] != -1 )
{
if ( text_log )
text_log->Print( "ON_Brep.m_T[%d] is deleted (m_trim_index = -1) but trim.m_vi[0]=%d (should be -1).\n",
ti, trim.m_vi[0] );
return ON_BrepIsNotValid();
}
if ( trim.m_vi[1] != -1 )
{
if ( text_log )
text_log->Print( "ON_Brep.m_T[%d] is deleted (m_trim_index = -1) but trim.m_vi[1]=%d (should be -1).\n",
ti, trim.m_vi[1] );
return ON_BrepIsNotValid();
}
}
else if ( m_T[ti].m_trim_index != ti )
{
if ( text_log )
text_log->Print( "ON_Brep.m_T[%d].m_trim_index = %d (should be %d)\n",
ti, m_T[ti].m_trim_index, ti );
return ON_BrepIsNotValid();
}
else if ( !m_T[ti].IsValid( text_log ) )
{
if ( text_log )
text_log->Print( "ON_Brep.m_T[%d] is not valid\n",ti );
return ON_BrepIsNotValid();
}
}
for ( li = 0; li < loop_count; li++ )
{
if ( m_L[li].m_loop_index == -1 )
{
const ON_BrepLoop& loop = m_L[li];
if ( loop.m_fi != -1 )
{
if ( text_log )
text_log->Print( "ON_Brep.m_L[%d] is deleted (m_loop_index = -1) but loop.m_fi=%d (should be -1).\n",
li, loop.m_fi );
return ON_BrepIsNotValid();
}
if ( loop.m_ti.Count() > 0 )
{
if ( text_log )
text_log->Print( "ON_Brep.m_L[%d] is deleted (m_loop_index = -1) but loop.m_ti.Count()=%d.\n",
li, loop.m_ti.Count() );
return ON_BrepIsNotValid();
}
}
else if ( m_L[li].m_loop_index != li )
{
if ( text_log )
text_log->Print( "ON_Brep.m_L[%d].m_loop_index = %d (should be %d)\n",
li, m_L[li].m_loop_index, li );
return ON_BrepIsNotValid();
}
}
for ( fi = 0; fi < face_count; fi++ )
{
if ( m_F[fi].m_face_index == -1 )
{
const ON_BrepFace& face = m_F[fi];
if ( face.m_si != -1 )
{
if ( text_log )
text_log->Print( "ON_Brep.m_F[%d] is deleted (m_face_index = -1) but face.m_si=%d (should be -1).\n",
fi, face.m_si );
return ON_BrepIsNotValid();
}
if ( face.ProxySurface() )
{
if ( text_log )
text_log->Print( "ON_Brep.m_F[%d] is deleted (m_face_index = -1) but face.ProxySurface() is not nullptr.\n",
fi );
return ON_BrepIsNotValid();
}
if ( face.m_li.Count() > 0 )
{
if ( text_log )
text_log->Print( "ON_Brep.m_F[%d] is deleted (m_face_index = -1) but face.m_li.Count()=%d.\n",
fi, face.m_li.Count() );
return ON_BrepIsNotValid();
}
}
else if ( m_F[fi].m_face_index != fi )
{
if ( text_log )
text_log->Print( "ON_Brep.m_F[%d].m_face_index = %d (should be %d)\n",
fi, m_F[fi].m_face_index, fi );
return ON_BrepIsNotValid();
}
}
// check 2d curve geometry
for ( c2i = 0; c2i < curve2d_count; c2i++ ) {
if ( !m_C2[c2i] )
{
continue;
// nullptr 2d curves are ok if they are not referenced
//if ( text_log )
// text_log->Print("ON_Brep.m_C2[%d] is nullptr.\n",c2i);
//return ON_BrepIsNotValid();
}
if ( !m_C2[c2i]->IsValid(text_log) )
{
if ( text_log )
text_log->Print("ON_Brep.m_C2[%d] is invalid.\n",c2i);
return ON_BrepIsNotValid();
}
int c2_dim = m_C2[c2i]->Dimension();
if ( c2_dim != 2 )
{
if ( text_log )
text_log->Print("ON_Brep.m_C2[%d]->Dimension() = %d (should be 2).\n", c2i, c2_dim );
return ON_BrepIsNotValid();
}
const ON_PolyCurve* polycurve = ON_PolyCurve::Cast(m_C2[c2i]);
if ( polycurve && polycurve->IsNested() )
{
if ( text_log )
text_log->Print("ON_Brep.m_C2[%d] is a nested polycurve.\n", c2i );
return ON_BrepIsNotValid();
}
}
// check 3d curve geometry
for ( c3i = 0; c3i < curve3d_count; c3i++ ) {
if ( !m_C3[c3i] )
{
continue;
// nullptr 3d curves are ok if they are not referenced
//if ( text_log )
// text_log->Print("ON_Brep.m_C3[%d] is nullptr.\n",c3i);
//return ON_BrepIsNotValid();
}
if ( !m_C3[c3i]->IsValid(text_log) )
{
if ( text_log )
text_log->Print("ON_Brep.m_C3[%d] is invalid.\n",c3i);
return ON_BrepIsNotValid();
}
int c3_dim = m_C3[c3i]->Dimension();
if ( c3_dim != 3 )
{
if ( text_log )
text_log->Print("ON_Brep.m_C3[%d]->Dimension() = %d (should be 3).\n", c3i, c3_dim );
return ON_BrepIsNotValid();
}
const ON_PolyCurve* polycurve = ON_PolyCurve::Cast(m_C3[c3i]);
if ( polycurve && polycurve->IsNested() )
{
if ( text_log )
text_log->Print("ON_Brep.m_C3[%d] is a nested polycurve.\n", c3i );
return ON_BrepIsNotValid();
}
}
// check 3d surface geometry
for ( si = 0; si < surface_count; si++ ) {
if ( !m_S[si] )
{
continue;
// nullptr 3d surfaces are ok if they are not referenced
//if ( text_log )
// text_log->Print("ON_Brep.m_S[%d] is nullptr.\n",si);
//return ON_BrepIsNotValid();
}
if ( !m_S[si]->IsValid(text_log) )
{
if ( text_log )
text_log->Print("ON_Brep.m_S[%d] is invalid.\n",si);
return ON_BrepIsNotValid();
}
int dim = m_S[si]->Dimension();
if ( dim != 3 )
{
if ( text_log )
text_log->Print("ON_Brep.m_S[%d]->Dimension() = %d (should be 3).\n", si, dim );
return ON_BrepIsNotValid();
}
}
// check vertices
for ( vi = 0; vi < vertex_count; vi++ ) {
if ( m_V[vi].m_vertex_index == -1 )
continue;
if ( !IsValidVertex( vi, text_log ) ) {
if ( text_log )
text_log->Print("ON_Brep.m_V[%d] is invalid.\n",vi);
return ON_BrepIsNotValid();
}
}
// check edges
for ( ei = 0; ei < edge_count; ei++ )
{
if ( m_E[ei].m_edge_index == -1 )
continue;
if ( !IsValidEdge( ei, text_log ) ) {
if ( text_log )
text_log->Print("ON_Brep.m_E[%d] is invalid.\n",ei);
return ON_BrepIsNotValid();
}
}
// check faces
for ( fi = 0; fi < face_count; fi++ )
{
if ( m_F[fi].m_face_index == -1 )
continue;
if ( !IsValidFace( fi, text_log ) ) {
if ( text_log )
text_log->Print("ON_Brep.m_F[%d] is invalid.\n",fi);
return ON_BrepIsNotValid();
}
}
// Check loops - this check is necessary at the brep level
// to make sure there are no orphaned loops.
// ON_Brep::IsValidLoop(), which is called by ON_Brep::IsValidFace(),
// performs loop-trim bookkeeping checks on all loops that are referenced
// by a face.
for ( li = 0; li < loop_count; li++ )
{
const ON_BrepLoop& loop = m_L[li];
if ( m_L[li].m_loop_index == -1 )
continue;
if ( loop.m_fi < 0 || loop.m_fi >= m_F.Count() )
{
if ( text_log )
text_log->Print("ON_Brep.m_L[%d].m_fi = %d is not invalid.\n",li,loop.m_fi);
return ON_BrepIsNotValid();
}
if ( m_F[loop.m_fi].m_face_index != loop.m_fi )
{
if ( text_log )
text_log->Print("ON_Brep.m_L[%d].m_fi = %d is a deleted face.\n",li,loop.m_fi);
return ON_BrepIsNotValid();
}
// This for() loop check is performed in IsValidLoop() which is
// called by IsValidFace() in the "check faces" loop above.
// I think it can be removed. If anybody every sees this code
// find a flaw, please tell Dale Lear.
for ( int lti = 0; lti < loop.m_ti.Count(); lti++ )
{
ti = loop.m_ti[lti];
if ( ti < 0 || ti >= m_T.Count() )
{
if ( text_log )
text_log->Print("ON_Brep.m_L[%d].m_ti[%d] = %d is not invalid.\n",li,lti,ti);
return ON_BrepIsNotValid();
}
if ( m_T[ti].m_trim_index != ti )
{
if ( text_log )
text_log->Print("ON_Brep.m_L[%d].m_ti[%d] = %d is a deleted trim.\n",li,lti,ti);
return ON_BrepIsNotValid();
}
}
}
// check trims - this check is necessary at the brep
// level to make sure there are no orphan trims and
// to test tolerances. Most of these tests are duplicates
// of ones in ON_Brep::IsValidTrim, which is called by
// ON_Brep::IsValidLoop, which is called by ON_Brep::IsValidFace.
int seam_trim_count = 0;
for ( ti = 0; ti < trim_count; ti++ )
{
const ON_BrepTrim& trim = m_T[ti];
if ( trim.m_trim_index == -1 )
continue;
if ( trim.m_vi[0] < 0 || trim.m_vi[0] >= m_V.Count() )
{
if ( text_log )
text_log->Print("ON_Brep.m_T[%d].m_vi[0] = %d is not invalid.\n",ti,trim.m_vi[0]);
return ON_BrepIsNotValid();
}
if ( trim.m_vi[1] < 0 || trim.m_vi[1] >= m_V.Count() )
{
if ( text_log )
text_log->Print("ON_Brep.m_T[%d].m_vi[1] = %d is not invalid.\n",ti,trim.m_vi[1]);
return ON_BrepIsNotValid();
}
if ( m_V[trim.m_vi[0]].m_vertex_index != trim.m_vi[0] )
{
if ( text_log )
text_log->Print("ON_Brep.m_T[%d].m_vi[0] is deleted.\n",ti);
return ON_BrepIsNotValid();
}
if ( m_V[trim.m_vi[1]].m_vertex_index != trim.m_vi[1] )
{
if ( text_log )
text_log->Print("ON_Brep.m_T[%d].m_vi[1] is deleted.\n",ti);
return ON_BrepIsNotValid();
}
if ( trim.m_c2i < 0 || trim.m_c2i >= m_C2.Count() )
{
if ( text_log )
text_log->Print("ON_Brep.m_T[%d].m_c2i = %d is not valid.\n",ti,trim.m_c2i);
return ON_BrepIsNotValid();
}
if ( 0 == m_C2[trim.m_c2i] )
{
if ( text_log )
text_log->Print("ON_Brep.m_T[%d].m_c2i = %d, but m_C2[%d] is nullptr.\n",ti,trim.m_c2i,trim.m_c2i);
return ON_BrepIsNotValid();
}
if ( trim.m_li < 0 || trim.m_li >= m_L.Count() )
{
if ( text_log )
text_log->Print("ON_Brep.m_T[%d].m_li = %d is not valid.\n",ti,trim.m_li);
return ON_BrepIsNotValid();
}
if ( m_L[trim.m_li].m_loop_index != trim.m_li )
{
if ( text_log )
text_log->Print("ON_Brep.m_T[%d].m_li = %d is a deleted loop.\n",ti,trim.m_li);
return ON_BrepIsNotValid();
}
{
const ON_Curve* c2 = m_C2[trim.m_c2i];
const ON_Surface* srf = m_S[m_F[m_L[trim.m_li].m_fi].m_si];
if ( srf )
{
ON_Interval PD = trim.ProxyCurveDomain();
ON_Surface::ISO iso = srf->IsIsoparametric(*c2, &PD);
if ( trim.m_iso != iso )
{
if ( text_log )
text_log->Print("ON_Brep.m_T[%d].m_iso = %d and it should be %d\n",ti,trim.m_iso,iso);
return ON_BrepIsNotValid();
}
}
}
if ( trim.m_type == ON_BrepTrim::singular )
{
if ( trim.m_ei != -1 )
{
if ( text_log )
text_log->Print("ON_Brep.m_T[%d].m_type = singular, but m_ei = %d (should be -1).\n",ti,trim.m_ei);
return ON_BrepIsNotValid();
}
continue;
}
if ( trim.m_ei < 0 || trim.m_ei >= m_E.Count() )
{
if ( text_log )
text_log->Print("ON_Brep.m_T[%d].m_ei = %d is not invalid.\n",ti,trim.m_ei);
return ON_BrepIsNotValid();
}
const ON_BrepEdge& edge = m_E[trim.m_ei];
if ( edge.m_edge_index != trim.m_ei )
{
if ( text_log )
text_log->Print("ON_Brep.m_T[%d].m_ei is deleted.\n",ti);
return ON_BrepIsNotValid();
}
const int evi0 = trim.m_bRev3d ? 1 : 0;
const int evi1 = trim.m_bRev3d ? 0 : 1;
if ( trim.m_vi[0] != edge.m_vi[evi0] )
{
if ( text_log )
text_log->Print("ON_Brep.m_T[%d].m_bRev3d = %d, but m_vi[0] != m_E[m_ei].m_vi[%d].\n",ti,trim.m_bRev3d,evi0);
return ON_BrepIsNotValid();
}
if ( trim.m_vi[1] != edge.m_vi[evi1] )
{
if ( text_log )
text_log->Print("ON_Brep.m_T[%d].m_bRev3d = %d, but m_vi[0] != m_E[m_ei].m_vi[%d].\n",ti,trim.m_bRev3d,evi1);
return ON_BrepIsNotValid();
}
// check tolerances and closed curve directions
{
ON_3dPoint trim_pt0, trim_pt1, srf_pt0, srf_pt1;
ON_3dVector trim_der0, trim_der1, srf_du0, srf_dv0, srf_du1, srf_dv1;
ON_Interval trim_domain = trim.Domain();
// trim_pt0 should be closed to trim_pt1 except when
// trim starts and ends on opposite sides of a surface
// seam. Even when the trim curve is closed, the
// derivatives can be different when there is
// a kink at the start/end of a trim.
trim.Ev1Der( trim_domain[0], trim_pt0, trim_der0 );
trim.Ev1Der( trim_domain[1], trim_pt1, trim_der1 );
const ON_Surface* trim_srf = m_F[ m_L[trim.m_li].m_fi ].SurfaceOf();
trim_srf->Ev1Der( trim_pt0.x, trim_pt0.y, srf_pt0, srf_du0, srf_dv0 );
trim_srf->Ev1Der( trim_pt1.x, trim_pt1.y, srf_pt1, srf_du1, srf_dv1 );
// estimate 3d tolerances from 2d trim tolerances
double t0_tol = srf_du0.Length()*trim.m_tolerance[0] + srf_dv0.Length()*trim.m_tolerance[1];
double t1_tol = srf_du1.Length()*trim.m_tolerance[0] + srf_dv1.Length()*trim.m_tolerance[1];
ON_3dVector trim_tangent0 = trim_der0.x*srf_du0 + trim_der0.y*srf_dv0;
trim_tangent0.Unitize();
ON_3dVector trim_tangent1 = trim_der1.x*srf_du1 + trim_der1.y*srf_dv1;
trim_tangent1.Unitize();
ON_3dVector edge_tangent0 = edge.TangentAt( edge.Domain()[trim.m_bRev3d ? 1 : 0] );
ON_3dVector edge_tangent1 = edge.TangentAt( edge.Domain()[trim.m_bRev3d ? 0 : 1] );
double d0 = trim_tangent0*edge_tangent0;
double d1 = trim_tangent1*edge_tangent1;
if ( trim.m_bRev3d )
{
d0 = -d0;
d1 = -d1;
}
if ( trim.m_vi[0] == trim.m_vi[1]
&& edge.m_vi[0] == edge.m_vi[1]
&& trim.m_vi[0] == edge.m_vi[0]
)
{
// For high quality models, d0 and d1 should be close to +1.
// If both are close to -1, the trim.m_bRev3d flag is most
// likely set opposite of what it should be.
// check start tangent to see if m_bRev3d is set correctly
if ( d0 < 0.0 || d1 < 0.0)
{
if ( text_log )
{
if ( trim.m_bRev3d )
text_log->Print("ON_Brep.m_T[%d].m_bRev3d = true, but closed curve directions are the same.\n",ti);
else
text_log->Print("ON_Brep.m_T[%d].m_bRev3d = false, but closed curve directions are opposite.\n",ti);
}
return ON_BrepIsNotValid();
}
}
// Make sure edge and tolerances are realistic
ON_3dPoint EdgeEnd[2];
EdgeEnd[trim.m_bRev3d?1:0] = edge.PointAtStart();
EdgeEnd[trim.m_bRev3d?0:1] = edge.PointAtEnd();
d0 = EdgeEnd[0].DistanceTo(srf_pt0);
d1 = EdgeEnd[1].DistanceTo(srf_pt1);
double etol = edge.m_tolerance;
double dtol = 10.0*(etol + t0_tol + t1_tol);
if ( dtol < 0.01 )
dtol = 0.01;
if ( d0 > dtol )
{
if ( text_log )
{
text_log->Print("Distance from start of ON_Brep.m_T[%d] to 3d edge is %g. (edge tol = %g, trim tol ~ %g).\n",
ti, d0, etol,t0_tol);
}
return ON_BrepIsNotValid();
}
if ( d1 > dtol )
{
if ( text_log )
{
text_log->Print("Distance from end of ON_Brep.m_T[%d] to 3d edge is %g. (edge tol = %g, trim tol ~ %g).\n",
ti, d1, etol,t1_tol);
}
return ON_BrepIsNotValid();
}
}
// check trim's m_pbox
{
if ( trim.m_pbox.m_min.z != 0.0 )
{
if ( text_log )
text_log->Print("ON_Brep.m_T[%d].m_pbox.m_min.z = %g (should be zero).\n",ti,trim.m_pbox.m_min.z);
return ON_BrepIsNotValid();
}
if ( trim.m_pbox.m_max.z != 0.0 )
{
if ( text_log )
text_log->Print("ON_Brep.m_T[%d].m_pbox.m_max.z = %g (should be zero).\n",ti,trim.m_pbox.m_max.z);
return ON_BrepIsNotValid();
}
if ( !TestTrimPBox( trim, text_log ) )
return ON_BrepIsNotValid();
}
if ( ON_BrepTrim::seam == trim.m_type )
{
// trim must be on a surface edge
switch ( trim.m_iso )
{
case ON_Surface::S_iso:
break;
case ON_Surface::E_iso:
break;
case ON_Surface::N_iso:
break;
case ON_Surface::W_iso:
break;
default:
if ( text_log )
text_log->Print("ON_Brep.m_T[%d].m_type = ON_BrepTrim::seam but m_iso is not N/E/W/S_iso.\n",ti);
return ON_BrepIsNotValid();
}
seam_trim_count++;
}
}
// check loop m_pboxes
for ( li = 0; li < loop_count; li++ )
{
const ON_BrepLoop& loop = m_L[li];
if ( loop.m_loop_index != li )
continue;
if ( loop.m_pbox.m_min.z != 0.0 )
{
if ( text_log )
text_log->Print("ON_Brep.m_L[%d].m_pbox.m_min.z = %g (should be zero).\n",li,loop.m_pbox.m_min.z);
return ON_BrepIsNotValid();
}
if ( loop.m_pbox.m_max.z != 0.0 )
{
if ( text_log )
text_log->Print("ON_Brep.m_L[%d].m_pbox.m_max.z = %g (should be zero).\n",li,loop.m_pbox.m_max.z);
return ON_BrepIsNotValid();
}
int first_trim_ti = -4;
int first_trim_vi0 = -3;
int prev_trim_vi1 = -2;
int prev_trim_ti=-9;
int lti;
for ( lti = 0; lti < loop.m_ti.Count(); lti++ )
{
const ON_BrepTrim& trim = m_T[loop.m_ti[lti]];
if ( !loop.m_pbox.IsPointIn(trim.m_pbox.m_min) || !loop.m_pbox.IsPointIn(trim.m_pbox.m_max) )
{
if ( text_log )
text_log->Print("ON_Brep.m_L[%d].m_pbox does not contain m_T[loop.m_ti[%d]].m_pbox.\n",li,lti);
return ON_BrepIsNotValid();
}
if ( 0 == lti )
{
first_trim_ti = loop.m_ti[lti];
first_trim_vi0 = trim.m_vi[0];
}
else if ( prev_trim_vi1 != trim.m_vi[0] )
{
// 23 May 2003 Dale Lear
// Added this test to make sure adjacent trims
// in a loop shared vertices.
if ( text_log )
text_log->Print("ON_Brep.m_L[%d] loop has trim vertex mismatch:\n m_T[loop.m_ti[%d]=%d].m_vi[1] = %d != m_T[loop.m_ti[%d]=%d].m_vi[0]=%d.\n",li,lti-1,prev_trim_ti,prev_trim_vi1,lti,loop.m_ti[lti],trim.m_vi[0]);
return ON_BrepIsNotValid();
}
prev_trim_ti = loop.m_ti[lti];
prev_trim_vi1 = trim.m_vi[1];
}
if ( first_trim_ti >= 0 && first_trim_vi0 != prev_trim_vi1 )
{
// 23 May 2003 Dale Lear
// Added this test to make sure adjacent trims
// in a loop shared vertices.
if ( text_log )
text_log->Print("ON_Brep.m_L[%d] loop has trim vertex mismatch:\n m_T[loop.m_ti[%d]=%d].m_vi[1] = %d != m_T[loop.m_ti[%d]=%d].m_vi[0]=%d.\n",
li,lti-1,prev_trim_ti,prev_trim_vi1,0,first_trim_ti,first_trim_vi0);
return ON_BrepIsNotValid();
}
}
// 21 October 2003 Dale Lear - fix RR 11980 - check for split seams
// This block of code assumes the preceding checks have all passed.
// It looks for boundary trims on seams that should be joined as a seam trim.
ON_Interval srf_domain[2];
for ( fi = 0; fi < face_count; fi++ )
{
const ON_BrepFace& face = m_F[fi];
if ( face.m_face_index < 0 )
continue;
const ON_Surface* srf = m_S[face.m_si];
if ( 0 == srf )
continue;
srf_domain[0] = srf->Domain(0);
srf_domain[1] = srf->Domain(1);
for ( int fli = 0; fli < face.m_li.Count(); fli++ )
{
int li_local = face.m_li[fli];
if ( li_local < 0 || li_local >= m_L.Count() )
continue;
if ( !CheckLoopOnSrfHelper(*this,srf_domain[0],srf_domain[1],m_L[li_local],text_log) )
return ON_BrepIsNotValid();
}
const ON_BrepLoop* outer_loop = face.OuterLoop();
if ( 0 == outer_loop )
continue;
bool bClosed[2];
bClosed[0] = srf->IsClosed(0);
bClosed[1] = srf->IsClosed(1);
if ( !bClosed[0] && !bClosed[1] )
continue;
const int outer_trim_count = outer_loop->m_ti.Count();
int lti, lti1;
int endpt_index = 0;
ON_Surface::ISO iso_type;
ON_Interval side_interval;
double s0, s1;
const double side_tol = 1.0e-4;
for ( lti = 0; lti < outer_trim_count; lti++ )
{
const ON_BrepTrim& trim = m_T[outer_loop->m_ti[lti]];
if ( ON_BrepTrim::boundary != trim.m_type )
continue;
if ( ON_Surface::E_iso == trim.m_iso && bClosed[0] )
{
iso_type = ON_Surface::W_iso;
endpt_index = 1;
}
else if ( ON_Surface::W_iso == trim.m_iso && bClosed[0] )
{
iso_type = ON_Surface::E_iso;
endpt_index = 1;
}
else if( ON_Surface::S_iso == trim.m_iso && bClosed[1] )
{
iso_type = ON_Surface::N_iso;
endpt_index = 0;
}
else if( ON_Surface::N_iso == trim.m_iso && bClosed[1] )
{
iso_type = ON_Surface::S_iso;
endpt_index = 0;
}
else
continue;
side_interval.Set(trim.PointAtStart()[endpt_index],trim.PointAtEnd()[endpt_index]);
if ( ON_Surface::N_iso == iso_type || ON_Surface::W_iso == iso_type )
{
if ( !side_interval.IsIncreasing() )
continue;
}
else
{
if ( !side_interval.IsDecreasing() )
continue;
}
// search for seam
for ( lti1 = 0; lti1 < outer_trim_count; lti1++ )
{
if ( lti1 == lti )
continue;
const ON_BrepTrim& trim1 = m_T[outer_loop->m_ti[lti1]];
if ( iso_type != trim1.m_iso )
continue;
if ( ON_BrepTrim::boundary != trim1.m_type )
continue;
s1 = side_interval.NormalizedParameterAt(trim1.PointAtStart()[endpt_index]);
if ( fabs(s1-1.0) > side_tol )
continue;
s0 = side_interval.NormalizedParameterAt(trim1.PointAtEnd()[endpt_index]);
if ( fabs(s0) > side_tol )
continue;
if ( text_log )
{
text_log->Print("ON_Brep.m_F[%d] is on a closed surface. Outer loop m_L[%d] contains boundary trims %d and %d. They should be seam trims connected to the same edge.\n",
face.m_face_index,outer_loop->m_loop_index,
trim.m_trim_index,trim1.m_trim_index
);
}
return ON_BrepIsNotValid();
}
}
}
// make sure seam trims are properly matched.
for ( ti = 0; seam_trim_count > 0 && ti < trim_count; ti++ )
{
const ON_BrepTrim& trim = m_T[ti];
if ( trim.m_trim_index == -1 )
continue;
if ( ON_BrepTrim::seam != trim.m_type )
continue;
seam_trim_count--;
if ( trim.m_ei < 0 || trim.m_ei >= edge_count )
{
if ( text_log )
{
text_log->Print("ON_Brep.m_T[%d] is a seam trim with an invalid m_ei.\n",ti);
return ON_BrepIsNotValid();
}
}
const ON_BrepEdge& edge = m_E[trim.m_ei];
int trim1_index = -1;
for ( int eti = 0; eti < edge.m_ti.Count(); eti++ )
{
const int ti1 = edge.m_ti[eti];
if ( ti1 == ti
|| ti < 0
|| ti >= trim_count
)
{
continue;
}
const ON_BrepTrim& trim1 = m_T[ti1];
if ( trim1.m_trim_index == -1 )
continue;
if ( ON_BrepTrim::seam != trim1.m_type )
continue;
if ( trim1.m_li != trim.m_li )
continue;
if ( -1 == trim1_index )
{
trim1_index = ti1;
continue;
}
text_log->Print("ON_Brep.m_T[%d,%d,%d] are three seam trims with the same edge in the same loop.\n",ti,trim1_index,ti1);
return ON_BrepIsNotValid();
}
if ( trim1_index < 0 || trim1_index >= trim_count )
{
text_log->Print("ON_Brep.m_T[%d] is a seam trim with no matching seam trim in the same loop.\n",ti);
return ON_BrepIsNotValid();
}
// previous validation step insures trim.m_iso = N/S/E/W_iso
switch(trim.m_iso)
{
case ON_Surface::S_iso:
if ( ON_Surface::N_iso != m_T[trim1_index].m_iso )
{
if (text_log )
text_log->Print("Seam trim ON_Brep.m_T[%d].m_iso = S_iso but matching seam ON_Brep.m_T[%d].m_iso != N_iso.\n",ti,trim1_index);
return ON_BrepIsNotValid();
}
break;
case ON_Surface::E_iso:
if ( ON_Surface::W_iso != m_T[trim1_index].m_iso )
{
if (text_log )
text_log->Print("Seam trim ON_Brep.m_T[%d].m_iso = E_iso but matching seam ON_Brep.m_T[%d].m_iso != W_iso.\n",ti,trim1_index);
return ON_BrepIsNotValid();
}
break;
case ON_Surface::N_iso:
if ( ON_Surface::S_iso != m_T[trim1_index].m_iso )
{
if (text_log )
text_log->Print("Seam trim ON_Brep.m_T[%d].m_iso = N_iso but matching seam ON_Brep.m_T[%d].m_iso != S_iso.\n",ti,trim1_index);
return ON_BrepIsNotValid();
}
break;
case ON_Surface::W_iso:
if ( ON_Surface::E_iso != m_T[trim1_index].m_iso )
{
if (text_log )
text_log->Print("Seam trim ON_Brep.m_T[%d].m_iso = W_iso but matching seam ON_Brep.m_T[%d].m_iso != E_iso.\n",ti,trim1_index);
return ON_BrepIsNotValid();
}
break;
case ON_Surface::not_iso:
case ON_Surface::x_iso:
case ON_Surface::y_iso:
case ON_Surface::iso_count:
break; // keep gcc quiet
}
}
// Dale Lear Fix https://mcneel.myjetbrains.com/youtrack/issue/RH-64277
// The commented out tests might have helped in debugging make2d, but they are making a mess of things now.
// In my view, destroying/remaking a box is beyond the scope of an appropriate IsValid() test.
// In addintion to replacing the test below with a warning Greg can use while debugging
// make2d, I've fixed the IO code in ON_Brep::Read() so old boxes are now reliably updated
// to their smaller versions.
//////// GBA 28-Aug-20 RH-60112 and RH-58462
//////// Adding bounding box (m_bbox) validation tests.
//////// Brep bounding box is cached and persists across sessions (since at least Rhino 6).
//////// In Rhino6 and earlier, the bounding box included entire underlying surface.
//////// Rhino7 bounding box calculation now uses "shrinked" surfaces.
//////// A bounding box is now reported as invalid if it could be significantly
//////// reduced by being recalculated.
//////if (!m_bbox.IsEmpty())
//////{
////// if (!m_bbox.IsValid())
////// {
////// if (text_log)
////// text_log->Print("Bounding Box is not valid.\n");
////// return ON_BrepIsNotValid();
////// }
////// else
////// {
////// ON_BoundingBox orig_box = m_bbox;
////// ON_BoundingBox computed_box;
////// {
////// ON_Brep* this_nonconst = const_cast<ON_Brep*>(this);
////// this_nonconst->ClearBoundingBox();
////// computed_box = BoundingBox();
////// this_nonconst->m_bbox = orig_box; // restore box as it as it was.
////// }
////// // expand the computed_box before we do the incusion test
////// // I'm trying to avoid making a lot of objects created in Rhino 6 and earlier
////// // reporting as Invalid objects in Rhino 7.
////// computed_box.Expand(computed_box.Diagonal() + ON_3dVector(1.0, 1.0, 1.0));
////// if (!computed_box.Includes(orig_box))
////// {
////// if (text_log)
////// text_log->Print("Stored Bounding Box extends far outside of computed bounding box.\n");
////// return ON_BrepIsNotValid();
////// }
////// }
//////}
if (m_bbox.IsNotEmpty())
{
// new_brep_bbox is calculated from scratch WITHOUT changing existing values on ON_BrepFace.m_bbox and ON_Brep.m_bbox.
const ON_BoundingBox new_brep_bbox = this->InternalBrepBoundingBox(false, false);
if (new_brep_bbox.IsNotEmpty())
{
ON_BoundingBox triple_size_new_brep_bbox = new_brep_bbox;
triple_size_new_brep_bbox.Expand(new_brep_bbox.Diagonal() + ON_3dVector(1.0, 1.0, 1.0));
const ON_BoundingBox cached_brep_bbox = m_bbox;
if (false == triple_size_new_brep_bbox.Includes(cached_brep_bbox, false))
{
if (nullptr != text_log)
text_log->Print("WARNING: The cached ON_Brep.m_bbox is much larger than one cacluated from current ON_BrepFace extents.\n");
ON_WARNING("The cached ON_Brep.m_bbox is much larger than one cacluated from current ON_BrepFace extents. This might effect make2d performance.");
// DO NOT RETURN FALSE HERE!
}
}
}
#if 0
// validate ON_BrepTrim.m_pline
for ( ti = 0; ti < trim_count; ti++ )
{
const ON_BrepTrim& trim = m_T[ti];
if ( trim.m_trim_index == -1 )
continue;
const int pline_count = trim.m_pline.Count();
if ( 0 == pline_count )
continue;
if ( pline_count <= 1 )
{
if (text_log )
text_log->Print("ON_Brep.m_T[%d].m_pline.Count() = 1. It should be 0 or >= 2\n",ti);
return ON_BrepIsNotValid();
}
const ON_Interval trim_domain = trim.Domain();
if ( !(trim.m_pline[0].t == trim_domain[0]) )
{
if (text_log )
text_log->Print("ON_Brep.m_T[%d].m_pline[0].t != start of trim domain.\n",ti);
return ON_BrepIsNotValid();
}
if ( !(trim.m_pline[pline_count-1].t == trim_domain[1]) )
{
if (text_log )
text_log->Print("ON_Brep.m_T[%d].m_pline[%d].t != end of trim domain.\n",ti,pline_count-1);
return ON_BrepIsNotValid();
}
for ( int i = 1; i < pline_count; i++ )
{
// pline trim "t" values must be valid
if ( !ON_IsValid(trim.m_pline[i].t) )
{
if (text_log )
text_log->Print("ON_Brep.m_T[%d].m_pline[%d].t is not a valid double.\n",ti,i);
return ON_BrepIsNotValid();
}
if ( !(trim.m_pline[i-1].t < trim.m_pline[i].t) )
{
if (text_log )
text_log->Print("ON_Brep.m_T[%d].m_pline[%d].t must be < m_pline[%d].t.\n",ti,i-1,i);
return ON_BrepIsNotValid();
}
}
if ( ON_UNSET_VALUE == trim.m_pline[0].e
&& ON_UNSET_VALUE == trim.m_pline[pline_count-1].e
)
{
// the "e" values are not set.
// This is permitted. The are set when extensive
// trim-edge parameter correspondence is needed.
// Meshing is an example of a calculation that sets
// the "e" paramters.
continue;
}
if ( trim.m_ei < 0 || trim.m_ei >= m_E.Count() )
{
if (text_log )
text_log->Print("ON_Brep.m_T[%d].m_pline has e parameters but trim.m_ei is not valid.\n",ti);
return ON_BrepIsNotValid();
}
const ON_BrepEdge& edge = m_E[trim.m_ei];
const ON_Interval edge_domain = edge.Domain();
const int i0 = trim.m_bRev3d ? pline_count-1 : 0;
const int i1 = trim.m_bRev3d ? 0 : pline_count-1;
const int di = trim.m_bRev3d ? -1 : 1;
if ( !(trim.m_pline[i0].e == edge_domain[0]) )
{
if (text_log )
text_log->Print("ON_Brep.m_T[%d].m_pline[%d].e != start of edge domain.\n",ti,i0);
return ON_BrepIsNotValid();
}
if ( !(trim.m_pline[i1].e == edge_domain[1]) )
{
if (text_log )
text_log->Print("ON_Brep.m_T[%d].m_pline[%d].e != end of edge domain.\n",ti,i1);
return ON_BrepIsNotValid();
}
int prev_valid_i = i0;
for ( int i = i0+di; i >= 0 && i < pline_count && i-di >= 0 && i-di < pline_count; i += di )
{
if ( !ON_IsValid(trim.m_pline[i].e) )
{
// internal "e" may be invalid when the setter
// had troubles. This is a symptom of a
// bad trim or edge curve, but is not conclusive
// proof.
continue;
}
if ( !(trim.m_pline[prev_valid_i].e < trim.m_pline[i].e) )
{
if (text_log )
text_log->Print("ON_Brep.m_T[%d].m_pline[%d].t must be < m_pline[%d].t.\n",ti,prev_valid_i,i);
return ON_BrepIsNotValid();
}
prev_valid_i = i;
}
}
#endif
return true;
}
bool ON_Brep::SetEdgeVertex( const int ei, const int evi, const int vi )
{
if ( ei < 0 || vi < 0 || evi < 0 || evi > 1 || ei >= m_E.Capacity() )
return false;
ON_BrepEdge& edge = m_E[ei];
if ( edge.m_vi[evi] != vi ) {
edge.m_vi[evi] = vi;
ON_BrepVertex& vertex = m_V[vi];
vertex.m_ei.Append(ei);
}
const int trim_count = edge.m_ti.Count();
int eti, ti, tvi;
for ( eti = 0; eti < trim_count; eti++ ) {
ti = edge.m_ti[eti];
if ( ti < 0 )
continue;
ON_BrepTrim& trim = m_T[ti];
tvi = trim.m_bRev3d ? 1-evi : evi;
trim.m_vi[tvi] = vi;
}
return true;
}
bool ON_Brep::HopAcrossEdge( int& ti, int& tvi ) const
{
// Tf ti is a trim associated with an interior manifold edge,
// then ti is set to twin.
int ei, evi, new_ti, new_tvi;
if ( ti < 0 )
return false;
ei = m_T[ti].m_ei;
if ( ei < 0 )
return false;
const ON_BrepEdge& edge = m_E[ei];
if ( edge.m_ti.Count() < 2 )
return false;
evi = (m_T[ti].m_bRev3d) ? 1-tvi : tvi;
new_ti = edge.m_ti[(edge.m_ti[0] == ti)?1:0];
if ( new_ti < 0 )
return false;
new_tvi = (m_T[new_ti].m_bRev3d) ? 1-evi : evi;
ti = new_ti;
tvi = new_tvi;
return true;
}
bool ON_Brep::SetTrimStartVertex( const int ti0, const int vi )
{
// Do not use NextEdge(), PrevEdge() because they require
// the information we are in the act of creating.
if ( ti0 < 0 || vi < 0 )
return false;
int next_ti, ti, ei, evi, tvi, counter;
const int edge_count = m_E.Count();
// Step counter clockwise around vertex until we hit a boundary
// or we get back to where we started.
for ( ti = ti0, tvi = 0, counter = 0; ti >= 0 && counter < 512; counter++ ) {
if ( counter > 0 ) {
if ( ti == ti0 && tvi == 0 )
return true; // vertex was interior
}
ON_BrepTrim& trim = m_T[ti];
if ( trim.m_type == ON_BrepTrim::singular ) {
trim.m_vi[0] = trim.m_vi[1] = vi;
tvi = 1-tvi;
next_ti = (tvi) ? NextTrim(ti) : PrevTrim(ti);
ti = next_ti;
tvi = 1-tvi;
if ( ti == ti0 && tvi == 0 )
return true; // vertex was interior
if ( m_T[ti].m_type != ON_BrepTrim::singular )
HopAcrossEdge( ti, tvi ); // OK if hop fails because ti is a boundary
continue;
}
ei = trim.m_ei;
evi = (trim.m_bRev3d) ? 1-tvi : tvi;
if ( !SetEdgeVertex( ei, evi, vi ) )
return false;
next_ti = (tvi) ? NextTrim(ti) : PrevTrim(ti);
ti = next_ti;
tvi = 1-tvi;
if ( ti < 0 )
return false; // should not happen
if ( m_T[ti].m_type == ON_BrepTrim::singular )
continue;
ei = m_T[ti].m_ei;
if ( ei < 0 || ei >= edge_count )
return false; // should not happen
evi = (m_T[ti].m_bRev3d) ? 1-tvi : tvi;
const int edge_trim_count = m_E[ei].m_ti.Count();
if ( edge_trim_count < 1 )
break; // should not happen
if ( edge_trim_count == 1 ) {
SetEdgeVertex( ei, evi, vi );
break; // ran into boundary
}
if ( !HopAcrossEdge( ti, tvi ) )
return false;
}
// Get ready to step counter clockwise around vertex until
// we hit a boundary.
ti = ti0;
tvi = 0;
if ( m_T[ti].m_type == ON_BrepTrim::singular ) {
// back up until we get to a non-singular trim
while ( m_T[ti].m_type == ON_BrepTrim::singular ) {
if ( ti != ti0 ) {
m_T[ti].m_vi[0] = vi;
m_T[ti].m_vi[1] = vi;
}
ti = PrevTrim(ti);
tvi = 1;
if ( ti == ti0 )
break;
}
ei = m_T[ti].m_ei;
if ( ei >= 0 ) {
evi = (m_T[ti].m_bRev3d) ? 1-tvi : tvi;
SetEdgeVertex( ei, evi, vi );
}
}
else {
ei = m_T[ti].m_ei;
}
if ( ei < 0 ) {
// did the best we could - return true so setter keeps going
// but the fact we are here means the brep is bogus.
return true;
}
if ( m_E[ei].m_ti.Count() < 2 )
return true; // ti0 is a boundary - we're done.
if ( !HopAcrossEdge( ti, tvi ) )
return false;
next_ti = (tvi) ? NextTrim(ti) : PrevTrim(ti);
if ( next_ti < 0 )
return false;
ti = next_ti;
tvi = 1-tvi;
if ( m_T[ti].m_type != ON_BrepTrim::singular ) {
ei = m_T[ti].m_ei;
if ( ei < 0 )
return false;
if ( m_E[ei].m_ti.Count() == 1 ) {
evi = (m_T[ti].m_bRev3d)? 1-tvi : tvi;
SetEdgeVertex( ei, evi, vi );
return true;
}
if ( !HopAcrossEdge( ti, tvi ) )
return false;
}
const int ti1 = ti;
const int tvi1 = tvi;
for ( ti = ti1, tvi = tvi1, counter = 0; ti >= 0 && counter < 512; counter++ ) {
if ( counter > 0 ) {
if ( ti == ti1 && tvi == tvi1 )
return false; // vertex is not interior - so this should not happen
}
ON_BrepTrim& trim = m_T[ti];
if ( trim.m_type == ON_BrepTrim::singular ) {
trim.m_vi[0] = trim.m_vi[1] = vi;
tvi = 1-tvi;
next_ti = (tvi) ? NextTrim(ti) : PrevTrim(ti);
ti = next_ti;
tvi = 1-tvi;
if ( ti == ti1 && tvi == tvi1 )
return false; // vertex is not interior - so this should not happen
if ( m_T[ti].m_type != ON_BrepTrim::singular )
HopAcrossEdge( ti, tvi ); // OK if hop fails because ti is a boundary
continue;
}
ei = trim.m_ei;
evi = (trim.m_bRev3d) ? 1-tvi : tvi;
if ( !SetEdgeVertex( ei, evi, vi ) )
return false;
next_ti = (tvi) ? NextTrim(ti) : PrevTrim(ti);
ti = next_ti;
tvi = 1-tvi;
if ( ti < 0 )
return false; // should not happen
if ( m_T[ti].m_type == ON_BrepTrim::singular )
continue;
ei = m_T[ti].m_ei;
if ( ei < 0 )
return false; // should not happen
evi = (m_T[ti].m_bRev3d) ? 1-tvi : tvi;
const int edge_trim_count = m_E[ei].m_ti.Count();
if ( edge_trim_count < 1 )
break; // should not happen
if ( edge_trim_count == 1 ) {
SetEdgeVertex( ei, evi, vi );
return true; // ran into boundary - expected
}
if ( !HopAcrossEdge( ti, tvi ) )
return false;
}
return false; // should have exited by hitting "expected" boundary ~10 lines above
}
void ON_Brep::SetLoopVertices( const int li )
{
ON_BrepLoop& loop = m_L[li];
const int loop_trim_count = loop.m_ti.Count();
int lti;
for ( lti = 0; lti < loop_trim_count; lti++ ) {
const int ti = loop.m_ti[lti];
ON_BrepTrim& trim = m_T[ti];
int vi = trim.m_vi[0];
if ( vi >= 0 )
continue;
ON_BrepVertex& v = NewVertex();
SetTrimStartVertex( ti, v.m_vertex_index );
}
}
void ON_Brep::ClearTrimVertices()
{
int ti;
const int tcnt = m_T.Count();
for ( ti = 0; ti < tcnt; ti++ ) {
ON_BrepTrim& trim = m_T[ti];
trim.m_vi[0] = -1;
trim.m_vi[1] = -1;
}
}
void ON_Brep::ClearEdgeVertices()
{
int ei;
const int ecnt = m_E.Count();
for ( ei = 0; ei < ecnt; ei++ ) {
ON_BrepEdge& edge = m_E[ei];
edge.m_vi[0] = -1;
edge.m_vi[1] = -1;
}
}
int ON_Brep::NextTrim(int ti) const
{
const ON_BrepTrim& trim = m_T[ti];
const int li = trim.m_li;
if (li < 0 || li >= m_L.Count())
return -1;
const ON_BrepLoop& loop = m_L[li];
const int trim_count = loop.m_ti.Count();
int lti;
for ( lti = 0; lti < trim_count && loop.m_ti[lti] != ti; lti++)
;/* empty for*/
if ( lti < 0 || lti >= trim_count )
return -1;
return loop.m_ti[(lti+1)%trim_count];
}
int ON_Brep::PrevTrim(int ti) const
{
const ON_BrepTrim& trim = m_T[ti];
const int li = trim.m_li;
if (li < 0 || li >= m_L.Count())
return -1;
const ON_BrepLoop& loop = m_L[li];
const int trim_count = loop.m_ti.Count();
int lti;
for ( lti = 0; lti < trim_count && loop.m_ti[lti] != ti; lti++)
;/* empty for*/
if ( lti < 0 || lti >= trim_count )
return -1;
return loop.m_ti[(lti+trim_count-1)%trim_count];
}
int ON_Brep::NextNonsingularTrim(int tid) const
{
if (tid >= m_T.Count() || tid < 0)
return -1;
bool bSing = (m_T[tid].m_type == ON_BrepTrim::singular) ? true : false;
int ntid;
ntid = NextTrim(tid);
while (ntid >=0 && m_T[ntid].m_type == ON_BrepTrim::singular){
ntid = NextTrim(ntid);
if (ntid == tid && bSing)
return -1;
}
return ntid;
}
int ON_Brep::PrevNonsingularTrim(int tid) const
{
if (tid >= m_T.Count() || tid < 0)
return -1;
bool bSing = (m_T[tid].m_type == ON_BrepTrim::singular) ? true : false;
int ntid;
ntid = PrevTrim(tid);
while (ntid >= 0 && m_T[ntid].m_type == ON_BrepTrim::singular){
ntid = PrevTrim(ntid);
if (ntid == tid && bSing)
return -1;
}
return ntid;
}
int ON_Brep::NextEdge(int ei, int endi, int* next_endi ) const
{
const ON_BrepEdge& edge = m_E[ei];
const int vi = edge.m_vi[endi];
const ON_BrepVertex& vertex = m_V[vi];
const int edge_count = vertex.m_ei.Count();
int vei;
if ( edge_count < 2 )
return -1;
if ( next_endi )
*next_endi = 0;
for ( vei = 0; vertex.m_ei[vei] != ei && vei < edge_count; vei++)
;/* empty for*/
if ( edge.m_vi[0] == edge.m_vi[1] && endi ) {
// get next occurance of edge index
//
// On closed edges, edge.m_vi[0] = edge.m_vi[1] and edge.edge_index
// appears TWICE in vertex.m_ei[]. The first occurance of edge.edge_index
// in vertex.m_ei[] corresponds to edge.m_vi[0]. The second occurance
// of edge.edge_index in vertex.m_ei[] corresponds to edge.m_vi[1].
vei++;
while ( vei < edge_count && vertex.m_ei[vei] != ei )
vei++;
}
if ( vei < 0 || vei >= edge_count )
return -1;
vei = (vei+1)%edge_count;
const int next_ei = vertex.m_ei[vei];
if ( next_endi ) {
if ( m_E[next_ei].m_vi[0] == m_E[next_ei].m_vi[1] ) {
*next_endi = 1;
for ( vei++; vei < edge_count; vei++ ) {
if ( vertex.m_ei[vei] == next_ei ) {
*next_endi = 0;
break;
}
}
}
else if ( m_E[next_ei].m_vi[1] == vi )
*next_endi = 1;
}
return next_ei;
}
int ON_Brep::PrevEdge(int ei, int endi, int* prev_endi ) const
{
const ON_BrepEdge& edge = m_E[ei];
const int vi = edge.m_vi[endi];
const ON_BrepVertex& vertex = m_V[vi];
const int edge_count = vertex.m_ei.Count();
if ( edge_count < 2 )
return -1;
int vei;
if ( prev_endi )
*prev_endi = 0;
for ( vei = 0; vertex.m_ei[vei] != ei && vei < edge_count; vei++)
;/* empty for*/
if ( edge.m_vi[0] == edge.m_vi[1] && endi ) {
// get next occurance of edge index
//
// On closed edges, edge.m_vi[0] = edge.m_vi[1] and edge.edge_index
// appears TWICE in vertex.m_ei[]. The first occurance of edge.edge_index
// in vertex.m_ei[] corresponds to edge.m_vi[0]. The second occurance
// of edge.edge_index in vertex.m_ei[] corresponds to edge.m_vi[1].
vei++;
while ( vei < edge_count && vertex.m_ei[vei] != ei )
vei++;
}
if ( vei < 0 || vei >= edge_count )
return -1;
vei = (vei+edge_count-1)%edge_count;
const int prev_ei = vertex.m_ei[(vei+edge_count-1)%edge_count];
if ( prev_endi ) {
if ( m_E[prev_ei].m_vi[0] == m_E[prev_ei].m_vi[1] ) {
*prev_endi = 1;
for ( vei++; vei < edge_count; vei++ ) {
if ( vertex.m_ei[vei] == prev_ei ) {
*prev_endi = 0;
break;
}
}
}
else if ( m_E[prev_ei].m_vi[1] == vi ) {
*prev_endi = 1;
}
}
return prev_ei;
}
// HELPER CLASS - DO NOT PUT DEFINITION IN A HEADER FILE
class ON__EDGE_ENDS
{
public:
// used to sort vertices of closed edges that need
// to be combined.
int vi0; // smallest edge vertex index
int vi1; // largest edge vertex index
int ei; // index of closed edge
bool operator<(const ON__EDGE_ENDS& other) const
{
int i = other.vi0 - vi0;
if ( i < 0 ) return true;
if ( i > 0 ) return false;
i = other.vi1 - vi1;
if ( i < 0 ) return true;
if ( i > 0 ) return false;
i = other.ei - ei;
if ( i < 0 ) return true;
return false;
}
};
void ON_Brep::SetVertices(void)
{
const int face_count = m_F.Count();
int fi;
ClearEdgeVertices();
ClearTrimVertices();
m_V.Empty();
m_V.Shrink();
fi = m_E.Count() - m_F.Count() + 8;
if ( fi < 32 )
fi = 32;
m_V.Reserve( fi );
for ( fi = 0; fi < face_count; fi++ ) {
ON_BrepFace& face = m_F[fi];
const int loop_count = face.m_li.Count();
int fli;
for ( fli = 0; fli < loop_count; fli++ ) {
SetLoopVertices( face.m_li[fli] );
}
}
// average edges' end location to get vertex location
const int vertex_count = m_V.Count();
int vi;
ON_3dPoint VP(ON_3dPoint::Origin), EP;
for ( vi = 0; vi < vertex_count; vi++ )
{
VP = ON_3dPoint::Origin;
double d = 0.0;
ON_BrepVertex& vertex = m_V[vi];
const int edge_count = vertex.m_ei.Count();
int vei;
for ( vei = 0; vei < edge_count; vei++ )
{
const int ei = vertex.m_ei[vei];
if ( ei < 0 )
continue;
const ON_BrepEdge& edge = m_E[ei];
if ( edge.m_c3i < 0 )
continue;
const ON_Curve* pC = edge.EdgeCurveOf();
if ( !pC )
continue;
if ( edge.m_vi[0] == vi )
EP = edge.PointAtStart();
else if ( edge.m_vi[1] == vi )
EP = edge.PointAtEnd();
else
continue;
VP.x += EP.x;
VP.y += EP.y;
VP.z += EP.z;
d += 1.0;
}
if ( d > 0.0 )
{
d = 1.0/d;
vertex.point = d*VP;
}
}
const int edge_count = m_E.Count();
int ei;
ON_SimpleArray<ON__EDGE_ENDS> edge_ends(edge_count/4 + 2);
for ( ei = 0; ei < edge_count; ei++ )
{
// see if we have any 3d edges that are closed as 3d curves
// but have distinct end vertices
const ON_BrepEdge& edge = m_E[ei];
if ( edge.m_vi[0] >= 0
&& edge.m_vi[1] >= 0
&& edge.m_vi[0] != edge.m_vi[1]
&& 0 != edge.EdgeCurveOf()
&& edge.IsClosed() )
{
ON__EDGE_ENDS& ee = edge_ends.AppendNew();
if ( edge.m_vi[0] < edge.m_vi[1] )
{
ee.vi0 = edge.m_vi[0];
ee.vi1 = edge.m_vi[1];
}
else
{
ee.vi0 = edge.m_vi[1];
ee.vi1 = edge.m_vi[0];
}
ee.ei = ei;
}
}
if ( edge_ends.Count() > 0 )
{
// we need to combine some vertices and the ends of closed edges
edge_ends.QuickSort( ON_CompareIncreasing<ON__EDGE_ENDS> );
int edge_ends_count = edge_ends.Count();
int i0, i1, vi0, vi1, i;
// adjust indices of chained closed edges
for ( i = 1; i < edge_ends_count; i++ )
{
bool bSortAgain = false;
for ( i0 = 0; i0 < edge_ends_count; i0++ )
{
vi0 = edge_ends[i0].vi0;
vi1 = edge_ends[i0].vi1;
for ( i1 = i0+1; i1 < edge_ends_count; i1++ )
{
ON__EDGE_ENDS& ee = edge_ends[i1];
if ( ee.vi0 == vi1 )
{
ee.vi0 = vi0;
bSortAgain = true;
}
if ( ee.vi1 == vi1 )
{
ee.vi1 = ee.vi0;
ee.vi0 = vi0;
bSortAgain = true;
}
}
}
if ( bSortAgain )
{
edge_ends.QuickSort( ON_CompareIncreasing<ON__EDGE_ENDS> );
}
else
break;
}
// combine vertices at ends of closed edges into a single vertex
bool bCullUnusedVertices = false;
for ( i0 = 0, i1 = 1; i0 < edge_ends.Count(); i0 = i1 )
{
vi0 = edge_ends[i0].vi0;
for ( i1 = i0+1; i1 < edge_ends.Count() && vi0 == edge_ends[i1].vi0; i1++ )
{
// empty body
}
vi1 = vi0;
for ( i = i0; i < i1; i++ )
{
if ( edge_ends[i].vi1 > vi1 )
{
vi1 = edge_ends[i].vi1;
if ( 0 <= vi0 && vi0 < vi1 && vi1 < m_V.Count())
{
CombineCoincidentVertices(m_V[vi0],m_V[vi1] );
bCullUnusedVertices = true;
}
}
}
}
if ( bCullUnusedVertices )
CullUnusedVertices();
}
}
void ON_BrepTrim::m__legacy_flags_Set(int gcon, int mono)
{
m__legacy_flags = 0;
switch(gcon)
{
case -1:
m__legacy_flags |= 1;
break;
case 0:
m__legacy_flags |= 2;
break;
case 1:
m__legacy_flags |= 3;
break;
case 2:
m__legacy_flags |= 4;
break;
}
if (mono)
m__legacy_flags |= 8;
else
m__legacy_flags |= 16;
}
bool ON_BrepTrim::m__legacy_flags_Get(int* gcon, int* mono) const
{
if ( gcon ) {
switch ( m__legacy_flags & 7 )
{
case 1:
*gcon = -1;
break;
case 2:
*gcon = 0;
break;
case 3:
*gcon = 1;
break;
case 4:
*gcon = 2;
break;
default:
*gcon = -1;
}
}
if ( mono ) {
if ( 0 != (m__legacy_flags&8) )
*mono = 1;
else
*mono = 0;
}
return m__legacy_flags ? true : false;
}
void ON_Brep::SetTolsFromLegacyValues()
{
// use m_2d_tol and m_3d_tol read from file to set public tolerances
const int vcnt = m_V.Count();
const int tcnt = m_T.Count();
ON_3dPoint endP;
double d;
int vi, ti, vei, evi, vecnt;
// set trim and edge tolerances from values in file
for ( ti = 0; ti < tcnt; ti++ ) {
ON_BrepTrim& trim = m_T[ti];
trim.m_tolerance[0] = trim.m__legacy_2d_tol; // "pe_tol"
trim.m_tolerance[1] = trim.m__legacy_2d_tol; // "pe_tol"
if ( trim.m_ei >= 0 ) {
ON_BrepEdge& edge = m_E[trim.m_ei];
if ( edge.m_tolerance < trim.m__legacy_3d_tol )
edge.m_tolerance = trim.m__legacy_3d_tol; // "e_tol"
}
}
// set vertex tolerances from edge tols and end evaluations
for ( vi = 0; vi < vcnt; vi++ ) {
ON_BrepVertex& vertex = m_V[vi];
vecnt = vertex.m_ei.Count();
for ( vei = 0; vei < vecnt; vei++ ) {
const ON_BrepEdge& edge = m_E[vertex.m_ei[vei]];
if ( vertex.m_tolerance < edge.m_tolerance )
vertex.m_tolerance = edge.m_tolerance;
const ON_Curve* c = m_C3[edge.m_c3i];
evi = 0;
if ( edge.m_vi[0] != vi )
evi = 1;
if ( edge.m_vi[evi] == vi ) {
endP = c->PointAt( c->Domain()[evi] );
d = vertex.point.DistanceTo( endP );
if ( d > vertex.m_tolerance ) {
vertex.m_tolerance = d;
}
}
}
}
}
ON::object_type ON_Brep::ObjectType() const
{
// This must ALWAYS return ON::brep_object.
// NEVER modify this function to return any
// other value.
return ON::brep_object;
}
bool ON_Brep::GetTightBoundingBox(ON_BoundingBox& tight_bbox, bool bGrowBox, const ON_Xform* xform) const
{
if (bGrowBox && !tight_bbox.IsValid())
{
bGrowBox = false;
}
if (!bGrowBox)
{
tight_bbox.Destroy();
}
ON_BoundingBox local_bbox;
// Test vertexes first, this will quickly give us a base bbox to work with.
int vct = m_V.Count();
for (int i = 0; vct > i; i++)
{
if (m_V[i].GetTightBoundingBox(local_bbox, bGrowBox, xform))
bGrowBox = true;
}
ON_SimpleArray<ON_Curve*> iso_curves;
ON_SimpleArray<double> greville_abcissae;
int fct = m_F.Count();
for (int i = 0; fct > i; i++)
{
const ON_BrepFace& face = m_F[i];
//15 July 2018 - Chuck - If a face has been deleted, it will not have an associated surface.
if (face.SurfaceOf() == NULL)
continue;
ON_NurbsSurface nsrf;
if (0 == face.SurfaceOf()->GetNurbForm(nsrf) || false == nsrf.IsValid())
return false;
// See if entire surface bbox is included in current local bbox, it is
// not necessary to calculate the tight bounding box.
// Continue to next face if it is.
if (local_bbox.Includes(nsrf.BoundingBox()))
continue;
const ON_Mesh* mesh = face.Mesh(ON::render_mesh);
if (nullptr == mesh)
mesh = face.Mesh(ON::analysis_mesh);
if (nullptr == mesh)
mesh = face.Mesh(ON::any_mesh);
if (nullptr != mesh)
{
if (mesh->GetTightBoundingBox(local_bbox, bGrowBox, xform))
bGrowBox = true;
}
for (int j = 0; 2 > j; j++)
{
int k, kct = nsrf.CVCount(j);
greville_abcissae.Reserve(kct);
greville_abcissae.SetCount(kct);
nsrf.GetGrevilleAbcissae(j, greville_abcissae);
kct = greville_abcissae.Count();
for (k = 0; kct > k; k++)
{
iso_curves.Append(nsrf.IsoCurve(j == 0 ? 1 : 0, greville_abcissae[k]));
}
}
int ict = iso_curves.Count();
for (int j = 0; ict > j; j++)
{
if (nullptr == iso_curves[j])
continue;
// See if entire iso_curve bbox is included in current local bbox, it is
// not necessary to calculate the tight bounding box.
// Continue to next iso_curve if it is.
if (false == local_bbox.Includes(iso_curves[j]->BoundingBox()))
{
if (iso_curves[j]->GetTightBoundingBox(local_bbox, bGrowBox, xform))
bGrowBox = true;
}
delete iso_curves[j];
iso_curves[j] = nullptr;
}
}
int ect = m_E.Count();
for (int i = 0; ect > i; i++)
{
// See if entire edge bbox is included in current local bbox, it is
// not necessary to calculate the tight bounding box.
// Continue to next edge if it is.
if (local_bbox.Includes(m_E[i].BoundingBox()))
continue;
if (m_E[i].GetTightBoundingBox(local_bbox, bGrowBox, xform))
bGrowBox = true;
}
if (bGrowBox)
tight_bbox.Union(tight_bbox, local_bbox);
else
{
tight_bbox = local_bbox;
bGrowBox = tight_bbox.IsValid();
}
return 0 != bGrowBox;
}
void ON_Brep::ClearBoundingBox()
{
m_bbox.Destroy();
}
void ON_BrepFace::ClearBoundingBox()
{
m_bbox.Destroy();
}
const ON_BoundingBox ON_BrepFace::InternalFaceBoundingBox(bool bLazy, bool bUpdateCachedBBox) const
{
if (bLazy && m_bbox.IsNotEmpty())
return m_bbox;
for (;;)
{
// Make sure this face is valid enough to query the trims and proxy surface
if (nullptr == m_brep)
break;
if (m_face_index < 0)
break;
if (m_face_index >= m_brep->m_F.Count())
break;
if (&m_brep->m_F[m_face_index] != this)
break;
const ON_Surface* proxy_srf = ProxySurface();
if (nullptr == proxy_srf)
break;
if (proxy_srf == this)
break;
ON_BoundingBox pbox = ON_BoundingBox::NanBoundingBox;
for (int li = 0; li < LoopCount(); li++)
{
ON_BrepLoop* loop = Loop(li);
if (loop && loop->m_type == ON_BrepLoop::outer)
{
m_brep->SetTrimBoundingBoxes(*loop, true); // sets loop->m_pbox
if (false == loop->GetBoundingBox(pbox, pbox.IsValid()))
pbox = ON_BoundingBox::UnsetBoundingBox;
break;
}
}
ON_BoundingBox face_bbox = ON_BoundingBox::NanBoundingBox;
if (pbox.IsNotEmpty())
{
ON_Interval pudom(pbox[0].x, pbox[1].x);
ON_Interval pvdom(pbox[0].y, pbox[1].y);
// fatten up invervals to get slightly larger boxes...
pudom.Expand(0.1 * pudom.Length());
pvdom.Expand(0.1 * pvdom.Length());
ON_Interval Sdom[] = { Domain(0), Domain(1) };
// but don't let the fattened intervals extend beyond Sdom
pudom.Intersection(Sdom[0]);
pvdom.Intersection(Sdom[1]);
if (pbox.IsValid() &&
(Sdom[0].Includes(pudom, true) || Sdom[1].Includes(pvdom, true))
)
{
ON_Surface* temp_srf = DuplicateSurface();
if (nullptr != temp_srf)
{
if (Sdom[0].Includes(pudom, true))
temp_srf->Trim(0, pudom);
if (Sdom[1].Includes(pvdom, true))
temp_srf->Trim(1, pvdom);
if (false == temp_srf->GetBoundingBox(face_bbox, false))
face_bbox = ON_BoundingBox::NanBoundingBox;
delete temp_srf;
temp_srf = nullptr;
}
}
}
if (false == face_bbox.IsNotEmpty())
{
if (false == proxy_srf->GetBoundingBox(face_bbox, false))
break;
if (false == face_bbox.IsNotEmpty())
break;
}
if (bUpdateCachedBBox)
const_cast<ON_BrepFace*>(this)->m_bbox = face_bbox;
return face_bbox;
}
// ON_Brep code has always used ON_BoundingBox::EmptyBoundingBox
// to indicate a bounding box is not set. If it were to be written
// in modern times, it would have used Nans.
return ON_BoundingBox::EmptyBoundingBox;
}
const ON_BoundingBox ON_Brep::InternalBrepBoundingBox(bool bLazy, bool bUpdateCachedBBox) const
{
if (bLazy && m_bbox.IsNotEmpty())
return m_bbox;
ON_BoundingBox brep_bbox;
const int face_count = m_F.Count();
int fi;
for (fi = 0; fi < face_count; fi++)
{
if (m_F[fi].m_face_index == -1)
continue;
//GBA 20 May 2020. RH-58462. Brep box now computed from face boxes, instead of surface boxes.
const ON_BrepFace* f = Face(fi);
if (nullptr == f)
continue;
const ON_BoundingBox face_bbox = f->InternalFaceBoundingBox(bLazy, bUpdateCachedBBox);
if (false == face_bbox.IsNotEmpty())
continue;
brep_bbox.Union(face_bbox);
}
if (false == brep_bbox.IsNotEmpty())
{
// ON_Brep code has always used ON_BoundingBox::EmptyBoundingBox
// to indicate a bounding box is not set. If it were to be written
// in modern times, it would have used Nans.
return ON_BoundingBox::EmptyBoundingBox;
}
if (bUpdateCachedBBox)
const_cast<ON_Brep*>(this)->m_bbox = brep_bbox;
return brep_bbox;
}
// ON_BrepFace::GetBBox performs lazy evaluation.
// Namely, if m_bbox is invalid then the bounding box is
// computed and the value is stored in m_bbox to speed future calls.
bool ON_BrepFace::GetBBox(
double* box_min, // [3],
double* box_max, // [3],
bool bGrowBox // = false
) const
{
ON_BoundingBox bbox = this->InternalFaceBoundingBox(true, true);
bool rc = bbox.IsValid();
if (rc)
{
if ( bGrowBox && box_min && box_max && box_min[0] <= box_max[0] )
{
bbox.Union( ON_BoundingBox( ON_3dPoint(box_min), ON_3dPoint(box_max) ) );
}
if ( box_min )
{
box_min[0] = bbox.m_min.x;
box_min[1] = bbox.m_min.y;
box_min[2] = bbox.m_min.z;
}
if ( box_max )
{
box_max[0] = bbox.m_max.x;
box_max[1] = bbox.m_max.y;
box_max[2] = bbox.m_max.z;
}
}
return rc;
}
bool ON_Brep::GetBBox(
double* box_min, // [3],
double* box_max, // [3],
bool bGrowBox // = false
) const
{
ON_BoundingBox bbox = this->InternalBrepBoundingBox(true, true);
bool rc = bbox.IsValid();
if (rc)
{
bbox = m_bbox;
if ( bGrowBox && box_min && box_max && box_min[0] <= box_max[0] )
{
bbox.Union( ON_BoundingBox( ON_3dPoint(box_min), ON_3dPoint(box_max) ) );
}
if ( box_min )
{
box_min[0] = bbox.m_min.x;
box_min[1] = bbox.m_min.y;
box_min[2] = bbox.m_min.z;
}
if ( box_max )
{
box_max[0] = bbox.m_max.x;
box_max[1] = bbox.m_max.y;
box_max[2] = bbox.m_max.z;
}
}
return rc;
}
bool
ON_Brep::SwapCoordinates( int i, int j )
{
bool rc = false;
// swap surface coordinates
const int srf_count = m_S.Count();
int si;
for ( si = 0; si < srf_count; si++ ) {
if ( !m_S[si] )
continue;
rc = m_S[si]->SwapCoordinates(i,j);
if ( !rc ) {
while ( --si >= 0 ) {
// undo any changes;
if ( m_S[si] )
m_S[si]->SwapCoordinates(i,j);
}
return false;
}
}
// swap 3d curve coordinates
const int crv_count = m_S.Count();
int ci;
for ( ci = 0; ci < crv_count; ci++ ) {
if ( !m_C3[ci] )
continue;
rc = m_C3[ci]->SwapCoordinates(i,j);
if ( !rc ) {
// undo any changes;
while ( --ci >= 0 ) {
if ( m_C3[ci] )
m_C3[ci]->SwapCoordinates(i,j);
for ( si = 0; si < srf_count; si++ ) {
if ( m_S[si] )
m_S[si]->SwapCoordinates(i,j);
}
}
return false;
}
}
return rc;
}
bool
ON_Brep::SwapTrimParameters(
int trim_index
)
{
// helper for SwapLoopParameters
if ( trim_index < 0 || trim_index >= m_T.Count() )
return false;
ON_BrepTrim& trim = m_T[trim_index];
StandardizeTrimCurve(trim_index);
const int ci = trim.m_c2i;
if ( ci < 0 || ci >= m_C2.Count() )
return false;
ON_Curve* pC = m_C2[ci];
if ( !pC )
return false;
//ON_Interval pdom = trim.ProxyCurveDomain();
//ON_Interval trimdom = trim.Domain();
// have to call SwapCoordinates on pC because
// ON_CurveProxy does not permit modification
// of "real" curve.
bool rc = pC->SwapCoordinates(0,1); // "u" <-> "v"
if ( !rc )
return false;
// reverse 2d curve
rc = pC->Reverse();
if (rc)
{
// take care of proxy house keeping, m_vi[] swapping, and toggle m_bRev3d.
trim.SetProxyCurve(pC);
int i = trim.m_vi[0];
trim.m_vi[0] = trim.m_vi[1];
trim.m_vi[1] = i;
if ( trim.m_ei >= 0 )
trim.m_bRev3d = trim.m_bRev3d ? false : true;
}
else
{
// undo changes
rc = pC->SwapCoordinates(0,1); // "u" <-> "v"
return false;
}
// reflect iso type
switch ( trim.m_iso )
{
case ON_Surface::not_iso:
trim.m_iso = ON_Surface::not_iso;
break;
case ON_Surface::x_iso:
trim.m_iso = ON_Surface::y_iso;
break;
case ON_Surface::y_iso:
trim.m_iso = ON_Surface::x_iso;
break;
case ON_Surface::W_iso:
trim.m_iso = ON_Surface::S_iso;
break;
case ON_Surface::S_iso:
trim.m_iso = ON_Surface::W_iso;
break;
case ON_Surface::E_iso:
trim.m_iso = ON_Surface::N_iso;
break;
case ON_Surface::N_iso:
trim.m_iso = ON_Surface::E_iso;
break;
default:
trim.m_iso = ON_Surface::not_iso;
break;
}
return true;
}
bool
ON_Brep::SwapLoopParameters(
int loop_index
)
{
bool rc = false;
if ( loop_index < 0 || loop_index >= m_L.Count() )
return false;
ON_BrepLoop& L = m_L[loop_index];
const int loop_trim_count = L.m_ti.Count();
if ( loop_trim_count < 1 )
return false;
int lti, ti;
for ( lti = 0; lti < loop_trim_count; lti++ ) {
ti = L.m_ti[lti];
rc = SwapTrimParameters( ti );
if ( !rc ) {
while ( --lti >= 0 ) {
// undo any changes
ti = L.m_ti[lti];
SwapTrimParameters( ti );
}
return false;
}
}
// reverse order of trimming curves
if ( rc )
L.m_ti.Reverse();
return rc;
}
bool
ON_Brep::IsSolid() const
{
bool bIsOriented = false;
bool bHasBoundary = true;
bool bIsManifold = IsManifold( &bIsOriented, &bHasBoundary );
return (bIsManifold && bIsOriented && !bHasBoundary) ? true : false;
}
void ON_Brep::SetSolidOrientationForExperts(int solid_orientation)
{
switch (solid_orientation)
{
case 0: // not a solid
m_is_solid = 3;
break;
case 1: // solid with normals pointing out
m_is_solid = 1;
break;
case -1: // solid with normals pointing in
m_is_solid = 2;
break;
default:
break;
}
}
int ON_Brep::SolidOrientation() const
{
// m_is_solid values:
// 0 = unset
// 1 = solid with normals pointing out
// 2 = solid with normals pointing in
// 3 = not solid
int rc = 0;
switch( m_is_solid )
{
case 1: // solid with normals pointing out
rc = 1;
break;
case 2: // solid with normals pointing in
rc = -1;
break;
case 3: // not a solid
rc = 0;
break;
default:
if ( IsSolid() )
{
// this virtual function is overridden in Rhino SDK
// and sets m_is_solid to appropriate values. This
// stand-alone version cannot tell the difference
// between solids with inward pointing normals and
// solids with outwards pointing normals.
//ON_Brep* p = const_cast<ON_Brep*>(this);
//p->m_is_solid = 1;
rc = 2;
}
else
{
ON_Brep* p = const_cast<ON_Brep*>(this);
p->m_is_solid = 3;
rc = 0;
}
}
return rc;
}
bool
ON_Brep::IsManifold( bool* pbIsOriented, bool* pbHasBoundary ) const
{
const int fcnt = m_F.Count();
bool bIsManifold = (fcnt > 0) ? true : false;
bool bIsOriented = bIsManifold;
bool bHasBoundary = false;
int fi, other_ti, lcnt, tcnt, fli, lti;
if ( pbIsOriented )
*pbIsOriented = bIsOriented;
if ( pbHasBoundary )
*pbHasBoundary = bHasBoundary;
const int brep_loop_count = m_L.Count();
const int brep_trim_count = m_T.Count();
const int brep_edge_count = m_E.Count();
bool bKeepGoing = bIsManifold;
for ( fi = 0; fi < fcnt && bKeepGoing; fi++ )
{
const ON_BrepFace& face = m_F[fi];
if ( -1 == face.m_face_index )
{
// 28 October 2010 - Dale Lear and Chuck
// Do not test deleted faces. The join
// command calls is manifold with some
// deleted faces to avoid calling Compact
// lots of times during a join.
continue;
}
lcnt = face.m_li.Count();
if ( lcnt < 1 ) {
bIsManifold = false;
if (!pbHasBoundary)
bKeepGoing = false;
}
for ( fli = 0; fli < lcnt && bKeepGoing; fli++ )
{
const int li = face.m_li[fli];
if ( li < 0 || li >= brep_loop_count )
{
ON_ERROR("Bogus loop index in face.m_li[]");
continue;
}
const ON_BrepLoop& loop = m_L[li];
tcnt = loop.m_ti.Count();
if (tcnt < 1 ) {
bIsManifold = false;
if (!pbHasBoundary)
bKeepGoing = false;
}
for ( lti = 0; lti < tcnt && bKeepGoing; lti++ )
{
const int ti = loop.m_ti[lti];
if ( ti < 0 || ti >= brep_trim_count )
{
ON_ERROR("Bogus loop index in loop.m_ti[]");
continue;
}
const ON_BrepTrim& trim = m_T[ti];
switch ( trim.m_type )
{
case ON_BrepTrim::boundary:
bHasBoundary = true;
break;
case ON_BrepTrim::mated:
case ON_BrepTrim::seam:
// make sure we have a manifold join
if ( trim.m_ei >= 0 && trim.m_ei < brep_edge_count )
{
const ON_BrepEdge& edge = m_E[trim.m_ei];
if ( edge.m_ti.Count() != 2 ) {
bIsManifold = false;
if (!pbHasBoundary)
bKeepGoing = false;
}
else
{
other_ti = edge.m_ti[0];
if ( other_ti == ti )
other_ti = edge.m_ti[1];
if ( other_ti == ti )
{
bIsManifold = false;
if (!pbHasBoundary)
bKeepGoing = false;
}
else
{
const ON_BrepTrim& other_trim = m_T[other_ti];
// Nov 9, 2011 Tim - Fix for crash bug RR 93743
// Better index checking.
bool bFlipTrim = trim.m_bRev3d;
if (0 <= trim.m_li && brep_loop_count > trim.m_li)
{
if (m_L[trim.m_li].m_fi >= 0 && m_L[trim.m_li].m_fi < m_F.Count()){
if ( m_F[m_L[trim.m_li].m_fi].m_bRev )
bFlipTrim = !bFlipTrim;
}
else
{
ON_ERROR("Bogus face index in m_L[trim.m_li]");
continue;
}
}
else
{
ON_ERROR("Bogus loop index in trim.m_li");
continue;
}
bool bFlipOther = other_trim.m_bRev3d;
if (0 <= other_trim.m_li && brep_loop_count > other_trim.m_li)
{
if (m_L[other_trim.m_li].m_fi >= 0 && m_L[other_trim.m_li].m_fi < m_F.Count()){
if ( m_F[m_L[other_trim.m_li].m_fi].m_bRev )
bFlipOther = !bFlipOther;
}
else
{
ON_ERROR("Bogus face index in m_L[other_trim.m_li]");
continue;
}
}
else
{
ON_ERROR("Bogus loop index in other_trim.m_li");
continue;
}
if ( bFlipTrim && bFlipOther )
{
bIsOriented = false;
}
else if ( !bFlipTrim && !bFlipOther )
{
bIsOriented = false;
}
}
}
}
else
{
ON_ERROR("Bogus trim.m_ei or trim.m_type value");
}
break;
case ON_BrepTrim::singular:
// nothing to check here
break;
default:
bIsManifold = false;
if (!pbHasBoundary)
bKeepGoing = false;
break;
}
}
}
}
if ( !bIsManifold ) {
bIsOriented = false;
//21 June 2017 - Chuck - bHasBoundary just says if there are any naked edges.
//Even if the brep is non-manifold, this should still be the case.
//bHasBoundary = false;
}
if ( pbIsOriented )
*pbIsOriented = bIsOriented;
if ( pbHasBoundary )
*pbHasBoundary = bHasBoundary;
if ( !bIsManifold || bHasBoundary )
{
if ( m_is_solid != 3 )
{
// lazy evaluation used on m_is_solid
const_cast<ON_Brep*>(this)->m_is_solid = 3;
}
}
return bIsManifold;
}
bool
ON_Brep::IsSurface() const
{
// returns true if the b-rep has a single face
// and that face is geometrically the same
// as the underlying surface. I.e., the face
// has trivial trimming. In this case, the
// surface is m_S[0].
return (m_F.Count() == 1 && FaceIsSurface(0));
}
bool
ON_Brep::FaceIsSurface( int face_index ) const
{
// returns true if the face has a single
// outer boundary and that boundary runs
// along the edges of the underlying surface.
// In this case the geometry of the surface
// is the same as the geometry of the face.
bool bTrivialFace = false;
if ( face_index >= 0 && face_index < m_F.Count() ) {
const ON_BrepFace& face = m_F[face_index];
if ( face.m_li.Count() == 1 ) {
bTrivialFace = LoopIsSurfaceBoundary( face.m_li[0] );
}
}
return bTrivialFace;
}
bool
ON_Brep::LoopIsSurfaceBoundary( int loop_index ) const
{
// returns true if the loop's trims run along the underlying surface boundary
bool bTrivialLoop = false;
if ( loop_index >= 0 && loop_index < m_L.Count() ) {
const ON_BrepLoop& loop = m_L[loop_index];
const int trim_count = loop.m_ti.Count();
if ( trim_count > 0 ) {
bTrivialLoop = true;
for ( int lti = 0; lti < trim_count && bTrivialLoop; lti++ )
{
int ti = loop.m_ti[lti];
if ( ti < 0 || ti >= m_T.Count() )
{
ON_ERROR("Bogus trim index in loop.m_ti[]");
return false;
}
const ON_BrepTrim& trim = m_T[ti];
if ( trim.m_iso == ON_Surface::W_iso )
continue;
if ( trim.m_iso == ON_Surface::S_iso )
continue;
if ( trim.m_iso == ON_Surface::N_iso )
continue;
if ( trim.m_iso == ON_Surface::E_iso )
continue;
bTrivialLoop = false;
}
}
}
return bTrivialLoop;
}
bool
ON_Brep::FlipReversedSurfaces()
{
// Clears all ON_BrepFace.m_bRev flags
// by calling SwapFaceParameters() on each
// face with a true m_bRev.
//
// Returns true if successful.
// 11 April 2008 Dale Lear and Tim:
// face.Transpose() is clearing the m_is_solid
// flag but we are not changing the orientation
// of the brep. This prevents having to perform
// the expensive step of calculating this flag
// again.
int saved_is_solid = m_is_solid;
const int face_count = m_F.Count();
bool rc = true;
int fi;
for ( fi = 0; fi < face_count; fi++ )
{
ON_BrepFace& face = m_F[fi];
if ( face.m_bRev )
{
if ( !face.Transpose() )
rc = false;
}
}
m_is_solid = saved_is_solid;
return rc;
}
////////
// Change the domain of a trim
bool ON_Brep::SetTrimDomain(
int trim_index, // index of trim in m_T[] array
const ON_Interval& domain
)
{
bool rc = false;
if ( trim_index >= 0 && trim_index < m_T.Count() && domain.IsIncreasing() )
{
ON_BrepTrim& trim = m_T[trim_index];
rc = trim.SetDomain(domain);
}
return rc;
}
////////
// Change the domain of an edge
bool ON_Brep::SetEdgeDomain(
int edge_index, // index of edge in m_E[] array
const ON_Interval& domain
)
{
bool rc = false;
if ( edge_index >= 0 && edge_index < m_E.Count() && domain.IsIncreasing() )
{
ON_BrepEdge& edge = m_E[edge_index];
rc = edge.SetDomain(domain);
}
return rc;
}
bool ON_BrepFace::Reverse(int dir)
{
if ( dir < 0 || dir > 1 || 0 == m_brep )
return false;
ON_Surface* srf = const_cast<ON_Surface*>(SurfaceOf());
if ( !srf )
return false;
ON_Interval dom0 = srf->Domain(dir);
if ( !dom0.IsIncreasing() )
return false;
// 2/18/03 GBA. Destroy surface cache on face.
DestroyRuntimeCache(true);
if ( m_brep->SurfaceUseCount( m_si, 2 ) > 1 )
{
srf = srf->DuplicateSurface();
m_si = m_brep->AddSurface( srf );
SetProxySurface(srf);
}
if ( !srf->Reverse(dir) )
return false;
ON_Interval dom1 = dom0;
dom1.Reverse();
if ( dom1 != srf->Domain(dir) )
{
srf->SetDomain( dir, dom1 );
dom1 = srf->Domain(dir);
}
// adjust location of 2d trim curves
ON_Xform xform(ON_Xform::IdentityTransformation);
xform.IntervalChange(dir,dom0,ON_Interval(dom1[1],dom1[0]));
TransformTrim(xform);
// reverse loop orientations.
int fli;
for ( fli = 0; fli < m_li.Count(); fli++ )
{
ON_BrepLoop* loop = m_brep->Loop(m_li[fli]);
if ( loop )
m_brep->FlipLoop( *loop );
}
m_bRev = m_bRev ? false : true;
if (m_brep->m_is_solid == 1 || m_brep->m_is_solid == 2) m_brep->m_is_solid = 0;
// Greg Arden 10 April 2003. Fix TRR#9624.
// Update analysis and render meshes.
{
auto rm = UniqueMesh(ON::render_mesh);
if(rm)
{
auto p = const_cast<ON_Mesh*>(rm.get());
p->ReverseSurfaceParameters(dir);
p->ReverseTextureCoordinates(dir);
}
}
{
auto rm = UniqueMesh(ON::analysis_mesh);
if (rm)
{
auto p = const_cast<ON_Mesh*>(rm.get());
p->ReverseSurfaceParameters(dir);
p->ReverseTextureCoordinates(dir);
}
}
return true;
}
bool ON_BrepFace::Transpose()
{
if ( 0 == m_brep )
return false;
ON_Surface* srf = const_cast<ON_Surface*>(SurfaceOf());
if ( 0 == srf )
return false;
// 2/18/03 GBA. Destroy cache on the face.
DestroyRuntimeCache(true);
// make sure only one face uses this surface
if ( m_brep->SurfaceUseCount( m_si, 2 ) > 1 )
{
srf = srf->DuplicateSurface();
m_si = m_brep->AddSurface(srf);
SetProxySurface(srf);
}
//ON_Interval u0 = srf->Domain(0);
//ON_Interval v0 = srf->Domain(1);
// swap surface "u" and "v"
bool rc = srf->Transpose();
if ( !rc )
return false;
//ON_Interval u1 = srf->Domain(0);
//ON_Interval v1 = srf->Domain(1);
ON_Xform xform(ON_Xform::IdentityTransformation);
xform[0][0] = 0.0;
xform[0][1] = 1.0;
xform[1][0] = 1.0;
xform[1][1] = 0.0;
TransformTrim(xform);
// reverse loop orientations.
int fli;
for ( fli = 0; fli < m_li.Count(); fli++ )
{
ON_BrepLoop* loop = m_brep->Loop(m_li[fli]);
if ( loop )
m_brep->FlipLoop( *loop );
}
m_bRev = m_bRev ? false : true;
// 11 April 2008 Dale Lear:
// Transposing the surface and then toggling the m_bRev
// does not alter the brep's orientation. Setting this flag
// to zero means we will have to do an unnecessary and
// expensive calculation in the future.
//if (m_brep->m_is_solid == 1 || m_brep->m_is_solid == 2) m_brep->m_is_solid = 0;
// Update analysis mesh and render mesh.
// (Greg Arden 10 April 2003. Fix TRR#9624.)
{
auto rm = UniqueMesh(ON::render_mesh);
if (rm)
{
auto p = const_cast<ON_Mesh*>(rm.get());
p->TransposeSurfaceParameters();
p->TransposeTextureCoordinates();
}
}
{
auto rm = UniqueMesh(ON::analysis_mesh);
if (rm)
{
auto p = const_cast<ON_Mesh*>(rm.get());
p->TransposeSurfaceParameters();
p->TransposeTextureCoordinates();
}
}
return true;
}
bool ON_BrepFace::SetDomain(
ON_Interval u_dom,
ON_Interval v_dom
)
{
if ( 0 == m_brep )
return false;
if ( !u_dom.IsIncreasing() )
return false;
if ( !v_dom.IsIncreasing() )
return false;
ON_Surface* srf = const_cast<ON_Surface*>(SurfaceOf());
if ( 0 == srf )
return false;
ON_Interval u0_dom = srf->Domain(0);
ON_Interval v0_dom = srf->Domain(1);
if ( u0_dom == u_dom && v0_dom == v_dom )
return true;
ON_Xform xform(ON_Xform::IdentityTransformation);
{
ON_Xform ux(ON_Xform::IdentityTransformation), vx(ON_Xform::IdentityTransformation);
if ( u0_dom != u_dom )
{
if ( !ux.IntervalChange(0,u0_dom,u_dom) )
return false;
}
if ( v0_dom != v_dom )
{
if ( !vx.IntervalChange(1,v0_dom,v_dom) )
return false;
}
xform = ux*vx;
}
// 2/18/03 GBA. Destroy cache on the face.
DestroyRuntimeCache(true);
if ( m_brep->SurfaceUseCount( m_si, 2 ) > 1 )
{
srf = srf->DuplicateSurface();
m_si = m_brep->AddSurface(srf);
SetProxySurface(srf);
}
if ( u_dom != u0_dom )
{
if ( !srf->SetDomain( 0, u_dom ) )
return false;
}
if ( v_dom != v0_dom )
{
if ( !srf->SetDomain( 1, v_dom ) )
{
srf->SetDomain(0,u0_dom );
return false;
}
}
// just to be sure 2d curves are in synch with actual surface
// domain in case srf->SetDomain() does something weird.
u_dom = srf->Domain(0);
v_dom = srf->Domain(1);
{
ON_Xform ux(ON_Xform::IdentityTransformation), vx(ON_Xform::IdentityTransformation);
if ( u0_dom != u_dom )
{
if ( !ux.IntervalChange(0,u0_dom,u_dom) )
return false;
}
if ( v0_dom != v_dom )
{
if ( !vx.IntervalChange(1,v0_dom,v_dom) )
return false;
}
xform = ux*vx;
}
if ( !TransformTrim(xform) )
return false;
std::array<std::shared_ptr<const ON_Mesh>, 3> meshes { UniqueMesh(ON::analysis_mesh), UniqueMesh(ON::render_mesh), UniqueMesh(ON::preview_mesh) };
for (auto& m : meshes)
{
if (m)
{
auto pMesh = const_cast<ON_Mesh*>(m.get());
for ( int dir = 0; dir < 2; dir++ )
{
ON_Interval& mdom = pMesh->m_srf_domain[dir];
const ON_Interval dom0 = dir ? v0_dom : u0_dom;
const ON_Interval dom1 = dir ? v_dom : u_dom;
if ( mdom.IsIncreasing() && dom0 != dom1 )
{
if ( mdom == dom0 )
{
mdom = dom1;
}
else
{
double t0 = dom1.ParameterAt(dom0.NormalizedParameterAt(mdom[0]));
double t1 = dom1.ParameterAt(dom0.NormalizedParameterAt(mdom[1]));
mdom.Set(t0,t1);
}
}
}
}
}
return true;
}
bool ON_BrepFace::SetDomain(
int dir, // 0 sets first parameter's domain, 1 gets second parameter's domain
double t0,
double t1
)
{
if ( dir < 0
|| dir > 1
|| t0 == ON_UNSET_VALUE
|| t1 == ON_UNSET_VALUE
|| t0 >= t1
|| 0 == m_brep )
return false;
ON_Surface* srf = const_cast<ON_Surface*>(SurfaceOf());
if ( 0 == srf )
return false;
ON_Interval udom = srf->Domain(0);
ON_Interval vdom = srf->Domain(1);
if ( dir )
vdom.Set(t0,t1);
else
udom.Set(t0,t1);
return SetDomain( udom, vdom );
}
//bool ON_Brep::ReverseFaceParameter(
// int face_index, // index of face
// int dir // dir = 0 reverse "u", 1 reverse "v"
// )
//{
// // OBSOLETE - use ON_BrepFace::Reverse(dir)
// bool rc = false;
// ON_BrepFace* face = Face(face_index);
// if ( face )
// rc = face->Reverse(dir)?true:false;
// return rc;
//}
int ON_Brep::TrimCurveUseCount( int c2_index, int max_count ) const
{
int ti, use_count = 0;
if ( max_count < 1 )
max_count = m_T.Count();
for ( ti = 0; ti < m_T.Count() && use_count < max_count; ti++ )
{
if ( m_T[ti].m_c2i == c2_index )
use_count++;
}
return use_count;
}
int ON_Brep::EdgeCurveUseCount( int c3_index, int max_count ) const
{
int ei, use_count = 0;
if ( max_count < 1 )
max_count = m_T.Count();
for ( ei = 0; ei < m_E.Count() && use_count < max_count; ei++ )
{
if ( m_E[ei].m_c3i == c3_index )
use_count++;
}
return use_count;
}
int ON_Brep::SurfaceUseCount( int surface_index, int max_count ) const
{
int fi, use_count = 0;
if ( max_count < 1 )
max_count = m_F.Count();
for ( fi = 0; fi < m_F.Count() && use_count < max_count; fi++ )
{
if ( m_F[fi].m_si == surface_index )
use_count++;
}
return use_count;
}
////////
// Change the domain of a face
// This also transforms the "u" and "v" coordinates of all the
// face's parameter space trimming curves.
//bool ON_Brep::SetFaceDomain(
// int face_index, // index of face in m_F[] array
// const ON_Interval& u_dom,
// const ON_Interval& v_dom
// )
//{
// // OBSOLETE
// bool rc = false;
// ON_BrepFace* face = Face(face_index);
// if ( face )
// rc = face->SetDomain(u_dom,v_dom);
// return rc;
//}
//bool
//ON_Brep::SwapFaceParameters( int face_index )
//{
// // OBSOLETE
// bool rc = false;
// ON_BrepFace* face = Face(face_index);
// if ( face )
// rc = face->Transpose()?true:false;
// return rc;
//}
void
ON_Brep::Flip()
{
const int fcnt = m_F.Count();
int fi;
int missolid = m_is_solid;
for ( fi = 0; fi < fcnt; fi++ ) {
FlipFace(m_F[fi]);
}
if (missolid==1) m_is_solid = 2;
else if (missolid==2) m_is_solid = 1;
}
//void
//ON_Brep::FlipEdge( ON_BrepEdge& edge )
//{
// edge.Reverse();
//}
void
ON_Brep::FlipFace( ON_BrepFace& face )
{
face.m_bRev = (face.m_bRev) ? false : true;
std::array<std::shared_ptr<const ON_Mesh>, 3> meshes{ face.UniqueMesh(ON::analysis_mesh), face.UniqueMesh(ON::render_mesh), face.UniqueMesh(ON::preview_mesh) };
for (auto& m : meshes)
{
auto pMesh = const_cast<ON_Mesh*>(m.get());
if (pMesh)
{
pMesh->Flip();
}
}
//Jun 16 2011 - Chuck - m_is_solid==3 for a brep with inconsistent normals.
//Flipping a face could make the normals consistent.
//if (m_is_solid == 1 || m_is_solid == 2)
if (0 != m_is_solid)
m_is_solid = 0;
}
//void
//ON_Brep::FlipTrim(ON_BrepTrim& trim)
//{
// trim.Reverse();
//}
void
ON_Brep::FlipLoop(ON_BrepLoop& loop)
{
int ti, lti;
const int brep_trim_count = m_T.Count();
const int loop_trim_count = loop.m_ti.Count();
// reverse order of trimming curves
loop.m_ti.Reverse();
// reverse direction of individual trimming curves
for ( lti = 0; lti < loop_trim_count; lti++ )
{
ti = loop.m_ti[lti];
if ( ti >= 0 && ti < brep_trim_count )
{
m_T[ti].Reverse();
}
}
}
static int curve_area( ON_3dPoint& start_point, const ON_Curve* curve, const ON_Interval& curve_domain, const ON_Xform* xform, double *area )
{
// ges a CRUDE approximation of curve area to use for
// determining if a simple closed curve 2d has
// clockwise or couterclockwise orientation.
ON_Workspace ws;
ON_Interval span_domain;
double *span_vector, *t, twice_area = 0.0;
ON_3dPoint p0, p1;
int degree, span_count, span_i, j;
if ( !area )
return false;
*area = 0;
if ( !curve )
return false;
const ON_PolyCurve* polycurve = ON_PolyCurve::Cast(curve);
if ( polycurve )
{
span_count = polycurve->Count();
ON_Interval segment_curve_domain;
double s0, s1;
for ( span_i = 0; span_i < span_count; span_i++ )
{
span_domain = polycurve->SegmentDomain(span_i);
if ( span_domain[1] <= curve_domain[0] )
continue;
if ( span_domain[0] >= curve_domain[1] )
break;
const ON_Curve* segment_curve = polycurve->SegmentCurve(span_i);
segment_curve_domain = segment_curve->Domain();
if ( curve_domain[0] > span_domain[0] || curve_domain[1] < span_domain[1] )
{
s0 = (curve_domain[0] > span_domain[0]) ? curve_domain[0] : span_domain[0];
s1 = (curve_domain[1] < span_domain[1]) ? curve_domain[1] : span_domain[1];
if ( segment_curve_domain != span_domain )
{
s0 = span_domain.NormalizedParameterAt(s0);
s1 = span_domain.NormalizedParameterAt(s1);
s0 = segment_curve_domain.ParameterAt(s0);
s1 = segment_curve_domain.ParameterAt(s1);
}
segment_curve_domain.Set(s0,s1);
}
if ( !curve_area( start_point, segment_curve, segment_curve_domain, xform, &twice_area ) )
{
*area = 0.0;
return false;
}
*area += twice_area;
}
}
else
{
span_count = curve->SpanCount();
if ( span_count < 1 )
return false;
degree = curve->Degree();
if ( degree <= 1 )
{
degree = 1;
}
else if ( degree < 4)
{
degree = 4;
// 6 January 2006 Dale Lear
// Every time you find a closed curve that
// gets the wrong dir, increase the number
// after the < by one until it works. Add
// the curve to RR and list the RR number here.
// 17 October 2012 Dale Lear
// To fix http://dev.mcneel.com/bugtrack/?q=113316
// I changed "< 16" to "< 17".
while ( span_count*degree < 17 )
degree *= 2;
}
span_vector = ws.GetDoubleMemory(span_count+1+degree);
t = span_vector+(span_count+1);
t[0] = 0.0;
for ( j = 1; j < degree; j++ ) {
t[j] = ((double)(j))/(degree);
}
if ( !curve->GetSpanVector( span_vector ) )
return false;
p1 = xform ? (*xform)*start_point : start_point;
for ( span_i = 0; span_i < span_count; span_i++ ) {
span_domain.Set( span_vector[span_i], span_vector[span_i+1] );
if ( span_domain[1] <= curve_domain[0] )
continue;
if ( span_domain[0] >= curve_domain[1] )
break;
if ( span_domain[1] > curve_domain[1] )
span_domain.m_t[1] = curve_domain[1];
if ( span_domain[0] < curve_domain[0] )
span_domain.m_t[0] = curve_domain[0];
if ( span_domain[0] >= span_domain[1] )
continue;
for ( j = 0; j < degree; j++ ) {
p0 = p1;
p1 = curve->PointAt(span_domain.ParameterAt(t[j]));
if ( xform )
p1 = (*xform)*p1;
twice_area += (p0.x-p1.x)*(p0.y+p1.y);
if ( !span_i && !j ) {
// check gap
}
}
}
p0 = p1;
p1 = curve->PointAt(curve_domain[1]);
start_point = p1;
if ( xform )
p1 = (*xform)*p1;
twice_area += (p0.x-p1.x)*(p0.y+p1.y);
*area = 0.5*twice_area;
}
return true;
}
int ON_ClosedCurveOrientation( const ON_Curve& curve, const ON_Xform* xform )
{
int curve_orientation = 0;
double area = 0.0;
ON_3dPoint start_point = curve.PointAtEnd();
const ON_Interval curve_domain = curve.Domain();
if ( xform && xform->IsIdentity() )
xform = 0;
if (curve_area( start_point, &curve, curve_domain, xform, &area ))
{
double noise = 0.0;
if ( area > noise )
curve_orientation = 1;
else if (area < noise )
curve_orientation = -1;
}
return curve_orientation;
}
int ON_ClosedCurveOrientation(const ON_Curve& curve, const ON_Plane& plane)
{
ON_Xform x;
x.Rotation(plane, ON_Plane::World_xy);
return ON_ClosedCurveOrientation(curve, &x);
}
double ON_CurveOrientationArea(
const ON_Curve* curve,
const ON_Interval* domain,
const ON_Xform* xform,
bool bReverseCurve
)
{
if ( 0 == curve )
return 0.0;
ON_Interval local_domain = curve->Domain();
if ( 0 != domain && domain->IsIncreasing() )
local_domain.Intersection(*domain);
ON_3dPoint start_point = curve->PointAt(local_domain[0]);
double a = 0.0;
if ( !curve_area( start_point, curve, local_domain, xform, &a ) )
a = 0.0;
else if ( bReverseCurve && 0.0 != a )
a = -a;
return a;
}
static int loop_type_compar(const ON_BrepLoop *const* ppLoopA, const ON_BrepLoop *const* ppLoopB )
{
const ON_BrepLoop* loopA = *ppLoopA;
const ON_BrepLoop* loopB = *ppLoopB;
if ( loopA->m_type == loopB->m_type )
return 0;
if ( loopA->m_type == ON_BrepLoop::unknown )
return 1;
if ( loopB->m_type == ON_BrepLoop::unknown )
return -1;
if ( loopA->m_type < loopB->m_type )
return -1;
if ( loopA->m_type > loopB->m_type )
return 1;
return 0;
}
bool ON_Brep::SortFaceLoops( ON_BrepFace& face ) const
{
int fli, li, loop_type;
const int face_loop_count = face.m_li.Count();
const int loop_count = m_L.Count();
if ( face_loop_count < 1 || loop_count < 1 )
return false;
bool rc = true;
ON_SimpleArray<const ON_BrepLoop*> loop_ptr(face_loop_count);
for ( fli = 0; fli < face_loop_count; fli++ )
{
li = face.m_li[fli];
if ( li < 0 || li >= loop_count )
return false;
const ON_BrepLoop& loop = m_L[li];
if ( loop.m_loop_index != li )
return false;
loop_type = loop.m_type;
if ( loop_type <= ON_BrepLoop::unknown || loop_type >= ON_BrepLoop::type_count )
rc = false;
loop_ptr.Append( &m_L[li] );
}
loop_ptr.QuickSort( loop_type_compar );
for ( fli = 0; fli < face_loop_count; fli++ )
{
face.m_li[fli] = loop_ptr[fli]->m_loop_index;
}
return rc;
}
int
ON_Brep::LoopDirection( const ON_BrepLoop& loop ) const
{
ON_3dPoint start_point;
double d, a = 0.0;
int ti, lti, c2i;
const int brep_trim_count = m_T.Count();
const int brep_C2_count = m_C2.Count();
const int loop_trim_count = loop.m_ti.Count();
double noise = 0.0;
// reverse direction of individual trimming curves
for ( lti = 0; lti < loop_trim_count; lti++ ) {
ti = loop.m_ti[lti];
if ( ti < 0 || ti >= brep_trim_count ) {
a = 0.0;
break;
}
c2i = m_T[ti].m_c2i;
if ( c2i < 0 || c2i >= brep_C2_count ) {
a = 0.0;
break;
}
if ( lti == 0 )
{
// evaluate start of first trim
if ( m_C2[c2i] )
start_point = m_T[ti].PointAtStart(); //m_C2[c2i]->PointAt(m_T[ti].m_t[0]);
}
if ( !curve_area( start_point, &m_T[ti], m_T[ti].Domain(), 0, &d ) ) {
a = 0.0;
break;
}
//noise += fabs(d);
a += d;
}
//this fixes trr 9351. Change at your own risk.
//noise *= 10.0*ON_EPSILON;
if (a > noise)
return 1;
else if (a < -noise)
return -1;
return 0;
}
bool ON_Brep::SetVertexTolerances( bool bLazy )
{
bool rc = true;
int vi, vertex_count = m_V.Count();
for ( vi = 0; vi < vertex_count; vi++ )
{
if ( !SetVertexTolerance( m_V[vi], bLazy ) )
rc = false;
}
return rc;
}
bool
ON_Brep::SetVertexTolerance( ON_BrepVertex& vertex,
bool bLazySet // default = false
// false: recompute tolerance even if
// its current value is positive
// true: recompute tolerance only if
// its current value is nonpositive
) const
{
if ( vertex.m_tolerance < 0.0 || !bLazySet ) {
const int vertex_edge_count = vertex.EdgeCount();
if ( vertex_edge_count < 1 ) {
vertex.m_tolerance = 0.0;
}
else {
vertex.m_tolerance = ON_UNSET_VALUE;
double tolerance = 0.0;
double d;
ON_3dPoint uv;
ON_Interval edge_domain;
//const ON_Curve* c=0;
const ON_Surface* s=0;
int vei, ei, eti, endi;
const int vertex_index = vertex.m_vertex_index;
for ( vei = 0; vei < vertex_edge_count; vei++ )
{
ei = vertex.m_ei[vei];
if ( ei < 0 )
return false;
const ON_BrepEdge& edge = m_E[ei];
if ( !edge.ProxyCurve() )
return false;
edge_domain = edge.Domain();
for ( endi = 0; endi < 2; endi++ )
{
if ( edge.m_vi[endi] == vertex_index )
{
d = vertex.point.DistanceTo( edge.PointAt(edge_domain[endi]) );
if ( tolerance < d )
tolerance = d;
}
}
const int edge_trim_count = edge.m_ti.Count();
for ( eti = 0; eti < edge_trim_count; eti++ )
{
const ON_BrepTrim* trim = Trim(edge.m_ti[eti]);
if ( 0 == trim )
continue;
if ( 0 == trim->TrimCurveOf() )
continue;
s = trim->SurfaceOf();
if ( 0 == s )
continue;
for ( endi = 0; endi < 2; endi++ ) {
if ( edge.m_vi[endi] == vertex_index ) {
uv = trim->PointAt( trim->Domain()[trim->m_bRev3d?1-endi:endi] );
d = vertex.point.DistanceTo( s->PointAt(uv.x,uv.y) );
if ( tolerance < d )
tolerance = d;
}
}
}
}
vertex.m_tolerance = (tolerance <= ON_ZERO_TOLERANCE) ? 0.0 : 1.001*tolerance;
}
}
return (vertex.m_tolerance >= 0.0) ? true : false;
}
bool
ON_Brep::SetTrimTolerance( ON_BrepTrim& trim, bool bLazy ) const
{
// The TL_Brep::SetTrimTolerance override of this virtual function
// sets ON_BrepTrim::m_tolerance[] correctly.
double ds, de, d;
int dir, lti, prev_ti, next_ti;
if ( trim.m_tolerance[0] < 0.0 || trim.m_tolerance[1] < 0.0 || !bLazy )
{
// set trim tolerance
if ( trim.m_li >= 0 && trim.m_li < m_L.Count() )
{
const ON_BrepLoop& loop = m_L[trim.m_li];
const int loop_trim_count = loop.m_ti.Count();
for ( lti = 0; lti < loop_trim_count; lti++ )
{
if ( loop.m_ti[lti] == trim.m_trim_index )
{
prev_ti = loop.m_ti[(lti-1+loop_trim_count)%loop_trim_count];
next_ti = loop.m_ti[(lti+1)%loop_trim_count];
if ( prev_ti >= 0 && next_ti >= 0 && prev_ti < m_T.Count() && next_ti < m_T.Count() )
{
const ON_BrepTrim& prev_trim = m_T[prev_ti];
const ON_BrepTrim& next_trim = m_T[next_ti];
const ON_Curve* prev_c2 = prev_trim.TrimCurveOf();
const ON_Curve* next_c2 = next_trim.TrimCurveOf();
const ON_Curve* c2 = trim.TrimCurveOf();
if ( prev_c2 && c2 && next_c2 )
{
ON_3dPoint prev_end = prev_trim.PointAtEnd(); //prev_c2->PointAt( prev_trim.m_t[1] );
ON_3dPoint this_start = trim.PointAtStart(); //c2->PointAt( trim.m_t[0] );
ON_3dPoint this_end = trim.PointAtEnd(); // c2->PointAt( trim.m_t[1] );
ON_3dPoint next_start = next_trim.PointAtStart(); //prev_c2->PointAt( next_trim.m_t[0] );
for ( dir = 0; dir < 2; dir++ )
{
if ( trim.m_tolerance[dir] < 0.0 || !bLazy )
{
ds = fabs(prev_end[dir] - this_start[dir] );
de = fabs(this_end[dir] - next_start[dir] );
d = (ds >= de) ? ds : de;
trim.m_tolerance[dir] = ( d > ON_ZERO_TOLERANCE ) ? 1.001*d : 0.0;
}
}
}
}
break;
}
}
}
}
return (trim.m_tolerance[0] >= 0.0 && trim.m_tolerance[1] >= 0.0) ? true : false;
}
bool
ON_Brep::SetEdgeTolerance( ON_BrepEdge& edge, bool bLazySet ) const
{
if ( edge.m_tolerance < 0.0 || !bLazySet )
{
const int edge_trim_count = edge.m_ti.Count();
if ( edge_trim_count < 1 )
{
edge.m_tolerance = 0.0;
}
else
{
edge.m_tolerance = ON_UNSET_VALUE;
// TL_Brep::SetEdgeTolerance overrides ON_Brep::SetEdgeTolerance
// and sets teh tolerance correctly.
}
}
return (edge.m_tolerance >= 0.0) ? true : false;
}
bool ON_Brep::SetTrimTolerances( bool bLazy )
{
bool rc = true;
int ti, trim_count = m_T.Count();
for ( ti = 0; ti < trim_count; ti++ )
{
if ( !SetTrimTolerance( m_T[ti], bLazy ) )
rc = false;
}
return rc;
}
bool ON_Brep::SetEdgeTolerances( bool bLazy )
{
bool rc = true;
int ei, edge_count = m_E.Count();
for ( ei = 0; ei < edge_count; ei++ )
{
if ( !SetEdgeTolerance( m_E[ei], bLazy ) )
rc = false;
}
return rc;
}
void ON_Brep::Dump( ON_TextLog& dump ) const
{
dump.Print("ON_Brep:\n");
if ( IsSurface() ) {
dump.Print("(B-rep geometry is the same as underlying surface.)\n");
}
dump.Print("surfaces: %d\n",m_S.Count());
dump.Print("3d curve: %d\n",m_C3.Count());
dump.Print("2d curves: %d\n",m_C2.Count());
dump.Print("vertices: %d\n",m_V.Count());
dump.Print("edges: %d\n",m_E.Count());
dump.Print("trims: %d\n",m_T.Count());
dump.Print("loops: %d\n",m_L.Count());
dump.Print("faces: %d\n",m_F.Count());
if (nullptr != m_region_topology)
{
dump.Print("regions: %d\n", m_region_topology->m_R.Count());
dump.Print("face sides: %d\n", m_region_topology->m_FS.Count());
}
int c2i;
for ( c2i = 0; c2i < m_C2.Count(); c2i++ )
{
const ON_Curve* c2 = m_C2[c2i];
if ( c2 )
{
ON_Interval cdom = c2->Domain();
ON_3dPoint c_start, c_end;
c_start = c2->PointAtStart();
c_end = c2->PointAtEnd();
const char* s = c2->ClassId()->ClassName();
if ( !s )
s = "";
dump.Print("curve2d[%2d]: %s domain(%g,%g) start(%g,%g) end(%g,%g)\n",
c2i, s, cdom[0], cdom[1],
c_start.x, c_start.y,
c_end.x, c_end.y);
}
else
{
dump.Print("curve2d[%2d]: nullptr\n",c2i);
}
}
int c3i;
for ( c3i = 0; c3i < m_C3.Count(); c3i++ )
{
const ON_Curve* c3 = m_C3[c3i];
if ( c3 )
{
ON_Interval cdom = c3->Domain();
ON_3dPoint c_start, c_end;
c_start = c3->PointAtStart();
c_end = c3->PointAtEnd();
const char* s = c3->ClassId()->ClassName();
if ( !s )
s = "";
dump.Print("curve3d[%2d]: %s domain(%g,%g) start(%g,%g,%g) end(%g,%g,%g)\n",
c3i, s, cdom[0], cdom[1],
c_start.x, c_start.y, c_start.z,
c_end.x, c_end.y, c_end.z);
}
else
{
dump.Print("curve2d[%2d]: nullptr\n",c3i);
}
}
int si;
for ( si = 0; si < m_S.Count(); si++ )
{
const ON_Surface* srf = m_S[si];
if ( srf )
{
ON_Interval udom = srf->Domain(0);
ON_Interval vdom = srf->Domain(1);
const char* s = srf->ClassId()->ClassName();
if ( !s )
s = "";
dump.Print("surface[%2d]: %s u(%g,%g) v(%g,%g)\n",
si, s,
udom[0], udom[1],
vdom[0], vdom[1]
);
if ( m_S.Count() == 1 && IsSurface() )
{
dump.PushIndent();
dump.Print("surface details:\n");
dump.PushIndent();
srf->Dump(dump);
dump.PopIndent();
dump.PopIndent();
}
}
else
{
dump.Print("surface[%2d]: nullptr\n",si);
}
}
int vi;
for ( vi = 0; vi < m_V.Count(); vi++ ) {
const ON_BrepVertex& vertex = m_V[vi];
dump.Print("vertex[%2d]: (%f %f %f) tolerance(%g)\n",
vi,vertex.point.x,vertex.point.y,vertex.point.z,
vertex.m_tolerance);
if ( vertex.m_ei.Count() > 0 ) {
int vei;
dump.PushIndent();
dump.Print("edges (");
for ( vei = 0; vei < vertex.m_ei.Count(); vei++ ) {
dump.Print( (vei)?",%d":"%d", vertex.m_ei[vei] );
}
dump.PopIndent();
dump.Print(")\n");
}
}
int ei,ti;
for ( ei = 0; ei < m_E.Count(); ei++ ) {
const ON_BrepEdge& edge = m_E[ei];
dump.Print("edge[%2d]: v0(%2d) v1(%2d) 3d_curve(%d) tolerance(%g)\n",
ei,edge.m_vi[0],edge.m_vi[1],edge.m_c3i,edge.m_tolerance);
dump.PushIndent();
const ON_Curve* c3 = edge.EdgeCurveOf();
if ( c3 )
{
ON_3dPoint edge_start = edge.PointAtStart();
ON_3dPoint edge_end = edge.PointAtEnd();
dump.Print("domain(%g,%g) start(%g,%g,%g) end(%g,%g,%g)\n",
edge.Domain()[0],edge.Domain()[1],
edge_start.x,edge_start.y,edge_start.z,
edge_end.x,edge_end.y,edge_end.z
);
}
else
{
dump.Print("domain(%g,%g) start(?,?,?) end(?,?,?)\n",
edge.Domain()[0],edge.Domain()[1]);
}
if ( edge.m_ti.Count() > 0 )
{
dump.Print("trims (",edge.m_ti.Count());
int eti;
for ( eti = 0; eti < edge.m_ti.Count(); eti++ )
{
ti = edge.m_ti[eti];
const char* sign = "?";
if ( ti >= 0 && ti < m_T.Count() ) {
sign = m_T[ti].m_bRev3d ? "-" : "+";
}
dump.Print( (eti)?",%s%d":"%s%d", sign,edge.m_ti[eti]);
}
dump.Print(")\n");
}
dump.PopIndent();
}
int fi;
for ( fi = 0; fi < m_F.Count(); fi++ ) {
const ON_BrepFace& face = m_F[fi];
const ON_Surface* face_srf = face.SurfaceOf();
dump.Print("face[%2d]: surface(%d) reverse(%d) loops(",
fi,face.m_si,face.m_bRev);
int fli;
for ( fli = 0; fli < face.m_li.Count(); fli++ ) {
dump.Print( (fli)?",%d":"%d", face.m_li[fli]);
}
dump.Print(")\n");
dump.PushIndent();
if (auto sp = face.SharedMesh(ON::render_mesh))
{
const ON_MeshParameters* mp = sp->MeshParameters();
ON_wString mp_description = (nullptr != mp) ? mp->Description() : ON_wString(L"Unknown");
dump.Print(L"Render mesh = %ls. %d polygons\n", static_cast<const wchar_t*>(mp_description), sp->FaceCount());
}
else
{
dump.Print("Render mesh = nullptr\n");
}
if (auto sp = face.SharedMesh(ON::analysis_mesh))
{
const ON_MeshParameters* mp = sp->MeshParameters();
ON_wString mp_description = (nullptr != mp) ? mp->Description() : ON_wString(L"Unknown");
dump.Print(L"Analysis mesh = %ls. %d polygons\n", static_cast<const wchar_t*>(mp_description), sp->FaceCount());
}
else
{
dump.Print("Analysis mesh = nullptr\n");
}
if (auto sp = face.SharedMesh(ON::preview_mesh))
{
const ON_MeshParameters* mp = sp->MeshParameters();
ON_wString mp_description = (nullptr != mp) ? mp->Description() : ON_wString(L"Unknown");
dump.Print(L"Preview mesh = %ls. %d polygons\n", static_cast<const wchar_t*>(mp_description), sp->FaceCount());
}
else
dump.Print("Preview mesh = nullptr\n");
if ( FaceIsSurface(fi) ) {
dump.Print("(Face geometry is the same as underlying surface.)\n");
}
for ( fli = 0; fli < face.m_li.Count(); fli++ )
{
const int li = face.m_li[fli];
const ON_BrepLoop& loop = m_L[li];
const char* sLoopType = 0;
switch( loop.m_type )
{
case ON_BrepLoop::unknown:
sLoopType = "unknown";
break;
case ON_BrepLoop::outer:
sLoopType = "outer";
break;
case ON_BrepLoop::inner:
sLoopType = "inner";
break;
case ON_BrepLoop::slit:
sLoopType = "slit";
break;
case ON_BrepLoop::crvonsrf:
sLoopType = "crvonsrf";
break;
default:
sLoopType = "unknown";
break;
}
dump.Print("loop[%2d]: type(%s) %d trims(",
li, sLoopType, loop.m_ti.Count());
int lti;
for ( lti = 0; lti < loop.m_ti.Count(); lti++ )
{
dump.Print( (lti)?",%d":"%d", loop.m_ti[lti]);
}
dump.Print(")\n");
dump.PushIndent();
for ( lti = 0; lti < loop.m_ti.Count(); lti++ )
{
const int ti_for_loop = loop.m_ti[lti];
const ON_BrepTrim& trim = m_T[ti_for_loop];
const char* sTrimType = "?";
const char* sTrimIso = "-?";
const ON_Curve* c2 = trim.TrimCurveOf();
ON_3dPoint trim_start, trim_end;
switch( trim.m_type ) {
case ON_BrepTrim::unknown:
sTrimType = "unknown ";
break;
case ON_BrepTrim::boundary:
sTrimType = "boundary";
break;
case ON_BrepTrim::mated:
sTrimType = "mated ";
break;
case ON_BrepTrim::seam:
sTrimType = "seam ";
break;
case ON_BrepTrim::singular:
sTrimType = "singular";
break;
case ON_BrepTrim::crvonsrf:
sTrimType = "crvonsrf";
break;
default:
sTrimType = "unknown";
break;
}
switch( trim.m_iso ) {
case ON_Surface::not_iso:
sTrimIso = "";
break;
case ON_Surface::x_iso:
sTrimIso = "-u iso";
break;
case ON_Surface::W_iso:
sTrimIso = "-west side iso";
break;
case ON_Surface::E_iso:
sTrimIso = "-east side iso";
break;
case ON_Surface::y_iso:
sTrimIso = "-v iso";
break;
case ON_Surface::S_iso:
sTrimIso = "-south side iso";
break;
case ON_Surface::N_iso:
sTrimIso = "-north side iso";
break;
default:
sTrimIso = "-unknown_iso_flag";
break;
}
dump.Print("trim[%2d]: edge(%2d) v0(%2d) v1(%2d) tolerance(%g,%g)\n",
ti_for_loop,
trim.m_ei,trim.m_vi[0],trim.m_vi[1],
trim.m_tolerance[0],trim.m_tolerance[1]);
dump.PushIndent();
dump.Print("type(%s%s) rev3d(%d) 2d_curve(%d)\n",
sTrimType, sTrimIso, trim.m_bRev3d, trim.m_c2i);
if ( c2 )
{
trim_start = trim.PointAtStart();
trim_end = trim.PointAtEnd();
dump.Print("domain(%g,%g) start(%g,%g) end(%g,%g)\n",
trim.Domain()[0],trim.Domain()[1],
trim_start.x,trim_start.y,
trim_end.x,trim_end.y);
if ( 0 != face_srf )
{
ON_3dPoint trim_srfstart = face_srf->PointAt(trim_start.x,trim_start.y);
ON_3dPoint trim_srfend = face_srf->PointAt(trim_end.x,trim_end.y);
dump.Print("surface points start(%g,%g,%g) end(%g,%g,%g)\n",
trim_srfstart.x,trim_srfstart.y,trim_srfstart.z,
trim_srfend.x,trim_srfend.y,trim_srfend.z);
}
}
else
{
dump.Print("domain(%g,%g) start(?,?) end(?,?)\n",
trim.Domain()[0],trim.Domain()[1]);
}
dump.PopIndent();
}
dump.PopIndent();
}
dump.PopIndent();
}
//int si;
//for ( si = 0; si < m_S.Count(); si++ ) {
// dump.Print("surface[%d]:\n",si);
// dump.PushIndent();
// if ( m_S[si] )
// m_S[si]->Dump(dump);
// else
// dump.Print("nullptr\n");
// dump.PopIndent();
//}
}
//int ON_Brep::FaceIndexOf( const ON_BrepTrim& trim ) const
//{
// int fi = -1;
// if ( trim.m_li >= 0 && trim.m_li < m_L.Count() )
// {
// fi = m_L[trim.m_li].m_fi;
// if ( fi < 0 || fi >= m_F.Count() )
// fi = -1;
// }
// return fi;
//}
//int ON_Brep::FaceIndexOf( const ON_BrepLoop& loop ) const
//{
// int fi = -1;
// if ( loop.m_fi >= 0 && loop.m_fi < m_F.Count() )
// fi = loop.m_fi;
// return fi;
//}
//const ON_BrepFace* ON_Brep::FaceOf( const ON_BrepTrim& trim ) const
//{
// // OBSOLETE
// return trim.Face();
//}
//const ON_BrepFace* ON_Brep::FaceOf( const ON_BrepLoop& loop ) const
//{
// // OBSOLETE
// return loop.Face();
//}
//int ON_Brep::SurfaceIndexOf( const ON_BrepTrim& trim ) const
//{
// // OBSOLETE
// return trim.SurfaceIndexOf();
//}
//int ON_Brep::SurfaceIndexOf( const ON_BrepLoop& loop ) const
//{
// // OBSOLETE
// return loop.SurfaceIndexOf();
//}
//int ON_Brep::SurfaceIndexOf( const ON_BrepFace& face ) const
//{
// // OBSOLETE FUNCTION
// return face.m_si;
//}
//ON_Surface* ON_Brep::SurfaceOf( const ON_BrepTrim& trim ) const
//{
// // OBSOLETE FUNCTION
// const int si = trim.SurfaceIndexOf();
// return (si>=0)?m_S[si]:0;
//}
//ON_Surface* ON_Brep::SurfaceOf( const ON_BrepLoop& loop ) const
//{
// // OBSOLETE FUNCTION
// return const_cast<ON_Surface*>(loop.SurfaceOf());
//}
//ON_Surface* ON_Brep::SurfaceOf( const ON_BrepFace& face ) const
//{
// // OBSOLETE FUNCTION
// return const_cast<ON_Surface*>(face.SurfaceOf());
//}
//int ON_Brep::EdgeCurveIndexOf( const ON_BrepTrim& trim ) const
//{
// // OBSOLETE FUNCTION
// return trim.EdgeCurveIndexOf();
//}
//int ON_Brep::EdgeCurveIndexOf( const ON_BrepEdge& edge ) const
//{
// // OBSOLETE FUNCTION
// return edge.m_c3i;
//}
//ON_Curve* ON_Brep::EdgeCurveOf( const ON_BrepTrim& trim ) const
//{
// // OBSOLETE FUNCTION
// return const_cast<ON_Curve*>(trim.EdgeCurveOf());
//}
//ON_Curve* ON_Brep::EdgeCurveOf( const ON_BrepEdge& edge ) const
//{
// return ((edge.m_c3i>=0&&edge.m_c3i<m_C3.Count())?m_C3[edge.m_c3i]:0);
//}
//int ON_Brep::TrimCurveIndexOf( const ON_BrepTrim& trim ) const
//{
// return trim.m_c2i;
//}
//ON_Curve* ON_Brep::TrimCurveOf( const ON_BrepTrim& trim ) const
//{
// return ((trim.m_c2i>=0&&trim.m_c2i<m_C2.Count())?m_C2[trim.m_c2i]:0);
//}
void ON_Brep::DeleteVertex(ON_BrepVertex& vertex)
{
const int vi = vertex.m_vertex_index;
vertex.m_vertex_index = -1;
if ( vi >= 0 && vi < m_V.Count() ) {
int vei, ei;
for ( vei = vertex.m_ei.Count()-1; vei>=0; vei-- ) {
ei = vertex.m_ei[vei];
if ( ei >= 0 && ei < m_E.Count() ) {
ON_BrepEdge& edge = m_E[ei];
if ( edge.m_vi[0] == vi )
edge.m_vi[0] = -1;
if ( edge.m_vi[1] == vi )
edge.m_vi[1] = -1;
DeleteEdge( edge, false );
}
}
}
vertex.m_ei.Empty();
vertex.m_tolerance = ON_UNSET_VALUE;
}
int ON_Brep::Loop3dCurve(
const ON_BrepLoop& loop,
ON_SimpleArray<ON_Curve*>& curve_list,
bool bRevCurveIfFaceRevIsTrue
) const
{
int curve_list_count0 = curve_list.Count();
ON_PolyCurve* poly_curve = nullptr;
ON_Curve* loop_curve = nullptr;
ON_SimpleArray<int> trim_index( 2*loop.m_ti.Count() + 8);
int i, lti, ti;
int loop_trim_count = loop.m_ti.Count();
if ( loop_trim_count < 1 )
return 0;
int seam_lti = -1; // index of first seam
int wire_lti = -1;
for ( lti = 0; lti < loop_trim_count; lti++ )
{
ti = loop.m_ti[lti];
if ( ti >= 0 && ti < m_T.Count() )
{
const ON_BrepTrim& trim = m_T[ti];
if ( seam_lti < 0 && trim.m_type == ON_BrepTrim::seam )
seam_lti = lti;
else if ( wire_lti < 0 && trim.m_type != ON_BrepTrim::singular )
wire_lti = lti;
}
}
if ( wire_lti < 0 )
return 0; // sphere boundary, torus boundary, etc.
if ( seam_lti < 0 )
{
// simple case;
loop_curve = Loop3dCurve(loop,bRevCurveIfFaceRevIsTrue);
if ( loop_curve )
curve_list.Append(loop_curve);
return curve_list.Count() - curve_list_count0;
}
bool bOnSeam = true;
for ( lti = seam_lti; lti < seam_lti+loop_trim_count; lti++ )
{
ti = loop.m_ti[lti%loop_trim_count];
if ( ti < 0 || ti >= m_T.Count() )
ti = loop.m_ti[seam_lti]; // treat bogus indices as trims
const ON_BrepTrim& trim = m_T[ti];
if ( trim.m_type == ON_BrepTrim::seam )
{
if (!bOnSeam)
{
trim_index.Append(-1);
bOnSeam = true;
}
continue;
}
// 10-1-03 Lowell - fixed typo
if ( trim.m_type == ON_BrepTrim::singular )
continue;
bOnSeam = false;
trim_index.Append(ti);
}
for ( i = 0; i < trim_index.Count(); i++ )
{
ti = trim_index[i];
if ( ti < 0 )
{
if ( loop_curve )
curve_list.Append(loop_curve);
loop_curve = 0;
poly_curve = 0;
continue;
}
// get 3d curve associated with this trim's edge
const ON_BrepTrim& trim = m_T[ti];
const ON_BrepEdge& edge = m_E[trim.m_ei];
ON_Curve* segment_curve = edge.DuplicateCurve();
if ( !segment_curve )
continue;
if ( trim.m_bRev3d )
segment_curve->Reverse();
if ( !loop_curve )
loop_curve = segment_curve;
else if ( !poly_curve )
{
poly_curve = new ON_PolyCurve();
poly_curve->Append(loop_curve);
poly_curve->Append(segment_curve);
loop_curve = poly_curve;
}
else
poly_curve->Append( segment_curve );
}
// 10-1-03 Lowell - add the last non-seam segment
if ( loop_curve )
curve_list.Append(loop_curve);
if ( bRevCurveIfFaceRevIsTrue )
{
int fi = loop.m_fi;
if ( fi >= 0 && fi < m_F.Count() && m_F[fi].m_bRev )
{
for ( i = curve_list_count0; i < curve_list.Count(); i++ )
curve_list[i]->Reverse();
}
}
return curve_list.Count() - curve_list_count0;
}
ON_Curve* ON_Brep::Loop3dCurve( const ON_BrepLoop& loop, bool bRevCurveIfFaceRevIsTrue ) const
{
ON_PolyCurve* poly_curve = nullptr;
ON_Curve* loop_curve = nullptr;
ON_SimpleArray<int> trim_index( loop.m_ti.Count() );
int i, lti, ti;
for ( lti = 0; lti < loop.m_ti.Count(); lti++ )
{
ti = loop.m_ti[lti];
if ( ti >= 0 && ti < m_T.Count() )
{
const ON_BrepTrim& trim = m_T[ti];
if ( trim.EdgeCurveOf() )
trim_index.Append(ti);
}
}
for ( i = 0; i < trim_index.Count(); i++ )
{
// get 3d curve associated with this trim's edge
const ON_BrepTrim& trim = m_T[trim_index[i]];
const ON_BrepEdge& edge = m_E[trim.m_ei];
ON_Curve* segment_curve = edge.DuplicateCurve();
if ( !segment_curve )
continue;
if ( trim.m_bRev3d )
segment_curve->Reverse();
if ( !loop_curve )
loop_curve = segment_curve;
else if ( !poly_curve )
{
poly_curve = new ON_PolyCurve();
poly_curve->Append(loop_curve);
poly_curve->Append(segment_curve);
loop_curve = poly_curve;
}
else
poly_curve->Append( segment_curve );
}
if ( loop_curve && bRevCurveIfFaceRevIsTrue )
{
int fi = loop.m_fi;
if ( fi >= 0 && fi < m_F.Count() && m_F[fi].m_bRev )
{
loop_curve->Reverse();
}
}
return loop_curve;
}
ON_Curve* ON_Brep::Loop2dCurve( const ON_BrepLoop& loop ) const
{
ON_PolyCurve* poly_curve = nullptr;
ON_Curve* loop_curve = nullptr;
ON_SimpleArray<int> trim_index( loop.m_ti.Count() );
int i, lti, ti;
for ( lti = 0; lti < loop.m_ti.Count(); lti++ )
{
ti = loop.m_ti[lti];
if ( ti >= 0 && ti < m_T.Count() )
{
const ON_BrepTrim& trim = m_T[ti];
if ( trim.TrimCurveOf() )
trim_index.Append(ti);
}
}
for ( i = 0; i < trim_index.Count(); i++ )
{
// get 2d curve associated with this trim's edge
const ON_BrepTrim& trim = m_T[trim_index[i]];
ON_Curve* segment_curve = trim.DuplicateCurve();
if ( !segment_curve )
continue;
if ( !loop_curve )
loop_curve = segment_curve;
else if ( !poly_curve )
{
poly_curve = new ON_PolyCurve();
poly_curve->Append(loop_curve);
poly_curve->Append(segment_curve);
loop_curve = poly_curve;
}
else
poly_curve->Append( segment_curve );
}
return loop_curve;
}
void ON_Brep::DeleteEdge(ON_BrepEdge& edge, bool bDeleteEdgeVertices )
{
const int ei = edge.m_edge_index;
edge.m_edge_index = -1;
if ( ei >= 0 && ei < m_E.Count() ) {
int eti, ti, evi, vei, vi;
for ( eti = edge.m_ti.Count()-1; eti >= 0; eti-- ) {
ti = edge.m_ti[eti];
if ( ti >= 0 && ti < m_T.Count() ) {
ON_BrepTrim& trim = m_T[ti];
trim.m_ei = -1;
if ( trim.m_li >= 0 && trim.m_li < m_L.Count() ) {
ON_BrepLoop& loop = m_L[trim.m_li];
if ( loop.m_fi >= 0 && loop.m_fi < m_F.Count() ) {
DeleteFace( m_F[loop.m_fi], bDeleteEdgeVertices );
}
}
DeleteTrim(trim,false);
}
}
for (evi = 0; evi < 2; evi++ )
{
vi = edge.m_vi[evi];
if ( vi >= 0 && vi < m_V.Count() )
{
ON_BrepVertex& v = m_V[vi];
for ( vei = v.m_ei.Count()-1; vei >= 0; vei-- )
{
if ( v.m_ei[vei] == ei )
v.m_ei.Remove(vei);
}
if ( bDeleteEdgeVertices && v.m_ei.Count() <= 0 )
{
v.m_ei.Destroy();
DeleteVertex(v);
}
}
}
}
edge.m_c3i = -1;
edge.m_vi[0] = -1;
edge.m_vi[1] = -1;
edge.m_ti.Empty();
edge.m_tolerance = ON_UNSET_VALUE;
edge.m_brep = 0;
edge.SetProxyCurve(0);
}
void ON_Brep::DeleteTrim(ON_BrepTrim& trim, bool bDeleteTrimEdges )
{
m_is_solid = 0;
const int ti = trim.m_trim_index;
trim.m_trim_index = -1;
if ( ti >= 0 && ti < m_T.Count() )
{
const int ei = trim.m_ei;
if ( ei >= 0 && ei < m_E.Count() )
{
ON_BrepEdge& edge = m_E[ei];
if ( bDeleteTrimEdges && edge.m_ti.Count() == 1 && edge.m_ti[0] == ti )
{
edge.m_ti.Empty();
DeleteEdge(edge,bDeleteTrimEdges);
}
else
{
int mate_ti = (trim.m_type == ON_BrepTrim::mated) ? -1 : -2; // set to >= 0 if a single mate exists
int seam_ti = (trim.m_type == ON_BrepTrim::seam) ? -1 : -2; // set to >= 0 if a single seam partner exists
int eti;
for ( eti = edge.m_ti.Count()-1; eti >= 0; eti-- )
{
int other_ti = edge.m_ti[eti];
if ( other_ti == ti )
{
edge.m_ti.Remove(eti);
if ( edge.m_ti.Count() == 0 )
edge.m_tolerance = 0.0;
continue;
}
if ( (mate_ti >= -1 || seam_ti >= -1 ) && other_ti >= 0 && other_ti < m_T.Count() )
{
const ON_BrepTrim& other_trim = m_T[other_ti];
if ( other_trim.m_trim_index != other_ti )
continue;
if ( mate_ti >= -1
&& (other_trim.m_type == ON_BrepTrim::mated || other_trim.m_type == ON_BrepTrim::slit || (other_trim.m_type == ON_BrepTrim::seam && other_trim.m_li != trim.m_li) )
)
{
// see if other_trim is the only mate of trim
if ( mate_ti == -1 )
mate_ti = other_ti;
else
mate_ti = -2;
}
else if ( seam_ti >= -1 && other_trim.m_type == ON_BrepTrim::seam && other_trim.m_li == trim.m_li )
{
// trim and other_trim are both seam trims in the same loop connected
// to the same edge.
if ( seam_ti == -1 )
seam_ti = other_ti;
else
seam_ti = -2;
}
}
}
if ( seam_ti >= 0 )
{
// m_T[seam_ti] used to be a seam partner with trim.
// Now it is either a boundary trim or is mated to m_T[mate_ti]
m_T[seam_ti].m_type = (mate_ti>=0)
? ON_BrepTrim::mated // m_T[mate_ti] is mated to m_T[seam_ti]
: ON_BrepTrim::boundary; // m_T[seam_ti] is all alone
}
else if ( mate_ti >= 0 )
{
// m_T[mate_ti] just lost its only mate and is now a boundary
m_T[mate_ti].m_type = ON_BrepTrim::boundary;
}
}
}
const int li = trim.m_li;
if ( li >= 0 && li < m_L.Count() ) {
ON_BrepLoop& loop = m_L[li];
int lti;
for ( lti = loop.m_ti.Count()-1; lti >= 0; lti-- ) {
if ( loop.m_ti[lti] == ti )
loop.m_ti.Remove(lti);
}
}
}
trim.m_c2i = -1;
trim.m_ei = -1;
trim.m_vi[0] = -1;
trim.m_vi[1] = -1;
trim.m_bRev3d = 0;
trim.m_type = ON_BrepTrim::unknown;
trim.m_iso = ON_Surface::not_iso;
trim.m_li = -1;
trim.m_tolerance[0] = ON_UNSET_VALUE;
trim.m_tolerance[1] = ON_UNSET_VALUE;
trim.m__legacy_2d_tol = ON_UNSET_VALUE;
trim.m__legacy_3d_tol = ON_UNSET_VALUE;
trim.m__legacy_flags = 0;
trim.m_pbox.Destroy();
trim.m_brep = 0;
trim.SetProxyCurve(0);
}
void ON_Brep::DeleteLoop(ON_BrepLoop& loop, bool bDeleteLoopEdges )
{
// 2 Sept 2020 S. Baer (RH-59952)
// Destroy cached bounding box on breps when messing around with loops
m_bbox.Destroy();
m_is_solid = 0;
const int li = loop.m_loop_index;
loop.m_loop_index = -1;
if ( loop.m_fi >= 0 )
DestroyMesh(ON::any_mesh);
if ( li >= 0 && li < m_L.Count() )
{
const int tcount = m_T.Count();
int lti, ti;
for ( lti = loop.m_ti.Count()-1; lti >= 0; lti-- )
{
ti = loop.m_ti[lti];
if ( ti >= 0 && ti < tcount )
{
ON_BrepTrim& trim = m_T[ti];
trim.m_li = -1;
DeleteTrim(trim,bDeleteLoopEdges);
}
}
const int fi = loop.m_fi;
if ( fi >= 0 && fi < m_F.Count() )
{
ON_BrepFace& face = m_F[fi];
int fli;
for ( fli = face.m_li.Count()-1; fli >= 0; fli-- )
{
if ( face.m_li[fli] == li )
{
face.m_li.Remove(fli);
// 2 Sept 2020 S. Baer (RH-59952)
// clear cached face bbox when a loop is removed
face.m_bbox.Destroy();
}
}
}
}
loop.m_type = ON_BrepLoop::unknown;
loop.m_ti.Empty();
loop.m_fi = -1;
loop.m_pbox.Destroy();
loop.m_brep = 0;
}
void ON_Brep::DeleteFace(ON_BrepFace& face, bool bDeleteFaceEdges )
{
m_bbox.Destroy();
m_is_solid = 0;
const int fi = face.m_face_index;
face.m_face_index = -1;
if ( fi >= 0 && fi < m_F.Count() ) {
const int lcount = m_L.Count();
int fli, li;
for ( fli = face.m_li.Count()-1; fli >= 0; fli-- ) {
li = face.m_li[fli];
if ( li >= 0 && li < lcount ) {
ON_BrepLoop& loop = m_L[li];
loop.m_fi = -1;
DeleteLoop(loop,bDeleteFaceEdges);
}
}
DestroyRegionTopology();
}
face.m_si = -1;
face.m_li.Empty();
face.SetProxySurface(0);
face.m_brep = 0;
face.m_bbox.Destroy();
}
static void PropagateLabel(const ON_Brep& B,
const ON_SimpleArray<int>& fids,
int label,
ON_SimpleArray<int>& new_fids
)
//on input, each face in fids must have m_face_user.i = label
{
if (fids.Count() == 0)
return;
new_fids.SetCount(0);
new_fids.Reserve(B.m_F.Count());
for (int face_i=0; face_i<fids.Count(); face_i++)
{
const ON_BrepFace& F = B.m_F[fids[face_i]];
for (int loop_i=0; loop_i<F.m_li.Count(); loop_i++)
{
const ON_BrepLoop& L = B.m_L[F.m_li[loop_i]];
memset(&L.m_loop_user,0,sizeof(L.m_loop_user));
L.m_loop_user.i = label;
for (int edge_i=0; edge_i<L.m_ti.Count(); edge_i++)
{
const ON_BrepTrim& T = B.m_T[L.m_ti[edge_i]];
memset(&T.m_trim_user,0,sizeof(T.m_trim_user));
T.m_trim_user.i = label;
if (T.m_ei < 0)
continue;
const ON_BrepEdge& E = B.m_E[T.m_ei];
memset(&E.m_edge_user,0,sizeof(E.m_edge_user));
E.m_edge_user.i = label;
for (int vertex_i=0; vertex_i<2; vertex_i++)
{
if (E.m_vi[vertex_i] >= 0)
{
const ON_BrepVertex& V = B.m_V[E.m_vi[vertex_i]];
memset(&V.m_vertex_user,0,sizeof(V.m_vertex_user));
V.m_vertex_user.i = label;
}
}
for (int trim_i=0; trim_i<E.m_ti.Count(); trim_i++)
{
int fi = B.m_T[E.m_ti[trim_i]].FaceIndexOf();
if (fi < 0 || B.m_F[fi].m_face_user.i == label)
continue;
const ON_BrepFace& F_local = B.m_F[fi];
memset(&F_local.m_face_user,0,sizeof(F_local.m_face_user));
F_local.m_face_user.i = label;
new_fids.Append(fi);
}
}
}
}
return;
}
static void PropagateLabel(const ON_Brep& B,
ON_SimpleArray<int>& fids,
int label
)
{
ON_SimpleArray<int> new_fids;
for (int i=0; i<B.m_F.Count(); i++){
PropagateLabel(B, fids, label, new_fids);
if (new_fids.Count() == 0)
return;
fids = new_fids;
}
}
void ON_Brep::LabelConnectedComponent(int face_index, int label) const
{
if (face_index < 0 || face_index >= m_F.Count())
return;
ON_SimpleArray<int> fids(1);
fids.Append(face_index);
const ON_BrepFace& F = m_F[face_index];
memset(&F.m_face_user,0,sizeof(F.m_face_user));
F.m_face_user.i = label;
PropagateLabel(*this, fids, label);
return;
}
int ON_Brep::LabelConnectedComponents() const
{
Clear_user_i();
int i;
for (i=0; i<m_F.Count(); i++){
if (m_F[i].m_face_index < 0)
m_F[i].m_face_user.i = -1;
}
int label = 0;
bool keep_going = true;
while (keep_going)
{
int face_index = -1;
for (int j=0; j<m_F.Count(); j++)
{
if (m_F[j].m_face_user.i == 0)
{
face_index = j;
break;
}
}
if (face_index == -1)
{
keep_going = false;
continue;
}
label++;
LabelConnectedComponent(face_index, label);
}
return label;
}
int ON_Brep::GetConnectedComponents( ON_SimpleArray< ON_Brep* >& components, bool bDuplicateMeshes ) const
{
const int count0 = components.Count();
ON_Brep brep(*this);
int count = brep.LabelConnectedComponents();
if ( count > 1 )
{
int cci;
ON_SimpleArray<int> fi(brep.m_F.Count());
for ( cci = 1; cci <= count; cci++ )
{
fi.SetCount(0);
for ( int j = 0; j < brep.m_F.Count(); j++ )
{
if ( brep.m_F[j].m_face_user.i == cci )
fi.Append(j);
}
if ( fi.Count() > 0 )
{
ON_Brep* cc = brep.DuplicateFaces( fi.Count(), fi.Array(), bDuplicateMeshes );
if ( cc )
components.Append(cc);
}
}
}
return components.Count() - count0;
}
ON_Brep* ON_Brep::DuplicateFace( int face_index, bool bDuplicateMeshes ) const
{
return DuplicateFaces( 1, &face_index, bDuplicateMeshes );
}
ON_Brep* ON_Brep::DuplicateFaces( int face_count, const int* face_index, bool bDuplicateMeshes ) const
{
int fi, si, fli, lti, li, ti, i;
bool rc = false;
ON_Brep* brep_copy = 0;
ON_Object* dup = 0;
// mark vertices, edges, faces, surfaces, and curves to duplicate
const int s_ct = m_S.Count();
ON_SimpleArray<int> s_remap(s_ct);
s_remap.SetCount(s_ct);
s_remap.Zero();
const int f_ct = m_F.Count();
ON_SimpleArray<int> f_remap(f_ct);
f_remap.SetCount(f_ct);
f_remap.Zero();
const int c2_ct = m_C2.Count();
ON_SimpleArray<int> c2_remap(c2_ct);
c2_remap.SetCount(c2_ct);
c2_remap.Zero();
const int c3_ct = m_C3.Count();
ON_SimpleArray<int> c3_remap(c3_ct);
c3_remap.SetCount(c3_ct);
c3_remap.Zero();
const int e_ct = m_E.Count();
ON_SimpleArray<int> e_remap(e_ct);
e_remap.SetCount(e_ct);
e_remap.Zero();
const int v_ct = m_V.Count();
ON_SimpleArray<int> v_remap(v_ct);
v_remap.SetCount(v_ct);
v_remap.Zero();
const int t_ct = m_T.Count();
const int l_ct = m_L.Count();
for (i = 0; i < face_count; i++ )
{
fi = face_index[i];
if (fi >= 0 && fi < f_ct )
{
const ON_BrepFace& face = m_F[fi];
rc = true;
f_remap[fi] = 1;
si = face.m_si;
if ( si >= 0 && si < s_ct )
{
s_remap[si] = 1;
}
for ( fli = 0; fli < face.m_li.Count(); fli++ )
{
li = face.m_li[fli];
if ( li < 0 || li >= l_ct )
continue;
const ON_BrepLoop& loop = m_L[li];
for ( lti = 0; lti < loop.m_ti.Count(); lti++ )
{
ti = loop.m_ti[lti];
if ( ti < 0 || ti >= t_ct )
continue;
const ON_BrepTrim& trim = m_T[ti];
if ( trim.m_ei >= 0 && trim.m_ei < e_ct )
{
int vi;
e_remap[trim.m_ei] = 1;
vi = m_E[trim.m_ei].m_vi[0];
if ( vi >= 0 && v_ct > vi)
v_remap[vi] = 1;
vi = m_E[trim.m_ei].m_vi[1];
if ( vi >= 0 && v_ct > vi)
v_remap[vi] = 1;
}
if ( trim.m_vi[0] >= 0 && v_ct > trim.m_vi[0])
v_remap[trim.m_vi[0]] = 1;
if ( trim.m_vi[1] >= 0 && v_ct > trim.m_vi[1])
v_remap[trim.m_vi[1]] = 1;
int ci = trim.EdgeCurveIndexOf();
if ( ci >= 0 && c3_ct > ci)
c3_remap[ci] = 1;
ci = trim.TrimCurveIndexOf();
if ( ci >= 0 && c2_ct > ci)
c2_remap[ci] = 1;
}
}
}
}
if ( !rc )
return nullptr;
brep_copy = new ON_Brep();
// duplicate surfaces
for ( i = 0; i < s_ct && rc; i++ )
{
if ( s_remap[i] ) {
if ( !m_S[i] )
break;
dup = m_S[i]->Duplicate();
ON_Surface* srf_copy = ON_Surface::Cast(dup);
if ( !srf_copy )
break;
s_remap[i] = brep_copy->AddSurface(srf_copy);
dup = 0;
}
else
s_remap[i] = -1;
}
rc = ( rc && i == s_ct );
// duplicate 2d curves
for ( i = 0; i < c2_ct && rc; i++ )
{
if ( c2_remap[i] ) {
if ( !m_C2[i] )
break;
dup = m_C2[i]->Duplicate();
ON_Curve* crv_copy = ON_Curve::Cast(dup);
if ( !crv_copy )
break;
c2_remap[i] = brep_copy->AddTrimCurve(crv_copy);
dup = 0;
}
else
c2_remap[i] = -1;
}
rc = ( rc && i == c2_ct );
// duplicate 3d curves
for ( i = 0; i < c3_ct && rc; i++ )
{
if ( c3_remap[i] ) {
if ( !m_C3[i] )
break;
dup = m_C3[i]->Duplicate();
ON_Curve* crv_copy = ON_Curve::Cast(dup);
if ( !crv_copy )
break;
c3_remap[i] = brep_copy->AddEdgeCurve(crv_copy);
dup = 0;
}
else
c3_remap[i] = -1;
}
rc = ( rc && i == c3_ct );
// duplicate vertices
for (i = 0; i < v_ct && rc; i++ )
{
if (v_remap[i])
{
ON_BrepVertex& vertex_copy = brep_copy->NewVertex(m_V[i].point);
memset(&vertex_copy.m_vertex_user,0,sizeof(vertex_copy.m_vertex_user));
vertex_copy.m_vertex_user.i = i;
v_remap[i] = vertex_copy.m_vertex_index;
}
else
v_remap[i] = -1;
}
rc = ( rc && i == v_ct );
// duplicate edges
for (i = 0; i < e_ct && rc; i++ )
{
if (e_remap[i])
{
const ON_BrepEdge& edge = m_E[i];
//int vi0 = edge.m_vi[0];
if ( edge.m_vi[0] < 0 || edge.m_vi[1] < 0 || edge.m_c3i < 0 )
break;
if ( v_remap[edge.m_vi[0]] < 0 || v_remap[edge.m_vi[1]] < 0 || c3_remap[edge.m_c3i] < 0)
break;
ON_BrepEdge& edge_copy = brep_copy->NewEdge( brep_copy->m_V[v_remap[edge.m_vi[0]]],
brep_copy->m_V[v_remap[edge.m_vi[1]]],
c3_remap[edge.m_c3i]
);
edge_copy.SetProxyCurveDomain( edge.ProxyCurveDomain());
if ( edge.ProxyCurveIsReversed() )
edge_copy.ON_CurveProxy::Reverse();
edge_copy.SetDomain(edge.Domain());
memset(&edge_copy.m_edge_user,0,sizeof(edge_copy.m_edge_user));
edge_copy.m_edge_user.i = i;
edge_copy.m_tolerance = edge.m_tolerance;
e_remap[i] = edge_copy.m_edge_index;
}
else
e_remap[i] = -1;
}
rc = ( rc && i == e_ct );
//03/11/2010 Tim
//More checking to prevent crashes
//from bogus array indices
bool bFoundBadIdx = false;
// duplicate faces
for ( fi = 0; rc && fi < f_ct && rc && !bFoundBadIdx; fi++ )
{
if ( f_remap[fi] == 0 )
continue;
rc = false;
const ON_BrepFace& face = m_F[fi];
// duplicate face
si = (face.m_si>=0 && s_ct > face.m_si) ? s_remap[face.m_si] : -1;
if ( si < 0 )
break;
ON_BrepFace& face_copy = brep_copy->NewFace(si);
memset(&face_copy.m_face_user,0,sizeof(face_copy.m_face_user));
face_copy.m_face_user.i = fi;
face_copy.m_bRev = face.m_bRev;
face_copy.m_bbox = face.m_bbox;
face_copy.m_domain[0] = face.m_domain[0];
face_copy.m_domain[1] = face.m_domain[1];
face_copy.m_per_face_color = face.m_per_face_color;
face_copy.SetMaterialChannelIndex(face.MaterialChannelIndex());
// do NOT duplicate meshes here
// duplicate loops and trims
for ( fli = 0; fli < face.m_li.Count() && !bFoundBadIdx; fli++ )
{
li = face.m_li[fli];
if (0 > li || l_ct <= li)
{
bFoundBadIdx = true;
break;
}
const ON_BrepLoop& loop = m_L[li];
ON_BrepLoop& loop_copy = brep_copy->NewLoop( loop.m_type, face_copy );
memset(&loop_copy.m_loop_user,0,sizeof(loop_copy.m_loop_user));
loop_copy.m_loop_user.i = li;
for ( lti = 0; lti < loop.m_ti.Count() && !bFoundBadIdx; lti++ )
{
ti = loop.m_ti[lti];
if (0 > ti || t_ct <= ti)
{
bFoundBadIdx = true;
break;
}
const ON_BrepTrim& trim = m_T[ti];
i = (trim.m_c2i >= 0 && c2_ct > trim.m_c2i) ? c2_remap[trim.m_c2i] : -1;
if ( trim.m_ei >= 0 && e_ct > trim.m_ei)
{
i = brep_copy->NewTrim( brep_copy->m_E[e_remap[trim.m_ei]], trim.m_bRev3d, loop_copy, i ).m_trim_index;
}
else
{
i = brep_copy->NewTrim( trim.m_bRev3d, loop_copy, i ).m_trim_index;
int vi0 = (trim.m_vi[0] >= 0 && v_ct > trim.m_vi[0]) ? v_remap[trim.m_vi[0]] : -1;
int vi1 = (trim.m_vi[1] >= 0 && v_ct > trim.m_vi[1]) ? v_remap[trim.m_vi[1]] : -1;
brep_copy->m_T[i].m_vi[0] = vi0;
brep_copy->m_T[i].m_vi[1] = vi1;
}
ON_BrepTrim& trim_copy = brep_copy->m_T[i];
//trim_copy.m_t = trim.m_t;
trim_copy.SetProxyCurveDomain( trim.ProxyCurveDomain());
if ( trim.ProxyCurveIsReversed() )
trim_copy.ON_CurveProxy::Reverse();
trim_copy.SetDomain(trim.Domain());
memset(&trim_copy.m_trim_user,0,sizeof(trim_copy.m_trim_user));
trim_copy.m_trim_user.i = ti;
trim_copy.m_iso = trim.m_iso;
trim_copy.m_tolerance[0] = trim.m_tolerance[0];
trim_copy.m_tolerance[1] = trim.m_tolerance[1];
trim_copy.m_pline = trim.m_pline;
trim_copy.m_pbox = trim.m_pbox;
trim_copy.m__legacy_2d_tol = trim.m__legacy_2d_tol;
trim_copy.m__legacy_3d_tol = trim.m__legacy_3d_tol;
trim_copy.m__legacy_flags = trim.m__legacy_flags;
}
if (bFoundBadIdx)
break;
loop_copy.m_pbox = loop.m_pbox;
}
if (bFoundBadIdx)
break;
if ( bDuplicateMeshes )
{
{
auto sp = face.SharedMesh(ON::render_mesh);
if (sp)
{
face_copy.SetMesh(ON::render_mesh, new ON_Mesh(*sp.get()));
}
}
{
auto sp = face.SharedMesh(ON::analysis_mesh);
if (sp)
{
face_copy.SetMesh(ON::analysis_mesh, new ON_Mesh(*sp.get()));
}
}
{
auto sp = face.SharedMesh(ON::preview_mesh);
if (sp)
{
face_copy.SetMesh(ON::preview_mesh, new ON_Mesh(*sp.get()));
}
}
}
rc = true;
}
rc = ( rc && fi == m_F.Count() );
if ( !rc ) {
if ( dup ) {
delete dup;
dup = 0;
}
if ( brep_copy ) {
delete brep_copy;
brep_copy = 0;
}
}
else
{
// set flags, tolerances, etc. that have changed
brep_copy->SetTrimTypeFlags();
brep_copy->SetVertexTolerances();
}
return brep_copy;
}
ON_Brep* ON_Brep::ExtractFace( int face_index )
{
ON_Brep* brep_copy = DuplicateFace(face_index,false);
if ( brep_copy ) {
ON_BrepFace& face = m_F[face_index];
ON_BrepFace& face_copy = brep_copy->m_F[0];
face_copy.SetMesh(ON::render_mesh, face.SharedMesh(ON::render_mesh));
face_copy.SetMesh(ON::analysis_mesh, face.SharedMesh(ON::analysis_mesh));
face_copy.SetMesh(ON::preview_mesh, face.SharedMesh(ON::preview_mesh));
DeleteFace( face, true );
}
return brep_copy;
}
void ON_Brep::DeleteSurface(int si)
{
if ( si >= 0 && si < m_S.Count() ) {
delete m_S[si];
m_S[si] = 0;
}
}
void ON_Brep::Delete2dCurve(int c2i)
{
if ( c2i >= 0 && c2i < m_C2.Count() ) {
delete m_C2[c2i];
m_C2[c2i] = 0;
}
}
void ON_Brep::Delete3dCurve(int c3i)
{
if ( c3i >= 0 && c3i < m_C3.Count() ) {
delete m_C3[c3i];
m_C3[c3i] = 0;
}
}
bool ON_Brep::CullUnusedFaces()
{
bool rc = true;
int fcount = m_F.Count();
if (fcount > 0 ) {
ON_Workspace ws;
int *fmap = ws.GetIntMemory(fcount+1);
*fmap++ = -1;
memset( fmap, 0, fcount*sizeof(*fmap) );
const int lcount = m_L.Count();
int fi, li, mi = 0;
// if face.m_face_index is -1, cull face
for ( fi = 0; fi < fcount; fi++ ) {
ON_BrepFace& face = m_F[fi];
if ( face.m_face_index == -1)
fmap[fi] = -1;
else if ( face.m_face_index == fi )
fmap[fi] = face.m_face_index = mi++;
else {
ON_ERROR("Brep face has illegal m_face_index.");
rc = false;
fmap[fi] = face.m_face_index;
}
}
if ( mi == 0 ) {
m_F.Destroy();
}
else if ( mi < fcount ) {
// set new face indices
mi = 0;
for ( fi = fcount-1; fi >= 0; fi-- ) {
if ( m_F[fi].m_face_index == -1 )
m_F.Remove(fi);
else
m_F[fi].m_face_index = fmap[fi];
}
// remap loop.m_fi indices
for ( li = 0; li < lcount; li++ ) {
ON_BrepLoop& loop = m_L[li];
fi = loop.m_fi;
if ( fi < -1 || fi >= fcount ) {
ON_ERROR("Brep loop has illegal m_fi.");
rc = false;
}
else
loop.m_fi = fmap[fi];
}
}
}
m_F.Shrink();
if (m_F.Count() < fcount)
DestroyRegionTopology();
return rc;
}
bool ON_Brep::CullUnusedSurfaces()
{
// remove unused surfaces
bool rc = true;
const int fcount = m_F.Count();
int scount = m_S.Count();
int si, fi, mi;
if ( scount > 0 ) {
ON_Workspace ws;
int* smap = ws.GetIntMemory(scount+1);
*smap++ = -1;
memset(smap,0,scount*sizeof(*smap));
mi = 0;
for ( fi = 0; fi < fcount; fi++ ) {
ON_BrepFace& face = m_F[fi];
if ( face.m_face_index == -1 ) {
face.m_si = -1;
continue;
}
si = face.m_si;
if ( si == -1 )
continue;
if ( si < 0 || si >= scount ) {
ON_ERROR("Brep face has illegal m_si.");
rc = false;
}
else {
if ( !smap[si] )
mi++;
smap[si]++;
}
}
if ( mi == 0 ) {
m_S.Destroy();
}
else if ( mi < scount ) {
mi = 0;
for ( si = 0; si < scount; si++ ) {
if ( smap[si] )
smap[si] = mi++;
else {
delete m_S[si];
m_S[si] = 0;
smap[si] = -1;
}
}
for ( fi = 0; fi < fcount; fi++ ) {
ON_BrepFace& face = m_F[fi];
si = face.m_si;
if ( si >= 0 && si < scount )
face.m_si = smap[si];
}
for ( si = scount-1; si >= 0; si-- ) {
if ( smap[si] < 0 ) {
m_S.Remove(si);
scount--;
}
}
}
}
m_S.Shrink();
return rc;
}
bool ON_Brep::CullUnused3dCurves()
{
// remove unused surfaces
bool rc = true;
const int ecount = m_E.Count();
int c3count = m_C3.Count();
int c3i, ei, mi;
if ( c3count > 0 ) {
ON_Workspace ws;
int* c3map = ws.GetIntMemory(c3count+1);
*c3map++ = -1;
memset(c3map,0,c3count*sizeof(*c3map));
mi = 0;
for ( ei = 0; ei < ecount; ei++ ) {
ON_BrepEdge& edge = m_E[ei];
if ( edge.m_edge_index == -1 ) {
edge.m_c3i = -1;
continue;
}
c3i = edge.m_c3i;
if ( c3i == -1 )
continue;
if ( c3i < -1 || c3i >= c3count ) {
ON_ERROR("Brep edge has illegal m_c3i.");
rc = false;
}
else {
if ( !c3map[c3i] )
mi++;
c3map[c3i]++;
}
}
if ( mi == 0 ) {
m_C3.Destroy();
}
else if ( mi < c3count ) {
mi = 0;
for ( c3i = 0; c3i < c3count; c3i++ ) {
if ( c3map[c3i] )
c3map[c3i] = mi++;
else {
delete m_C3[c3i];
m_C3[c3i] = 0;
c3map[c3i] = -1;
}
}
for ( ei = 0; ei < ecount; ei++ ) {
ON_BrepEdge& edge = m_E[ei];
c3i = edge.m_c3i;
if ( c3i >= 0 && c3i < c3count )
edge.m_c3i = c3map[c3i];
}
for ( c3i = c3count-1; c3i >= 0; c3i-- ) {
if ( c3map[c3i] < 0 ) {
m_C3.Remove(c3i);
c3count--;
}
}
}
}
m_C3.Shrink();
return rc;
}
bool ON_Brep::CullUnused2dCurves()
{
// remove unused surfaces
bool rc = true;
const int tcount = m_T.Count();
int c2count = m_C2.Count();
int c2i, ti, mi;
if ( c2count > 0 )
{
ON_Workspace ws;
int* c2map = ws.GetIntMemory(c2count+1);
*c2map++ = -1;
memset(c2map,0,c2count*sizeof(*c2map));
mi = 0;
for ( ti = 0; ti < tcount; ti++ ) {
ON_BrepTrim& trim = m_T[ti];
if ( trim.m_trim_index == -1 ) {
trim.m_c2i = -1;
continue;
}
c2i = trim.m_c2i;
if ( c2i == -1 )
continue;
if ( c2i < -1 || c2i >= c2count ) {
ON_ERROR("Brep trim has illegal m_c2i.");
rc = false;
}
else {
if ( !c2map[c2i] )
mi++;
c2map[c2i]++;
}
}
if ( mi == 0 ) {
m_C2.Destroy();
}
else if ( mi < c2count ) {
mi = 0;
for ( c2i = 0; c2i < c2count; c2i++ ) {
if ( c2map[c2i] )
c2map[c2i] = mi++;
else {
delete m_C2[c2i];
m_C2[c2i] = 0;
c2map[c2i] = -1;
}
}
for ( ti = 0; ti < tcount; ti++ ) {
ON_BrepTrim& trim = m_T[ti];
c2i = trim.m_c2i;
if ( c2i >= 0 && c2i < c2count )
trim.m_c2i = c2map[c2i];
}
for ( c2i = c2count-1; c2i >= 0; c2i-- ) {
if ( c2map[c2i] < 0 ) {
m_C2.Remove(c2i);
c2count--;
}
}
}
}
m_C2.Shrink();
return rc;
}
bool ON_Brep::CullUnusedLoops()
{
bool rc = true;
const int lcount = m_L.Count();
if ( lcount > 0 ) {
ON_Workspace ws;
int* lmap = ws.GetIntMemory(lcount+1);
*lmap++ = -1;
memset( lmap, 0, lcount*sizeof(*lmap) );
const int fcount = m_F.Count();
const int tcount = m_T.Count();
int li, fli, flcnt, fi, ti, mi;
mi = 0;
for ( li = 0; li < lcount; li++ ) {
ON_BrepLoop& loop = m_L[li];
if ( loop.m_loop_index == -1)
lmap[li] = -1;
else if ( loop.m_loop_index == li )
lmap[li] = loop.m_loop_index = mi++;
else {
ON_ERROR("Brep loop has illegal m_loop_index.");
rc = false;
lmap[li] = loop.m_loop_index;
}
}
if ( mi == 0 ) {
m_L.Destroy();
}
else if ( mi < lcount ) {
// remap loops
for ( li = lcount-1; li >= 0; li-- ) {
if ( m_L[li].m_loop_index == -1 )
m_L.Remove(li);
else
m_L[li].m_loop_index = lmap[li];
}
// remap ON_BrepFace.m_li[] indices
for ( fi = 0; fi < fcount; fi++ ) {
ON_BrepFace& face = m_F[fi];
flcnt = face.m_li.Count();
for ( fli = flcnt-1; fli >= 0; fli-- ) {
li = face.m_li[fli];
if ( li < -1 || li >= lcount ) {
ON_ERROR("Brep face m_li[] has illegal loop index.");
rc = false;
}
else {
li = lmap[li];
if (li >= 0 ) {
face.m_li[fli] = li;
}
else {
face.m_li.Remove(fli);
}
}
}
}
// remap ON_BrepTrim.m_li indices
for ( ti = 0; ti < tcount; ti++ ) {
ON_BrepTrim& trim = m_T[ti];
li = trim.m_li;
if ( li < -1 || li >= lcount ) {
ON_ERROR("Brep trim has illegal m_li.");
rc = false;
}
else {
trim.m_li = lmap[li];
}
}
}
}
m_L.Shrink();
return rc;
}
bool ON_Brep::CullUnusedTrims()
{
bool rc = true;
const int tcount = m_T.Count();
if ( tcount > 0 ) {
ON_Workspace ws;
int *tmap = ws.GetIntMemory(tcount+1);
*tmap++ = -1;
memset( tmap, 0, tcount*sizeof(*tmap));
const int lcount = m_L.Count();
const int ecount = m_E.Count();
int ti, li, ei, mi, ltcnt, lti, etcnt, eti;
mi = 0;
for ( ti = 0; ti < tcount; ti++ ) {
ON_BrepTrim& trim = m_T[ti];
if ( trim.m_trim_index == -1)
tmap[ti] = -1;
else if ( trim.m_trim_index == ti )
tmap[ti] = trim.m_trim_index = mi++;
else {
ON_ERROR("Brep trim has illegal m_trim_index.");
rc = false;
tmap[ti] = trim.m_trim_index;
}
}
if ( mi == 0 ) {
m_T.Destroy();
}
else if ( mi < tcount ) {
// remap trim indices
for ( ti = tcount-1; ti >= 0; ti-- ) {
if ( m_T[ti].m_trim_index == -1 ) {
m_T.Remove(ti);
}
else {
m_T[ti].m_trim_index = tmap[ti];
}
}
// remap loop.m_ti[] indicies
for ( li = 0; li < lcount; li++ ) {
ON_BrepLoop& loop = m_L[li];
ltcnt = loop.m_ti.Count();
for ( lti = ltcnt-1; lti >= 0; lti-- ) {
ti = loop.m_ti[lti];
if ( ti < -1 || ti >= tcount ) {
ON_ERROR("Brep loop.m_ti[] has illegal index.");
rc = false;
}
else {
ti = tmap[ti];
if (ti >= 0 ) {
loop.m_ti[lti] = ti;
}
else {
loop.m_ti.Remove(lti);
}
}
}
}
// remap edge.m_ti[] indicies
for ( ei = 0; ei < ecount; ei++ ) {
ON_BrepEdge& edge = m_E[ei];
etcnt = edge.m_ti.Count();
for ( eti = etcnt-1; eti >= 0; eti-- ) {
ti = edge.m_ti[eti];
if ( ti < -1 || ti >= tcount ) {
ON_ERROR("Brep edge.m_ti[] has illegal index.");
rc = false;
}
else {
ti = tmap[ti];
if (ti >= 0 ) {
edge.m_ti[eti] = ti;
}
else {
edge.m_ti.Remove(eti);
}
}
}
}
}
}
m_T.Shrink();
return rc;
}
bool ON_Brep::CullUnusedEdges()
{
bool rc = true;
const int ecount = m_E.Count();
if ( ecount > 0 ) {
ON_Workspace ws;
int* emap = ws.GetIntMemory(ecount+1);
*emap++ = -1;
memset( emap, 0, ecount*sizeof(*emap) );
const int vcount = m_V.Count();
const int tcount = m_T.Count();
int ei, ti, vi, mi, vecnt, vei;
mi = 0;
for ( ei = 0; ei < ecount; ei++ ) {
ON_BrepEdge& edge = m_E[ei];
if ( edge.m_edge_index == -1)
emap[ei] = -1;
else if ( edge.m_edge_index == ei )
emap[ei] = edge.m_edge_index = mi++;
else {
ON_ERROR("Brep edge has illegal m_edge_index.");
rc = false;
emap[ei] = edge.m_edge_index;
}
}
if ( mi == 0 ) {
m_E.Destroy();
}
else if ( mi < ecount )
{
// remap edge indices
for ( ei = ecount-1; ei >= 0; ei-- ) {
if ( m_E[ei].m_edge_index == -1 ) {
m_E.Remove(ei);
}
else {
m_E[ei].m_edge_index = emap[ei];
}
}
// remap trim.m_ei
for ( ti = 0; ti < tcount; ti++ ) {
ON_BrepTrim& trim = m_T[ti];
ei = trim.m_ei;
if ( ei < -1 || ei >= ecount ) {
ON_ERROR("Brep trim.m_ei has illegal index.");
rc = false;
}
else {
trim.m_ei = emap[ei];
}
}
// remap vertex.m_ei[]
for ( vi = 0; vi < vcount; vi++ ) {
ON_BrepVertex& vertex = m_V[vi];
vecnt = vertex.m_ei.Count();
for ( vei = vecnt-1; vei >= 0; vei-- ) {
ei = vertex.m_ei[vei];
if ( ei < -1 || ei >= ecount ) {
ON_ERROR("Brep vertex.m_ei[] has illegal index.");
rc = false;
}
else {
ei = emap[ei];
if (ei >= 0 ) {
vertex.m_ei[vei] = ei;
}
else {
vertex.m_ei.Remove(vei);
}
}
}
}
}
}
m_E.Shrink();
return rc;
}
bool ON_Brep::CullUnusedVertices()
{
bool rc = true;
const int vcount = m_V.Count();
if ( vcount > 0 )
{
ON_Workspace ws;
int* vmap = ws.GetIntMemory(vcount+1);
*vmap++ = -1;
memset(vmap,0,vcount*sizeof(*vmap));
const int tcount = m_T.Count();
const int ecount = m_E.Count();
int vi, ei, ti, mi, j;
if ( tcount > 0 )
{
// 11 Nov 2009 Dale Lear
// I added this code to fix bugs 55879 and 56191.
for ( ti = 0; ti < tcount; ti++ )
{
const ON_BrepTrim& trim = m_T[ti];
if ( -1 == trim.m_trim_index )
continue;
vi = trim.m_vi[0];
if ( vi >= 0 && vi < vcount && -1 == m_V[vi].m_vertex_index )
{
// undelete this vertex
// This error happens when the ON_Brep is invalid to begin with.
// However, in order to prevent crashes, we have to refuse to delete
// the vertex. See bugs 55879 and 56191.
ON_ERROR("ON_Brep::CullUnusedVertices() - deleted vertex referenced by trim.m_vi[0]");
m_V[vi].m_vertex_index = vi;
}
vi = trim.m_vi[1];
if ( vi >= 0 && vi < vcount && -1 == m_V[vi].m_vertex_index )
{
// undelete this vertex
// This error happens when the ON_Brep is invalid to begin with.
// However, in order to prevent crashes, we have to refuse to delete
// the vertex. See bugs 55879 and 56191.
ON_ERROR("ON_Brep::CullUnusedVertices() - deleted vertex referenced by trim.m_vi[1]");
m_V[vi].m_vertex_index = vi;
}
}
}
mi = 0;
for ( vi = 0; vi < vcount; vi++ ) {
ON_BrepVertex& vertex = m_V[vi];
if ( vertex.m_vertex_index == -1)
vmap[vi] = -1;
else if ( vertex.m_vertex_index == vi )
vmap[vi] = vertex.m_vertex_index = mi++;
else {
ON_ERROR("Brep vertex has illegal m_vertex_index.");
rc = false;
vmap[vi] = vertex.m_vertex_index;
}
}
if ( mi == 0 )
{
m_V.Destroy();
}
else if ( mi < vcount )
{
// remap vertex indices
for ( vi = vcount-1; vi >= 0; vi-- )
{
if ( m_V[vi].m_vertex_index == -1 )
{
m_V.Remove(vi);
}
else {
m_V[vi].m_vertex_index = vmap[vi];
}
}
// remap edge indices
for ( ei = 0; ei < ecount; ei++ )
{
ON_BrepEdge& edge = m_E[ei];
for ( j = 0; j < 2; j++ )
{
vi = edge.m_vi[j];
if ( vi < -1 || vi >= vcount )
{
ON_ERROR("Brep edge.m_vi[] has illegal index.");
rc = false;
}
else {
edge.m_vi[j] = vmap[vi];
}
}
}
// remap trim indices
for ( ti = 0; ti < tcount; ti++ )
{
ON_BrepTrim& trim = m_T[ti];
for ( j = 0; j < 2; j++ )
{
vi = trim.m_vi[j];
if ( vi < -1 || vi >= vcount )
{
ON_ERROR("Brep trim.m_vi[] has illegal index.");
rc = false;
}
else {
trim.m_vi[j] = vmap[vi];
}
}
}
}
}
m_V.Shrink();
return rc;
}
bool ON_Brep::Compact()
{
// Removes any unreferenced objects from arrays,
// reindexes as needed, and shrinks arrays to
// minimum required size.
bool rc = true;
if (!CullUnusedFaces())
rc = false;
if (!CullUnusedEdges())
rc = false;
if (!CullUnusedVertices())
rc = false;
if (!CullUnusedLoops())
rc = false;
if (!CullUnusedTrims())
rc = false;
if (!CullUnusedSurfaces())
rc = false;
if (!CullUnused3dCurves())
rc = false;
if (!CullUnused2dCurves())
rc = false;
// If 1-1 relationships exist between geometry and topology,
// the synchronize the geometry and topology indices. This
// helps confused users of breps not have to understand the
// differences between geometry and topology data.
ON_SimpleArray<bool> used;
bool bSyncUp;
if ( m_C2.Count() == m_T.Count() )
{
int i, count = m_C2.Count();
used.Reserve(count);
used.SetCount(count);
used.Zero();
bSyncUp = true;
for ( i = 0; i < count && bSyncUp; i++ )
{
const ON_BrepTrim& trim = m_T[i];
if ( trim.m_trim_index != i || trim.m_c2i < 0 || trim.m_c2i >= count )
bSyncUp = false;
else
{
if (used[trim.m_c2i])
bSyncUp = false;
else
used[trim.m_c2i] = true;
}
}
if ( bSyncUp )
{
ON_SimpleArray< ON_Curve* > ptr(count);
for( i = 0; i < count; i++ )
{
ON_BrepTrim& trim = m_T[i];
ptr[i] = m_C2[trim.m_c2i];
trim.m_c2i = i;
}
for( i = 0; i < count; i++ )
{
m_C2[i] = ptr[i];
}
}
}
if ( m_C3.Count() == m_E.Count() )
{
int i, count = m_C3.Count();
used.Reserve(count);
used.SetCount(count);
used.Zero();
bSyncUp = true;
for ( i = 0; i < count && bSyncUp; i++ )
{
const ON_BrepEdge& edge = m_E[i];
if ( edge.m_edge_index != i || edge.m_c3i < 0 || edge.m_c3i >= count )
bSyncUp = false;
else
{
if (used[edge.m_c3i])
bSyncUp = false;
else
used[edge.m_c3i] = true;
}
}
if ( bSyncUp )
{
ON_SimpleArray< ON_Curve* > ptr(count);
for( i = 0; i < count; i++ )
{
ON_BrepEdge& edge = m_E[i];
ptr[i] = m_C3[edge.m_c3i];
edge.m_c3i = i;
}
for( i = 0; i < count; i++ )
{
m_C3[i] = ptr[i];
}
}
}
if ( m_S.Count() == m_F.Count() )
{
int i, count = m_S.Count();
used.Reserve(count);
used.SetCount(count);
used.Zero();
bSyncUp = true;
for ( i = 0; i < count && bSyncUp; i++ )
{
const ON_BrepFace& face = m_F[i];
if ( face.m_face_index != i || face.m_si < 0 || face.m_si >= count )
bSyncUp = false;
else
{
if (used[face.m_si])
bSyncUp = false;
else
used[face.m_si] = true;
}
}
if ( bSyncUp )
{
ON_SimpleArray< ON_Surface* > ptr(count);
for( i = 0; i < count; i++ )
{
ON_BrepFace& face = m_F[i];
ptr[i] = m_S[face.m_si];
face.m_si = i;
}
for( i = 0; i < count; i++ )
{
m_S[i] = ptr[i];
}
}
}
return rc;
}
ON_Brep& ON_Brep::operator=(const ON_Brep& src)
{
if ( this != &src )
{
Destroy();
ON_Geometry::operator=(src);
m_V.SetCapacity(src.m_V.Count());
m_E.SetCapacity(src.m_E.Count());
m_F.SetCapacity(src.m_F.Count());
m_T.SetCapacity(src.m_T.Count());
m_L.SetCapacity(src.m_L.Count());
m_V.SetCount(src.m_V.Count());
m_E.SetCount(src.m_E.Count());
m_F.SetCount(src.m_F.Count());
m_T.SetCount(src.m_T.Count());
m_L.SetCount(src.m_L.Count());
src.m_C2.Duplicate( m_C2 );
src.m_C3.Duplicate( m_C3 );
src.m_S.Duplicate( m_S );
const int C2_count = m_C2.Count();
const int C3_count = m_C3.Count();
const int S_count = m_S.Count();
int i, count = m_V.Count();
for ( i = 0; i < count; i++ )
{
m_V[i] = src.m_V[i];
}
count = m_E.Count();
for ( i = 0; i < count; i++ )
{
m_E[i] = src.m_E[i];
ON_BrepEdge& e = m_E[i];
e.m_brep = this;
if (e.m_c3i >= C3_count)
{
ON_ERROR("src brep has invalid ON_BrepEdge.m_c3i value.");
e.m_c3i = -1;
}
// update curve proxy info to point at 3d curve in this brep
e.SetProxyCurve( ( e.m_c3i >= 0 ) ? m_C3[e.m_c3i] : 0,
src.m_E[i].ProxyCurveDomain()
);
if ( src.m_E[i].ProxyCurveIsReversed() )
e.ON_CurveProxy::Reverse();
e.SetDomain( src.m_E[i].Domain() );
}
count = m_L.Count();
for ( i = 0; i < count; i++ )
{
m_L[i].m_brep = this;
}
count = m_F.Count();
for ( i = 0; i < count; i++ )
{
m_F[i] = src.m_F[i];
ON_BrepFace& f = m_F[i];
f.m_brep = this;
if (f.m_si >= S_count)
{
ON_ERROR("src brep has invalid ON_BrepFace.m_si value.");
f.m_si = -1;
}
// update surface proxy info to point at 3d surface in this brep
f.SetProxySurface(( f.m_si >= 0 ) ? m_S[f.m_si] : 0);
f.m_bbox = src.m_F[i].m_bbox; // because SetProxySurface destroys it
}
count = m_T.Count();
for ( i = 0; i < count; i++ )
{
m_T[i] = src.m_T[i];
ON_BrepTrim& trim = m_T[i];
trim.m_brep = this;
if (trim.m_c2i >= C2_count)
{
ON_ERROR("src brep has invalid ON_BrepTrim.m_c2i value.");
trim.m_c2i = -1;
}
// update curve proxy info to point at 2d curve in this brep
trim.SetProxyCurve( ( trim.m_c2i >= 0 ) ? m_C2[trim.m_c2i] : 0,
src.m_T[i].ProxyCurveDomain()
);
if ( src.m_T[i].ProxyCurveIsReversed() )
trim.ON_CurveProxy::Reverse();
trim.SetDomain( src.m_T[i].Domain() );
}
count = m_L.Count();
for ( i = 0; i < count; i++ )
{
m_L[i] = src.m_L[i];
}
m_bbox = src.m_bbox;
m_is_solid = src.m_is_solid;
if (nullptr != src.m_region_topology){
m_region_topology = new ON_BrepRegionTopology(*src.m_region_topology);
m_region_topology->m_brep = this;
}
}
return *this;
}
void ON_Brep::Destroy()
{
m_aggregate_status = ON_AggregateComponentStatus::Empty;
if (nullptr != m_region_topology)
{
delete m_region_topology;
m_region_topology = nullptr;
}
m_V.Empty();
m_E.Empty();
m_F.Empty();
m_T.Empty();
m_L.Empty();
int i, count = m_C2.Count();
for ( i = 0; i < count; i++ ) {
delete m_C2[i];
m_C2[i] = 0;
}
m_C2.Empty();
m_C2.Zero();
count = m_C3.Count();
for ( i = 0; i < count; i++ ) {
delete m_C3[i];
m_C3[i] = 0;
}
m_C3.Empty();
m_C3.Zero();
count = m_S.Count();
for ( i = 0; i < count; i++ ) {
delete m_S[i];
m_S[i] = 0;
}
m_S.Empty();
m_S.Zero();
m_bbox.Destroy();
m_is_solid = 0;
// returns Brep to state it has after default construction
}
void ON_Brep::EmergencyDestroy()
{
// call if memory pool used by b-rep members becomes invalid
// but ON_Brep class memory is in a valid pool
m_V.EmergencyDestroy();
m_E.EmergencyDestroy();
m_F.EmergencyDestroy();
m_T.EmergencyDestroy();
m_L.EmergencyDestroy();
m_C2.EmergencyDestroy();
m_C3.EmergencyDestroy();
m_S.EmergencyDestroy();
m_bbox.Destroy();
m_is_solid = 0;
m_region_topology = nullptr;
}
bool ON_Brep::CombineCoincidentVertices(ON_BrepVertex& vertex0, ON_BrepVertex& vertex1)
{
bool rc = false;
if (&vertex0 == &vertex1) {
ON_ERROR("ON_Brep::CombineCoincidentVertices - vertex0 = vertex1.");
return rc;
}
// moves information to vertex0 and deletes vertex1
int runaway, vei, vecnt, ei, eti, etcnt, ti, prev_ti, next_ti;
if ( vertex0.m_vertex_index >= 0 && vertex1.m_vertex_index != vertex0.m_vertex_index ) {
rc = true;
// update edges and trim references from vertex0 to vertex1
vecnt = vertex1.m_ei.Count();
for ( vei = 0; vei < vecnt; vei++ ) {
ei = vertex1.m_ei[vei];
if ( ei >= 0 ) {
// update edge vertex indices
ON_BrepEdge& edge = m_E[ei];
if ( edge.m_vi[0] == vertex1.m_vertex_index )
edge.m_vi[0] = vertex0.m_vertex_index;
if ( edge.m_vi[1] == vertex1.m_vertex_index )
edge.m_vi[1] = vertex0.m_vertex_index;
// update trim vertex indices
etcnt = edge.m_ti.Count();
for (eti = 0; eti < etcnt; eti++ ) {
ti = edge.m_ti[eti];
if (ti >= 0 ) {
ON_BrepTrim& trim = m_T[ti];
if ( trim.m_vi[0] == vertex1.m_vertex_index ) {
trim.m_vi[0] = vertex0.m_vertex_index;
// check for previous singular trims using vertex0
for (prev_ti = PrevTrim(ti), runaway=0;prev_ti >= 0 && prev_ti != ti && runaway < 1024;prev_ti=PrevTrim(prev_ti),runaway++) {
ON_BrepTrim& prevtrim = m_T[prev_ti];
if ( prevtrim.m_ei >= 0 )
break;
if ( prevtrim.m_vi[0] == vertex1.m_vertex_index )
prevtrim.m_vi[0] = vertex0.m_vertex_index;
if ( prevtrim.m_vi[1] == vertex1.m_vertex_index )
prevtrim.m_vi[1] = vertex0.m_vertex_index;
}
}
if ( trim.m_vi[1] == vertex1.m_vertex_index ) {
trim.m_vi[1] = vertex0.m_vertex_index;
// check for previous singular trims using vertex0
for (next_ti = NextTrim(ti), runaway=0;next_ti >= 0 && next_ti != ti && runaway < 1024;next_ti=NextTrim(next_ti),runaway++) {
ON_BrepTrim& nexttrim = m_T[next_ti];
if ( nexttrim.m_ei >= 0 )
break;
if ( nexttrim.m_vi[0] == vertex1.m_vertex_index )
nexttrim.m_vi[0] = vertex0.m_vertex_index;
if ( nexttrim.m_vi[1] == vertex1.m_vertex_index )
nexttrim.m_vi[1] = vertex0.m_vertex_index;
}
}
}
}
vertex0.m_ei.Append(ei);
}
}
}
// update vertex tolerances
if ( vertex0.m_tolerance != ON_UNSET_VALUE)
SetVertexTolerance(vertex0);
vertex1.m_vertex_index = -1;
vertex1.m_ei.Destroy();
DeleteVertex(vertex1);
return rc;
}
ON_BrepEdge* ON_Brep::CombineContiguousEdges(
int ei0,
int ei1,
double angle_tolerance_radians
)
{
// Bug fixers:
//
// Lots of (fast)testing is done to ensure the brep is
// 100% valid at the merge vertex. Do not change the
// return 0 bail outs unless you are 100% sure of what
// you are doing.
// get edges to be merged
const ON_BrepEdge* edge0 = Edge(ei0);
const ON_BrepEdge* edge1 = Edge(ei1);
if ( !edge0 || !edge1 )
return 0;
// clear any component index bits
ei0 = edge0->m_edge_index;
ei1 = edge1->m_edge_index;
if ( ei0 < 0 || ei1 < 0 || ei0 == ei1 )
return 0;
// make sure edges have same number of trims
if ( edge0->m_ti.Count() != edge1->m_ti.Count() )
return 0;
// figure out which edge ends to merge
// GBA 1/6/03 Fixed TRR#8951.
// Check that the vertex to be eliminated has exactly 2 incident edges.
int end0 = 1, end1 = 0;
bool MatchFound = false;
for(end0=1; !MatchFound && end0>=0; /* empty */){
int vi = edge0->m_vi[end0];
const ON_BrepVertex* v = Vertex(vi);
if(v && v->m_ei.Count()==2 ){
for(end1=0; !MatchFound && end1<2; /*empty*/){
MatchFound = (vi == edge1->m_vi[end1]);
if(!MatchFound)
end1++;
}
}
if(!MatchFound)
end0--;
}
if(!MatchFound)
return 0;
// vi_mid = index of vertex to be eliminated
const int vi_mid = edge0->m_vi[end0];
{
const ON_BrepVertex* v = Vertex(vi_mid);
if ( !v )
return 0;
if ( v->m_ei.Count() != 2 )
return 0;
if ( v->m_ei[0] != ei0 && v->m_ei[1] != ei0 )
return 0;
if ( v->m_ei[0] != ei1 && v->m_ei[1] != ei1 )
return 0;
}
// evi0 = vertex index and other end of edge0
const int evi0 = edge0->m_vi[1-end0];
// evi = vertex index and other end of edge1
const int evi1 = edge1->m_vi[1-end1];
if ( evi0 == vi_mid )
return 0;
if ( evi1 == vi_mid )
return 0;
// new edge will start at vi0 and end at vi1
const int vi0 = (end0==1) ? evi0 : evi1;
const int vi1 = (end0==1) ? evi1 : evi0;
// make sure the 3d kink angle at the merge point is <= angle_tolerance
{
ON_3dVector tan0 = edge0->TangentAt( edge0->Domain()[end0] );
if (end0 == 0)
tan0 = -tan0;
ON_3dVector tan1 = edge1->TangentAt( edge1->Domain()[end1] );
if (end1 == 1)
tan1 = -tan1;
double d = tan0*tan1;
if ( d < cos(angle_tolerance_radians) )
return 0;
}
// get corresponding pairs of trims to merge
int trim_count = edge0->m_ti.Count();
ON_SimpleArray<int> trim0_index(trim_count);
ON_SimpleArray<int> trim1_index(trim_count);
ON_SimpleArray<int> loop_lti0(trim_count);
ON_SimpleArray<int> loop_lti1(trim_count);
int eti;
for ( eti = 0; eti < trim_count; eti++ )
{
const ON_BrepTrim* trim0 = Trim( edge0->m_ti[eti] );
if ( !trim0 )
return 0;
int ti0 = trim0->m_trim_index;
const ON_BrepLoop* loop = trim0->Loop();
if ( !loop )
return 0;
if ( loop->m_ti.Count() < 2 )
return 0;
// get index of next/prev trim that corresponds to edge1
bool bRev = (end0==0);
if ( trim0->m_bRev3d )
bRev = !bRev;
int lti1 = -1;
int lti0 = loop->m_ti.Search( &ti0, ON_CompareIncreasing<int> );
if ( lti0 < 0 )
return 0;
if ( bRev )
lti1 = lti0 - 1 + loop->m_ti.Count();
else
lti1 = lti0 +1;
lti1 %= loop->m_ti.Count();
const ON_BrepTrim* trim1 = loop->Trim(lti1);
if ( !trim1 )
return 0;
if ( trim1->m_ei != ei1 )
return 0;
if ( trim0->m_trim_index == trim1->m_trim_index )
return 0;
// test for valid trim vertices and orientations
int tend0 = trim0->m_bRev3d ? (1-end0) : end0;
int tend1 = trim1->m_bRev3d ? (1-end1) : end1;
if ( tend0 == tend1 )
return 0;
if ( trim0->m_vi[tend0] != vi_mid )
return 0;
if ( trim1->m_vi[tend1] != vi_mid )
return 0;
if ( trim0->m_vi[1-tend0] != evi0 )
return 0;
if ( trim1->m_vi[1-tend1] != evi1 )
return 0;
trim0_index.Append(trim0->m_trim_index);
trim1_index.Append(trim1->m_trim_index);
loop_lti0.Append(lti0);
loop_lti1.Append(lti1);
}
// create new 3d edge curve geometry
// new edge goes same direction as edge0
ON_PolyCurve* ec = 0;
{
ON_Curve* ec0 = edge0->DuplicateCurve();
if ( !ec0 )
return 0;
ON_Curve* ec1 = edge1->DuplicateCurve();
if ( !ec1 )
{
delete ec0;
return 0;
}
if ( end0 == end1 )
{
if ( !ec1->Reverse() )
{
delete ec0;
delete ec1;
return 0;
}
}
ec = new ON_PolyCurve();
if ( end0 == 1 )
{
ec->Append(ec0);
ec->AppendAndMatch(ec1);
}
else
{
ec->Append(ec1);
ec->AppendAndMatch(ec0);
}
ec->RemoveNesting();
//23 March 2022 - Chuck - Added here to match ON_PolyCurve::Read(). Otherwise
//after saving and reopening, the edge will be invalid if the start or end parameter
//is changed in the SanitzeDomain() call in Read(). See RH-67919.
ec->SanitizeDomain();
}
// create new 2d trim curve geometry
ON_SimpleArray<ON_Curve*> tc(trim_count);
for ( eti = 0; eti < trim_count; eti++ )
{
const ON_BrepTrim* trim0 = Trim(trim0_index[eti]);
if ( !trim0 )
break;
const ON_BrepTrim* trim1 = Trim(trim1_index[eti]);
if ( !trim1 )
break;
ON_NurbsCurve* c0 = trim0->NurbsCurve();
if ( !c0 )
break;
ON_NurbsCurve* c1 = trim1->NurbsCurve();
if ( !c1 )
{
delete c0;
break;
}
if ( trim0->m_vi[1] == vi_mid && trim1->m_vi[0] == vi_mid )
{
if ( !c0->Append(*c1) )
{
delete c0;
delete c1;
break;
}
delete c1;
c1 = 0;
tc.Append(c0);
}
else if ( trim0->m_vi[0] == vi_mid && trim1->m_vi[1] == vi_mid )
{
if ( !c1->Append(*c0) )
{
delete c0;
delete c1;
break;
}
delete c0;
c0 = c1;
c1 = 0;
tc.Append(c0);
}
}
if ( eti < trim_count )
{
delete ec;
for ( eti = 0; eti < tc.Count(); eti++ )
delete tc[eti];
return 0;
}
// Add new edge from vi0 to vi1 that has the same orientation
// as edge0. Adding the new edge may change pointer values,
// so the edge0 and edge1 pointers are reset.
edge0 = 0;
edge1 = 0;
const int c3i = AddEdgeCurve(ec);
ON_BrepEdge& edge = NewEdge( m_V[vi0], m_V[vi1], c3i );
edge0 = Edge(ei0);
edge1 = Edge(ei1);
// Set edge tolerance
if(edge0->m_tolerance<0 || edge1->m_tolerance<0)
edge.m_tolerance= ON_UNSET_VALUE;
else if ( edge0->m_tolerance> edge1->m_tolerance)
edge.m_tolerance= edge0->m_tolerance;
else
edge.m_tolerance= edge1->m_tolerance;
// dynamic m_T[] is grown to full size here.
// Trim refs are good after NewTrim()
m_T.Reserve( m_T.Count() + trim_count );
for ( eti = 0; eti < trim_count; eti++ )
{
int c2i = AddTrimCurve( tc[eti] );
ON_BrepTrim& trim0 = m_T[trim0_index[eti]];
ON_BrepTrim& trim1 = m_T[trim1_index[eti]];
ON_BrepTrim& trim = NewTrim( edge, trim0.m_bRev3d, c2i );
// Set trim tolerance
for(int i=0; i<2; i++){
if( trim0.m_tolerance[i]<0 || trim1.m_tolerance[i]<0)
trim.m_tolerance[i] = ON_UNSET_VALUE;
else if(trim0.m_tolerance[i]>trim1.m_tolerance[i])
trim.m_tolerance[i] = trim0.m_tolerance[i];
else
trim.m_tolerance[i] = trim1.m_tolerance[i];
}
trim.m_li = trim0.m_li;
ON_BrepLoop& loop = m_L[trim.m_li];
loop.m_ti[loop_lti0[eti]] = trim.m_trim_index;
loop.m_ti.Remove( loop_lti1[eti] );
//GBA 1/29/03 Fixes TRR#9233. Removing an item from loop.m_ti
//will cause loop indicies stored in loop_lti0[] and loop_lti1[]
//to be wrong. So they must be reindexed
int ri = loop_lti1[eti]; // removed index
int li = loop.m_loop_index;
for(int ii=0; ii<trim_count; ii++){
if(loop_lti0[ii]>ri && m_T[trim0_index[ii]].m_li == li)
loop_lti0[ii]--;
if(loop_lti1[ii]>ri && m_T[trim1_index[ii]].m_li == li)
loop_lti1[ii]--;
}
trim.m_type = trim0.m_type;
trim.m_iso = ON_Surface::not_iso;
if( trim0.m_iso==trim1.m_iso)
trim.m_iso = trim0.m_iso;
trim0.m_li = -1;
trim1.m_li = -1;
}
// delete old edges
DeleteEdge(m_E[ei0],true);
DeleteEdge(m_E[ei1],true);
return &m_E[edge.m_edge_index];
}
bool ON_Brep::CombineCoincidentEdges(ON_BrepEdge& edge0, ON_BrepEdge& edge1)
{
bool rc = false;
if ( edge0.m_edge_index == edge1.m_edge_index )
{
ON_ERROR("ON_Brep::CombineCoincidentEdges - edge0 = edge1.");
return rc;
}
int ti, eti, etcnt;
if ( edge0.m_edge_index >= 0
&& edge1.m_edge_index >= 0
&& edge0.m_edge_index != edge1.m_edge_index
&& edge0.m_vi[0] == edge1.m_vi[0]
&& edge0.m_vi[1] == edge1.m_vi[1] )
{
bool bIsGoodIso0 = false;
if (edge0.m_tolerance == 0.0){
for (eti=0; eti<edge0.m_ti.Count(); eti++){
const ON_BrepTrim& T = m_T[edge0.m_ti[eti]];
if (T.m_iso)
{
bIsGoodIso0 = true;
break;
}
}
}
bool bIsGoodIso1 = false;
if (edge1.m_tolerance == 0.0){
for (eti=0; eti<edge1.m_ti.Count(); eti++){
const ON_BrepTrim& T = m_T[edge1.m_ti[eti]];
if (T.m_iso)
{
bIsGoodIso1 = true;
break;
}
}
}
bool bKeep0 = (edge0.m_tolerance <= edge1.m_tolerance) ? true : false;
if (edge0.m_tolerance == edge1.m_tolerance && edge0.m_tolerance == 0.0){
if (bIsGoodIso1){
if (!bIsGoodIso0)
bKeep0 = false;
else {//both are good. Take the one with the lowest degree.
if (edge1.Degree() < edge0.Degree())
bKeep0 = false;
else if (edge1.Degree() == edge0.Degree() && edge1.SpanCount() < edge0.SpanCount())
bKeep0 = false;
}
}
}
ON_BrepEdge& EKeep = (bKeep0) ? edge0 : edge1;
ON_BrepEdge& EToss = (bKeep0) ? edge1 : edge0;
/*
rc = true;
etcnt = edge1.m_ti.Count();
int tcount = m_T.Count();
for ( eti = 0; eti < etcnt; eti++ ) {
ti = edge1.m_ti[eti];
if ( ti >= 0 && ti < tcount ) {
ON_BrepTrim& trim = m_T[ti];
trim.m_ei = edge0.m_edge_index;
edge0.m_ti.Append(ti);
// TODO - tolerances ?
//set edge tolerance
if (edge0.m_tolerance != ON_UNSET_VALUE && edge1.m_tolerance != ON_UNSET_VALUE)
SetEdgeTolerance(edge0, false);
else edge0.m_tolerance = ON_UNSET_VALUE;
}
}
edge1.m_ti.Destroy();
DeleteEdge( edge1, false );
etcnt = edge0.m_ti.Count();
if ( etcnt >= 2 ) for ( eti = 0; eti < etcnt; eti++ )
{
ti = edge0.m_ti[eti];
if ( ti >= 0 && ti < tcount )
{
ON_BrepTrim& trim = m_T[ti];
if ( trim.m_type == ON_BrepTrim::boundary )
trim.m_type = ON_BrepTrim::mated;
}
}
*/
rc = true;
etcnt = EToss.m_ti.Count();
int tcount = m_T.Count();
for ( eti = 0; eti < etcnt; eti++ ) {
ti = EToss.m_ti[eti];
if ( ti >= 0 && ti < tcount ) {
ON_BrepTrim& trim = m_T[ti];
trim.m_ei = EKeep.m_edge_index;
EKeep.m_ti.Append(ti);
trim.UnsetPlineEdgeParameters();
// TODO - tolerances ?
//set edge tolerance
if (EKeep.m_tolerance != ON_UNSET_VALUE && EToss.m_tolerance != ON_UNSET_VALUE)
SetEdgeTolerance(EKeep, false);
else EKeep.m_tolerance = ON_UNSET_VALUE;
}
}
EToss.m_ti.Destroy();
DeleteEdge( EToss, false );
etcnt = EKeep.m_ti.Count();
if ( etcnt >= 2 ) for ( eti = 0; eti < etcnt; eti++ )
{
ti = EKeep.m_ti[eti];
if ( ti >= 0 && ti < tcount )
{
ON_BrepTrim& trim = m_T[ti];
if ( trim.m_type == ON_BrepTrim::boundary )
trim.m_type = ON_BrepTrim::mated;
}
}
}
return rc;
}
bool ON_Brep::DisconnectEdgeFaces(int eid)
{
if (eid < 0 || eid > m_E.Count())
return false;
ON_BrepEdge& E = m_E[eid];
if (E.m_edge_index < 0 || E.m_ti.Count() < 1)
return false;
int c3 = E.m_c3i;
ON_Interval cdom = E.ProxyCurveDomain();
ON_3dPoint VP[2];
bool bClosed = E.m_vi[0] == E.m_vi[1];
ON_BrepVertex* pEV0 = E.Vertex(0);
if (!pEV0)
return false;
VP[0] = pEV0->Point();
ON_BrepVertex* pEV1 = 0;
if (!bClosed){
pEV1 = E.Vertex(1);
if (!pEV1)
return false;
VP[1] = pEV1->Point();
}
else {
pEV1 = pEV0;
VP[1] = VP[0];
}
int evid[2];
evid[0] = E.m_vi[0];
evid[1] = E.m_vi[1];
int tc = E.m_ti.Count();
ON_SimpleArray<ON_2dex> tids(tc);
ON_SimpleArray<bool> bUsed(tc);
int i;
for (i=0; i<tc; i++)
bUsed.Append(false);
//Separate trims into singles or pairs from the same face.
for (i=0; i<tc; i++){
if (bUsed[i])
continue;
ON_2dex& dex = tids.AppendNew();
dex.i = E.m_ti[i];
dex.j = -1;
const ON_BrepTrim* pTi = E.Trim(i);
if (!pTi)
return false;
int fi = pTi->FaceIndexOf();
if (fi < 0)
return false;
int j;
for (j=i+1; j<tc; j++){
if (bUsed[j])
continue;
const ON_BrepTrim* pTj = E.Trim(j);
if (!pTj)
return false;
if (pTj->FaceIndexOf() == fi){
bUsed[j] = true;
if (dex.j == -1)
dex.j = E.m_ti[j];
else // 3 trims from the same face. Not good.
return false;
}
}
}
if (tids.Count() < 2)
return false;
//Disconnect trims not part of tids[0] from E
E.m_ti.Empty();
E.m_ti.Append(tids[0].i);
if (tids[0].j >= 0)
E.m_ti.Append(tids[0].i);
/*
int nvc = tids.Count()-1;
if (!bClosed)
nvc *= 2;
m_V.Reserve(m_V.Count() + nvc);
*/
//E will no longer be a valid reference. Use the info saved above.
for (i=1; i<tids.Count(); i++){//Make a new edge for each member of tids.
//New vertices
//ON_BrepVertex& V0 = NewVertex(VP[0]);
//ON_BrepVertex& V1 = (bClosed) ? V0 : NewVertex(VP[1]);
ON_BrepVertex& V0 = m_V[evid[0]];
ON_BrepVertex& V1 = m_V[evid[1]];
//New edge
ON_BrepEdge& NE = NewEdge(V0, V1, c3, &cdom);
NE.m_ti.Append(tids[i].i);
ON_BrepTrim& Ti = m_T[tids[i].i];
Ti.m_ei = NE.m_edge_index;
Ti.m_vi[0] = (Ti.m_bRev3d) ? NE.m_vi[1] : NE.m_vi[0];
Ti.m_vi[1] = (Ti.m_bRev3d) ? NE.m_vi[0] : NE.m_vi[1];
int ptid = PrevTrim(tids[i].i);
if (ptid != tids[i].i)
m_T[ptid].m_vi[1] = Ti.m_vi[0];
int ntid = NextTrim(tids[i].i);
if (ntid != tids[i].i)
m_T[ntid].m_vi[0] = Ti.m_vi[1];
if (tids[i].j >= 0){
NE.m_ti.Append(tids[i].j);
ON_BrepTrim& Tj = m_T[tids[i].j];
Tj.m_ei = NE.m_edge_index;
Tj.m_vi[0] = (Tj.m_bRev3d) ? NE.m_vi[1] : NE.m_vi[0];
Tj.m_vi[1] = (Tj.m_bRev3d) ? NE.m_vi[0] : NE.m_vi[1];
ptid = PrevTrim(tids[i].j);
if (ptid != tids[i].j)
m_T[ptid].m_vi[1] = Tj.m_vi[0];
ntid = NextTrim(tids[i].j);
if (ntid != tids[i].j)
m_T[ntid].m_vi[0] = Tj.m_vi[1];
}
}
return true;
}
bool ON_Brep::Create( ON_Surface*& pS )
{
bool rc = false;
Destroy();
ON_Surface* p = pS;
if (p)
{
int vid[4] = {-1,-1,-1,-1};
int eid[4] = {-1,-1,-1,-1};
bool bRev3d[4] = {0,0,0,0};
ON_BrepFace* face = NewFace(p,vid,eid,bRev3d);
if ( face )
{
rc = true;
pS = 0;
}
}
return rc;
}
/*
bool ON_Brep::FaceTool( ON_Surface* pS )
{
// private face adding tool
if (!pS)
return false;
double u[2], v[2];
if (!pS->GetDomain(0, &u[0], &u[1]))
return false;
if (!pS->GetDomain(1, &v[0], &v[1]))
return false;
ON_3dPoint srf_P[2][2];
if ( !pS->EvPoint(u[0],v[0],srf_P[0][0] )
return false;
if ( !pS->EvPoint(u[1],v[0],srf_P[1][0] )
return false;
if ( !pS->EvPoint(u[0],v[1],srf_P[0][1] )
return false;
if ( !pS->EvPoint(u[1],v[1],srf_P[1][1] )
return false;
int sw_vi, se_vi, ne_vi, nw_vi;
m_F.Reserve( m_F.Count() + 1 );
m_T.Reserve( m_T.Count() + 4 );
m_L.Reserve( m_L.Count() + 1 );
m_V.Reserve( m_V.Count() + 4 );
m_E.Reserve( m_E.Count() + 4 );
m_S.Reserve( m_S.Count() + 1 );
m_C2.Reserve( m_C2.Count() + 1 );
m_C3.Reserve( m_C3.Count() + 1 );
sw_vi = NewVertex( srf_P[0][0], 0.0 ).m_vertex_index;
bool bIsClosed[2];
bIsClosed[0] = pS->IsClosed(0);
bIsClosed[1] = pS->IsClosed(1);
bool bIsSingular[4];
bIsSingular[0] = pS->IsSingular(0);
bIsSingular[1] = pS->IsSingular(1);
bIsSingular[2] = pS->IsSingular(2);
bIsSingular[3] = pS->IsSingular(3);
if (bIsSingular[0] || bIsClosed[0])
se_vi = sw_vi;
else
se_vi = NewVertex( srf_P[1][0], 0.0 ).m_vertex_index;
if (bIsSingular[1] || bIsClosed[1])
ne_vi = se_vi;
else
ne_vi = NewVertex( srf_P[1][1], 0.0 ).m_vertex_index;
if (bIsSingular[2] || bIsClosed[0])
nw_vi = ne_vi;
else if (bIsSingular[3] || bIsClosed[1])
nw_vi = sw_vi;
else
nw_vi = NewVertex( srf_P[0][1], 0.0 ).m_vertex_index;
ON_BrepVertex& sw_vertex = m_V[sw_vi];
ON_BrepVertex& se_vertex = m_V[se_vi];
ON_BrepVertex& ne_vertex = m_V[ne_vi];
ON_BrepVertex& nw_vertex = m_V[nw_vi];
ON_BrepFace& face = NewFace(AddSurface(pS));
ON_BrepLoop& loop = NewLoop(ON_BrepLoop::outer, face);
loop.m_pbox.m_min.x = u[0];
loop.m_pbox.m_min.y = v[0];
loop.m_pbox.m_min.z = 0.0;
loop.m_pbox.m_max.x = u[1];
loop.m_pbox.m_max.y = v[1];
loop.m_pbox.m_max.z = 0.0;
int id3[4] = {-1,-1,-1,-1};
int eid[4] = {-1,-1,-1,-1};
int c2i;
ON_2dPoint sw_corner(u[0],v[0]);
ON_2dPoint se_corner(u[1],v[0]);
ON_2dPoint ne_corner(u[1],v[1]);
ON_2dPoint nw_corner(u[0],v[1]);
{//south side
c2i = AddTrimCurve(new ON_LineCurve(sw_corner,se_corner));
if (bIsSingular[0]) {
NewSingularTrim(sw_vertex,loop,ON_Surface::S_iso,c2i);
}
else {
id3[0] = AddEdgeCurve( pS->IsoCurve(0, v[0]) );
ON_BrepEdge& edge = NewEdge(sw_vertex, se_vertex, id3[0]);
edge.m_tolerance = 0.0;
eid[0] = edge.m_edge_index;
ON_BrepTrim& trim = NewTrim(edge, false, loop, c2i);
trim.m_iso = ON_Surface::S_iso;
if (bIsClosed[1])
trim.m_type = ON_BrepTrim::seam;
else
trim.m_type = ON_BrepTrim::boundary;
}
}
{ //east side
c2i = AddTrimCurve(new ON_LineCurve(se_corner,ne_corner));
if (bIsSingular[1]) {
NewSingularTrim(se_vertex,loop,ON_Surface::E_iso,c2i);
}
else {
id3[1] = AddEdgeCurve(pS->IsoCurve(1, u[1]));
ON_BrepEdge& edge = NewEdge(se_vertex, ne_vertex, id3[1]);
edge.m_tolerance = 0.0;
eid[1] = edge.m_edge_index;
ON_BrepTrim& trim = NewTrim(edge, false, loop, c2i);
trim.m_iso = ON_Surface::E_iso;
if (bIsClosed[0])
trim.m_type = ON_BrepTrim::seam;
else
trim.m_type = ON_BrepTrim::boundary;
}
}
{ //north side
c2i = AddTrimCurve(new ON_LineCurve(ne_corner,nw_corner));
bool rev = false;
if (bIsSingular[2]) {
NewSingularTrim(ne_vertex,loop,ON_Surface::N_iso,c2i);
}
else{
if (bIsClosed[1]) {
id3[2] = id3[0];
eid[2] = eid[0];
rev = true;
}
else {
ON_Curve* pC3 = pS->IsoCurve(0, v[1]);
if (pC3) pC3->Reverse();
id3[2] = AddEdgeCurve(pC3);
ON_BrepEdge& edge = NewEdge(ne_vertex, nw_vertex, id3[2]);
edge.m_tolerance = 0.0;
eid[2] = edge.m_edge_index;
}
ON_BrepTrim& trim = NewTrim(m_E[eid[2]], rev, loop, c2i);
trim.m_iso = ON_Surface::N_iso;
if (bIsClosed[1])
trim.m_type = ON_BrepTrim::seam;
else
trim.m_type = ON_BrepTrim::boundary;
}
}
{ //west side
c2i = AddTrimCurve(new ON_LineCurve(nw_corner,sw_corner));
bool rev = false;
if (bIsSingular[3]){
NewSingularTrim(nw_vertex,loop,ON_Surface::W_iso,c2i);
}
else {
if (bIsClosed[0]){
id3[3] = id3[1];
eid[3] = eid[1];
rev = true;
}
else {
ON_Curve* pC3 = pS->IsoCurve(1, u[0]);
if (pC3) pC3->Reverse();
id3[3] = AddEdgeCurve(pC3);
ON_BrepEdge& edge = NewEdge(nw_vertex, sw_vertex, id3[3]);
edge.m_tolerance = 0.0;
eid[3] = edge.m_edge_index;
}
ON_BrepTrim& trim = NewTrim( m_E[eid[3]], rev, loop, c2i );
trim.m_iso = ON_Surface::W_iso;
if (bIsClosed[0])
trim.m_type = ON_BrepTrim::seam;
else
trim.m_type = ON_BrepTrim::boundary;
}
}
for ( int lti = 0; lti < 4; lti++ )
{
ti = loop.m_ti[lti];
ON_BrepTrim& trim = m_T[ti];
trim.m_tolerance[0] = 0.0;
trim.m_tolerance[1] = 0.0;
trim.m__legacy_2d_tol = 0.0;
trim.m__legacy_3d_tol = 0.0;
trim.m__legacy_flags_Set(-1,1);
}
return true;
}
*/
bool ON_Brep::Create( ON_NurbsSurface*& pNurbsSurface )
{
ON_Surface* pSurface = pNurbsSurface;
bool rc = Create(pSurface);
if ( !pSurface )
pNurbsSurface = 0;
return rc;
}
bool ON_Brep::Create( ON_PlaneSurface*& pPlaneSurface )
{
ON_Surface* pSurface = pPlaneSurface;
bool rc = Create(pSurface);
if ( !pSurface )
pPlaneSurface = 0;
return rc;
}
bool ON_Brep::Create( ON_RevSurface*& pRevSurface )
{
ON_Surface* pSurface = pRevSurface;
bool rc = Create(pSurface);
if ( !pSurface )
pRevSurface = 0;
return rc;
}
bool ON_Brep::Create( ON_SumSurface*& pSumSurface )
{
ON_Surface* pSurface = pSumSurface;
bool rc = Create(pSurface);
if ( !pSurface )
pSumSurface = 0;
return rc;
}
bool ON_Brep::HasBrepForm() const
{
return true;
}
ON_Brep* ON_Brep::BrepForm( ON_Brep* brep ) const
{
if ( brep )
{
if ( brep != this )
{
*brep = *this;
brep->DestroyMesh(ON::any_mesh);
}
}
else
{
brep = new ON_Brep(*this);
brep->DestroyMesh(ON::any_mesh);
}
return brep;
}
void ON_Brep::Clear_vertex_user_i() const
{
int vi;
int vertex_count = m_V.Count();
for ( vi = 0; vi < vertex_count; vi++ )
{
memset(&m_V[vi].m_vertex_user,0,sizeof(ON_U));
}
}
void ON_Brep::Clear_edge_user_i() const
{
int ei;
int edge_count = m_E.Count();
for ( ei = 0; ei < edge_count; ei++ )
{
memset(&m_E[ei].m_edge_user,0,sizeof(ON_U));
}
}
void ON_Brep::Clear_edge_user_i(int i) const
{
int ei;
int edge_count = m_E.Count();
for ( ei = 0; ei < edge_count; ei++ )
{
memset(&m_E[ei].m_edge_user,0,sizeof(ON_U));
m_E[ei].m_edge_user.i = i;
}
}
void ON_Brep::Clear_trim_user_i() const
{
int ti;
int trim_count = m_T.Count();
for ( ti = 0; ti < trim_count; ti++ ) {
memset(&m_T[ti].m_trim_user,0,sizeof(ON_U));
}
}
void ON_Brep::Clear_loop_user_i() const
{
int li;
int loop_count = m_L.Count();
for ( li = 0; li < loop_count; li++ ) {
memset(&m_L[li].m_loop_user,0,sizeof(ON_U));
}
}
void ON_Brep::Clear_face_user_i() const
{
int fi;
int face_count = m_F.Count();
for ( fi = 0; fi < face_count; fi++ ) {
memset(&m_F[fi].m_face_user,0,sizeof(ON_U));
}
}
void ON_Brep::Clear_user_i() const
{
memset(&m_brep_user,0,sizeof(m_brep_user));
Clear_vertex_user_i();
Clear_edge_user_i();
Clear_trim_user_i();
Clear_loop_user_i();
Clear_face_user_i();
}
void ON_Brep::Set_user(ON_U u) const
{
int i, count;
m_brep_user=u;
count = m_V.Count();
const ON_BrepVertex* V = m_V.Array();
for ( i = 0; i < count; i++ )
{
V[i].m_vertex_user = u;
}
count = m_E.Count();
const ON_BrepEdge* E = m_E.Array();
for ( i = 0; i < count; i++ )
{
E[i].m_edge_user = u;
}
count = m_T.Count();
const ON_BrepTrim* T = m_T.Array();
for ( i = 0; i < count; i++ )
{
T[i].m_trim_user = u;
}
count = m_L.Count();
const ON_BrepLoop* L = m_L.Array();
for ( i = 0; i < count; i++ )
{
L[i].m_loop_user = u;
}
count = m_F.Count();
const ON_BrepFace* F = m_F.Array();
for ( i = 0; i < count; i++ )
{
F[i].m_face_user = u;
}
}
ON_BrepVertex& ON_Brep::NewPointOnFace(
ON_BrepFace& face,
double s,
double t
)
{
ON_3dPoint point = face.PointAt(s,t);
ON_BrepVertex& vertex = NewVertex( point );
ON_BrepLoop& loop = NewLoop( ON_BrepLoop::ptonsrf, face );
ON_BrepTrim& trim = NewTrim(false, loop, -1);
vertex.m_tolerance = 0.0;
trim.m_type = ON_BrepTrim::ptonsrf;
trim.m_pbox.m_min.Set(s,t,0.0);
trim.m_pbox.m_max.Set(s,t,0.0);
trim.m_tolerance[0] = 0.0;
trim.m_tolerance[1] = 0.0;
loop.m_pbox = trim.m_pbox;
trim.m_vi[0] = trim.m_vi[1] = vertex.m_vertex_index;
return vertex;
}
ON_BrepTrim& ON_Brep::NewCurveOnFace( ON_BrepFace& face, ON_BrepEdge& edge, bool bRev3d, int c2i )
{
ON_BrepLoop& loop = NewLoop( ON_BrepLoop::crvonsrf, face );
ON_BrepTrim& trim = NewTrim( edge, bRev3d, loop, c2i );
trim.m_type = ON_BrepTrim::crvonsrf;
const ON_Curve* trimcurve = trim.TrimCurveOf();
if (trimcurve)
{
trimcurve->GetBoundingBox( trim.m_pbox );
loop.m_pbox = trim.m_pbox;
}
return trim;
}
//For each i, let ti be the parameter along the chord (Points[0], Points[last])
//of the closest point to Points[i], and let di be the distance to the chord.
//Transform Points so that Points[0] = P0, Points[last] = P1,
//and the new ti and di remain the same. Don't do anything if the chord is short
//relative to the cummulative dist between consecutive points on input.
static bool AdjustPointListAlongChord(ON_3dPointArray& Points,
const ON_3dPoint& P0,
const ON_3dPoint& P1)
{
int count = Points.Count();
if (count < 2)
return false;
ON_3dPoint A0 = Points[0];
ON_3dPoint A1 = Points[count-1];
double chord_dist = A0.DistanceTo(A1);
if (chord_dist < ON_SQRT_EPSILON)
return false;
double cum_dist = 0.0;
int i;
for (i=1; i<count; i++)
cum_dist += Points[i-1].DistanceTo(Points[i]);
if (chord_dist < 0.01*cum_dist)
return false;
ON_3dVector V0 = P0-A0;
ON_3dVector V1 = P1-A1;
ON_Line Aline(A0, A1);
Points[0] = P0;
Points[count-1] = P1;
for (i=1; i<count-1; i++){
double t;
Aline.ClosestPointTo(Points[i], &t);
Points[i] = Points[i] + (1.0-t)*V0 + t*V1;
}
return true;
}
static void AdjustNurbsCurve(ON_NurbsCurve& crv,
const ON_3dPoint& P0,
const ON_3dPoint& P1)
{
if (crv.Dimension() > 3)
return;
crv.ClampEnd(2);
int cvc = crv.CVCount();
ON_3dPointArray Points(cvc);
int i;
for (i=0; i<cvc; i++)
crv.GetCV(i, Points.AppendNew());
if (!AdjustPointListAlongChord(Points, P0, P1)){
crv.SetStartPoint(P0);
crv.SetEndPoint(P1);
return;
}
bool rat = crv.IsRational();
for (i=0; i<cvc; i++){
double w = 1.0;
if (rat){
w = crv.Weight(i);
Points[i] *= w;
}
crv.SetCV(i, Points[i]);
if (rat)
crv.SetWeight(i, w);
}
return;
}
static void AdjustPolylineCurve(ON_PolylineCurve& crv,
const ON_3dPoint& P0,
const ON_3dPoint& P1)
{
AdjustPointListAlongChord(crv.m_pline, P0, P1);
crv.SetStartPoint(P0);
crv.SetEndPoint(P1);
return;
}
static bool AdjustCurve(ON_Curve& crv,
const ON_3dPoint& P0,
const ON_3dPoint& P1);
static bool AdjustPolyCurve(ON_PolyCurve& crv,
const ON_3dPoint& P0,
const ON_3dPoint& P1)
{
if (crv.Count() == 1){
ON_Curve* pSeg = crv.SegmentCurve(0);
if (!pSeg)
return false;
return AdjustCurve(*pSeg, P0, P1);
}
ON_3dPointArray Points(crv.Count() + 1);
Points.Append(crv.PointAtStart());
int i;
for (i=0; i<crv.Count(); i++)
Points.Append(crv.SegmentCurve(i)->PointAtEnd());
if (!AdjustPointListAlongChord(Points, P0, P1)){
return (crv.SetStartPoint(P0) || crv.SetEndPoint(P1)) ? true : false;
}
bool rc = false;
for (i=0; i<crv.Count(); i++){
ON_Curve* pSeg = crv.SegmentCurve(i);
if (!pSeg)
return rc;
if (AdjustCurve(*pSeg, Points[i], Points[i+1]))
rc = true;
Points[i+1] = pSeg->PointAtEnd();
}
return rc;
}
//Afterwards it is up to caller to check to see if the endpoints are where they should be.
static bool AdjustCurve(ON_Curve& crv,
const ON_3dPoint& P0,
const ON_3dPoint& P1)
{
ON_LineCurve* lc = ON_LineCurve::Cast(&crv);
if (lc){
lc->SetStartPoint(P0);
lc->SetEndPoint(P1);
return true;
}
ON_CurveProxy* pc = ON_CurveProxy::Cast(&crv);
if (pc)
return false;
if (crv.IsClosed()){
if (P0 != P1)
return false;
ON_3dPoint P = crv.PointAtStart();
ON_3dVector TVec = P0-P;
if (TVec.Length() > ON_SQRT_EPSILON){
ON_Xform T(ON_Xform::TranslationTransformation(TVec));
crv.Transform(T);
}
else
return false;
return true;
}
ON_PolylineCurve* plc = ON_PolylineCurve::Cast(&crv);
if (plc) {
AdjustPolylineCurve(*plc, P0, P1);
return true;
}
ON_NurbsCurve* nc = ON_NurbsCurve::Cast(&crv);
if (nc){
AdjustNurbsCurve(*nc, P0, P1);
return true;
}
ON_PolyCurve* plyc = ON_PolyCurve::Cast(&crv);
if (plyc){
return AdjustPolyCurve(*plyc, P0, P1);
}
ON_3dPoint A0 = crv.PointAtStart();
ON_3dPoint A1 = crv.PointAtEnd();
if (A0.DistanceTo(P0) < ON_SQRT_EPSILON && A1.DistanceTo(P1) < ON_SQRT_EPSILON){
return (crv.SetStartPoint(P0) || crv.SetEndPoint(P1)) ? true : false;
}
double alen = A0.DistanceTo(A1);
double plen = P0.DistanceTo(P1);
if (alen < 0.1*plen || plen < 0.1*alen){
return (crv.SetStartPoint(P0) || crv.SetEndPoint(P1)) ? true : false;
}
ON_3dPoint Ac = 0.5*(A0+A1);
ON_3dPoint Pc = 0.5*(P0+P1);
ON_3dVector TVec = Pc-Ac;
if (TVec.Length() > ON_SQRT_EPSILON){
const ON_Xform T(ON_Xform::TranslationTransformation(TVec));
crv.Transform(T);
}
A0 = crv.PointAtStart();
A1 = crv.PointAtEnd();
if (A0.DistanceTo(P0) < ON_SQRT_EPSILON && A1.DistanceTo(P1) < ON_SQRT_EPSILON){
return (crv.SetStartPoint(P0) || crv.SetEndPoint(P1)) ? true : false;
}
if (fabs(plen - alen) > ON_SQRT_EPSILON){
double scale = plen/alen;
Ac = 0.5*(A0+A1);
ON_Xform T(ON_Xform::ScaleTransformation(Ac, scale));
crv.Transform(T);
}
A0 = crv.PointAtStart();
A1 = crv.PointAtEnd();
if (A0.DistanceTo(P0) < ON_SQRT_EPSILON && A1.DistanceTo(P1) < ON_SQRT_EPSILON){
return (crv.SetStartPoint(P0) || crv.SetEndPoint(P1)) ? true : false;
}
if (plen < ON_SQRT_EPSILON){
return (crv.SetStartPoint(P0) || crv.SetEndPoint(P1)) ? true : false;
}
ON_3dPoint C = 0.5*(Pc+Ac);
ON_3dVector VA = A0-C;
VA.Unitize();
ON_3dVector VP = P0-C;
VP.Unitize();
ON_3dVector Axis = ON_CrossProduct(VA, VP);
double sina = Axis.Length();
if (sina < ON_SQRT_EPSILON){
return (crv.SetStartPoint(P0) || crv.SetEndPoint(P1)) ? true : false;
}
Axis.Unitize();
double cosa = VA*VP;
ON_Xform T;
T.Rotation(sina, cosa, Axis, C);
crv.Transform(T);
return (crv.SetStartPoint(P0) || crv.SetEndPoint(P1)) ? true : false;
}
static void AdjustEdgeEnds(ON_BrepEdge& edge)
{
ON_Brep* pB = edge.Brep();
if (!pB)
return;
ON_Curve* c3 = const_cast<ON_Curve*>(edge.EdgeCurveOf());
if( c3 )
{
ON_3dPoint A0 = c3->PointAtStart();
ON_3dPoint P0 = A0;
if (edge.m_vi[0] >= 0){
ON_BrepVertex& V = pB->m_V[edge.m_vi[0]];
if (V.IsValid())
P0 = V.Point();
}
ON_3dPoint A1 = c3->PointAtEnd();
ON_3dPoint P1 = A1;
if (edge.m_vi[1] >= 0){
ON_BrepVertex& V = pB->m_V[edge.m_vi[1]];
if (V.IsValid())
P1 = V.Point();
}
bool bQuit = true;
if (P0 != A0 && edge.m_vi[0] >= 0){
ON_BrepVertex& V = pB->m_V[edge.m_vi[0]];
V.m_tolerance = ON_UNSET_VALUE;
bQuit = false;
}
if (P1 != A1 && edge.m_vi[1] >= 0){
ON_BrepVertex& V = pB->m_V[edge.m_vi[1]];
V.m_tolerance = ON_UNSET_VALUE;
bQuit = false;
}
if (bQuit)
return;
if (AdjustCurve(*c3, P0, P1))
edge.m_tolerance = ON_UNSET_VALUE;
}
return;
}
bool ON_Brep::StandardizeEdgeCurve( int edge_index, bool bAdjustEnds )
{
return StandardizeEdgeCurve(edge_index, bAdjustEnds, 0);
}
bool ON_Brep::StandardizeEdgeCurve( int edge_index, bool bAdjustEnds, int EdgeCurveUse )
{
bool rc = false;
ON_BrepEdge* edge = Edge(edge_index);
if ( 0 != edge && edge->m_edge_index >= 0 )
{
edge_index = edge->m_edge_index;
const ON_Curve* c3 = edge->EdgeCurveOf();
if( c3 )
{
ON_Interval c3dom = c3->Domain();
ON_Interval pdom = edge->ProxyCurveDomain();
ON_Interval edom = edge->Domain();
bool bNewCurve = false;
if ( edge->ProxyCurveIsReversed() )
bNewCurve = true;
else if ( c3dom != pdom )
bNewCurve = true; // curve proxy is trimmed
else if ( EdgeCurveUse > 1 || (EdgeCurveUse < 1 && EdgeCurveUseCount( edge->m_c3i,2 ) > 1 ))
bNewCurve = true; // 2 or more edges use c3
else if ( edom != c3dom )
{
// can fix this problem by changing c3 domain
// and proxy settings
if ( m_C3[edge->m_c3i]->SetDomain(edom) )
{
edge->SetProxyCurveDomain(edom);
edge->SetDomain(edom);
rc = true;
}
else
{
bNewCurve = true;
}
}
else
rc = true;
if ( bNewCurve )
{
ON_Curve* newc3 = c3->Duplicate();
if ( !newc3 )
return false;
if ( !newc3->Trim(pdom) )
{
delete newc3;
return false;
}
if ( edge->ProxyCurveIsReversed() )
{
if ( !newc3->Reverse() )
{
delete newc3;
return false;
}
}
newc3->SetDomain(edom);
if ( newc3->Domain() != edom )
{
delete newc3;
return false;
}
int c3i = AddEdgeCurve(newc3);
edge->m_c3i = c3i;
edge->SetProxyCurve(newc3);
}
}
}
if (rc && bAdjustEnds)
AdjustEdgeEnds(*edge);
/*
{
ON_Curve* c3 = const_cast<ON_Curve*>(edge->EdgeCurveOf());
if( c3 )
{
if (edge->m_vi[0] >= 0)
{
const ON_BrepVertex& V = m_V[edge->m_vi[0]];
if (V.IsValid())
c3->SetStartPoint(V.Point());
}
if (edge->m_vi[1] >= 0)
{
const ON_BrepVertex& V = m_V[edge->m_vi[1]];
if (V.IsValid())
c3->SetEndPoint(V.Point());
}
}
}
*/
return rc;
}
static int sort_ci(const ON_BrepEdge* E0, const ON_BrepEdge* E1)
{
if (E0->m_c3i < E1->m_c3i)
return -1;
if (E0->m_c3i < E1->m_c3i)
return 1;
return 0;
}
void ON_Brep::StandardizeEdgeCurves( bool bAdjustEnds)
{
//The ends will not adjust properly unless
//all of the edge curves have been standardized first.
//So call standardize on all edges without adjusting, then do the adjusting
//chuck - 9/5/2006
int ei, edge_count = m_E.Count();
//chuck - 10/13/2008. The edge curve use counter called in StandardizeEdgeCurves(int,bool)
//searches through the entire edge array. In huge breps, this takes a long time.
int* index = (int*)onmalloc(edge_count*sizeof(int));
m_E.Sort(ON::sort_algorithm::quick_sort, index, sort_ci);
for ( ei = 0; ei < edge_count; ei++ ){
int ecc = (ei==edge_count-1 || m_E[index[ei+1]].m_c3i == m_E[index[ei]].m_c3i) ? 2 : 1;
StandardizeEdgeCurve( index[ei], false, ecc);
}
onfree((void*)index);
/*
for ( ei = 0; ei < edge_count; ei++ )
{
StandardizeEdgeCurve( ei, false );
}
*/
if (bAdjustEnds){
for ( ei = 0; ei < edge_count; ei++ )
AdjustEdgeEnds(m_E[ei]);
SetVertexTolerances(true);
SetEdgeTolerances(true);
}
}
bool ON_Brep::StandardizeTrimCurve( int trim_index )
{
bool rc = false;
ON_BrepTrim* trim = Trim(trim_index);
if ( 0 != trim && trim->m_trim_index >= 0 )
{
trim_index = trim->m_trim_index;
const ON_Curve* c2 = trim->TrimCurveOf();
if( c2 )
{
ON_Interval c2dom = c2->Domain();
ON_Interval pdom = trim->ProxyCurveDomain();
ON_Interval tdom = trim->Domain();
bool bNewCurve = false;
if ( trim->ProxyCurveIsReversed() )
bNewCurve = true;
else if ( c2dom != pdom )
bNewCurve = true; // curve proxy is trimmed
else if ( TrimCurveUseCount( trim->m_c2i, 2 ) > 1 )
bNewCurve = true; // 2 or more edges use c3
else if ( tdom != c2dom )
{
// can fix this problem by changing c3 domain
// and proxy settings
if ( m_C2[trim->m_c2i]->SetDomain(tdom) )
{
trim->SetProxyCurveDomain(tdom);
trim->SetDomain(tdom);
rc = true;
}
else
{
bNewCurve = true;
}
}
else
rc = true;
if ( bNewCurve )
{
ON_Curve* newc2 = c2->Duplicate();
if ( !newc2 )
return false;
if ( !newc2->Trim(pdom) )
{
delete newc2;
return false;
}
if ( trim->ProxyCurveIsReversed() )
{
if ( !newc2->Reverse() )
{
delete newc2;
return false;
}
}
newc2->SetDomain(tdom);
if ( newc2->Domain() != tdom )
{
delete newc2;
return false;
}
int c2i = AddTrimCurve(newc2);
trim->m_c2i = c2i;
trim->SetProxyCurve(newc2);
rc = true;
}
}
}
return rc;
}
void ON_Brep::StandardizeTrimCurves()
{
int ti, trim_count = m_T.Count();
for ( ti = 0; ti < trim_count; ti++ )
{
StandardizeTrimCurve( ti );
}
}
bool ON_Brep::StandardizeFaceSurface( int face_index )
{
bool rc = false;
ON_BrepFace* face = Face(face_index);
if ( 0 != face && face->m_face_index >= 0 )
{
face_index = face->m_face_index;
const ON_Surface* srf = face->SurfaceOf();
if ( srf )
{
//Feb 9 2013 - Chuck - Old code doesn't do anything if bRev is false
/*
if ( face->m_bRev )
{
if ( SurfaceUseCount( face->m_si, 2 ) >= 2 )
{
ON_Surface* newsrf = srf->Duplicate();
face->m_si = AddSurface(newsrf);
face->SetProxySurface(m_S[face->m_si]);
srf = newsrf;
}
rc = face->Transpose() ? true : false;
}
else
rc = true;
*/
if ( face->m_bRev )
rc = face->Transpose() ? true : false; //Transpose does the SurfaceUseCount check
else
{
if ( SurfaceUseCount( face->m_si, 2 ) >= 2 )
{
ON_Surface* newsrf = srf->Duplicate();
face->m_si = AddSurface(newsrf);
face->SetProxySurface(m_S[face->m_si]);
srf = newsrf;
}
rc = true;
}
}
}
return rc;
}
void ON_Brep::StandardizeFaceSurfaces()
{
int fi, face_count = m_F.Count();
for ( fi = 0; fi < face_count; fi++ )
{
StandardizeFaceSurface( fi );
}
}
void ON_Brep::Standardize()
{
StandardizeFaceSurfaces();
StandardizeEdgeCurves(true);
StandardizeTrimCurves();
}
bool ON_Brep::ShrinkSurface( ON_BrepFace& face, int DisableMask )
{
ON_Surface* srf = const_cast<ON_Surface*>(face.SurfaceOf());
if ( !srf )
return false;
ON_Interval srf_udom = srf->Domain(0);
ON_Interval srf_vdom = srf->Domain(1);
int fli, li, si=-1;
int lti, ti;
int outer_loop_li=-1;
const int loop_count = m_L.Count();
const int trim_count = m_T.Count();
ON_BoundingBox outer_pbox;
bool bAllTrimsAreIsoTrims = true;
bool bSomeTrimsAreIsoTrims = false;
// 4 April 2003 Dale Lear:
// Shrink srf fix.
ON_BoundingBox trim_iso_endbox; // bounding box of iso curve trim ends
int face_loop_count = face.m_li.Count();
bool bIsSrfEdge[4];
int sei;
for (sei=0; sei<4; sei++)
bIsSrfEdge[sei] = false;
for ( fli = 0; fli < face_loop_count; fli++ )
{
li = face.m_li[fli];
if ( li < 0 )
continue;
if ( li >= loop_count )
continue;
const ON_BrepLoop& loop = m_L[li];
if ( loop.m_type == ON_BrepLoop::outer )
{
// may be more than one outer loop
if ( outer_loop_li )
outer_loop_li = li;
outer_pbox.Union( loop.m_pbox );
int loop_trim_count = loop.m_ti.Count();
for ( lti = 0; lti < loop_trim_count; lti++ )
{
ti = loop.m_ti[lti];
if ( ti >= 0 && ti < trim_count )
{
bool bIsIso = false;
switch( m_T[ti].m_iso )
{
case ON_Surface::x_iso:
case ON_Surface::y_iso:
bIsIso = true;
break;
case ON_Surface::W_iso:
bIsIso = true;
bIsSrfEdge[0] = true;
break;
case ON_Surface::S_iso:
bIsIso = true;
bIsSrfEdge[1] = true;
break;
case ON_Surface::E_iso:
bIsIso = true;
bIsSrfEdge[2] = true;
break;
case ON_Surface::N_iso:
bIsIso = true;
bIsSrfEdge[3] = true;
break;
default:
// it's not an iso curve trim
bAllTrimsAreIsoTrims = false;
}
if (bIsIso){
// it's an iso curve trim
trim_iso_endbox.Set( m_T[ti].PointAtStart(), true );
trim_iso_endbox.Set( m_T[ti].PointAtEnd(), true );
bSomeTrimsAreIsoTrims = true;
}
}
}
}
}
if ( !outer_pbox.IsValid() )
return false;
bool rc = false;
ON_Interval outer_udom( outer_pbox.m_min.x, outer_pbox.m_max.x );
ON_Interval outer_vdom( outer_pbox.m_min.y, outer_pbox.m_max.y );
if ( !bAllTrimsAreIsoTrims )
{
// 4 April 2003 Dale Lear:
// Prevent shrinking surface to
// interior edge of wiggly trims so that
// 3d edge curves will pullback correctly and
// brep-brep intersections will be
// transverse along complicated trims.
double d;
d = outer_udom.Length()*0.01;
if ( (!bSomeTrimsAreIsoTrims || outer_udom[0] < trim_iso_endbox.m_min.x) && !bIsSrfEdge[0] )
outer_udom[0] -= d;
if ( (!bSomeTrimsAreIsoTrims || outer_udom[1] > trim_iso_endbox.m_max.x) && !bIsSrfEdge[2])
outer_udom[1] += d;
d = outer_vdom.Length()*0.01;
if ( (!bSomeTrimsAreIsoTrims || outer_vdom[0] < trim_iso_endbox.m_min.y) && !bIsSrfEdge[1] )
outer_vdom[0] -= d;
if ( (!bSomeTrimsAreIsoTrims || outer_vdom[1] > trim_iso_endbox.m_max.y) && !bIsSrfEdge[3] )
outer_vdom[1] += d;
}
outer_udom.Intersection( srf_udom );
outer_vdom.Intersection( srf_vdom );
bool bShrinkIt = false;
/*
// removed 4 April 2003 Dale Lear
if ( outer_udom.IsIncreasing() && outer_vdom.IsIncreasing() )
{
if ( outer_udom.Length() < 0.99*srf_udom.Length() || outer_vdom.Length() < 0.99*srf_vdom.Length())
{
bShrinkIt = true;
}
else if ( outer_udom.Length() < srf_udom.Length() || outer_vdom.Length() < srf_vdom.Length())
{
// 13 Feb 2003 Dale Lear added this --
// if all trims are isos, then perform micro shrink
// so iso trims will lie on surface boundaries
bShrinkIt = bAllTrimsAreIsoTrims;
}
}
*/
// GBA 8 May 2006. Added DiasbleMask
if( DisableMask & 0x0001) // West
outer_udom[0] = srf_udom[0];
if( DisableMask & 0x0002) // South
outer_vdom[0] = srf_vdom[0];
if( DisableMask & 0x0004) // East
outer_udom[1] = srf_udom[1];
if( DisableMask & 0x0008) // North
outer_vdom[1] = srf_vdom[1];
// added 4 April 2003 Dale Lear
if ( outer_udom.IsIncreasing() && outer_vdom.IsIncreasing() )
{
//TRR #33381 28-April-08 GBA
// Make sure we don't keep allowing the surface to be shrunk.
if ( outer_udom.Length()*ON_ZERO_TOLERANCE < (srf_udom.Length() - outer_udom.Length()) ||
outer_vdom.Length()*ON_ZERO_TOLERANCE < (srf_vdom.Length() - outer_vdom.Length()) )
bShrinkIt = true;
}
if ( bShrinkIt )
{
int srf_use = SurfaceUseCount( face.m_si, 2);
ON_Surface* small_srf = srf->Duplicate();
if ( small_srf->Trim( 0, outer_udom ) )
{
if ( small_srf->Trim( 1, outer_vdom) )
si = AddSurface(small_srf);
if ( si >= 0 )
{
int srf_index = face.m_si;
face.m_si = si;
face.SetProxySurface( m_S[face.m_si] );
// 5 Dec 2002 Chuck - dont delete original surface if used by more than one face
if (srf_use == 1) DeleteSurface(srf_index);
// 1 Nov 2002 Dale Lear - reset face bbox and destroy brep too big bounding box
face.m_bbox = small_srf->BoundingBox();
m_bbox.Destroy();
// Set trim.m_iso flags
for(int li_for_loop=0; li_for_loop<face.LoopCount(); li_for_loop++)
{
ON_BrepLoop& loop = *face.Loop(li_for_loop);
for(int ti_for_loop=0; ti_for_loop<loop.TrimCount(); ti_for_loop++)
{
ON_BrepTrim& trim = *loop.Trim(ti_for_loop);
//Since the slop used in calculating m_iso depends on the srf domain
//all isos should be rechecked after shrinking
/*
if( trim.m_iso==ON_Surface::x_iso ||
trim.m_iso==ON_Surface::y_iso )
*/
if (trim.m_iso != ON_Surface::not_iso)
trim.m_iso = face.IsIsoparametric(trim);
}
}
rc = true;
}
}
if ( !rc )
delete small_srf;
}
return rc;
}
bool ON_Brep::ShrinkSurfaces()
{
bool rc = true;
int fi, face_count = m_F.Count();
for ( fi = 0; fi < face_count; fi++ )
{
if ( !ShrinkSurface( m_F[fi] ) )
rc = false;
}
Compact();
return rc;
}
/*
int ON_Brep::ComponentIndex( const ON_BrepVertex& vertex ) const
{
int component_index = vertex.m_vertex_index;
if ( component_index >= 0 )
component_index += brep_vertex;
else
component_index = -1;
return component_index;
}
int ON_Brep::ComponentIndex( const ON_BrepEdge& edge ) const
{
int component_index = edge.m_edge_index;
if ( component_index >= 0 )
component_index += brep_edge;
else
component_index = -1;
return component_index;
}
int ON_Brep::ComponentIndex( const ON_BrepTrim& trim ) const
{
int component_index = trim.m_trim_index;
if ( component_index >= 0 )
component_index += brep_trim;
else
component_index = -1;
return component_index;
}
int ON_Brep::ComponentIndex( const ON_BrepLoop& loop ) const
{
int component_index = loop.m_loop_index;
if ( component_index >= 0 )
component_index += brep_loop;
else
component_index = -1;
return component_index;
}
int ON_Brep::ComponentIndex( const ON_BrepFace& face ) const
{
int component_index = face.m_face_index;
if ( component_index >= 0 )
component_index += brep_face;
else
component_index = -1;
return component_index;
}
ON_Brep::COMPONENT_TYPE ON_Brep::ComponentIndexType( int component_index )
{
switch( brep_component_mask & component_index )
{
case brep_vertex: return brep_vertex;
case brep_edge: return brep_edge;
case brep_trim: return brep_trim;
case brep_loop: return brep_loop;
case brep_face: return brep_face;
}
return brep_component_unset;
}
*/
const ON_Geometry* ON_Brep::BrepComponent(
ON_COMPONENT_INDEX ci
) const
{
const ON_Geometry* component = 0;
switch ( ci.m_type )
{
case ON_COMPONENT_INDEX::brep_vertex:
component = Vertex(ci.m_index);
break;
case ON_COMPONENT_INDEX::brep_edge:
component = Edge(ci.m_index);
break;
case ON_COMPONENT_INDEX::brep_face:
component = Face(ci.m_index);
break;
case ON_COMPONENT_INDEX::brep_trim:
component = Trim(ci.m_index);
break;
case ON_COMPONENT_INDEX::brep_loop:
component = Loop(ci.m_index);
break;
default:
// other enum values skipped on purpose
break;
}
return component;
}
/*
const ON_Geometry* ON_Brep::BrepComponent(
int component_index
) const
{
const ON_Geometry* component = 0;
if ( -1 != component_index && 0 != component_index)
{
switch( ON_Brep::ComponentIndexType(component_index) )
{
case brep_vertex:
component = Vertex(component_index);
break;
case brep_edge:
component = Edge(component_index);
break;
case brep_trim:
component = Trim(component_index);
break;
case brep_loop:
component = Loop(component_index);
break;
case brep_face:
component = Face(component_index);
break;
}
}
return component;
}
*/
ON_BrepVertex* ON_Brep::Vertex( int vertex_index ) const
{
ON_BrepVertex* vertex = 0;
if ( vertex_index>=0 && vertex_index < m_V.Count() )
vertex = const_cast<ON_BrepVertex*>(&m_V[vertex_index]);
return vertex;
}
ON_BrepVertex* ON_Brep::Vertex( ON_COMPONENT_INDEX vertex_index ) const
{
ON_BrepVertex* vertex = 0;
if ( ON_COMPONENT_INDEX::brep_vertex == vertex_index.m_type
&& vertex_index.m_index >= 0
&& vertex_index.m_index < m_V.Count() )
{
vertex = const_cast<ON_BrepVertex*>(&m_V[vertex_index.m_index]);
}
return vertex;
}
ON_BrepEdge* ON_Brep::Edge( int edge_index ) const
{
ON_BrepEdge* edge = 0;
if ( edge_index>=0 && edge_index < m_E.Count() )
edge = const_cast<ON_BrepEdge*>(&m_E[edge_index]);
return edge;
}
ON_BrepEdge* ON_Brep::Edge( ON_COMPONENT_INDEX edge_index ) const
{
ON_BrepEdge* edge = 0;
if ( ON_COMPONENT_INDEX::brep_edge == edge_index.m_type
&& edge_index.m_index >= 0
&& edge_index.m_index < m_E.Count() )
{
edge = const_cast<ON_BrepEdge*>(&m_E[edge_index.m_index]);
}
return edge;
}
ON_BrepTrim* ON_Brep::Trim( int trim_index ) const
{
ON_BrepTrim* trim = 0;
if ( trim_index>=0 && trim_index < m_T.Count() )
trim = const_cast<ON_BrepTrim*>(&m_T[trim_index]);
return trim;
}
ON_BrepTrim* ON_Brep::Trim( ON_COMPONENT_INDEX trim_index ) const
{
ON_BrepTrim* trim = 0;
if ( ON_COMPONENT_INDEX::brep_trim == trim_index.m_type
&& trim_index.m_index >= 0
&& trim_index.m_index < m_T.Count() )
{
trim = const_cast<ON_BrepTrim*>(&m_T[trim_index.m_index]);
}
return trim;
}
ON_BrepLoop* ON_Brep::Loop( int loop_index ) const
{
ON_BrepLoop* loop = 0;
if ( loop_index>=0 && loop_index < m_L.Count() )
loop = const_cast<ON_BrepLoop*>(&m_L[loop_index]);
return loop;
}
ON_BrepLoop* ON_Brep::Loop( ON_COMPONENT_INDEX loop_index ) const
{
ON_BrepLoop* loop = 0;
if ( ON_COMPONENT_INDEX::brep_loop == loop_index.m_type
&& loop_index.m_index >= 0
&& loop_index.m_index < m_L.Count() )
{
loop = const_cast<ON_BrepLoop*>(&m_L[loop_index.m_index]);
}
return loop;
}
ON_BrepFace* ON_Brep::Face( int face_index ) const
{
ON_BrepFace* face = 0;
if ( face_index>=0 && face_index < m_F.Count() )
face = const_cast<ON_BrepFace*>(&m_F[face_index]);
return face;
}
ON_BrepFace* ON_Brep::Face( ON_COMPONENT_INDEX face_index ) const
{
ON_BrepFace* face = 0;
if ( ON_COMPONENT_INDEX::brep_face == face_index.m_type
&& face_index.m_index >= 0
&& face_index.m_index < m_F.Count() )
{
face = const_cast<ON_BrepFace*>(&m_F[face_index.m_index]);
}
return face;
}
const ON_Surface* ON_BrepFace::SurfaceOf() const
{
const ON_Surface* srf = ProxySurface();
if ( 0 == srf && 0 != m_brep && m_si >= 0 && m_si < m_brep->m_S.Count() )
{
srf = m_brep->m_S[m_si];
}
return srf;
}
void ON_BrepTrim::DestroyPspaceInformation()
{
m_pline.Destroy();
m_pbox.Destroy();
}
bool ON_BrepTrim::ChangeTrimCurve( int c2i )
{
if ( 0 == m_brep )
return 0;
if ( c2i < 0 || c2i >= m_brep->m_C2.Count() )
return 0;
const ON_Curve* c2 = m_brep->m_C2[c2i];
m_c2i = c2i;
DestroyPspaceInformation();
SetProxyCurve(c2);
if ( c2 )
{
m_pbox = c2->BoundingBox();
m_pbox.m_min.z = 0.0;
m_pbox.m_max.z = 0.0;
}
return true;
}
bool ON_BrepTrim::RemoveFromEdge(
bool bRemoveFromStartVertex,
bool bRemoveFromEndVertex
)
{
bool rc = false;
if ( 0 != m_brep || m_ei < 0 )
{
UnsetPlineEdgeParameters();
if ( 0 != m_brep )
{
ON_BrepEdge* edge = m_brep->Edge(m_ei);
if ( 0 != edge )
{
int eti = 0;
while( eti < edge->m_ti.Count() )
{
if ( edge->m_ti[eti] == m_trim_index )
edge->m_ti.Remove(eti);
else
eti++;
}
}
}
m_ei = -1;
if (bRemoveFromStartVertex)
m_vi[0] = -1;
if (bRemoveFromEndVertex)
m_vi[1] = -1;
rc = true;
}
return rc;
}
bool ON_BrepTrim::AttachToEdge(
int edge_index,
bool bRev3d
)
{
bool rc = false;
if ( 0 != m_brep )
{
ON_BrepEdge* edge = m_brep->Edge(edge_index);
if ( 0 != edge )
{
rc = RemoveFromEdge(true,true);
if (rc)
{
edge->m_ti.Append(m_trim_index);
m_ei = edge->m_edge_index;
m_bRev3d = bRev3d ? true : false;
m_vi[0] = edge->m_vi[bRev3d?1:0];
m_vi[1] = edge->m_vi[bRev3d?0:1];
}
}
}
return rc;
}
const ON_Curve* ON_BrepEdge::EdgeCurveOf() const
{
const ON_Curve* c3 = ProxyCurve();
if ( !c3 && m_brep && m_c3i >= 0 && m_c3i < m_brep->m_C3.Count())
{
// fallback to get answer if developer forgot to
// set proxy ptr.
c3 = m_brep->m_C3[m_c3i];
if ( c3 )
{
ON_ERROR("ON_BrepEdge ProxyCurve() is nullptr but m_c3i is valid");
}
}
return c3;
}
int ON_BrepEdge::EdgeCurveIndexOf() const
{
return (m_brep && m_c3i >= 0 && m_c3i < m_brep->m_C3.Count()) ? m_c3i : -1;
}
int ON_BrepTrim::EdgeCurveIndexOf() const
{
int c3i = -1;
if ( m_brep && m_ei >= 0 && m_ei < m_brep->m_E.Count() )
{
c3i = m_brep->m_E[m_ei].m_c3i;
if ( c3i < 0 || c3i >= m_brep->m_C3.Count() )
c3i = -1;
}
return c3i;
}
int ON_BrepTrim::TrimCurveIndexOf() const
{
return ((m_brep && m_c2i >= 0 && m_c2i < m_brep->m_C2.Count()) ? m_c2i : -1);
}
const ON_Curve* ON_BrepTrim::EdgeCurveOf() const
{
const ON_Curve* c3 = 0;
if ( m_brep && m_ei >= 0 && m_ei < m_brep->m_E.Count() )
{
c3 = m_brep->m_E[m_ei].EdgeCurveOf();
}
return c3;
}
bool ON_BrepEdge::ChangeEdgeCurve( int c3i )
{
if ( 0 == m_brep )
return 0;
if ( c3i < 0 || c3i >= m_brep->m_C3.Count() )
return 0;
const ON_Curve* c3 = m_brep->m_C3[c3i];
m_c3i = c3i;
SetProxyCurve(c3);
UnsetPlineEdgeParameters();
return true;
}
const ON_Curve* ON_BrepTrim::TrimCurveOf() const
{
const ON_Curve* c2 = ProxyCurve();
if ( !c2 && m_brep && m_c2i >= 0 && m_c2i < m_brep->m_C2.Count() )
{
// fallback to get answer if developer forgot to
// set proxy ptr.
c2 = m_brep->m_C2[m_c2i];
if ( c2 )
{
ON_ERROR("ON_BrepTrim ProxyCurve() = nullptr but m_c2i is valid");
}
}
return c2;
}
const ON_Surface* ON_BrepTrim::SurfaceOf() const
{
const ON_Surface* srf = 0;
if ( m_brep && m_li >= 0 && m_li < m_brep->m_L.Count() )
{
const int fi = m_brep->m_L[m_li].m_fi;
if ( fi >= 0 && fi < m_brep->m_F.Count() )
{
srf = m_brep->m_F[fi].SurfaceOf();
}
}
return srf;
}
const ON_Surface* ON_BrepLoop::SurfaceOf() const
{
const ON_Surface* srf = 0;
if ( m_brep && m_fi >= 0 && m_fi < m_brep->m_F.Count() )
{
srf = m_brep->m_F[m_fi].SurfaceOf();
}
return srf;
}
int ON_BrepTrim::SurfaceIndexOf() const
{
int si = -1;
if ( m_brep && m_li >= 0 && m_li < m_brep->m_L.Count() )
{
const int fi = m_brep->m_L[m_li].m_fi;
if ( fi >= 0 && fi < m_brep->m_F.Count() )
{
si = m_brep->m_F[fi].m_si;
if ( si < 0 || si >= m_brep->m_S.Count() )
si = -1;
}
}
return si;
}
int ON_BrepTrim::FaceIndexOf() const
{
int fi = -1;
if ( m_brep && m_li >= 0 && m_li < m_brep->m_L.Count() )
{
fi = m_brep->m_L[m_li].m_fi;
if ( fi< 0 || fi >= m_brep->m_F.Count() )
{
fi = -1;
}
}
return fi;
}
static
const ON_BrepTrim* SlitSeamMateHelper( const ON_BrepTrim& trim )
{
if ( ON_BrepTrim::seam != trim.m_type )
return 0;
if ( trim.m_li < 0 )
return 0;
if ( trim.m_ei < 0 )
return 0;
const ON_Brep* brep = trim.Brep();
if ( !brep )
return 0;
if ( trim.m_ei >= brep->m_E.Count() )
return 0;
const ON_BrepEdge& edge = brep->m_E[trim.m_ei];
int other_ti = -1;
for ( int eti = 0; eti < edge.m_ti.Count(); eti++ )
{
int ti = edge.m_ti[eti];
if ( trim.m_trim_index == ti )
continue;
if ( ti < 0 || ti >= brep->m_T.Count() )
continue;
if ( trim.m_li == brep->m_T[ti].m_li )
{
if (other_ti >= 0 )
return 0;
other_ti = ti;
}
}
if ( other_ti < 0 )
return 0;
return &brep->m_T[other_ti];
}
bool ON_BrepTrim::IsSlit() const
{
// 17 Nov 2006
// At this point in the development cycle, I cannot
// add a "slit" type to trim. So, I will use this
// function to distinguish between "slit" and "seam"
// trims.
switch(m_iso)
{
case ON_Surface::E_iso:
case ON_Surface::N_iso:
case ON_Surface::S_iso:
case ON_Surface::W_iso:
return false;
break;
case ON_Surface::not_iso:
case ON_Surface::x_iso:
case ON_Surface::y_iso:
case ON_Surface::iso_count:
// anything else might be a slit
break;
}
const ON_BrepTrim* other_trim = SlitSeamMateHelper(*this);
if ( !other_trim )
return false;
return ( other_trim->m_iso == m_iso );
}
bool ON_BrepTrim::IsSeam() const
{
// 17 Nov 2006
// At this point in the development cycle, I cannot
// add a "slit" type to trim. So, I will use this
// function to distinguish between "slit" and "seam"
// trims.
ON_Surface::ISO other_iso = ON_Surface::not_iso;
switch(m_iso)
{
case ON_Surface::E_iso:
other_iso = ON_Surface::W_iso;
break;
case ON_Surface::N_iso:
other_iso = ON_Surface::S_iso;
break;
case ON_Surface::S_iso:
other_iso = ON_Surface::N_iso;
break;
case ON_Surface::W_iso:
other_iso = ON_Surface::E_iso;
break;
default:
return false;
}
const ON_BrepTrim* other_trim = SlitSeamMateHelper(*this);
if ( !other_trim )
return false;
return ( other_trim->m_iso == other_iso );
}
int ON_BrepLoop::SurfaceIndexOf() const
{
const ON_BrepFace* face = Face();
return face ? face->m_si : -1;
}
int ON_BrepFace::SurfaceIndexOf() const
{
return (m_brep && m_si >= 0 && m_si < m_brep->m_S.Count()) ? m_si : -1;
}
void ON_BrepTrim::UnsetPlineEdgeParameters()
{
int count = m_pline.Count();
if ( count > 0 )
{
ON_BrepTrimPoint* pline = m_pline.Array();
while ( count-- )
(pline++)->e = ON_UNSET_VALUE;
}
}
void ON_BrepEdge::UnsetPlineEdgeParameters()
{
int edge_trim_count, brep_trim_count, eti, ti;
if ( 0 != m_brep )
{
edge_trim_count = m_ti.Count();
if ( edge_trim_count > 0 )
{
brep_trim_count = m_brep->m_T.Count();
for ( eti = 0; eti < edge_trim_count; eti++ )
{
ti = m_ti[eti];
if ( ti >= 0 && ti < brep_trim_count )
{
m_brep->m_T[ti].UnsetPlineEdgeParameters();
}
}
}
}
}
bool ON_BrepFace::TransformTrim( const ON_Xform& xform )
{
if ( !m_brep )
return false;
int fli;
for ( fli = 0; fli < m_li.Count(); fli++ )
{
ON_BrepLoop* loop = m_brep->Loop( m_li[fli] );
if ( loop )
{
if ( !loop->TransformTrim(xform) )
return false;
}
}
return true;
}
bool ON_BrepLoop::TransformTrim( const ON_Xform& xform )
{
if ( !m_brep )
return false;
int lti;
m_pbox.Destroy();
for ( lti = 0; lti < m_ti.Count(); lti++ )
{
ON_BrepTrim* trim = m_brep->Trim( m_ti[lti] );
if ( trim )
{
if ( !trim->TransformTrim(xform) )
return false;
m_pbox.Union( trim->m_pbox );
}
}
return true;
}
bool ON_BrepTrim::TransformTrim( const ON_Xform& xform )
{
// destroy cached information used to accelerate calculations
DestroyCurveTree();
m_pline.Destroy();
if ( !m_brep )
return false;
// make sure only one trim uses the 2d curve
if ( !m_brep->StandardizeTrimCurve( m_trim_index ) )
return false;
// transform 2d curve geometry
ON_Curve* c2 = const_cast<ON_Curve*>(TrimCurveOf());
if ( !c2 )
return true;
if ( !c2->Transform(xform) )
return false;
// update bounding box stored on trim
m_pbox = c2->BoundingBox();
m_pbox.m_min.z = 0.0;
m_pbox.m_max.z = 0.0;
// update 2d tolerances
// Trim transforms can translate, scale and/or swap parameters.
// The tolerances need to be adjusted for scaling and swapping.
// Since the determinant can be < 0, fabs() must be applied.
double tol0 = xform[0][0]*m_tolerance[0] + xform[0][1]*m_tolerance[1];
double tol1 = xform[1][0]*m_tolerance[0] + xform[1][1]*m_tolerance[1];
m_tolerance[0] = fabs(tol0);
m_tolerance[1] = fabs(tol1);
if ( m_iso != ON_Surface::not_iso )
{
m_iso = ON_Surface::not_iso;
m_brep->SetTrimIsoFlags(*this);
}
return true;
}
void ON_BrepTrim::DestroyRuntimeCache( bool bDelete )
{
ON_CurveProxy::DestroyRuntimeCache(bDelete);
// This doesn't work right as of 30 Oct 2002 because
// the pline is getting destroyed while it is still
// valid and needed due to excessive calling
// of DestroyRuntimeCache();
//if ( bDelete )
// m_pline.Destroy();
//else
// m_pline.EmergencyDestroy();
// m_pbox.Destroy(); do not do this - it is not a runtime setting
// and you will break the copy operators
}
void ON_BrepLoop::DestroyRuntimeCache( bool bDelete )
{
ON_Object::DestroyRuntimeCache(bDelete);
// m_pbox.Destroy(); do not do this - it is not a runtime setting
// and you will break the copy operators
}
void ON_BrepFace::DestroyRuntimeCache( bool bDelete )
{
ON_SurfaceProxy::DestroyRuntimeCache(bDelete);
// 15 August 2003 Dale Lear:
// I added the line to destroy the face's m_bbox.
// Since m_bbox is private, it will be recalculated
// when it is needed. (We hope.) The fact the face
// m_bbox is private and recalculated as needed makes
// it different than the m_pbox info on trims and loops.
m_bbox.Destroy();
}
int ON_BrepLoop::Dimension() const
{
return 2;
}
bool ON_BrepLoop::GetBBox(
double* boxmin,
double* boxmax,
bool bGrowBox
) const
{
bool rc = m_pbox.IsValid();
if (rc)
{
ON_BoundingBox bbox;
if ( bGrowBox )
{
bbox.m_min.x = boxmin[0];
bbox.m_min.y = boxmin[1];
bbox.m_min.z = 0.0;
bbox.m_max.x = boxmax[0];
bbox.m_max.y = boxmax[1];
bbox.m_max.z = 0.0;
bbox.Union(m_pbox);
boxmin[0] = bbox.m_min.x;
boxmin[1] = bbox.m_min.y;
boxmax[0] = bbox.m_max.x;
boxmax[1] = bbox.m_max.y;
}
else
{
boxmin[0] = m_pbox.m_min.x;
boxmin[1] = m_pbox.m_min.y;
boxmax[0] = m_pbox.m_max.x;
boxmax[1] = m_pbox.m_max.y;
}
}
return rc;
}
bool ON_BrepLoop::Transform( const ON_Xform& )
{
return false;
}
ON_COMPONENT_INDEX ON_BrepVertex::ComponentIndex() const
{
ON_COMPONENT_INDEX ci(ON_COMPONENT_INDEX::brep_vertex,m_vertex_index);
return ci;
}
ON_COMPONENT_INDEX ON_BrepEdge::ComponentIndex() const
{
ON_COMPONENT_INDEX ci(ON_COMPONENT_INDEX::brep_edge,m_edge_index);
return ci;
}
ON_COMPONENT_INDEX ON_BrepFace::ComponentIndex() const
{
ON_COMPONENT_INDEX ci(ON_COMPONENT_INDEX::brep_face,m_face_index);
return ci;
}
ON_COMPONENT_INDEX ON_BrepTrim::ComponentIndex() const
{
ON_COMPONENT_INDEX ci(ON_COMPONENT_INDEX::brep_trim,m_trim_index);
return ci;
}
ON_COMPONENT_INDEX ON_BrepLoop::ComponentIndex() const
{
ON_COMPONENT_INDEX ci(ON_COMPONENT_INDEX::brep_loop,m_loop_index);
return ci;
}